[ 프로그래머스 백엔드 데브코스 ] JPA 게시판 과제 회고

2022. 12. 21. 01:45프로젝트

더보기

프로그래머스 백엔드 데브코스를 진행하면서 계산기, 바우처, A-Z 프로젝트를 해왔는데 이걸 진행하면서 회고를 제대로 작성하지 못해서 이번 과제에선 회고를 꼭 작성하겠다는 마음과 팀원들과의 약속으로 작성하게 되었다.

Github Repository

https://github.com/twotwobread/springboot-board-jpa

JPA 게시판 과제 설명

해당 과제는 JPA를 이용하여 정말 간단한 게시판 서비스 구현에 관한 과제였다.

과제에서의 요구 사항은

  1. SpringDataJPA 설정 -> datasource : h2 or mysql.
  2. 엔티티(User, Post) 구성 -> 주어진 엔티티의 자세한 정보 존재.
  3. 회원과 게시글에 대한 연관관계 설정 -> 1:N 관계
  4. 게시글 Repository 구현
  5. API 구현 -> 세부적인 URL 정보 존재.
    • 게시글 조회 ( 페이징 조회, 단건 조회 )
    • 게시글 작성
    • 게시글 수정
  6. REST-DOCS 이용한 문서화.

간단한 요구 사항이 주어졌고 거기에 맞춰서 과제를 진행했다.

 

과제 진행 중 직면했던 문제, 생각

1. 서비스 계층에서 컨트롤러로의 반환 데이터 타입을 Entity 객체 그대로 해도 괜찮을까?

: 이 부분은 사실 많은 개발자분들이 고민을 하는 부분이라고 생각을 한다. 필자는 Dto로 반환하는게 더 좋다고 생각하고 인자로 들어올때도 Dto로 들어와서 서비스에서 Entity로의 변환해주는게 좋을 것 같다. 이유는 아래와 같다.

  1. 트랜잭션 내에서만 entity를 다루어서 예상치 못하게 entity 값이 변하는 경우가 사라진다.
  2. Entity를 반환해줄 때, lazy loading 전략을 사용한다고 하면 내부 필드로 가지고 있는 연관관계가 있는 객체의 값에 대해 getter 사용 시 프록시 객체로 존재하여 예외가 발생할 수 있다.
    • 서비스에서 Entity를 반환받았기에 프록시 객체로써 존재하는데 트랜잭션이 끝나버린 것이다. 그리고 그 후에 프록시 객체를 가지고 오려고 하니까 예외가 발생한다.
    • https://kafcamus.tistory.com/31, 해당 문제의 해결방법에 대해선 이 글을 참고하면 좋을 것 같다.

이런 추가적인 처리를 방지하고자 Dto를 반환하는 것이 더 좋다고 생각한다.

 

2. Entity의 id를 GenerateValue를 붙였는데 생성자에 id를 넣는 경우가 생기게 만들어도 될까?

: 사실 생성자에서 id를 넣게 만들게 되는 이유가 테스트를 편하게 하기 위해서 이런 경우가 발생했는데 이는 역시 안된다고 생각을 한다. 테스트를 좀 더 쉽게 짜는 부분을 생각해서 코딩을 하는 것은 좋지만 역전되어서 테스트만을 위한 코드가 존재하는 것은 아니라고 생각을 한다.

 

3. Response Dto에 어떤 값들이 들어가는 것이 좋을까?

: 이 부분은 조회 기능을 통해서 사용자에게 응답 값을 줘야하는데 그때 해당 Dto에는 어떤 데이터를 담아서 보내줘야할까? 라는 고민이었다. 게시글 내부에 있는 모든 정보를 다 담아주기엔 너무 많은 정보를 주는 것 같다고도 느껴서 이런 고민을 했었다.

그래서 페이징을 이용한 모든 게시글의 목록을 조회하면 게시글 ID, 게시글의 제목, 게시글 생성일자 정도만을 반환하고 단건 조회의 경우엔 게시글을 클릭한 경우를 생각해서 위 내용에 게시글의 본문, 생성자 ID, 생성자 이름에 관한 정보도 더 포함시켰다.

이런 부분들을 더 잘 설계하고 계획하기 위해선 먼저 UI 상에서 어떤 식으로 보여지길 기대하는지를 생각해보는 것이 좋을 것 같다. 그리고 어떤 기능들이 배치될 것인지를 생각하다 보면 어떤 정보가 필요하겠다는 것이 그려지는 것 같다. 이런 방법을 다음 프로젝트에서 적용해보고 싶다.

 

4. Controller에서 변환시에 ResponseEntity로 dto를 감싸는게 좋을지 아니면 dto 그대로 반환하는게 좋을지에 대한 궁금증.

: 이 부분은 명확히 잘 모르겠지만 그래도 나의 생각을 말해보자면 ResponseEntity를 이용한다면 dto 그대로를 담아서 반환할 것 같고 ResponseEntity를 이용하면 Reponse를 위한 범용 Dto를 만들고 isOk()와 같은 메서드를 이용해서 Http 상태 코드를 담아서 줄 수 있게 만들 것 같다. 이렇게 처리를 했을 때 더 세부적인 정보를 줄 수 있다고 생각이 들고 강사분들 두 분의 코드를 보게 되었는데 범용 Dto를 만들어서 처리를 하는걸 보았고 일단 그렇게 해보면서 장단점을 생각해봐야겠다는 느낌이 들었다.

 

5. Dto에서 Entity로, Entity에서 Dto로 변환을 위한 Convert 클래스를 만들었는데 이걸 static 유틸 클래스 형태로 만드는 것이 좋을지 빈으로 등록해서 의존성 주입을 통해 사용하는 것이 좋을지에 대한 궁금증.

: 정말 용도에 따라 다른 것 같다. Util 클래스로 만든다는 것은 응집도가 떨어져도 메서드들을 모아두기 위해서 만든다고 생각을 하는데 빈으로써 등록하기 위해서 클래스를 구성하는 것은 응집도가 좋아야 한다고 생각을 해서 상황에 따라 다르게 사용할 것 같다. 그리고 만약 Util 클래스처럼의 구현을 원한다면 엔티티마다 변환되어야 하는 작업이 있다고 느껴지고 비지터 패턴을 이용해보는 것도 가능하다는 생각이 들어서 다음 프로젝트에서 생각을 해보면 좋을 것 같다.

 

6. @Builder라는 어노테이션을 어떤 경우 사용하면 좋을까?

: 이 부분은 https://johngrib.github.io/wiki/pattern/builder/ 해당 링크를 참고하면 좋을 것 같다. @Builder라는 어노테이션를 사용하는 이유가 가독성 때문이라고 생각한다. 그럼 가독성이 어떨 때 떨어질까를 생각해보면 엔티티의 필드가 너무 많은 경우라는 생각이 들어서 엔티티의 필드가 너무 많은 경우 혹은 final이 아닌 필드가 많아서 생성자가 너무 많아지는 경우 사용하면 좋을 것 같다는 생각이 든다.

 

7. WebMvcTest 어노테이션과 SpringBootTest 어노테이션의 차이점이 뭘까?

:  springboottest : 전체 애플리케이션 컨텍스트를 시작한다.

webmvctest : 웹 레이어와 관련된 빈들만 이용해서 애플리케이션 컨텍스트를 생성한다.

 

⇒ springboottest는 애플리케이션의 웹, 서비스 및 데이터 계층을 포함하는 통합 테스트를 생성할 때 사용하는 것이 좋다.

⇒ webmvctest는 웹 mvc 계층 (컨트롤러와 같은)의 통합 테스트 혹은 단위 테스트를 생성해야 하는 경우에 사용하는 것이 좋다. @Component, @Service, @Repository 등의 어노테이션은 건너뜀.

 

8. String 필드에 @Max를 이용하여 Validation 처리를 했는데 예외가 발생했다.

: @Max, @Min 은 value 값을 long으로 받길래 뭔가 이상하다고 느꼈다. 범위에 관한 내용도 아니고 String 길이를 알아서 찾아서 비교하나? 이런 생각이 들었는데 역시 예외가 발생했고 @Size(min = xxx, max = xxx) 로 수정했다.

 

9. Entity 필드 값에 primitive 타입의 필드가 존재해도 될까? 존재해도 된다면 어떤 경우에 쓰면 좋을까?

: 진짜 숫자로의 연산만을 하는 경우 수량에서 +1, -1의 연산만 하는 경우엔 primitive 타입이 더 좋다. Wrapper에서 이런 연산을 적용하면 primitive로 변환하고 이런 연산이 수행되어야 해서 변환이라는 과정이 하나 더 생긴다.

그럼 Wrapper는 언제 필요할까? null 값이 들어갈 수 있는 경우에 사용하는 것이 좋다라고 결론을 내렸다. null이 허용되지 않는 경우라면 primitive 타입을 사용할 것 같다.

 


느낀 점

먼저, 팀원들과 페어 프로그래밍을 통해서 이번 과제를 진행했는데 진짜 도움이 많이 되었다. A-Z 프로젝트에서 Controller에서 반환 시에 ViewResolver를 이용하게 구성해서 반환 타입을 어떤 식으로 해줘야 하고 controller를 어떻게 구성해야할 지 감이 잘 안잡혔다. 팀원들과 페어 프로그래밍을 통해서 감을 잡았던 것 같다. 그리고 진짜 재밌게 코딩했던 것 같다.

그리고 이번 프로젝트에서 JPA와 Rest Docs를 처음 써봤는데 JPA는 진짜 편하지만 내부적으로 동작하는 방식을 정확히 이해해야 하고 공부를 깊게 해봐야겠다고 느낀게 어떤 에러가 발생해도 추상화된 부분이 많아서 이해도 없이는 이유를 알기 힘들 것 같다는 생각을 많이 했다. 예를 들어서 트랜잭션 어노테이션을 붙였는데 find 메서드 수행 시에 쿼리가 날라가는 이유가 내부적으로 jpql을 사용하기 때문이라는 부분과 같은 맥락이다. Rest Docs는 문서와 코드 간의 동일성을 보장하기 좋긴 하지만 적용하기에 불편하고 힘들었다. => https://techblog.woowahan.com/2597/ 이부분에 대한 글을 읽어보면 좋을 것 같다. 힘들긴 했지만 mockMvc, mocking에 대해서 연습을 할 수 있어서 좋았던 것 같다.

회고를 작성하는 것과 프로젝트 하면서 직면했던 문제나 생각들을 즉시 써놓는 것 이 두가지는 꼭 해야겠다고 느낀 것이 해당 문제, 생각에 대한 리마인드가 된다는 점이 일단 너무 좋은 것 같다. 그리고 이 리마인드를 해당 글을 읽을 때마다 할 수 있다는 것. 되게 도움이 될 것 같다.

마지막으로 페어 프로그래밍을 처음 해봤는데 속도는 좀 더디지만 참고할 수 있는 예시를 볼 수 있다는 점과 다른 사람들의 생각을 들을 수 있다는 점 그리고 재미있다는 점에서 앞으로도 하면 좋을 것 같다는 생각이 들었다. 하지만 시간적 여유가 없다면 포기하는 것이 맞겠다는 생각또한 들었다. 해당 페어 프로그래밍을 진행하면서 TDD라는 걸 시도해봤는데 원숭이책에서 공부했던 방식 그대로를 적용해보았다. 먼저 구현부에 기능별 메서드의 틀을 잡고 거기에 Unsupported 예외를 던져서 예외가 터지게 만들고 테스트를 전부 구현 후 구현부를 코딩했는데 어떤 식으로 짜야할지에 대한 생각과 어떤 기능을 가질 지에 대한 부분이 정리되어서 좋았는데 익숙하지 않아서 이 역시 속도가 더 느려지는 느낌이 들었다,,,