본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 23. · 3 Views
AWS 로드 밸런서와 보안 그룹으로 트래픽 분산 구성 완벽 가이드
초급 개발자를 위한 AWS 로드 밸런서와 보안 그룹 실습 가이드입니다. Application Load Balancer와 Network Load Balancer의 차이부터 보안 그룹 설정, 대상 그룹 생성, 실제 EC2 연결까지 실무에 필요한 모든 내용을 이북처럼 술술 읽히는 스타일로 담았습니다.
목차
- 로드_밸런서의_역할과_종류
- Application_Load_Balancer_vs_Network_Load_Balancer
- 보안_그룹의_개념
- 인바운드_아웃바운드_규칙_설정
- 대상_그룹_생성
- 로드_밸런서_생성_및_EC2_연결_실습
1. 로드 밸런서의 역할과 종류
어느 날 신입 개발자 김개발 씨는 자신이 만든 웹 서비스가 갑자기 느려지는 현상을 발견했습니다. 사용자가 100명일 때는 괜찮았는데, 1000명이 되자 서버가 버거워하기 시작했습니다.
선배 개발자 박시니어 씨가 다가와 말했습니다. "이제 로드 밸런서를 도입할 때가 됐네요."
로드 밸런서는 여러 대의 서버로 트래픽을 골고루 분산시켜주는 장치입니다. 마치 은행의 번호표 시스템처럼, 많은 고객이 몰려도 여러 창구로 적절히 안내해 대기 시간을 줄여줍니다.
AWS에서는 Application Load Balancer(ALB), Network Load Balancer(NLB), Classic Load Balancer 등 다양한 종류를 제공합니다.
다음 코드를 살펴봅시다.
// AWS SDK를 사용한 로드 밸런서 정보 조회 예제
const AWS = require('aws-sdk');
const elbv2 = new AWS.ELBv2({ region: 'ap-northeast-2' });
// 로드 밸런서 목록 조회
async function describeLoadBalancers() {
try {
const result = await elbv2.describeLoadBalancers().promise();
// 각 로드 밸런서의 정보 출력
result.LoadBalancers.forEach(lb => {
console.log(`이름: ${lb.LoadBalancerName}`);
console.log(`타입: ${lb.Type}`); // application, network, gateway
console.log(`DNS: ${lb.DNSName}`);
console.log(`상태: ${lb.State.Code}`); // active, provisioning 등
});
} catch (error) {
console.error('로드 밸런서 조회 실패:', error.message);
}
}
김개발 씨는 입사 2개월 차 프론트엔드 개발자입니다. 회사에서 처음으로 맡은 프로젝트인 쇼핑몰 웹사이트가 드디어 오픈했습니다.
처음 며칠은 순조로웠습니다. 하루 방문자가 100명 정도였고, 서버도 잘 돌아갔습니다.
그런데 SNS에서 입소문이 나면서 상황이 달라졌습니다. 어느 날 갑자기 동시 접속자가 1000명을 넘어섰고, 웹사이트가 느려지기 시작했습니다.
일부 사용자는 아예 접속조차 되지 않았습니다. 김개발 씨는 당황했습니다.
선배 개발자 박시니어 씨가 모니터링 화면을 보더니 말했습니다. "예상했던 일이에요.
이제 서버 한 대로는 감당이 안 되는 거죠." 그렇다면 로드 밸런서란 정확히 무엇일까요? 쉽게 비유하자면, 로드 밸런서는 마치 백화점 입구의 안내 직원과 같습니다.
고객이 몰려들면 빈 엘리베이터를 찾아 안내하고, 특정 엘리베이터에만 사람이 몰리지 않도록 조절합니다. 이처럼 로드 밸런서도 들어오는 네트워크 트래픽을 여러 서버에 골고루 분배하는 역할을 담당합니다.
로드 밸런서가 없던 시절에는 어땠을까요? 개발자들은 단일 서버로 모든 요청을 처리해야 했습니다.
트래픽이 증가하면 서버의 CPU와 메모리를 업그레이드하는 수직 확장만 가능했습니다. 하지만 이것도 한계가 있었습니다.
더 큰 문제는 서버 한 대가 다운되면 서비스 전체가 멈춘다는 점이었습니다. 바로 이런 문제를 해결하기 위해 로드 밸런서가 등장했습니다.
로드 밸런서를 사용하면 수평 확장이 가능해집니다. 서버를 더 크게 만드는 게 아니라, 같은 크기의 서버를 여러 대 추가하는 방식입니다.
또한 고가용성도 얻을 수 있습니다. 서버 한 대가 다운되어도 나머지 서버들이 계속 서비스를 제공합니다.
무엇보다 트래픽 급증에 유연하게 대응할 수 있다는 큰 이점이 있습니다. AWS는 세 가지 주요 로드 밸런서를 제공합니다.
첫 번째는 **Application Load Balancer(ALB)**입니다. HTTP와 HTTPS 트래픽을 처리하며, URL 경로나 호스트 기반으로 라우팅할 수 있습니다.
웹 애플리케이션에 가장 많이 사용됩니다. 두 번째는 **Network Load Balancer(NLB)**입니다.
TCP와 UDP 트래픽을 처리하며, 초당 수백만 개의 요청을 처리할 수 있을 만큼 고성능입니다. 실시간 게임 서버나 IoT 서비스에 적합합니다.
세 번째는 Classic Load Balancer입니다. 구세대 로드 밸런서로, 이제는 거의 사용하지 않습니다.
새로운 프로젝트라면 ALB나 NLB를 선택하는 것이 좋습니다. 위의 코드를 살펴보겠습니다.
먼저 AWS SDK를 불러오고 ELBv2 클라이언트를 생성합니다. 여기서 ELBv2는 Elastic Load Balancing Version 2의 약자입니다.
describeLoadBalancers 메서드를 호출하면 현재 계정에 있는 모든 로드 밸런서 정보를 가져올 수 있습니다. 반환된 데이터에서 각 로드 밸런서의 이름, 타입, DNS 이름, 상태를 확인할 수 있습니다.
DNS 이름은 특히 중요한데, 이 주소로 트래픽을 보내면 로드 밸런서가 자동으로 백엔드 서버들에게 분산시켜줍니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 쇼핑몰 서비스를 개발한다고 가정해봅시다. 평소에는 서버 2대로 충분하지만, 블랙프라이데이 같은 특별 세일 기간에는 트래픽이 10배 이상 증가합니다.
로드 밸런서를 사용하면 Auto Scaling과 연계하여 트래픽에 따라 서버를 자동으로 늘렸다 줄였다 할 수 있습니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 로드 밸런서만 설정하면 모든 문제가 해결될 거라고 생각하는 것입니다. 실제로는 세션 관리, 데이터베이스 연결, 파일 업로드 등 고려해야 할 사항이 많습니다.
예를 들어 세션을 서버 메모리에 저장한다면, 같은 사용자의 요청이 매번 다른 서버로 가면 로그인이 풀리는 문제가 발생합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "그럼 우리 서비스에는 어떤 로드 밸런서가 필요할까요?" 로드 밸런서를 제대로 이해하면 확장 가능하고 안정적인 서비스를 구축할 수 있습니다.
다음 섹션에서는 ALB와 NLB의 구체적인 차이점을 알아보겠습니다.
실전 팁
💡 - 웹 애플리케이션이라면 대부분 ALB가 적합합니다
- 로드 밸런서는 여러 가용 영역에 분산 배치하여 장애 대응력을 높이세요
- 비용을 고려하여 트래픽 패턴에 맞는 타입을 선택하세요
2. Application Load Balancer vs Network Load Balancer
박시니어 씨는 화이트보드에 그림을 그리며 설명을 시작했습니다. "ALB와 NLB는 둘 다 로드 밸런서지만, 작동하는 계층이 다릅니다." 김개발 씨는 궁금해졌습니다.
둘의 차이가 뭐길래 따로 만들었을까요?
**Application Load Balancer(ALB)**는 OSI 7계층 중 애플리케이션 계층에서 작동하며, HTTP/HTTPS 트래픽을 처리합니다. **Network Load Balancer(NLB)**는 전송 계층에서 작동하며, TCP/UDP 트래픽을 초고속으로 처리합니다.
마치 택배 회사에서 일반 배송과 당일 배송을 구분하는 것처럼, 용도에 따라 적절한 선택이 필요합니다.
다음 코드를 살펴봅시다.
// ALB용 리스너 규칙 생성 예제 (경로 기반 라우팅)
const createALBRule = async () => {
const params = {
ListenerArn: 'arn:aws:elasticloadbalancing:...',
Priority: 10,
Conditions: [{
Field: 'path-pattern',
Values: ['/api/*'] // /api로 시작하는 경로는 API 서버로
}],
Actions: [{
Type: 'forward',
TargetGroupArn: 'arn:aws:elasticloadbalancing:...:targetgroup/api-servers'
}]
};
const result = await elbv2.createRule(params).promise();
console.log('라우팅 규칙 생성 완료:', result.Rules[0].RuleArn);
};
김개발 씨는 박시니어 씨의 설명을 들으며 노트에 열심히 메모했습니다. "그러니까 둘 다 트래픽을 분산시키는 건 같은데, 방식이 다르다는 거죠?" 박시니어 씨가 고개를 끄덕였습니다.
"정확해요. 어떤 종류의 트래픽을 처리하느냐에 따라 선택이 달라집니다." Application Load Balancer를 자세히 살펴봅시다.
ALB는 HTTP와 HTTPS 프로토콜을 이해합니다. 단순히 패킷을 전달하는 게 아니라, 요청의 내용을 분석할 수 있습니다.
마치 우체국 직원이 편지를 받아서 주소를 보고 어느 구역으로 보낼지 결정하는 것과 같습니다. 예를 들어 같은 도메인이지만 URL 경로에 따라 다른 서버로 보낼 수 있습니다.
example.com/web으로 오는 요청은 웹 서버 그룹으로, example.com/api로 오는 요청은 API 서버 그룹으로 라우팅하는 식입니다. 이를 경로 기반 라우팅이라고 합니다.
또한 호스트 기반 라우팅도 가능합니다. www.example.com과 api.example.com을 같은 로드 밸런서에서 처리하면서도 각각 다른 서버 그룹으로 보낼 수 있습니다.
심지어 HTTP 헤더나 쿼리 문자열을 기준으로도 라우팅할 수 있습니다. ALB의 또 다른 강력한 기능은 WebSocket과 HTTP/2 지원입니다.
실시간 채팅 서비스나 게임 서버처럼 양방향 통신이 필요한 경우에도 문제없이 사용할 수 있습니다. 이제 Network Load Balancer를 살펴봅시다.
NLB는 훨씬 빠릅니다. HTTP 내용을 분석하지 않고 패킷을 그대로 전달하기 때문입니다.
마치 택배 분류 센터에서 상자를 열어보지 않고 바로 컨베이어 벨트에 올리는 것과 같습니다. NLB는 초당 수백만 개의 요청을 처리할 수 있습니다.
레이턴시도 극도로 낮아서 밀리초 단위 응답이 중요한 서비스에 적합합니다. 게다가 고정 IP 주소를 제공하므로, 방화벽 규칙을 설정하기가 쉽습니다.
TCP나 UDP를 사용하는 서비스라면 NLB가 정답입니다. 예를 들어 데이터베이스 서버, MQTT 기반 IoT 서비스, 실시간 게임 서버 등이 있습니다.
TLS 암호화도 지원하므로 보안도 걱정할 필요가 없습니다. 위의 코드를 분석해봅시다.
이 코드는 ALB의 강력한 기능 중 하나인 경로 기반 라우팅을 설정하는 예제입니다. ListenerArn은 로드 밸런서에 연결된 리스너의 ARN입니다.
Priority는 규칙의 우선순위로, 숫자가 낮을수록 먼저 평가됩니다. Conditions 부분이 핵심입니다.
여기서는 path-pattern을 사용하여 /api로 시작하는 모든 경로를 지정했습니다. 이 조건에 맞는 요청은 Actions에 정의된 대로 api-servers 타겟 그룹으로 전달됩니다.
이런 유연한 라우팅은 NLB에서는 불가능합니다. NLB는 포트 번호만으로 라우팅을 결정하기 때문입니다.
실무에서는 어떻게 선택할까요? 대부분의 웹 애플리케이션은 ALB를 사용합니다.
REST API 서버, 웹사이트, 모바일 앱 백엔드 등이 여기에 해당합니다. HTTP 프로토콜을 사용하고, 경로나 헤더 기반으로 라우팅해야 한다면 ALB가 최선의 선택입니다.
반면 금융 거래 시스템처럼 극한의 성능이 필요하거나, 게임 서버처럼 UDP를 사용하거나, 고정 IP가 필요한 경우라면 NLB를 선택해야 합니다. 또한 HTTP가 아닌 프로토콜을 사용하는 레거시 애플리케이션도 NLB가 적합합니다.
하지만 주의할 점도 있습니다. 초보자들이 흔히 하는 실수는 무조건 NLB가 빠르다는 이유로 선택하는 것입니다.
웹 애플리케이션에 NLB를 사용하면 경로 기반 라우팅, 쿠키 기반 세션 고정, WAF 통합 같은 유용한 기능들을 사용할 수 없습니다. 성능만 보지 말고 필요한 기능을 먼저 파악해야 합니다.
김개발 씨가 질문했습니다. "그럼 우리 쇼핑몰은 웹 서비스니까 ALB를 써야겠네요?" 박시니어 씨가 미소 지으며 답했습니다.
"정확합니다. 나중에 모바일 앱을 추가하더라도 ALB 하나로 웹과 앱 API를 모두 처리할 수 있어요." ALB와 NLB의 차이를 이해하면 프로젝트에 맞는 최적의 선택을 할 수 있습니다.
다음에는 이 로드 밸런서를 안전하게 지키는 보안 그룹에 대해 알아보겠습니다.
실전 팁
💡 - 웹 서비스라면 90% 이상 ALB가 정답입니다
- 고정 IP가 필요하다면 NLB를 선택하거나, ALB 앞단에 NLB를 추가할 수 있습니다
- 비용은 ALB가 약간 높지만, 제공하는 기능을 고려하면 충분히 가치가 있습니다
3. 보안 그룹의 개념
로드 밸런서 설정을 시작하려던 김개발 씨는 AWS 콘솔에서 "보안 그룹"이라는 항목을 발견했습니다. 필수 항목이라 건너뛸 수도 없었습니다.
"이건 또 뭐죠?" 박시니어 씨가 웃으며 말했습니다. "AWS에서 가장 중요한 보안 장치 중 하나입니다."
'웹 서버용 보안 그룹', VpcId: 'vpc-12345678' }).promise(); const sgId = sgResult.GroupId; console.log('보안 그룹 생성:', sgId); // HTTP 트래픽 허용 규칙 추가 (인바운드) await ec2.authorizeSecurityGroupIngress({ GroupId: sgId, IpPermissions: [{ IpProtocol: 'tcp', FromPort: 80, ToPort: 80, IpRanges: [{ CidrIp: '0.0.0.0/0', Description: '모든 곳에서 HTTP 허용' }] }] }).promise(); console.log('HTTP(80) 포트 개방 완료'); }
다음 코드를 살펴봅시다.
// AWS SDK로 보안 그룹 생성 및 규칙 추가
const ec2 = new AWS.EC2({ region: 'ap-northeast-2' });
async function createSecurityGroup() {
// 보안 그룹 생성
const sgResult = await ec2.createSecurityGroup({
GroupName: 'web-server-sg',
김개발 씨는 처음 AWS를 접했을 때 모든 설정이 복잡하게 느껴졌습니다. VPC, 서브넷, 라우팅 테이블, 보안 그룹까지.
하나하나가 낯설었습니다. 하지만 박시니어 씨는 말했습니다.
"이 중에서 가장 중요한 게 보안 그룹이에요." 그렇다면 보안 그룹이란 정확히 무엇일까요? 쉽게 비유하자면, 보안 그룹은 건물 입구의 보안 시스템과 같습니다.
출입증이 있는 사람만 들어올 수 있고, 특정 층으로만 갈 수 있도록 통제합니다. 누가 어디서 왔는지, 어느 층으로 가려는지를 체크합니다.
보안 그룹도 이와 똑같이 어디서 오는 트래픽인지, 어느 포트로 가는지를 검사합니다. 보안 그룹이 없던 시절에는 어땠을까요?
과거 물리 서버 시대에는 하드웨어 방화벽을 별도로 구매해서 설치했습니다. 설정도 복잡했고, 변경할 때마다 네트워크 담당자에게 요청해야 했습니다.
실수로 잘못된 규칙을 설정하면 서비스 전체가 다운되기도 했습니다. 더 큰 문제는 비용이었습니다.
서버가 늘어날 때마다 방화벽도 추가로 구매해야 했습니다. 바로 이런 문제를 해결하기 위해 보안 그룹이 등장했습니다.
보안 그룹을 사용하면 소프트웨어로 방화벽을 관리할 수 있습니다. 콘솔에서 클릭 몇 번이면 규칙을 추가하거나 수정할 수 있습니다.
또한 무료입니다. 아무리 많은 규칙을 만들어도 추가 비용이 없습니다.
무엇보다 상태 저장 방식이라는 큰 장점이 있습니다. 상태 저장 방식이란 무엇일까요?
예를 들어 웹 서버가 외부 API를 호출한다고 가정해봅시다. 요청은 서버에서 나가고, 응답은 들어옵니다.
전통적인 방화벽이라면 나가는 규칙과 들어오는 규칙을 모두 설정해야 합니다. 하지만 보안 그룹은 요청을 기억합니다.
나가는 요청을 허용했다면, 그에 대한 응답은 자동으로 허용됩니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 createSecurityGroup으로 새로운 보안 그룹을 만듭니다. GroupName은 알아보기 쉬운 이름을, Description에는 용도를 적습니다.
VpcId는 보안 그룹이 속할 VPC를 지정합니다. 모든 보안 그룹은 반드시 하나의 VPC에 속해야 합니다.
그다음 authorizeSecurityGroupIngress로 인바운드 규칙을 추가합니다. IpProtocol은 tcp, udp, icmp 중 하나를 선택합니다.
FromPort와 ToPort로 포트 범위를 지정하는데, 단일 포트라면 같은 값을 씁니다. IpRanges의 CidrIp는 허용할 IP 범위입니다.
0.0.0.0/0은 모든 IP를 의미합니다. 실제 현업에서는 어떻게 활용할까요?
대부분의 웹 서비스는 최소 세 개의 보안 그룹을 사용합니다. 첫째는 로드 밸런서용 보안 그룹으로, 인터넷에서 오는 HTTP(80)와 HTTPS(443) 트래픽을 허용합니다.
둘째는 웹 서버용 보안 그룹으로, 로드 밸런서에서 오는 트래픽만 허용합니다. 셋째는 데이터베이스용 보안 그룹으로, 웹 서버에서 오는 트래픽만 허용합니다.
이렇게 계층별로 보안 그룹을 나누면 최소 권한 원칙을 구현할 수 있습니다. 외부에서 직접 데이터베이스에 접근하는 것을 원천적으로 차단하는 것입니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 모든 포트를 0.0.0.0/0으로 열어버리는 것입니다.
"일단 되게 만들고 나중에 수정하자"는 생각이 위험합니다. 한번 해킹당하면 돌이킬 수 없습니다.
처음부터 필요한 포트만 열고, 필요한 IP 범위만 허용해야 합니다. 또 다른 실수는 보안 그룹 규칙이 즉시 적용된다는 점을 모르는 것입니다.
수정하자마자 바로 반영되므로, 실수로 중요한 규칙을 지우면 즉시 서비스에 영향을 줍니다. 변경하기 전에 항상 현재 규칙을 백업하거나 스크린샷을 찍어두는 습관이 중요합니다.
김개발 씨가 물었습니다. "그럼 보안 그룹 여러 개를 하나의 인스턴스에 적용할 수 있나요?" 박시니어 씨가 고개를 끄덕였습니다.
"네, 최대 5개까지 가능합니다. 규칙이 많아지면 관리하기 편하게 역할별로 나눠서 적용할 수 있어요." 보안 그룹을 제대로 이해하면 안전하고 체계적인 인프라를 구축할 수 있습니다.
다음에는 인바운드와 아웃바운드 규칙을 구체적으로 어떻게 설정하는지 알아보겠습니다.
실전 팁
💡 - 보안 그룹 이름에 용도를 명확히 표시하세요 (예: web-alb-sg, web-server-sg, db-sg)
- SSH(22) 포트는 절대 0.0.0.0/0으로 열지 마세요. 회사 IP나 VPN IP만 허용하세요
- 규칙을 삭제하기 전에는 반드시 현재 설정을 백업하세요
4. 인바운드 아웃바운드 규칙 설정
보안 그룹 화면을 연 김개발 씨는 "인바운드 규칙"과 "아웃바운드 규칙" 두 개의 탭을 발견했습니다. 둘의 차이가 뭘까요?
박시니어 씨가 설명했습니다. "인바운드는 들어오는 것, 아웃바운드는 나가는 것입니다.
하지만 단순히 방향만 다른 게 아니에요."
'ALB에서만 HTTP 허용' }] }] }).promise(); // 아웃바운드: HTTPS로 외부 API 호출 허용 await ec2.authorizeSecurityGroupEgress({ GroupId: sgId, IpPermissions: [{ IpProtocol: 'tcp', FromPort: 443, ToPort: 443, IpRanges: [{ CidrIp: '0.0.0.0/0', Description: 'HTTPS 외부 호출' }] }] }).promise(); console.log('웹 서버 보안 그룹 설정 완료'); }
다음 코드를 살펴봅시다.
// 웹 서버용 보안 그룹 규칙 설정 예제
async function setupWebServerSecurityGroup(sgId, albSecurityGroupId) {
// 인바운드: ALB에서 오는 HTTP 트래픽만 허용
await ec2.authorizeSecurityGroupIngress({
GroupId: sgId,
IpPermissions: [{
IpProtocol: 'tcp',
FromPort: 80,
ToPort: 80,
UserIdGroupPairs: [{
GroupId: albSecurityGroupId,
김개발 씨는 보안 그룹을 처음 만들었을 때 이상한 점을 발견했습니다. 인바운드 규칙은 비어있는데, 아웃바운드 규칙에는 "모든 트래픽 허용"이 기본으로 들어있었습니다.
"왜 처음부터 이렇게 설정되어 있을까요?" 박시니어 씨가 답했습니다. "AWS의 보안 철학 때문입니다.
기본적으로 외부에서 들어오는 것은 전부 위험하다고 가정하고, 나가는 것은 서버가 필요해서 하는 것이라고 가정합니다." 인바운드 규칙부터 자세히 알아봅시다. 인바운드는 서버 입장에서 들어오는 모든 트래픽을 의미합니다.
사용자의 웹 브라우저에서 오는 HTTP 요청, 관리자가 접속하는 SSH 연결, 다른 서버에서 오는 API 호출 등이 모두 인바운드입니다. 기본적으로 보안 그룹은 모든 인바운드를 차단합니다.
마치 문을 잠근 상태에서 시작하는 것과 같습니다. 필요한 사람에게만 열쇠를 주는 방식입니다.
웹 서버라면 HTTP(80)와 HTTPS(443)만 열면 되고, SSH 관리가 필요하면 22번 포트를 추가로 여는 식입니다. 중요한 점은 소스를 지정할 수 있다는 것입니다.
모든 IP에서 오는 트래픽을 허용할 수도 있고, 특정 IP 대역만 허용할 수도 있습니다. 더 강력한 기능은 다른 보안 그룹을 소스로 지정하는 것입니다.
예를 들어 웹 서버 보안 그룹에서 "로드 밸런서 보안 그룹에서 오는 80번 포트 트래픽만 허용"이라고 설정할 수 있습니다. 이렇게 하면 로드 밸런서를 거치지 않은 직접 접근을 원천 차단할 수 있습니다.
이제 아웃바운드 규칙을 살펴봅시다. 아웃바운드는 서버에서 나가는 모든 트래픽입니다.
외부 API를 호출하거나, 데이터베이스에 연결하거나, 패키지를 다운로드하는 모든 행위가 아웃바운드입니다. 기본적으로 보안 그룹은 모든 아웃바운드를 허용합니다.
이는 대부분의 서버가 외부와 통신해야 하기 때문입니다. 하지만 보안을 더 강화하고 싶다면 기본 규칙을 삭제하고 필요한 것만 명시적으로 허용할 수 있습니다.
예를 들어 웹 서버가 외부 결제 API만 호출한다면, HTTPS(443) 포트의 해당 API IP 대역만 허용하고 나머지는 차단할 수 있습니다. 이렇게 하면 서버가 해킹당하더라도 공격자가 외부로 데이터를 빼내기 어렵습니다.
위의 코드를 분석해봅시다. 첫 번째 authorizeSecurityGroupIngress는 인바운드 규칙을 추가합니다.
여기서 주목할 부분은 UserIdGroupPairs입니다. IP 주소 대신 다른 보안 그룹의 ID를 지정했습니다.
이렇게 하면 ALB 보안 그룹이 적용된 리소스에서 오는 트래픽만 허용합니다. 두 번째 authorizeSecurityGroupEgress는 아웃바운드 규칙을 추가합니다.
443 포트로 모든 IP에 HTTPS 요청을 보낼 수 있도록 허용했습니다. 외부 API 호출이나 패키지 다운로드를 위해 필요한 설정입니다.
실무에서는 어떻게 활용할까요? 3계층 웹 애플리케이션을 예로 들어봅시다.
첫 번째 계층은 로드 밸런서입니다. 인바운드로 전 세계 모든 IP에서 오는 80, 443 포트를 허용합니다.
아웃바운드는 웹 서버 보안 그룹으로만 나갈 수 있게 합니다. 두 번째 계층은 웹 서버입니다.
인바운드로 로드 밸런서 보안 그룹에서 오는 80 포트만 허용합니다. 아웃바운드는 데이터베이스 보안 그룹의 3306 포트와, 외부 API 호출을 위한 443 포트를 허용합니다.
세 번째 계층은 데이터베이스입니다. 인바운드로 웹 서버 보안 그룹에서 오는 3306 포트만 허용합니다.
아웃바운드는 아예 차단할 수도 있습니다. 데이터베이스는 외부와 통신할 필요가 없기 때문입니다.
하지만 주의할 점도 있습니다. 초보자들이 흔히 하는 실수는 아웃바운드를 과도하게 제한하는 것입니다.
예를 들어 yum이나 apt로 패키지를 설치하려면 HTTP/HTTPS 아웃바운드가 필요합니다. DNS 조회를 위해서는 53번 포트도 필요합니다.
너무 엄격하게 제한하면 정상적인 운영이 어려울 수 있습니다. 또 다른 실수는 상태 저장 방식을 오해하는 것입니다.
인바운드 규칙으로 80번 포트를 허용했다면, 그에 대한 응답은 아웃바운드 규칙에 명시하지 않아도 자동으로 나갑니다. 반대로 아웃바운드로 HTTPS 요청을 보냈다면, 그 응답은 인바운드 규칙 없이도 자동으로 들어옵니다.
김개발 씨가 질문했습니다. "그럼 보안을 최대한 강화하려면 아웃바운드도 최소한만 허용해야 하나요?" 박시니어 씨가 답했습니다.
"원칙적으로는 맞지만, 실무에서는 운영 편의성과 균형을 맞춰야 합니다. 웹 서버는 보통 아웃바운드를 전체 허용하고, 데이터베이스는 엄격하게 제한하는 식으로 차등 적용하는 게 일반적입니다." 인바운드와 아웃바운드 규칙을 적절히 설정하면 계층별 보안을 구현할 수 있습니다.
다음에는 로드 밸런서가 트래픽을 보낼 대상 그룹을 만드는 방법을 알아보겠습니다.
실전 팁
💡 - 웹 서버는 인바운드만 제한하고 아웃바운드는 전체 허용하는 것이 일반적입니다
- SSH 포트는 반드시 회사 IP로 제한하고, 가능하면 VPN을 통해서만 접근하세요
- 보안 그룹 간 참조를 활용하면 IP 변경 시에도 규칙 수정이 필요 없습니다
5. 대상 그룹 생성
"로드 밸런서가 트래픽을 분산시킨다는 건 알겠는데, 어디로 보내는 거죠?" 김개발 씨의 질문에 박시니어 씨가 답했습니다. "바로 대상 그룹입니다.
실제 요청을 처리할 서버들의 묶음이죠."
**대상 그룹(Target Group)**은 로드 밸런서가 트래픽을 전달할 서버들의 집합입니다. EC2 인스턴스, IP 주소, 람다 함수 등을 대상으로 등록할 수 있습니다.
마치 택배 기사가 배송할 주소 목록을 가지고 있는 것처럼, 로드 밸런서는 대상 그룹에 등록된 서버들에게 요청을 분배합니다. 헬스 체크 기능으로 정상 동작하는 서버에만 트래픽을 보냅니다.
다음 코드를 살펴봅시다.
// 대상 그룹 생성 및 EC2 인스턴스 등록
async function createTargetGroup(vpcId) {
// 대상 그룹 생성
const tgResult = await elbv2.createTargetGroup({
Name: 'web-servers-tg',
Protocol: 'HTTP',
Port: 80,
VpcId: vpcId,
HealthCheckProtocol: 'HTTP',
HealthCheckPath: '/health', // 헬스 체크 경로
HealthCheckIntervalSeconds: 30, // 30초마다 체크
HealthyThresholdCount: 2, // 2번 연속 성공하면 정상
UnhealthyThresholdCount: 3, // 3번 연속 실패하면 비정상
TargetType: 'instance' // instance, ip, lambda 중 선택
}).promise();
const tgArn = tgResult.TargetGroups[0].TargetGroupArn;
console.log('대상 그룹 생성:', tgArn);
// EC2 인스턴스를 대상으로 등록
await elbv2.registerTargets({
TargetGroupArn: tgArn,
Targets: [
{ Id: 'i-1234567890abcdef0', Port: 80 },
{ Id: 'i-0987654321fedcba0', Port: 80 }
]
}).promise();
console.log('인스턴스 등록 완료');
return tgArn;
}
김개발 씨는 로드 밸런서를 만들려고 AWS 콘솔을 열었습니다. 그런데 중간에 "대상 그룹을 선택하세요"라는 단계가 나왔습니다.
대상 그룹이 뭔지도 모르는데 선택하라니요. 박시니어 씨가 설명했습니다.
"로드 밸런서는 중간 다리일 뿐이에요. 실제 일을 할 서버들을 등록해야 합니다." 그렇다면 대상 그룹이란 정확히 무엇일까요?
쉽게 비유하자면, 대상 그룹은 피자 배달 가게의 배달원 목록과 같습니다. 주문이 들어오면 가게 사장님이 지금 일할 수 있는 배달원에게 배정합니다.
다친 배달원이나 휴가 중인 배달원에게는 주문을 주지 않습니다. 로드 밸런서도 똑같습니다.
대상 그룹에 등록된 서버 중 건강한 서버에게만 요청을 보냅니다. 대상 그룹이 없던 시절에는 어땠을까요?
과거에는 DNS 라운드 로빈 방식을 사용했습니다. 같은 도메인에 여러 IP를 등록해두고, DNS 서버가 순서대로 IP를 반환하는 방식입니다.
하지만 이 방식은 큰 문제가 있었습니다. 서버 한 대가 다운되어도 DNS는 그 사실을 모릅니다.
사용자는 죽은 서버의 IP를 받아서 접속 실패를 경험하게 됩니다. 바로 이런 문제를 해결하기 위해 대상 그룹과 헬스 체크가 등장했습니다.
대상 그룹을 사용하면 자동으로 서버 상태를 모니터링할 수 있습니다. 로드 밸런서가 주기적으로 각 서버에 헬스 체크 요청을 보냅니다.
정상 응답이 오면 계속 트래픽을 보내고, 실패하면 자동으로 제외합니다. 또한 동적으로 서버를 추가하거나 제거할 수 있습니다.
Auto Scaling과 연동하면 트래픽에 따라 자동으로 서버가 늘었다 줄었다 합니다. 헬스 체크는 어떻게 작동할까요?
로드 밸런서는 설정된 주기마다 각 서버에 HTTP 요청을 보냅니다. 예를 들어 /health 경로로 GET 요청을 보내고, 200 OK 응답이 오는지 확인합니다.
연속으로 몇 번 성공하면 정상으로 판단하고, 연속으로 몇 번 실패하면 비정상으로 판단합니다. 이 숫자는 조절할 수 있습니다.
HealthyThresholdCount를 2로 설정하면 2번 연속 성공해야 정상으로 복귀합니다. UnhealthyThresholdCount를 3으로 설정하면 3번 연속 실패해야 제외됩니다.
일시적인 네트워크 지연 때문에 정상 서버가 제외되는 것을 방지하기 위한 장치입니다. 위의 코드를 단계별로 살펴보겠습니다.
먼저 createTargetGroup으로 대상 그룹을 만듭니다. Protocol과 Port는 백엔드 서버가 실제로 리스닝하는 프로토콜과 포트입니다.
VpcId는 대상 서버들이 있는 VPC를 지정합니다. HealthCheckPath는 헬스 체크에 사용할 경로입니다.
보통 /health나 /ping 같은 간단한 엔드포인트를 만들어 놓습니다. 이 경로는 데이터베이스 연결 같은 무거운 작업 없이 빠르게 200을 반환해야 합니다.
HealthCheckIntervalSeconds는 헬스 체크 주기입니다. 30초마다 체크하면 서버가 다운되었을 때 최대 90초(3번 연속 실패) 후에 제외됩니다.
너무 짧으면 서버에 부담이 되고, 너무 길면 장애 감지가 느립니다. TargetType은 세 가지 중 선택할 수 있습니다.
instance는 EC2 인스턴스 ID로 등록하는 방식입니다. ip는 IP 주소로 등록하는 방식으로, 온프레미스 서버나 다른 VPC의 서버도 등록할 수 있습니다.
lambda는 Lambda 함수를 등록하여 서버리스 아키텍처를 구현할 수 있습니다. registerTargets로 실제 서버를 등록합니다.
Targets 배열에 인스턴스 ID와 포트를 지정합니다. 여러 인스턴스를 한 번에 등록할 수 있습니다.
실제 현업에서는 어떻게 활용할까요? 마이크로서비스 아키텍처를 예로 들어봅시다.
사용자 서비스, 상품 서비스, 주문 서비스가 각각 독립적으로 운영됩니다. 각 서비스마다 별도의 대상 그룹을 만들고, ALB의 경로 기반 라우팅으로 연결합니다.
/api/users는 사용자 서비스 대상 그룹으로, /api/products는 상품 서비스 대상 그룹으로 보내는 식입니다. 블루-그린 배포에도 활용할 수 있습니다.
새 버전의 서버들을 별도의 대상 그룹에 등록하고, 헬스 체크가 통과하면 로드 밸런서의 타겟을 새 대상 그룹으로 전환합니다. 문제가 생기면 즉시 이전 대상 그룹으로 롤백할 수 있습니다.
하지만 주의할 점도 있습니다. 초보자들이 흔히 하는 실수는 헬스 체크 경로를 메인 페이지로 설정하는 것입니다.
예를 들어 /를 헬스 체크 경로로 쓰면, 메인 페이지를 렌더링하는 무거운 작업이 30초마다 실행됩니다. 서버 수가 많아지면 헬스 체크만으로도 상당한 부하가 됩니다.
가볍고 빠른 전용 엔드포인트를 만드세요. 또 다른 실수는 드레이닝(Draining) 시간을 고려하지 않는 것입니다.
서버를 대상 그룹에서 제거하면, 즉시 중단되지 않습니다. 진행 중인 요청을 처리할 시간을 줍니다.
기본값은 300초입니다. 배포 시간을 계산할 때 이를 고려해야 합니다.
김개발 씨가 질문했습니다. "헬스 체크가 실패하면 서버가 자동으로 재시작되나요?" 박시니어 씨가 고개를 저었습니다.
"아니요, 단지 트래픽을 보내지 않을 뿐입니다. 서버를 재시작하려면 Auto Scaling 그룹의 헬스 체크와 연동해야 합니다." 대상 그룹을 제대로 설정하면 자동으로 장애를 감지하고 복구하는 시스템을 만들 수 있습니다.
이제 마지막으로 로드 밸런서를 실제로 만들어 EC2와 연결해보겠습니다.
실전 팁
💡 - 헬스 체크 경로는 가볍고 빠른 전용 엔드포인트를 만드세요 (예: /health)
- 헬스 체크 간격과 임계값은 서비스 특성에 맞게 조정하세요 (빠른 감지 vs 안정성)
- Auto Scaling과 연동하면 트래픽에 따라 자동으로 서버가 증감합니다
6. 로드 밸런서 생성 및 EC2 연결 실습
드디어 모든 준비가 끝났습니다. 김개발 씨는 설렘 반 긴장 반으로 로드 밸런서 생성 버튼을 눌렀습니다.
"지금까지 배운 걸 실제로 적용해볼 시간이네요." 박시니어 씨가 옆에서 지켜보며 말했습니다. "천천히, 단계별로 진행하면 어렵지 않습니다."
'ALB용 보안 그룹', VpcId: vpcId }).promise(); const albSgId = sgResult.GroupId; // HTTP/HTTPS 인바운드 허용 await ec2.authorizeSecurityGroupIngress({ GroupId: albSgId, IpPermissions: [ { IpProtocol: 'tcp', FromPort: 80, ToPort: 80, IpRanges: [{ CidrIp: '0.0.0.0/0' }] }, { IpProtocol: 'tcp', FromPort: 443, ToPort: 443, IpRanges: [{ CidrIp: '0.0.0.0/0' }] } ] }).promise(); // 2. ALB 생성 const albResult = await elbv2.createLoadBalancer({ Name: 'my-application-lb', Subnets: subnetIds, // 최소 2개의 가용 영역 SecurityGroups: [albSgId], Scheme: 'internet-facing', // internet-facing 또는 internal Type: 'application', IpAddressType: 'ipv4' }).promise(); const albArn = albResult.LoadBalancers[0].LoadBalancerArn; const albDns = albResult.LoadBalancers[0].DNSName; console.log('ALB 생성 완료:', albDns); // 3.
리스너 생성 (HTTP:80) await elbv2.createListener({ LoadBalancerArn: albArn, Protocol: 'HTTP', Port: 80, DefaultActions: [{ Type: 'forward', TargetGroupArn: targetGroupArn // 이전에 생성한 대상 그룹 }] }).promise(); console.log('리스너 생성 완료'); return { albArn, albDns }; }
다음 코드를 살펴봅시다.
// Application Load Balancer 전체 생성 및 설정 예제
async function createCompleteALB(vpcId, subnetIds, targetGroupArn) {
// 1. ALB용 보안 그룹 생성
const sgResult = await ec2.createSecurityGroup({
GroupName: 'alb-sg',
김개발 씨는 지금까지 배운 내용을 머릿속으로 정리했습니다. 보안 그룹으로 방화벽을 설정하고, 대상 그룹으로 서버 목록을 만들고, 로드 밸런서로 트래픽을 분산시킵니다.
이제 실제로 만들어볼 차례입니다. 박시니어 씨가 말했습니다.
"콘솔에서 클릭으로 만들 수도 있지만, 코드로 이해하면 자동화할 때 도움이 됩니다. 천천히 따라와 보세요." 첫 번째 단계는 보안 그룹 생성입니다. ALB는 인터넷에서 오는 트래픽을 받아야 하므로, 인바운드로 HTTP(80)와 HTTPS(443)를 전 세계 모든 IP(0.0.0.0/0)에서 허용합니다.
이것이 일반적인 웹 서비스의 설정입니다. 내부 전용 ALB라면 회사 VPN IP만 허용할 수도 있습니다.
보안 그룹 이름은 용도를 명확히 표시하는 게 좋습니다. alb-sg, alb-web-sg처럼 나중에 봐도 바로 알 수 있게 짓습니다.
실무에서는 수십 개의 보안 그룹을 관리하기 때문에 네이밍 규칙이 중요합니다. 두 번째 단계는 로드 밸런서 생성입니다. createLoadBalancer 함수의 파라미터를 하나씩 살펴봅시다.
Name은 알아보기 쉬운 이름을 짓습니다. AWS 콘솔에서 이 이름으로 표시됩니다.
Subnets는 중요합니다. ALB는 최소 2개의 가용 영역에 배포되어야 합니다.
고가용성을 보장하기 위한 AWS의 요구사항입니다. 만약 한 가용 영역 전체가 다운되어도 다른 가용 영역의 ALB가 계속 서비스를 제공합니다.
Scheme는 두 가지 중 선택할 수 있습니다. internet-facing은 인터넷에서 접근 가능한 공개 로드 밸런서입니다.
internal은 VPC 내부에서만 접근 가능한 비공개 로드 밸런서입니다. 마이크로서비스 간 통신용으로 사용합니다.
Type은 application, network, gateway 중 선택합니다. 여기서는 HTTP 기반 웹 서비스이므로 application을 선택했습니다.
세 번째 단계는 리스너 생성입니다. 리스너는 로드 밸런서가 어떤 포트로 트래픽을 받을지 정의합니다. Protocol과 Port를 지정하고, DefaultActions로 기본 동작을 설정합니다.
가장 흔한 설정은 Type: 'forward'로 대상 그룹에 트래픽을 전달하는 것입니다. 하지만 다른 옵션도 있습니다.
redirect로 HTTP를 HTTPS로 리다이렉트할 수 있고, fixed-response로 정적 응답을 반환할 수도 있습니다. HTTPS 리스너를 만들려면 SSL 인증서가 필요합니다.
AWS Certificate Manager(ACM)에서 무료로 발급받을 수 있습니다. 인증서 ARN을 리스너에 연결하면 자동으로 SSL 종료(Termination)를 처리해줍니다.
실제 동작 흐름을 살펴봅시다. 사용자가 웹 브라우저에 ALB의 DNS 이름을 입력합니다.
예를 들어 my-application-lb-1234567890.ap-northeast-2.elb.amazonaws.com입니다. DNS 쿼리를 통해 ALB의 IP 주소를 받아옵니다.
브라우저가 HTTP 요청을 ALB로 보냅니다. ALB의 보안 그룹이 80번 포트 인바운드를 허용하므로 요청이 들어옵니다.
리스너가 요청을 받아서 대상 그룹으로 전달합니다. 대상 그룹은 등록된 서버 중 헬스 체크를 통과한 서버 하나를 선택합니다.
라운드 로빈 또는 최소 연결 수 알고리즘을 사용합니다. 선택된 서버의 보안 그룹이 ALB 보안 그룹에서 오는 트래픽을 허용하므로 요청이 전달됩니다.
서버가 응답을 생성하여 다시 ALB로 보냅니다. ALB가 최종적으로 사용자에게 응답을 전달합니다.
이 모든 과정이 수십 밀리초 안에 일어납니다. 실무에서는 추가 설정을 고려해야 합니다.
스티키 세션은 같은 사용자의 요청을 항상 같은 서버로 보내는 기능입니다. 세션을 서버 메모리에 저장하는 레거시 애플리케이션에 유용합니다.
하지만 가능하면 세션을 Redis나 데이터베이스에 저장하여 스티키 세션 없이도 작동하게 만드는 것이 좋습니다. 연결 드레이닝은 서버를 대상 그룹에서 제거할 때 진행 중인 요청을 완료할 시간을 주는 기능입니다.
기본값은 300초이지만, 요청 처리 시간에 맞게 조정할 수 있습니다. 액세스 로그를 활성화하면 모든 요청이 S3에 저장됩니다.
나중에 분석하거나 보안 감사에 사용할 수 있습니다. 하지만 트래픽이 많으면 로그 비용이 상당할 수 있으니 주의하세요.
하지만 주의할 점도 있습니다. 초보자들이 흔히 하는 실수는 ALB를 만들자마자 바로 접속을 시도하는 것입니다.
ALB는 프로비저닝에 몇 분이 걸립니다. 상태가 "active"가 될 때까지 기다려야 합니다.
provisioning 상태에서는 접속이 되지 않습니다. 또 다른 실수는 대상 그룹에 서버가 없거나 모든 서버가 unhealthy 상태인데 접속을 시도하는 것입니다.
ALB는 정상 서버가 하나도 없으면 503 Service Unavailable 에러를 반환합니다. 먼저 대상 그룹의 헬스 체크 상태를 확인해야 합니다.
DNS 이름이 너무 길어서 기억하기 어렵다면 Route 53으로 사용자 정의 도메인을 연결할 수 있습니다. 예를 들어 www.myshop.com을 ALB의 DNS 이름으로 연결하는 것입니다.
사용자는 훨씬 간단한 주소로 접속할 수 있습니다. 김개발 씨가 코드를 실행하고 몇 분을 기다렸습니다.
드디어 ALB의 상태가 active가 되었습니다. DNS 이름을 브라우저에 입력하자 웹 페이지가 나타났습니다.
"성공했어요!" 김개발 씨가 환호했습니다. 박시니어 씨가 미소 지으며 말했습니다.
"축하합니다. 이제 트래픽이 아무리 많아져도 서버를 추가하기만 하면 자동으로 분산됩니다.
고가용성도 확보했고요." 로드 밸런서와 보안 그룹을 제대로 설정하면 확장 가능하고 안전한 웹 서비스를 구축할 수 있습니다. 처음에는 복잡해 보이지만, 한 번 이해하고 나면 AWS 인프라의 핵심 패턴을 익힌 것입니다.
마지막으로 김개발 씨는 회사 쇼핑몰의 트래픽 그래프를 확인했습니다. 동시 접속자가 5000명이 넘었지만, 서버들은 안정적으로 요청을 처리하고 있었습니다.
"이제 진짜 서비스다운 서비스를 만든 것 같아요." 김개발 씨가 뿌듯해하며 중얼거렸습니다.
실전 팁
💡 - ALB는 프로비저닝에 5-10분 정도 소요됩니다. active 상태를 확인한 후 접속하세요
- 대상 그룹의 헬스 체크 상태를 먼저 확인하고, healthy 서버가 있는지 확인하세요
- Route 53으로 사용자 정의 도메인을 연결하면 훨씬 전문적인 서비스가 됩니다
- HTTPS를 적용하려면 ACM에서 SSL 인증서를 무료로 발급받을 수 있습니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
VPC 네트워크의 기초 - CIDR과 서브넷 설계 완벽 가이드
초급 개발자를 위한 VPC와 서브넷 설계 입문서입니다. 도서관 비유로 CIDR 개념을 쉽게 이해하고, 실무에서 자주 사용하는 서브넷 분할 전략을 단계별로 배워봅니다. 점프 투 자바 스타일로 술술 읽히는 네트워크 입문 가이드입니다.
AWS 리소스 정리와 비용 관리 완벽 가이드
AWS 사용 후 리소스를 안전하게 정리하고 예상치 못한 과금을 방지하는 방법을 배웁니다. 프리티어 관리부터 비용 모니터링까지 실무에서 꼭 필요한 내용을 다룹니다.
AWS Certificate Manager로 HTTPS 인증서 발급 완벽 가이드
AWS Certificate Manager를 사용하여 무료로 SSL/TLS 인증서를 발급받고, 로드 밸런서에 적용하여 안전한 HTTPS 웹 서비스를 구축하는 방법을 초급자도 쉽게 따라 할 수 있도록 단계별로 안내합니다.
Route 53으로 도메인 연결 완벽 가이드
AWS Route 53을 사용하여 도메인을 등록하고 실제 서비스에 연결하는 전 과정을 실무 스토리와 함께 쉽게 배워봅니다. DNS의 기본 개념부터 레코드 설정, ELB 연결까지 초급 개발자도 쉽게 따라할 수 있도록 구성했습니다.
AWS RDS 관리형 데이터베이스 완벽 가이드
직접 데이터베이스를 설치하고 관리하는 것이 부담스러운 초급 개발자를 위한 RDS 가이드입니다. 데이터베이스 엔진 선택부터 인스턴스 생성, 보안 설정, 백업까지 실무에 필요한 모든 내용을 다룹니다.