Modu Square

Kafka 기반 실시간 인기글 집계 구현

문제

매일 0시 스케줄러로 인기 점수를 계산하면 두 가지 문제가 생김

  • 게시글과 댓글, 좋아요, 조회수를 모두 다시 읽어야 해 데이터가 늘수록 DB 조회량과 계산 시간 증가
  • 자정 이후의 반응은 다음 실행까지 반영되지 않아 최대 24시간 지연

해결 전략

  • 각 서비스가 게시글과 댓글, 좋아요, 조회수 변경을 Kafka 이벤트로 발행
  • Hot Article Service가 이벤트를 구독해 게시글별 최신 수치를 Redis에 갱신
  • 좋아요 3점, 댓글 2점, 조회수 1점으로 점수를 매겨 당일 Sorted Set에 Top 10 유지

기술 선택 이유

  • Domain Service의 인기글 API 직접 호출 - 제외

    • Hot Article Service의 장애나 지연이 게시글과 댓글, 좋아요, 조회수의 쓰기 요청까지 전파
  • 스케줄러로 전체 데이터 재계산 - 제외

    • 전체 데이터를 반복 조회해야 하고 집계 주기 사이의 변화를 바로 반영하기 어려움
  • Kafka로 변경 사항 전달 - 채택

    • 발행 측과 집계 측이 분리돼 인기글 쪽 장애나 지연이 쓰기 요청으로 번지지 않음
    • 장애 중 쌓인 이벤트를 복구 뒤 다시 처리 가능
Domain Service변경 이벤트Kafka토픽별 이벤트Consumer GroupHot Article Service게시글별 최신 수치댓글, 좋아요, 조회수인기글 Top 10Sorted Set최신값 저장점수 반영

구현

인기 점수 계산

private static final long LIKE_WEIGHT = 3;
private static final long COMMENT_WEIGHT = 2;
private static final long VIEW_WEIGHT = 1;

public long calculate(Long articleId) {
    return likeCountRepository.read(articleId) * LIKE_WEIGHT
            + commentCountRepository.read(articleId) * COMMENT_WEIGHT
            + viewCountRepository.read(articleId) * VIEW_WEIGHT;
}
  • 서비스 API를 다시 호출하지 않도록 Redis에 게시글별 최신 댓글과 좋아요, 조회수 저장
  • 좋아요 3점, 댓글 2점, 조회수 1점으로 인기 점수 계산

인기글 Top 10 저장

public void add(Long articleId, LocalDateTime time, Long score, Duration ttl) {
    redisTemplate.executePipelined((RedisCallback<?>) action -> {
        StringRedisConnection conn = (StringRedisConnection) action;
        String key = generateKey(time);
        conn.zAdd(key, score, String.valueOf(articleId));
        conn.zRemRange(key, 0, -11);
        conn.expire(key, ttl.toSeconds());
        return null;
    });
}
  • 게시글 생성일별 Sorted Set에서 점수순으로 상위 10건 유지
  • 점수 저장과 하위 순위 제거, TTL 설정을 Redis Pipeline 한 번으로 처리

검증

게시글을 만든 뒤 조회, 좋아요, 댓글을 발생시킨 후 인기글 API 조회

후보입력기대 점수
좋아요 게시글좋아요 35개105점
조회수 게시글조회수 100회100점
댓글 게시글댓글 36개72점

조회수는 100건마다 최신값 이벤트를 발행하는 정책에 맞춰 100회 요청

점수 반영 직후

Modu Square 실제 화면에 좋아요, 조회수, 댓글 게시글이 점수순으로 표시된 모습

계산한 점수와 실제 인기글 순서가 105 → 100 → 72로 일치

좋아요 12개 추가 후

댓글 게시글이 108점으로 갱신되어 1위로 이동한 Modu Square 실제 화면

3위였던 댓글 게시글에 좋아요 12개를 추가하자 72점 → 108점으로 갱신되며 1위로 이동

한계

  • 인기글 집계 대상이 생성일 기준이라 어제 쓴 글이 오늘 반응을 받아도 목록에 오르지 않음
  • 조회수는 100건마다 이벤트를 발행하므로 그 사이의 변화는 다음 이벤트 전까지 점수에 반영되지 않음