[EXT-042][실무] 작업 결과를 원래 요청과 연결하기 > IT 기술 공유

본문 바로가기
사이트 내 전체검색

IT 기술 공유

[EXT-042][실무] 작업 결과를 원래 요청과 연결하기

페이지 정보

profile_image
작성자 기술팀장
댓글 0건 조회 8회 작성일 26-09-09 08:33

본문

[이번 수업]
작업자 풀의 Worker는 여러 요청이 함께 씁니다. 결과 이벤트만 보면 어느 요청에서 시작한 작업인지 진단 도구가 놓칠 수 있습니다. 이번에는 AsyncResource(비동기 작업의 시작과 끝을 나타내는 객체)로 제출 시점의 실행 문맥을 저장하고 완료 콜백에 다시 연결합니다.

[선수지식]
EXT-040의 작업자 풀, 콜백과 이벤트를 이해해야 합니다. AsyncLocalStorage는 요청 ID 같은 문맥 값을 비동기 흐름에 보관한다는 점도 알아두세요.

[학습목표]
1. Worker 자체와 Worker에 맡긴 개별 작업을 구분한다.
2. runInAsyncScope()로 완료 콜백을 제출 시점 문맥에서 실행한다.
3. emitDestroy()로 작업 수명이 끝났음을 알린다.

[핵심개념]
오래 살아 있는 Worker의 message 이벤트는 Worker의 비동기 문맥과 연결됩니다. 그러나 한 Worker가 처리하는 작업들은 서로 다른 요청에서 왔을 수 있습니다. 작업을 제출할 때 AsyncResource를 하나 만들면 당시의 실행 문맥을 기록할 수 있습니다. 결과가 오면 runInAsyncScope()로 사용자 콜백을 그 문맥 안에서 실행합니다. emitDestroy()는 해당 자원이 끝났다는 신호이며 한 번만 호출해야 합니다. 이렇게 하면 비동기 스택, 요청별 로그, 성능 진단이 작업의 원인을 더 정확히 보여 줍니다. 공식 Node.js 문서도 Worker 풀에 작업별 AsyncResource를 사용하는 예제를 제공합니다.

[따라하기]
아래 내용을 task-context.mjs로 저장하세요. waiting 배열은 Worker 풀의 대기 작업을 간단히 흉내 냅니다.

```js
import { AsyncLocalStorage, AsyncResource } from 'node:async_hooks';

const context = new AsyncLocalStorage();
const waiting = [];

class PoolTask extends AsyncResource {
  constructor(done) {
    super('PoolTask');
    this.done = done;
  }

  finish(value) {
    this.runInAsyncScope(this.done, null, value);
    this.emitDestroy();
  }
}

context.run({ requestId: 'req-7' }, () => {
  const task = new PoolTask(value => {
    const store = context.getStore();
    console.log(`inside=${store.requestId} result=${value}`);
  });
  waiting.push(task);
});

setImmediate(() => {
  console.log(`outside=${context.getStore()}`);
  waiting.shift().finish(42);
});
```

Windows·macOS·Linux 모두 터미널에서 `node task-context.mjs`를 실행합니다. setImmediate 콜백 바깥에는 문맥이 없지만 finish() 안의 완료 콜백은 req-7을 다시 봅니다.

```text
outside=undefined
inside=req-7 result=42
```

[흔한 실수]
Worker 하나에 AsyncResource 하나만 만들면 여러 요청을 구분하지 못합니다. runInAsyncScope() 없이 콜백을 직접 부르거나 emitDestroy()를 빼먹지 마세요. 같은 자원에서 emitDestroy()를 두 번 호출하는 것도 오류입니다.

[보안 주의]
실행 문맥은 관측을 돕는 정보이지 권한 검사의 근거가 아닙니다. 요청 ID는 서버에서 만들고 인증·인가 값은 매 작업에서 별도로 검증하세요. 문맥과 로그에 토큰, 개인정보, 입력 원문을 넣지 않습니다. 작업 완료와 실패 경로 모두 자원을 정리하는지 본인 소유의 로컬·격리 환경에서 시험하세요.

[직접 해볼 과제]
requestId가 서로 다른 작업 두 개를 waiting에 넣고 완료 순서를 뒤집어 보세요. 각 콜백이 자신의 ID를 출력하는지 확인한 뒤, PoolTask에 실패 메서드를 추가해 오류 콜백도 runInAsyncScope()에서 실행하고 자원을 한 번만 정리하세요.

[확인문제]
1. Worker별 문맥만으로는 여러 요청의 작업을 구분하기 어려운 이유는 무엇일까요?
2. runInAsyncScope()는 완료 콜백을 어느 실행 문맥에서 동작하게 할까요?
3. emitDestroy()를 작업마다 정확히 한 번 호출해야 하는 이유는 무엇일까요?

[다음 학습]
다음에는 diagnostics_channel로 작업의 제출·시작·완료 이벤트를 낮은 결합도로 발행하고 필요한 관측 코드만 구독합니다.

[공식 참고 자료]
Node.js AsyncResource
https://nodejs.org/api/async_context.html#class-asyncresource
Node.js runInAsyncScope()
https://nodejs.org/api/async_context.html#asyncresourceruninasyncscopefn-thisarg-args
Node.js Worker 풀 추적 예제
https://nodejs.org/api/async_context.html#using-asyncresource-for-a-worker-thread-pool
Node.js AsyncLocalStorage
https://nodejs.org/api/async_context.html#class-asynclocalstorage

댓글목록

등록된 댓글이 없습니다.

회원로그인

회원가입

사이트 정보

회사명 : 회사명 / 대표 : 대표자명
주소 : OO도 OO시 OO구 OO동 123-45
사업자 등록번호 : 123-45-67890
전화 : 02-123-4567 팩스 : 02-123-4568
통신판매업신고번호 : 제 OO구 - 123호
개인정보관리책임자 : 정보책임자명

접속자집계

오늘
3,937
어제
6,862
최대
16,772
전체
769,888
Copyright © 소유하신 도메인. All rights reserved.