Skip to content

이용일 sprint7 늦은제출#165

Open
lyi980403-arch wants to merge 43 commits into
codeit-bootcamp-spring:이용일from
lyi980403-arch:이용일-sprint7

Hidden character warning

The head ref may contain hidden characters: "\uc774\uc6a9\uc77c-sprint7"
Open

이용일 sprint7 늦은제출#165
lyi980403-arch wants to merge 43 commits into
codeit-bootcamp-spring:이용일from
lyi980403-arch:이용일-sprint7

Conversation

@lyi980403-arch
Copy link
Copy Markdown
Collaborator

요구사항

기본 요구사항
프로파일 기반 설정 관리
[x] 개발, 운영 환경에 대한 프로파일을 구성하세요.
[x] application-dev.yaml, application-prod.yaml 파일을 생성하세요.
[x] 다음과 같은 설정값을 프로파일별로 분리하세요.
[x] 데이터베이스 연결 정보
[x] 서버 포트
로그 관리
[x] Lombok의 @slf4j 어노테이션을 활용해 로깅을 쉽게 추가할 수 있도록 구성하세요.
[x] application.yaml에 기본 로깅 레벨을 설정하세요.
기본적으로 info 레벨로 설정합니다.
[x] 환경 별 적절한 로깅 레벨을 프로파일 별로 설정해보세요.
SQL 로그를 보기위해 설정했던 레벨은 유지합니다.
우리가 작성한 프로젝트의 로그는 개발 환경에서 debug, 운영 환경에서는 info 레벨로 설정합니다.
[x] Spring Boot의 기본 로깅 구현체인 Logback의 설정 파일을 구성하세요.
[x] logback-spring.xml 파일을 생성하세요.

[x] 다음 예시와 같은 로그 메시지를 출력하기 위한 로깅 패턴과 출력 방식을 커스터마이징하세요.
[x] 콘솔과 파일에 동시에 로그를 기록하도록 설정하세요.

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

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

[x] 서비스 레이어와 컨트롤러 레이어의 주요 메소드에 로깅을 추가하세요.
[x] 로깅 레벨을 적절히 사용하세요: ERROR, WARN, INFO, DEBUG
[x] 다음과 같은 메소드에 로깅을 추가하세요:
[x] 사용자 생성/수정/삭제
[x] 채널 생성/수정/삭제
[x] 메시지 생성/수정/삭제
[x] 파일 업로드/다운로드
예외 처리 고도화
[x] 커스텀 예외를 설계하고 구현하세요.
[x] ErrorCode Enum 클래스를 통해 예외 코드명과 메시지를 정의하세요.
[x] 모든 예외의 기본이 되는 DiscodeitException 클래스를 정의하세요.
클래스 다이어그램
[x] DiscodeitException을 상속하는 주요 도메인 별 메인 예외 클래스를 정의하세요.
[x] 도메인 메인 예외 클래스를 상속하는 구체적인 예외 클래스를 정의하세요.
UserNotFoundException, UserAlreadyExistException 등 필요한 예외를 정의하세요.
[x] 기존에 구현했던 예외를 커스텀 예외로 대체하세요.
NoSuchElementException
IllegalArgumentException

[x] ErrorResponse를 통해 일관된 예외 응답을 정의하세요.

클래스 다이어그램

3gqeampkw-image.png

int status: HTTP 상태코드

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

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

모든 핸들러는 일관된 응답(ErrorResponse)을 가져야 합니다.
유효성 검사
[x] Spring Validation 의존성을 추가하세요.
[x] 주요 Request DTO에 제약 조건 관련 어노테이션을 추구하세요.
@NotNull, @notblank, @SiZe, @Email 등
[x] 컨트롤러에 @Valid 를 사용해 요청 데이터를 검증하세요.
[x] 검증 실패 시 발생하는 MethodArgumentNotValidException을 전역 예외 핸들러에서 처리하세요.
[x] 유효성 검증 실패 시 상세한 오류 메시지를 포함한 응답을 반환하세요.
Actuator
[x] Spring Boot Actuator 의존성을 추가하세요.
[x] 기본 Actuator 엔트포인트를 설정하세요.
health, info, metrics, loggers
[x] Actuator info를 위한 애플리케이션 정보를 추가하세요.
애플리케이션 이름: Discodeit
애플리케이션 버전: 1.7.0
자바 버전: 17
스프링 부트 버전: 3.4.0
주요 설정 정보
데이터소스: url, 드라이버 클래스 이름
jpa: ddl-auto
storage 설정: type, path
multipart 설정: max-file-size, max-request-size
[x] Spring Boot 서버를 실행 후 각종 정보를 확인해보세요.
/actuator/info
/actuator/metrics
/actuator/health
/actuator/loggers
단위 테스트
[x] 서비스 레이어의 주요 메소드에 대한 단위 테스트를 작성하세요.
[x] 다음 서비스의 핵심 메소드에 대해 각각 최소 2개 이상(성공, 실패)의 테스트 케이스를 작성하세요.
[x] UserService: create, update, delete 메소드
[x] ChannelService: create(PUBLIC, PRIVATE), update, delete, findByUserId 메소드
[x] MessageService: create, update, delete, findByChannelId 메소드
[x] Mockito를 활용해 Repository 의존성을 모의(mock)하세요.
[x] BDDMockito를 활용해 테스트 가독성을 높이세요.
슬라이스 테스트
[x] 레포지토리 레이어의 슬라이스 테스트를 작성하세요.
[x] @DataJpaTest를 활용해 테스트를 구현하세요.
[x] 테스트 환경을 구성하는 프로파일을 구성하세요.
[x] application-test.yaml을 생성하세요.
[x] 데이터소스는 H2 인메모리 데이터 베이스를 사용하고, PostgreSQL 호환 모드로 설정하세요.
[x] H2 데이터베이스를 위해 필요한 의존성을 추가하세요.
[x] 테스트 시작 시 스키마를 새로 생성하도록 설정하세요.
[x] 디버깅에 용이하도록 로그 레벨을 적절히 설정하세요.
[x] 테스트 실행 간 test 프로파일을 활성화 하세요.
[x] JPA Audit 기능을 활성화 하기 위해 테스트 클래스에 @EnableJpaAuditing을 추가하세요.
[x] 주요 레포지토리(User, Channel, Message)의 주요 쿼리 메소드에 대해 각각 최소 2개 이상(성공, 실패)의 테스트 케이스를 작성하세요.
[x] 커스텀 쿼리 메소드
[x] 페이징 및 정렬 메소드
[x] 컨트롤러 레이어의 슬라이스 테스트를 작성하세요.
[x] @WebMvcTest를 활용해 테스트를 구현하세요.
[x] WebMvcTest에서 자동으로 등록되지 않는 유형의 Bean이 필요하다면 @import를 활용해 추가하세요.
[x] 주요 컨트롤러(User, Channel, Message)에 대해 최소 2개 이상(성공, 실패)의 테스트 케이스를 작성하세요.
[x] MockMvc를 활용해 컨트롤러를 테스트하세요.
[x] 서비스 레이어를 모의(mock)하여 컨트롤러 로직만 테스트하세요.
[x] JSON 응답을 검증하는 테스트를 포함하세요.
통합 테스트
[x] 통합 테스트 환경을 구성하세요.
[ ] @SpringBootTest를 활용해 Spring 애플리케이션 컨텍스트를 로드하세요.
[x] H2 인메모리 데이터베이스를 활용하세요.
[ ] 테스트용 프로파일을 구성하세요.
[ ] 주요 API 엔드포인트에 대한 통합 테스트를 작성하세요.
[ ] 주요 API에 대해 최소 2개 이상의 테스트 케이스를 작성하세요.
[ ] 사용자 관련 API (생성, 수정, 삭제, 목록 조회)
[ ] 채널 관련 API (생성, 수정, 삭제)
[ ] 메시지 관련 API (생성, 수정, 삭제, 목록 조회)
[ ] 각 테스트는 @transactional을 활용해 독립적으로 실행하세요.

심화

  • 심화 항목 1
  • 심화 항목 2

주요 변경사항

스크린샷

image

멘토에게

  • 셀프 코드 리뷰를 통해 질문 이어가겠습니다.

이용일 and others added 13 commits March 25, 2026 18:52
* Re:Zero

* FileRepository created

* 복구완료

* 스프린트미션3 세í시작��세팅시작점

* bean 추가 작업(미완성)

* 다시만들어낸 Bean(메인ì�은 너무 어렵다)

* configBean

* 롬복?êRombok+Time+Status

* 고도화전 중간저장

* DTO find 작업중

* ë유저서비스 find고도화 작업중

* 아DTO작업에서 막힘

* ì 미완성  제출

* 베이스코드 받아옴

---------

Co-authored-by: 이용일 <lyi980403>
# Conflicts:
#	build.gradle
#	src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java
#	src/main/java/com/sprint/mission/discodeit/dto/data/ChannelDto.java
#	src/main/java/com/sprint/mission/discodeit/dto/data/UserDto.java
#	src/main/java/com/sprint/mission/discodeit/dto/request/BinaryContentCreateRequest.java
#	src/main/java/com/sprint/mission/discodeit/dto/request/LoginRequest.java
#	src/main/java/com/sprint/mission/discodeit/dto/request/MessageCreateRequest.java
#	src/main/java/com/sprint/mission/discodeit/dto/request/MessageUpdateRequest.java
#	src/main/java/com/sprint/mission/discodeit/dto/request/PrivateChannelCreateRequest.java
#	src/main/java/com/sprint/mission/discodeit/dto/request/PublicChannelCreateRequest.java
#	src/main/java/com/sprint/mission/discodeit/dto/request/PublicChannelUpdateRequest.java
#	src/main/java/com/sprint/mission/discodeit/dto/request/ReadStatusCreateRequest.java
#	src/main/java/com/sprint/mission/discodeit/dto/request/ReadStatusUpdateRequest.java
#	src/main/java/com/sprint/mission/discodeit/dto/request/UserCreateRequest.java
#	src/main/java/com/sprint/mission/discodeit/dto/request/UserStatusCreateRequest.java
#	src/main/java/com/sprint/mission/discodeit/dto/request/UserStatusUpdateRequest.java
#	src/main/java/com/sprint/mission/discodeit/dto/request/UserUpdateRequest.java
#	src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java
#	src/main/java/com/sprint/mission/discodeit/entity/Channel.java
#	src/main/java/com/sprint/mission/discodeit/entity/ChannelType.java
#	src/main/java/com/sprint/mission/discodeit/entity/Message.java
#	src/main/java/com/sprint/mission/discodeit/entity/ReadStatus.java
#	src/main/java/com/sprint/mission/discodeit/entity/User.java
#	src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java
#	src/main/java/com/sprint/mission/discodeit/repository/BinaryContentRepository.java
#	src/main/java/com/sprint/mission/discodeit/repository/ChannelRepository.java
#	src/main/java/com/sprint/mission/discodeit/repository/MessageRepository.java
#	src/main/java/com/sprint/mission/discodeit/repository/ReadStatusRepository.java
#	src/main/java/com/sprint/mission/discodeit/repository/UserRepository.java
#	src/main/java/com/sprint/mission/discodeit/repository/UserStatusRepository.java
#	src/main/java/com/sprint/mission/discodeit/service/AuthService.java
#	src/main/java/com/sprint/mission/discodeit/service/BinaryContentService.java
#	src/main/java/com/sprint/mission/discodeit/service/ChannelService.java
#	src/main/java/com/sprint/mission/discodeit/service/MessageService.java
#	src/main/java/com/sprint/mission/discodeit/service/ReadStatusService.java
#	src/main/java/com/sprint/mission/discodeit/service/UserService.java
#	src/main/java/com/sprint/mission/discodeit/service/UserStatusService.java
#	src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java
#	src/main/java/com/sprint/mission/discodeit/service/basic/BasicBinaryContentService.java
#	src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java
#	src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java
#	src/main/java/com/sprint/mission/discodeit/service/basic/BasicReadStatusService.java
#	src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java
#	src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserStatusService.java
#	src/main/resources/application.yaml
Copy link
Copy Markdown

@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

전체 요약

Spring Boot 프로파일 기반 설정 분리, Logback 로깅 구성, 커스텀 예외 처리 체계(DiscodeitException 계층), Spring Validation, Actuator, 그리고 서비스/레포지토리/컨트롤러/통합 테스트를 추가한 스프린트7 구현입니다. 전반적으로 예외 계층 설계와 테스트 구조가 잘 잡혀 있지만, 보안 취약점(운영 DB 자격증명 하드코딩) 및 불필요한 파일 커밋 등 반드시 수정이 필요한 사항이 있습니다.


[P2] 커밋에 포함되어서는 안 되는 파일들

PR에 다음과 같이 커밋에 포함되어서는 안 되는 파일들이 있습니다:

  • .logs/app-2026-03-27.log, .logs/app-2026-04-05.log, .logs/app.log — 런타임에 생성되는 로그 파일
  • .discodeit/storage/c1bdadbd-..., c35a78db-... — 로컬 바이너리 스토리지 데이터
  • tatusgit log 명령어 출력 결과가 실수로 파일로 커밋된 것으로 보입니다
  • truct만 하면 끝less 명령어 매뉴얼이 실수로 파일로 커밋됨
  • src/main/resources/fe_bundle_1.2.3.zip, 각종 PNG 이미지 등 바이너리 파일

.gitignore.logs/, .discodeit/, *.log 등을 추가하고 해당 파일들을 정리해 주세요.

datasource:
url: jdbc:postgresql://prod-server/mydb # 운영용 DB
username: prod_user
password: prod_password
Copy link
Copy Markdown

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: prod_password
username: ${DB_USERNAME}
password: ${DB_PASSWORD}

@@ -0,0 +1,2 @@
server:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[P2] .properties 파일에 YAML 형식으로 작성됨

application.properties 파일은 key=value 형식을 사용해야 합니다. 현재 YAML 형식으로 작성되어 있어 Spring Boot가 이 설정을 올바르게 파싱하지 못합니다. 이미 application-dev.yaml이 있으므로 이 파일을 제거하거나, 아래와 같이 올바른 형식으로 수정해 주세요.

Suggested change
server:
server.port=8080

username: sa
password:

main:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[P2] YAML 들여쓰기 오류 — spring.main 설정이 datasource.password 하위에 위치함

spring.main.allow-bean-definition-overriding을 설정하려는 의도였겠지만, 현재 들여쓰기 구조에서는 spring.datasource.password.main.allow-bean-definition-overriding으로 파싱됩니다. 빈 오버라이딩 설정이 실제로 적용되지 않을 수 있습니다. main: 블록을 spring: 직하위로 이동시켜 주세요:

spring:
  datasource:
    url: ...
    username: sa
    password:
  main:
    allow-bean-definition-overriding: true

</appender>

<!-- Root에 연결 -->
<root level="info">
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[P3] 프로파일별 로그 레벨 미적용

요구사항에서 개발 환경은 debug, 운영 환경은 info 레벨로 설정하라고 명시되어 있지만, 현재 <root level="info">로 항상 고정되어 있습니다. <springProfile> 태그를 활용해 프로파일별 로그 레벨을 분기해 주세요:

  <springProfile name="dev">
    <root level="debug">
      <appender-ref ref="CONSOLE"/>
      <appender-ref ref="FILE"/>
    </root>
  </springProfile>
  <springProfile name="prod">
    <root level="info">
      <appender-ref ref="CONSOLE"/>
      <appender-ref ref="FILE"/>
    </root>
  </springProfile>


logging:
level:
com.your.package: info No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[P3] 패키지명 플레이스홀더가 실제 패키지명으로 대체되지 않음

com.your.package는 템플릿의 예시 값으로, 이 상태에서는 로깅 설정이 아무 효과도 없습니다. 실제 프로젝트 패키지명으로 교체해 주세요.

Suggested change
com.your.package: info
com.sprint.mission.discodeit: info

String exceptionType,
int status
) {
public static ErrorResponse of(DiscodeitException e, String path) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[P4] path 파라미터를 받지만 사용하지 않음

of(DiscodeitException e, String path) 메서드에서 path를 파라미터로 받지만 ErrorResponse 생성 시 전혀 사용하지 않습니다. ErrorResponse 레코드에 path 필드를 추가하거나, 사용하지 않는 파라미터를 제거하는 것을 고려해 보세요.

log.error("처리되지 않은 예외 발생: path={}, message={}",
request.getRequestURI(), e.getMessage());
return ResponseEntity
.status(500)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[P4] 이미 import된 클래스를 풀 패키지명으로 사용

파일 상단에 import java.time.Instant;import java.util.Map;이 이미 선언되어 있는데, 메서드 내에서 java.time.Instant.now(), java.util.Map.of()처럼 풀 패키지명으로 사용하고 있습니다. 단순 일관성 문제입니다.

Suggested change
.status(500)
Instant.now(),

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants