Search
Duplicate

webserv

Holy Graph
5Circle
간략한 내용
nginx를 c++로 만들어보자
적정 기간
210 hours
제작에 참여한 사람
진행 중인 사람
최종 편집일
Jul 10
통과한 사람
1 more property

Subject

subject.pdf
subject.ko (ver.2022.04.30)
HTTP 서버 만들기 연습
Search
webserv 레퍼런스
NGINX 기본 환경 설정 튜닝
nginx
configuration
NGINX 기본 환경 설정 튜닝

기본 개념 정리

네트워크 개념 기본 정리

네트워크에 대한 기본 개념은 이전에 정리한 netwhat을 참고하자!
OSI 7계층, 패킷 등 기본적인 용어에 대해 알아야 함수이해가 쉬울 것이다!

소켓이란?

함수 정리를 보기 전, 아래 블로그들을 꼭 참고하도록 하자!!

HTTP란?

HTTP는 MDN에 모든 것이 정리되어 있다. 부족한 부분은 RFC문서도 참고해보자!

function and system call

함수 정리

0) 소켓 함수 흐름도

1) socket()

#include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol);
C
복사
Arguments
domain : 인터넷을 통해 통신할 지, 같은 시스템 내에서 프로세스 끼리 통신할 지의 여부를 설정한다.
PF_INET, AF_INET
IPv4 인터넷 프로토콜을 사용한다.
PF_INET6
IPv6 인터넷 프로토콜을 사용한다.
PF_LOCAL, AF_UNIX
같은 시스템 내에서 프로세스 끼리 통신한다.
PF_PACKET
Low level socket 인터페이스를 이용한다.
PF_IPX
IPX 노벨 프로토콜을 사용한다.
IPX 프로토콜 TCP/IP프로토콜과 마찬가지로 라우티드 프로토콜이지만 내부 네트워크에서 사용한다
PF_* 와 AF_* 4.x BSD에서 PF_*와 AF_*를 따로 정의했지만, 이미 BSD 매뉴얼에 PF와 AF는 같다고 약속이 되어 있었고, 그 뒤의 표준들은 그냥 AF를 사용했다고 한다. 자세한 설명은 여기
PF_PACKET 일반적으로 소켓은 TCP 또는 UDP 데이터를 다루지만, 때때로 TCP 이하의 레이어를 건드려야 할 때가 있다. 이때 PF_PACKET을 명시해 주면 된다. 자세한 설명은 여기
type : 어떤 타입의 프로토콜을 사용할 것인지에 대해 설정하는 것이다.
SOCK_STREAM
TCP / IP 프로토콜을 이용한다
SOCK_DGRAM
UDP / IP 프로토콜을 이용한다
SOCK_RAW
전송 계층 포맷팅 없이 패킷을 직접 주고받게 한다 → 링크
protocol : 어떤 프로토콜의 값을 결정하는 것이다.
IPPROTO_TCP
TCP를 기반으로 하는 소켓을 생성
IPPROTO_UDP
UDP를 기반으로 하느 소켓을 생성
Return
success
소켓 디스크립터
fail
-1
각 인자로 들어갈 수 있는 옵션이 추가로 더 있으니 궁금한 사람들은 찾아보길 바란다.

2) sockaddr_in, sockaddr <struct>

#include <netinet/in.h> struct sockaddr { sa_family_t sin_family; // 소켓 주소 체계 (2bytes) char sa_data[14]; // 프로토콜 주소 (14bytes) }
C
복사
소켓의 주소를 담는 기본 구조체이다. sockaddr은 기본 형태이고, 주소체계(AF_INET, AF_INET6 등) 값에 따라서 구조체를 형 변환해서 사용하면 편리하다. sockaddr_in, sockaddr_un, sockaddr_in6 등의 구조체를 활용하면 된다.
sockaddr은 아래와 같은 변수를 가진 구조체이다.
sa_family : 주소체계를 구분하기 위한 변수 (2bytes)
sa_data : 실제 주소를 저장하기 위한 변수 (14bytes)
즉 16bytes의 크기를 가지고 있다.

[1] sockaddr_in

위에서 정의된 sockaddr 구조체에서 sa_familyAF_INET인 경우에 사용하는 구조체이다. sockaddr을 그대로 사용할 경우, sa_dataIP주소와 Port번호가 조합되어 있어 쓰거나 읽기 불편하다. 그래서 sockaddr_in을 사용한다. 이 구조체에서 사용하는 IP주소는 IPv4 주소체계를 사용한다.
#include <netinet/in.h> struct sockaddr_in { sa_family_t sin_family; // 소켓 주소 체계 (2bytes) unsigned short int sin_port; // 포트주소 (2bytes) struct in_addr sin_addr; // IP주소 (4bytes) unsigned char sin_zero[8]; // 사용되지 않는 필드 0을 채워준다 (8bytes) } struct in_addr { u_long s_addr; };
C
복사
sin_family : sockaddr 계열의 구조체가 모두 공통으로 가지고 있는 변수라고 보면 된다.
sin_port : 포트 번호를 가진다. 포트는 0 ~ 65535의 범위를 가지는 값이다. 이 변수에 저장되는 값은 network byte order이여야 한다.
sin_addr : 호스트 IP 주소이다. 이 변수에는 INADDR_ 로 시작하는 값, 예를 들면 'INADDR_ANY' 와 같은 것이 저장되어야 한다. 혹은 inet_aton( ), inet_addr( ), inet_makeaddr( ) 과 같은 라이브러리가 제공하는 함수의 반환값이 저장되어야 한다. 혹은 name resolver를 통해 직접 설정도 가능하다. 이 방법은 gethostbyname( )을 알아보면 된다.
inet_addr(), inet_aton() IPv4 주소는 최종 사용자가 기억하기 쉽게 “127.0.0.1” 처럼 0~255사이의 정수를 . 으로 구별하여 표현한다. 하지만 실제 IPv4주소는 4byte의 정수 값이다. 따라서 위와 같이 문자열을 network byte order 방식의 4byte 정수로 변환해주는 함수를 제공한다. inet_aton()이 inet_addr() 보다 최신 버전의 함수이다. 리턴값으로 에러를 체크할 수 있다. 자세한 설명은 여기
sin_zero : sockaddrsa_data변수가 총 14bytes이기 때문에 각 구조체별로 남는 공간을 0으로 채워주기 위한 변수이다. 반드시 0으로 채워져 있어야 한다! padding bytes라고도 불린다.

[2] sockaddr_in6

IPv6의 경우 이 구조체를 사용한다. 아래 구조에 대해 자세히 알고 싶다면 여기로 이동하자.
#include <netinet/in.h> struct sockaddr_in6 { sa_family_t sin6_family; // 2 in_port_t sin6_port; // 2 uint32_t sin6_flowinfo; // 4 struct in6_addr sin6_addr; // 16 uint32_t sin6_scope_id; // 4 } struct in6_addr { unsigned char s6_addr[6]; };
C
복사

[3] sockaddr_un

AF_UNIX 또는 AF_LOCAL인 경우 이 구조체를 사용한다.
#include <sys/un.h> #define UNIX_PATH_MAX 108 struct sockaddr_un { sa_family_t sun_family; char sun_path[UNIX_PATH_MAX]; };
C
복사
sun_family : 항상 AF_UNIX 값을 가져야 한다.
sun_path : 파일 시스템 경로를 지정한다. NUL로 끝나는 문자열 이여야 한다. 경로의 최대 길이는 NUL terminator를 포함해서 108bytes이다.
이외에도 다양한 구조체들이 있다. 자세히 알고싶다면 여기로 이동하자.

3) bind()

#include <sys/socket.h> int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
C
복사
argument
sockfd : 소켓 디스크립터
addr : 주소 정보 할당
AF_INET
struct sockaddr_in 사용
AF_UNIIX
struct sockaddr 사용
addrlen : addr 구조체 크기
return
success
0 (주소 할당 성공)
fail
-1 (주소 할당 실패)
소켓(fd 로 정해진 소켓)에 주소(sockaddr)를 할당해주는 함수 접속을 기다리는 포트 번호를 지정한다고 생각하면 된다.
Example
#include <sys/socket.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <string.h> #include <stdlib.h> int main() { struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; // IPv4 인터넷 프로토롤 addr.sin_port = htons(4000); // 사용할 port 번호는 4000 addr.sin_addr.s_addr = htonl(INADDR_ANY); // network byte order 32bit IPv4 주소 int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { printf("bind() error!!\n"); exit(1); } return 0; }
C
복사
bind() 후 소켓 정보 확인하기

4) listen()

#include <sys/socket.h> int listen(int sockfd, int backlog);
C
복사
어떤 컴퓨터로부터 요청이 와도 수락할 수 있게 대기 상태에 들어가는 함수.
listen() 함수 호출을 성공하게 되면 여러 클라이언트들이 연결을 요청할 것이고, 모든 연결 요청은 서버가 미리 만들어놓은 대기실로 들어가 순서대로 연결요청이 수락될 때까지 기다려야 한다.
listen() 함수를 호출하면 서버 소켓 상태는 CLOSE 에서 LISTEN 상태로 변경되고, 연결을 요청한 클라이언트 소켓은 SYN_RCVD 상태에서 3-way-handshaking 을 완료하고 ESTABLISHED상태가 된다. 사진은 아래 동작을 순서대로 실행한 후 netstat을 출력한 것이다.
listen() 함수 호출 전
listen() 함수 호출 후
client에서 connect() 호출 후
Argument
sockfd : 소켓 디스크립터
backlog : 연결 요청 대기 큐의 크기 설정
kernal은 listen소켓을 위한 두가지 큐를 가지고 있다. 바로 불완전 연결 큐완전 연결 큐이다. backlog는 이 두가지 큐의 합에대한 최대 값이다.
불완전 연결 큐는 3-way-handshaking(이하 3WHS)을 하기위해 보낸 SYN 패킷을 저장하는 큐이다. 이때 서버측 소켓의 상태는 SYN_RCVD가 된다. 하지만 SYN_RCVD상태는 관찰하기 어렵다. 3WHS가 너무 빠르게 진행되기 때문에 연결이 가능하다면 출력을 하더라도 바로 ESTABLISHED상태가 되기 때문이다. 혹시 SYN_RCVD상태를 관찰할 수 있는 방법을 안다면 내용을 추가해주길 바란다.
3WHS가 완료되면 불완전 연결 큐에 있던 요청이 완전 연결 큐에 저장된다. 이때 소켓의 상태가 ESTABLISHED이다.
그렇다면 backlog에 저장가능한 큐의 size를 넘는 요청이 들어오면 어떻게 될까. 아래 사진을 보면 Client의 소켓의 상태가 SYN_SENT에서 멈춰있는 것을 확인할 수 있다.
이 부분은 netwhat을 공부하면 자세히 알 수 있다. 간단히 설명하면, 3WHS과정에서 connection timeout이 발생하고 재전송 과정을 거치게 된다. 연결 요청에 대한 설정을 하지 않는다면 기본값은 30초 정도로 되어있다.
connection timeout에 대해 자세히 알아보려면 여기로 이동하자.
linux 기준으로 listen의 backlog 값이 net.core.somaxconn 커널 파라미터 값보다 크다면, 실제 적용은 net.core.somaxconn 값이 된다.
listen의 backlog 인자값과 실제 backlog queue 사이즈는 OS에 따라 다를 수도 있다. backlog 인자값이 1로 지정하더라도, OS에 따라 실제 queue의 사이즈는 4로 적용될 수도 있음을 유의하자.
Return
success
0
fail
-1
Example
// client #include <sys/socket.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <arpa/inet.h> #define PORT 4000 #define IPADDR "127.0.0.1" int main() { struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; // IPv4 인터넷 프로토롤 addr.sin_port = htons(PORT); // 사용할 port 번호는 4000 addr.sin_addr.s_addr = inet_addr(IPADDR); // network byte order 32bit IPv4 주소 int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { printf("Can not connect\n"); return -1; } while (1) ; return 0; }
C
복사
// server #include <sys/socket.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <string.h> #include <stdlib.h> int main() { struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; // IPv4 인터넷 프로토롤 addr.sin_port = htons(4000); // 사용할 port 번호는 4000 addr.sin_addr.s_addr = htonl(INADDR_ANY); // network byte order 32bit IPv4 주소 int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { printf("bind() error!!"); exit(1); } if (listen(sockfd, 5) == -1) { printf("listen Fail\n"); return -1; } while (1) ; return 0; }
C
복사

5) accept()

#include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addlen);
C
복사
accept()는 connection-based socket 과 함께 쓰인다. 연결 요청 대기 큐에서 첫번째 요청을 가져와 새로운 소켓을 생성한다. accept()는 이때 생성한 새로운 sockfd를 반환한다. listen상태의 original socket은 accept()의 영향을 받지 않는다.
만약 큐에 요청이 없고 소켓이 차단되지 않은 경우 accept()는 연결이 있을 때까지 호출자를 차단한다. 만약 소켓이 nonblocking이고, 큐가 비어있다면, accept()는 실패하고, EAGAIN 혹은 EWOULDBLOCK 에러를 발생시킨다.
Argument
sockfd : 소켓 디스크립터
addr : (클라이언트)주소 정보 구조체
addlen : addr 길이
Return
success
성공한 소켓의 fd 리턴 (양수)
fail
-1
Example
클라이언트는 기존 코드와 동일하다.
// server ... int main() { ... // 추가된 부분 struct sockaddr_in c_addr; socklen_t c_addr_len = sizeof(c_addr); while (1) { int c_sockfd = accept(sockfd, (struct sockaddr *)&c_addr, &c_addr_len); if (c_sockfd == -1) printf("accept error!!\n"); printf("original fd : %d\n", sockfd); printf("new socket fd : %d\n", c_sockfd); } return 0; }
C
복사
소켓이 nonblocking 상태로 설정 된 경우에도 테스트를 진행해보자. 아래와 같이 코드를 수정하고 실패한 경우 에러 메세지와 errno를 출력해보자. 실제로 nonblocking의 경우, errno가 35인 EAGAIN 을 출력하는 것을 알 수 있다. client의 요청이 들어오는 순간, 정상적인 fd를 출력하고 break를 통해 프로그램이 종료한다.
int main() { ... // 수정하기 int flag = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, flag | O_NONBLOCK); struct sockaddr_in c_addr; socklen_t c_addr_len = sizeof(c_addr); printf("EAGAIN : %d\n", EAGAIN); while (1) { int c_sockfd = accept(sockfd, (struct sockaddr *)&c_addr, &c_addr_len); if (c_sockfd == -1) { fprintf(stderr, "2 : %s(%d)\n",strerror(errno), errno); } else { printf("original fd : %d(%d)\n", sockfd, addr.sin_addr.s_addr); printf("new socket fd : %d(%u)\n", c_sockfd, ntohl(c_addr.sin_addr.s_addr)); break ; } } return 0; }
C
복사

6) connect()

#include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
C
복사
connect()함수가 리턴되는 시점 : 연결 요청이 서버에 의해 수락되거나, 오류가 발생해서 연결 요청이 중단되는 경우이다. 만약 바로 이루어지지 않고 서버의 대기 큐에서 대기하고 있는 상태라면 connect() 함수는 리턴되지 않고 블로킹 상태에 있게 된다.
Argument
sockfd : (서버)소켓 디스크립터
addr : (클라이언트)주소정보 구조체
addrlen : addr길이
Return
success
0
fail
-1
클라이언트의 주소 할당은 connect() 함수를 호출할 때, 커널이 자동으로 해준다.(bind 필요 없음)
Example
// server ... int main() { ... while (1) { int c_sockfd = accept(sockfd, (struct sockaddr *)&c_addr, &c_addr_len); if (c_sockfd == -1) { fprintf(stderr, "2 : %s(%d)\n", strerror(errno), errno); } else { printf("original fd : %d(%d)\n", sockfd, addr.sin_addr.s_addr); printf("new socket fd : %d(%u)\n", c_sockfd, ntohl(c_addr.sin_addr.s_addr)); read(c_sockfd, &buff, BUFF_SIZE); printf("server : %s\n", buff); write(c_sockfd, buff, strlen(buff) + 1); } } return 0; }
C
복사
// client ... int main(int argc, char** argv) { ... // 소켓 연결 if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { printf("Can not connect\n"); return -1; } printf("client socket fd : %d\n", sockfd); write(sockfd, argv[1], strlen(argv[1]) + 1); read(sockfd, &buff, BUFF_SIZE); printf("client : %s\n", buff); close(sockfd); return 0; }
C
복사
추가로 connect() 함수를 공부하다가 통신에 사용되는 fd를 close하는 부분에서 개념적으로 헷갈리는 부분이 있었다. 해당 부분에 대한 자료는 여기서 볼 수 있다.
CLOES_WAIT과 TIME_WAIT (feat. TCP 통신)

7) htons(), htonl(), ntohl(), ntoh()

// Some systems require the inclusion of <netinet/in.h> instead of <arpa/inet.h> #include <arpa/inet.h> uint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort);
C
복사
서로 다른 데이터 저장 방식을 사용하는 시스템끼리 통신을 하게 될경우 전혀 원하지 않는 값들을 서로 주고 받는 경우가 발생할 수 있다. 따라서 데이터 통신을 할 때에는 명시적으로 big endian방식의 network byte order를 따라야 한다. 위 함수들은 통신을 위한 byte order을 맞춰주는 함수이다.
argument
uint*_t * : 변환을 하기위한 인자
return
byte order에 맞게 변환된 값

8) recv(), send()

#include <sys/types.h> #include <sys/socket.h> int send(int sockfd, const void *msg, size_t len, int flags); int recv(int sockfd, void *buf, size_t len, int flags);
C
복사
실제 read/write와 recv/send간의 차이는 크지 않다. 대부분의 경우에는 read/write로도 충분하지만 flag를 사용하고 싶은 경우, send/recv를 사용할 수 있다. 자세한 차이는 man을 참고하자.
Argument
sockfd : 소켓 디스크립터
msg : 전송할 메세지
buf : 수신한 메세지를 저장할 버퍼
len : 전송(수신)할 메세지 크기
flags : 옵션
recv()
내용
MSG_DONTWAIT
MSG_ERRQUEUE
MSG_OOB
MSG_PEEK
send()
내용
MSG_DONTWAIT
전송 혹은 수신을 위한 대기상태가 필요하다면 기다리지 않고 -1을 반환
MSG_NOSIGNAL
상대방과 연결이 끊겼을 때, SIGPIPE 시그널을 받지 않도록 한다.
Return
success
실제 전송(수신)한 바이트 수
fail
-1
Example
// server ... int main() { ... while (1) { int c_sockfd = accept(sockfd, (struct sockaddr *)&c_addr, &c_addr_len); if (c_sockfd == -1) { fprintf(stderr, "2 : %s(%d)\n", strerror(errno), errno); } else { printf("original fd : %d(%d)\n", sockfd, addr.sin_addr.s_addr); printf("new socket fd : %d(%u)\n", c_sockfd, ntohl(c_addr.sin_addr.s_addr)); recv(c_sockfd, &buff, BUFF_SIZE, MSG_DONTWAIT); printf("server : %s\n", buff); send(c_sockfd, buff, strlen(buff) + 1, MSG_DONTWAIT); } } return 0; }
C
복사

8) kqueue()

#include <sys/time.h> #include <sys/event.h> #include <sys/types.h> int kqueue(); int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout);
C
복사
// 나머지 정리해보기

Configuration File

Config File(구성파일) 이란? config 파일은 웹서버나 웹 응용 프로그램에 대한 구성 정보가 들어있는 .config 확장자를 가진 파일로, XML이나 JSON 등의 방식으로 저장할 수 있다. (근데 우리의 config 파일은 xml도 아니도 json도 아닌것같음....)
과제에서는 nginx의 config 와 비교하라고 하기 때문에, nginx의 config 파일을 기준으로 정리한다.
다른 분들 config 파일을 보거나 파싱을 보면, nginx와는 다른 형태로 적혀있는 경우도 많다. nginx의 config는 참고용으로, 어떤 지시어를 사용해야하는지 정도로 알도록 하자!

Nginx configuration

설정 파일의 최상단에 위치하며 nginx의 프로세스 관리, 보안과 같은 기본적인 동작 방식을 정의한다.
Nginx 구성 파일은 하나의 논리적인 구조로 조직된 지시어의 목록으로 이루어져 있고, 이러한 지시어에 정의한 값으로 애플리케이션 전체 동작을 정의한다.
# 예시 user www www; worker_processes 2; error_log /var/log/nginx-error.log info; events { use kqueue; worker_connections 2048; } http { include mime.types; . .
XML
복사
지시어는 항상 세미콜론; 으로 끝나야 한다.
Nginx는 모듈 방식으로 동작하며, 각 모듈은 각각의 지시어를 갖고 있다.
어떤 모듈이 활성화되면 모듈 내에 있는 지시어도 활성화되는데, 이 때 지시어 블록도 활성화 된다.모듈은 지시어 블록을 활성화하여 논리적으로 구성할 수 있다. 모듈이 활성화한 지시어는 해당 모듈 블록에서만 사용할 수 있다.
서버 전반에 영향을 미치는 지시어는 최 상단에 위치해야 하고, 이를 main블록이라고 한다.
http { server { listen 80; server_name example.com; access_log /var/log/nginx/example.com.log; location ^~ /admin/ { index index.php; } } }
XML
복사
위의 예시처럼 중첩하여 쓸 수도 있다. 유의할 점은, 구성이 하위블록에 상속된다는 것이다. access_log 지시어는 이 서버로 들어오는 모든 HTTP요청을 텍스트파일로 기록하게 하는데, 이 구성은 아래의 location 블록에서도 유효하다. (location 블록에서 off 시킬 수도 있다.)
기반모듈 은 엔진엑스의 기본적인 기능을 가진 매개변수를 정의할 수 있는 지시어를 제공한다.
핵심 모듈 (core module) : 프로세스 관리나 보안 같은 필수 기능 및 지시어로 이루어짐
이벤트 모듈(event module): 네트워킹 기능의 내부 동작 방식을 구성한다.
구성 모듈 (configuration module): 구성을 외부 파일에서 가져와 포함시킨다. (include)
다양한 지시어들이 있지만, 웹서브 과제 subject에서 요구하는 기능을 하는 지시어 혹은 자주 쓰이(는것처럼 보이)는 지시어 위주로 정리하였다. (부족한 부분 추가 가능, Nginx HTTP 서버 책 참조)

핵심 모듈 지시어

핵심 모듈 지시어 중 대부분은 반드시 구성 파일의 최상위에 위치해야 하고, 한번만 사용할 수 있다.
지시어
구문과 설명
user
user username groupname; user username; 기본값: 컴파일 할 때 결정 작업자 프로세스 (worker process)를 시작시키는 사용자 계정과 그룹을 지정할 수 있다. groupname 생략 시 user가 사용된다. user root root; 는 보안상 위험하다.
worker_process
worker_process 4; 기본값: 1 작업자 프로세스(worker_process)의 수를 정의한다. 보통 CPU의 코어 수만큼 할당하는 것이 좋으며, 확실하지 않은 경우 auto를 입력하여 자동 감지를 시도할 수 있다. Nginx는 하나의 mater process와 n개의 worker process로 구성되어 실행된다. master process는 설정 파일을 읽고, 프로세스를 유지 및 관리한다.모든 요청은 worker process에서 처리한다. master에서는 클라이언트의 요청도 처리하지 않는다.
error_log (main, http, mail, stream, server, location 에서 사용 가능)
error_log /file/path level; 기본값: logs/err.log error level : debug, info, notice, warn, error, crit, alert, emerg 오류 로그를 끄려면 구성파일 최상단에 error_log /dev/null crit; 로 지정한다. 파일 경로 대신 syslog, memory, stderr 등으로 지정할 수도 있다.

이벤트 모듈 지시어

지시어
구문과 설명
use
use kqueue; 기본값: 없음 어떤 이벤트 모델을 사용할지 결정한다. (nginx는 자동으로 가장 적합한 모델을 선택한다)

구성모듈

은 include 로 다른 구성 파일 가져오는 모듈이다. SKIP
한참 정리하고 쓰다보니 알게 된건데, 우리 과제에서는 그냥 server 블록 위주로만 봐도 될 것 같다. 그걸 여기까지 정리하고 나서야 알아버렸다;;;;

HTTP 핵심 모듈

http 서버의 모든 기반 블록, 지시어, 변수를 포함하는 구성 요소. 주 블록은 http, server , location 이다.
nginx와 같은 config 파일을 만들 필요는 없다. 하지만 서브젝트에 나온 기능들을 모두 구현해야 하기 때문에 어떤 지시어를 우리의 config파일에 넣고 어떻게 동작할지를 정해야 하기 때문에 지시어들을 정리해봐야 할 것 같다. 그거에 맞춰서 클래스 만들고 넣어서 사용하면 될 것 같다.
listen
listen [주소][:포트] [추가옵션]
host와 port 지시어로 구분해서 만들기도 한다.
추가 옵션으로는 default_server, ssl, proxy_protocol 등이 있다.
→이 지시어에서는 host 주소와 포트 번호를 파싱해서 저장하면 된다.
server_name
location
err_page(default_error_page)
allow_methods
root
index (auto_index)
cgi_info
auth_key
(port, cgi)
Nginx Config Core Functionality
Nginx 기본 설정 파일 및 사용방법

HTTP request

Method

GET
POST
PUT
DELETE
HEAD
Search
이름
키워드
링크

CGI

Reference