Skip to content

[1주차] 멀티모듈 구성 및 영화목록 조회 API 개발#4

Open
kimbro97 wants to merge 17 commits intohanghae-skillup:mainfrom
kimbro97:1week_kimbro97
Open

[1주차] 멀티모듈 구성 및 영화목록 조회 API 개발#4
kimbro97 wants to merge 17 commits intohanghae-skillup:mainfrom
kimbro97:1week_kimbro97

Conversation

@kimbro97
Copy link

[1주차] 멀티모듈 구성 및 영화목록 조회 API 개발


작업 내용

  • api, application, domain 3개로 멀티모듈 구성
    • 모듈구성은 README.md 파일에 자세히 작성했습니다.
  • DB 테이블 설계
    • 테이블을 설계전 도메인부터 작업 후 Entity로 맵핑했습니다.
  • 상영 중인 영화 조회 API 개발
  • docker compose를 이용해서 DB 구성

발생했던 문제와 해결 과정을 남겨 주세요.

문제 1

  • application 모듈에서 domain 모듈을 의존하고 있지만 JpaRepository를 찾을 수 없는다는 에러가 있었습니다.

해결방법

  • domain 모듈의 jpa 의존성을 implementation -> api로 변경해서 해결할 수 있었습니다

문제 2

  • fetch join으로 N+1 문제를 해결할려고 하였으나 Movie Entity에 @onetomany로 맵핑된게 2개 이상일때 MultipleBagFetchException 예외가 발생하는 문제였습니다

해결방법

  • List가 아니 Set 자료구조로 변경하여 MultipleBagFetchException를 해결했습니다

이번 주차에서 고민되었던 지점이나, 어려웠던 점을 알려 주세요.

  • 이번 프로젝트를 통해서 멀티 모듈을 구성해봤는데 너무 어렵웠습니다. 블로그마다 모듈을 나누는 기준도 다르고 모듈마다 @SpringBootApplication이 있어야 한느건지도 의문이고 application.yml파일은 어떤식으로 관리해야할지도 잘 모르겠습니다.

리뷰 포인트

리뷰어가 특히 의견을 주었으면 하는 부분이 있다면 작성해 주세요.

  • 모듈을 알맞게 구성한건지 궁금합니다.
  • domain 설계를 잘했는지 궁금합니다.

기타 질문

추가로 질문하고 싶은 내용이 있다면 남겨주세요.

  • domain 모듈에서 jpa를 implementation -> api로 변경했는데 이렇게되면 application 모듈도 jpa를 의존하는걸로 생각되는데 그럼 모듈을 분리하는 의미가 있는지 궁금합니다.

@soonhankwon
Copy link

soonhankwon commented Jan 11, 2025

안녕하세요 형재님! 1주차 리뷰어를 맡게된 권순한입니다!
바쁜 와중에 여러모로 고민하신 흔적이 보여서 좋습니다.
열심히 리뷰해보겠습니다 🔥


private final MovieService movieService;

@GetMapping("/")

Choose a reason for hiding this comment

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

API url이 restful하게 작성되면 좋을 것 같습니다 :)

Copy link
Author

Choose a reason for hiding this comment

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

/movies로 변경했습니다!

private final MovieService movieService;

@GetMapping("/")
public List<MovieResponse> getMovieList() {

Choose a reason for hiding this comment

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

리턴 타입이 List인데 메서드 이름에 또 List를 붙이는 것은 개인적으로는 불필요하다고 생각하는 편입니다 :)

Copy link
Author

Choose a reason for hiding this comment

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

메서드명에서 List를 삭제했습니다


private static List<String> createTheaters(Movie movie) {
return movie.getMovieTheaters().stream()
.map(movieTheater -> movieTheater.getTheater().getName())

Choose a reason for hiding this comment

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

map을 쓸 때 메서드 레퍼런스를 활용한 방법도 소개해드립니다.

.map(MovieTheater::getTheater)
.map(Theater::getName)

Copy link
Author

Choose a reason for hiding this comment

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

메서드 레퍼런스를 활용해서 가독성을 향상시켰습니다


@Getter
public class MoviesServiceResponse {
public static List<MovieResponse> of(List<Movie> movies) {
Copy link

@soonhankwon soonhankwon Jan 11, 2025

Choose a reason for hiding this comment

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

불필요한 변수 a, forEach 와 같은 부분은 제거하고 바로 stream을 통해서 리턴해주는게 가독성 면에서 좋아보입니다 :)

Copy link
Author

Choose a reason for hiding this comment

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

급하게 개발하다보니까 나중에 수정한다는걸 깜빡했습니다
stream을 이용해서 코드를 개선했습니다

my-db:
container_name: theater_mysql
image: mysql
environment:

Choose a reason for hiding this comment

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

Timezone, Charset 에 대한 부분이 없어서 조금 아쉽습니다. 현업에서 많은 버그를 만들어내는 부분이기도 해서요 :)

Copy link
Author

Choose a reason for hiding this comment

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

mysql 초기설정시 Timezone, Charset 부분을 알아보고 중요성을 알게되었습니다
docker-compose.yml에 설정추가했습니다!

}

@Override
public boolean equals(Object object) {
Copy link

@soonhankwon soonhankwon Jan 11, 2025

Choose a reason for hiding this comment

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

equals and hashcode 재정의한 이유가 특별하게 있으셨나요?

Copy link
Author

Choose a reason for hiding this comment

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

Movie 클래스에서 Screening, MovieTheater Set 자료구조를 사용하고있습니다.
Set 자료구조를 오류없이 사용하려먼 equals and hashcode를 재정의해서 사용해야하는걸로 알고있어서 사용했습니다

import java.util.List;

public interface MovieRepository extends JpaRepository<Movie, Long> {
List<Movie> findAllByOrderByReleaseDateDesc();
Copy link

@soonhankwon soonhankwon Jan 11, 2025

Choose a reason for hiding this comment

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

OrderByReleaseDateDesc 대신 Sort를 사용하면 가독성, 메서드 재사용성, 유연성이 개선될것 같습니다 😃

Copy link
Author

Choose a reason for hiding this comment

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

Sort를 사용하여 외부에서 값을 받아 정렬할 수 있도록 수정했습니다!

import static org.assertj.core.api.Assertions.*;

@Transactional
@SpringBootTest
Copy link

@soonhankwon soonhankwon Jan 11, 2025

Choose a reason for hiding this comment

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

DataJpaTest 를 사용해서 불필요한 부분은 로드하지 않도록 테스트를 만드는게 좋을것 같다고 생각합니다.
결국 테스트라는게 자주 돌려야하고 속도가 중요해지는 시점이 현업에서 꼭 옵니다.(eg ci/cd시 테스트 프로세스가 들어가있다면 빌드속도 영향)

  • 지극히 개인적인 관점으로 의견을 드리면 JPA repository에 대한 테스트는 JPA에서 이미 보증하고 있다고 생각합니다.
  • 도메인, 서비스에 대한 테스트 그리고 통합테스트(integration test)가 훨씬 중요하다고 생각하는 편입니다.

Copy link
Author

Choose a reason for hiding this comment

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

피드백 감사합니다!
앞으로 테스트 작성 시 이러한 관점을 참고해서 더 개선된 방향으로 진행하도록 하겠습니다.

@@ -0,0 +1,52 @@
-- Movie 테이블 생성
CREATE TABLE movie (
id BIGINT AUTO_INCREMENT PRIMARY KEY,

Choose a reason for hiding this comment

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

BIGINT 말고 unsigned int형을 사용하면 좋았을 것 같습니다. 불필요한 DB 리소스 낭비라고 생각됩니다.

Copy link
Author

Choose a reason for hiding this comment

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

unsigned int을 사용하기위해 entity에 @column(columnDefinition = "INT UNSIGNED") 사용했습니다!

updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
movie_id BIGINT NOT NULL,
theater_id BIGINT NOT NULL,
FOREIGN KEY (movie_id) REFERENCES movie(id) ON DELETE CASCADE,

Choose a reason for hiding this comment

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

실무에서는 FK 참조 무결성으로 인한 성능, 스키마 변경, 데이터 이관 및 데이터 클렌징에 대한 편의성 때문에 FK 를 걸지 않는 경우가 많습니다 :)

  • 데이터의 무결성이 아주 중요한 서비스는 아니므로 FK constraint를 삭제하는 것도 고려해주시면 좋겠습니다.

Copy link
Author

Choose a reason for hiding this comment

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

JPA를 사용하면 연관관계를 설정할 경우 기본적으로 외래 키(FK)가 자동으로 생성됩니다.
그렇다면, 스키마 생성 시 외래 키 제약 조건(FK)이 적용되지 않도록 설정할 방법이 있을까요?

Choose a reason for hiding this comment

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

jpa hibernate ddl-auto 를 validate 나 none으로 설정하시고 DDL은 직접 데이터베이스에서 실행하셔도 됩니다 :)

@soonhankwon
Copy link

soonhankwon commented Jan 11, 2025

리뷰 포인트

Readme에 구성과 설명을 잘 정리해주셔서 좋았습니다! :)

  • 모듈을 알맞게 구성했는지?

    • 현재 모듈간 의존성을 보면 api → application, application → domain으로 되어있습니다.
    • “도메인 모듈”이 불필요한 의존을 하지 않게 잘 구성하셨습니다.
    • 모듈을 구성하면서 아키텍처에 대해서 고민을 많이 하셨을텐데 제 의견을 말씀드리자면, 레퍼런스마다 아키텍처를 다르게 설명하고 있는 것에 대한 이유는 심플하게도 각자의 상황이 다르기 때문입니다.
    • 하지만 아키텍처의 본질은 저는 “제약”이라고 생각합니다. 어떠한 모듈은 “독립적”이고 “변화에 유연”해야 한다고 생각되면 이 모듈은 최대한 의존성이 없도록 “제약”을 걸어주는 거죠.
    • 하지만 아키텍처 때문에 정말 중요한 본질인 “비즈니스 요구사항, 생산성”을 저하된다면 심플한 아키텍처로 진행하는 것이 더 좋습니다.
    • Application 모듈에서 트랜잭션 관리, 비즈니스 로직을 구현하신 부분도 적합해 보입니다.
  • domain 설계를 잘했는지?

    • 요구사항을 만족하는 설계를 대체적으로 잘 하신 것 같습니다 🙂

기타 질문

  • domain 모듈에서 jpa를 implementation -> api로 변경했는데 이렇게되면 application 모듈도 jpa를 의존하는걸로 생각되는데 그럼 모듈을 분리하는 의미가 있는지 궁금합니다.
    • 맞습니다! JPA에 대한 의존성은 대체적으로 끊기가 어렵습니다. 하지만 Domain 객체와 영속성 엔티티를 분리, DIP를 활용하면 도메인에 대한 독립성을 최대한 보장할 수 있습니다.
    • 하지만! 결국 “관리 포인트”가 늘어나게되고 실제로 무의미할 수 도 있고 생산성만 저하시킬 수 있습니다. 아키텍처는 정말 “조직의 상황” 그리고 “미래에 대한 예상”을 최대한 생각해서 설계해야한다고 생각합니다.
    • 따라서 남은 주차에서 계속해서 자신의 프로젝트에 맞는 합리적인 아키텍처를 고민하고 적용해보는 경험을 가지면 좋겠습니다.

Choose a reason for hiding this comment

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

불필요한 래퍼클래스라고 보이는데 특별하게 여기서 변환하는 이유가 있을까요?

@kimbro97 kimbro97 closed this Jan 15, 2025
@kimbro97 kimbro97 reopened this Jan 15, 2025
@phyeran phyeran changed the base branch from main to kimbro97 January 17, 2025 02:07
@spartacontents spartacontents deleted the branch hanghae-skillup:main January 17, 2025 03:20
@phyeran phyeran changed the base branch from kimbro97 to main January 19, 2025 05:59
dbdb1114 added a commit to dbdb1114/redis_1st that referenced this pull request Jan 27, 2025
[ 2주차 잔여 ] Feature/caching 캐싱 테스트 완료
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants