본문 바로가기
Spring boot

[Spring boot] - 스프링 부트 프로젝트 구조 (계층형 vs 도메인형)

by 개발 고양이 2025. 2. 9.

Spring Boot 프로젝트를 세팅하게 되면, 패키지를  Model, View, Controller로 나누는 MVC 패턴을 일반적으로 사용한다.

MVC 패턴은 각 패키지의 역할이 명확하게 구분되어 있어 코드의 관리가 쉽다는 장점이 있다. 

MVC 패턴

 

이러한 일반적인 계층형 구조가 바로 이 MVC 패턴을 말하는 것이다. 

하지만 Spring Boot 프로젝트는 계층형 말고도 도메인형 구조로도 설계할 수 있다.

계층형 구조(Layered Architecture)와, 도메인형 구조(Domain-Driven Design)의 특징 및 장단점에 대해 알아보자.


계층형 구조 (Layered Architecture)

프로젝트를 기능에 따라 계층적으로 나눈다.

쉽게 말해, controller 파일들은 전부 controller 디렉토리에, service 파일들은 전부 service 디렉토리에, repository 파일들은 전부 repository 디렉토리에 넣는 방식이다.

📂 backend (Spring Boot 프로젝트)
 ├── 📂 controller    # 컨트롤러 계층 (사용자 요청/응답 처리)
 ├── 📂 service       # 서비스 계층 (비즈니스 로직 담당)
 ├── 📂 repository    # 데이터 접근 계층 (DB 관련 작업)
 ├── 📂 domain        # 데이터 모델 (DB 테이블과 매핑)
 ├── 📂 dto           # 요청/응답 데이터 객체 (클라이언트와 데이터 교환)

위 폴더를 펼친 예시를 그려보자면, 아래와 같다.

📂 backend
 ├── 📂 controller
 │    ├── MemberController.java  # 회원 관련 요청 처리
 │    ├── SearchController.java  # 검색 관련 요청 처리
 │
 ├── 📂 service
 │    ├── MemberService.java     # 회원 비즈니스 로직
 │    ├── SearchService.java     # 검색 비즈니스 로직
 │
 ├── 📂 dto
 │    ├── MemberDto.java         # 회원 요청/응답 데이터 객체
 │    ├── SearchDto.java         # 검색 요청/응답 데이터 객체
 │
 ├── 📂 repository
 │    ├── MemberRepository.java  # 회원 데이터 접근
 │    ├── SearchRepository.java  # 검색 데이터 접근
 │
 ├── 📂 domain
 │    ├── Member.java            # 회원 데이터 모델
 │    ├── Search.java            # 검색 데이터 모델

 

장점

각 계층별 역할이 명확하기 때문에, 코드 가독성이 높은 편이며, 전체 구조를 파악하기 용이하다.

중복되는 코드의 양이 적다.

 

단점

프로젝트가 커질 경우, 디렉토리에 클래스들이 과도하게 몰릴 수 있다.

도메인별 응집도가 낮기 때문에, 도메인의 흐름을 파악하기 어려울 수 있다. 

 


도메인형 구조 (Domain-Driven Design, DDD)

 

도메인을 중심으로 설계하는 방식이다. 즉 도메인의 행위를 중심으로 data와 logic이 결합된 구조이다.

쉽게 말하자면, 각 도메인마다 관련된 controller, service, repository들을 따로 만들어 넣는 방식이다.

📂 backend
 ├── 📂 Member
 │    ├── 📂 controller
 │    ├── 📂 service
 │    ├── 📂 repository
 │    ├── 📂 domain
 │    └── 📂 dto
 │
 ├── 📂 Recipe
 │    ├── 📂 controller
 │    ├── 📂 service
 │    ├── 📂 repository
 │    ├── 📂 domain
 │    └── 📂 dto

위 폴더를 펼친 예시를 그려보자면, 아래와 같다.

📂 backend
 ├── 📂 Member
 │    ├── 📂 controller
 │    │    └── MemberController.java  # 회원 관련 요청 처리
 │    ├── 📂 service
 │    │    └── MemberService.java     # 회원 비즈니스 로직
 │    ├── 📂 repository
 │    │    └── MemberRepository.java  # 회원 데이터 접근
 │    ├── 📂 domain
 │    │    └── Member.java            # 회원 데이터 모델
 │    └── 📂 dto
 │         └── MemberDto.java         # 회원 요청/응답 데이터 객체
 ├── 📂 Search
 │    ├── 📂 controller
 │    │    └── SearchController.java  # 검색 관련 요청 처리
 │    ├── 📂 service
 │    │    └── SearchService.java     # 검색 비즈니스 로직
 │    ├── 📂 repository
 │    │    └── SearchRepository.java  # 검색 데이터 접근
 │    ├── 📂 domain
 │    │    └── Search.java            # 검색 데이터 모델
 │    └── 📂 dto
 │         └── SearchDto.java         # 검색 요청/응답 데이터 객체

 

장점

특정 도메인에 대한 코드를 한 디렉토리 안에서 찾을 수 있다. (큰 규모의 프로젝트에도 적합)

각 도메인의 독립성이 보장됨에 따라, 도메인별로 관련된 코드들의 응집도가 높다.

=> 코드의 유지보수성 및 재활용이 높은 편이며, 도메인 별로 작업을 분리시키는데 유리하다.

새로운 도메인이나 기능을 추가해야 할 때 계층형 구조에 비해 간단한 수정만으로도 가능하다. (해당 도메인에 대한 패키지를 확장하기만 하면 되므로)

 

 

단점

프로젝트에 대한 이해도가 높지 않으면, 코드의 전체적인 구조를 파악하기 어려울 수 있다. 

개발자의 관점에 따라서, 어느 디렉토리(or 패키지)에 넣어야 할지 애매한 클래스들이 존재할 수 있다.

 

참고로 최근에는 도메인형 구조를 더 많이 추천 및 사용하고 있는 추세라고 한다 :)