💻 뚝딱뚝딱/북북클럽 (bookbookclub)

[개발일지 #044] 알림 서비스 만들기 (Kafka 활용)

뚜루리 2025. 6. 17. 17:42
728x90
320x100

🎯 오늘의 개발 내용 (요약)

  • Kafka 알림 시스템 구현

 


🧩 도입 배경

BookBookClub은 책에 대한 피드와 소셜 기능을 제공하는 SNS로, 피드 작성, 좋아요, 팔로우 이벤트에 대해 사용자에게 실시간 알림을 제공하고자 알림 기능을 구현하게 되었음.

 

🏛️ 아키텍처 구조

[post-service / user-service] --> Kafka Event 발행 --> [notification-service] 소비 및 저장
 
  • post-service: 피드 작성/좋아요 발생 시 Kafka 이벤트 발행
  • user-service: 팔로우 생성 시 Kafka 이벤트 발행
  • notification-service: Kafka Listener로 이벤트 수신 → DB 저장

즉, notification-service로 별도 모듈을 생성하고 Kafka에서 이벤트를 발생함과 동시에 데이터베이스에도 저장하는 구조임.

 

 

🔄 구현 흐름

1. Kafka 이벤트 발행 (예: 좋아요 이벤트 발생 시)

피드 등록 서비스에서 해당 메세드 추가

// post-service
likeEventProducer.sendLikeCreated(feedId, userId);
// 이벤트 클래스 (common module)
public record LikeCreatedEvent(Long senderUserId, Long receiverUserId, Long feedId) {}

 

2. Kafka 리스너 수신 및 알림 저장

@Slf4j
@Service
@RequiredArgsConstructor
public class LikeCreatedEventListener {

    private final NotificationService notificationService;

    @KafkaListener(topics = "like.created", groupId = "notification-group")
    public void handleLikeCreatedEvent(LikeCreatedEvent event) {
        log.info("👍 좋아요 이벤트 수신! feedId={}, senderUserId={}, receiverUserId={}",
                event.feedId(), event.senderUserId(), event.receiverUserId());

        String content = "누군가 회원님의 피드에 좋아요를 눌렀어요!";
        notificationService.saveNotification(event.receiverUserId(), event.feedId(), content, NotificationType.LIKE);
    }
}

 

3. 알림 저장 엔티티

package com.bookbookclub.bbcnotificationservice.notification.entity;

import com.bookbookclub.bbcnotificationservice.notification.enums.NotificationType;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;

@Entity
@Getter
@NoArgsConstructor
@Table(name = "notifications")
public class Notification {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /** 알림을 받는 유저 ID */
    @Column(nullable = false)
    private Long userId;

    /** 피드 ID (알림 대상) */
    private Long feedId;

    /** 알림 내용 */
    @Column(nullable = false)
    private String content;

    /** 읽음 여부 */
    @Column(name = "is_read", nullable = false)
    private boolean isRead = false;

    /** 알림 유형 */
    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private NotificationType type;

    /** 삭제 여부 */
    @Column(name = "is_deleted", nullable = false)
    private boolean isDeleted = false;

    /** 생성일시 */
    private LocalDateTime createdAt;

    public void markAsRead() {
        this.isRead = true;
    }

    public void delete() {
        this.isDeleted = true;
    }

    /**
     * 생성자: 기본적으로 읽지 않은 상태로 생성
     */
    private Notification(Long userId, Long feedId, String content, NotificationType type) {
        this.userId = userId;
        this.feedId = feedId;
        this.content = content;
        this.type = type;
        this.createdAt = LocalDateTime.now();
        this.isRead = false;
    }

    /**
     * 정적 팩토리 메서드
     */
    public static Notification from(Long userId, Long feedId, String content, NotificationType type) {
        return new Notification(userId, feedId, content, type);
    }
}

 

✅ 주요 구현 포인트

항목 설명
MSA 구조 알림 서비스는 독립 모듈로 분리되어 동작
Kafka 이벤트 기반 서비스 간 강결합 없이 이벤트 발행 및 수신
Enum 기반 타입 분리 NotificationType(enum) 도입으로 알림 종류 명확화
JWT 인증 연동 토큰 파싱 후 유저 식별하여 알림 분기 처리
논리 삭제 적용 삭제는 DB 삭제 대신 isDeleted = true 플래그로 처리
728x90
320x100