개발하는 자몽

[Spring] 패키지 구조 설계 본문

Java & Kotlin/Spring

[Spring] 패키지 구조 설계

jaamong 2023. 6. 19. 15:14

이전에 패키지 구조에 관한 글을 쓴 적이 있다.

 

[Spring Boot] 패키지 구조

여러 글을 찾아봤는데 크게 두 가지 방식으로 패키지 구조를 구성한다. 도메인형 패키지 구조 계층형 패키지 구조 개발의 규모나 성격, 회사에서 진행하는 방식에 따라 정해지는 것으로 보인다.

backend-jaamong.tistory.com

요즘 들어 다시 패키지 구조를 생각해 보는데, 이전 글에서 좀 더 변화한 부분이 있어서 아예 새로 글을 쓴다.

 

※이 글은 주관적으로 작성된 글입니다. 틀리거나 추가적으로 보충할 내용이 있다면 댓글로 알려주세요.

 

 

 

이전에 알던 패키지 구조

패키지를 설계할 때 도메인형이든 계층형이든 내부적으로는 크게 변하지 않는 것 같다. 예를 들어 도메인 형이라고 했을 때 User라는 엔티티가 있다고 하자. 그러면 기본적으로 이렇게 설계할 수 있을 것이다. 

└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── spring
    │   │           └── example
    │   │               └── User
    │   │                   ├── controller
    │   │                   ├── domain
    │   │                   ├── service
    │   │                   └── repository    
    │   │                   
    │   └── resources
    │       ├── application-dev.yml
    │       ├── application-prod.yml
    │       └── application.yml

위 패키지 구조는 도메인을 기준으로 하지만 어쨌든 안에 controller, domain 등을 포함하고 있어서 계층형과 크게 다르지 않다는 것을 알 수 있다.

아 이전 포스팅에는 Provider나 Dao 패키지가 있었는데 지금은 쓰지 않는다. 왜냐면 여러 모로 개발을 하거나 글을 읽어보니 대부분 repository나 dao로 작성한다. (참고로 dao와 repository는 다르다. 이에 관한 자세한 내용은 이 링크를 참고해 보자 https://www.baeldung.com/java-dao-vs-repository )

 

위 패키지 구조를 기본으로 알고 있었고 변화될 부분이 있을 거라고 생각해보지 못했다. 하지만 여러 예시를 보다 보니 패키지는 더 세세하게 구분이 가능하다는 것과 프로젝트 마다 효율적인 패키지 구조가 무엇인지 고민해야 함을 알게 되었다.

 

Re: 패키지 구조 with API

이번에도 도메인형을 기준으로 작성하지만, 위에서 언급한 것처럼 도메인형과 계층형은 크게 변하지 않으며 어떤 패키지 구조를 선택하던지 다른 구조로 바꾸는 것은 크게 어렵지 않을 것이다. (바꾸는 것보다는 설계할 때가 어렵다...)

 

잠깐 딴소리 ORM을 많이 사용한다면 아무래도 객체 지향 프로그래밍 관점에서 생각하게 된다. 이러한 관점에서 생각해 보면 JPA를 사용할 땐 도메인형이 더 알맞지 않을까?라고 생각한다.

 

도메인형 패키지 구조

└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── spring
    │   │           └── example
    │   │               │   └── Order
    │   │               │       ├── controller 
    │   │               │       ├── dto
    │   │               │       ├── entity
    │   │               │       ├── exception
    │   │               │       │     └── 커스텀 예외 파일들
    │   │               │       ├── service
    │   │               │       └── repository
    │   │               │             └── repository와 dao 파일들 (jpa라면 거의 repository로만 구성됨)
    │   │               ├── config 
    │   │               │     ├── SwaggerConfig.java
    │   │               │     ├── properties
    │   │               │     └── security
    │   │               └── util // 공통으로 사용하는 소스 코드
    │   └── resources
    │       ├── application-dev.yml
    │       ├── application-prod.yml
    │       └── application.yml

controller, service와 repository

controller, service 패키지에는 모두가 알다시피 controller, service 클래스들이 위치한다. repository 패키지에는 repository 클래스나 dao 클래스가 위치하게 된다. 

 

dto와 entity

entity에는 JPA 사용 시 table로 매핑되는 객체를 넣고, dto는 말 그대로 dto를 넣으면 된다. 만일 응답과 요청 dto를 구분해서 사용한다면 해당 파일들을 이 패키지에 넣으면 된다. 간혹 entity안에 dto 패키지를 넣는 경우도 있는데 그건 프로젝트와 개발자 성향에 따라 정하면 된다. 여기서는 dto와 entity를 구분했다. 

 

exception

exception 패키지에는 커스텀 예외들을 넣으면 된다. 예를 들어 Order id를 조회했는데 찾을 수 없으면 던질 수 있는 커스텀 not found 예외나 invalid 등 이러한 예외들을 넣으면 된다.

 

config

config 패키지에는 애플리케이션에 적용할 configuration 클래스와 각종 설정들이 위치하게 된다. 

 

util

util 패키지에는 프로젝트 전역에서 공통적으로 사용할 기능들을 모아둔다. 예를 들어 숫자를 랜덤하게 만드는 기능, 문자열 처리, 날짜 처리, 보안 등 특정 개념과 독립적인 기능들은 여기에 위치하게 된다.

 

enum

enum은 위 예시에서 보이지 않는데, 해당 enum 클래스와 가장 밀접한 곳에 위치해도 좋고 도메인 패키지 바로 아래에 둬도 된다. 위 예시에서는 order 패키지 바로 아래 위치하게 된다.

 

또 다른 구조 with MVC와 클린 아키텍처

위 형태는 view가 없는 형태인 API를 개발할 때 기본적으로 떠올릴 수 있고 쉽게 찾아볼 수 있는 구조이다. 이번에는 MVC를 기준으로 domainweb 패키지로 나눠보겠다. (이 부분부터는 나도 제대로 아는 것이 아니라 여러 글들을 참고했다.)

 

domain과 web

└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── spring
    │   │           └── example
    │   │               │   └── domain
    │   │               │       ├── item
    │   │               │       │    ├── Item.java
    │   │               │       │    ├── ItemService.java
    │   │               │       │    └── ItemRepository.java
    │   │               │       ├── login
    │   │               │       └── member
    │   │               └── web
    │   │                    ├── item
    │   │                    │    ├── form //dto와 같은 역할, 다만 제약을 더 두어서 명확하게 컨트롤러까지만 사용해야 한다는 의미가 강함.
    │   │                    │    └── ItemController.java
    │   │                    ├── login
    │   │                    ├── member
    │   │                    ├── filter
    │   │                    ├── interceptor
    │   │                    ├── ...
    │   │                    └── ...
    │   └── resources
    │       ├── application-dev.yml
    │       ├── application-prod.yml
    │       └── application.yml

domain

이 패키지에는 화면, UI, 기술 인프라를 제외한 시스템이 구현해야 하는 핵심적인 비즈니스 업무 영역 클래스를 두어야 한다. 따라서 시스템의 기술을 변경하더라도 변하지 않아야 한다. 즉, 독립적으로 생성되어 있어야 한다.

 

web

web은 입력과 출력을 담당하는 GUI이다. 따라서 service와 같은 핵심 업무 로직에서 분리하여 둬야 한다. 

 

설계 시 web을 다른 기술로 바꿔도 domain은 그대로 유지할 수 있어야 한다.

web → domain 방식으로 web은 domain을 알지만 domain은 web을 몰라야 한다. 극단적으로 말하자면 web 패키지를 모두 없애도 domain에 오류가 없어야 한다. 이를 web은 domain을 의존하지만, domain은 web을 의존하지 않는다고 한다. 

 

Form에 맞추어 생성된 객체(web)은 Controller(web)에서만 사용되어야 한다. Repository(domain) 영역까지 넘어가면 안 된다.

 

Form과 DTO는 같은 역할을 수행한다. 하지만 Form이라는 이름에서 알 수 있는 것처럼 web 기술에 종속적인 단어이므로 이 클래스는 컨트롤러까지만 사용해야 한다. DTO는 Form 보다 더 범용적으로 사용되는 단어이다. DTO를 어디에 정의해 두는 가에 따라 다르겠지만, service에서도 사용할 수 있고 repository에서 사용할 수도 있다.

 

 

 

 

 

참고

 

form, DTO 차이 - 인프런 | 질문 & 답변

안녕하세요 강사님.공부를 하던중 form 과 DTO에 차이에 대해 궁금한점이 생겨 질문 드립니다.form은 아래와 같이 컨트롤러에서 Model에 담아 뷰에 전달 할때 사용하셨고[사진]DTO는 아래와 같이 데이

www.inflearn.com

 

[개발서적] 클린 아키텍처 6부 세부사항 - 내용 정리 및 요약

이번에는 로버트 C 마틴의 클린 아키텍처를 읽은 내용을 정리해보도록 하겠습니다. 개인적인 설명은 기울임으로 표시해두었으니, 읽으면서 참고하시면 될 것 같습니다. 30장. 데이터베이스는 세

mangkyu.tistory.com

 

[MVC 기초] 패키지 구조 설계 domain vs web package

domain vs web 패키지 domain 시스템이 구현해야하는 핵심 비즈니스 업무 영역 (화면, UI, 기술 인프라 등등을 제외한) 따라서 시스템의 기술을 변경하더라도 변하지 않아야 한다. 독립적으로 생성되어

nomoreft.tistory.com

 

Comments