Layered Architecture
Layered Architecture(계층형 아키텍처)는 소프트웨어 디자인 패턴 중 가장 일반적이고 주로 쓰이는 방식 중 하나이다. 말 그대로 소프트웨어 시스템의 구성 요소들을 수직적인 계층(Layer)으로 나누어 구조화한 형태인데, 각 Layer(계층)가 특정한 역할/책임만 담당하도록 분리하는 것이 핵심이다. (관심사를 분리하기 위해서이다.)
또 다른 핵심적인 원칙은 상위 계층은 바로 아래 계층에만 의존하며, 아래 계층은 절대 위 계층에 대해 몰라야 한다는 점이다. (아래 방향으로만 의존한다.) 이러한 원칙에 의해 각 계층은 자신의 역할에만 집중할 수 있고, 코드 변경 시에도 다른 계층에 미치는 영향을 최소화할 수 있다.
일반적인 Layered Architecture는 다음과 같은 4가지 주요 계층으로 구성된다. (Layer의 개수에 따라 N Layered Architecture라고 한다.)
1. Presentation Layer
사용자 인터페이스 및 요청 처리를 하는 계층이다.
책임: HTTP 요청/응답 처리, 데이터 유효성 검사 (입력 형식), 요청을 다음 계층인 Application Layer로 전달한다.
예시: Controller
2. Application Layer
업무 흐름 정의하고 실행한다. 여러 도메인 객체를 조합하여 트랜잭션을 관리한다.
책임: 요청에 따른 비즈니스 로직 순서 제어, 트랜잭션 시작/종료, 도메인 계층을 호출하여 데이터 조작
예시: 서비스 (Service), 유스케이스 (UseCase)
3. Domain Layer
핵심 비즈니스 로직과 규칙 및 데이터를 담고 있음. (중요한 계층)
책임: 데이터가 가져야 할 순수한 규칙(예: "재고는 0 미만이 될 수 없다", "비밀번호는 8자 이상이어야 한다")을 정의한다. 객체 간 관계와 동작을 구현한다.
예시: 엔티티 (Entity), 값 객체 (Value Object)
4. Infrastructure Layer
외부 시스템 연결 및 기술 구현을 담당한다. 데이터베이스 접근, 메시징 큐, 파일 시스템 등 외부 시스템과의 연결을 처리한다.
책임: Domain 객체를 저장하고 불러오는 방식을 구현한다. Domain 계층이 정의한 인터페이스를 구현한다.
예시: Repository 구현체, DB 설정 파일, 외부 API 클라이언트 (예를 들어, Gemini AI API의 경우에도 이에 포함된다.)
스프링 부트 패키지 구조와 연관짓기
Controller Layer, Service Layer, Repository Layer (전통적인 3계층 구조)
일반적으로 자주 쓰이는 계층 구조로는 Domain 별로 Controller Layer, Service Layer, Repository Layer 이렇게 3계층이 있다. 단순히 기술적인 역할에 따라 코드를 수직적으로 분리한 구조이다.
com.project.app
├── controller // 모든 도메인의 요청 처리
├── service // 모든 도메인의 비즈니스 로직
└── repository // 모든 도메인의 데이터 접근
1. Controller Layer
엔드포인트를 통해 클라이언트의 요청을 받아 Service Layer으로 전달하고, 결과를 Response DTO 객체 형태로 반환해준다.
2. Service Layer
비즈니스 로직을 처리하는 일들을 한다. Service Layer에 이러한 비즈니스 로직이 집중되며, Repository Layer와 강하게 결합되어 있다.
3. Repository Layer
데이터베이스나 기타 저장소에 접근하여 데이터를 CRUD (생성, 조회, 수정, 삭제)하는 역할을 한다. 엄밀히 말하면 데이터 접근 기술(예: JDBC, JPA)의 복잡성을 숨기고, 도메인 객체의 영속성(Persistence)을 관리하는 역할을 추상화하는 역할을 한다.
Repository의 경우, 쿼리를 통해 데이터 조작을 수행하기에 DB와 밀접한 관련이 있다. DB에 접근하기 바로 직전의 계층이 Domain이기에 Domain Layer에도 일부 속한다고 볼 수 있다.
Repository 인터페이스는 Domain Layer에 속하여 비즈니스 규칙의 일부로 간주되고, 이 인터페이스의 구현체(실제 DB 접근 코드)는 Infrastructure Layer에 속하게 된다.
그러나 프로젝트 규모가 커질수록 각 패키지 안에 수많은 클래스가 몰리게 되어 관리하기가 어려워질 것이다. 특히 Service 계층 같은 경우, 비즈니스 로직 등이 많아질수록 서비스 흐름 등, 도메인 간의 코드가 뒤섞일 문제가 발생할 수도 있다. 이러한 문제점을 해결하고자 아래와 같은 아키텍처 구조가 등장했다.
Layer Architecture(도메인 중심 4계층 구조)
위에 설명한대로 presentation, application, domain, infrastructure 등 총 4개의 계층으로 이루어져 있으며, 도메인(비즈니스) 단위로 구조화한다.
이러한 계층 구조를 따르며 패키지 구조를 구성한다면, 먼저 도메인 단위로 나누고 그 안에 계층별로 역할을 분리하는 방식으로 쓴다.
src/main/java/com/example
│
├── global
│ └── infrastructure
│ └── config
│ ├── security
│ │ └── SecurityConfig.java
│ └── schedule
│ └── ScheduleConfig.java
│
├── article
│ ├── application
│ │ └── service
│ │ └── ArticleServiceV1.java
│ │
│ ├── domain
│ │ ├── entity
│ │ │ └── ArticleEntity.java
│ │ └── repository
│ │ └── ArticleRepository.java
│ │
│ ├── infrastructure
│ │ ├── api
│ │ │ └── gemini
│ │ │ ├── dto
│ │ │ │ └── response
│ │ │ │ └── ResGeminiPostGenerateContentDto.java
│ │ │ └── client
│ │ │ └── GeminiClient.java
│ │ ├── repository
│ │ │ └── article
│ │ │ └── ArticleRepositoryImpl.java (Repository 구현체이나 생략가능)
│ │ └── schedule
│ │ └── articleScheduler.java
│ │
│ └── presentation
│ ├── controller
│ │ └── ArticleControllerV1.java
│ ├── advice
│ │ └── GlobalExceptionHandler.java
│ └── dto
│ ├── request
│ │ └── ReqArticleGetByIdDtoV1.java
│ └── response
│ └── ResArticleGetByIdDtoV1.java
│
│
└── AppApplication.java
장점
일반적으로 프로젝트 규모가 큰 개발팀일 경우, 기존의 기술적 역할에 따른 3계층 구조보다는 Layered Architecture가 더 권장되는 편이다. 앞서 말했던 3계층 구조의 단점을 비즈니스 로직과 인프라 구현의 명확한 분리를 통해 응집도를 향상시키는 방식으로 해결할 수 있기 때문이다.
이러한 방식은 특정 도메인의 코드를 한 곳에서 모두 찾을 수 있게 하여 협업 시 혼란을 줄이고, 핵심 비즈니스 규칙을 외부 인프라 관련 코드로부터 분리하여 관리할 수 있다는 이점이 있다.
'TIL' 카테고리의 다른 글
| [TIL] 무중단배포에 대해 (0) | 2025.10.28 |
|---|---|
| [TIL] jakarta vs springframework Transactional (0) | 2025.10.02 |
| [TIL] Java에서의 병렬 처리 (1) | 2025.09.26 |
| [TIL] 스케줄링 (@Scheduled & Quartz) (0) | 2025.09.24 |
| [TIL] ResponseEntity (0) | 2025.09.22 |