[Spring] Package Structure

민찬기·2022년 10월 19일
0

몇 개의 프로젝트를 하며 패키지 구조에 대해 느낀 바를 정리해보고자 한다.

패키지 구조

이번 프로젝트 구조를 살펴보기에 앞서서 다른 구조도 쓱 살펴보자.

기존의 프로젝트 (계층형)

└─ src
    └── main
         └── java
              └── com/example/project
                   ├── controller
                   |	└── dto
                   ├── service
                   ├── repository
                   │    ├── entity
                   │    └── repository
                   ├── config
                   ├── util
                   ├── exception
                   └─ ProjectApplication.java

계층형 구조는 controller -> service -> repository 로 연결되는 계층을 패키지에도 반영한 것이다. MVC 패턴의 유산(?)이라고 해야 될까.

프로젝트에 대한 이해가 낮아도 전체 구조를 빠르게 파악할 수 있다는 게 장점이라는데, 와닿지는 않는다. '서비스에 대한 이해 없이 구조를 파악해야 할 일이 있나?' 의문도 들고, 무엇보다 하나의 디렉토리에 많은 클래스 파일이 모인다는 특징 때문에 되려 구조 파악이 어렵지 않나 생각한다.

(남의 코드를 많이 안 읽어봐서 잘 모르는 거 같다)

최근 프로젝트 (도메인형)

└─ src
    └── main
         └── java
              └── com/example/project
                   ├── post
                   |	├── controller
                   |	|	 └─ dto
                   |	|		 ├── request
                   |    |        └── response
                   |	├── service
                   |	├── repository
                   |    └── util
                   ├── user
                   ├── exception
                   ├── config
                   ├── jwt
                   └─ ProjectApplication.java

가장 최근에 진행했던 프로젝트에서는 도메인형을 쓰되, 그 내부 구조는 일반적인 계층형 구조를 따랐다. 일반적이라고 함은 controller, service, repository의 구조인데, 일반적이라기보단 강의자료에서 흔하게 보았던 구조다.

도메인형의 장점은 관련성 있는 코드들끼리 모여있다는 장점이 있다. 최근에 도메인 주도 개발(DDD) 등이 떠오르면서 대두되는 것이 '관심사'인데, 도메인형 구조는 이를 충실하게 반영하고 있다.

이번 프로젝트 (도메인형)

길고 길었던 서론이 끝났다. 사실 빌드업이 제대로 된 거 같지도 않지만, 이번 포스팅해서 하고 싶은 얘기는 지금부터다.

└─ src
    └── main
         └── java
              └── com/example/project
                   ├── member
                   |	├── api
                   |	|	 ├── MemberAuthenticationApi
                   |	|	 ├── MemberJoinApi
                   |	|	 ├── MemberFindApi
                   |    |    └── MemberUpdateApi                   
                   |	├── application
                   |	|	 ├── MemberComponent
                   |	|	 ├── MemberFindService                   
                   |	|	 ├── MemberJoinService                   
                   |	|	 ├── MemberModifyService
                   |    |    └── MemberSecurityService                         
                   |	├── config                   
                   |    |    └── SecurityConfig                   
                   |	├── domain
                   |	|	 ├── Auth       
                   |    |    └── Member
                   |	├── repository                 
                   |    |    └── MemberRepository                   
                   |	├── dto
                   |	|	 ├── request
                   |    |    └── param
                   |    └── util
                   |         └── MemberUtil                         
                   |
                   ├── post
                   |    └── ...  
                   ├── mail
                   |    └── ...                     
                   ├── exception
                   |
                   └─ ProjectApplication.java

이번에 프로젝트 구조를 위와 같이 시도한 이유는 Spring Guide - Directory를 읽고 나름대로 적용해보고자 했기 때문이다.

⭐️ Controller -> API

사실 포스트를 작성하면서 든 생각인데, thymeleaf를 사용하는 이번 프로젝트에는 controller라는 네이밍이 더 맞지 않았나 생각이 든다.

Controller와 API는 Rest API 구현 여부에 있다. MVC 패턴에서는 Controller가 View Resolver에 반환하는 역할을 했는데, 근래의 Rest API에서는 View가 아닌 HTTP Response Body를 반환한다. 이러한 차이에서 Controller보다 API라는 네이밍이 더 적절하다고 판단했다.

[참조]

Controller와 RestController의 차이

⭐️ Service -> Application

Service Directory의 이름을 변경한 건, Controller가 그 자체의 의미로 인해 변경한 것과는 달리 그 내부의 구성 때문이다. Service 로직을 구현하다보면, 코드를 분리하고자 Util 클래스나 Component, Config를 별도로 만든다. 그렇다면 이들을 어디에 위치시킬 것인지가 문제다. Util과 Component, Configuration의 특징은 아래와 같다.

Util : Component와 달리 빈 객체에 등록될 필요가 없는 로직
Component : 외부 라이브러리 사용 없이 개발자가 직접 작성한 클래스를 빈에 등록할 때
Config : 외부 라이브러리 사용 or 내부 메서드를 @Bean 등록하는 경우

Service 어노테이션이 Component 어노테이션의 세부라는 점만 봐도, Component는 그 성질이 Service와 크게 다르지 않다. Component를 어디에 위치시킬 것인가를 고민했을 때, 일반적으로 데이터를 실어 나르는 컨트롤러(API)단과 로직을 구현하는 서비스단 중, Component는 로직을 구현하므로 Service단이 더 적절해 보인다.

그런데 OOOOComponent 클래스를 service Directory 안에 넣기가 뭔가 어색해서 application이라는 이름을 쓰기로 했다.

(결론은 구색 맞추기란 소리)

[참조]

스프링 @COMPONENT, @CONFIGURATION BEAN 생성과 차이
@Component vs @Configuration - @Bean (in @Configuration) / @Bean (in @Component): Lite Mode 비교

⭐️ Repository와 Domain의 분리

사실 Repository와 Domain을 '왜 분리했지?' 생각하기 보다는, 기존 프로젝트에서 둘을 '왜 같은 폴더에 뒀을까?'를 고민해야 하지 않을까 싶다. 유추해보자면, Entity가 Repository에 쓰이는 객체이므로 둘을 같은 Directory에 두지 않았을까 싶지만..

아 참고로, Domain Directory에는 Entity를 비롯하여 권한 설정을 위한 Enum 등이 저장되고, Repository에는 Interface가 포함된다.

⭐️ DTO 네이밍

DTO 네이밍을 어떻게 할 지가 근래의 큰 고민이었다. Request와 Response로 이원화하여 (외부 -> Controller -> Service) 방향으로는 RequestOOOO를, 반대 방향으로는 ResponseOOOO을 사용하였다. 그러나, 특정 테이블을 다른 테이블에서 참조해야 하는 경우도 생기면서, ResponseDTO를 요청에 사용하는 경우도 생기게 되었다.

Response라는 이름이 더이상 적절치 않다는 것을 느끼게 되고, 어떤 이름을 사용해야 될까 찾아보다가, inflearn 영한님 답변을 보게 됐다. 그래서 요청 DTO의 경우 Request라는 네이밍을 그대로 사용하고, 그 외의 계층간 양방향 이동이 예상되는 경우에는 OOOOParam이라는 네이밍을 사용하기로 했다.

결론

디렉토리 구조는 정해진 게 없다. 사실 도메인 구조가 더 좋다고 생각은 하지만, 프로젝트의 규모가 작고 도메인 분류가 애매한 경우에는 계층형 구조가 더 좋을 수 있다.

또한 정답이 없이, 더 적절한 대안이 있을 수 있는 가능성이 재밌는 부분이고, 그래서 이런저런 괜찮아 보이는 구조들을 발견하면 계속 시도해보고자 한다.

profile
https://github.com/devmizz

0개의 댓글