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

[4기 안재영, 유원우] Spring Boot JPA 게시판 구현 미션 제출합니다. #259

Open
wants to merge 23 commits into
base: 안재영/유원우
Choose a base branch
from

Conversation

JaeyoungAhn
Copy link
Member

요구 사항

SpringDataJPA 를 설정한다.

  • datasource : h2 or mysql

엔티티를 구성한다.

  • 회원(User)
    • id (PK) (auto increment)
    • name
    • age
    • hobby
    • created_at
    • created_by
  • 게시글(Post)
    • id (PK) (auto increment)
    • title
    • content
    • created_at
    • created_by
  • 회원과 게시글에 대한 연관관계를 설정한다.
    • 회원과 게시글은 1:N 관계이다.
  • 게시글 Repository를 구현한다. (PostRepository)

API를 구현한다.

  • 게시글 조회
    • 페이징 조회 (GET "/posts")
    • 단건 조회 (GET "/posts/{id}")
  • 게시글 작성 (POST "/posts")
  • 게시글 수정 (POST "/posts/{id}")

REST-DOCS를 이용해서 문서화한다.

👩‍💻 구현 내용

init : 프로젝트 초기 설정 관련 파일 추가

feat : BaseEntity 및 Custom Audit 기능 추가

feat : Post 및 User 엔티티 추가

feat : Post 및 User JpaRepository 추가

feat : User Service 클래스

feat : User Service 레이어 내 Converter 및 DTO

feat : UserNotFoundException 추가

feat : Post 서비스 클래스

feat : Post 서비스 레이어 관련 Converter 및 DTO

feat : PostNotFoundException 추가

feat : User 컨트롤러 추가

feat : User 컨트롤러 레이어 Convert 및 DTO 추가

feat : Post 컨트롤러 클래스 추가

feat : Post 컨트롤러 레이어 Converter 및 DTO 추가

feat : Global Exception Handler 추가

feat : User, Post 요청 관련 .http 파일 추가

test : REST Docs 설정

test : 테스트 설정 파일 추가

test : User 컨트롤러 내 MockMVC 이용 통합 테스팅 및 REST Docs 문서화

test : Post 컨트롤러 내 MockMVC 이용 통합 테스팅 및 REST Docs 문서화

docs : index.adoc 작성을 통한 REST Docs 문서화

import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
Copy link

@young970 young970 Aug 8, 2023

Choose a reason for hiding this comment

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

해당 Repository의 구현체의 SimpleJpaRepository클래스를 보면 클래스 레벨로 Transactional(readOnly = true)가 이미 붙어 있어 저는 현재의 PostService에서 굳이 클래스 레벨에 Transactional(readOnly = true)을 달아줄 필요가 없다고 생각합니다.

혹시 다른 개발자들이 해당 메소드들이 읽기 전용이라는 것을 쉽게 알기위해 명시해 놓은 것일까요??
아니면 제가 모르는 다른것이 있는걸까요??

Copy link

Choose a reason for hiding this comment

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

영운님께서 말씀해주셔서 SimpleJpaRepositoryTransactional(readOnly = true)이 들어있다는 것을 알게 되었습니다!
감사합니다
한데, 그래도 ServiceRepository는 각 계층으로 보는 게 맞아 붙여주는 게 맞지 않을까 생각합니다.
SimpleJpaRepositoryTransactional(readOnly = true)이 되어 있어 ServiceTransaction을 처리해주지 않게 되면
Repository가 변경되었을 때 문제가 발생할 것 같습니다!

Choose a reason for hiding this comment

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

저는 Service 계층에 @ Transactional을 붙이는 것이 좋다고 생각합니다!
repository의 코드 내에 존재하는 @ Transactional은 repository 메소드 내에서 특정 기능을 실행할 때 유효하다고 생각합니다.
예를 들어, 서비스 코드 내에 SimpleJpaRepository에 구현되어있는 메소드가 아닌 것을 실행하는 등 예외적인 상황이 충분히 발생할 수 있을 것 같아요.
그것 외에도 repository까지 확인하지 않고도 Service단에 명시해준다면 협업할때도 좋을 듯 합니다 :)

Copy link

@young970 young970 Aug 9, 2023

Choose a reason for hiding this comment

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

생각해보니 OSIV가 꺼져있다면 find관련 메서드에서 LAZY 로딩을 할 수 없게 되는 이유도 있겠군요!!
Service 계층에 @ Transactional(readOnly = true)을 붙이는게 좋을 것 같습니다!!ㅎㅎ

public PostDetailedResults findAllWithPagination(Pageable pageable) {
Page<Post> retrievedPosts = postRepository.findAll(pageable);
if (retrievedPosts.getSize() == 0) {
throw new PostNotFoundException("Post가 존재하지 않습니다.");
Copy link

Choose a reason for hiding this comment

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

저는 해당 경우를 예외로 처리하지 않는 것이 낫다고 생각합니다.
프론트 입장에서 단순히 비어있는 조회 결과를 받는다면 빈 화면을 출력해주면 되지만
404 응답을 받는다면 그에 맞게 처리를 해주어야 하기때문에 비효율적이라고 생각합니다.

혹시 원우님과 재영님의 생각은 어떠신가요??

Copy link

Choose a reason for hiding this comment

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

저는 예외처리하는게 좋다고 생각해요.

404 응답이 아니라, 204 응답이 더 어울리는 상황인 것 같다는 생각이 가장 우선이고,

프론트엔드에서도 빈 화면일 때 표시할 화면이 있다면, 사이즈를 확인하는 것보다, 단순히 상태코드를 보고 분기처리하는 것이 더 효율적일 것 같아요.

Copy link

@wonu606 wonu606 Aug 9, 2023

Choose a reason for hiding this comment

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

두 분 말씀이 모두 맞다고 생각합니다.
재영님께서는 200과 204 모두 좋은 방법이라 생각하시고 다수의 의견에 따라가는 것이 좋지 않을까 생각하십니다.
그리고
저는 세영님처럼 204를 날려 front에서 nocontent.html, 200일 때 list.html을 구분할 수 있게 해주는 것이
좀 더 바람직하지 않을까 싶습니다.

결론

저희에 코드는 틀렸고, 두 분 말씀이 맞음

Choose a reason for hiding this comment

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

약속만 잘되어있으면 뭐든 됩니다.

Choose a reason for hiding this comment

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

http 400대 안내려다주는 글로벌 일류 기업도 있어요. 다만 그들만의 약속은 잘 정의되어 있는거죠.


@Transactional
public PostShortResult update(Long id, PostDetailedParam postDetailedParam) {
userService.validateUserExistence(postDetailedParam.userId());
Copy link

@young970 young970 Aug 8, 2023

Choose a reason for hiding this comment

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

저는 validateUserExistence() 메서드 보다 userService.findByid() 메서드를 만들어 응용하는 것이 더 좋다고 생각합니다!!
추후 기능 확장으로 user를 id로 검색하는 요구사항이 생길시 userService의 findByid() 메서드를 재사용 할 수 있을테니까요ㅎㅎ

Copy link

@wonu606 wonu606 Aug 9, 2023

Choose a reason for hiding this comment

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

userService.validateUserExistence()을 따로 만들기보다

UserDto dto = userService.findByid(userId);

이 형태 맞으실까요?
이것이 재활용면에서는 더 좋을 것 같습니다.

PostShortResponse response = converter.toPostShortResponse(result);
return ResponseEntity
.status(HttpStatus.CREATED)
.body(response);
Copy link

Choose a reason for hiding this comment

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

혹시 헤더의 Location에 생성된 post의 URI가 들어가나요??
들어가지 않는다면 이왕 201로 응답하는거 한번 해보시면 좋을거 같습니다!!ㅎㅎ

Copy link

@wonu606 wonu606 Aug 9, 2023

Choose a reason for hiding this comment

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

URI에 대해서는 고민해보지 못했었습니다.
감사합니다 201이니 영운님 말씀대로 URI을 넣어주는 게 더 좋다고 생각합니다!


return ResponseEntity.badRequest().body(errors);
}

Copy link

@young970 young970 Aug 8, 2023

Choose a reason for hiding this comment

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

서버에서 발생할 수 있는 모든 예외를 잡아서 핸들링하는 것이 좋을거 같습니다!!

prgrms-be-devcourse/springboot-basic#857

관련해서 예전에 제가 맨토님께 받았던 피드백 입니다!!ㅎㅎ

Copy link

Choose a reason for hiding this comment

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

감사합니다! 잡아주는 것이 더 바람직하다고 생각합니다.👍

Copy link

@onetuks onetuks left a comment

Choose a reason for hiding this comment

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

재영님, 원우님
과제 고생하셨습니다.

고민 많이 하신 부분이 어느정도 정해졌나보네요.

그간 보지 못한 것들을 과감히 시도하시는게 좋아보여요.

코드 읽는게 재밌었습니다. 고생하셨습니다! 😄👍

@@ -0,0 +1,92 @@
@rem
Copy link

Choose a reason for hiding this comment

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

Gradlew 류 파일은 빌드하면 생겨서, gitignore 로 커밋 제외시키는 걸 권장한다고 해요.

Copy link

Choose a reason for hiding this comment

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

윈도우 시스템에서 gradle 없을 때 실행해주는 파일로 알고 있는데 제외해주는 게 나을까요?
gradle이 없는 경우가 발생하진 않을 거 같긴 하지만 혹시 몰라 넣어주었습니다.
이게 요즘 트렌드일까요?

Comment on lines +20 to +25
{
"title": "Updated Title2",
"content": "Updated Content2",
"userId": 1
}

Copy link

Choose a reason for hiding this comment

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

관리하기 어렵지 않으신가요?
PostMan 이라는 프로그램을 사용해보세요!

Comment on lines +11 to +12
@Component
public class CustomAuditAware implements AuditorAware<String>, DateTimeProvider {
Copy link

Choose a reason for hiding this comment

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

커스텀 Auditor 도 좋은 방법이네요!

Comment on lines +21 to +22
consumes = APPLICATION_JSON_VALUE,
produces = APPLICATION_JSON_VALUE)
Copy link

Choose a reason for hiding this comment

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

와! 이건 생각 못했다!

Copy link

Choose a reason for hiding this comment

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

요청이랑 응답의 데이터 타입을 이런 방식으로 강제할 수 있었군요!!
덕분에 좋은 정보 알아갑니다!!ㅎㅎ


import java.util.List;

public record PostDetailedResponses(List<PostDetailedResponse> list) {
Copy link

Choose a reason for hiding this comment

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

findAll에 사용하는 응답은 Pageable이 거의 필수라고 합니다.
List 대신 Page를 감싸는 wrapper도 시도해보시면 좋을 것 같아요!

Copy link

Choose a reason for hiding this comment

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

그렇군요!! 감사합니다!
다시 한번 찾아보겠습니다

public PostDetailedResults findAllWithPagination(Pageable pageable) {
Page<Post> retrievedPosts = postRepository.findAll(pageable);
if (retrievedPosts.getSize() == 0) {
throw new PostNotFoundException("Post가 존재하지 않습니다.");
Copy link

Choose a reason for hiding this comment

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

저는 예외처리하는게 좋다고 생각해요.

404 응답이 아니라, 204 응답이 더 어울리는 상황인 것 같다는 생각이 가장 우선이고,

프론트엔드에서도 빈 화면일 때 표시할 화면이 있다면, 사이즈를 확인하는 것보다, 단순히 상태코드를 보고 분기처리하는 것이 더 효율적일 것 같아요.

Comment on lines +4 to +6
public UserNotFoundException(String message) {
super(message);
}
Copy link

Choose a reason for hiding this comment

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

메시지 만으로는 해당 예외가 어떤 상황인지 분석하는게 어렵다고 생각해요.
디버깅 기능을 잘 활용하려면, checked-exception이 발생한 원인을 같이 확인할 수 있도록 하는게 좋을 것 같아요.

Copy link

Choose a reason for hiding this comment

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

UserNotFoundException(message, exception);

이렇게 말씀이실까요?
세영님 코드 참조해보았을 때 enum으로 묶어두면 더 깔끔할 것 같습니다.

@@ -0,0 +1,5 @@
package com.prgrms.board.user.controller.dto;

public record UserShortResponse(Long id) {
Copy link

Choose a reason for hiding this comment

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

혹시 아이디를 따로 감싸신 이유가 있을까요?
저도 이 문제로 한참 고민했는데, 명확한 답을 내리지는 못했습니다.

Copy link

Choose a reason for hiding this comment

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

당장에는 id만 필요하지만 추후 변경이 일어날수도 있다 생각하여 감싸놓게 되었습니다.

Copy link

Choose a reason for hiding this comment

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

추후변경까지 생각하시다니 대단쓰


@Test
@Order(2)
@DisplayName("[findById] 단건 조회")
Copy link

Choose a reason for hiding this comment

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

이 컨벤션 좋은 것 같습니다.

실패한 테스트 추적에 아주 요긴할 것 같아요.

Comment on lines +4 to +5
username: ${DB_PASSWORD}
password: ${DB_PASSWORD}
Copy link

Choose a reason for hiding this comment

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

이런 방법도 있었네요.

이 방법도 보안을 위한 방법으로 볼 수 있을까요?

Copy link

Choose a reason for hiding this comment

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

옙 그렇습니다!! 😁
저희가 생각하기에 보안을 위한 방법이
세영님께서 알려준 관리툴 방법, 위와 같이 환경변수로 받는 방법 2가지 있다고 생각하였습니다.

그 중 좀 더 간편하다고 생각되는 환경변수 방법을 채택하였습니다.

Copy link

@young970 young970 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 +21 to +22
consumes = APPLICATION_JSON_VALUE,
produces = APPLICATION_JSON_VALUE)
Copy link

Choose a reason for hiding this comment

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

요청이랑 응답의 데이터 타입을 이런 방식으로 강제할 수 있었군요!!
덕분에 좋은 정보 알아갑니다!!ㅎㅎ

Copy link

@KimMinheee KimMinheee 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 +20 to +22
@RequestMapping(value = POST_REST_URI,
consumes = APPLICATION_JSON_VALUE,
produces = APPLICATION_JSON_VALUE)

Choose a reason for hiding this comment

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

API로 JSON을 주고받는 컨트롤러라면 이렇게 선언하는 것이 좋을 것 같네요! 하나 배워갑니다 :)

Choose a reason for hiding this comment

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

GET인데 consumes가 필요가 있을까요?
@SY97P @KimMinheee @young970

Comment on lines +20 to +21
@Column(name = "user_id", nullable = false)
Long userId;

Choose a reason for hiding this comment

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

userId로 간접참조 좋은 방식 인 것 같습니다!:)👍🏼👍🏼

Choose a reason for hiding this comment

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

이거 간접참조할꺼면 Reply 추가해보시지 그러셨어요 ㅎㅎ

import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)

Choose a reason for hiding this comment

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

저는 Service 계층에 @ Transactional을 붙이는 것이 좋다고 생각합니다!
repository의 코드 내에 존재하는 @ Transactional은 repository 메소드 내에서 특정 기능을 실행할 때 유효하다고 생각합니다.
예를 들어, 서비스 코드 내에 SimpleJpaRepository에 구현되어있는 메소드가 아닌 것을 실행하는 등 예외적인 상황이 충분히 발생할 수 있을 것 같아요.
그것 외에도 repository까지 확인하지 않고도 Service단에 명시해준다면 협업할때도 좋을 듯 합니다 :)

Comment on lines +20 to +21

private String hobby;

Choose a reason for hiding this comment

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

hobby는 nullable 가능하도록 의도하신걸까요?? :)

}

@PostMapping
public ResponseEntity<PostShortResponse> create(@RequestBody @Validated PostDetailedRequest request) {

Choose a reason for hiding this comment

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

@ Valid가 아닌 @ Validated를 사용하신 이유가 궁금합니다!
추가로 @ Validated를 사용한다면 controller 단에서 발생하는 MethodArgumentNotValidException가 아닌 ConstraintViolationException 예외가 발생하게 되는데 현재 프로젝트의 ExceptionAdvice에서는 MethodArgumentnotValidException을 핸들링하고 있어 이 부분을 확인해보시면 좋을 것 같습니다!

@ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<List<String>> handleValidationExceptions(MethodArgumentNotValidException exception) {
        List<String> errors = exception.getBindingResult().getAllErrors().stream()
                .map(DefaultMessageSourceResolvable::getDefaultMessage).toList();

        return ResponseEntity.badRequest().body(errors);
    }

Choose a reason for hiding this comment

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

원래 둘다선언이 필요함.

Choose a reason for hiding this comment

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

short과 detail 로 응답 요청을 이분화해서 나누기보다 각각의 사용에 맞게 하나씩 만들어서 쓰셔요. 귀찮아도 습관들여야 합니다. 저도 귀찮은거 알아요ㅠㅠ 근데 short detail 중간정도 싸이즈 되는거 나타나면 결국 short은 뚱뚱해지고 detail이랑 똑같게 됩니다. 그러느니 필요한것만 정의해서 내보내셔요.
지금이야 화면 스펙이 없어 추상적으로 정리한것이지만 나중에 프로젝트에서도 이렇게 요청/응답 공통으로 쓰고있으면 안됩니다. 분명 필드하나 늘리고 api 문서 갱신 안되고 예상치못하게 어디 응답 key값 달라져서 프론트에서 dm오고 그래요.


@Test
@Order(1)
@DisplayName("[create] 저장")

Choose a reason for hiding this comment

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

저도 예전에 과제하면서 받았던 피드백인데요!
저희가 맞춰두었던 테스트 코드 네이밍 컨벤션에 맞추면 추가 설명이 필요하게 된다고 생각하는데
그때 DisplayName에서 설명을 할 때 "~ 한다"라는 문장으로 작성하면 좋을 것 같습니다.

Copy link
Member

Choose a reason for hiding this comment

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

테스트 코드의 DisplayName은 문서화를 진행한다 생각하고 더 구체적으로 작성하는 것이 좋을 것 같아요 🐣

Copy link

@WooSungHwan WooSungHwan left a comment

Choose a reason for hiding this comment

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

아무래도 바우처에서 한번 빡시게 잡았더니 확실히 좀 괜찮은 코드로 나오네요 ㅎㅎ 하지만 짚고 넘어가야할 부분 많으니까 리뷰내용 참조하시구요!
그리고 테스트코드 너무 없습니다. controller 테스트코드 뿐만아니라 service 각각 request 파라미터들도 테스트 해보셔야 합니다. 그리고 service 실패 테스트까지 넣어야해요.

이게 갖춰지면 approve 드릴거에요~

Comment on lines +34 to +39
public ResponseEntity<List<String>> handleValidationExceptions(MethodArgumentNotValidException exception) {
List<String> errors = exception.getBindingResult().getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage).toList();

return ResponseEntity.badRequest().body(errors);
}

Choose a reason for hiding this comment

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

이럴거면 List 형태가 아닌 표준 응답을 정의하는게 좋을 것 같네요. 대표적으로 ErrorResponse 같은 이거 응답 바디가
{"메시지"} 이럴 것 같은데 해당 json으로 �화면단 개발하는 개발자들도 생각해줍시다. ㅎㅎ

Comment on lines +20 to +22
@RequestMapping(value = POST_REST_URI,
consumes = APPLICATION_JSON_VALUE,
produces = APPLICATION_JSON_VALUE)

Choose a reason for hiding this comment

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

GET인데 consumes가 필요가 있을까요?
@SY97P @KimMinheee @young970

}

@PostMapping
public ResponseEntity<PostShortResponse> create(@RequestBody @Validated PostDetailedRequest request) {

Choose a reason for hiding this comment

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

원래 둘다선언이 필요함.

produces = APPLICATION_JSON_VALUE)
public class PostController {

public static final String POST_REST_URI = "api/posts";

Choose a reason for hiding this comment

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

이거 왜뺀건가요? 그리고 앞에 / 붙여주세요.
/api/posts 스프링이 만들어주는거 알고있지만 controller 내에서 path 경로의 시작은 슬래쉬(/) 로 시작해야합니다.


import static com.prgrms.board.post.controller.PostController.POST_REST_URI;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;

Choose a reason for hiding this comment

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

개인적으로는 적절한 header 셋팅이 필요하다고 생각되는데 이러면 너무 전역적으로 선언되어 불필요한 헤더값을 요구할 수 있을 것 같아요. 적절한 곳에 전역으로 선언해야 합니다.

public PostDetailedResults findAllWithPagination(Pageable pageable) {
Page<Post> retrievedPosts = postRepository.findAll(pageable);
if (retrievedPosts.getSize() == 0) {
throw new PostNotFoundException("Post가 존재하지 않습니다.");

Choose a reason for hiding this comment

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

http 400대 안내려다주는 글로벌 일류 기업도 있어요. 다만 그들만의 약속은 잘 정의되어 있는거죠.

import jakarta.validation.constraints.*;

public record UserDetailedRequest(
@NotBlank

Choose a reason for hiding this comment

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

validation 메시지 없네요.

Comment on lines +26 to +30
public User(String name, Integer age, String hobby) {
this.name = name;
this.age = age;
this.hobby = hobby;
}

Choose a reason for hiding this comment

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

not null 검증하는 로직 있으면 좋을듯 합니다.

Comment on lines +25 to +29
public UserShortResult save(UserDetailedParam param) {
User savingUser = converter.toUser(param);
User savedUser = repository.save(savingUser);
return converter.toUserShortResult(savedUser);
}

Choose a reason for hiding this comment

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

이 서비스가 controller가 아닌 다른데 노출되게 된다면...
user 는 150살도 들어가고 말거에요. 현재 코드상으론.

Choose a reason for hiding this comment

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

service domain이 분명히 가져야할 validation을 web 계층에서 막았다고 생각하시면 안됩니다.

}

@Test
@Order(1)

Choose a reason for hiding this comment

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

순서 빼고 진행하세요.


@Column(name = "title", length = 100, nullable = false)
String title;

Copy link
Member

Choose a reason for hiding this comment

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

@Column의 nullable 속성과 @NotNull의 차이가 무엇이 있을까요?
만약에 내부적으로 new 연산자를 통해서 null인 상태의 title을 만들게 된다면 어떻게 될까요?


@Test
@Order(1)
@DisplayName("[create] 저장")
Copy link
Member

Choose a reason for hiding this comment

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

테스트 코드의 DisplayName은 문서화를 진행한다 생각하고 더 구체적으로 작성하는 것이 좋을 것 같아요 🐣

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

Successfully merging this pull request may close these issues.

None yet

7 participants