Skip to content

[김지예] sprint11#166

Open
hyo37009 wants to merge 24 commits into
codeit-bootcamp-spring:김지예from
hyo37009:김지예-sprint11

Hidden character warning

The head ref may contain hidden characters: "\uae40\uc9c0\uc608-sprint11"
Open

[김지예] sprint11#166
hyo37009 wants to merge 24 commits into
codeit-bootcamp-spring:김지예from
hyo37009:김지예-sprint11

Conversation

@hyo37009
Copy link
Copy Markdown
Collaborator

@hyo37009 hyo37009 commented Mar 4, 2026

요구사항

기본

  • BinaryContentStorage.put 직접 호출 대신 BinaryContentCreatedEvent를 발행하여 파일 업로드 로직 분리
  • @TransactionalEventListener로 트랜잭션 커밋 후 바이너리 데이터 저장
  • BinaryContentstatus 필드 추가 (PROCESSING → SUCCESS / FAIL)
  • MessageCreatedEvent, RoleUpdatedEvent 정의 및 발행으로 알림 기능 구현
  • ReadStatusnotificationEnabled 필드 추가
  • Notification 엔티티 + 조회/삭제 API 구현
  • AsyncConfig + @EnableAsync + TaskExecutor Bean 등록으로 비동기 전환
  • TaskDecorator로 MDC, SecurityContext 비동기 스레드 전파
  • @Retryable로 재시도 정책 설정 및 @Recover로 최종 실패 시 관리자 알림 생성
  • Caffeine 캐시 적용 (@Cacheable, @CacheEvict, @CachePut)
  • Spring Actuator로 캐시 적중률 확인

심화

  • Spring Kafka 도입 (Spring Event → Kafka 중계)
  • Redis Cache 도입 (Caffeine → Redis 전환)

주요 변경사항

  • Spring Event를 활용하여 파일 업로드 로직을 메인 트랜잭션에서 분리
  • 메시지 생성/권한 변경 시 이벤트 기반 알림 기능 추가
  • @Async로 이벤트 리스너를 비동기 전환하여 응답 속도 개선
  • Spring Retry로 비동기 실패 시 자동 재시도 및 관리자 통지 구현

멘토에게

  • 셀프 코드 리뷰를 통해 질문 이어가겠습니다.
  • 현재 Step 4(비동기 실패 처리)까지 구현 완료
  • Step 5(캐시)와 심화 미션(Kafka, Redis)은 마감 시간 관계로 먼저 제출하고, 이어서 구현 예정

hyo37009 added 24 commits March 3, 2026 22:34
- BinaryContent 생성 이벤트(BinaryContentCreatedEvent) 클래스 추가
- 이벤트 리스너(BinaryContentCreatedEventListener) 구현 및 저장 처리 로직 추가
- BinaryContent 상태 관리를 위한 BinaryContentStatus 열거형 추가
- 파일 저장 실패 시 예외(BinaryContentUploadFailException) 처리 로직 구현
- Message 생성 시 MessageCreatedEvent 이벤트 발행 로직 구현
- ApplicationEventPublisher 의존성 추가
- MessageCreatedEvent 레코드 클래스 생성
- BinaryContent에 BinaryContentStatus 열거형 필드 추가
- 상태 변경 메서드(uploadSucceed, uploadFailed) 및 확인 메서드(isUploaded) 구현
- 생성자 변경 및 BaseUpdatableEntity 상속으로 변경
- 기본 업로드 상태를 PROCESSING으로 설정
- 파일 저장 로직을 이벤트 기반(BinaryContentCreatedEvent)로 변경
- ApplicationEventPublisher 의존성 추가
- BinaryContent 생성 시 파일 업로드 이벤트 발행 및 상태 관리 개선
- BinaryContentCreatedEventListener에서 파일 저장과 상태 업데이트 처리
- BinaryContentStorage 의존성 제거 및 관련 코드 삭제
- notificationEnabled 필드 추가 및 기본값 설정 로직 구현
- 채널 유형에 따라 알림 활성화 여부 초기화
- 알림 활성화된 사용자를 조회하는 쿼리 메서드 추가
- ReadStatusUpdateRequest에 알림 설정 요청 필드 추가
- Notification 엔티티 생성 및 JPA 매핑 정의
- NotificationService 인터페이스 및 기본 구현체 추가
- NotificationDto 및 NotificationCreateRequest 레코드 클래스 생성
- NotificationRequiredEventListener 클래스 추가하여 메시지 생성 이벤트 처리
- 알림 수신 설정된 사용자 조회 및 알림 전송 로직 초기 구현
- 테이블 및 Foreign Key 정의 가독성 개선을 위한 포맷팅 수정
- notifications 테이블 스키마 추가
- ReadStatus 테이블에 notification_enabled 필드 추가 및 관련 인덱스 생성
- BinaryContents 테이블에 status 필드 추가
- users, channels 등 일부 테이블의 컬럼 정의 포맷 개선 및 인덱스 추가
- NotificationService 의존성을 NotificationRepository로 교체
- 메시지 생성 이벤트 시 알림 저장 로직 수정 및 간소화
- Notification 엔티티 생성 및 저장 처리 로직 스트림 활용으로 변경
- Notification 엔티티를 관리하기 위한 JpaRepository 구현
- UUID를 기본 키로 사용하는 Notification 엔티티 지원
- DTO와 엔티티 간 매핑을 간소화하기 위해 MapStruct 추가
- build.gradle 파일에 MapStruct 라이브러리 및 어노테이션 프로세서 설정
- 사용자별 알림 리스트 조회 메서드(getAllByUserId) 추가
- NotificationMapper 인터페이스 생성 및 MapStruct 매핑 설정
- 알림 확인(confirm) 로직 구현 및 권한 체크 추가
- NotificationRepository에 사용자(receiver) 기준 조회 메서드 추가
- RoleUpdatedEvent 레코드 클래스 생성
- NotificationRequiredEventListener에 권한 변경 이벤트 처리 메서드 추가
- BasicUserService에서 권한 변경 시 RoleUpdatedEvent 발행하도록 수정
- NotificationCreateRequest 삭제로 사용되지 않는 DTO 정리
- NotificationService에서 NotificationCreateRequest 사용 코드 제거 및 메서드 시그니처 수정
- 알림 전체 조회 메서드(getAllNotifications) 추가
- 알림 단건 확인(confirm) 메서드 및 사용자 인증 정보 활용 로직 구현
- NotificationService와 연동하여 알림 데이터 처리
- NotificationException 및 NotificationNotFoundException 구현
- 알림 ID와 에러 코드 기반 예외 처리 로직 지원
- @timed를 사용해 message.create.async 메트릭 타이머 구현
- 메시지 생성 요청에 대한 성능 데이터를 수집 및 모니터링 가능하도록 설정
- Spring Security와 MDC(Context Map) 컨텍스트를 유지하는 TaskDecorator 구현
- 다중 스레드 환경에서 실행 컨텍스트를 안전하게 전파하도록 설정
- NotificationRequiredEventListener와 BinaryContentCreatedEventListener 메서드에 @async 어노테이션 추가
- 비동기 처리로 이벤트 리스너 성능 개선
- ThreadPoolTaskExecutor를 활용한 AsyncConfig 구현
- 이벤트 리스너에서 사용될 eventTaskExecutor 빈 등록
- 성능 최적화를 위해 비동기 처리 스레드풀 구성
- index-COLcXNzv.js 및 관련 정적 파일 제거
- 빌드 결과물에서 사용되지 않는 파일 정리
- management.observations.annotations.enabled 설정값 추가
- 메트릭 관리 활성화를 위한 설정 변경 수행
- 네트워크 요청 재시도 로직 구현을 위해 spring-retry 라이브러리 추가
- BinaryContentCreatedEventListener에 Spring Retry를 활용한 재시도 로직(@retryable) 구현
- 재시도 실패 시 BinaryContentCreateFailedEvent를 발행하도록 @recover 메서드 추가
- BinaryContentCreateFailedEvent 처리 로직(NotificationRequiredEventListener)에 관리자 알림 기능 구현
- RetryConfig를 추가해 Spring Retry 설정 활성화
- 이벤트 리스너 패키지명 오타 수정 (lilstener → listener)
- findAllByRole(Roles roles) 메서드 추가로 역할별 사용자 리스트 조회 기능 지원
@hyo37009 hyo37009 requested a review from jinho-yoo-jack March 4, 2026 09:41
@hyo37009 hyo37009 self-assigned this Mar 4, 2026
@hyo37009 hyo37009 added 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. 미완성🛠️ 스프린트 미션 제출일이지만 미완성했습니다. 죄송합니다. 지각제출⏰ 제출일 이후에 늦게 제출한 PR입니다. labels Mar 4, 2026
Copy link
Copy Markdown
Collaborator

@jinho-yoo-jack jinho-yoo-jack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

전반적으로 모두 잘 작성하셨습니다.
다만, 코드 작성 스타일에서 약간의 아쉬움 부분들이 있어서 그러한 부분들을 위주로 리뷰 드렸습니다.

고생하셨습니다.

@Getter
@AllArgsConstructor
public class BinaryContent extends BaseEntity {
public class BinaryContent extends BaseUpdatableEntity {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BaseUpdatableEntity 업데이트 할 수 있는 없는 기준으로 BaseEntity를 분기를 나눠야 했던 이유가 있을까요?!!

ApplicationEventPublisher publisher;

@TransactionalEventListener
@Transactional(propagation = Propagation.REQUIRES_NEW)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

트랜잭션 격리 부분까지 아주 휼륭하게 잘 작성하셨네요!
좋습니다.

String title = channel.getType() == ChannelScope.PUBLIC
? String.format("%s (#%s)", author.getUsername(), channel.getName())
: author.getUsername();
String summary = event.content().length() > 30 ?
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런 제목 길이 한정하고 요약하는 부분은 StringUtils와 같은 클래스로 따로 빼서 구현 및 관리하면 코드의 가독성과 유지보수성이 더 좋아질 수 있을 것 같네요.

public void on(RoleUpdatedEvent event) {
User user = userRepository.findById(event.userId())
.orElseThrow(() -> new UserNotFoundException(event.userId()));
String title = "권한이 변경되었습니다.";
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"권한이 변경되었습니다."와 같은 변하지 않는 문자열은 public static final로 표현하는 것도 하나의 방법

messageCreateRequest.channelId(),
attachmentFiles == null ? 0 : attachmentFiles.size());
files == null ? 0 : files.size());
Message message = messageRepository.save(new Message(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메서드 파라미터로 할당할 인자 값들을 메서드 호출부를 선언함으로써 변수명을 선언하지 않는 이점(?)을 얻고자 이렇게 작성한 것 같은데, 파라미터가 많은 경우에는 가독성이 떨어져 좋지 않을 수 있습니다.

지금 처럼 파라미터가 많은 경우에는 적절하게 변수에 값을 담아서 인자로 전달하는 것이 더 좋습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. 미완성🛠️ 스프린트 미션 제출일이지만 미완성했습니다. 죄송합니다. 지각제출⏰ 제출일 이후에 늦게 제출한 PR입니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants