코드를 논리적 역할에 따라 독립된 층으로 나누는 아키텍처 패턴. 백엔드 API에 가장 널리 쓰인다.

층을 나눠 얻는 것

층이 서로 독립적이면 시스템 전체를 건드리지 않고 한 층만 개선하거나 교체할 수 있다. 구조를 파악하기 쉬워 재사용성이 높아지고, 층마다 테스트를 짜기도 수월하며, 코드 가독성도 올라간다.

세 층과 의존 방향

지켜야 할 규칙이 둘이다. 각 층은 자기보다 아래에 있는 층에만 의존한다. 위를 알아서는 안 된다. 그리고 각 층이 무엇을 하는지 분명해야 한다. 이 둘이 지켜져야 나머지 장점이 따라온다.

하는 일스프링에서
Presentation엔드포인트 정의, HTTP 요청 읽기, 인증, JSON 변환Controller
Business비즈니스 로직, 유효성 검사, 권한 확인Service
PersistenceDB와 주고받으며 저장하고 검색Repository

Presentation은 딱 여기까지만 한다. 요청을 읽고 나면 실제 로직은 다음 층으로 넘긴다. 회원가입 요청에서 필수 항목이 빠졌으면 거부하는 것 같은 판단은 Business의 몫이다. Service 객체가 하나의 트랜잭션 단위로 동작하므로 트랜잭션 경계도 이 구조를 따라 걸린다.

Validation과 Verification

Business 층에서 하는 것은 Validation이다. Verification은 개발자 중심이라 만드는 과정을 잘 지켰는지, 스펙 요구사항을 충족했는지를 본다. Validation은 사용자 중심이라 최종 결과물이 잘 나왔는지, 사용자 요구사항을 충족했는지를 본다.

싱크홀 안티패턴

싱크홀 안티패턴을 조심한다. 요청이 한 층에서 다음 층으로 넘어갈 때 아무 로직도 처리하지 않고 그냥 통과하는 것을 말한다. Controller가 받아서 Service에 그대로 넘기고 Service가 Repository에 그대로 넘기기만 하는 코드다. 불필요한 객체 생성과 처리를 유발하고 메모리를 쓰며 성능에도 나쁘다.

다만 완전히 없앨 수는 없다. 단순 조회는 어쩔 수 없이 통과하게 되고, 전체 요청의 20% 정도면 그런대로 괜찮은 수준으로 본다.

트래픽이나 복잡도가 늘면 이 구조만으로 감당이 안 된다. 그때 나오는 선택지가 모놀리식과 MSA이고, 도메인을 중심으로 다시 짜는 DDD다.

관련

출처