DEVELOPMENT NOTE / Modu Square
Kafka 기반 묶음 알림 구현
알림 기능 구현
- 댓글·좋아요 이벤트를 받는 Notification Service를 추가하고 게시글 작성자에게 알림 제공
- 작성자 본인의 반응은 알림에서 제외하며 게시글 정보가 없을 때만 Article Service에서 보완 조회
- 웹 상단 알림 버튼에서 최근 알림을 조회하고 클릭하면 해당 게시글로 이동
초기에는 이벤트 한 건을 알림 한 건으로 저장.
댓글·좋아요 이벤트 1건 → 알림 저장 1건 → 사용자 알림 목록 1행
문제 발생
인기 게시글 한 건에 5분 동안 좋아요 100개와 댓글 20개가 들어오는 상황을 재현.
- 이벤트 120건이 알림 120건으로 노출돼 같은 게시글이 알림 목록을 차지
- 저장 건수와 목록 조회 비용도 이벤트 수에 비례해 증가
- Kafka에서 같은 이벤트가 재전달되면 알림 수치가 중복될 가능성
조회수는 발생량이 많고 알림 가치가 낮아 대상에서 제외.

개선 전 — 댓글·좋아요 이벤트마다 같은 게시글 알림이 한 행씩 추가
해결
사용자·게시글·5분 구간을 하나의 키로 묶고 같은 구간의 댓글과 좋아요 수를 누적.
long windowStartMillis = occurredAtMillis / bundleWindowMillis * bundleWindowMillis;
String notificationId = articleId + ":" + windowStartMillis;
redisTemplate.execute(
ADD_TO_BUNDLE,
List.of(eventKey, bundleKey, userIndexKey),
retentionSeconds,
articleId,
title,
occurredAtMillis,
countField,
notificationId
);
- Redis Hash에 게시글 제목과 댓글·좋아요 수 저장
HINCRBY로 반응 수를 누적하고 사용자별 Sorted Set에는 최근 묶음 ID만 저장- 이벤트 120건을
댓글 20개 · 좋아요 100개알림 한 건으로 표시

개선 후 — 5분 동안 발생한 댓글과 좋아요를 게시글별 알림 한 건으로 집계
중복 확인과 수치 증가를 Lua Script 한 번으로 처리.
if redis.call('SET', KEYS[1], '1', 'NX', 'EX', ARGV[1]) == false then
return 0
end
redis.call('HINCRBY', KEYS[2], ARGV[5], 1)
redis.call('ZADD', KEYS[3], ARGV[4], ARGV[6])
return 1
- 처음 들어온 이벤트 ID만 묶음 수치에 반영
- 중복 확인·Hash 증가·Sorted Set 갱신을 Redis에서 원자적으로 실행
테스트
| 검증 항목 | 결과 |
|---|---|
| 5분 내 고유 이벤트 | 좋아요 100건 · 댓글 20건 |
| 동일 이벤트 재전달 | 수치 증가 없음 |
| 생성된 알림 | 1건 |
| 5분 구간 경계 통과 | 새 알림 1건 생성 |
- Article·Like·Comment API → Outbox → Kafka → Notification Service → Redis 전체 흐름에서도 게시글별 알림 한 건 확인
판단과 한계
- 고정 구간 경계 직전과 직후의 반응은 실제 간격이 짧아도 두 알림으로 분리
- 현재 UI는 30초 polling 방식이며 실시간 push와 읽음 상태 관리는 후속 과제