NGINX JavaScript 모듈로 각 요청마다 JavaScript의 강력함과 편리함 활용하기

NGINX | 2021년 4월 23일

편집자 – “NGINX JavaScript 모듈 소개”라는 제목의 블로그 게시물은 이 페이지로 리디렉션됩니다. 이 게시물은 2021년 4월 기준으로 지원되는 NGINX JavaScript 모듈 지시문과 기능을 반영하여 업데이트되었습니다.

NGINX JavaScript 모듈(njs)은 NGINX Open Source 1.11.10 및 NGINX Plus R12에서 안정화 모듈로 정식 출시되었습니다. [이 모듈은 원래 nginScript라고 불렸으며, 일부 이전 게시물에는 이 이름이 사용되어 있습니다.] NGINX JavaScript가 2015년 9월에 출시된 이후, 안정화 모듈에 포함된 기능과 언어 지원을 추가하며 꾸준히 개발해 왔습니다.

NGINX JavaScript는 NGINX 및 NGINX Plus를 위한 고유한 JavaScript 구현으로, 서버 측 사용 사례와 요청별 처리를 위해 특별히 설계되었습니다. JavaScript 코드로 NGINX 구성 구문을 확장하여 정교한 구성 솔루션을 구현할 수 있습니다.

사용 사례는 매우 다양하며, 특히 NGINX JavaScript 모듈은 HTTP 및 TCP/UDP 프로토콜 모두에서 사용할 수 있습니다. NGINX JavaScript의 사용 사례는 다음과 같습니다:

NGINX JavaScript에 대해 더 자세히 살펴보기 전에, 먼저 흔히 있는 두 가지 오해를 짚고 넘어가겠습니다.

NGINX JavaScript는 Lua가 아닙니다

NGINX 커뮤니티는 수년에 걸쳐 여러 프로그래밍 확장 기능을 개발해 왔습니다. 이 글을 작성하는 시점에는 그중 Lua가 가장 널리 사용되며, NGINX용 모듈 및 NGINX Plus용 공식 지원되는 사전 빌드된 타사 동적 모듈로 제공됩니다. Lua 모듈과 추가 라이브러리는 NGINX 코어와 긴밀하게 통합되며 Redis용 드라이버를 비롯한 다양한 기능을 제공합니다.

Lua는 강력한 스크립팅 언어입니다. 하지만 아직 널리 사용되지는 않아 프런트엔드 개발자나 DevOps 엔지니어가 일반적으로 갖추는 기술 역량에는 포함되지 않는 경우가 많습니다.

NGINX JavaScript는 Lua를 대체하려는 것이 아니며, Lua와 비슷한 수준의 기능을 갖추기까지는 다소 시간이 걸릴 것입니다. NGINX JavaScript의 목표는 널리 사용되는 프로그래밍 언어를 활용하여 최대한 폭넓은 커뮤니티에 프로그래밍 방식의 구성 솔루션을 제공하는 것입니다.

NGINX JavaScript는 Node.js가 아닙니다

NGINX JavaScript는 NGINX 또는 NGINX Plus를 애플리케이션 서버로 전환하는 것을 목표로 하지 않습니다. 간단히 말해, JavaScript 코드가 클라이언트와 콘텐츠 사이에서 실행되므로 NGINX JavaScript의 사용 사례는 미들웨어와 유사합니다. 기술적으로 말하면, Node.js는 NGINX JavaScript와 NGINX 또는 NGINX Plus의 조합과 두 가지 공통점, 즉 이벤트 기반 아키텍처와 JavaScript 프로그래밍 언어를 공유하지만, 유사점은 거기까지입니다.

Node.js는 Google V8 JavaScript 엔진을 사용하는 반면, NGINX JavaScript는 NGINX 및 NGINX Plus에 맞게 특별히 설계된 ECMAScript 표준의 독자적인 구현입니다. Node.js는 메모리에 상주하는 JavaScript 가상 머신(VM)을 사용하고 메모리 관리를 위해 정기적으로 가비지 컬렉션을 수행하는 반면, NGINX JavaScript는 각 요청마다 새로운 JavaScript VM과 필요한 메모리를 초기화하고 요청이 완료되면 해당 메모리를 해제합니다.

서버 측 언어로서의 JavaScript

앞서 언급했듯이 NGINX JavaScript는 JavaScript 언어를 독자적으로 구현한 것입니다. 기존의 다른 모든 JavaScript 런타임 엔진은 웹 브라우저 내에서 실행되도록 설계되었습니다. 클라이언트 측 코드 실행은 시스템 리소스의 가용성부터 동시에 실행 가능한 런타임 수에 이르기까지 여러 측면에서 서버 측 코드 실행과 다릅니다.

서버 측 코드 실행 요구 사항을 충족하고 NGINX의 요청 처리 아키텍처와 자연스럽게 통합하기 위해 자체 JavaScript 런타임을 구현하기로 결정했습니다. NGINX JavaScript의 설계 원칙은 다음과 같습니다:

  • 런타임 환경은 요청과 함께 생성되고 소멸합니다NGINX JavaScript 모듈은 빠르게 초기화하고 폐기할 수 있도록 설계된 단일 스레드 바이트코드 실행 방식을 사용합니다. 런타임 환경은 요청마다 초기화됩니다. 초기화해야 할 복잡한 상태나 헬퍼가 없으므로 시작 속도가 매우 빠릅니다. 실행 중 메모리는 풀에 누적되며, 실행이 완료되면 풀을 해제하여 반환됩니다. 이 메모리 관리 방식에서는 개별 객체를 추적하고 해제하거나 가비지 컬렉터를 사용할 필요가 없습니다.
  • 비차단 코드 실행NGINX 및 NGINX Plus의 이벤트 기반 모델은 개별 NGINX JavaScript 런타임 환경의 실행을 예약합니다. NGINX JavaScript 규칙이 차단 작업(예: 네트워크 데이터 읽기 또는 외부 하위 요청 실행)을 수행하면 NGINX 및 NGINX Plus는 관련 NGINX JavaScript VM의 실행을 자동으로 일시 중단하고 이벤트가 완료될 때 다시 예약합니다. 따라서 규칙을 단순하고 순차적인 방식으로 작성할 수 있으며, NGINX 및 NGINX Plus는 내부 차단 없이 규칙의 실행을 예약합니다.
  • 필요한 언어 기능만 구현JavaScript 사양은 ECMAScript 표준에 정의되어 있습니다. NGINX JavaScript는 ECMAScript 5.1을 따르며, 수학 함수에는 일부 ECMAScript 6 기능을 사용합니다. 자체 JavaScript 런타임을 구현함으로써 서버 측 사용 사례에 필요한 언어 기능을 우선적으로 지원하고 불필요한 기능은 제외할 수 있습니다. 현재 지원되는 언어 요소 목록을 관리하고 있습니다.
  • 요청 처리 단계와의 긴밀한 통합NGINX와 NGINX Plus는 요청을 여러 개별 단계로 나누어 처리합니다. 구성 지시문은 일반적으로 특정 단계에서 작동하며, 네이티브 NGINX 모듈은 특정 단계에서 요청을 검사하거나 수정할 수 있는 기능을 활용하는 경우가 많습니다. NGINX JavaScript는 일부 처리 단계를 구성 지시문을 통해 노출하여 JavaScript 코드가 실행되는 시점을 제어할 수 있도록 합니다. 이러한 구성 구문과의 통합을 통해 JavaScript 코드의 간결함과 네이티브 NGINX 모듈의 강력한 기능 및 유연성을 모두 누릴 수 있습니다. 아래 표에는 이 글을 작성하는 시점을 기준으로 NGINX JavaScript를 통해 접근할 수 있는 처리 단계와 이를 제공하는 구성 지시문이 나와 있습니다.
처리 단계HTTP 모듈스트림 모듈
Access – 인증 및 액세스 제어auth_request 및 js_contentjs_access
Pre-read – 페이로드 읽기/쓰기해당 없음js_preread
Filter – 프록시 처리 중 응답 읽기/쓰기js_body_filter
js_header_filter
js_filter
Content – 클라이언트에 응답 전송js_content해당 없음
Log / Variables – 요청 시 평가js_setjs_set

NGINX JavaScript 시작하기 – 실제 사례

NGINX JavaScript는 NGINX Open Source 바이너리에 컴파일하거나 NGINX 또는 NGINX Plus에 동적으로 로드할 수 있는 모듈로 구현됩니다. NGINX JavaScript 활성화 지침은 이 문서의 끝부분에서 확인할 수 있습니다.

이 예제에서는 NGINX 또는 NGINX Plus를 간단한 리버스 프록시로 사용하고, NGINX JavaScript를 사용하여 다음과 같은 특수 형식의 액세스 로그 항목을 생성합니다.

  • 클라이언트가 전송한 요청 헤더를 포함합니다
  • 백엔드에서 반환한 응답 헤더를 포함합니다
  • ELK Stack(현재 Elastic Stack으로 불림), Graylog, Splunk와 같은 로그 처리 도구에서 효율적으로 수집하고 검색할 수 있도록 키-값 쌍을 사용합니다

이 예제의 NGINX 구성은 매우 간단합니다.

코드 예제를 로드하는 중…

보시다시피 NGINX JavaScript 코드는 구성 구문에 인라인으로 포함되지 않습니다. 대신 js_import 지시어를 사용하여 모든 JavaScript 코드가 들어 있는 파일을 지정합니다. js_set 지시어는 새로운 NGINX 변수인 $access_log_headers와 이 변수에 값을 채우는 JavaScript 함수를 정의합니다. log_format 지시어는 kvpairs라는 새 형식을 정의하며, 각 로그 줄에 $access_log_headers의 값을 기록합니다.

The server 블록은 모든 요청을 https://www.example.com으로 전달하는 간단한 HTTP 리버스 프록시를 정의합니다. access_log 지시어는 모든 요청이 kvpairs 형식으로 기록되도록 지정합니다.

이제 로그 항목을 준비하는 JavaScript 코드를 살펴보겠습니다.

코드 예제를 로드하는 중…

The return value from the kvAccess 함수의 반환 값인 로그 항목은 js_set 구성 지시어에 전달됩니다. 이 지시어는 rawheader_logging.conf에 있습니다. NGINX 변수는 필요할 때 평가되므로, js_set으로 정의된 JavaScript 함수는 변수 값이 필요할 때 실행된다는 점에 유의하세요. 이 예에서는 $access_log_headers가 log_format 지시어에 사용되므로 kvAccess()는 로그 기록 시점에 실행됩니다. map 또는 rewrite 지시어의 일부로 사용되는 변수(이 예에는 나와 있지 않음)는 해당 JavaScript가 더 이른 처리 단계에서 실행되도록 합니다.

리버스 프록시를 통해 요청을 전달한 후 생성된 로그 파일 항목을 살펴보면, 이 NGINX JavaScript 기반 로깅 솔루션이 실제로 작동하는 모습을 확인할 수 있습니다. 이 항목에는 in. 접두사가 붙은 요청 헤더와 out. 접두사가 붙은 응답 헤더가 포함됩니다.

$ curl http://127.0.0.1/$ tail --lines=1 /var/log/nginx/access_headers.log2021-04-23T10:08:15+00:00 client=172.17.0.1 method=GET uri=/index.html status=200 in.Host=localhost:55081 in.User-Agent=curl/7.64.1 in.Accept=*/* out.Content-Type=text/html out.Content-Length=612 out.ETag=x22606339ef-264x22 out.Accept-Ranges=bytes

NGINX JavaScript의 유용성은 대부분 NGINX 내부 기능에 액세스할 수 있다는 점에서 비롯됩니다. 이 예제에서는 요청(r) 객체의 속성 몇 가지를 활용합니다. Stream NGINX JavaScript 모듈(TCP 및 UDP 애플리케이션용)은 고유한 속성 집합을 가진 세션 객체(s)를 활용합니다. HTTP 및 TCP/UDP용 NGINX JavaScript 솔루션의 다른 예제는 NGINX JavaScript 모듈 사용 사례를 참조하세요.

여러분이 생각해 낸 NGINX JavaScript 활용 사례를 듣고 싶습니다. 아래 댓글란에 공유해 주세요.

NGINX JavaScript 모듈 활용 사례

다음 블로그 게시물에서 NGINX JavaScript 모듈을 활용한 다른 HTTP 및 TCP/UDP 사용 사례를 살펴보세요:

[ngx_snippet name=’njs-enable-instructions’]


공유
태그: F5 NGINX, 2021, Tech

작성자 소개

관련 블로그 게시물

시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric
NGINX | 2023. 11. 30.

시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric

이 영상에서는 KubeCon의 NGINX 부스를 찾아가, 저희가 공개한 Kubernetes Gateway API 구현을 살펴봅니다.

F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법
NGINX | 2023. 03. 23.

F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법

앱 보안을 강화하는 두 가지 방법을 소개합니다. NGINX Management Suite를 통해 WAF 운영에 대한 가시성을 높이고, NGINX App Protect WAF를 활용해 코드형 보안을 구현하는 것입니다.

Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션
NGINX | 2021. 09. 27.

Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션

개발 주체, 개발 철학, 프로덕션 환경 준비도, 보안 및 지원을 기준으로 어떤 NGINX Ingress 컨트롤러가 가장 적합한지 알아보세요.

진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법
NGINX | 2021. 07. 06.

진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법

NGINX App Protect Denial of Service(DoS)가 계층 7 DoS 공격을 방어하기 위해 완화 방법을 실시간으로 조정하는 방식을 설명합니다. 정상적인 사용자 행동과 서버 상태에 대한 모델을 생성하여 공격을 나타내는 이상 징후를 감지하고 맞춤형 완화 시그니처를 적용할 수 있습니다.

NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리
NGINX | 2021. 06. 30.

NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리

NGINX Instance Manager 버전 1.0에는 두 가지 새로운 기능이 도입되었습니다. NGINX 인스턴스에 태그와 사용자 역할을 지정하면 전체 그룹에 구성을 한 번에 적용할 수 있습니다. 인증서 검사는 만료 예정인 인증서를 감지하여 서비스 중단을 방지할 수 있도록 인증서를 교체할 수 있게 해줍니다.

Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유
NGINX | 2015. 03. 25.

Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유

Netflix가 글로벌 콘텐츠 전송 네트워크인 Open Connect의 스트리밍 미디어 기술로 NGINX를 선택한 이유를 NGINX 개발자 Gleb Smirnoff에게서 알아보세요.