Spring 프로젝트를 진행하면서, 디렉토리의 구조를 정하는 데에 있어서 처음에는 딱 한가지 규칙만 생각했다.
바로 src/main/java/groupName/projectName 하위에 파일을 위치시켜야 스프링 컨테이너가 이를 인식하고 실행한다는 것
그래서 자연스레 아래와 같은 디렉토리 구조를 사용했다.
계층형 구조
└── src
├── main
│ ├── java
│ │ └── groupName
│ │ └── projectName
│ │ └── demo
│ │ ├── config
│ │ ├── controller
│ │ ├── dao
│ │ ├── domain
│ │ ├── exception
│ │ └── service
│ └── resources
│ └── application.yml
이와 같은 구조는 계층형 구조라고 불린다.
- config : configuration 파일로 이루어져있다. ex) SwaggerConfig, SecurityConfig 등등
- controller : controller 파일로 이루어져있다.
- dao : Jpa repository 와 구현체 들로 이루어져 있다.
- domain : Entity, DTO 들로 이루어져 있다.
- exception : custom exception 들과 exception handler 로 이루어져 있다.
- service : service 들로 이루어져 있다.
이렇게 디렉토리 구조를 구성하면, 모든 파일을 한 눈에 보기 용이하고 구조가 단순하기에 이해하기 쉽다.
그러나 Entity의 종류가 계속해서 늘어나고 그에 맞춰 Jpa repository 가 늘어나면서, 모든 요청을 한 controller와 service 에서 다룰 수 없기 때문에 마찬가지로 늘어나면서 한 디렉토리 내에 너무 많은 종류의 controller, service 등의 쌓이게 되어
파일을 찾아내기가 곤란해졌다.
그리고 무엇보다 협업을 하다보니 이러한 불편한 점이 두드러졌다. 모두들 같은 디렉토리에서 파일을 수정하다보니, 거리낌 없이 다른 파일을 수정하게 되고, 이는 code conflict 로 이어졌다.
그러한 와중에 새로운 디렉토리 구조를 찾아보게 되었고, 마침 발견하게 된 것이 도메인형 디렉토리 구조였다.
도메인이라는 단어는 어떤걸까? DDD (Domain Driven Design) 와 관련이 있다.
DDD (Domain Driven Design)
소프트웨어 개발 방법론 중 하나로 도메인에 집중하는 소프트웨어 설계법 이다. 여기서 말하는 도메인이란, 개발자들만 필요하고 이해할 수 있는 영역이 아닌 사용자도 이해할 수 있는, 유비쿼터스(ubiquitous)하게 정의되는, 해결 해야하는 문제의 영역을 의미한다.
실제로 예를 들자면,
학교 축제 웹페이지라면
1) 축제 부스 관련 정보를 검색 및 조회 2) 부스 페이지에 댓글 작성 및 삭제 등등...
학교 도서관 예약 시스템이라면
1) 기존 예약 조회 및 예약 진행 2) 사용자 맞춤 기능(마이페이지, 로그인 등) 등등...
이렇게 해결 해야하는 문제 즉, 구현해야하는 기능들을 종류별로 나누고 그 기능들을 우선적으로 생각하면서 소프트웨어를 개발하는 방법론이다. DDD 를 지향하는 개발 방법은 여러가지가 있을 것이다.
예를 들면 ERD를 먼저 짜는 게 아니고, Controller 부터 Service 그 다음 Repository 까지 개발하는 방식이 있다. 이러한 방식은 프론트 팀원들에게 API 명세서를 빠르게 던져줄 수도 있고, 먼저 그려진 ERD에 의해 개발자의 사고가 제한되는 것을 막을 수 있다고 한다.
하여튼 그래서 DDD 의 주요 특징 중 하나가 Bounded Context 이다.
Bounded Context: A description of a boundary (typically a subsystem, or the work of a specific team) within which a particular model is defined and applicable. Every domain model lives in precisely one BC, and a BC contains precisely one domain model. BC is a specific responsibility, with explicit boundaries that separate it from other parts of the system.
https://wubw.github.io/2017/domain_driven_design_introduction/
간단히 말하면 앞서 말했던 도메인들의 경계를 명확히 나눠 제한된 컨텍스트로 나누어야한다는 것이다.
그래서 우리는 이걸 우리 프로젝트에 적용하고자 아래와 같은 디렉토리 구조를 도입하였다.
도메인형 구조
└── src
├── main
│ ├── java
│ │ └── gruoupName
│ | └── projectName
│ | ├── domain
│ | │ ├── booth
│ | │ │ ├── api
│ | │ │ ├── application
│ | │ │ ├── dao
│ | │ │ ├── domain
│ | │ │ ├── dto
│ | │ │ └── exception
│ | │ ├── comment
│ | │ │ ├── api
│ | │ │ ├── application
│ | │ │ ├── dao
│ | │ │ ├── domain
│ | │ │ ├── dto
│ | │ │ └── exception
│ | │ └── model
│ | │
│ | ├── global
│ | │ ├── common
│ | │ ├── config
│ | │ ├── error
│ | │ └── util
│ | │
│ | └── infra
│ └── resources
│ └── application.yml
크게 보면 1) domain 2) global 3) infra 등으로 나눌 수 있다.
1. domain
위에서 말했던 해결해야하는 문제 즉, 도메인을 나눠놓은 디렉토리이다.
먼저 미리 구분해놓은 도메인별로 디렉토리를 만들어 준 다음 아래와 같이 구성한다.
- api : controller 로 이루어져 있다. api를 처리하는 역할을 수행한다는 의미이다.
- application : service 로 이루어져 있다. 애플리케이션의 주요 로직을 수행한다는 의미이다.
- dao : repositoy 와 그 구현체로 이루어져 있다. Data Access Object 라는 뜻이다.
- domain : Entity로 이루어져 있다. 실제로 구현되는 domain 즉 data를 표현한다는 뜻이다.
- dto : DTO Data Transfer Object 로 이루어져 있다.
- exception : 해당 도메인에서 발생할 수 있는 예외(RuntimeException)들로 이루어져 있다.
직관적으로 api 가 아니고 controller 이런 식으로 정하면 안되나? 생각이 든다.
허나 위 폴더 명들은 구글링을 했을 때 가장 빈번하게 볼 수 있는 이름들이다. 폴더의 이름은 일종의 약속이다. 기술적인 요소나 다른 나에게 편한 이름을 고려하기 보다 다른 개발자들 과의 소통과 협업을 고려하여 그냥 다른 사람들이 많이 쓰는 걸 쓰는 게 맞다고 생각한다.
2. global
도메인 마다 적용되는 것이 아닌, 프로젝트 전역(global)에 적용되는 파일들을 모아놓은 디렉토리이다.
- common : 기본적으로 모든 곳에서 사용되는 BasicResponse DTO나 모든 Entity들이 상속하는 BasicEntity 등 으로 이루어진다.
- config : 전역적으로 적용되는 Configuration 들로 이루어진다. ex) SwaggerConfig, SecurityConfig etc
- error : 전역적인 예외나 전역적으로 예외를 처리하는 ExceptionHandler 들로 이루어진다.
- util : util 용 파일들로 이루어진다.
3. infra
infrastructure 관련된 코드들로 구성된다. infrastructure는 이메일/SMS 알림,외부 API 등 외부 서비스에 대한 파일들을 모아놓은 디렉토리이다.
암튼 그렇다. 실제로 위와 같이 디렉토리를 적용해보니 개발 단계에서도 협업할 때 분업을 하기가 좋았다. 그저 하나씩 도메인을 정해서 디렉토리를 만들어서 그 위에서만 작업하면 되니까 github merge 할 때도 굉장히 편했던 거 같다.
그리고 리팩토링 할 때도 모든 코드를 한 번에 다 한다고 치면 진짜 토나올 거 같은데, 도메인 폴더 별로 오늘은 이거 내일은 저거 이런 식으로 하면 생각보다(?) 편한 것 같다...
Reference
https://velog.io/@haron/Spring-Project-Structure
'스프링' 카테고리의 다른 글
SpringBoot 생성자 패턴 (Lombok) (0) | 2024.07.08 |
---|---|
SpringBoot 서버 시차 맞추기 (0) | 2024.07.04 |