Layered Architecture
Layered Architecture는 하나의 애플리케이션 안에서 코드를 책임별 층으로 나누는 구조입니다. 프레젠테이션, 애플리케이션(유스케이스), 도메인, 인프라 각 층은 자기 역할만 하고 바로 아래 층과만 소통합니다. 서버 코드가 복잡해졌을 때 처음 도달하게 되는 구조 정리 방식이고, Spring MVC의 Controller-Service-Repository 패턴이 대표적인 구현입니다.
▶아키텍처 다이어그램
🔍 구조 다이어그램점선 애니메이션은 데이터 또는 요청의 흐름 방향을 나타냅니다
서버를 분리했다고 서버 내부가 깔끔해지지는 않습니다. 기능이 늘어날수록 HTTP 파싱, 입력 검증, 비즈니스 규칙, DB 쿼리, 외부 API 호출이 같은 함수 안에 뒤섞입니다. 이 상태에서 규칙 하나를 고치면 쿼리가 깨지고, 응답 형식을 바꾸면 도메인 로직이 영향을 받습니다. 비즈니스 규칙만 테스트하고 싶은데 DB 연결 없이는 함수가 실행되지 않습니다. 코드를 읽을 때 '이 파일이 무엇을 하는지' 파악하려면 전체를 훑어야 합니다. 서로 다른 이유로 변하는 코드가 같은 곳에 섞여 있는 것이 근본 문제입니다. 층 분리는 그 뒤섞임을 끊는 작업입니다.
처음에는 컨트롤러 하나에 규칙과 쿼리를 같이 넣어도 별 문제가 없었습니다. 기능이 적을 때는 그 편이 더 빠르기도 했습니다. 하지만 업무가 복잡해지고 외부 연동이 붙으면서, 서로 다른 이유로 바뀌는 코드가 같은 파일에 사는 비용이 커졌습니다. 비즈니스 규칙을 고치려는데 DB 스키마 변경이 따라오고, UI 수정이 규칙 테스트를 깨뜨렸습니다. 이때 필요한 것은 서비스를 분리하는 것이 아니라, 한 앱 안에서 각 코드가 무슨 역할인지 읽히게 만드는 것이었습니다. Spring MVC의 Controller-Service-Repository, .NET의 계층 구조가 이 요구에 대한 업계의 답으로 자리잡았습니다.
건물 층에 비유하면, 1층 로비(프레젠테이션)는 방문자를 맞이하지만 업무는 처리하지 않습니다. 2층 사무실(애플리케이션)은 요청을 조율합니다. 3층 연구실(도메인)은 핵심 규칙을 실행합니다. 지하 기계실(인프라)은 DB나 외부 API 같은 기술 연결을 담당합니다. 코드로 옮기면, 프레젠테이션 층이 HTTP 요청을 파싱하고 응답을 내립니다. 애플리케이션 층이 트랜잭션을 열고 도메인 객체를 호출합니다. 도메인 층은 HTTP도 DB도 모르고 순수한 비즈니스 규칙만 처리합니다. 인프라 층이 실제 쿼리와 외부 호출을 맡습니다. 원칙은 하나입니다. 위에서 아래로만 호출합니다. 도메인이 프레젠테이션을 참조하거나 인프라가 애플리케이션에 의존하면 층이 무너집니다. 이 방향성 덕분에 비즈니스 규칙을 고칠 때 HTTP 형식을 신경 쓰지 않아도 되고, DB를 교체해도 도메인 코드는 그대로입니다.
Layered Architecture와 Microservices는 둘 다 복잡도를 나누는 방식이지만, 나누는 축이 다릅니다. Layered는 하나의 배포 단위 안에서 책임을 수직으로 나눕니다. Microservices는 배포 단위 자체를 도메인 경계로 수평으로 분리합니다. 주문 서비스, 결제 서비스, 사용자 서비스처럼요. 코드가 복잡해서 읽기 어렵고 변경 영향이 넓다면, Layered로 내부를 먼저 정리하는 것이 순서입니다. 팀이 여럿이고 특정 도메인만 독립 배포해야 한다면 Microservices가 맞습니다. 둘은 양자택일이 아닙니다. Microservices로 나눈 각 서비스 안에서도 Layered 구조가 쓰입니다. 층이 많아지면 간단한 기능 하나에도 모든 층을 관통해야 하고, 층 사이 데이터 변환이 반복됩니다. 층 안에서 수평으로 기능이 커지는 문제는 이 구조가 해결하지 못합니다. 그 시점에 모듈 분리나 서비스 분리가 다음 고민이 됩니다.
코드 규모가 수만 줄을 넘어서고, 팀원이 늘어나면서 '이 파일이 무엇을 하는지' 파악하는 데 시간이 걸리기 시작할 때 Layered Architecture가 첫 번째 정리 수단이 됩니다. 비즈니스 규칙 테스트에 DB 연결이 필요하거나, 컨트롤러가 수백 줄이 되어 쿼리와 검증과 변환이 뒤섞여 있거나, 외부 API를 교체하려는데 도메인 코드까지 수정해야 한다면 층 분리가 필요한 시점입니다. Layered는 그 자체가 목적지이기보다 다음 결정을 가능하게 만드는 구조입니다. 층이 분리되어 있으면 '이 도메인 층만 서비스로 뽑으면 되겠다'는 판단이 서고, 엉켜 있으면 서비스 분리 전에 내부 정리가 먼저라는 사실이 드러납니다.