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

[5기 남은찬, 박주한] Spring Boot JPA 게시판 구현 미션 제출합니다. #267

Open
wants to merge 18 commits into
base: main
Choose a base branch
from

Conversation

ParkJuhan94
Copy link

@ParkJuhan94 ParkJuhan94 commented Nov 22, 2023

요구 사항

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를 이용해서 문서화한다.



✅ PR 포인트 & 궁금한 점

Copy link

@ordilov ordilov left a comment

Choose a reason for hiding this comment

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

과제 고생하셨습니다~!!

전반적으로 깔끔하게 작성해주신 점이 좋았습니다.
아쉬웠던 부분은 유효성 검사가 다 빠져있는데 이 부분은 조금 고민해보시면 좋을 것 같습니다!


@LastModifiedDate
@Column(columnDefinition = "TIMESTAMP")
private LocalDateTime updatedDate;
Copy link

Choose a reason for hiding this comment

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

질문) createdDateprotectedupdatedDateprivate으로 선언하신 이유가 있을까요?

Choose a reason for hiding this comment

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

왜 이렇게 선언돼있는지 모르겠네요 .. 실수한 것 같아요 수정하겠습니다!


@LastModifiedDate
@Column(columnDefinition = "TIMESTAMP")
private LocalDateTime updatedDate;
Copy link

Choose a reason for hiding this comment

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

질문) ZonedDateTime,Instant 대신 LocalDateTime을 사용하신 이유가 있을까요?

Choose a reason for hiding this comment

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

솔직히 특별한 이유 없고, 자바에선 항상 LocalDateTime 만 사용해와서 사용했습니다

Choose a reason for hiding this comment

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

지나가다가 발견해서 코멘트 남깁니다.
면접에서 자바8 기능에 대하여 종종 물어볼 수도 있어서 참고해보시면 좋을 것 같아요
https://min103ju.github.io/java/localdatetime/


@LastModifiedDate
@Column(columnDefinition = "TIMESTAMP")
private LocalDateTime updatedDate;
Copy link

Choose a reason for hiding this comment

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

LocalDateTime 필드의 네이밍을 ~Date로 선언하신 이유가 있을까요?
LocalDate 필드를 사용할 일이 있을 때 변수명을 보고 datetime 값을 가지고 있는지 date 값을 가지고 있는지 확인하기 어려울 것 같습니다.

Copy link
Author

Choose a reason for hiding this comment

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

넵 정확한 네이밍 으로 수정하겠습니다!


@LastModifiedDate
@Column(columnDefinition = "TIMESTAMP")
private LocalDateTime updatedDate;
Copy link

Choose a reason for hiding this comment

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

요구사항은 created_atcreated_by를 만드는 걸로 기억하고 있는데 created_by는 빠진 이유가 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

넵 빠트린 created_by 필드도 추가해야겠습니다

@RequiredArgsConstructor
public class BoardException extends RuntimeException {

private final String message;
Copy link

Choose a reason for hiding this comment

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

질문) RuntimeException을 상속받았으면 message를 인자로 전달해줄 수 있는데 다시 정의하신 이유가 있을까요?

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.

테스트는 잘 작성해주셨는데 AsciiDoc 다른 파일들 (index.adoc 등?)은 구현 안하셨을까요?


public static Post getPost(Long id) {
Post post = getPost();
ReflectionTestUtils.setField(post, "id", id);
Copy link

Choose a reason for hiding this comment

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

get을 하는데 안에서 그 값을 설정하는게 어색한 거 같은데 어떻게 생각하시나요?
setField 를 사용할 땐 사용하는게 맞나 구조적으로 한 번 더 고민해보시면 좋습니다.

Comment on lines +58 to +71
Post post = PostFixture.getPost(1L);
User user = UserFixture.getUser();
CreatePostRequest request = new CreatePostRequest(post.getTitle(), post.getContent(), 1L);

given(postRepository.save(any(Post.class)))
.willReturn(post);
given(userRepository.findById(1L))
.willReturn(Optional.of(user));

//when
Long result = postService.createPost(request);

//then
assertThat(result).isEqualTo(1L);
Copy link

Choose a reason for hiding this comment

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

테스트 픽스처 생성 부분에서 어색한 부분이 있습니다!
Post를 생성하는 테스트인데 픽스처에서 Post를 미리 만들어야할까요?

테스트하려는 부분도 어색한 부분이 있습니다.
ID는 유일성을 보장하는 값으로, 기존에 없는 값으로 어떤 값이 나오게 될지 모를텐데 ID가 일치하는지 확인해도 될까요?
실제로 유저가 게시글을 만들 때 잘 생성되었는지 확인하고 싶을 때, ID가 예상한대로 나왔구나 생각할지 고민해보시면 좋습니다.

@Transactional
@AutoConfigureMockMvc
@ExtendWith(RestDocumentationExtension.class)
public abstract class ApiTestSupport extends TestContainerSupport {
Copy link

Choose a reason for hiding this comment

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

질문) ApiTest를 하는데 TestContainer 설정을 넣으신 이유가 궁금합니다.
ApiTest를 할때마다 데이터베이스가 꼭 필요할까요?

Copy link

Choose a reason for hiding this comment

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

local은 datasourcemysql을 쓰고, 기본은 testcontainers를 쓰는게 살짝 profile 분리가 어색해 보입니다.

Copy link

@hoon-space hoon-space 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 +21
public class BaseEntity {

@CreatedDate
@Column(columnDefinition = "TIMESTAMP")
protected LocalDateTime createdDate;

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.

앗 맞는말인거같아요! 수정하겠습니다

Comment on lines +23 to +24
@Column(columnDefinition = "TIMESTAMP")
private LocalDateTime updatedDate;

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.

넵 한번 적용해보겠습니다

Comment on lines +29 to +32
public ResponseEntity<Long> savePost(@RequestBody CreatePostRequest request) {
Long postId = postService.createPost(request);

return ResponseEntity.ok(postId);

Choose a reason for hiding this comment

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

응답 값만 보아서는 어떤 값이 리턴이 되는 지 알기 어렵습니다.
객체화해서 그 객체 필드에 postId 를 전달하는 것이 명확할 것 같아여

Choose a reason for hiding this comment

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

이 부분에 대해서 솔직히 id 를 응답할 필요가 없을거같아서 리턴 타입 자체를 불린으로 바꾸려 하는데, 불린에 대한 응답도 한번 객체화하는게 좋을까요?

Choose a reason for hiding this comment

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

불린은 괜찬을듯

}

@GetMapping
public ResponseEntity<PostListResponse> getPosts(@PageableDefault Pageable pageable) {

Choose a reason for hiding this comment

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

@PageableDefault 를 사용하면 어떤 효과가 있나여 ??

Choose a reason for hiding this comment

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

상세한 sizepage 에 대해서 특별한 요구사항을 정하지 않아서 페이징 정보를 받지 않았을 때, 기본으로 @PageableDefault 에서 지정되는 디폴트 값으로 설정하려고 사용했습니다!

Comment on lines +27 to +30
public Post(String title, String content, User user) {
this.title = title;
this.content = content;
this.user = user;

Choose a reason for hiding this comment

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

정적팩토리메서드 패턴을 바꿔도 좋을 것 같아요

Comment on lines +5 to +6
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public record CreatePostRequest(

Choose a reason for hiding this comment

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

fieldVisibility = JsonAutoDetect.Visibility.ANY 는 어떤 역할을 하나요 ????

Choose a reason for hiding this comment

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

정말 이유를 모르겠지만 @RequestBody 에서 레코드를 역직렬화를 못해서 지정했습니다
항상 이상없이 잘 돼왔었는데... 일단 급한대로 해당 어노테이션으로 해결했습니다

Comment on lines +5 to +6
public record PostListResponse(
Page<PostResponse> responses

Choose a reason for hiding this comment

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

reponses 보다는 postInfos 혹은 posts 정도가 나은 것 같아아요

Comment on lines +11 to +12
@Table(name = "users")
public class User extends BaseEntity {

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.

user 가 mysql 에서 이미 사용중인 네이밍이라 테이블 이름으로 사용이 안되는거로 알고있어서 복수형으로 선언했습니다!

Choose a reason for hiding this comment

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

아하 이미 사용중인 네임이였군여, 어쩐지 대부분 MEMBER, ACCOUNT 이런식으로 테이블 이름을 사용하더라고요 ㅎㅎㅎ

</encoder>
</appender>

<logger name="io.undertow" level="OFF"/>

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.

Copy link
Author

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.

아하 다른거 써보시길 기대했는데 아니였구만요 ㅋㅋㅋ


import java.nio.charset.StandardCharsets;

@SpringBootTest

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.

controller 로 단위테스트로 할 수 있었는데, 컨트롤러 테스트 하는 겸 통합테스트를 한번 수행하려고 @SpringBootTest 를 사용했습니다
현재 성공 케이스에 대해서만 통합테스트로 진행하는데 이런식으로 간단한 통합테스트만 진행할바에 단위테스트로 진행하는게 좋을까요?

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

4 participants