[Cloud & CERT] 조사 1. 3-Tier Architecture 설계(오병은)[Cloud & CERT] 조사 1. 3-Tier Architecture 설계(오병은)

[Cloud & CERT] 조사 1. 3-Tier Architecture 설계(오병은)

생성 일시
Sep 20, 2026 11:00 AM
최종 편집 일시
Last updated September 20, 2026
태그
작성자
@오병은
Date

1. 3-Tier Architecture 설계

상황: 작은 이커머스 서비스 구축. 기능은 사용자 로그인, 상품 조회, 상품 주문, 회원 정보 저장, 주문 정보 저장.

1) 서버 한 대에 웹서버+애플리케이션+DB를 모두 설치

문제: 사용자가 늘자 서버가 느려졌음. 웹 처리가 느린 건지 주문 로직이 느린 건지 DB 조회가 느린 건지 구분이 안 됨. 하나를 고치려고 재시작하면 나머지도 같이 멈춤.
질문: 이 서버를 어떤 기준으로 나눠야 원인 분리와 개별 확장이 가능해지는가?
구체적인 설명
서버 한 대에서 모니터링 지표(CPU, 메모리, 디스크 I/O)를 봐도 원인을 못 찾는 이유는, 세 가지 완전히 다른 작업 패턴이 하나의 리소스 풀을 공유하고 있기 때문이다.
  • 웹 서버(정적 파일 응답, 요청 라우팅) → 네트워크 I/O 위주
  • 애플리케이션(주문 검증, 재고 계산 같은 비즈니스 로직) → CPU 연산 위주
  • DB(조회, 쓰기, 인덱스 스캔) → 디스크 I/O + 메모리(캐시) 위주
한 서버에 다 있으면 "CPU 사용률 80%"라는 지표 하나가 애플리케이션 로직 때문인지, DB가 디스크를 읽으면서 커널 레벨에서 CPU를 같이 쓴 건지 구분이 안 된다. 그리고 세 프로세스가 메모리를 두고 경쟁하기 때문에, DB 캐시가 커지면 애플리케이션이 쓸 메모리가 줄어드는 식으로 서로한테 영향을 준다.
나누는 기준은 **역할(계층)**이다. 사용자를 직접 상대하는 계층(Presentation), 비즈니스 로직을 처리하는 계층(Application), 데이터를 영속적으로 저장하는 계층(Data) — 이렇게 셋으로 나누면:
  • 각 서버의 지표(CPU/메모리/디스크)가 온전히 그 계층의 작업량만 반영하게 되어 원인 분리가 가능해짐
  • 하나를 재시작해도 나머지 두 계층은 프로세스 자체가 물리적으로 분리돼 있어 영향을 안 받음
  • 계층마다 필요한 리소스가 다르므로(웹은 네트워크, DB는 메모리·디스크 위주로), 각자에 맞는 사양으로 서버를 고를 수 있음
핵심 개념
  • 3-Tier Architecture: Presentation(표현) / Application(응용) / Data(데이터) 3개 계층으로 물리적·논리적으로 분리하는 설계
  • 핵심 목적: 장애 격리(Isolation) + 원인 관측 가능성(Observability) + 계층별 독립적 확장

2) Web / Application / DB 세 대로 나눔

문제: 사용자가 100명에서 10,000명으로 늘어남. Application 서버만 CPU 100%. 더 큰 서버로 교체해도 잠깐 버티다 다시 한계, 교체할 때마다 서비스를 내려야 함.
질문: 서버를 키우는 것 말고 다른 확장 방법은? 두 방식의 한계는 각각 무엇인가?
구체적인 설명
서버 확장에는 두 축이 있다.
Vertical Scaling(수직 확장, Scale-up)
  • 서버 1대의 CPU 코어 수, 메모리 용량을 올림 (예: AWS EC2라면 t3.medium → t3.2xlarge처럼 인스턴스 타입을 교체)
  • 한계 ①: 아무리 좋은 인스턴스 타입도 물리적 상한선이 있음 (클라우드 제공사가 제공하는 최대 스펙까지만 가능)
  • 한계 ②: 인스턴스 타입 변경은 보통 인스턴스 중지 → 타입 변경 → 재시작이 필요해서 몇 분간 서비스가 끊김(다운타임)
  • 한계 ③: 트래픽이 다시 늘면 또 똑같은 작업을 반복해야 함 — 근본적 해결이 아니라 "미루는" 것
Horizontal Scaling(수평 확장, Scale-out)
  • 같은 사양의 서버를 여러 대로 늘림 (Application 서버 1대 → 3대, 5대…)
  • 장점: 이론상 대수를 늘리는 데 제한이 거의 없고, 한 대를 새로 추가/제거해도 나머지는 서비스 중단 없이 계속 동작
  • 다만 이 방식이 실제로 작동하려면, 여러 대의 서버를 "하나처럼" 다루기 위한 앞단 장치가 필요해짐 — 사용자가 서버 여러 대 중 어디로 접속해야 할지 모르는 문제가 새로 생기기 때문 (다음 단계로 이어짐)
핵심 개념
  • Scale-up: 서버 성능 자체를 올림. 빠르고 간단하지만 상한선·다운타임 존재
  • Scale-out: 서버 대수를 늘림. 무중단·거의 무제한 확장 가능하지만 트래픽 분산 장치가 필수로 따라와야 함

3) Application 서버를 두 대로 늘림

문제: 사용자는 여전히 서버 한 대의 주소(IP/도메인)로만 접속함. 두 번째 서버는 트래픽을 전혀 받지 못함.
질문: 사용자 요청을 두 서버에 나눠주려면 앞단에 어떤 역할이 있어야 하는가? 그 역할은 어떤 기준으로 요청을 나누는가?
구체적인 설명
서버를 아무리 늘려도, "사용자가 알고 있는 주소"가 한 대만 가리키고 있으면 나머지는 죽은 자원이다. 여기 필요한 게 Load Balancer다.
동작 방식:
  1. 사용자는 로드밸런서의 주소 하나만 알고 접속함 (뒤에 App 서버가 몇 대인지는 전혀 모름)
  1. 로드밸런서가 요청을 받아서, 미리 등록된 서버 목록(Target Group) 중 하나로 트래픽을 전달
  1. 분배 기준(알고리즘)은 여러 가지가 있음:
      • Round Robin: 순서대로 돌아가면서 배정 (서버1 → 서버2 → 서버1 → …)
      • Least Connections: 현재 연결 수가 가장 적은 서버로 배정 (부하가 균등하지 않을 때 더 정교함)
  1. 뒤에서 서버를 추가하거나 빼도(Auto Scaling으로 자동 증감해도), 로드밸런서의 Target Group만 갱신되면 되고 사용자 쪽 주소는 전혀 안 바뀜 — 이게 로드밸런서가 "추상화 계층" 역할을 한다는 의미
핵심 개념
  • Load Balancing: 여러 서버로 요청을 분산시키는 기법. Horizontal Scaling이 실제로 작동하기 위한 필수 전제조건
  • AWS에서는 **ALB(Application Load Balancer)**가 이 역할 — HTTP/HTTPS 레벨(7계층)에서 URL 경로 기반 라우팅까지 가능

4) 앞단에 요청 분산 장치를 둠

문제: 로그인한 사용자가 페이지를 넘길 때마다 로그아웃됨. 요청이 App1로 갔다가 App2로 가면, App2는 이 사용자를 모름.
질문: 서버가 "이 사용자를 기억하는 상태"를 어디에 두어야 서버를 늘려도 문제가 없는가?
구체적인 설명
원인은 "로그인했다"는 정보(세션, Session)가 App1의 로컬 메모리 안에만 저장돼 있었기 때문이다. 로드밸런서는 매 요청을 어느 서버로 보낼지 그때그때 결정하기 때문에, 같은 사용자의 요청이라도 App1, App2로 번갈아 갈 수 있다. App2 입장에서는 이 사용자의 로그인 기록이 자기 메모리에 없으니 "로그인 안 한 사용자"로 취급하고 다시 로그인 화면을 보여준다.
두 가지 서버 설계 방식을 구분해야 한다.
  • Stateful: 서버가 사용자별 상태(세션 등)를 자기 메모리/디스크에 들고 있음. 이 방식이면 같은 사용자의 다음 요청은 반드시 같은 서버로 가야만 정상 동작함(이걸 강제하는 걸 Sticky Session이라 하는데, 이러면 결국 그 서버에 부하가 쏠리는 문제가 다시 생김)
  • Stateless: 서버 자체는 아무 상태도 안 갖고 있고, 매 요청마다 "이 사용자가 누구인지"를 확인하려면 외부의 공용 저장소를 조회함
해결책: 세션 데이터를 App 서버 밖의 별도 공용 저장소(예: Redis, Memcached 같은 인메모리 DB)로 빼낸다. 그러면 어떤 App 서버가 요청을 처리하든, 그 공용 저장소에 "이 사용자 로그인 상태"를 물어보면 동일한 답을 얻는다 — 서버가 몇 대든 상관없이 사용자 경험은 끊기지 않는다.
핵심 개념
  • Stateful vs Stateless: 서버가 사용자 상태를 자기 안에 보관하는지 여부
  • Horizontal Scaling이 되려면 애플리케이션 서버는 반드시 Stateless로 설계하고, 상태는 서버 밖 공용 저장소로 분리해야 함 (AWS라면 ElastiCache for Redis)

5) 세션을 서버 밖으로 뺌

문제: App1이 죽음. 로드밸런서는 이걸 모르고 여전히 App1로도 요청을 보냄. 사용자 절반이 에러를 봄.
질문: 서버가 죽었다는 것을 누가 어떻게 판단하고, 죽은 서버를 어떻게 트래픽에서 빼는가? 한 대가 죽어도 서비스가 유지되려면 최소 몇 대가 필요한가?
구체적인 설명
로드밸런서는 기본적으로 "서버 상태"를 스스로 알지 못한다. 그래서 로드밸런서가 각 서버에게 정기적으로(예: 30초마다) 확인 요청을 보내야 한다. 이걸 Health Check라고 한다.
동작 방식:
  1. 로드밸런서가 각 서버의 특정 경로(예: /health)로 HTTP 요청을 주기적으로 보냄
  1. 정상이면 200 OK 응답, 서버가 죽었거나 응답이 없으면 실패로 판단
  1. 연속으로 일정 횟수(예: 3회) 실패하면 → 그 서버를 자동으로 Target Group에서 제외, 이후 트래픽을 안 보냄
  1. 서버가 다시 정상 응답하면 → 자동으로 다시 트래픽 대상에 포함
"몇 대가 필요한가"에 대한 답은 최소 2대 이상. 1대뿐이면 그 서버가 죽는 순간 대체할 서버가 없어서 전체 서비스가 멈춘다(단일 장애점, Single Point of Failure). 이렇게 여분의 자원을 미리 두는 것을 **Redundancy(이중화)**라 부르고, 이런 식으로 일부가 죽어도 전체 서비스는 유지되는 상태를 **High Availability(고가용성, HA)**라고 한다.
핵심 개념
  • Health Check: 로드밸런서가 서버 생존 여부를 주기적으로 확인하고, 죽은 서버를 자동으로 트래픽 대상에서 제외하는 메커니즘
  • High Availability: 일부 구성 요소가 죽어도 서비스 전체는 계속 정상 동작하는 상태
  • Redundancy: 여분의 자원(서버, 인스턴스 등)을 미리 확보해두는 것 — HA를 실현하는 방법

6) App 서버를 다중화함

문제: DB는 아직 한 대이고, 처음에 편하다고 App1과 같은 서버에 뒀음. App1을 배포하느라 재시작했더니 DB도 같이 내려갔고, App2까지 전부 멈춤.
질문: 하나가 죽었을 때 같이 죽는 범위는 어디까지인가? DB를 왜 애플리케이션과 다른 장애 범위에 두어야 하는가?
구체적인 설명
DB를 App1과 같은 물리 서버(또는 같은 인스턴스)에 뒀다는 건, 이 둘이 하나의 장애 단위로 묶였다는 뜻이다. App1을 배포·재시작하면 그 서버의 프로세스 전체(App1 + DB)가 같이 내려간다. 그리고 DB가 죽으면, DB에 의존하는 App2도(같은 물리 서버에 있지 않더라도) DB 접속이 안 되니 서비스가 안 된다. 결국 App1 서버 하나의 재시작이 전체 서비스 마비로 번진 것이다.
이렇게 "하나의 장애가 함께 영향을 미치는 범위"를 **Failure Domain(장애 범위, 장애 도메인)**이라고 한다. 잘 설계된 시스템은 장애 범위를 최대한 잘게, 서로 독립적으로 쪼개서 하나가 죽어도 다른 게 안 죽게 만든다.
해결책: DB를 App 서버와 **물리적으로 완전히 별도의 서버(인스턴스)**로 독립시킨다.
  • App1을 재시작·배포해도 DB는 별개 프로세스/인스턴스이므로 영향받지 않음
  • App1이 죽어도 App2는 여전히 DB에 접근 가능해서 서비스 지속
  • DB는 데이터(상태)를 들고 있는 유일한 계층이라, App(Stateless)보다 훨씬 신중하게 다뤄야 함 — 예를 들어 DB는 Multi-AZ(서로 다른 물리적 데이터센터) 복제로 별도의 이중화를 하기도 함
핵심 개념
  • Failure Domain: 하나의 장애가 전파되는 범위. 장애 범위를 독립적으로 최대한 작게 쪼개는 게 안정적인 설계의 기본 원칙
  • App(무상태, 쉽게 늘리고 죽여도 됨) vs DB(상태 보유, 더 신중하게 관리) — 이 성격 차이 때문에 애초에 3-Tier에서 Data 계층을 별도로 분리하는 것

7) 구조가 정리됨 — AWS 서비스 매핑

질문: 지금까지 그린 구조의 각 역할(가상 네트워크, 요청 분산, 컴퓨팅, 관계형 DB, DNS)을 AWS에서는 어떤 서비스가 담당하는가?
구체적인 설명 + 매핑표
지금까지 만든 역할
AWS 서비스
구체적인 담당 기능
가상 네트워크
VPC (Virtual Private Cloud)
클라우드 안에 논리적으로 격리된 내 전용 네트워크 구역. Subnet, 라우팅 등 이후 모든 네트워크 구성의 기반
요청 분산 장치
ALB (Application Load Balancer)
Round Robin/Least Connections 기반 트래픽 분산 + Health Check 기본 내장
컴퓨팅 (Web/App 서버)
EC2 + Auto Scaling Group
가상 서버 인스턴스. ASG를 함께 쓰면 "다중화"를 트래픽에 따라 자동으로 늘리고 줄여줌
세션 저장소
ElastiCache for Redis
App 서버 밖에 두는 공용 세션 저장소
관계형 DB
RDS
관리형 DB. Multi-AZ 옵션을 켜면 "DB 독립 장애범위" 요구사항을 AWS가 자동으로 구현해줌(자동 장애조치 포함)
DNS (도메인 → 서버 주소 연결)
Route 53
사용자가 도메인으로 접속하면 실제 ALB 주소로 연결해주는 DNS 서비스
정리하면: 앞서 하나씩 부딪히며 해결해온 문제들(계층 분리, 수평 확장, 트래픽 분산, 세션 외부화, 헬스체크·다중화, 장애범위 분리)을, 실제 AWS는 이미 관리형 서비스 형태로 제공한다. VPC 안에 EC2(Web/App)를 여러 대 띄우고 ALB로 앞단에서 분산하며, 세션은 ElastiCache로, DB는 RDS로 분리하고, Route 53으로 도메인을 연결하는 구조가 지금까지 그려온 설계의 실제 구현체다.

✏️ 정리하면서 든 생각

3-Tier Architecture는 처음부터 정답으로 존재하는 게 아니라, 문제를 하나씩 겪으면서 필요에 의해 만들어진 구조라는 걸 알게 됐다. 서버 분리 → 로드밸런싱 → 세션 외부화 → 헬스체크/다중화 → 장애 범위 분리라는 순서가, 실제로 서비스가 커질 때 마주치는 순서와 정확히 일치한다는 점이 인상 깊었다.