왜 VPC를 직접 설계해야 할까?
여러분이 스타트업의 첫 번째 클라우드 엔지니어로 입사했다고 상상해 보세요. CTO가 이렇게 말합니다:

"우리 서비스가 다음 달에 출시되는데, AWS에 안전한 네트워크를 구축해 주세요. 웹 서버는 외부에서 접근 가능해야 하고, 데이터베이스는 절대 외부에 노출되면 안 됩니다."
이 요구사항을 듣고 무엇을 떠올리시나요? 바로 VPC(Virtual Private Cloud)입니다.
VPC는 AWS 클라우드 안에 여러분만의 격리된 네트워크 공간을 만드는 것입니다. 마치 회사 건물을 짓는 것과 비슷합니다:
- VPC = 회사 건물 전체 부지
- 퍼블릭 서브넷 = 1층 로비 (누구나 들어올 수 있음)
- 프라이빗 서브넷 = 서버실 (관계자 외 출입 금지)
- Internet Gateway = 건물의 정문
- NAT Gateway = 서버실에서 외부로 택배 보내는 전용 출구
- Security Group = 각 방의 출입 카드 리더기
- NACL = 건물 층별 보안 게이트
이 실습을 마치면 여러분은 프로덕션급 VPC를 설계하고 구축할 수 있게 됩니다. 실제 회사에서 사용하는 것과 동일한 네트워크 아키텍처를 직접 만들어 보겠습니다.
실무에서는 이 VPC 위에 EC2, RDS, Lambda 등 모든 서비스가 배치됩니다. 네트워크를 잘못 설계하면 보안 사고가 나거나, 나중에 구조를 바꾸기가 매우 어렵습니다. 처음부터 제대로 설계하는 것이 핵심이고, 이 실습이 그 첫걸음입니다.
실습을 시작하기 전에 AWS 콘솔에 로그인되어 있는지 확인하세요. 리전은 ap-northeast-2 (서울) 을 사용합니다.
아키텍처 개요

네트워크 흐름
비용 예측
비용 계산기
시간
GB
* 실제 비용은 AWS 요금 정책에 따라 달라질 수 있습니다.
Step 1: VPC 생성
이 단계에서는 모든 리소스가 배치될 VPC 자체를 먼저 만듭니다. VPC를 만들 때 가장 중요한 결정은 CIDR 블록 선택입니다.
CIDR 블록은 이 VPC에서 사용할 수 있는 IP 주소의 범위를 결정합니다.
10.0.0.0/16은 10.0.0.0부터 10.0.255.255까지 65,536개의 IP 주소를 사용할 수 있다는 뜻입니다.
프로덕션에서는 이 정도면 충분하고, 나중에 서브넷을 다양하게 나누기에도 좋습니다.
- AWS 콘솔 상단 검색창에서 VPC를 검색하고 클릭합니다
- 좌측 메뉴에서 VPC → 오른쪽 상단 VPC 생성 버튼 클릭
- VPC만 옵션을 선택합니다 (VPC 등을 자동으로 생성하는 옵션은 사용하지 않습니다)
- 이름 태그:
lab-vpc입력 - IPv4 CIDR 블록: IPv4 CIDR 수동 입력 선택 후
10.0.0.0/16입력 - 테넌시: 기본값(Default) 선택 (Dedicated는 비용이 훨씬 비쌉니다)
- 하단의 VPC 생성 버튼 클릭
- 생성 완료 후 VPC ID를 메모해 둡니다 (vpc-xxxxxxxx 형식)
aws ec2 create-vpc \
--cidr-block 10.0.0.0/16 \
--tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=lab-vpc}]'resource "aws_vpc" "lab" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "lab-vpc"
}
}CIDR /16은 65,536개의 IP를 제공합니다. 프로덕션에서는 향후 확장을 고려하여 충분한 크기를 선택하세요. /16보다 작은 범위(예: /24)를 선택하면 나중에 서브넷을 추가할 여유가 없어집니다.
흔한 실수: VPC 생성 시 "VPC 등" 옵션을 선택하면 서브넷, IGW 등이 자동 생성됩니다. 학습 목적에서는 VPC만 선택하여 각 구성 요소를 직접 만들어 보는 것을 권장합니다.
Step 2: 서브넷 생성
이제 VPC 안에 4개의 서브넷을 만듭니다. 서브넷은 VPC의 IP 범위를 더 작은 단위로 나눈 것입니다.
왜 4개일까요? 고가용성을 위해 2개의 가용 영역(AZ)에 각각 퍼블릭/프라이빗 서브넷을 배치합니다. 하나의 AZ에 장애가 발생해도 다른 AZ에서 서비스를 계속할 수 있습니다.
CIDR 범위를 설계할 때는 명확한 규칙을 정하면 관리가 편합니다:
- 1~9번대: 퍼블릭 서브넷 (10.0.1.0/24, 10.0.2.0/24)
- 10번대 이상: 프라이빗 서브넷 (10.0.10.0/24, 10.0.20.0/24)
- 1VPC 콘솔 좌측 메뉴에서 서브넷 클릭 → 오른쪽 상단 서브넷 생성 클릭
- 2VPC 선택: 드롭다운에서 lab-vpc를 선택합니다
- 3서브넷 설정 섹션에서 첫 번째 서브넷 정보를 입력합니다: 서브넷 이름: lab-public-a 가용 영역: ap-northeast-2a 선택 IPv4 CIDR 블록: 10.0.1.0/24 입력 (256개 IP)
- 4새 서브넷 추가 버튼을 클릭하여 나머지 3개도 추가합니다: lab-public-b: AZ=ap-northeast-2c, CIDR=10.0.2.0/24 lab-private-a: AZ=ap-northeast-2a, CIDR=10.0.10.0/24 lab-private-b: AZ=ap-northeast-2c, CIDR=10.0.20.0/24
- 54개 모두 입력했으면 서브넷 생성 클릭
- 6서브넷 목록에서 4개가 정상 생성되었는지 확인합니다
퍼블릭과 프라이빗 서브넷의 CIDR 범위를 명확히 구분하면 관리가 편합니다. 예: 1-9 퍼블릭, 10+ 프라이빗. 이름에도 public/private을 명시하세요.
주의: 서브넷을 만들었다고 해서 자동으로 퍼블릭이 되지 않습니다! 퍼블릭 서브넷이 되려면 Internet Gateway 연결 + 퍼블릭 IP 자동 할당이 필요합니다. 이 설정은 다음 단계에서 진행합니다.
Step 3: Internet Gateway 생성 및 연결
Internet Gateway(IGW)는 VPC가 인터넷과 통신할 수 있게 해주는 관문입니다. IGW가 없으면 VPC 안의 어떤 리소스도 외부 인터넷에 접속할 수 없습니다.
- 1VPC 콘솔 → 좌측 인터넷 게이트웨이 클릭 → 인터넷 게이트웨이 생성 클릭
- 2이름 태그: lab-igw 입력 → 인터넷 게이트웨이 생성 클릭
- 3생성된 IGW를 선택 → 상단 작업 → VPC에 연결 클릭
- 4사용 가능한 VPC에서 lab-vpc 선택 → 인터넷 게이트웨이 연결 클릭
- 5상태가 Attached로 변경된 것을 확인합니다
중요: 하나의 VPC에는 하나의 Internet Gateway만 연결할 수 있습니다. 이미 연결된 IGW가 있다면 새로 만든 IGW를 연결할 수 없습니다.
Step 4: 라우팅 테이블 설정
라우팅 테이블은 네트워크 트래픽의 길잡이입니다. "이 목적지로 가려면 어디로 보내라"는 규칙들의 모음입니다.
퍼블릭 서브넷과 프라이빗 서브넷에는 서로 다른 라우팅 테이블이 필요합니다:
- 퍼블릭: 외부 트래픽(0.0.0.0/0)을 IGW로 보냄
- 프라이빗: 외부 트래픽(0.0.0.0/0)을 NAT Gateway로 보냄
- 1VPC 콘솔 → 라우팅 테이블 → 라우팅 테이블 생성 클릭
- 2이름: lab-public-rt, VPC: lab-vpc 선택 → 생성 클릭
- 3생성된 라우팅 테이블 선택 → 하단 라우팅 탭 → 라우팅 편집 클릭
- 4라우팅 추가: 대상 0.0.0.0/0, 대상 유형 Internet Gateway → lab-igw 선택 → 변경 사항 저장
- 5서브넷 연결 탭 → 서브넷 연결 편집 → lab-public-a, lab-public-b 체크 → 저장
- 6퍼블릭 서브넷에서 자동 할당 IP 설정: 서브넷 → lab-public-a 선택 → 작업 → 자동 할당 IP 설정 수정 → 퍼블릭 IPv4 주소 자동 할당 활성화 체크 → 저장
- 7lab-public-b에도 동일하게 자동 할당 IP를 활성화합니다
Step 5: NAT Gateway 생성
NAT Gateway는 프라이빗 서브넷의 리소스가 인터넷에 접속할 수 있게 해줍니다. 예를 들어, 프라이빗 서브넷의 EC2가 소프트웨어 업데이트를 받거나 외부 API를 호출할 때 필요합니다.
핵심은 아웃바운드만 허용한다는 점입니다. 외부에서 프라이빗 서브넷으로 들어오는 연결은 차단됩니다.
- 1VPC 콘솔 → NAT 게이트웨이 → NAT 게이트웨이 생성 클릭
- 2이름: lab-nat-gw 입력
- 3서브넷: lab-public-a 선택 (NAT Gateway는 반드시 퍼블릭 서브넷에 배치해야 합니다!)
- 4연결 유형: 퍼블릭 선택
- 5탄력적 IP 할당 버튼 클릭 → 자동으로 EIP가 할당됩니다
- 6NAT 게이트웨이 생성 클릭
- 7상태가 Available이 될 때까지 1~2분 기다립니다
- 8이제 프라이빗 라우팅 테이블을 생성합니다: 라우팅 테이블 생성 → 이름: lab-private-rt, VPC: lab-vpc 라우팅 추가: 0.0.0.0/0 → NAT Gateway → lab-nat-gw 선택 서브넷 연결: lab-private-a, lab-private-b 연결
흔한 실수: NAT Gateway를 프라이빗 서브넷에 배치하는 경우가 많습니다. NAT Gateway는 반드시 퍼블릭 서브넷에 있어야 합니다! 프라이빗 서브넷 → NAT Gateway(퍼블릭) → IGW → 인터넷 순서로 트래픽이 흐릅니다.
비용 주의: NAT Gateway는 시간당 $0.045 + 데이터 전송 비용이 발생합니다. 실습이 끝나면 반드시 삭제하세요. 하루 방치하면 약 $1.08가 청구됩니다.
Step 6: 보안 그룹 설정
보안 그룹(Security Group)은 인스턴스 수준의 방화벽입니다. 어떤 트래픽이 들어올 수 있고(인바운드), 나갈 수 있는지(아웃바운드)를 제어합니다.
보안 그룹은 Stateful입니다. 인바운드로 허용된 요청의 응답은 자동으로 아웃바운드 허용됩니다.
- 1VPC 콘솔 → 보안 그룹 → 보안 그룹 생성 클릭
- 2웹 서버용 보안 그룹: 이름: lab-web-sg, 설명: "Web server security group" VPC: lab-vpc 선택 인바운드 규칙 추가: HTTP(80) - 소스: 0.0.0.0/0 (전체) 인바운드 규칙 추가: HTTPS(443) - 소스: 0.0.0.0/0 (전체) 인바운드 규칙 추가: SSH(22) - 소스: 내 IP (본인 IP만 허용) 아웃바운드: 기본값 유지 (모든 트래픽 허용)
- 3DB 서버용 보안 그룹: 이름: lab-db-sg, 설명: "Database security group" VPC: lab-vpc 선택 인바운드 규칙 추가: MySQL/Aurora(3306) - 소스: lab-web-sg (보안 그룹 참조!) 아웃바운드: 기본값 유지
- 4생성 클릭 후 규칙이 올바른지 확인합니다
DB 보안 그룹의 소스로 IP 대신 보안 그룹 ID를 지정하면, 해당 보안 그룹에 속한 모든 인스턴스가 접근 가능합니다. 인스턴스가 추가/제거되어도 규칙을 수정할 필요가 없어 관리가 편리합니다.
Step 7: NACL(네트워크 ACL) 설정
NACL은 서브넷 수준의 방화벽입니다. 보안 그룹과 달리 Stateless이므로, 인바운드와 아웃바운드 규칙을 각각 설정해야 합니다.
- 1VPC 콘솔 → 네트워크 ACL → 네트워크 ACL 생성 클릭
- 2이름: lab-public-nacl, VPC: lab-vpc → 생성
- 3인바운드 규칙 편집: 규칙 100: HTTP(80), 소스 0.0.0.0/0, 허용 규칙 110: HTTPS(443), 소스 0.0.0.0/0, 허용 규칙 120: SSH(22), 소스 내 IP/32, 허용 규칙 130: 임시 포트(1024-65535), 소스 0.0.0.0/0, 허용
- 4아웃바운드 규칙 편집: 규칙 100: HTTP(80), 대상 0.0.0.0/0, 허용 규칙 110: HTTPS(443), 대상 0.0.0.0/0, 허용 규칙 120: 임시 포트(1024-65535), 대상 0.0.0.0/0, 허용
- 5서브넷 연결: lab-public-a, lab-public-b 연결
흔한 실수: NACL 아웃바운드에서 임시 포트(Ephemeral Ports, 1024-65535)를 빼먹는 경우가 많습니다. HTTP 요청의 응답이 임시 포트를 통해 돌아오기 때문에, 이 규칙이 없으면 웹 페이지가 로드되지 않습니다.
핵심 개념 확인
직접 설명해 보기
본인의 말로 설명해 보세요
친구에게 VPC의 퍼블릭 서브넷과 프라이빗 서브넷의 차이를 설명해 보세요. 왜 두 종류가 필요한지, 트래픽이 어떻게 다르게 흐르는지 포함해서 설명해 보세요.
💡 건물의 1층 로비(누구나 출입)와 서버실(관계자만 출입)의 비유를 활용해 보세요.
본인의 말로 설명해 보세요
보안 그룹이 'Stateful'이라는 것이 실제로 어떤 의미인지 설명해 보세요.
💡 웹 서버에 HTTP 요청을 보내는 상황을 예로 들어 보세요.
완성 후 테스트 가이드
VPC 구성이 완료되었다면, 다음 테스트를 통해 올바르게 작동하는지 확인하세요.
- 1퍼블릭 서브넷 테스트: lab-public-a에 EC2 인스턴스(t2.micro)를 하나 생성합니다. 보안 그룹은 lab-web-sg를 사용합니다.
- 2EC2에 퍼블릭 IP가 자동 할당되었는지 확인합니다. 할당되지 않았다면 서브넷의 자동 할당 설정을 다시 확인하세요.
- 3터미널에서 ssh -i key.pem ec2-user@<퍼블릭IP>로 접속이 되는지 확인합니다.
- 4EC2 안에서 curl https://aws.amazon.com 명령이 정상 동작하는지 확인합니다 (인터넷 아웃바운드 테스트).
- 5프라이빗 서브넷 테스트: lab-private-a에 EC2 인스턴스를 생성합니다 (퍼블릭 IP는 할당되지 않아야 합니다).
- 6퍼블릭 서브넷의 EC2에서 프라이빗 EC2의 프라이빗 IP로 SSH 접속합니다 (Bastion Host 패턴).
- 7프라이빗 EC2에서 curl https://aws.amazon.com이 동작하면 NAT Gateway 설정이 정상입니다.
- 8외부에서 프라이빗 EC2의 프라이빗 IP로 직접 접속이 불가능한지 확인합니다 (보안 테스트).
트러블슈팅
EC2에서 인터넷 접속이 안 되는 경우:
- 퍼블릭 서브넷인데 안 되면 → 라우팅 테이블에
0.0.0.0/0 → IGW규칙이 있는지 확인 - 퍼블릭 IP가 할당되었는지 확인 (서브넷의 자동 할당 설정 또는 EIP)
- 보안 그룹 아웃바운드에 HTTPS(443) 허용이 있는지 확인
프라이빗 서브넷에서 인터넷 접속이 안 되는 경우:
- NAT Gateway 상태가 Available인지 확인 (Pending이면 1~2분 대기)
- 프라이빗 라우팅 테이블에
0.0.0.0/0 → NAT Gateway규칙이 있는지 확인 - NAT Gateway가 퍼블릭 서브넷에 있는지 확인 (프라이빗에 있으면 동작하지 않음)
- NAT Gateway에 Elastic IP가 연결되어 있는지 확인
SSH 접속이 안 되는 경우:
- 보안 그룹에 SSH(22) 인바운드 규칙이 있는지 확인
- 소스 IP가 올바른지 확인 (내 IP가 변경되었을 수 있음)
- NACL에서 SSH(22)와 임시 포트(1024-65535)가 양방향 허용되어 있는지 확인
- 키 페어 파일의 권한이 400인지 확인:
chmod 400 key.pem
확장 아이디어
이 실습을 마쳤다면, 다음 단계로 도전해 보세요:
- VPC Peering: 두 개의 VPC를 만들어 서로 연결하기. 마이크로서비스 아키텍처에서 서비스별 VPC를 분리할 때 사용합니다.
- VPC Endpoint: S3나 DynamoDB에 프라이빗 서브넷에서 인터넷을 거치지 않고 직접 접속하기. NAT Gateway 비용을 절약할 수 있습니다.
- Flow Logs: VPC 트래픽 로그를 CloudWatch Logs로 보내 네트워크 모니터링하기. 보안 감사에 필수입니다.
- Transit Gateway: 여러 VPC와 온프레미스 네트워크를 중앙에서 관리하기. 대규모 조직에서 사용합니다.
- IPv6 Dual-Stack: VPC에 IPv6를 추가하여 듀얼 스택 네트워크 구성하기.
학습 정리
핵심 치트시트
이 프로젝트에서는 VPC, 서브넷, IGW, NAT Gateway, 라우팅 테이블, 보안 그룹, NACL을 조합하여 프로덕션급 네트워크를 설계했습니다. 핵심은 퍼블릭/프라이빗 분리와 보안 계층화입니다.
리소스 정리
실습 완료 후 반드시 아래 순서대로 리소스를 정리하여 불필요한 과금을 방지하세요. 특히 NAT Gateway는 시간당 비용이 발생하므로 가장 먼저 삭제합니다.
- 테스트용 EC2 인스턴스 종료
- NAT Gateway 삭제
- Elastic IP 릴리스
- 서브넷 삭제 (4개)
- Internet Gateway 분리 및 삭제
- 라우팅 테이블 삭제 (커스텀)
- 보안 그룹 삭제 (커스텀)
- NACL 삭제 (커스텀)
- VPC 삭제