기능은 다 되는데 코드가 스파게티처럼 엉켜서 고치기 무서운 경험, 한 번쯤 있을 거다. 처음부터 구조를 잘 잡아두면 나중에 기능 추가도 쉽고, 버그 찾기도 쉽고, 팀원이 봐도 이해가 빠르다. 그게 바로 아키텍처 설계다. 핵심부터 한 줄로 정리하면 이렇다.
응용 소프트웨어 아키텍처 = 소프트웨어를 구성하는 컴포넌트들을 어떻게 나누고, 어떻게 연결할지에 대한 큰 그림
도시 설계라고 생각하면 됨! 주거지·상업지·공업지를 구역별로 나누고, 도로로 연결하는 게 도시 설계다. 이 구역 나누기 없이 집·공장·마트를 막 지으면 나중에 교통 지옥이 되고 확장도 못 한다.
아키텍처도 똑같다. 기능들을 역할별로 구역(레이어·모듈)으로 나누고, 그 사이에 규칙(인터페이스)을 만들어서 연결한다. 처음엔 귀찮아 보여도, 서비스가 커질수록 이 구조가 있고 없고의 차이가 엄청나게 벌어지는 것 같음!
가장 기본적이고 많이 쓰이는 구조. 역할별로 층을 나눈다.
[ Presentation Layer ] ← 화면, API 엔드포인트
↓
[ Business Layer ] ← 핵심 비즈니스 로직
↓
[ Data Layer ] ← DB 접근, 데이터 처리
웹 프레임워크에서 제일 많이 보이는 패턴.
Model ← 데이터, 비즈니스 로직
View ← 화면 (UI)
Controller ← 요청 받아서 Model에 시키고 View에 전달
Robert C. Martin(Uncle Bob)이 제안한 구조. 핵심 비즈니스 로직을 외부(DB·프레임워크·UI)로부터 완전히 독립시키는 게 목표다.
[외부 세계]
DB / 프레임워크 / UI
↓
[인터페이스 어댑터]
↓
[유스케이스(비즈니스 로직)]
↓
[엔티티(핵심 도메인)]
하나의 큰 앱 대신, 기능별로 독립된 작은 서비스들로 쪼개는 구조.
[모놀리식] [마이크로서비스]
하나의 큰 서버 → 유저 서비스
결제 서비스
알림 서비스
(각각 독립 배포)
컴포넌트들이 직접 호출하지 않고, 이벤트를 발행/구독하는 방식으로 소통.
결제 완료 이벤트 발행
→ 알림 서비스가 구독 → 문자 발송
→ 포인트 서비스가 구독 → 포인트 적립
→ 주문 서비스가 구독 → 주문 상태 업데이트
백엔드만의 이야기가 아님. 프론트엔드도 구조를 잡아야 한다.
백엔드를 Express + TypeScript로 짠다고 하면, 레이어드 아키텍처 기준으로 이런 구조가 된다.
src/
├── routes/ ← Presentation Layer (엔드포인트 정의)
├── controllers/ ← 요청 받아서 서비스에 넘김
├── services/ ← Business Layer (핵심 로직)
├── repositories/ ← Data Layer (DB 쿼리)
└── models/ ← 데이터 구조 정의
repositories/만 수정하면 됨. 서비스 로직은 그대로프론트엔드에서 OpenPoll 같은 규모가 커지면 FSD가 빛을 발한다. 페이지가 20개 넘어가면 기능별로 폴더 안 나눠두면 어디에 뭐가 있는지 찾기 힘들어지기 시작함. "이 컴포넌트가 투표 기능 건지 유저 기능 건지"가 폴더 구조만 봐도 바로 보이게 되는 거다!
| 아키텍처 | 핵심 아이디어 | 언제 |
|---|---|---|
| 레이어드 | 역할별 층으로 나누기 | 백엔드 기본 구조 |
| MVC | Model·View·Controller 분리 | 대부분 웹 프레임워크 |
| 클린 아키텍처 | 핵심 로직을 외부로부터 독립 | 대규모·장기 프로젝트 |
| MSA | 기능별 독립 서비스로 쪼개기 | 대형 서비스·큰 팀 |
| 이벤트 드리븐 | 이벤트 발행/구독으로 느슨하게 연결 | 서비스 간 결합 줄일 때 |
| 아토믹 디자인 / FSD | 컴포넌트·기능 단위 계층화 | 프론트엔드 |
쉽게 외우면 "아키텍처는 코드 짜기 전에 어떻게 나눌지 합의하는 것" — 지금 당장 완벽한 구조가 아니어도 괜찮지만, 아무 구조 없이 짜는 것보다 레이어드라도 잡고 시작하면 나중이 훨씬 편해지는 것 같음!