@gj-kit/nest-notifications 핵심 개념
알림 파이프라인에서 위험한 결정은 프로덕션이 아니라 컴파일 단계에서 막습니다.
왜 이 경계가 필요한가
섹션 제목: “왜 이 경계가 필요한가”직접 짠 알림 outbox는 claim이 원자적이지 않아 두 워커가 같은 행을 집고, inbox 메시지가 중복으로 쓰이며, relay 도중 삭제된 계정으로 push가 나갑니다. 조용시간은 대개 Date에 고정 offset을 더하는 식이라 DST 전환마다 한 시간씩 어긋나고, 24시간으로 나누어떨어지지 않는 batch 창은 집계 버킷을 매일 옮겨 놓습니다.
내구성은 호스트 store에 있습니다
섹션 제목: “내구성은 호스트 store에 있습니다”패키지는 순서와 relay 규칙을 제공하고, 내구성 store, presenter, push gateway, 제품 정책은 앱이 구현합니다.
시간 정책에는 시간대가 필요합니다
섹션 제목: “시간 정책에는 시간대가 필요합니다”quiet hours와 batching은 고정 offset이 아닙니다. timezone과 올바른 일일 window가 DST와 집계 동작을 설명 가능하게 유지합니다.
패키지가 강제하는 것
섹션 제목: “패키지가 강제하는 것”- 필수 옵션은 컴파일 에러입니다 — presenter 없는 createNotificationDispatcher, timeZone 없는 createQuietHoursPolicy는 컴파일되지 않습니다. 기본 카피도 기본 지역도 두지 않기 때문입니다.
- 직접 만든 저장소에 돌리는 적합성 케이스 30개 — notificationStoreContractCases()가 원자적 claim, 배치 유일성, purge와 relay의 교차까지 29개 저장소 의무를 실행 가능한 케이스 30개로 돌려줍니다.
- Nest가 스며들 수 없는 core — 가드가 src/core·src/expo·src/testing 소스와 dist/core.·dist/expo.·dist/testing.* 모듈 그래프 전체에서 @nestjs·rxjs·reflect-metadata 문자열을 한 건도 찾지 못해야 통과합니다. 반대로 dist/index.js에는 @nestjs가 있어야 통과하는 대조군이 함께 있어, 이 가드가 빈 집합을 검사하고 있는 게 아니라는 것까지 확인합니다.
- wakeup 힌트의 반환 타입은 void입니다 — NotificationPipelineWakeup.request()의 반환 타입은 void입니다. await할 것도, 확인할 결과도, 잡을 에러도 없으니 여기에 정확성을 기댈 수 없습니다. 힌트만 배선한 호스트에서 시계를 12시간 앞으로 돌려도 배치 배달은 그대로 남아 있고, dispatchDue()를 부른 뒤에야 나간다는 것을 테스트가 고정합니다.
- DST 해석은 계약입니다 — 봄 전진 갭에 삼켜진 조용시간 해제 시각은 갭 직후 첫 순간으로, 가을 후퇴로 두 번 존재하는 시각은 이른 쪽으로 확정됩니다. batchWindowMs는 24시간이 나누어떨어지는 값이어야 하고, 아니면 조립 시점에 ERR_NOTIFICATION_POLICY_INVALID로 부팅이 멈춥니다.
앱에 남는 책임
섹션 제목: “앱에 남는 책임”credential, endpoint 소유권, 사용자 노출 제품 문구는 앱에 둡니다. 원본 provider 실패 대신 typed error와 delivery outcome을 사용하세요.
다음은 검증된 레시피 두 개입니다.