🎯 목표 설정
AWS 클라우드 기반 CERT 인프라 설계를 위해 3-Tier Architecture 구성에 대해 이해하고, 실제 팀 근두운 프로젝트에서 AWS SecOps 인프라 구축 시 고려해야할 부분이 어떤 것이 있는지를 학습하는 것을 목표로 한다.
🔍 3-Tier Architecture
3-Tier Architecture는 시스템을 역할에 따라 Presentation Tier, Application Tier, Data Tier 3가지 계층으로 분리된다.
Tier | 역할 |
Presentation(Client) | 사용자가 직접 액세스할 수 있게 화면을 보여주고 입력을 받는 계층(WEB)
(HTML, CSS, JavaScript, React 등) |
Application(Logic) | 서비스 규칙에 따라 요청을 처리하는 계층(WAS)
(Java, C++, Python과 같은 백엔드 언어) |
Data | 애플리케이션에 필요한 데이터를 저장하고 조회하는 역할(DBMS)
(MySQL, Postgres 등) |
일반적으로 플랫폼이나 웹 애플리케이션 서버를 구성할 때, 대부분의 기업/기관에서는 3-Tier Architecture 구성을 기본으로 하고 있다.
여기서 3-Tier는 각 서비스의 실행 환경을 물리적・배포 단위로 분리하는 것을 의미하며, 쇼핑몰과 같은 작은 이커머스 서비스를 예시로 들면, 상품 목록과 조회와 같은 UI・UX를 제공하는 웹 서버, 상품 주문 시 주문이라는 요청을 처리하는 로직을 가진 WAS 서버, 해당 로직 처리를 위해 실제 데이터를 저장하고 조회하는 DB 서버가 각각 물리적으로 분리된 구성을 얘기한다.

각 서비스 역할별로 계층이 물리적으로 분리되어 있어
유지보수 용이성, 확장성, 보안 강화의 이점이 있어 플랫폼 또는 서비스 설계 시 고려하면 좋은 구성으로, Failure Domain(장애 영역)을 분리할 수도 있어 가용성의 이점도 가진다.현재 우리 팀 근두운 프로젝트의 인프라 중 Wazuh SIEM을 보면, 하나의 EC2 인스턴스에 Wazuh 서비스의 Manager, Indexer, Dashboard가 함께 구성된 것을 볼 수 있다. 즉, Presentation, Application, Data Tier의 기능을 하나의 EC2가 담당(자원을 공유)하는 것이다.

해당 구성은 1-Tier Architecture로 하나의 물리적인 서버에 3가지의 다른 기능을 함께 구현하는 방식이다.
비용 절감과 구성의 단순함 이라는 이점에 있어서는 소규모 프로젝트에서 적합한 방식일 수도 있지만, 장애가 발생하면 모든 서비스가 마비되는 단일 장애점, 자원 경합으로 인한 속도 지연, 확장성 제한, 보안 취약성 등의 한계가 분명하다.따라서 팀 근두운 인프라에서 3-Tier Architecture를 적용 할지에 대해 프로젝트 목표를 근거로 고민할 필요가 있다.
하지만, 3-Tier Architecture 구성을 하였다고 해서, 모든 한계가 해결되는 것은 아니다. 3-Tier Architecture는 플랫폼 또는 서비스를 구축할 때의 뼈대가 되는 구성 방식일 뿐이다.
실제로 쇼핑몰 플랫폼을 3-Tier Architecture로 구성 하였다고 해도 사용자가 급증하면, 비즈니스 로직을 처리하는 WAS 서버만 자원의 한계가 발생하는데, 이를 해결하기 위한 방법에서 해당 구성의 이점이 발휘하게 된다.
3-Tier Architecture는 확장성의 이점을 가진다고 앞서 설명 하였는데, 여기서
Vertical Scaling(Scale-up)과 Horizontal Scaling(Scale-out) 2가지의 확장 방식을 활용할 수 있다.구분 | Vertical Scaling(Scale-up) | Horizontal Scaling(Scale-out) |
특징 | 기존의 서버의 하드웨어 스펙을 더 높은 사양으로 업그레이드 하는 확장 방식 | 서버 하드웨어 스펙을 올리지 않고, 비슷한 성능의 서버를 여러 대 추가하여 부하를 분산하는 확장 방식
(고가용성) |
한계 | 여전히 단일 장애점을 가지고, 부품 교체 시 서비스 중단이 발생(가용성의 한계) | 서버가 여러 대로 늘어나면 요청을 나누어 전달하고, 필요한 상태와 데이터를 서버 간에 공유하거나 나누어 관리해야 하므로 설계와 운영이 복잡해질 수 있음 |
각 계층의 특성과 병목에 따라
Scale-up과 Scale-out을 선택하거나 병행하는 것이 일반적이다.

Horizontal Scaling 방식으로 서버의 대수를 늘렸다고 해도, 즉시 부하가 분산되는 것은 아니다. 이는 위 확장 방식 비교 표에서 얘기했던, 설계 및 운영의 복잡성에 대한 한계가 드러나는 부분이다.서버를 단순히 늘린 것만으로는 실제 트래픽이 늘어난 서버로 가지는 못하는 것이다. 그래서 실제 구성 시에는 트래픽을 분산해서 각 서버로 나눠주는 역할이 필요하다. 이를 지원하는 것이 바로
Load Balancer이다.Load Balancer가 Load Balancing을 통해 서버가 처리해야 하는 대량의 트래픽을 여러 대의 서버로 나누어 처리하게 해주어 부하를 분산 시킨다. 예를 들어 두 명의 손님이 계산을 하려고 할 때, 한 명은 1번 계산대 나머지 한 명은 2번 계산대에서 처리하여 부하가 몰리는 것을 나눠주는 것이다.
Load Balancer는 설정한 분배 방식, 서버의 상태, 요청의 정보를 기준으로 트래픽을 여러 서버로 나눈다. 대표적인 로드 밸런싱 알고리즘은 다음과 같다.방식 | 분배 기준 |
라운드 로빈 | 요청을 여러 대의 서버에 순차적으로 분배 |
가중 라운드 로빈 | 설정한 가중치에 비례하여 요청을 분배, 가중치가 높은 서버에 더 많은 요청을 전달 |
최소 연결 | 요청이 들어온 시점에 연결 수가 가장 적은 서버로 전달 |
최소 응답 시간 | 서버의 현재 연결 상태를 고려하여 응답이 빠른 서버에 우선적으로 전달 |
IP 해시 | 사용자의 IP 주소를 계산해 서버로 전달(같은 IP는 대체로 같은 서버로 연결) |
이제는 부하 분산 구성이 완성되었다. 하지만 문제는 여전히 존재한다. WAS #1 서버가 처리한 내용을 WAS #2는 모른 다는 것이다. 즉, 사용자가 로그인 서비스를 통해 로그인 하였지만, 다음 페이지에서는 로그인 상태를 기억하지 못하는 것이다.
이처럼 사용자가 로그인한 뒤에도 여러 요청에 걸쳐 로그인 상태를 유지할 수 있도록 관리하는 것을 세션이라고 한다. 이러한 상태를 관리하는 방식은 서버 측에 상태를 저장하는
Stateful 방식과, 요청에 포함된 토큰을 검증하는 Stateless 방식으로 나누어 살펴볼 수 있다.Stateful 방식은 사용자의 상태 정보를 서버의 메모리나 별도 저장소에 저장해 두는 방식이다. 하지만 WAS #1의 메모리에만 로그인 상태를 저장한다면, WAS #2에서는 해당 상태를 확인할 수 없다. 따라서 현재처럼 WAS 서버가 여러 대인 구성에서는 Redis나 DB 같은 공용 세션 저장소에 상태를 저장하고, 각 WAS가 이를 조회하도록 구성하여 이 문제를 해결할 수 있다.반대로
Stateless 방식은 서버가 사용자별 세션 상태를 별도로 저장하지 않고, 사용자가 요청과 함께 보내는 토큰을 검증하여 처리하는 방식이다. 사용자는 로그인 시 발급받은 토큰을 보관하고 이후 요청마다 함께 전송한다. 각 WAS는 토큰의 서명과 만료 시간 등을 검증하므로, 다른 WAS가 요청을 받더라도 사용자의 인증 정보를 확인할 수 있다. 여기서 서명 검증은 토큰이 신뢰하는 발급자가 만든 것이며 내용이 변조되지 않았는지를 확인하는 과정이다.다만 쿠키는 세션 ID와 토큰 모두를 보관할 수 있으므로, 쿠키 사용 여부로 Stateful과 Stateless를 구분하는 것은 아니다.
구분 | Stateful 세션 | Stateless 세션 |
상태 저장 위치 | 서버(메모리, Disk, Redis 등) | 클라이언트(브라우저 쿠키, 로컬스토리지) |
대표 기술 | 세션 ID | JWT |
여러 서버에서
처리하는 방식 | 공용 세션 저장소 사용 | 각 서버가 사용자의 토큰을 검증 |
세션 강제 제어 | 서버에 저장된 세션을 삭제 또는 무효화 | 별도의 무효화 장치가 없다면, 만료 전 개별
토큰을 즉시 폐기하기 어려움 |
웹 브라우저가 서버에 보내는 것 | 세션 ID | 서명된 토큰 |


이제 사용자가 로그인을 하면, 다른 서비스를 원활하게 사용할 수 있게 된다.
그런데 만약, WAS 서버 #1에서 장애가 발생하여 죽었을 경우에 사용자가 요청을 보내면 어떻게 될까?
당연히
Load Balancer가 살아있는 서버로 요청을 전달 하겠지 라고 생각할 수 있지만, 이것은 반은 맞고 반은 틀리다.Load Balancer가 서버 상태를 확인하고 죽었을 경우에는 트래픽 전달에서 제외하는 역할을 하지만, 이는 Health Check가 선행되어야 가능하다.Health Check는 Load Balancer가 각 서버의 /health 같은 주소로 주기적으로 요청을 보내 응답 상태에 따라 정상/비정상 상태를 구분한다. 그리고 비정상 상태로 판단된 서버에 대해서는 트래픽을 전달하지 않도록 설정해주는 것이 필요하다.비정상 상태를 판단하는 것은 검사 주기(interval), 응답 대기 시간(Timeout), 횟수(Healthy/Unhealthy Threshold)와 같은 임계값(Threshold) 설정에 따라 달라진다.
Health Check가 실패하여 비정상 상태로 판단된 서버는 일반적으로 트래픽 전달 대상에서 제외되고, 이후 새 요청은 남아 있는 정상 서버로 전달된다. 그런데 이러한 방식으로 서비스를 계속 제공하려면, 같은 역할을 수행하는 서버가 미리 둘 이상 준비되어 있어야 한다. 이처럼 장애에 대비해 같은 역할의 구성요소를 중복으로 마련하는 것을 Redundancy(이중화 또는 다중화)라고 한다.현재 구성에서는 WAS 서버를 두 대로 이중화하고, Load Balancer가 헬스 체크 결과에 따라 정상 서버로 요청을 전달하도록 하였다. 이를 통해 서버 한 대에 장애가 발생하더라도 나머지 서버가 요청을 처리하여
고가용성(HA)을 높일 수 있다. 고가용성이란 장애가 발생하더라도 서비스를 계속 제공할 수 있는 성질을 말한다. 다만 이를 위해서는 남은 서버가 요청을 감당할 수 있어야 하고, 서비스에 필요한 데이터와 세션도 계속 사용할 수 있어야 한다.이중화 방식에는 여러 서버가 동시에 요청을 처리하는
Active-Active 방식과, 주 서버가 요청을 처리하고 예비 서버는 대기하다가 장애 발생 시 처리를 이어받는 Active-Standby 방식이 있다. 장애가 발생했을 때 정상 구성요소로 처리를 전환하는 것을 Failover라고 한다.
WAS 서버의 고가용성을 위한 이중화 구성까지 완성하였다.
앞의 단계에서 거쳤던 과정들을 모두 적용하여 3-Tier Architecture의 이상적인 구조도를 그려보면 다음과 같다.

각 구성요소들의 역할을 AWS에서 어떤 서비스가 담당하는지 살펴보면 다음 표로 설명할 수 있다.
이전 구성요소 | AWS 서비스 | 역할(AWS) |
DNS 서버
(웹 브라우저에서 도메인 이름으로 서비스 접속) | Route 53 | 도메인 이름으로 접속할 주소를 찾을 수 있도록 해주는 관리형 DNS 서비스, 사용자가 브라우저에 service.kintoun.work과 같은 도메인 이름을 입력하면, DNS 조회를 통해 ALB의 접속 주소를 알아내고 해당 주소로 웹 요청을 보낼 수 있도록 한다. |
Load Balancer | ALB | 헬스 체크를 통해 사용자의 요청에 대한 트래픽을 분산하여 처리 해준다. |
Web/WAS 서버 | EC2 | AWS가 제공하는 가상 서버로 WEB 서버와, WAS 서버를 올려서 사용할 수 있도록 해준다. |
DB서버 | RDS | AWS 관리형 RDB 서비스로 MySQL, PostgreSQL과 같은 관계형 데이터베이스를 사용할 수 있도록 해준다. |
위 서비스들은 앞에서 설계한 구성요소들을 AWS 환경에서 구현하기 위해 선택한 서비스들이다. 이를 활용하여 보안성과 가용성을 고려한 3-Tier Architecture를 구성할 수 있다.
이제 완성된 최종 구조도를 AWS 서비스와 매핑해보면, 다음과 같은 최종 구조도가 완성된다.

💁 마무리
이번 과정에서는 3-Tier Architecture 구성을 설계하는 방법을 단계별로 확인하였다. 이러한 구성을 팀 근두운에도 적용하여 인프라를 설계한다면, 조금 더 완벽한 인프라 구축이 가능할 것으로 기대된다.
다음 과정에서는 이번 과정에서 생성한 서비스들을 네트워크에 어떻게 배치해야 하는지에 대해 학습해 볼 예정이다.
