Skip to content

refactor: 알림 서비스 책임 분리 - #133

Merged
wkdalswn11 merged 1 commit into
devfrom
refactor/notification-service
Aug 12, 2026
Merged

wkdalswn11 merged 1 commit into
devfrom
refactor/notification-service

Conversation

@juengseulki

@juengseulki juengseulki commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

📌 작업 내용

  • notification 서비스에 혼재되어 있던 정책 판단, 응답 매핑, SSE 관련 상수 책임을 분리했습니다.
  • 알림 읽기/만료/cleanup 및 bulk 알림 관련 순수 로직을 policy로 분리했습니다.
  • 목록/읽음/미읽음 수 응답 조립을 mapper로 분리했습니다.
  • SSE heartbeat/event name 등 운영 상수를 constants로 분리했습니다.
  • Repository의 활성 알림 조회 조건과 create/createMany data 조립 중복을 정리했습니다.
  • 기존 DB 저장 / SSE 전송 경계, 읽음 처리 Transaction, Bulk 알림 동작, Cleanup Scheduler는 변경하지 않았습니다.

✅ 변경 사항

notification.constants.ts 추가

알림 도메인에서 사용하는 정책 및 SSE 관련 상수를 분리했습니다.

  • BULK_NOTIFICATION_BATCH_SIZE
  • CHAT_READ_VISIBILITY_DAYS
  • NOTIFICATION_RETENTION_DAYS
  • HEARTBEAT_INTERVAL_MS
  • SUPPORTED_NOTICE_AUDIENCES
  • NOTIFICATION_SSE_EVENTS

notification.policy.ts 추가

DB/Repository 의존 없이 입력값만으로 판단하거나 계산하는 순수 정책을 분리했습니다.

  • assertSupportedNoticeAudience
  • normalizeBulkNotificationSourceId
  • assertValidBulkNotificationSnapshotAt
  • assertReadableNotification
  • resolveReadExpiresAt
  • resolveReadAllChatExpiresAt
  • resolveCleanupDeleteBefore

notification.mapper.ts 추가

Service에서 처리하던 API Response 조립 책임을 분리했습니다.

  • mapNotificationListResponse
  • mapUnreadNotificationCountResponse
  • mapReadNotificationResponse
  • mapReadAllNotificationsResponse
  • mapOwnedNotificationToItem

notification.service.ts 정리

Service에는 다음 책임을 유지했습니다.

  • 알림 Use Case Orchestration
  • Repository 호출 순서
  • readAllNotifications() Transaction
  • Bulk Create 처리 흐름
  • SSE Refresh 호출 시점 결정
  • createNotification() DB 저장 경계
  • sendNotification() SSE 전송 위임

순수 정책 판단 및 Response 조립 로직은 각각 policy, mapper로 이동했습니다.

notification.repository.ts 정리

다음 공통 로직을 분리해 중복을 줄였습니다.

  • notificationSelect
  • ownedNotificationSelect
  • buildActiveNotificationWhere
  • toNotificationCreateData

Pagination의 findMany + count 구조는 기존대로 유지했습니다.

notification-sse.service.ts 정리

SSE Connection Lifecycle은 기존 구조를 그대로 유지했습니다.

  • Connection 등록/제거
  • User별 Connection 관리
  • Heartbeat
  • Event 전송
  • Timer Cleanup

Heartbeat Interval 및 Event Name만 constants를 참조하도록 변경했습니다.


🔄 Transaction / SSE 경계

기존 DB Transaction과 외부 I/O 경계를 유지했습니다.

DB 저장
  ↓
Transaction Commit
  ↓
SSE 전송
  • createNotification() → DB 저장만 담당
  • sendNotification() → SSE 전송만 담당
  • sendNotificationRefresh() → SSE Refresh Event 전송만 담당
  • Transaction Callback 내부에서 SSE Write를 수행하지 않음

readAllNotifications()

기존 Transaction 구조를 유지했습니다.

Transaction
 ├─ CHAT Notification updateMany
 └─ Non-CHAT Notification updateMany

두 작업은 동일한 Transaction 내에서 수행되며 중간 실패 시 부분 반영되지 않도록 기존 원자성을 유지했습니다.


📢 Bulk Notification

기존 Bulk 알림 생성 동작을 유지했습니다.

  • createMany() 유지
  • BULK_NOTIFICATION_BATCH_SIZE = 500 유지
  • skipDuplicates 유지
  • sourceId 기반 중복 방지 의미 유지
  • DB 저장 이후 sendNotificationRefresh() 호출

sourceId.trim()은 반복 호출하지 않고 Policy에서 한 번 정규화한 뒤 재사용하도록 정리했습니다.


⏰ Read / Expiration

기존 알림 만료 정책을 유지했습니다.

  • 단건 읽음 시 기존 ownership / 만료 검증 유지
  • CHAT 알림은 읽은 시점 기준 3일 동안 유지
  • 전체 읽음에서도 동일한 CHAT_READ_VISIBILITY_DAYS = 3 적용
  • 일반 알림은 기존 expiresAt 유지
  • Cleanup Retention은 기존과 동일하게 90일 유지

🧹 Cleanup

기존 notification-cleanup.job.ts Scheduler를 유지했습니다.

  • KST 기준 매일 03:00 실행
  • Retention 90일 유지
  • Request Path에서 Cleanup 직접 수행하지 않음
  • Scheduler 자체 변경 없음

🔢 Prisma Count 점검

추가적인 count() / _count 변경은 진행하지 않았습니다.

  • countUnreadByUserId() → 기존 count() 사용 유지
  • Pagination → 기존 findMany + count 유지
  • _count로 전환할 명확한 Relation Count 구간 없음

🔗 외부 모듈 영향 확인

다음 Notification 호출처를 확인했습니다.

  • estimate-request
  • estimate/customer
  • estimate/mover
  • review
  • inquiry
  • admin/contents
  • admin/notice
  • jobs

다음 Public Method Signature는 변경하지 않았습니다.

  • createNotification
  • createBulkNotification
  • sendNotification
  • cleanupExpiredNotifications

따라서 기존 호출처 수정은 발생하지 않았습니다.


🛡️ API Contract

기존 Contract를 유지했습니다.

  • Endpoint 변경 없음
  • Request / Query 변경 없음
  • Response Shape 변경 없음
  • HTTP Status 변경 없음
  • Error Code / Message 변경 없음
  • Pagination 변경 없음
  • Notification Type 변경 없음
  • title / content / linkUrl / expiresAt 변경 없음
  • Read / Unread 의미 변경 없음
  • SSE Event Name 변경 없음
  • SSE Payload 변경 없음

🧪 테스트

실행

npm run lint
npm run build
npm test

결과

  • lint: PASS
  • build: PASS
  • test: PASS
  • 74 passed, 0 failed

테스트 로그의 Rate Limit 관련 Error 출력은 기존 기대 로그이며 테스트 실패는 아닙니다.


📷 스크린샷

백엔드 구조 리팩토링으로 UI 변경 사항은 없습니다.


🔥 체크리스트

  • 코드 컨벤션을 준수했습니다.
  • 불필요한 console.log를 제거했습니다.
  • ESLint 오류가 없습니다.
  • Build가 정상적으로 완료됩니다.
  • 전체 테스트가 통과합니다.
  • 기존 API Contract를 유지했습니다.
  • Transaction 경계를 유지했습니다.
  • SSE / 외부 I/O 경계를 유지했습니다.
  • 관련 문서를 업데이트했습니다. (필요 시)

🙏 To Reviewer

기능 변경이 아닌 Notification 모듈의 책임 분리를 목적으로 진행한 리팩토링입니다.

Service에는 Use Case Orchestration, Repository 호출 순서, Transaction 및 SSE 전송 시점 결정을 유지했습니다.

DB 접근이 필요 없는 읽기/만료/Bulk/Cleanup 관련 순수 로직은 policy로, API Response 조립은 mapper로 분리했습니다.

SSE Connection Lifecycle은 기존 notification-sse.service.ts에 그대로 유지하고 Event Name / Heartbeat 값만 Constants로 정리했습니다.

특히 아래 부분이 기존과 동일하게 유지되는지 중점적으로 확인 부탁드립니다.

  • createNotification()의 DB 저장 책임
  • sendNotification()의 SSE 전송 책임
  • readAllNotifications()의 Transaction
  • CHAT 알림 읽음 후 만료 정책
  • Bulk Notification의 sourceId + skipDuplicates 중복 방지
  • SSE Event Name / Payload
  • 기존 API Response Shape

후속 개선 후보

  • Notification 모듈 전용 Unit / Integration Test 추가
  • SSE In-memory Connection Map의 멀티 인스턴스 환경 대응 검토
  • 다른 도메인의 createNotification(..., tx)와 Commit 이후 sendNotification() 호출 경계 일관성 재점검
  • Cleanup Scheduler 관측성 및 Heartbeat 운영값 조정 검토

Summary by CodeRabbit

  • 개선 사항
    • 알림 목록, 읽지 않은 알림 수, 읽음 처리 및 전체 읽음 처리 응답 형식이 일관되게 제공됩니다.
    • 알림 읽음 가능 여부와 만료 기준이 안정적으로 적용됩니다.
    • 대량 알림 생성 시 지원 대상과 기준 정보가 검증됩니다.
    • 실시간 알림 연결 및 목록 갱신 이벤트 처리가 일관화되었습니다.
    • 알림 보존 기간과 정리 기준이 명확해져 오래된 알림 관리가 개선됩니다.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 42f4d775-f1dc-4f83-9fe4-5c2f218d4c06

📥 Commits

Reviewing files that changed from the base of the PR and between 87a0a40 and 4fe97b2.

📒 Files selected for processing (7)
  • src/modules/notification/notification-sse.service.ts
  • src/modules/notification/notification.constants.ts
  • src/modules/notification/notification.mapper.ts
  • src/modules/notification/notification.policy.ts
  • src/modules/notification/notification.repository.ts
  • src/modules/notification/notification.service.ts
  • src/modules/notification/notification.type.ts

📝 Walkthrough

Walkthrough

알림 모듈의 상수, 정책, 응답 매퍼, 저장소 헬퍼를 분리했습니다. 알림 서비스와 SSE 서비스는 분리된 모듈을 사용하도록 변경했습니다.

Changes

알림 처리 구조 정리

Layer / File(s) Summary
알림 상수·타입·정책 정의
src/modules/notification/notification.constants.ts, src/modules/notification/notification.type.ts, src/modules/notification/notification.policy.ts
알림 기간, 배치 크기, SSE 이벤트 상수와 OwnedNotificationItem을 추가했습니다. 대상 역할, sourceId, 기준 시각, 소유권, 만료 상태를 검증하는 정책 함수를 추가했습니다.
저장소 공통 헬퍼 적용
src/modules/notification/notification.repository.ts
활성 알림 조건, 생성 데이터 변환, 소유 알림 조회 선택 필드를 공통 헬퍼로 통합했습니다. 단건·대량 생성과 목록·미읽음·전체 읽음 처리에서 헬퍼를 사용합니다.
응답 변환과 서비스 위임
src/modules/notification/notification.mapper.ts, src/modules/notification/notification.service.ts
페이지네이션, 읽음 처리, 미읽음 수 응답 매퍼를 추가했습니다. 서비스는 검증, 만료 계산, 응답 변환을 외부 모듈에 위임합니다.
SSE 상수 적용
src/modules/notification/notification-sse.service.ts
연결, 일반 알림, 알림 갱신 이벤트와 heartbeat 간격에 중앙 상수를 사용합니다.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested labels: 장민주

Suggested reviewers: wkdalswn11

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed 알림 서비스의 정책, 응답 매핑, 상수 책임을 분리한 주요 변경을 명확하게 설명합니다.
Description check ✅ Passed 변경 내용, 테스트 결과, 체크리스트, 검토 요청 사항을 템플릿에 맞게 구체적으로 작성했습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/notification-service

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@wkdalswn11

Copy link
Copy Markdown
Contributor

Service에 있던 정책 판단과 응답 매핑이 분리돼서 알림 처리 흐름이 훨씬 보기 좋아진 것 같습니다!
기존 Transaction / SSE 경계와 Bulk 알림 동작도 그대로 유지되는 것 확인했습니다.
Repository 중복 로직도 같이 정리되어서 좋네요. 추가로 수정 요청드릴 부분은 없습니다! 고생하셨습니다!!!

@wkdalswn11
wkdalswn11 merged commit b04d79a into dev Aug 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants