서론
Node.js 는 여러 요청을 순차적으로 처리하지 않는다. 작업이 완료되면 등록해둔 콜백을 실행하는 방식이기 때문에, 기본적으로는 각 단위의 작업들 간 컨텍스트가 유지되지 않는다!
그런데 웹 서버를 구현할 때, 만든 값(요청 ID, 사용자 정보 등)을 그 뒤에 이어지는 모든 비동기 작업에서 꺼내 쓰려면 컨텍스트가 필요하다. 모니터링 툴은 트레이스에서 DB요청, 서비스 함수 실행등을 요청별로 묶어 보여준다. 이러한 기능은 어떻게 구현될 수 있는 것인지 알아보았다.
AsyncHooks 와 AsyncLocalStorage
개요
- AsyncLocalStorage는 AsyncHooks 을 이용하여 작업 간 공유되는 데이터를 관리한다
- AsyncHooks는 비동기 리소스의 생명주기를 추적하여 사용자가 각 주기에 커스텀 처리를 가능하게 함.
- (hook: 기존 실행 루틴에 내 코드를 걸어서 끼워 넣는다는 의미)
AsyncHooks
1. 훅 등록하기
async_hooks.js의 createHook()과 enable()을 호출하면, 아래 흐름을 거쳐 C++ 런타임(Environment)에 훅 실행용 트램펄린 함수가 등록된다. 훅에 등록한 함수들이 트램펄린 함수를 거치도록 해서 컨텍스트 복원·에러 격리·성능 최적화를 중앙집중식으로 처리하는 목적이다. (트램펄린에 한번 튕겨서 호출한다는 의미)
2. 훅 실행
예를 들어 net.js의 서버 객체를 생성하여 사용하면 TCPWrap 객체가 생성되는데 (이 과정은 포스트에 정리해두었다), 이 때 부모 클래스인 AsyncWrap 가 TCPWrap 객체의 동작에 따라 해당하는 주기에 등록해둔 함수를 호출한다.
new TCPWrap()
AsyncWrap::AsyncWrap
└─ AsyncWrap::AsyncReset
└─ AsyncWrap::EmitAsyncInit

TCPWrap::Listen
└─ OnConnection
└─ MakeCallback



AsyncLocalStorage
AsyncLocalStorage 생성자에서 _enable()을 거쳐 storageHook가 실행된다. 여기서 새로 생성된 비동기 리소스에 부모 리소스가 가진 store를 복사하는 함수(_propage)를 init 에 등록한다. store는 심볼로 정의된, 리소스에 종속된 프로퍼티로 관리한다.


활용 사례
- OpenTelemetry SDK의 AsyncHooksContextManager
한계
- context가 동기 경계에서만 유지된다는 점 (예: 다른 프로세스로의 IPC, Worker Threads 간에는 단절)
- 내부적으로 Map을 기반으로 store를 관리하므로 메모리 누수 가능성
출처
Node.js Docs 와 소스코드
'기술 조사 > Node.js' 카테고리의 다른 글
| Node.js 가 JS 레이어를 유저 코드로부터 보호하는 방법: Symbol과 클로저, primordials (2) | 2025.11.12 |
|---|---|
| Node.js C++ Codebase 영한 번역 (0) | 2025.11.09 |
| inspect 옵션으로 CPU 프로파일 얻고 분석하기 (0) | 2025.10.09 |
| Node.js 딥다이브 (2) - libuv 의 I/O 멀티플렉싱 살펴보기 (3) | 2025.05.03 |
| Node.js에서 멀티코어를 활용하기 (8) | 2025.01.01 |