기존에 저는 JSP 기반 MVC2 구조에서
Controller → Service → DAO(또는 MyBatis Mapper)
형태로 웹 애플리케이션을 구성해 왔습니다.
그러다 Spring Boot를 혼자 공부하면서 JPA 예제를 접하게 되었고,
기존에 사용하던 DAO 대신 Repository라는 계층이 등장하는 것을 처음 알게 되었습니다.
처음에는 DAO와 무엇이 다른지,
왜 굳이 이름이 바뀌었는지 혼란스러웠지만,
정리해보니 Repository는 JPA 환경에서
영속성 계층을 표현하는 표준적인 역할 이름에 가깝다는 것을 알게 되었습니다.
이번 글에서는 Spring에서 흔히 쓰는 레이어 구조인
Controller / Service / Repository가 각각 무엇을 책임지는지 정리해 보겠습니다.
핵심은 “각 레이어의 책임을 분리해서 변경에 강한 구조를 만든다”는 점입니다.
Controller는 HTTP 요청/응답을 다루는 곳입니다.
GET/POST/PUT/DELETE)@PathVariable, @RequestParam, @RequestBody)Controller의 목표는 한 문장으로 요약하면
“웹 요청을 애플리케이션 로직으로 안전하게 연결하고, 결과를 다시 변환한다” 입니다.
Service는 비즈니스 로직을 처리하는 중심입니다.
@Transactional)Controller는 "어떻게 들어오고 나가는지"에 집중한다면,
Service는 "무엇을 해야 하는지”를 결정하는 계층입니다.
Repository는 DB 접근을 담당합니다.
Repository는 비즈니스 규칙을 알 필요가 없고,
“데이터를 어떻게 가져오고 저장할지”에만 집중하는 것이 이상적입니다.
Repository라는 용어는 JPA(Spring Data JPA)에서 공식적으로 굳어진 개념입니다.
JpaRepository를 사용하면 기본적인 CRUD와 단순 조회 로직을
비교적 간단한 코드로 구현할 수 있다는 점이 꽤 인상적이었습니다.
이 부분은 나중에 별도의 글에서 더 자세히 다뤄보도록 하겠습니다.
Controller가 두꺼워질수록 아래 문제가 쉽게 생깁니다.
HTTP 처리 로직과 비즈니스 로직이 섞여서 읽기 어려워집니다.
같은 로직이 여러 Controller에 복붙되기 쉽습니다.
테스트가 어려워집니다(웹 환경 없이 로직만 검증하기 힘듦).
반대로 Controller를 얇게 유지하면 장점이 큽니다.
Service 중심으로 로직이 모이므로 재사용이 쉬워집니다.
테스트(단위 테스트/서비스 테스트)가 쉬워집니다.
화면 반환 방식이 바뀌어도 핵심 로직은 덜 흔들립니다.
그래서 일반적으로는
정답은 하나로 고정되어 있진 않지만, 기준을 세우면 일관성을 만들 수 있습니다.
이렇게 두면 계층의 경계가 명확해집니다.
Controller가 얇아야 한다는 목표와 충돌하기 쉽습니다.
대부분의 CRUD/중간 규모 프로젝트에서는 이 방식이 가장 무난합니다.
프로젝트가 커지거나 응답 구조가 복잡해지면 자연스럽게 선택되는 방향입니다.
DTO ↔ Entity 변환은 기본적으로 Service(또는 Mapper)에 두고,
Controller에는 변환 로직이 쌓이지 않도록 관리하는 것을 목표로 하겠습니다.
Spring의 계층 분리는 각 역할의 책임을 명확히 하고,
변경에 강한 구조를 만들 수 있다는 점에서
개인적으로 흥미롭게 느껴집니다.
이 글에서 정리한 기준을 바탕으로,
이후 글에서는 JPA와 Entity 설계 전반을 정리해보겠습니다.