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 게시판 구현 미션 제출합니다. #223

Open
wants to merge 20 commits into
base: hyukgon
Choose a base branch
from

Conversation

Curry4182
Copy link

@Curry4182 Curry4182 commented Jul 31, 2023

📌 과제 설명

강병곤, 최준혁 spring boot jpa 게시판 구현하여 제출 합니다.

👩‍💻 요구 사항과 구현 내용

  • 게시글 조회
    • 페이징 조회 (GET "/posts")
    • 단건 조회 (GET "/posts/{id}")
  • 게시글 작성 (POST "/posts")
  • 게시글 수정 (POST "/posts/{id}")
  • REST-DOCS를 이용해서 문서화한다.

✅ PR 포인트 & 궁금한 점

  • post 테스트에 getPosts 메서드에서 페이지네이션 응답 결과를 문서화하기 위해 slice 객체를 다음과 같이 작성하였는데 이런 식으로 하는게 맞는지 궁금합니다! (767a062)
    -> 팀 미팅 때 답변 해주셔서 반영하고 있습니다!
  • user와 post 문서를 포함하는 index에서 같은 폴더에 있는 문서 결과를 표시해야 하는데 include 관련해서 오류가 나서 나중에 직접 여쭤 보겠습니다! (e0ffc36)

@Curry4182
Copy link
Author

Curry4182 commented Jul 31, 2023

Rest docs 작성 파일 입니다!

게시글 문서

post-1
post-2
post-3
post-4
post-6
post-7

사용자 문서

user-1
user-2

Comment on lines 121 to 135
List<PostResponseDTO> postList = Arrays.asList(
PostResponseDTO.builder()
.title("title3")
.content("content3")
.createdAt(LocalDateTime.of(2000, 1, 1, 1, 1))
.modifiedAt(LocalDateTime.of(2000, 1, 1, 1, 1))
.build(),

PostResponseDTO.builder()
.title("title4")
.content("content4")
.createdAt(LocalDateTime.of(2000, 1, 1, 1, 1))
.modifiedAt(LocalDateTime.of(2000, 1, 1, 1, 1))
.build()
);

Choose a reason for hiding this comment

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

반복해서 사용하는 내용들이라면 beforeEach를 만들어서 넣고 사용해도 좋을 것 같아요~

Copy link
Author

Choose a reason for hiding this comment

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

PostResponseDTO를 공통적으로 생성해서 그 부분을 setUP메서드에 넣었습니다~! a9824bc

Comment on lines 94 to 96
Long postId = 1L;

when(postService.findPost(postId)).thenReturn(responseDTO);

Choose a reason for hiding this comment

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

PostId 를 직접 지정해서 사용하는 것보다 anyLong을 사용해보는 건 어떤가요??

Copy link
Author

Choose a reason for hiding this comment

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

고민 해보았는데 입력한 postId를 컨트롤러에서 동일하게 찾는지 테스트 해보기 위해서 postId를 계속 사용할려고 합니다!

import lombok.Getter;

@Getter
@AllArgsConstructor

Choose a reason for hiding this comment

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

저는 최근부터 AllargsConstructor를 지양하는 방향으로 생각해서 사용하고 있는데요, 그 이유에 대해서도 한번 생각해보면 좋을 것 같아요!

Copy link
Author

@Curry4182 Curry4182 Aug 5, 2023

Choose a reason for hiding this comment

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

저도 AllargsConstructor가 일으키는 버그에 대해 검색해 보고 AllargsConstructor를 사용하지 않도록 전체적으로 수정 하였습니다! f74b8d9

@NoArgsConstructor
public class Post extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)

Choose a reason for hiding this comment

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

(strategy = GenerationType.AUTO) default라 생략해도 될 것 같아요 의도하신거라면 패쓰

Comment on lines 9 to 11
private String title;
private String content;
private Long userId;

Choose a reason for hiding this comment

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

개인적으로 생각했을때, 필드를 final로 선언하지 않으면 데이터의 조작가능성이 생길 수도 있어서, Dto객체는 final로 선언하는 것이 좋다고 생각합니다 ! 어떻게 생각하실까요 ?!

Copy link
Author

Choose a reason for hiding this comment

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

음 저도 다시 생각 해보니 그렇게 생각해서 Dto 객체를 record 클래스로 변경 하였습니다! f74b8d9

Comment on lines 34 to 35
@Lob
private String content;

Choose a reason for hiding this comment

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

Lob 어노테이션은 처음 알아가네요 ! 배워갑니다 👍

Copy link
Member

Choose a reason for hiding this comment

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

p2;
@Lob는 내부에서 추가로 별도의 조회를 하여 느릴 수도 있다고 합니다. 컬럼 타입을 text 로 직접 명시해서 사용하는 것은 어떤가요~?

참고

this.content = content;
}

public void setUser(User user) {
Copy link

Choose a reason for hiding this comment

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

p3
요즘 setter의 사용에 관심이 많은데요, 한 번에 메소드의 사용의도를 파악하기 힘든 setUser보다는,
협업에서도 사용 가능한 메소드이니 allocate / attach User같은 의미있는 메소드명은 어떠실까요 ?!

Copy link
Author

@Curry4182 Curry4182 Aug 5, 2023

Choose a reason for hiding this comment

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

set 대신 의도를 쉽게 파악할 수 있도록 수정하였고 사용하지 않는 setter도 전체적으로 삭제하였습니다~! c7107c4

Comment on lines 48 to 53
if (Objects.nonNull(this.user)) {
this.user.getPosts().remove(this);
}

this.user = user;
user.getPosts().add(this);
Copy link

Choose a reason for hiding this comment

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

p1
만약 user가 갖고있는 post의 자료형이 list가 아닌 hashMap 등으로 변경되면 setUser 메소드도 수정이 추가되어야 할 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

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

내부 자료형을 모르더라도 추가, 삭제할 수 있도록 수정하였습니다! aac52b4

Comment on lines 50 to 52
public ApiResponse<Slice<PostResponseDTO>> getAllPost(@RequestParam int page, @RequestParam int size) {
Slice<PostResponseDTO> posts = postService.getPosts(page, size);
return ApiResponse.ok(posts);

Choose a reason for hiding this comment

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

p3
slice를 그냥 반환하면 프론트에서 식별하기 힘든 정보들 (ex : Pageable type : "INSTANCE")같은 정보들도 반환되는 것으로 알고 있는데, 프론트단에서 좀 더 알아보기 쉬운 필드만 갖고 있는 Wrapper 클래스를 하나 만들어 주는 건 어떨까요 ?

Copy link
Author

Choose a reason for hiding this comment

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

slice를 그대로 반환하지 않고 클라이언트에 필요한 정보만 반환될 수 있도록 수정하고 restdocs도 수정하였습니다~! 0db39ee

User user = userRepository.findById(createPostRequestDto.getUserId())
.orElseThrow(() -> new RuntimeException("User Not Found!"));

post.setUser(user);

Choose a reason for hiding this comment

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

p3
저는 Post객체에 User는 필수적인 존재라고 생각하여 Post생성자에 user를 넣어주었습니다.
만약 post.setUser(user)메소드를, post를 생성하는 다른 메소드에서 호출하는 것을 깜빡하는 것을 방지하기 위해 생성자 혹은 user세팅을 강제하는 다른 방법을 고안해 보는건 어떨까요 ?!

Copy link
Author

Choose a reason for hiding this comment

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

이 부분은 준혁님과 의논하고 반영하도록 하겠습니다!

Copy link
Author

Choose a reason for hiding this comment

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

Post의 생성자에 User를 넣어도 매핑 메서드를 실행하지 않으면 동일한 문제가 발생한다고 생각합니다.

Comment on lines 41 to 45
@Builder
public Post(String title, String content) {
this.title = title;
this.content = content;
}

Choose a reason for hiding this comment

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

p1
Builder로 Post객체를 생성할 시에, nullable하지 않은 필드에 대한 검증(Notnull)이 이루어지지 않은 것 같습니다.
이는 DB에 영속화 시키기 이전까지 잡기 힘든 에러인것 같은데, 유효성 검증로직을 추가하는 것은 어떨까요 ?!

Copy link
Author

Choose a reason for hiding this comment

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

생성자에서 유효성 검사를 하도록 수정하였습니다! 3f95c3c

Comment on lines +6 to +10
@EnableJpaAuditing
@Configuration
public class JpaAuditingConfiguration {

}

Choose a reason for hiding this comment

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

저는 main 어플리케이션 클래스에 EnableJpaAuditing 어노테이션을 붙여주었는데, Config클래스에 붙이는게 좀 더 맞다는 생각이 드네요 ! 배워갑니다 👍

Choose a reason for hiding this comment

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

성공, 실패에 대한 메소드를 따로 만든 점 좋은 것 같습니다 ! 배워갑니다 👍

Copy link
Member

@Yiseull Yiseull 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 34 to 35
@Lob
private String content;
Copy link
Member

Choose a reason for hiding this comment

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

p2;
@Lob는 내부에서 추가로 별도의 조회를 하여 느릴 수도 있다고 합니다. 컬럼 타입을 text 로 직접 명시해서 사용하는 것은 어떤가요~?

참고

public record PostResponseDto(String title, String content, LocalDateTime createdAt, LocalDateTime modifiedAt) {
@Builder
public PostResponseDto {
}
Copy link
Member

Choose a reason for hiding this comment

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

p5;
@Builder를 record 단에 붙일 수 있는 것으로 압니다! 어노테이션을 저기에 달아주신 이유가 있나여??

Copy link
Author

Choose a reason for hiding this comment

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

이 부분은 몰랐네요 가독성을 위해 수정하였습니다

.orElseThrow(() -> new RuntimeException("Post Not Found!"));

return PostResponseDto.toResponse(retrievedPost);
}
Copy link
Member

Choose a reason for hiding this comment

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

p1;
조회 같은 경우에는 성능을 위해 트랜잭션을 읽기 전용 모드로 사용하시는 것이 어떤가요??

Copy link
Author

Choose a reason for hiding this comment

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

오호 꿀팁 감사합니다~ 수정하였습니다~! abdc8c8

.orElseThrow(() -> new RuntimeException("Post Not Found!"));

retrievedPost.changeTitle(updatePostRequestDto.title());
retrievedPost.changeContents(updatePostRequestDto.content());
Copy link
Member

Choose a reason for hiding this comment

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

p2;
서비스에서 changeTitle, changeContents 를 각각 호출하기 보다 Post 내부에서 처리해주도록 하는 것은 어떤가여? 만약 메소드명이 변경될 경우 서비스까지 영향을 받을 것 같네요~

Copy link
Author

@Curry4182 Curry4182 Aug 7, 2023

Choose a reason for hiding this comment

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

post 내부에서 처리하도록 수정하였습니다~! 54c57af

private Long id;

@Column
private String name;
Copy link
Member

Choose a reason for hiding this comment

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

p5;
컬럼에 따로 nullable 같은 세팅도 없고, 컬럼이라는 것을 명시적으로 표현하기 위해 @Column을 붙여주신 건가요??

Copy link
Author

Choose a reason for hiding this comment

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

전체적으로 유효성 검사를 하도록 수정하였습니다~! 3f95c3c

public static <T> ApiResponse<T> fail(int statusCode, T data) {
return new ApiResponse<>(statusCode, data);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

P4;
ApiSliceResponse와 공통적인 부분이 있는 것 같은데 상속을 활용해보는 것은 어떤가요??

Copy link
Author

@Curry4182 Curry4182 Aug 7, 2023

Choose a reason for hiding this comment

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

컨트롤러에서 전체적으로 ApiResponse를 응답하도록 수정하였습니다! 5171145

Copy link

@born-A born-A left a comment

Choose a reason for hiding this comment

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

준혁님 병곤님! 과제 진행하시느라 수고하셨어요!
다른 팀원분들이 리뷰를 정성스레 달아주셔서 공감하는 부분에 이모지 표시해두었습니다! 👍

private final UserService userService;

@PostMapping
public ApiResponse<UserResponseDto> createUser(@RequestBody CreateUserRequestDto createUserRequestDto) {
Copy link

Choose a reason for hiding this comment

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

@Valid 를 사용해서 dto 검증을 진행하는것도 좋을거 같습니다!

Copy link
Author

Choose a reason for hiding this comment

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

@Valid를 사용하여 검증하도록 수정하였습니다! 3f95c3c

Comment on lines 38 to 44
@Transactional
public Slice<PostResponseDto> getPosts(int page, int size) {
PageRequest pageRequest = PageRequest.of(page, size);

return postRepository.findSliceBy(pageRequest)
.map(PostResponseDto::toResponse);
}

Choose a reason for hiding this comment

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

p1;
offset으로 구현한 걸로 알고 있었는데 이야기해보니 cursor 방식으로 구현하신 건가요?? 설명을 한번 해주시면 감사하겠습니다!

Copy link
Author

Choose a reason for hiding this comment

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

offset 방식으로 구현했는데 그 때 제가 잘 못 말했습니다.

@0923kdh
Copy link

0923kdh commented Sep 24, 2023

라이브 리뷰 완료

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