Skip to content

[이규빈] Sprint7#235

Open
plzslp wants to merge 38 commits into
codeit-bootcamp-spring:이규빈from
plzslp:sprint7
Open

[이규빈] Sprint7#235
plzslp wants to merge 38 commits into
codeit-bootcamp-spring:이규빈from
plzslp:sprint7

Conversation

@plzslp
Copy link
Copy Markdown
Collaborator

@plzslp plzslp commented Mar 30, 2026

프로젝트 마일스톤

  • 로그 관리
  • 커스텀 예외 설계
  • 유효성 검사
  • Actuator를 활용한 모니터링
  • 단위 테스트
  • 슬라이스 테스트
  • 통합 테스트

베이스 코드

이전 미션에서 아직 달성하지 못한 요구사항이 있어 이 미션을 수행하기 어렵다면 베이스 코드를 참고해보세요.


주요 변경 사항

H2 데이터베이스와 호환을 고려해 schema.sql이 변경되었습니다. timestamptz → timestamp with time zone


CREATE TABLE ...
...
    created_at timestamptz NOT NULL,
    updated_at timestamptz,
    created_at timestamp with time zone NOT NULL,
    updated_at timestamp with time zone,
...

프론트엔드가 변경되었습니다. v1.2.3

  • 예외 메시지 표시 모달 UI가 개선되었습니다.

  • 한글 메시지 입력 시 중복 전송되는 버그가 픽스되었습니다.

  • 정적리소스: 베이스 코드에 적용되어 있습니다.

    프론트엔드 소스 코드는 참고용으로만 활용하세요. 수정하여 활용하는 경우 이어지는 요구사항 또는 미션을 수행하는 데 어려움이 있을 수 있습니다.

기본 요구사항

프로파일 기반 설정 관리

  • 개발, 운영 환경에 대한 프로파일을 구성하세요.
    • [ ] application-dev.ymlapplication-prod.yml 파일을 생성하세요.
    • 다음과 같은 설정값을 프로파일별로 분리하세요.
      • 데이터베이스 연결 정보
      • 서버 포트

로그 관리

  • Lombok의 @Slf4j 어노테이션을 활용해 로깅을 쉽게 추가할 수 있도록 구성하세요.
  • [ ] application.yaml에 기본 로깅 레벨을 설정하세요.
    • 기본적으로 info 레벨로 설정합니다.
  • 환경 별 적절한 로깅 레벨을 프로파일 별로 설정해보세요.
    • SQL 로그를 보기위해 설정했던 레벨은 유지합니다.
    • 우리가 작성한 프로젝트의 로그는 개발 환경에서 debug, 운영 환경에서는 info 레벨로 설정합니다.
  • Spring Boot의 기본 로깅 구현체인 Logback의 설정 파일을 구성하세요.
    • logback-spring.xml 파일을 생성하세요.

    • 다음 예시와 같은 로그 메시지를 출력하기 위한 로깅 패턴과 출력 방식을 커스터마이징하세요.

      • 로그 출력 예시

        # 패턴
        {년}-{월}-{일} {시}:{분}:{초}:{밀리초} [{스레드명}] {로그 레벨(5글자로 맞춤)} {로거 이름(최대 36글자)} - {로그 메시지}{줄바꿈}
        
        # 예시
        25-01-01 10:33:55.740 [main] DEBUG c.s.m.discodeit.DiscodeitApplication - Running with Spring Boot v3.4.0, Spring v6.2.0
    • 콘솔과 파일에 동시에 로그를 기록하도록 설정하세요.

      • 파일은 {프로젝트 루트}/.logs 경로에 저장되도록 설정하세요.
    • 로그 파일은 일자별로 롤링되도록 구성하세요.

    • 로그 파일은 30일간 보관하도록 구성하세요.

  • 서비스 레이어와 컨트롤러 레이어의 주요 메소드에 로깅을 추가하세요.
    • 로깅 레벨을 적절히 사용하세요: ERROR, WARN, INFO, DEBUG
    • 다음과 같은 메소드에 로깅을 추가하세요:
      • 사용자 생성/수정/삭제
      • 채널 생성/수정/삭제
      • 메시지 생성/수정/삭제
      • 파일 업로드/다운로드

예외 처리 고도화

  • 커스텀 예외를 설계하고 구현하세요.

    • 패키지명: com.sprint.mission.discodeit.exception[.{도메인}]

    • ErrorCode Enum 클래스를 통해 예외 코드명과 메시지를 정의하세요.

      • 아래는 예시입니다. 필요하다고 판단되는 다양한 코드를 정의하세요.

      • 예시

        6ag3lzl9i-image
    • 모든 예외의 기본이 되는 DiscodeitException 클래스를 정의하세요.

      • 클래스 다이어그램

        j5vtp941a-image
      • details는 예외 발생 상황에 대한 추가정보를 저장하기 위한 속성입니다.

        • 예시
          • 조회 시도한 사용자의 ID 정보
          • 업데이트 시도한 PRIVATE 채널의 ID 정보
    • DiscodeitException을 상속하는 주요 도메인 별 메인 예외 클래스를 정의하세요.

      • UserExceptionChannelException 등
      • 실제로 활용되는 클래스라기보다는 예외 클래스의 계층 구조를 명확하게 하기 위한 클래스 입니다.
    • 도메인 메인 예외 클래스를 상속하는 구체적인 예외 클래스를 정의하세요.

      • UserNotFoundException, UserAlreadyExistException 등 필요한 예외를 정의하세요.

      • 예시

        a6f585icy-image
  • 기존에 구현했던 예외를 커스텀 예외로 대체하세요.

    • NoSuchElementException
    • IllegalArgumentException
  • ErrorResponse를 통해 일관된 예외 응답을 정의하세요.

    • 클래스 다이어그램

      3gqeampkw-image
    • int status: HTTP 상태코드

    • String exceptionType: 발생한 예외의 클래스 이름

  • 앞서 정의한 ErrorResponse@RestControllerAdvice를 활용해 예외를 처리하는 예외 핸들러를 구현하세요.

    • 모든 핸들러는 일관된 응답(ErrorResponse)을 가져야 합니다.

유효성 검사

  • Spring Validation 의존성을 추가하세요.
  • 주요 Request DTO에 제약 조건 관련 어노테이션을 추구하세요.
    • @NotNull@NotBlank@Size@Email 등
  • 컨트롤러에 @Valid 를 사용해 요청 데이터를 검증하세요.
  • 검증 실패 시 발생하는 MethodArgumentNotValidException을 전역 예외 핸들러에서 처리하세요.
  • 유효성 검증 실패 시 상세한 오류 메시지를 포함한 응답을 반환하세요.

Actuator

  • Spring Boot Actuator 의존성을 추가하세요.
  • 기본 Actuator 엔트포인트를 설정하세요.
    • health, info, metrics, loggers
  • Actuator info를 위한 애플리케이션 정보를 추가하세요.
    • 애플리케이션 이름: Discodeit
    • 애플리케이션 버전: 1.7.0
    • 자바 버전: 17
    • 스프링 부트 버전: 3.4.0
    • 주요 설정 정보
      • 데이터소스: url, 드라이버 클래스 이름
      • jpa: ddl-auto
      • storage 설정: type, path
      • multipart 설정: max-file-size, max-request-size
  • Spring Boot 서버를 실행 후 각종 정보를 확인해보세요.
    • /actuator/info
    • /actuator/metrics
    • /actuator/health
    • /actuator/loggers

단위 테스트

  • 서비스 레이어의 주요 메소드에 대한 단위 테스트를 작성하세요.
    • 다음 서비스의 핵심 메소드에 대해 각각 최소 2개 이상(성공, 실패)의 테스트 케이스를 작성하세요.
      • UserService: create, update, delete 메소드
      • ChannelService: create(PUBLIC, PRIVATE), update, delete, findByUserId 메소드
      • MessageService: create, update, delete, findByChannelId 메소드
    •  Mockito를 활용해 Repository 의존성을 모의(mock)하세요.
    •  BDDMockito를 활용해 테스트 가독성을 높이세요.

슬라이스 테스트

  • 레포지토리 레이어의 슬라이스 테스트를 작성하세요.
    •  @DataJpaTest를 활용해 테스트를 구현하세요.
    • 테스트 환경을 구성하는 프로파일을 구성하세요.
      •  application-test.yml을 생성하세요.
      • 데이터소스는 H2 인메모리 데이터 베이스를 사용하고, PostgreSQL 호환 모드로 설정하세요.
      • H2 데이터베이스를 위해 필요한 의존성을 추가하세요.
      • 테스트 시작 시 스키마를 새로 생성하도록 설정하세요.
      • 디버깅에 용이하도록 로그 레벨을 적절히 설정하세요.
    • 테스트 실행 간 test 프로파일을 활성화 하세요.
    • JPA Audit 기능을 활성화 하기 위해 테스트 클래스에 @EnableJpaAuditing을 추가하세요.
    • 주요 레포지토리(User, Channel, Message)의 주요 쿼리 메소드에 대해 각각 최소 2개 이상(성공, 실패)의 테스트 케이스를 작성하세요.
      • 커스텀 쿼리 메소드
      • 페이징 및 정렬 메소드
  • 컨트롤러 레이어의 슬라이스 테스트를 작성하세요.
    • @WebMvcTest를 활용해 테스트를 구현하세요.

    • WebMvcTest에서 자동으로 등록되지 않는 유형의 Bean이 필요하다면 @Import를 활용해 추가하세요.

      • 예시

        @Import({ErrorCodeStatusMapper.class})
    • 주요 컨트롤러(User, Channel, Message)에 대해 최소 2개 이상(성공, 실패)의 테스트 케이스를 작성하세요.

    • MockMvc를 활용해 컨트롤러를 테스트하세요.

    • 서비스 레이어를 모의(mock)하여 컨트롤러 로직만 테스트하세요.

    • JSON 응답을 검증하는 테스트를 포함하세요.

통합 테스트

  • 통합 테스트 환경을 구성하세요.
    •  @SpringBootTest를 활용해 Spring 애플리케이션 컨텍스트를 로드하세요.
    • H2 인메모리 데이터베이스를 활용하세요.
    • 테스트용 프로파일을 구성하세요.
  • 주요 API 엔드포인트에 대한 통합 테스트를 작성하세요.
    • 주요 API에 대해 최소 2개 이상의 테스트 케이스를 작성하세요.
      • 사용자 관련 API (생성, 수정, 삭제, 목록 조회)
      • 채널 관련 API (생성, 수정, 삭제)
      • 메시지 관련 API (생성, 수정, 삭제, 목록 조회)
    • 각 테스트는 @Transactional을 활용해 독립적으로 실행하세요.

심화 요구사항

MDC를 활용한 로깅 고도화

  • 요청 ID, 요청 URL, 요청 방식 등의 정보를 MDC에 추가하는 인터셉터를 구현하세요.
    • 클래스명: MDCLoggingInterceptor
    • 패키지명: com.**.discodeit.config
    • 요청 ID는 랜덤한 문자열로 생성합니다. (UUID)
    • 요청 ID는 응답 헤더에 포함시켜 더 많은 분석이 가능하도록 합니다.
      • 헤더 이름: Discodeit-Request-ID
  •  WebMvcConfigurer를 통해 MDCLoggingInterceptor를 등록하세요.
    • 클래스명: WebMvcConfig
    • 패키지명: com.**.discodeit.config
  • Logback 패턴에 MDC 값을 포함시키세요.
    • 로그 출력 예시

      # 패턴
      {년}-{월}-{일} {시}:{분}:{초}:{밀리초} [{스레드명}] {로그 레벨(5글자로 맞춤)} {로거 이름(최대 36글자)} [{MDC:요청ID} | {MDC:요청 메소드} | {MDC:요청 URL}] - {로그 메시지}{줄바꿈}
      
      # 예시
      25-01-01 10:33:55.740 [main] DEBUG o.s.api.AbstractOpenApiResource [827cbc0b | GET | /v3/api-docs] - Init duration for springdoc-openapi is: 216 ms

Spring Boot Admin을 활용한 메트릭 가시화

  • Spring Boot Admin 서버를 구현할 모듈을 생성하세요.

    • IntelliJ 화면 참고

      9l6b0q2dn-image.png

    • 모듈 정보는 다음과 같습니다.

      b8812d4su-image.png

    • 의존성

      zjh3frl0m-image.png

  • admin 모듈의 메인 클래스에 @EnableAdminServer 어노테이션을 추가하고, 서버는 9090번 포트로 설정합니다.

    import de.codecentric.boot.admin.server.config.EnableAdminServer;
    
    @SpringBootApplication
    @EnableAdminServer
    public class AdminApplication {
        public static void main(String[] args) {
            SpringApplication.run(AdminApplication.class, args);
        }
    }
    # application.yaml
    spring:
        application:
            name: admin
    server:
        port: 9090
  • admin 서버 실행 후 localhost:9090/applications 에 접속해봅니다.

  • discodeit 프로젝트에 Spring Boot Admin Client를 적용합니다.

    • 의존성을 추가합니다.

      dependencies {
          ...
          implementation 'de.codecentric:spring-boot-admin-starter-client:3.4.5
      }
    • admin 서버에 등록될 수 있도록 설정 정보를 추가합니다.

      # application.yml
      spring:
        application:
          name: discodeit
          ...
        boot:
          admin:
            client:
              instance:
                name: discodeit
      ...
      # application-dev.yml
      spring:
        application:
          name: discodeit
          ...
        boot:
          admin:
            client:
              url: http://localhost:9090
      ...
      # application-prod.yml
      spring:
        application:
          name: discodeit
          ...
        boot:
          admin:
            client:
              url: ${SPRING_BOOT_ADMIN_CLIENT_URL}
      ...
    • discodeit 서버를 실행하고, admin 대시보드에 discodeit 인스턴스가 추가되었는지 확인합니다.

  • admin 대시보드 화면을 조작해보면서 각종 메트릭 정보를 확인해보세요.

    • 주요 API의 요청 횟수, 응답시간 등
    • 서비스 정보

테스트 커버리지 관리

  • JaCoCo 플러그인을 추가하세요.

    
    plugins {
        id 'jacoco'
    }
    
    test {
        finalizedBy jacocoTestReport
    }
    
    jacocoTestReport {
        dependsOn test
        reports {
            xml.required = true
            html.required = true
        }
    }
    
  • 테스트 실행 후 생성된 리포트를 분석해보세요.

    • 리포트는 build/reports/jacoco 경로에서 확인할 수 있습니다.
  • com.sprint.mission.discodeit.service.basic 패키지에 대해서 60% 이상의 코드 커버리지를 달성하세요.

변경사항

  • JpaAuditingConfig 클래스를 만들어서 테스트 환경시에 JPA 의존성 충돌을 방지하도록 하였습니다.
  • MultipartFile과 JSON 데이터(RequestPart)가 혼합된 유저/메시지 생성 테스트에 MockMultipartFile을 사용했습니다.

멘토에게

  • Mar 25, 2026 커밋부터 미션 7 내용입니다.
  • 테스트 코드를 작성할 때 고려해야하는 내용이 정말 많아서 어려웠던 것 같습니다.

-모든 FileRepository 구현체에 ReentrantLock 적용

- SpringDoc OpenAPI 의존성 추가
- UserStatus 업데이트 로직 및 PATCH 메서드 수정

- UserController 엔드포인트 변경 및 RequestBody 적용

- ChannelDto.Response 필드명 수정 (lastMessageAt)
- ChannelController 및 Service의 채널 생성 로직 수정

- UserController의 생성, 수정 메서드 RequestPart 어노테이션 적용

- 제공된 정적 리소스 파일 추가
- MessageController, ReadStatusController, BinaryContentController 로직 수정

- 연관된 Service 및 Entity 비즈니스 로직 수정
- Update 요청 DTO 필드명 변경

- RequestParam 어노테이션에 키(value) 지정

- 프론트엔드 연동 및 API 일관성 확보를 위한 리팩토링
- ErrorResponse 및 BusinessLogicException 기반의 공통 예외 응답 규격 적용

- 서비스 계층 내 하드코딩된 예외를 커스텀 예외로 교체

- BasicChannelService 참여자 목록 동기화 누락 해결

- 미사용 Import 제거 및 코드 최적화 진행
- BasicChannelService 채널 전체 조회 시 lastMessageAt 필드 누락 문제 해결

- 공개 및 비공개 채널의 최근 메시지 시간(Instant) 매핑 로직 적용

- 프라이빗 메서드 추출(getParticipantIds, getLastMessageAt)을 통한 코드 가독성 개선
- 내부 클래스로 관리되던 DTO를 도메인별 개별 클래스로 추출

- 어노테이션을 통한 요청 데이터 유효성 검증 로직 적용

- DTO 클래스 분리에 따른 연관 Service 및 Controller 참조 로직 수정
- build.gradle: Spring Data JPA 및 MapStruct 의존성 추가

- application.yaml: 데이터베이스 연결 및 JPA/SQL 디버깅 로그 설정 추가

- schema.sql: 초기 테이블 생성을 위한 데이터베이스 스키마 정의 파일 추가
- BaseEntity 추상 클래스 생성 및 JPA Auditing 적용

- 엔티티 간 ID 참조 방식을 객체 참조 기반의 연관관계 매핑으로 개편

- ManyToOne, OneToMany 설정 및 영속성 전이(cascade), 고아 객체 제거 적용

- JpaRepository 기반 레포지토리 재작성 및 기존(JCF, File) 구현체 삭제
- BinaryContentStorage 인터페이스 설계 및 로컬 구현체 추가

- ConditionalOnProperty 기반 스토리지 자동 빈 등록 설정

- 파일 다운로드 API 구현 및 ResponseEntity 반환 타입 적용
- Pageable을 활용한 오프셋 방식 페이지네이션 로직 적용

- 공통 응답 규격 PageResponse DTO 도입 및 데이터 직렬화 최적화
- N+1 문제 해결 및 OSIV 비활성화에 따른 쿼리 효율성 개선

- Transactional 경계 설정 및 변경 감지(Dirty Checking) 기반 로직 리팩토링
- ReadStatus 엔티티 업데이트 로직 수정 및 요청 DTO 필드 추가

- Channel 엔티티 (name, description) Nullable 허용

- 파일 다운로드 Content-Disposition 헤더 추가 및 로그인 401 상태 코드 반영

- ErrorResponse 수정
- MessageRepository, BasicMessageService: 페이징 로직 수정
- LocalBinaryContentStorage: 디렉토리 위치 변경
- BaseUpdatableEntity: updateIfChanged 추가
- Channel, User, Message: 변경된 수정 로직 적용
- schema.sql: timestamp with time zone 적용
- frontend 폴더: 정적 리소스와 중복되어 삭제
- application-dev.yml: 개발 환경에 대한 프로필 추가
- application-prod.yml: 운영 환경에 대한 프로필 추가
- logback-spring.xml: 로그 파일 출력 및 롤링 설정
- User, Channel, Message, BinaryContent의 CUD 작업에 INFO 레벨 로깅 적용
- 목록 및 단건 조회(Read) 메서드에 DEBUG 레벨 로깅 일관성 있게 적용
- DiscodeitException 기반의 도메인별 예외 계층 구조(User, Channel 등) 구축
- ErrorResponse 규격에 필드별 상세 오류 정보(details) 및 클래스명(getSimpleName) 반영
- 모든 서비스 레이어에서 예외 발생 시 관련 식별자(ID, Username 등)를 details 맵에 포함하도록 개선
- Valid를 활용한 검증 로직 고도화
- GlobalExceptionHandler:
 - MethodArgumentNotValidException 처리 로직 추가
 - ConstraintViolationException 처리 로직 추가
- 프론트엔드 정적리소스 업데이트
- BinaryContentException을 상속받지 않던 예외들 수정
plzslp added 8 commits March 26, 2026 18:32
- Spring Boot Actuator 의존성 추가 및 엔드포인트 노출 설정
- application.yml에 앱 이름, 버전, 자바/스프링 버전 및 주요 설정 정보(info) 추가
- MessageController에서 첨부파일(attachments)이 null일 때 .size() 호출로 인한 NPE 발생 로직 수정
- UserService: create, update, delete 성공/실패 케이스 작성
- ChannelService: 공개/비공개 채널 생성 및 수정/삭제/조회 로직 검증
- MessageService: 첨부파일 포함 생성, 비공개 채널 권한 체크, 커서 기반 페이징 로직 검증
- 비공개 채널 생성 로직 수정
  - 존재하지 않는 참여자 ID가 포함된 경우 무시하지 않고 즉시 예외를 발생시키도록 변경
- 메시지 목록 조회(findAllByChannelId) 로직 개선
  - 조회 전 채널 존재 여부를 확인하는 방어 로직 추가 및 관련 실패 테스트 케이스 작성
- 애플리케이션 로깅 및 예외 처리 레벨 최적화
  - 컨트롤러와 서비스 계층 간의 중복된 로그 제거
- DTO 유효성 검증(Validation) 강화
  - UserCreateRequest: email 필드에 null 및 공백 방지를 위한 NotBlank 추가
  - PrivateChannelCreateRequest: 참가자 리스트 내부 UUID에 대한 NotNull 검증 추가
- MDCLoggingInterceptor:
  - 요청 진입 시 UUID, HTTP 메소드, URI 정보를 MDC에 저장
  - 응답 헤더에 'Discodeit-Request-ID' 추가
- WebMvcConfig: 애플리케이션의 모든 API 경로에 MDC 인터셉터 동작 등록
- logback-spring.xml: 출력 패턴 변경
- schema.sql: timestamp with time zone이 누락된 부분 수정
- Admin 서버 모듈 추가:
  - admin 모듈 구성 및 settings.gradle 등록
  - EnableAdminServer 적용 및 9090 포트 실행
- Discodeit:
  - build.gradle: spring-boot-admin-starter-client 의존성 추가
- DataJpaTest를 이용한 리포지토리 계층 슬라이스 테스트 구현
- WebMvcTest를 이용한 컨트롤러 슬라이스 테스트 구현
- JPA Auditing 설정을 JpaAuditingConfig로 분리
- - User, Channel, Message 주요 API 통합 테스트 작성
- 기존 테스트 코드 불필요한 import 제거
@plzslp plzslp requested a review from joonfluence March 30, 2026 12:08
@plzslp plzslp self-assigned this Mar 30, 2026
@plzslp plzslp added the 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. label Mar 30, 2026
@plzslp plzslp changed the title [이규빈] sprint7 [이규빈] Sprint7 Mar 30, 2026
Copy link
Copy Markdown
Collaborator

@joonfluence joonfluence left a comment

Choose a reason for hiding this comment

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

PR 리뷰: [이규빈] Sprint7

전체 요약

프로파일 기반 설정 분리, Logback 로깅/MDC 인터셉터, 커스텀 예외 계층 설계, Spring Validation, Actuator, Spring Boot Admin, JaCoCo, 그리고 단위/슬라이스/통합 테스트를 망라한 스프린트7 구현입니다. MDCLoggingInterceptor와 DiscodeitException 계층 설계, 테스트 코드 구조가 전반적으로 우수합니다.


👍 잘한 점

  • DiscodeitException의 생성자 오버로드(errorCode, errorCode+details, errorCode+cause, errorCode+details+cause)가 다양한 상황에 대응할 수 있어 확장성이 좋습니다.
  • MDCLoggingInterceptorafterCompletion에서 MDC.clear()를 호출해 ThreadLocal 메모리 누수를 방지하는 점이 훌륭합니다.
  • 통합 테스트에서 @Transactional을 사용해 각 테스트가 독립적으로 실행되고, DB 상태 검증까지 철저히 하고 있습니다.

[P3] UserRepositoryTest 누락

테스트 디렉터리에 ChannelRepositoryTest, MessageRepositoryTest는 있지만 UserRepositoryTest.java가 없습니다. 요구사항에 주요 레포지토리(User, Channel, Message) 모두에 대한 슬라이스 테스트를 작성하도록 명시되어 있으니 추가해 주세요.

datasource:
url: jdbc:postgresql://localhost:5432/discodeit
username: discodeit_user
password: discodeit1234
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.

[P1] 운영 DB 자격증명 하드코딩 — 반드시 수정 필요

운영 환경 DB의 username과 password가 소스 코드에 직접 노출되어 있습니다. Git 히스토리에 영구적으로 남으므로 나중에 삭제해도 완전히 제거되지 않습니다. 환경 변수로 주입받도록 변경해주세요.

Suggested change
password: discodeit1234
username: ${DB_USERNAME}
password: ${DB_PASSWORD}

}
@ExceptionHandler
public ResponseEntity<ErrorResponse> handleBusinessLogicException(DiscodeitException e) {
log.warn("비즈니스 예외 발생 - code: {}, message: {}, details: {}",
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.

[P2] 비즈니스 예외에 스택 트레이스 과도 출력

SLF4J에서 마지막 인수가 Throwable이면 자동으로 스택 트레이스를 출력합니다. UserNotFoundException, ChannelNotFoundException 등 예상된 비즈니스 예외가 발생할 때마다 스택 트레이스 전체가 출력되어 운영 로그가 오염됩니다. 비즈니스 예외는 코드/메시지 정도만 로깅하는 것이 적절합니다.

Suggested change
log.warn("비즈니스 예외 발생 - code: {}, message: {}, details: {}",
log.warn("비즈니스 예외 발생 - code: {}, message: {}, details: {}",
e.getErrorCode().name(), e.getMessage(), e.getDetails());

</encoder>
</appender>

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
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.

[P3] RollingFileAppender<file> 태그 누락

<file> 태그가 없으면 현재 날짜의 로그가 날짜 패턴 파일로만 기록되고 '현재 로그 파일'이 존재하지 않습니다. tail -f .logs/app.log 처럼 오늘 로그를 실시간 추적하는 운영 작업이 불편해집니다. 아래와 같이 활성 파일을 지정해 주세요.

Suggested change
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/app.log</file>
<encoder>

Comment thread build.gradle
annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
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.

[P3] testImplementation 의존성 중복 선언

org.springframework.boot:spring-boot-starter-test가 바로 위 줄과 동일하게 두 번 선언되어 있습니다. 한 줄을 제거해주세요.

Suggested change
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

Comment thread admin/build.gradle
}

ext {
set('springBootAdminVersion', "3.5.8")
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.

[P3] Spring Boot Admin 서버-클라이언트 버전 불일치

admin 모듈은 Spring Boot Admin 3.5.8을 사용하지만, discodeit 메인 모듈의 build.gradle에서는 클라이언트를 de.codecentric:spring-boot-admin-starter-client:3.4.5로 직접 고정해 버전이 다릅니다. 마이너 버전 차이도 API 비호환을 유발할 수 있으니 동일한 버전으로 맞춰 주세요.

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String requestId = UUID.randomUUID().toString().substring(0, 8);
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.

[P4] request_id로 UUID 앞 8자리만 사용

substring(0, 8)로 앞 8자리(약 43억 가지)만 사용합니다. 트래픽이 높은 운영 환경에서 충돌 가능성이 있습니다. 전체 UUID를 사용하면 충돌 확률이 사실상 0에 수렴합니다.

Suggested change
String requestId = UUID.randomUUID().toString().substring(0, 8);
String requestId = UUID.randomUUID().toString();


@Getter
public enum ErrorCode {
USER_NOT_FOUND(404, "유저를 찾을 수 없습니다."),
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.

[P4] HTTP 상태 코드를 int 리터럴로 직접 관리

404, 409, 401 등 숫자를 직접 사용하면 실수가 발생하기 쉽습니다. HttpStatus enum을 사용하면 타입 안전성과 가독성이 높아집니다. 현재 동작에는 문제없으나 참고해보세요.

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

Labels

매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants