Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

입력값과 엔티티 필드에 대한 유효성 검증 구현 #120

Merged
merged 19 commits into from
Jul 31, 2024

Conversation

Mingyum-Kim
Copy link
Contributor

@Mingyum-Kim Mingyum-Kim commented Jul 25, 2024

PR의 목적이 무엇인가요?

  • 입력값과 엔티티 필드 등에 대한 유효성 검증 구현
  • Bean Validation 에러 발생 시 에러 핸들링 구현

이슈 ID는 무엇인가요?

설명

엔티티와 DTO에서의 유효성 검증을 다르게 가져갔습니다.
엔티티는 모임 생성 API 뿐만 아니라 의도치 않은 객체 생성을 고려해서 모든 유효성 검증을 처리하였습니다. 반면 DTO는 세밀한 유효성 검증이 필요하지 않고, NPE 등을 방지하기 위해 NULL과 BLANK 체크만 이루어지면 된다고 생각했어요.

정리하자면 유효성 검증 목록은 아래와 같습니다 😄

  1. 엔티티 레벨에서의 유효성 검증
  • NULL 체크
  • BLANK 체크
  • 문자열 길이 검증
  • 숫자 범위 검증
  • 모임 날짜와 시간이 과거인지 검증
  1. DTO 레벨에서의 유효성 검증
  • NULL 체크
  • BLANK 체크

추가로, 회의 결과를 반영하여 Bean Validation 오류 시 던져지는 MethodArgumentNotValidException을 핸들링하여 제일 처음의 오류 메시지만 반환하도록 구현하였습니다.

질문 혹은 공유 사항 (Optional)

  1. 유효성 검증에 의해 엔티티가 길어지는 현상
    MIN, MAX 등의 상수와 validate 함수로 인해 엔티티가 너무 길어지는 문제가 있었습니다.
    VO 사용 등을 통해 해당 문제와 중복 코드를 해결할 수 있을 것 같습니다.

  2. DTO와 엔티티의 유효성 검증 범위
    앞서 설명 드린 유효성 검증에 대한 의견이 궁금합니다 😄
    엔티티와 DTO에 대해 유효성 검증 역할이 적절히 분배된 게 맞나요?

@Mingyum-Kim Mingyum-Kim added BE 백엔드 관련 이슈입니다. 🌱 기능추가 feature (새로운 기능 구현) labels Jul 25, 2024
@Mingyum-Kim Mingyum-Kim self-assigned this Jul 25, 2024
Copy link
Contributor

@ay-eonii ay-eonii left a comment

Choose a reason for hiding this comment

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

고생했어요 안나 👍

코멘트 확인 부탁드려용~!!

Comment on lines +16 to +22
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException exception,
HttpHeaders headers, HttpStatusCode status, WebRequest request) {
String error = exception.getBindingResult().getFieldErrors().get(0).getDefaultMessage();

return ResponseEntity.badRequest().body(new ErrorResponse(error));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

개인적인 취향인데 오버라이딩한 메서드를 아래에 두는 건 어떤가용?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

조금 더 이유를 붙여서 설명해주면 좋을 것 같아요 -!

Copy link
Contributor

Choose a reason for hiding this comment

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

논리적인 이유는 없고 ..
오버라이딩한 메서드보다 GlobalExceptionHandler 만이 가진 메서드(handleMoudaException, handleException)가 클래스를 더 잘 나타낸다는 느낌이 있어서 위에 말한 방식을 선호하는 것 같아요 ㅎ_ㅎ

Copy link
Contributor

Choose a reason for hiding this comment

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

논리적인 이유는 없고 ..
오버라이딩한 메서드보다 GlobalExceptionHandler 만이 가진 메서드(handleMoudaException, handleException)가 클래스를 더 잘 나타낸다는 느낌이 있어서 위에 말한 방식을 선호하는 것 같아요 ㅎ_ㅎ

Comment on lines +12 to +25
PAST_DATE_TIME("모임 날짜를 현재 시점 이후로 입력해주세요."),
TITLE_NOT_EXIST("모임 제목을 입력해주세요."),
TITLE_TOO_LONG("모임 제목을 조금 더 짧게 입력해주세요."),
DATE_NOT_EXIST("모임 날짜를 입력해주세요."),
TIME_NOT_EXIST("모임 시간을 입력해주세요."),
PLACE_NOT_EXIST("모임 장소를 입력해주세요."),
PLACE_TOO_LONG("모임 장소를 조금 더 짧게 입력해주세요."),
MAX_PEOPLE_IS_POSITIVE("모임 최대 인원은 양수여야 합니다."),
MAX_PEOPLE_TOO_MANY("모임 최대 인원을 조금 더 적게 입력해주세요."),
AUTHOR_NICKNAME_NOT_EXIST("모임 생성자 닉네임을 입력해주세요."),
AUTHOR_NICKNAME_TOO_LONG("모임 생성자 이름을 조금 더 짧게 입력해주세요."),
DESCRIPTION_TOO_LONG("모임 설명을 조금 더 짧게 입력해주세요."),
MEMBER_NICKNAME_NOT_EXISTS("모임 참여자 닉네임을 입력해주세요."),
MEMBER_NICKNAME_TOO_LONG("모임 참여자 닉네임을 조금 더 짧게 입력해주세요.");
Copy link
Contributor

Choose a reason for hiding this comment

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

저는 백엔드의 예외메시지는 개발자가 예외를 빨리 파악하기 위한 용도라고 생각해요. 현재 메시지는 사용자에게 좋지만, 상태를 표현하는 예외메시지는 개발자에게 좋을 것 같아요. 예를 들어, "모임 참여자 닉네임 길이를 초과했습니다." 처럼요. 이 예외가 발생하는 게 사용자의 입력에 의해 발생하는 건 맞지만 Moim 객체는 어디서 파라미터가 들어오는지 모르니까요!

물론 취향 문제라고도 생각해서 안나 의견 존중합니다~!

Copy link
Contributor

Choose a reason for hiding this comment

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

저는 백엔드의 예외메시지는 개발자가 예외를 빨리 파악하기 위한 용도라고 생각해요. 현재 메시지는 사용자에게 좋지만, 상태를 표현하는 예외메시지는 개발자에게 좋을 것 같아요. 예를 들어, "모임 참여자 닉네임 길이를 초과했습니다." 처럼요. 이 예외가 발생하는 게 사용자의 입력에 의해 발생하는 건 맞지만 Moim 객체는 어디서 파라미터가 들어오는지 모르니까요!

물론 취향 문제라고도 생각해서 안나 의견 존중합니다~!

나중에 로깅이 추가될때 다시 짚어보면 좋을듯요!

Comment on lines +128 to +129
.date(LocalDate.now())
.time(LocalTime.now().minusHours(1))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
.date(LocalDate.now())
.time(LocalTime.now().minusHours(1))
.date(DATE)
.time(TIME)

과거 시간이어서 예외가 발생할 수 있을 것 같아요~!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

과거 시간을 테스트하는 게 맞아요 😀

Comment on lines 223 to 225
@DisplayName("설명의 길이가 길면 모임 객체 생성에 성공한다.")
@Test
void createMoimWhenDescriptionIsTooLong() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
@DisplayName("설명의 길이가 길면 모임 객체 생성에 성공한다.")
@Test
void createMoimWhenDescriptionIsTooLong() {
@DisplayName("설명의 길이가 길면 모임 객체 생성에 실패한다.")
@Test
void failToCreateMoimWhenDescriptionIsTooLong() {

Copy link
Contributor

@ksk0605 ksk0605 left a comment

Choose a reason for hiding this comment

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

고생하셨어요! 몇 가지 코멘트들 확인 부탁드릴게요.
테스트 픽스쳐 같은 경우는 이번 이슈에서 적용안해도 될거같아요 참고만 부탁드려요 ㅎㅎ

Comment on lines 10 to 12
public record MoimCreateRequest(
@NotBlank
String title,
Copy link
Contributor

Choose a reason for hiding this comment

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

클래스 명 이후 한 줄 띄어쓰는 것이 컨벤션 맞죠..?!

Copy link
Contributor Author

@Mingyum-Kim Mingyum-Kim Jul 26, 2024

Choose a reason for hiding this comment

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

Record의 경우에는 이 부분이 파라미터에 해당해서 안 띄우는 게 맞다고 생각했어요 👍

Comment on lines 41 to 47
.title("모임 제목")
.date(LocalDate.now().plusDays(1))
.time(LocalTime.now().minusHours(1))
.place("서울시 강북구 중앙로 2길 25")
.maxPeople(10)
.authorNickname("안나")
.description("모임 설명입니다.")
Copy link
Contributor

Choose a reason for hiding this comment

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

반복적인 모임 생성이 슬슬 보이기 시작하군요..!
테스트 픽스쳐 적용도 고려해보시죠!

@@ -107,7 +107,7 @@ void joinMoim() {
@Test
void deleteMoim() {
MoimCreateRequest moimCreateRequest = new MoimCreateRequest(
"title", LocalDate.now(), LocalTime.now(), "place",
"title", LocalDate.now().plusDays(1), LocalTime.now(), "place",
10, "안나", "설명"
);
Copy link
Contributor

Choose a reason for hiding this comment

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

요청 객체도 마찬가지구요! (Test Fixture)

Copy link
Contributor

@hoyeonyy hoyeonyy left a comment

Choose a reason for hiding this comment

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

꼼꼼하게 검증 처리해준 안나에게 발박수 드립니다,,

테바가 제안해준 test fixture가 슬슬 필요할 시기라고도 느껴지네요!

고생하셨습니다! approve 남길께요!

@@ -43,7 +44,7 @@ public ResponseEntity<RestResponse<MoimFindAllResponses>> findAllMoim() {

@Override
@GetMapping("/{moimId}")
public ResponseEntity<RestResponse<MoimDetailsFindResponse>> findMoimDetails(@PathVariable Long moimId) {
public ResponseEntity<RestResponse<MoimDetailsFindResponse>> findMoimDetails(@PathVariable("moimId") Long moimId) {
Copy link
Contributor

Choose a reason for hiding this comment

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

이 코드는 조금 더 명시적인 측면에서 작성하신 건가요?!

Copy link
Contributor

Choose a reason for hiding this comment

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

이 코드는 조금 더 명시적인 측면에서 작성하신 건가요?!

moimId를 명시하지 않았을때 안나 테스트가 터지더라고~! 머지 되면 각자 컴퓨터에서 다시 돌려서 확인해봐용~~

@@ -57,6 +76,67 @@ public Moim(
this.description = description;
}

private void validateTitle(String title) {
Copy link
Contributor

Choose a reason for hiding this comment

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

안나의 꼼꼼함에 발박수 드립니다.

Copy link
Contributor

@hoyeonyy hoyeonyy left a comment

Choose a reason for hiding this comment

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

꼼꼼하게 검증처리를 해준 안나에게 발 박수 드립니다,,

테바가 제안해준 Test Fixture를 도입해야 될 때가 된것 같긴하네요..!

고생하셨습니다~! approve 할께요!

Copy link
Contributor

@pricelees pricelees left a comment

Choose a reason for hiding this comment

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

안나 고생많았어~!👍

MIN, MAX 등의 상수와 validate 함수로 인해 엔티티가 너무 길어지는 문제가 있었습니다.
VO 사용 등을 통해 해당 문제와 중복 코드를 해결할 수 있을 것 같습니다.

아직은 괜찮은 것 같아요!

앞서 설명 드린 유효성 검증에 대한 의견이 궁금합니다 😄
엔티티와 DTO에 대해 유효성 검증 역할이 적절히 분배된 게 맞나요?

저는 좋아요~! 역할에 따라 꼼꼼히 잘 해주신 것 같습니다!

Comment on lines 58 to 59
String title, LocalDate date, LocalTime time,
String place, int maxPeople, String authorNickname, String description
Copy link
Contributor

Choose a reason for hiding this comment

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

여기서 파라미터 3개,4개로 나누는건 합의가 된 부분인가용?? 나는 동의하지만 반대 의견도 있었어서~!

}
}

private void validateMoimIsFuture(LocalDate date, LocalTime time) {
Copy link
Contributor

Choose a reason for hiding this comment

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

(개취) 메서드명은 validateIsFutureMoim이 조금 더 자연스러운것 같음~! 아니면 다른 validation 메서드처럼 validateDate 형식으로 가도될듯요!

}

private void validateDescription(String description) {
if (description != null && description.length() > DESCRIPTION_MAX_LENGTH) {
Copy link
Contributor

Choose a reason for hiding this comment

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

같은 string인데 authorNickname처럼 isBlank가 아니라 null 체크를 한 이유가 있나용?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

description은 NULL이 가능하기 때문에 위와 같이 처리하였습니다 👍

Comment on lines -55 to +60
"date", "2024-07-19",
"date", date.format(formatter),
Copy link
Contributor

@pricelees pricelees Jul 27, 2024

Choose a reason for hiding this comment

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

테스트 수정까지 꼼꼼하게 했네요👍 formatter 사용도 좋습니다!

(참고)
LocalDate의 toString()은 yyyy-MM-dd가 기본값이라 formatter 없이도 정상적으로 동작할거에요!

@DisplayName("회원을 정상적으로 생성한다.")
@Test
void createMember() {
Assertions.assertDoesNotThrow(() -> Member.builder()
Copy link
Contributor

Choose a reason for hiding this comment

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

전체적으로 Assertions의 static import를 통일해보면 좋겠네요~!

Comment on lines +12 to +25
PAST_DATE_TIME("모임 날짜를 현재 시점 이후로 입력해주세요."),
TITLE_NOT_EXIST("모임 제목을 입력해주세요."),
TITLE_TOO_LONG("모임 제목을 조금 더 짧게 입력해주세요."),
DATE_NOT_EXIST("모임 날짜를 입력해주세요."),
TIME_NOT_EXIST("모임 시간을 입력해주세요."),
PLACE_NOT_EXIST("모임 장소를 입력해주세요."),
PLACE_TOO_LONG("모임 장소를 조금 더 짧게 입력해주세요."),
MAX_PEOPLE_IS_POSITIVE("모임 최대 인원은 양수여야 합니다."),
MAX_PEOPLE_TOO_MANY("모임 최대 인원을 조금 더 적게 입력해주세요."),
AUTHOR_NICKNAME_NOT_EXIST("모임 생성자 닉네임을 입력해주세요."),
AUTHOR_NICKNAME_TOO_LONG("모임 생성자 이름을 조금 더 짧게 입력해주세요."),
DESCRIPTION_TOO_LONG("모임 설명을 조금 더 짧게 입력해주세요."),
MEMBER_NICKNAME_NOT_EXISTS("모임 참여자 닉네임을 입력해주세요."),
MEMBER_NICKNAME_TOO_LONG("모임 참여자 닉네임을 조금 더 짧게 입력해주세요.");
Copy link
Contributor

Choose a reason for hiding this comment

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

저는 백엔드의 예외메시지는 개발자가 예외를 빨리 파악하기 위한 용도라고 생각해요. 현재 메시지는 사용자에게 좋지만, 상태를 표현하는 예외메시지는 개발자에게 좋을 것 같아요. 예를 들어, "모임 참여자 닉네임 길이를 초과했습니다." 처럼요. 이 예외가 발생하는 게 사용자의 입력에 의해 발생하는 건 맞지만 Moim 객체는 어디서 파라미터가 들어오는지 모르니까요!

물론 취향 문제라고도 생각해서 안나 의견 존중합니다~!

나중에 로깅이 추가될때 다시 짚어보면 좋을듯요!

@ss0526100 ss0526100 deleted the branch develop-backend July 27, 2024 13:01
@ss0526100 ss0526100 closed this Jul 27, 2024
@ss0526100 ss0526100 reopened this Jul 27, 2024
Copy link
Contributor

@pricelees pricelees left a comment

Choose a reason for hiding this comment

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

굿~~!!

Copy link
Contributor

@ksk0605 ksk0605 left a comment

Choose a reason for hiding this comment

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

고생했어요 안나~

@Mingyum-Kim Mingyum-Kim merged commit c85a227 into develop-backend Jul 31, 2024
1 check passed
@Mingyum-Kim Mingyum-Kim deleted the feature/#104 branch July 31, 2024 02:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BE 백엔드 관련 이슈입니다. 🌱 기능추가 feature (새로운 기능 구현)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants