Skip to content
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package com.back.domain.notification.controller;

import com.back.domain.notification.dto.NotificationCreateRequest;
import com.back.domain.notification.dto.NotificationResponse;
import com.back.domain.notification.dto.NotificationListResponse;
import com.back.domain.notification.entity.Notification;
import com.back.domain.notification.entity.NotificationSettingType;
import com.back.domain.notification.service.NotificationService;
import com.back.domain.studyroom.entity.Room;
import com.back.domain.studyroom.repository.RoomRepository;
import com.back.domain.user.entity.User;
import com.back.domain.user.repository.UserRepository;
Expand Down Expand Up @@ -37,116 +34,7 @@ public class NotificationController {
private final UserRepository userRepository;
private final RoomRepository roomRepository;

@Operation(
summary = "알림 전송",
description = "USER/ROOM/COMMUNITY/SYSTEM 타입별 알림 생성 및 전송\n\n" +
"- USER: 개인 알림 (actorId, targetId 필수)\n" +
"- ROOM: 스터디룸 알림 (actorId, targetId(roomId) 필수)\n" +
"- COMMUNITY: 커뮤니티 알림 (actorId, targetId 필수)\n" +
"- SYSTEM: 시스템 전체 알림 (actorId, targetId 불필요)"
)
@PostMapping
public ResponseEntity<RsData<NotificationResponse>> createNotification(
@Parameter(hidden = true) @AuthenticationPrincipal CustomUserDetails userDetails,
@RequestBody NotificationCreateRequest request) {

log.info("알림 전송 요청 - 타입: {}, 제목: {}", request.targetType(), request.title());

// targetType 검증
if (!isValidTargetType(request.targetType())) {
throw new CustomException(ErrorCode.NOTIFICATION_INVALID_TARGET_TYPE);
}

// SYSTEM이 아닌 경우 필수 필드 검증
if (!"SYSTEM".equals(request.targetType())) {
if (request.actorId() == null) {
throw new CustomException(ErrorCode.NOTIFICATION_MISSING_ACTOR);
}
if (request.targetId() == null) {
throw new CustomException(ErrorCode.NOTIFICATION_MISSING_TARGET);
}
}

Notification notification = switch (request.targetType()) {
case "USER" -> {
// 수신자 조회
User receiver = userRepository.findById(request.targetId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

// 발신자 조회
User actor = userRepository.findById(request.actorId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

// 개인 알림 생성
yield notificationService.createPersonalNotification(
receiver,
actor,
request.title(),
request.message(),
request.redirectUrl(),
NotificationSettingType.SYSTEM
);
}
case "ROOM" -> {
// 스터디룸 조회
Room room = roomRepository.findById(request.targetId())
.orElseThrow(() -> new CustomException(ErrorCode.ROOM_NOT_FOUND));

// 발신자 조회
User actor = userRepository.findById(request.actorId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

// 스터디룸 알림 생성
yield notificationService.createRoomNotification(
room,
actor,
request.title(),
request.message(),
request.redirectUrl(),
NotificationSettingType.ROOM_NOTICE
);
}
case "COMMUNITY" -> {
// 수신자 조회 (리뷰/게시글 작성자)
User receiver = userRepository.findById(request.targetId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

// 발신자 조회 (댓글/좋아요 작성자)
User actor = userRepository.findById(request.actorId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

// 커뮤니티 알림 생성
yield notificationService.createCommunityNotification(
receiver,
actor,
request.title(),
request.message(),
request.redirectUrl(),
NotificationSettingType.POST_COMMENT
);
}
case "SYSTEM" -> {
// 시스템 알림은 발신자/수신자 없음
yield notificationService.createSystemNotification(
request.title(),
request.message(),
request.redirectUrl()
);
}
default -> throw new CustomException(ErrorCode.NOTIFICATION_INVALID_TARGET_TYPE);
};

NotificationResponse response = NotificationResponse.from(notification);

return ResponseEntity.ok(RsData.success("알림 전송 성공", response));
}

@Operation(
summary = "알림 목록 조회",
description = "사용자의 알림 목록 조회 (페이징)\n\n" +
"- unreadOnly=true: 읽지 않은 알림만\n" +
"- unreadOnly=false: 모든 알림"
)
@Operation(summary = "알림 목록 조회")
@GetMapping
public ResponseEntity<RsData<NotificationListResponse>> getNotifications(
@Parameter(hidden = true) @AuthenticationPrincipal CustomUserDetails userDetails,
Expand All @@ -157,13 +45,10 @@ public ResponseEntity<RsData<NotificationListResponse>> getNotifications(
log.info("알림 목록 조회 - 유저 ID: {}, 읽지 않은 것만: {}", userDetails.getUserId(), unreadOnly);

Pageable pageable = PageRequest.of(page, size);
Page<Notification> notifications;

if (unreadOnly) {
notifications = notificationService.getUnreadNotifications(userDetails.getUserId(), pageable);
} else {
notifications = notificationService.getUserNotifications(userDetails.getUserId(), pageable);
}
Page<Notification> notifications = notificationService.getNotifications(
userDetails.getUserId(), pageable, unreadOnly
);

long unreadCount = notificationService.getUnreadCount(userDetails.getUserId());

Expand Down Expand Up @@ -221,14 +106,4 @@ public ResponseEntity<RsData<Void>> markAllAsRead(

return ResponseEntity.ok(RsData.success("전체 알림 읽음 처리 성공"));
}

// ==================== 헬퍼 메서드 ====================

private boolean isValidTargetType(String targetType) {
return targetType != null &&
(targetType.equals("USER") ||
targetType.equals("ROOM") ||
targetType.equals("COMMUNITY") ||
targetType.equals("SYSTEM"));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.springframework.data.domain.Page;

import java.util.List;
import java.util.Set;

/**
* 알림 목록 응답 DTO
Expand All @@ -27,9 +28,13 @@ public static NotificationListResponse from(
long unreadCount,
NotificationService notificationService) {

// 현재 페이지의 알림들에 대해 읽음 처리된 ID 목록을 한 번에 조회
Set<Long> readNotificationIds = notificationService.getReadNotificationIds(userId, notifications.getContent());

List<NotificationItemDto> items = notifications.getContent().stream()
.map(notification -> {
boolean isRead = notificationService.isNotificationRead(notification.getId(), userId);
// DB 조회가 아닌 메모리에서 읽음 여부를 빠르게 확인
boolean isRead = readNotificationIds.contains(notification.getId());
return NotificationItemDto.from(notification, isRead);
})
.toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@

import com.back.domain.notification.entity.NotificationSettingType;
import com.back.domain.notification.service.NotificationService;
import com.back.domain.user.entity.User;
import com.back.domain.user.repository.UserRepository;
import com.back.global.exception.CustomException;
import com.back.global.exception.ErrorCode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
Expand All @@ -18,7 +14,6 @@
public class CommunityNotificationEventListener {

private final NotificationService notificationService;
private final UserRepository userRepository;

// 댓글 작성 시 - 게시글 작성자에게 알림
@EventListener
Expand All @@ -28,15 +23,9 @@ public void handleCommentCreated(CommentCreatedEvent event) {
event.getPostId(), event.getCommentId(), event.getActorId());

try {
User actor = userRepository.findById(event.getActorId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

User receiver = userRepository.findById(event.getReceiverId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

notificationService.createCommunityNotification(
receiver,
actor,
event.getReceiverId(),
event.getActorId(),
event.getTitle(),
event.getContent(),
"/posts/" + event.getPostId(),
Expand All @@ -58,15 +47,9 @@ public void handleReplyCreated(ReplyCreatedEvent event) {
event.getParentCommentId(), event.getReplyId(), event.getActorId());

try {
User actor = userRepository.findById(event.getActorId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

User receiver = userRepository.findById(event.getReceiverId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

notificationService.createCommunityNotification(
receiver,
actor,
event.getReceiverId(),
event.getActorId(),
event.getTitle(),
event.getContent(),
"/posts/" + event.getPostId() + "#comment-" + event.getParentCommentId(),
Expand All @@ -88,15 +71,9 @@ public void handlePostLiked(PostLikedEvent event) {
event.getPostId(), event.getActorId());

try {
User actor = userRepository.findById(event.getActorId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

User receiver = userRepository.findById(event.getReceiverId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

notificationService.createCommunityNotification(
receiver,
actor,
event.getReceiverId(),
event.getActorId(),
event.getTitle(),
event.getContent(),
"/posts/" + event.getPostId(),
Expand All @@ -118,15 +95,9 @@ public void handleCommentLiked(CommentLikedEvent event) {
event.getCommentId(), event.getActorId());

try {
User actor = userRepository.findById(event.getActorId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

User receiver = userRepository.findById(event.getReceiverId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

notificationService.createCommunityNotification(
receiver,
actor,
event.getReceiverId(),
event.getActorId(),
event.getTitle(),
event.getContent(),
"/posts/" + event.getPostId() + "#comment-" + event.getCommentId(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
public class StudyNotificationEventListener {

private final NotificationService notificationService;
private final UserRepository userRepository;

// 학습 기록 등록 시 - 본인에게 알림
@EventListener
Expand All @@ -28,11 +27,8 @@ public void handleStudyRecordCreated(StudyRecordCreatedEvent event) {
event.getUserId(), event.getDuration());

try {
User user = userRepository.findById(event.getUserId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

notificationService.createSelfNotification(
user,
event.getUserId(),
event.getTitle(),
event.getContent(),
"/study/records/" + event.getStudyRecordId(),
Expand All @@ -55,11 +51,8 @@ public void handleDailyGoalAchieved(DailyGoalAchievedEvent event) {
event.getCompletedPlans(), event.getTotalPlans());

try {
User user = userRepository.findById(event.getUserId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

notificationService.createSelfNotification(
user,
event.getUserId(),
event.getTitle(),
event.getContent(),
"/study/plans?date=" + event.getAchievedDate(),
Expand Down
Loading