최종 프로젝트는 3레이어드 아키택쳐를 이용한 프로젝트를 진행했다
발단(순환 참조를 해결하자)
Controller, Service, Repository 계층을 이용했다.
이때 우리는
다른 도메인의 Entity를 찾아와야할 때 해당 도메인의 Service에서 예외처리까지 마쳐서 Entity를 얻는 메서드를 public으로 열어두도록 컨벤션을 정했다.
여기에는 장/단점이 존재한다.
장점은
- 코드 중복을 줄일 수 있다. Repository에서 Optional로 Entity를 가져올 때 예외처리를 해줘야 한다. 이를 각 도메인에서 만들어서 public으로 열어두면 다른 도메인의 서비스에서 사용할 수 있어 코드중복을 피할 수 있다.
단점은
- Service끼리 참조를 하기 때문에 순환 참조에 걸릴 수 있다.
우리의 프로젝트에서는 Topster와 연관관계를 맺고 있는 Like를 생성할 때 Topster연관 관계를 맺어주기 위해 LikeService에서는 TopsterService를 참조한다.
그리고 TopsterService에서 API 응답을 할 때 Like의 상태를 보기 위해 Like를 확인해야한다. 따라서 TopsterService도 LikeService를 참조한다. 순환참조에 걸렸다.
해결방법
1. Service를 참조하지 말고, Repository를 참조하는게 어떤가? 트레이드 오프를 하자. 코드 중복은 어쩔수 없이 일어나겠지만, 순환참조를 막을 수 있기 때문이다.
2. 연관관계에서 1:N관계에 있을 때 1만 N을 바라보도록 로직을 변경한다.
Topster 1:N Like 관계에 있을 때 TopsterService에서는 LikeService를 가질 수 있지만 LikeService에서는 TopsterService를 참조할 수 없도록! 이방식이 튜터님들이 해주신 이런 문제에 있을 때의 해결방법이였다.
그렇다면 따라오는 질문. Like를 생성할 때는 그럼?? LikeService는 Topstser를 찾아올 수가 없는데?
-> Like를 TopsterService에서 만들어줘야된다. -> 그럼... LikeService는 뭐하지...? 왜 자신의 도메인 Entity를 자기가 생성하지 않고 다른 도메인의 Service에 의존해야 할까? -> 방향을 바꾸자.
Topster 1:N Like 이런 관계에서 방향을 뒤집었다. Topster는 더이상 Like를 참조하지 않는다. Like는 Topster를 참조한다.
마찬가지로 댓글과 게시글 관계도 이 방향으로 만들었다. 그렇다면 이런 방향의 참조는 어떤 문제를 야기할까?
1:N관계에서 1의 위치에 있는 Entity가 더이상 하위 Entity에 대한 정보를 불러올 수가 없다. 근데... 이거 잘생각해보면 그냥 각 도메인의 서비스에 대한 분리가 더 잘 되는거 아닌가?? 라는 생각이 든다. 아직 내 개념에서는 1의 위치한 Entity가 N의 관계에 있는 Entity를 굳이 가져올 필요가 없다. N의 관계의 Service에서 API를 따로 만들어서 공급하면 된다.
Topster에서 Like에 대한 정보가 필요할 땐 그냥 Like 정보를 가져오는 API를 따로 호출해서 가져오면 된다.
아직도 조금 이해가 어렵다. 튜터님들은 우리의 생각과는 다른 방향의 참조 방향이 맞다고 하셨다. 그런데 그런 식이라면 1의 위치한 도메인의 책임이 너무 과해진다고 생각한다.
Facade Pattern
위와 같은 어려움을 겪고 test코드를 작성하던 중 겪었던 트러블 슈팅이다.
Topster는 Album과 N:M 관계를 맺는다. 이를 풀어 주기 위해 TopsterAlbum이라는 중간 테이블을 뒀다.
Topster를 하나 생성할 때 최대 9개의 엘범이 들어간다. Topster 생성 로직을 설명해보자면 이렇다.
DTO로부터 받아온 AlbumList가 우리 RDB에 있는지 확인한다. RDB에 있으면 Entity를 가져오고, 없으면 Entity를 만들어서 save를 한다. 찾아온(생성된) Album 1:N TopsterAlbum을 매핑을 해준다.
매핑된 TopsterAlbumList를 Topster에 연관관계를 맺어주고 TopsterAlbum과 Topster를 save 한다.
Topster Entity 하나를 저장하는데 3개의 Entity가 같이 움직인다.
이 서비스 하나 Test하는데 굉장히 어려웠다. 스터빙 해줘야 하는 부분도 많고, 코드가 너무 길어졌다.
이러한 구조 동작에는 무리가 없다. 하지만 변경에 너무 취약하다.
Album저장 방식이 변경되면? TopsterService에서 Album 저장 방식을 바꿔줘야 한다. 너무 강한 결합을 맺고 있어서 이런 결합을 풀어줘야 된다. 그래야 추후에 유지/보수에도 유연하게 대처할 수 있게 된다.
TopsterCreateFlowService를 추가했다.
이는 각 도메인의 Entity는 각 도메인의 Service에서 생성한다. 그리고 각 Entity의 연관관계를 TopsterCreateFlowService에서 연관관계를 맺어준다. 이런 방식의 장점을 보자.
1. 각 도메인의 Service는 이제 더이상 다른 도메인의 Service 의존하지 않는다. 각 도메인의 Entity를 생성해서 TosterCreateFlowService에 올려주면 된다.
2. TestCode 쉽다. 각 도메인의 Service는 test를 간단하게 할 수 있어졌다. 그리고 TopsterCreateFlowService도 test가 어렵지 않았다. 스터빙도 간단했다.
3. 앞으로 이렇게 한 흐름을 갖는 기능들은 이렇게 묶어서 관리하면 모듈간 결합을 낮출 수 있어진다.
인생은 트레이드 오프 단점을 보자.
1. 3레이어 아키택쳐가 깨졌다. 이는 Controller와 Service사이에 하나의 계층이 더 추가된것.
이 레이어가 추가돼서 잃는 단점은 무엇일까?우선은 클래스가 늘어나니까 클래스 구조가 복잡해진다.
'공부 > 트러블슈팅' 카테고리의 다른 글
TestSuiteExecutionException 오류(수정) (0) | 2024.01.06 |
---|---|
스프링 내부 객체 사용 실수 (0) | 2023.11.22 |
빈 생성 오류 트러블 슈팅 (1) | 2023.11.22 |
[Spring] Could not write JSON: Infinite recursion (StackOverflowError) (0) | 2023.11.16 |
[Spring] DB 연결 실패 (0) | 2023.11.14 |