한성재 sprint7#159
Hidden character warning
Conversation
기본 요구사함 프로파일 기반 설정 관리에 맞춘 프로파일 추가
Spring Validation 의존성을 추가
joonfluence
left a comment
There was a problem hiding this comment.
각 코드 위치에 인라인 코멘트로 리뷰를 작성했습니다. Critical → Major → Minor 순으로 확인해주세요.
| @ExceptionHandler(DiscodeitException.class) | ||
| public ResponseEntity<ErrorResponse> handleDiscodeitException(DiscodeitException e){ | ||
| HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; | ||
| ErrorResponse response = ErrorResponse.of(e, status.value()); |
There was a problem hiding this comment.
[Critical] DiscodeitException fallback 핸들러가 항상 500 반환
새로운 커스텀 예외를 추가할 때 전용 핸들러를 빠뜨리면 이 fallback에 걸려 항상 500 Internal Server Error가 반환됩니다.
ErrorCode에 HTTP status를 매핑하거나, DiscodeitException에 getHttpStatus() 메서드를 추가하세요.
// ErrorCode에 status 추가 예시
USER_NOT_FOUND(2001, "존재하지 않는 사용자입니다.", HttpStatus.NOT_FOUND),
// fallback 핸들러에서 활용
HttpStatus status = e.getErrorCode().getHttpStatus();| // Message | ||
| @ExceptionHandler(MessageNotFoundException.class) | ||
| public ResponseEntity<ErrorResponse> handleChannelNotFoundException(MessageNotFoundException e){ | ||
| HttpStatus status = HttpStatus.NOT_FOUND; |
There was a problem hiding this comment.
[Minor] 메서드 이름 중복
handleChannelNotFoundException이라는 이름의 핸들러가 ChannelNotFoundException과 MessageNotFoundException 두 곳에 정의되어 있습니다. 컴파일은 되지만 혼란을 줍니다.
// 변경
public ResponseEntity<ErrorResponse> handleMessageNotFoundException(MessageNotFoundException e) { ... }| throws IOException { | ||
|
|
||
| log.info("사용자 정보 수정 요청 수신: username={}, userId={}",request, userId); | ||
| Optional<BinaryContentCreateRequest> binaryContentCreateRequest = Optional.empty(); |
There was a problem hiding this comment.
[Critical] 로그에 비밀번호 노출 위험
request 객체를 통째로 로깅하면 UserUpdateRequest의 newPassword 필드가 로그에 평문으로 기록될 수 있습니다.
// 수정
log.info("사용자 정보 수정 요청 수신: username={}, userId={}", request.newUsername(), userId);| }); | ||
|
|
||
| if (!user.getPassword().equals(request.password())){ | ||
| log.warn("로그인 실패: 비밀번호 불일치 - username={}", request.username()); |
There was a problem hiding this comment.
[Critical] 비밀번호 평문 비교
비밀번호를 평문으로 저장·비교하고 있습니다. 학습 프로젝트라도 PasswordEncoder(BCrypt 등) 사용을 권장합니다.
// 예시
if (!passwordEncoder.matches(request.password(), user.getPassword())) {
throw new AuthenticationException(...);
}| import org.springframework.data.annotation.CreatedDate; | ||
| import org.springframework.data.jpa.domain.support.AuditingEntityListener; | ||
| import org.springframework.data.jpa.repository.config.EnableJpaAuditing; | ||
|
|
There was a problem hiding this comment.
[Major] 미사용 import
@EnableJpaAuditing은 JpaConfig로 이동했는데, 여기서는 사용하지 않는 import가 추가되었습니다. 제거해주세요.
|
|
||
|
|
||
| import jakarta.validation.constraints.*; | ||
|
|
There was a problem hiding this comment.
[Major] 와일드카드 import 사용
jakarta.validation.constraints.* 대신 필요한 애노테이션만 명시적으로 import하세요. 코드 가독성이 높아지고 IDE 지원도 더 원활합니다.
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;| discodeit.iml | ||
| /storage/ No newline at end of file | ||
| /storage/ | ||
|
|
There was a problem hiding this comment.
[Minor] /storage/ 항목 중복
/storage/가 이 파일에 두 번 정의되어 있습니다. 하나를 제거해주세요.
|
|
||
| // MESSAGE | ||
| MESSAGE_NOT_FOUND(4001, "메시지가 존재하지 않습니다") | ||
| ; |
There was a problem hiding this comment.
[Minor] 마침표 누락으로 일관성 깨짐
다른 ErrorCode 항목은 메시지 끝에 마침표가 있는데 MESSAGE_NOT_FOUND만 없습니다.
MESSAGE_NOT_FOUND(4001, "메시지가 존재하지 않습니다.") // 마침표 추가| plugins { | ||
| id 'java' | ||
| id 'org.springframework.boot' version '4.0.5' | ||
| id 'io.spring.dependency-management' version '1.1.7' |
There was a problem hiding this comment.
[Major] Spring Boot 버전 오류
org.springframework.boot version '4.0.5'는 존재하지 않는 버전입니다. Spring Boot는 현재 3.x 라인입니다. discodeit 모듈(3.5.10 — 이것도 확인 필요)과 동일한 버전으로 맞춰주세요.
또한 springBootAdminVersion도 "4.0.2"로 설정되어 있는데, 이 버전도 존재하지 않습니다.
| @@ -0,0 +1,163 @@ | |||
| package com.sprint.mission.discodeit.integraton; | |||
| import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; | |||
There was a problem hiding this comment.
[Minor] 패키지 이름 오타
integraton은 integration의 오타입니다. 패키지 이름과 디렉토리를 수정해주세요.
// 수정
package com.sprint.mission.discodeit.integration;| // custom exception | ||
| @ExceptionHandler(DiscodeitException.class) | ||
| public ResponseEntity<ErrorResponse> handleDiscodeitException(DiscodeitException e){ | ||
| HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; |
There was a problem hiding this comment.
[Critical] DiscodeitException fallback 핸들러가 항상 500 반환
새로운 커스텀 예외를 추가할 때 전용 핸들러를 빠뜨리면 이 fallback에 걸려 항상 500 Internal Server Error가 반환됩니다.
ErrorCode에 HTTP status를 매핑하거나, DiscodeitException에 getHttpStatus() 메서드를 추가하세요.
// ErrorCode에 status 추가 예시
USER_NOT_FOUND(2001, "존재하지 않는 사용자입니다.", HttpStatus.NOT_FOUND),
// fallback 핸들러에서 활용
HttpStatus status = e.getErrorCode().getHttpStatus();|
|
||
| // Message | ||
| @ExceptionHandler(MessageNotFoundException.class) | ||
| public ResponseEntity<ErrorResponse> handleChannelNotFoundException(MessageNotFoundException e){ |
There was a problem hiding this comment.
[Minor] 메서드 이름 중복
handleChannelNotFoundException이라는 이름의 핸들러가 ChannelNotFoundException과 MessageNotFoundException 두 곳에 정의되어 있습니다. 컴파일은 되지만 혼란을 줍니다.
// 변경
public ResponseEntity<ErrorResponse> handleMessageNotFoundException(MessageNotFoundException e) { ... }| , @RequestPart(value = "profile", required = false) MultipartFile imageFile) | ||
| throws IOException { | ||
|
|
||
| log.info("사용자 정보 수정 요청 수신: username={}, userId={}",request, userId); |
There was a problem hiding this comment.
[Critical] 로그에 비밀번호 노출 위험
request 객체를 통째로 로깅하면 UserUpdateRequest의 newPassword 필드가 로그에 평문으로 기록될 수 있습니다.
// 수정
log.info("사용자 정보 수신: username={}, userId={}", request.newUsername(), userId);| return new AuthenticationException("Not Exist User - userName: " + request.username()); | ||
| }); | ||
|
|
||
| if (!user.getPassword().equals(request.password())){ |
There was a problem hiding this comment.
[Critical] 비밀번호 평문 비교
비밀번호를 평문으로 저장·비교하고 있습니다. 학습 프로젝트라도 PasswordEncoder(BCrypt 등) 사용을 권장합니다.
// 예시
if (!passwordEncoder.matches(request.password(), user.getPassword())) {
throw new AuthenticationException(...);
}|
|
||
| import org.springframework.data.annotation.CreatedDate; | ||
| import org.springframework.data.jpa.domain.support.AuditingEntityListener; | ||
| import org.springframework.data.jpa.repository.config.EnableJpaAuditing; |
There was a problem hiding this comment.
[Major] 미사용 import
@EnableJpaAuditing은 JpaConfig로 이동했습니다. 이 파일에서는 사용하지 않으므로 제거해주세요.
| import jakarta.persistence.criteria.CriteriaBuilder.In; | ||
| import java.util.NoSuchElementException; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.data.jpa.repository.config.EnableJpaAuditing; |
There was a problem hiding this comment.
[Major] Service 클래스에 미사용 import
@EnableJpaAuditing은 @Configuration 클래스(JpaConfig)에서만 사용해야 합니다. 이 Service에서는 사용되지 않으므로 제거해주세요.
| log.warn("채널 삭제 실패: 존재하지 않는 채널 ID={}", id); | ||
| return new ChannelNotFoundException(id); | ||
| }); | ||
| log.info("채널 삭제 완료: channelId={}", id); |
There was a problem hiding this comment.
[Minor] 로그 순서 오류 — 삭제 전에 "완료" 로그가 찍힘
현재 channelRepository.delete(channel) 호출 전에 "채널 삭제 완료" 로그가 기록됩니다. DB 작업 실패 시 로그와 실제 상태가 불일치합니다.
channelRepository.delete(channel);
log.info("채널 삭제 완료: channelId={}", id); // 이 위치로 이동| import java.util.UUID; | ||
|
|
||
| public record MessageCreateRequest( | ||
| @Size(max = 100, message = "메시지 내용은 최대 100자입니다.") |
There was a problem hiding this comment.
[Major] content 필드에 @NotBlank 누락
@Size(max = 100)만으로는 null 또는 빈 문자열("")을 막지 못합니다. 빈 메시지 전송을 방지하려면 @NotBlank를 함께 사용해야 합니다.
@NotBlank(message = "메시지 내용은 필수입니다.")
@Size(max = 100, message = "메시지 내용은 최대 100자입니다.")
String content,| package com.sprint.mission.discodeit.dto.request; | ||
|
|
||
|
|
||
| import jakarta.validation.constraints.*; |
There was a problem hiding this comment.
[Major] 와일드카드 import 사용
jakarta.validation.constraints.* 대신 필요한 애노테이션만 명시적으로 import하세요.
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;| PRIVATE_CHANNEL_UPDATE(3002, "PRIVATE 채널은 수정할 수 없습니다."), | ||
|
|
||
| // MESSAGE | ||
| MESSAGE_NOT_FOUND(4001, "메시지가 존재하지 않습니다") |
There was a problem hiding this comment.
[Minor] 마침표 누락으로 일관성 깨짐
다른 ErrorCode 항목은 메시지 끝에 마침표가 있는데 MESSAGE_NOT_FOUND만 없습니다.
MESSAGE_NOT_FOUND(4001, "메시지가 존재하지 않습니다.") // 마침표 추가| @@ -0,0 +1,39 @@ | |||
| plugins { | |||
| id 'java' | |||
| id 'org.springframework.boot' version '4.0.5' | |||
There was a problem hiding this comment.
[Major] 존재하지 않는 Spring Boot 버전
org.springframework.boot version '4.0.5'는 존재하지 않는 버전입니다. Spring Boot는 현재 3.x 라인입니다. springBootAdminVersion "4.0.2"도 마찬가지로 확인이 필요합니다. discodeit 모듈과 동일한 버전으로 맞춰주세요.
| @@ -0,0 +1,163 @@ | |||
| package com.sprint.mission.discodeit.integraton; | |||
| import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; | |||
There was a problem hiding this comment.
[Minor] 패키지 이름 오타
integraton → integration으로 수정이 필요합니다. 디렉토리명도 함께 변경해주세요.
package com.sprint.mission.discodeit.integration;| discodeit.iml | ||
| /storage/ No newline at end of file | ||
| /storage/ | ||
|
|
There was a problem hiding this comment.
[Minor] /storage/ 항목 중복
/storage/가 두 번 정의되어 있습니다. 하나를 제거해주세요.
요구사항
기본
심화
주요 변경사항
스크린샷
멘토에게