소개
Nginx는 세계에서 가장 인기 있는 웹 서버 옵션 중 하나입니다. 수많은 동시 클라이언트 연결을 성공적으로 처리할 수 있습니다. 동시에 메일, 웹 또는 역방향 프록시 서버로도 작동합니다.
이 가이드는 Nginx가 클라이언트 요청을 처리하는 방식을 지시하는 배후의 방법을 개략적으로 설명하는 것을 목표로 합니다. 서버 및 location 블록 디자인을 명확히 설명하고, 요청 처리의 예측 불가능해 보이는 요소를 가장 잘 줄이는 방법을 설명합니다.
가장 먼저, 다음은 Ubuntu 서버에 Nginx를 설치하는 방법에 대한 포괄적인 튜토리얼입니다. 이제 시작하겠습니다!
Nginx를 사용한 블록 구성
Nginx의 논리적 접근 방식은 서로 다른 목적을 위한 구성을 별도의 더 논리적인 콘텐츠 블록으로 분류하는 것을 포함합니다. 이들은 계층 구조로 존재하게 됩니다. 클라이언트가 요청을 생성하면, Nginx는 이러한 구성 블록 중 이 요청을 처리하는 데 가장 적합한 블록이 무엇인지 결정하는 프로세스를 시작합니다. 우리는 이 결정 프로세스에 초점을 맞출 것입니다.
우리가 논의할 주요 블록은 server 블록과 location 블록입니다. Server 블록은 정의된 유형의 요청을 처리할 가상 서버를 정의하기 위해 Nginx가 설정하는 구성의 하위 집합입니다. 이들은 가장 일반적으로 들어오는 요청의 IP 주소, 도메인 이름 또는 포트를 기반으로 합니다. 관리자는 여러 server 블록을 구성합니다. 그런 다음 어떤 연결이 요청을 처리해야 하는지 결정해야 합니다.
Location 블록은 server 블록 내에 위치합니다. 이들은 특정 상위 서버로 들어오는 요청을 처리하기 위해 어떤 리소스를 어떻게 활용할 수 있는지 결정하는 역할을 합니다. 이 모델은 매우 유연합니다. URI 공간은 관리자가 가장 좋다고 생각하는 방식으로 이러한 블록을 사용하도록 구성할 수 있습니다.
Nginx로 어떤 블록이 어떤 요청을 처리할지 결정하기
Nginx는 여러 server 블록을 정의할 수 있도록 허용합니다. 이들은 모두 서로 다른 가상 웹 서버로 작동합니다. 따라서 특정 수신 요청을 어떤 서버가 처리할지 구분하는 방법이 필요합니다. 이는 정의된 검사 시스템을 통해 요청 성능에 가장 적합한 것을 찾음으로써 수행됩니다.
Nginx는 주로 두 가지 주요 server 블록 지시문을 다룹니다: listen 및 server_name.
‘Listen’ 지시문으로 가능한 일치 항목 찾기
Nginx가 가장 먼저 평가하는 것은 요청의 포트와 IP 주소입니다. 그런 다음 이를 각 서버의 listen 지시문과 대조합니다. 이 서버 목록 파싱은 해당 요청을 해결할 수 있는 server 블록만 격리하는 데 도움이 됩니다.
일반적으로 listen 지시문은 특정 server 블록이 응답을 담당할 포트와 IP 주소를 정의합니다. listen 지시문이 없는 server 블록은 기본적으로 0.0.0.0:80의 listen 매개변수를 받습니다. Nginx가 루트가 아닌 일반 사용자에 의해 실행되는 경우, listen 매개변수는 0.0.0.0:8080으로 정의됩니다. 즉, 인터페이스가 무엇이든 포트 80에서 블록이 들어오는 경우 이러한 방식으로 정의된 블록이 이에 응답하게 됩니다. 그러나 이 기본값은 서버를 선택하는 과정에서 큰 비중을 차지하지 않습니다.
listen 지시문을 다음과 같이 구성할 수 있습니다:
- 기본 포트(80)에서 요청을 수신 대기하는 단일 IP 주소.
- 해당 포트의 모든 인터페이스를 수신 대기하는 단일 포트.
- 포트 및 IP 주소 조합.
- 설정된 Unix 소켓 경로 (이 옵션은 요청이 서로 다른 서버를 통과할 때만 영향을 미칩니다).
Nginx는 요청을 보낼 server 블록을 결정할 때 일련의 규칙을 구현합니다. 규칙은 listen 지시문의 특정 구성에 따라 다릅니다. 규칙은 다음과 같습니다:
- If a listen directive is incomplete, the missing pieces get their default values. This means that the IP address and port will be forced into completion with default values in order to process the request.
- 이 경우 listen 지시문이 포함되지 않은 블록은 기본값인 0.0.0.0:80을 사용합니다.
- 포트가 누락되고 IP 주소가 111.111.111.111인 블록은 111.111.111.111:80이 됩니다.
- IP 주소가 없는 경우, 포트가 8888인 블록은 기본 IP 주소를 획득하여 추가함으로써 0.0.0.0:8888을 생성합니다.
- 결정된 IP 주소와 포트를 가지고, Nginx는 해당 포트와 일치하는 것으로 제공되는 서버 블록을 찾습니다.
- 특정 일치 항목을 하나만 찾으면 이것이 해당 서버 블록이 됩니다. 조건에 맞는 블록이 여러 개 있는 경우, Nginx는 server_name 지시문을 참조하여 문제가 되는 정확한 서버 블록을 더 자세히 좁혀 나갑니다.
Nginx는 listen 지시문에서 정확한 구체성 수준을 가진 서버 블록을 찾지 못한 경우에만 server_name 지시문을 평가하는 방법을 사용합니다. 만약 example.com이 IP 192.168.1.10의 80번 포트에 있다면, 이 예시의 첫 번째 블록이 항상 요청을 수용하는 블록이 됩니다. 이는 server_name 지시문의 내용과 관계없이 적용됩니다:

구체성이 일치하는 적격 서비스 블록이 둘 이상인 경우, server_name 지시문이 고려됩니다.
‘Server_Name’ 지시문으로 가능한 일치 항목 찾기
listen 지시문이 동일하게 구체적인 경우, Nginx는 요청’의 ‘Host’ 헤더를 확인합니다. 이 값은 클라이언트가 처음에 도달하고자 했던 도메인의 IP를 가집니다. Nginx는 여전히 적격한 각 서버 블록 후보 내부의 server_name 지시문을 활용합니다. 이러한 평가는 공식에 따라 수행됩니다. 공식은 다음과 같습니다:
- Nginx의 첫 번째 시도는 요청의 ‘Host’ 헤더 값과 정확히 일치하는 server_name을 가진 블록을 식별하는 것입니다. 이를 찾으면 정확히 일치하는 항목이 포함된 블록이 요청을 처리하는 블록이 됩니다. 여러 블록을 찾는 경우 목록의 첫 번째 블록을 선택합니다.
- 정확히 일치하는 항목이 없으면 Nginx는 server_name을 사용하여 구성의 서버 블록 이름 시작 부분에 와일드카드인 *를 사용하는 일치하는 서버 블록을 찾으려고 시도합니다. 이 방법으로 일치하는 블록을 찾으면 해당 서버 블록이 결정된 것입니다. 둘 이상의 일치 항목을 찾으면 가장 긴 일치 항목이 요청을 처리하게 됩니다.
- 일치하는 와일드카드가 없으면 Nginx는 일치하는 후행 와일드카드가 있는 서버 블록을 찾으려고 시도합니다. 즉, 이는 구성에서 끝에 *가 있는 서버 이름이 됩니다. 일치하는 항목을 찾으면 요청에 사용됩니다. 반면, 여러 개를 찾으면 Nginx는 다시 한번 가장 긴 일치 항목을 사용합니다.
- 두 와일드카드 시도 후에도 여전히 일치하는 항목이 없는 경우, Nginx는 일반적인 표현식(이름 앞에 ~로 지정됨)을 사용하여 server_name을 정의하는 서버 블록을 평가합니다. ‘Host’ 헤더의 표현식과 일치하는 표현식을 가진 첫 번째 server_name 인스턴스가 요청 처리를 위한 서버 블록으로 간주됩니다.
- 이 시점에서도 여전히 일치하는 항목이 없으면 Nginx는 해당 포트 및 IP 주소 조합에 대한 기본 서버 블록을 사용합니다.
모든 포트/IP 주소 조합에는 지정된 서버 블록이 있습니다. 요청 처리를 위한 적절한 서버 블록을 결정하는 규칙이 성과가 없을 때 이 블록이 사용됩니다. 이는 listen 지시문에 default_server 옵션이 포함된 구성의 첫 번째 블록이 됩니다(처음에 발견된 알고리즘을 재정의함). 각 IP 주소/포트 조합은 최대 하나의 default_server 설정만 가질 수 있습니다.
서버 블록 선택 예시
정의된 server_name이 ‘Host’ 헤더 값과 정확히 일치하면 요청 처리를 위해 선택되는 서버 블록이 됩니다. 다음 예시는 “host1.example.com”으로 지정된 요청의 ‘Host’ 헤더를 보여줍니다. 이 경우 두 번째 서버를 선택합니다:

정확히 일치하는 항목이 없으면 Nginx는 와일드카드가 있는 server_name이 존재하는지 확인합니다. 그렇지 않은 경우 와일드카드로 시작하는 가장 긴 일치 항목이 선택됩니다. 다음에서 “www.example.org”가 “Host” 헤더에 있습니다. 즉, 두 번째 블록을 선택하게 됩니다:

와일드카드로 시작하는 일치 항목이 없으면 Nginx는 뒤에 붙는 와일드카드 검사로 넘어갑니다. 와일드카드로 끝나는 가장 긴 일치 항목이 요청 처리를 위해 선택됩니다. 이 예에서 “Host” 헤더는 “www.example.com”, so it will choose the third server block:

여전히 일치하는 항목이 없으면 Nginx는 정규 표현식을 사용하여 server_name 지시문 일치를 시도합니다. 해당 표현식 중 첫 번째 표현식이 요청 처리를 위해 선택됩니다. “Host”가 “www.example.com,” the second server block will be the choice to attend to the request:

여전히 일치하는 항목이 없으면 요청은 일치하는 기본 서버가 설정된 IP 주소 및 포트 조합으로 이동합니다.
Location 블록 매칭
Nginx는 또한 서버의 어떤 location 블록이 요청에 응답할지 결정하는 알고리즘을 수립해야 합니다.
Location 블록 구문
Nginx가 요청을 처리할 location 블록을 지정하는 방법을 설명하기 전에, location 블록 정의의 구문을 살펴보겠습니다. 앞서 언급했듯이 location 블록은 server 블록(및 다른 location 블록) 내에 위치합니다. 이들의 목적은 요청 URI를 처리하는 방법에 대한 결정을 내리는 것입니다. URI는 요청에서 IP 주소 및 포트 또는 도메인 이름 뒤에 오는 부분입니다.
Location 블록은 일반적으로 다음과 같습니다:

Nginx는 요청의 URI를 location_match와 대조하여 확인합니다. 위의 수식어(modifier) 존재 여부에 따라 Nginx가 블록을 일치시키려는 방식이 결정됩니다. 수식어에 따라 location 블록은 다음 규칙에 따라 해석됩니다:
- 수식어 없음: 수식어가 없으면 location은 접두사 일치(prefix match)로 해석됩니다. 즉, 제공된 location이 요청의 URI 시작 부분과 일치하는지 확인하여 올바른 일치 항목을 결정합니다.
- =: 등호(=)는 요청의 URI가 제공된 location과 정확히 일치하는 경우에만 이 블록이 일치하는 것으로 간주됨을 의미합니다.
- ~: 물결표(~) 수식어는 location 블록의 일치가 대소문자를 구분함을 나타냅니다.
- ~*: 물결표와 별표(~*) 수식어의 조합은 location 블록이 일치 항목을 찾을 때 대소문자를 구분하지 않음을 나타냅니다.
- ^~: 물결표 수식어 앞에 캐럿(^~)이 있으면, 이 블록이 가장 적합한 비정규 표현식 일치 항목으로 선택되는 한 정규 표현식 매칭이 발생하지 않습니다.
Location 블록 구문 예시
접두사 일치의 예를 들면, location 블록은 /site, /site/page1/index.html 또는 /site/index/html 형식의 요청 URI에 응답하도록 선택됩니다:

필수 URI 일치에 대한 이번 시연의 목적상, 이 블록은 항상 /page1 형식의 URI 요청에 응답하는 데 사용되며, /page1/index.html 요청 URI에는 사용되지 않습니다. 이 블록이 선택되고 인덱스 페이지를 사용하여 요청을 처리하는 경우, 요청의 실제 처리기는 내부적으로 다른 location으로 리디렉션됩니다:

예를 들어, 대소문자를 구분하는 표현식으로 해석되어야 하는 location의 경우, 다음 블록은 /FLOWER.PNG에 대한 요청을 처리할 수 없습니다. 하지만 /tortoise.jpg에 대한 요청은 처리합니다:

다음으로, 위와 유사하지만 대소문자를 구분하지 않는 일치를 허용하는 블록을 살펴보겠습니다. 이 경우 블록은 //tortoise.jpg 및 /FLOWER.PNG 모두를 처리할 수 있습니다:

마지막 변형은 해당 블록이 최적의 비정규 표현식 일치 항목으로 결정될 경우 정규 표현식 매칭이 실행되지 않도록 방지하는 블록입니다. 이 블록은 /costumes/ninja.html에 대한 요청을 처리할 수 있습니다:

요약하자면, 수식어는 location 블록이 결정되는 방식을 규정합니다. 그러나 이것이 Nginx가 요청을 보낼 location 블록을 식별하기 위해 어떤 의사 결정 알고리즘을 사용하는지 알려주지는 않습니다. 다음으로 이 부분을 다루어 보겠습니다.
Nginx가 요청을 처리할 Location 선택하기
Nginx가 요청을 처리할 location을 선택하는 방법은 서버 블록이 선택되는 방식과 유사합니다. 즉, 일련의 과정을 거쳐 모든 요청에 대한 최적의 location을 결정합니다. Nginx를 정확하고 적절하게 구성하려면 이 과정을 이해하는 것이 필수적입니다.
앞서 언급한 location 선언을 염두에 두고, Nginx는 이와 유사하게 제공된 요청의 URI와 비교하여 모든 location의 자격을 확인함으로써 잠재적인 location 컨텍스트를 사용합니다. 이 과정에서 다음과 같은 알고리즘을 적용합니다:
- 첫째, Nginx는 정규 표현식을 포함하지 않는 모든 location 유형을 확인합니다. 이는 모든 location 기반 접두사 일치(prefix match)를 검색하여 수행됩니다. 이를 위해 요청의 전체 URI와 location을 대조하여 확인합니다.
- Nginx는 정확한 일치(exact match)를 찾기 시작합니다. = 수정자(modifier)를 사용하는 location 블록이 식별되면, 이를 URI 요청과 비교합니다. 두 개가 정확히 일치하면, 해당 location 블록이 즉시 요청을 처리하도록 선택됩니다.
- = 수정자 비교와 정확히 일치하는 location이 없으면, Nginx는 정확히 일치하지 않는 접두사 평가를 진행합니다. 요청의 URI와 일치하는 가장 긴 접두사 location을 결정하면, 다음과 같은 평가를 수행합니다:
- 가장 긴 접두사 일치를 가진 location이 ^~ 수정자를 사용하는 경우, 이 location이 즉시 선택됩니다.
- 가장 긴 접두사를 가진 location이 ^~ 수정자를 사용하지 않는 경우, Nginx는 검색의 초점을 전환할 수 있도록 일치 항목을 임시로 보관합니다.
- 가장 긴 접두사 location 일치를 찾아 저장하면, Nginx는 정규 표현식 location 평가로 전환합니다. 여기에는 대소문자를 구분하는 일치와 구분하지 않는 일치가 모두 포함됩니다. 가장 길게 일치하는 접두사 location 내에 정규 표현식 location이 있는 경우, Nginx는 목록을 재구성하여 이를 location 목록의 상단 부근에 배치합니다. 재정렬된 목록에서 요청의 URI와 일치하는 첫 번째 표현식이 요청을 처리할 location으로 선택됩니다.
- 요청 RI를 만족하는 정규 표현식이 없으면, 이전에 저장된 location이 요청을 처리하도록 선택됩니다.
Nginx는 기본적으로 우선순위가 지정된 접두사 일치보다 정규 표현식 일치를 우선시합니다. 하지만 접두사 location을 먼저 평가하므로, 관리자는 = 및 ^~ 수정자를 사용하여 이러한 경향을 무효화할 수 있습니다.
또 다른 중요한 점은 접두사 location은 일반적으로 가장 구체적이고 가장 길게 일치하는 항목을 기준으로 하는 반면, 정규 표현식 검사는 첫 번째 일치 항목이 식별되는 즉시 중단된다는 것입니다. 이는 구성 파일 내에서의 위치가 정규 표현식 location에 실질적인 영향을 미친다는 것을 의미합니다.
마지막으로 언급할 점은, 가장 긴 접두사 일치 내의 정규 표현식 일치가 Nginx의 location 평가 중에 본질적으로 순서를 건너뛰게 된다는 것입니다. 이들은 목록의 맨 위에 배치되어 다른 정규 표현식보다 먼저 평가됩니다.
Location 블록 평가에서 다른 Location으로의 이동은 언제 발생하나요?
일반적으로 요청이 평가되고 이를 처리할 location 블록이 선택되면, 해당 컨텍스트 내에서 완전히 처리됩니다. 즉, 형제(sibling) location 블록의 개입 없이 상속된 지시어와 선택된 location만이 요청 처리의 결정 요인이 됩니다.
이는 location 블록의 예측 가능한 설계를 가능하게 하는 일반적인 지침이지만, 때로는 location 내의 특정 지시어가 새로운 검색을 트리거할 수도 있습니다. 즉, '단 하나의 location 블록' 규칙에는 몇 가지 예외가 있습니다. 이러한 예외는 location 블록의 예상과 일치하지 않을 수 있습니다. 따라서 요청이 예상대로 처리되지 않을 수 있습니다.
이러한 내부 리디렉션은 다음과 같은 일부 지시어로 인해 발생할 수 있습니다:
- index
- rewrite
- error_page
- try_files
index 지시문을 사용하면 요청 처리 중에 항상 내부 리디렉션이 발생합니다. 일반적으로 일치하는 location을 찾으면 선택 프로세스의 속도를 높이기 위해 알고리즘 실행이 종료되지만, 찾은 location 일치 항목이 디렉터리인 경우 요청이 공식적으로 처리되기 위해 다른 location으로 리디렉션될 가능성이 높습니다.
예를 들어, 다음의 첫 번째 location은 요청 URI인 /exact와 일치합니다. 그러나 요청을 처리하기 위해 location 블록이 상속하는 index 지시문은 요청을 보조 블록으로 리디렉션합니다.

이 시나리오에서 실행이 기본 블록 내에 유지되어야 하는 경우, 다른 방식으로 디렉터리에 대한 요청을 처리해야 합니다. 이를 수행하는 한 가지 방법은 해당 블록에 대해 유효하지 않은 index를 설정하고 대신 auto index를 활성화하는 것입니다.

이 방법이 몇 가지 경우에는 작동할 수 있지만, 대부분의 상황에서 대체로 실용적으로 적용할 수는 없습니다. 정확한 디렉터리 일치는 요청을 다시 작성(rewrite)해야 하는 상황에서 유용할 수 있습니다. 이는 완전히 새로운 location 검색을 트리거합니다.
처리 location을 재평가하는 데 사용할 수 있는 또 다른 지시문은 try_files 지시문입니다. 이는 Nginx에 지정된 파일 또는 디렉터리 세트가 존재하는지 구체적으로 확인하도록 지시하며, 마지막 검색 기준은 Nginx가 내부적으로 리디렉션할 URI가 됩니다.
다음 구성을 살펴보겠습니다.

/blahblah에 대한 요청이 있는 경우, 첫 번째 location이 이를 수신합니다. /var/www/main 디렉터리에서 blahblah 파일을 찾지 못하면 blahblah.html에 대한 후속 검색이 트리거됩니다. 그런 다음 /var/www/main 디렉터리에서 blahblah라는 이름의 하위 디렉터리를 찾습니다. 이러한 모든 확인이 실패하면 /fallback/index.html로 리디렉션됩니다. 이는 다른 location 블록이 감지할 또 다른 location 검색을 트리거합니다. 그런 다음 /var/www/another/fallback/index.html 파일을 처리합니다.
다른 location 블록으로 리디렉션되는 결과를 낳는 또 다른 지시문은 rewrite 지시문입니다. Nginx는 last 매개변수가 사용될 때 rewrite 지시문의 결과에 따라 새로운 일치하는 location을 검색합니다. 마지막 예시를 이 rewrite 지시문을 포함하도록 수정하면, try_files 지시문을 구현하지 않고도 요청을 다른 location으로 리디렉션할 수 있음이 분명해집니다.

이 예시의 경우, /rewrite/hello에 대한 요청은 처음에 첫 번째 location에서 처리됩니다. /hello로 다시 작성(rewrite)된 후, 보조 location 검색이 트리거됩니다. 이는 첫 번째 location과 일치하게 됩니다. 이는 try_file 지시문에 의해 처리되며, 일치하는 항목이 없으면 잠재적으로 /fallback/index.html로 되돌아갑니다.
그러나 /rewrite/fallback/hello에 대한 요청이 발생하면 첫 번째 블록과의 일치 항목이 발견됩니다. 따라서 rewrite가 다시 처리되지만, 이번에는 결과로 /fallback/hello를 생성합니다. 요청은 다른 location 블록에서 처리됩니다.
return 지시문을 사용하여 301 또는 302 상태 코드를 보낼 때도 유사한 상황이 발생합니다. 유일한 차이점은 새로운 요청이 발생하고 매우 명백한 리디렉션으로 나타난다는 것입니다. 마찬가지로, permanent 또는 redirect 플래그를 적용할 때 rewrite 지시문에서도 이와 같은 현상이 발생할 수 있습니다.
try_again과 유사한 내부 리디렉션을 유도할 수 있는 또 다른 지시문은 error_page 지시문입니다. 처리 중에 특정 오류 코드가 발생할 때 이를 사용할 수 있습니다. try_files 지시문이 설정되어 있으면 error_page 지시문은 실행되지 않을 가능성이 높습니다. 그것은’ 해당 지시문이 요청의 전체 수명 주기를 처리하기 때문입니다.
다음 예시를 살펴보겠습니다.

이 경우, 모든 요청은 /var/www/main에서 파일을 제공하는 첫 번째 블록에 의해 처리됩니다. 이는 /another로 시작하는 요청에는 적용되지 않습니다. 하지만 파일을 찾을 수 없는 경우, /another/whoops/html로 내부 리디렉션이 시작됩니다. 이는 또 다른 location 검색으로 이어집니다. 결과적으로 요청을 보조 블록으로 안내하며, 해당 파일은 /var/www/another/whoops.html에서 처리됩니다.
분명하듯이, Nginx가 새로운 location 검색을 트리거하는 상황을 이해하면 요청이 처리될 때 시스템 동작을 더 잘 예측하는 데 도움이 될 수 있습니다.
결론
관리자가 Nginx가 클라이언트 요청을 처리하는 방식을 이해하면 업무가 훨씬 더 단순해집니다. 이를 통해 관리자는 요청이 어느 server 블록으로 이동할지 확인할 수 있습니다. 또한 요청 URI를 기반으로 선택될 location 블록을 결정할 수도 있습니다. 대체로 이는 관리자에게 각 요청을 처리할 때 Nginx가 적용한 컨텍스트를 추적할 수 있는 기능도 제공합니다.
마지막으로, 다음에서 다른 튜토리얼을 살펴보실 수 있습니다: 저희 블로그의 Nginx에 초점을 맞춘 튜토리얼들입니다. 이들은 세계에서 가장 인기 있는 웹 서버 중 하나를 더 잘 활용하는 데 도움이 될 것입니다:
- 웹 서버의 세계: Apache vs. Nginx
- Ubuntu 20.04에서 Let’s Encrypt로 Nginx를 보호하는 방법
- Nginx를 위한 LetsEncrypt SSL 인증서 자동 갱신 방법
- Docker Compose로 Laravel, Nginx 및 MySQL 배포하기
즐거운 컴퓨팅 되세요!
댓글
아직 댓글이 없습니다. 첫 번째로 작성해 보세요.