3-Layer Architecture

3계층 아키텍처

Controller, Service, Data Access 계층으로 이어지는 요청과 응답 흐름을 확인합니다.

3계층 아키텍처는 요청 처리, 비즈니스 로직, 데이터 접근의 책임을 각각 분리하는 구조입니다. 브라우저의 요청은 Controller와 Service를 거쳐 Repository 또는 MyBatis Mapper에 전달되고, 처리 결과는 반대 방향으로 반환됩니다.

Controller Service Repository MyBatis Mapper Database

도메인 드리븐을 쓸 수 없는 자리. 외주처럼 구조를 우리가 정할 수 없는 일감에서는 네 겹으로 나누고 package-private으로 막는 방식을 강제할 수 없다.

Request Flow

브라우저의 HTTP 요청이 Controller와 Service를 거쳐 Data Access Layer에 닿고, 결과가 반대 방향으로 돌아온다. 계층 카드를 누르면 아래 표준 목록이 그 계층 것만 남긴다.

Request · 실선 Response · 점선

Request 사용자의 동작이 Database에 닿기까지

Browserapi · webservicerepositoryDatabase순서로 흐른다. 계층을 고르면 아래 목록이 그 계층의 표준만 남긴다.

Response 조회한 데이터가 화면으로 돌아오기까지

  1. Database
  2. repository
  3. service
  4. apiweb
  5. Browser

돌아오는 길에는 Query Result, Entity / Record, 결과 객체, HTTP Response 순서로 타입이 바뀐다. 각 계층은 자기 타입만 알고, 옆 계층의 타입은 모른다.

패키징은 도메인 드리븐과 같다 — 최상위를 도메인으로 나누고 계층은 그 안에 둔다(order/api, order/service, order/repository). 다른 것은 그 안이 몇 겹이냐다. Repository, DAO, MyBatis Mapper는 모두 데이터 접근 책임을 수행한다. 기술 스택에 따라 이름과 구현 방식은 달라질 수 있어서, 이 셋을 서로 다른 계층으로 나누지 않고 하나로 묶었다. domain이 없으므로 Repository 인터페이스로 의존을 뒤집지 않고, 호출 방향과 의존 방향이 같다. 첫 칸의 apiweb은 계층이 둘이라는 뜻이 아니다 — 같은 Presentation 계층의 패키지 둘이고, 나가는 것이 JSON이냐 HTML이냐만 다르다.

Layer Responsibilities

세 계층이 각각 무엇을 맡고, 무엇을 맡지 않는지다. 계층이 흐려지는 자리는 대부분 「하지 않는다」 쪽에 있다.

  • Presentation Layer

    api

    HTTP 요청을 받아 입력값을 확인하고, 적절한 Service를 호출한 뒤 JSON 응답을 생성한다.

    대표 경로 {도메인}/api

    여기서 한다

    • Route 연결
    • Request Parameter 수신
    • Request DTO 변환
    • 입력 형식 검증
    • 인증 정보 수신
    • Service 호출
    • Response DTO 반환
    • HTTP Status Code 지정

    여기서 하지 않는다

    • 핵심 비즈니스 로직
    • 직접 SQL 실행
    • Mapper 직접 호출
    • 복잡한 데이터 가공
    • 장시간 트랜잭션 처리
    • 여러 업무 로직을 직접 조율
  • Business Layer

    service

    비즈니스 처리 순서를 조율하고, 트랜잭션 범위 안에서 Repository 또는 Mapper를 호출한다.

    대표 경로 {도메인}/service

    여기서 한다

    • 업무 흐름 조율
    • 비즈니스 조건 검사
    • 트랜잭션 관리
    • 여러 Repository 또는 Mapper 호출
    • 데이터 생성 및 변경
    • 외부 API 호출 조율
    • 업무 예외 발생
    • 결과 데이터 구성

    여기서 하지 않는다

    • HTTP Request 직접 의존
    • HTTP Response 직접 생성
    • View 객체 직접 생성
    • SQL 문자열 직접 작성
    • Controller 역할 수행
    • 프레젠테이션 전용 포맷에 강하게 의존
  • Data Access Layer

    repository

    Database에 접근하여 데이터를 조회하고 저장하며, 데이터 접근 구현을 상위 계층에서 분리한다.

    대표 경로 {도메인}/repository

    여기서 한다

    • SQL 실행
    • 데이터 조회 · 저장 · 수정 · 삭제
    • 검색 조건 적용
    • Paging 처리
    • Database Record 변환
    • 영속성 기술 처리

    여기서 하지 않는다

    • HTTP 처리
    • 사용자 메시지 결정
    • 업무 흐름 조율
    • Controller 호출
    • Service 역할 수행
    • 화면 전용 데이터 생성
    • 복잡한 업무 정책 판단

Data Access Layer의 구현 방식

Repository, DAO, MyBatis Mapper는 모두 데이터 접근 책임을 수행합니다. 프로젝트의 기술 스택과 규칙에 따라 이름과 구현 방식은 달라질 수 있습니다. 셋을 서로 다른 계층으로 나누지 않는다 — 같은 자리의 다른 구현이다.

  • MyBatis · XML

    ControllerServiceMapper InterfaceMapper XMLDatabase

  • MyBatis · Annotation

    ControllerServiceMapper InterfaceSQL AnnotationDatabase

  • JPA · ORM

    ControllerServiceRepositoryORMDatabase

Request Examples

실제 요청 하나가 어느 클래스를 어떤 순서로 지나가는지다. 예시를 고르면 위 다이어그램의 계층에 그 요청에서의 순서 번호가 붙는다.

GET /users/10 MyBatis Mapper

  1. GET /users/10 Request
  2. UserController.getUser() Request
  3. UserService.getUser() Request
  4. UserMapper.selectUserById() Request
  5. users 테이블 조회 Request
  6. UserResponse 반환 Response

POST /posts MyBatis Mapper

  1. POST /posts Request
  2. PostController.createPost() Request
  3. PostService.createPost() Request
  4. PostMapper.insertPost() Request
  5. posts 테이블 저장 Request
  6. 생성 결과 반환 Response

PATCH /orders/100/status JPA Repository

  1. PATCH /orders/100/status Request
  2. OrderController.updateStatus() Request
  3. OrderService.updateStatus() Request
  4. OrderRepository.updateStatus() Request
  5. orders 테이블 수정 Request
  6. 변경 결과 반환 Response

Coding Standards

여기 실린 15개는 계층 구조를 전제하지 않아 이 구조에서도 그대로 성립하는 표준이다. 3계층만의 표준은 아직 정한 것이 없다 — 비어 있는 자리는 아직 정하지 않은 것에 적어뒀다.

계층을 가리지 않는 규칙

객체 생성, 예외, assert처럼 어느 계층에서 쓰든 같은 모양이어야 하는 것들이다. 위 다이어그램에는 자리가 없지만 세 계층 모두에 걸린다.

전체 표준 15개

표준 15개를 모두 보는 중

Dependency Rules

호출은 Controller에서 Data Access 쪽으로만 흐른다. 거꾸로 부르는 순간 계층이 아니라 그물이 된다.

허용 이 방향으로만 부른다

  • Controller 호출 가능 Service
  • Service 호출 가능 Repository / Mapper

금지 거꾸로 부르지 않는다

  • Service 호출 금지 Controller
  • Repository / Mapper 호출 금지 Service
  • Repository / Mapper 호출 금지 Controller
  • Controller가 Repository 또는 Mapper를 직접 호출하지 않는다.
  • Controller에 핵심 비즈니스 로직을 작성하지 않는다.
  • Service에 HTTP 전용 처리 로직을 작성하지 않는다.
  • Repository 또는 Mapper에 업무 흐름을 작성하지 않는다.
  • SQL은 Data Access Layer에서 관리한다.
  • 트랜잭션 범위는 일반적으로 Service에서 관리한다.
  • 계층 간 순환 참조를 만들지 않는다.

프로젝트에 조회 전용 구조나 별도의 Query Service 규칙이 이미 있다면 그 프로젝트의 코딩 스탠다드가 위 규칙보다 앞선다. 여기 적은 것은 아무 규칙도 없을 때의 기본값이다.

Related Architecture

더 엄격하게 의존성을 분리한 구조가 필요하다면 도메인 드리븐 아키텍처를 확인하세요.

3계층 아키텍처

  • Controller, Service, Data Access 중심
  • 구조가 단순하고 이해하기 쉬움
  • 일반적인 업무 시스템에 적용하기 쉬움
  • 빠른 개발과 유지보수에 적합
  • 계층별 책임 분리가 핵심

표준 보기

도메인 드리븐 아키텍처

  • Domain을 중심으로 의존성 방향을 엄격히 제어
  • UseCase, Adapter, Infrastructure 등을 세분화
  • 외부 기술로부터 핵심 비즈니스 규칙을 보호
  • 구조와 추상화가 상대적으로 복잡함

표준 보기

모든 프로젝트에 복잡한 아키텍처가 필요한 것은 아닙니다. Controller, Service, Data Access의 책임이 명확히 분리되어 있다면 3계층 아키텍처도 충분히 안정적이고 유지보수하기 좋은 구조가 될 수 있습니다.

둘 중 하나가 더 낫다는 이야기가 아니다. 프로젝트 규모, 업무 복잡도, 팀의 숙련도에 따라 고르는 구조다. 이 저장소가 도메인 드리븐을 먼저 정한 것도 우열 때문이 아니라, 구조를 우리가 정할 수 있는 프로젝트가 먼저 있었기 때문이다.