Controller, Service, Repository만 있다고 믿는 당신에게 ( 3 Layered Architecture )

KUN·2025년 5월 8일
post-thumbnail

목적

Layered Architecture 에는 많은 스타일이 있다.
2 Layered Architecture
3 Layered Architecture
4 Layered Architecture ... N Layered Architecture 가 있다.

그럼 우리는 가장 일반적인 3 Layered Architecture 를 배우게되는 데,
3 Layered Architecture 가 무엇이고, 왜 가장 보편적인 스타일이 되었을까?


왜 만들어졌나?

간단소개 - Wikipedia

정리 : 사용자 인터페이스, 비즈니스 로직, 데이터 계층을 분리해 각각 독립적으로 개발,운영할 수 있도록 한 구조로, 서버 부하 분산유지보수성 향상을 위해 도입된 아키텍처이다.

탄생배경

John J. Donovan이 미국 매사추세츠주 케임브리지에 설립한 도구 개발 회사인 Open Environment Corporation(OEC)에서 개발되었다.

Apart from the usual advantages of modular software with well-defined interfaces,
the three-tier architecture 
is intended to allow any of the three tiers to be upgraded or replaced
independently in response to changes in requirements or technology.

명확하게 정의된 인터페이스를 갖는 모듈화된 소프트웨어가 주는 일반적인 장점 외에도, 
3계층 아키텍처는 
요구사항이나 기술 변화에 따라 각 계층을 독립적으로 업그레이드하거나 교체할 수 있도록 설계되었습니다.

정리 : 요구사항 변화나 기술 변화에
유연하게 대응하기 위해 그리고
각 계층을 독립적으로 교체하거나 수정할 수 있는 구조가 필요했기 때문에
3계층 구조가 채택되었다는 것을 알 수 있다.


실제 사용 ( MVC 와 3 Layered Architecture )

간단한 게시글 조회 기능
게시글 1건을 조회하는 API

/posts/{id} 호출 시, 게시글 제목과 내용을 응답하게 만들어보았다.

https://github.com/NewJKH/My_Spring_space - 아래 내용 실습


MVC

// Model
@Getter
@AllArgsConstructor
public class Post {
    private Long id;
    private String title;
    private String content;
}

// Controller
@Controller
public class PostController {
    private final PostRepository repository = new PostRepository();

    @GetMapping("/posts/{id}")
    public String getPost(@PathVariable Long id, Model model) {
        Post post = repository.findById(id);
        model.addAttribute("post", post);
        return "postView";
    }
}

// Repository
public class PostRepository {
    public Post findById(Long id) {
        return new Post(id, "제목", "내용");
    }
}

페이지 자체를 반환한다.

3 Layered Architecture

// Domain
@Getter
@AllArgsConstructor
public class Post {
    private Long id;
    private String title;
    private String content;
}

// Repository
@Repository
public class PostRepository {
    public Post findById(Long id) {
        return new Post(id, "제목", "내용");
    }
}

// Service
@Service
public class PostService {
    private final PostRepository repository;

    public PostService(PostRepository repository) {
        this.repository = repository;
    }

    public Post getPost(Long id) {
        return repository.findById(id);
    }
}

// Controller
@RestController
@RequestMapping("/posts")
public class PostController {
    private final PostService service;

    public PostController(PostService service) {
        this.service = service;
    }

    @GetMapping("/{id}")
    public Post getPost(@PathVariable Long id) {
        return service.getPost(id);
    }
}

Json을 반환한다.


왜 가장 보편적인 스타일이 되었을까?

MVC 예제를 보면, 비즈니스 로직을 Controller에서 직접 처리해야 한다.
즉, HTTP 요청 처리비즈니스 로직이 한 곳에 섞여 있어 관심의 분리가 이루어지지 않았다.

하지만 3 Layered Architecture에서는
Controller는 HTTP 요청/응답 처리만 담당하고,
Service는 비즈니스 로직,
Repository는 데이터 접근 로직을 각각 담당한다.

이로 인해 다음과 같은 장점이 생긴다.

  • 유지보수가 쉬워진다.
    비즈니스 요구사항이 변경되더라도 Service 계층만 수정하면 되기 때문에 Controller나 Repository에 영향을 주지 않는다.
  • 테스트가 쉬워진다.
    Service와 Repository는 독립적으로 단위 테스트가 가능하며,
    Controller는 MockMvc를 통해 별도로 테스트할 수 있다.
  • 역할 분담이 명확하다.
    개발팀 내에서 각 계층을 담당하는 역할을 나누기 쉬워 협업이나 코드 리뷰 시 효율이 높아진다.
  • 확장성과 유연성이 높다.
    계층이 분리되어 있으므로 기능을 추가하거나 교체하기가 쉽고,
    Spring, NestJS 등 주요 프레임워크에서도 이 구조를 전제로 설계된다.

결론: 3 Layered Architecture는
관심사를 분리하여 복잡도를 관리하고,
유지보수성과 테스트 효율성을 높이며,
실무에 최적화된 구조이기 때문에
가장 널리 사용되는 아키텍처 스타일이 되었다.


그럼에도 의문점

우리가 개발하다 보면 Controller, Service, Repository만 쓰는 것이 아니다.
실제로는 DTO, Entity, Domain, Mapper 등 다양한 구성 요소들이 함께 사용된다.
그렇다면 Controller–Service–Repository–Domain–Entity–DTO…까지 포함한 구조는 엄밀히 말하면 더 이상 단순한 3계층 구조는 아니라고 볼 수 있다.

그럼에도 불구하고 우리는 이 구조를 여전히 3계층 아키텍처(3-Layered Architecture)라고 부른다.
왜일까?

우리는 흔히 3 레이어드 아키텍처를 단순히
Controller, Service, Repository로 배우곤 한다.
하지만 사실 그건 편의적 추상화에 불과하다.
그 자체가 하나의 함정인 셈이다.

아래 그림을 보면 그 이유가 명확해진다.

이 그림 어디에도 Controller, Service, Repository라는 단어는 없다.
대신 Presentation, Business Logic, Data Access라는 추상적인 개념 계층만 존재한다.

com.example.project
│
├── controller       ← 프레젠테이션 계층 (Presentation Layer)
│   └── PostController.java
│
├── dto              ← 프레젠테이션 계층과 비즈니스 로직 계층 사이의 "데이터 전달 객체"
│   ├── PostRequestDto.java   ← Controller → Service 전달
│   └── PostResponseDto.java  ← Service → Controller 반환
│
├── service          ← 비즈니스 로직 계층 (Business Logic Layer)
│   └── PostService.java
│
├── domain           ← 비즈니스 로직 계층 내부의 도메인 모델
│   └── Post.java     ← JPA @Entity 또는 도메인 모델 (상태/로직 포함 가능)
│
├── repository       ← 데이터 접근 계층 (Data Access Layer)
│   └── PostRepository.java

이처럼 다양한 구성 요소가 존재하지만,
결국 이들은 모두 " 어느 계층의 책임을 맡고 있는가 "를 기준으로 분류될 수 있다.
즉, 우리가 Controller, Service, Repository만 보고 3계층이다라고 말하는 건
프레임워크 관점의 구체적인 구현을 계층 개념에 대응시킨 것일 뿐,
본질적으로는 Presentation / Business Logic / Data Access라는 세 가지 책임을 기준으로 나누었기 때문에
여전히 3계층 아키텍처라고 부를 수 있는 것이다.

profile
배우노라, 실험하노라, 기록하노라

0개의 댓글