Skip to content

[전명훈] sprint7#162

Open
coje1111 wants to merge 8 commits into
codeit-bootcamp-spring:전명훈from
coje1111:전명훈_sprint7

Hidden character warning

The head ref may contain hidden characters: "\uc804\uba85\ud6c8_sprint7"
Open

[전명훈] sprint7#162
coje1111 wants to merge 8 commits into
codeit-bootcamp-spring:전명훈from
coje1111:전명훈_sprint7

Conversation

@coje1111
Copy link
Copy Markdown
Collaborator

@coje1111 coje1111 commented Apr 5, 2026

요구사항

기본 요구사항
프로파일 기반 설정 관리
[ ] 개발, 운영 환경에 대한 프로파일을 구성하세요.
[ ] application-dev.yaml, application-prod.yaml 파일을 생성하세요.
[ ] 다음과 같은 설정값을 프로파일별로 분리하세요.
[ ] 데이터베이스 연결 정보
[ ] 서버 포트
로그 관리
[ ] 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.png

[ ] 모든 예외의 기본이 되는 DiscodeitException 클래스를 정의하세요.

클래스 다이어그램

j5vtp941a-image.png

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

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

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

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

예시

a6f585icy-image.png

[ ] 기존에 구현했던 예외를 커스텀 예외로 대체하세요.

NoSuchElementException
IllegalArgumentException

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

클래스 다이어그램

3gqeampkw-image.png

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.yaml을 생성하세요.
[ ] 데이터소스는 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을 활용해 독립적으로 실행하세요.

주요 변경사항

스크린샷

image

멘토에게

  • 기본 요구사항만 진행했습니다

@coje1111 coje1111 requested a review from bellman66 April 5, 2026 13:08
@coje1111 coje1111 added the 미완성🛠️ 스프린트 미션 제출일이지만 미완성했습니다. 죄송합니다. label Apr 5, 2026
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.

불필요한 자료는 제거하거나 gitignore에 추가해주세요

Comment on lines +8 to +14
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 사용자를 찾을 수 없습니다."),
DUPLICATE_USER(HttpStatus.CONFLICT, "이미 존재하는 사용자입니다."),

// Channel 관련 예외
CHANNEL_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 채널을 찾을 수 없습니다."),
PRIVATE_CHANNEL_UPDATE(HttpStatus.FORBIDDEN, "비공개 채널은 수정할 수 없습니다."),
USER_NOT_IN_CHANNEL(HttpStatus.FORBIDDEN, "채널에 참여하지 않은 사용자입니다."),
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.

HttpStatus 추가 좋습니다 👍 👍 👍

Comment on lines +15 to 16
@RestControllerAdvice
public class GlobalExceptionHandler {
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.

Advice는 패키지로 따로 분리하시는게 어떠신가요
Exception이 늘어날수록 Handler와 섞여서 분기하기 쉽지않아보입니다.


private final Instant timestamp;
private final ErrorCode errorCode;
private final Map<String, Object> 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.

Map 형태로 Detail을 반환하도록 되어있는데요 ( 아무래도 템플릿인거 같지만 )
이런 경우 도출되는 형태가 예측이 되지않기때문에 사용하기 어려운 부분이 있습니다.
따라서 객체를 따로 지정해서 업로드 하는 게 좋아보입니다.

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.

다른 Entity에 대한 Exception이 안보여서 추가 설정이 필요해보입니다.
Ex) ReadStatus, BinaryContent

Comment on lines +4 to +6
public UserNotFoundException() {
super(ErrorCode.USER_NOT_FOUND);
}
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.

실패한 사유에 대한 값을 입력 파라미터에 받는 것도 괜찮아 보입니다.

.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.name").value("Test Channel"));
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.

검증 value 값을 상단에 변수로 지정후에 사용하시면 좀더 유연하게 사용가능합니다.

// when & then
mvc.perform(delete("/api/users/{userId}", userId))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.code").value("USER_NOT_FOUND"));
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.

Errorcode의 경우 Enum 값으로 핸들링하시면 좀더 안전하게 구현하실수 있습니다.

entityManager.flush();
try { Thread.sleep(10); } catch (InterruptedException e) {}
entityManager.persist(new Message("Message 3", channel, user, List.of()));
entityManager.flush();
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.

entityManager.flush(); 후에 .clear() 호출이 필요합니다.
1차 캐시 값을 clear로 제거해주는 로직을 통해 정합성을 맞추는게 가능합니다.


@DisplayName("update - 성공 (PUBLIC 채널)")
@Test
void update_Success_PublicChannel() {
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.

네이밍 규칙을 맞춰보는게 필요해보입니다.
테스트 케이스가 다양해지면 질수록 네이밍이 길어지면서 식별하기 어려워집니다.

Comment on lines +62 to +63
UserDto user = userService.create(new UserCreateRequest("msgUser", "msg@email.com", "pass123"), Optional.empty());
testUserId = user.id();
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.

Fixture 로직을 한번 참고해보시면 좋을거 같아요. 주로 사용하는 객체들을
정의하여 재사용성을 늘릴수 있습니다.

Comment on lines +7 to +9
UUID userId,
UUID channelId,
Instant lastReadAt
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.

Valid 로직이 필요해보입니다. ID 값이 안들어오는 케이스를 제거해주세요

@bellman66
Copy link
Copy Markdown
Collaborator

심화 학습의 경우 한번 살펴보시고 천천히 해주시면 될거같습니다.

추가로 코드가 충돌나는 부분은 개선해서 업데이트 한번 요청드립니다.

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