🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

VPC 네트워크의 기초 - CIDR과 서브넷 설계 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 24. · 3 Views

VPC 네트워크의 기초 - CIDR과 서브넷 설계 완벽 가이드

초급 개발자를 위한 VPC와 서브넷 설계 입문서입니다. 도서관 비유로 CIDR 개념을 쉽게 이해하고, 실무에서 자주 사용하는 서브넷 분할 전략을 단계별로 배워봅니다. 점프 투 자바 스타일로 술술 읽히는 네트워크 입문 가이드입니다.


목차

  1. VPC의_필요성
  2. CIDR_주소_체계_이해하기
  3. VPC_생성_및_IP_대역_설정
  4. 퍼블릭_서브넷_vs_프라이빗_서브넷
  5. 서브넷_분할_전략
  6. 서브넷_생성_실습

1. VPC의 필요성

김개발 씨는 스타트업에 입사한 지 한 달이 된 주니어 개발자입니다. 오늘 CTO님이 "김개발 씨, AWS에 VPC 하나 만들어서 웹 서버 띄워봐요"라고 말씀하셨습니다.

VPC라는 단어를 처음 듣는 김개발 씨는 당황할 수밖에 없었습니다.

**VPC(Virtual Private Cloud)**는 클라우드 환경에서 논리적으로 격리된 나만의 네트워크 공간입니다. 마치 아파트 단지 안에서 우리 집만의 영역을 확보하는 것과 같습니다.

VPC를 사용하면 보안을 강화하고, IP 주소를 자유롭게 관리하며, 외부 네트워크와의 연결을 통제할 수 있습니다.

다음 코드를 살펴봅시다.

import boto3

# VPC 생성 - 나만의 네트워크 공간 만들기
ec2 = boto3.resource('ec2', region_name='ap-northeast-2')

# 10.0.0.0/16 대역의 VPC 생성 (65,536개 IP 사용 가능)
vpc = ec2.create_vpc(CidrBlock='10.0.0.0/16')
vpc.create_tags(Tags=[{"Key": "Name", "Value": "MyFirstVPC"}])

# VPC의 DNS 호스트네임 활성화
vpc.modify_attribute(EnableDnsHostnames={'Value': True})

print(f'VPC 생성 완료: {vpc.id}')

김개발 씨는 선배 개발자 박시니어 씨에게 도움을 청했습니다. "선배님, VPC가 뭔가요?" 박시니어 씨는 커피를 한 모금 마시고 설명을 시작했습니다.

"자, 김개발 씨. 우리 회사 사무실을 생각해봐요.

이 건물에는 여러 회사가 입주해 있지만, 우리는 5층 전체를 사용하죠. 다른 층 사람들은 우리 층에 함부로 들어올 수 없어요." VPC도 마찬가지입니다.

AWS 클라우드라는 거대한 건물에서, 우리 회사만의 전용 층을 만드는 것입니다. 이 층 안에서는 우리가 원하는 대로 방을 나누고, 문을 설치하고, 보안 시스템을 구축할 수 있습니다.

그렇다면 왜 VPC가 필요할까요? 과거에는 물리적인 서버를 직접 운영했습니다.

데이터센터에 랙을 설치하고, 네트워크 케이블을 직접 연결했습니다. 이런 환경에서는 물리적으로 격리된 네트워크를 쉽게 만들 수 있었습니다.

하지만 클라우드 시대가 되면서 상황이 달라졌습니다. 수많은 회사가 같은 물리적 인프라를 공유합니다.

A회사의 서버와 B회사의 서버가 같은 데이터센터에 있을 수 있습니다. 만약 네트워크가 제대로 격리되지 않는다면 어떻게 될까요?

보안 문제가 발생할 수 있습니다. 다른 회사의 서버가 우리 서버에 접근할 수도 있고, 민감한 데이터가 유출될 위험도 있습니다.

IP 주소 충돌도 문제입니다. A회사와 B회사가 똑같은 IP 주소를 사용하려고 하면 혼란이 발생합니다.

바로 이런 문제를 해결하기 위해 VPC가 등장했습니다. VPC를 사용하면 논리적으로 완전히 격리된 네트워크를 만들 수 있습니다.

물리적으로는 같은 하드웨어를 공유하지만, 소프트웨어적으로 완벽하게 분리됩니다. 마치 아파트에서 옆집과 벽 하나로 구분되지만, 서로의 생활 공간은 완전히 독립적인 것과 같습니다.

또한 IP 주소를 자유롭게 선택할 수 있습니다. 10.0.0.0/16 대역을 사용할 수도 있고, 172.16.0.0/12 대역을 사용할 수도 있습니다.

우리 마음대로 결정하면 됩니다. 위의 코드를 살펴봅시다.

먼저 boto3 라이브러리를 사용해 AWS에 연결합니다. 이것은 AWS를 프로그래밍 방식으로 제어하는 도구입니다.

create_vpc 메서드로 새로운 VPC를 생성하는데, 이때 CidrBlock이라는 파라미터를 전달합니다. '10.0.0.0/16'이라는 값은 무엇일까요?

이것은 이 VPC에서 사용할 수 있는 IP 주소 범위를 의미합니다. /16은 앞의 16비트가 고정되고, 나머지 16비트를 자유롭게 사용할 수 있다는 뜻입니다.

계산해보면 65,536개의 IP 주소를 사용할 수 있습니다. 다음으로 create_tags로 VPC에 이름을 붙입니다.

AWS에서는 모든 리소스에 태그를 달아 관리할 수 있습니다. 마지막으로 EnableDnsHostnames를 활성화하는데, 이것은 VPC 내부에서 DNS 이름을 사용할 수 있게 해줍니다.

실제 현업에서는 어떻게 활용할까요? 스타트업 A사는 쇼핑몰 서비스를 운영합니다.

웹 서버, 데이터베이스, 캐시 서버 등 여러 서버를 운영하는데, 이들을 모두 하나의 VPC 안에 배치합니다. 외부에서는 웹 서버에만 접근할 수 있고, 데이터베이스는 VPC 내부에서만 접근 가능하도록 설정합니다.

이렇게 하면 보안이 크게 향상됩니다. 해커가 데이터베이스를 직접 공격하려고 해도, VPC 외부에서는 아예 접근할 수 없습니다.

웹 서버를 먼저 뚫어야만 데이터베이스에 도달할 수 있습니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수는 VPC를 너무 작게 만드는 것입니다. 예를 들어 /24 대역으로 VPC를 만들면 256개의 IP 주소만 사용할 수 있습니다.

처음에는 충분해 보이지만, 서비스가 성장하면서 IP가 부족해집니다. VPC의 CIDR 블록은 나중에 변경할 수 없으므로, 처음부터 충분히 크게 만드는 것이 좋습니다.

박시니어 씨의 설명을 들은 김개발 씨는 이제 VPC의 필요성을 이해했습니다. "아, VPC가 클라우드에서 우리만의 네트워크 영역을 만드는 거군요!" VPC를 제대로 이해하면 클라우드 인프라를 안전하고 효율적으로 구축할 수 있습니다.

다음 단계로, 이 VPC 안에서 IP 주소를 어떻게 할당하는지 알아봐야 합니다. 바로 CIDR 개념이 등장하는 순간입니다.

실전 팁

💡 - VPC는 /16 대역으로 만드는 것이 일반적입니다. 충분한 IP 확장성을 제공합니다.

  • 프라이빗 IP 대역(10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)을 사용하세요.
  • VPC 생성 시 DNS 호스트네임을 활성화하면 내부 통신이 편리해집니다.

2. CIDR 주소 체계 이해하기

김개발 씨는 VPC를 만들 때 '10.0.0.0/16'이라는 표기법을 봤습니다. 숫자 뒤에 붙은 /16은 무엇을 의미할까요?

박시니어 씨는 "이것이 바로 CIDR 표기법이에요"라고 설명하기 시작했습니다.

**CIDR(Classless Inter-Domain Routing)**은 IP 주소 범위를 유연하게 표현하는 방법입니다. 마치 "서울시 강남구" 전체를 한 번에 표현하는 것처럼, 여러 IP 주소를 하나의 블록으로 묶어서 표현합니다.

/뒤의 숫자는 네트워크 부분의 비트 수를 나타내며, 이 값이 작을수록 더 많은 IP 주소를 포함합니다.

다음 코드를 살펴봅시다.

import ipaddress

# CIDR 블록 정의 - IP 주소 범위 계산
network = ipaddress.IPv4Network('10.0.0.0/16')

# 사용 가능한 전체 IP 개수
print(f'전체 IP 개수: {network.num_addresses}')  # 65536개

# 실제 사용 가능한 IP (AWS는 5개 예약)
print(f'사용 가능 IP: {network.num_addresses - 5}')

# 첫 번째와 마지막 IP 주소
print(f'시작 IP: {network.network_address}')  # 10.0.0.0
print(f'마지막 IP: {network.broadcast_address}')  # 10.0.255.255

# /24로 서브넷 분할 시 몇 개 만들 수 있나?
print(f'/24 서브넷 개수: {2 ** (24 - 16)}')  # 256개

박시니어 씨는 화이트보드에 그림을 그리기 시작했습니다. "김개발 씨, 도서관을 상상해봐요.

도서관에는 책이 수만 권 있죠. 각 책마다 고유한 청구기호가 있습니다." "예를 들어 '한국문학 813.7-김철수-2024'라는 청구기호가 있다면, 이것은 책의 위치를 정확하게 알려줍니다.

813.7은 분류 번호고, 김철수는 저자명이고, 2024는 출판연도죠." CIDR도 비슷합니다. IP 주소를 '네트워크 부분'과 '호스트 부분'으로 나눕니다.

도서관 비유에서 분류 번호가 네트워크 부분이고, 저자명과 출판연도가 호스트 부분입니다. 그렇다면 왜 이런 체계가 필요할까요?

인터넷 초창기에는 클래스 기반 주소 체계를 사용했습니다. Class A는 1,600만 개, Class B는 65,536개, Class C는 256개의 IP를 제공했습니다.

문제는 이 분류가 너무 경직되어 있다는 점이었습니다. 예를 들어 한 회사가 1,000개의 IP가 필요하다고 가정해봅시다.

Class C는 256개라서 부족하고, Class B는 65,536개라서 너무 많습니다. 결국 Class B를 받으면 64,536개의 IP가 낭비됩니다.

IP 주소는 한정된 자원인데, 이렇게 낭비하면 금방 고갈됩니다. 바로 이 문제를 해결하기 위해 CIDR이 1993년에 도입되었습니다.

CIDR을 사용하면 필요한 만큼만 정확하게 IP를 할당할 수 있습니다. /22를 사용하면 1,024개, /23을 사용하면 512개, /25를 사용하면 128개의 IP를 얻습니다.

딱 필요한 크기로 자를 수 있는 것입니다. CIDR 표기법의 원리를 알아봅시다.

IP 주소는 32비트입니다. 10.0.0.0을 2진수로 바꾸면 00001010.00000000.00000000.00000000입니다.

/16은 앞의 16비트가 네트워크 부분이고, 나머지 16비트가 호스트 부분이라는 뜻입니다. 네트워크 부분은 고정됩니다.

10.0까지는 변하지 않습니다. 호스트 부분은 자유롭게 변할 수 있습니다.

0.0부터 255.255까지 사용할 수 있습니다. 그래서 10.0.0.0부터 10.0.255.255까지 총 65,536개의 IP 주소가 이 블록에 포함됩니다.

만약 /24라면 어떻게 될까요? 앞의 24비트가 고정되므로 10.0.0까지는 변하지 않습니다.

마지막 8비트만 변할 수 있어서, 10.0.0.0부터 10.0.0.255까지 256개의 IP가 포함됩니다. 위의 코드를 분석해봅시다.

Python의 ipaddress 모듈은 CIDR 계산을 쉽게 해줍니다. IPv4Network 객체를 생성하면, 해당 CIDR 블록에 대한 모든 정보를 얻을 수 있습니다.

num_addresses는 전체 IP 개수를 반환합니다. AWS에서는 각 서브넷마다 5개의 IP를 예약합니다.

첫 번째 IP는 네트워크 주소, 두 번째는 VPC 라우터, 세 번째는 DNS 서버, 네 번째는 미래를 위한 예약, 마지막 IP는 브로드캐스트 주소입니다. 따라서 실제로 사용 가능한 IP는 5개를 뺀 값입니다.

network_address와 broadcast_address는 블록의 시작과 끝을 나타냅니다. 마지막 계산은 /16 블록을 /24 크기로 쪼개면 몇 개의 서브넷을 만들 수 있는지 계산합니다.

2의 8승, 즉 256개입니다. 실무에서는 어떻게 활용할까요?

B사는 개발 환경, 스테이징 환경, 프로덕션 환경을 각각 별도의 VPC로 관리합니다. 개발은 10.0.0.0/16, 스테이징은 10.1.0.0/16, 프로덕션은 10.2.0.0/16을 사용합니다.

이렇게 CIDR을 체계적으로 관리하면 나중에 네트워크를 연결할 때도 IP 충돌이 발생하지 않습니다. 또한 서브넷을 만들 때도 CIDR을 사용합니다.

웹 서버용 서브넷은 10.0.1.0/24, 애플리케이션 서버용은 10.0.2.0/24, 데이터베이스용은 10.0.3.0/24로 분리합니다. 각각 256개의 IP를 가지며, 명확하게 구분됩니다.

주의할 점이 있습니다. 초보자들은 CIDR 블록이 겹치지 않게 주의해야 합니다.

예를 들어 10.0.0.0/16과 10.0.1.0/16을 동시에 사용하면 안 됩니다. 두 번째 블록이 첫 번째 블록에 완전히 포함되기 때문입니다.

10.0.1.0/24처럼 더 작은 블록을 만들어야 합니다. 김개발 씨는 이제 CIDR의 원리를 이해했습니다.

"아, /뒤의 숫자가 클수록 더 작은 범위를 나타내는 거네요!" CIDR을 제대로 이해하면 네트워크 설계가 훨씬 쉬워집니다. IP 주소를 낭비하지 않으면서도, 필요한 만큼 정확하게 할당할 수 있습니다.

다음 단계에서는 실제로 VPC를 생성하고 IP 대역을 설정해보겠습니다.

실전 팁

💡 - /16은 65,536개, /24는 256개, /28은 16개의 IP를 제공합니다. 자주 사용하는 값은 외워두세요.

  • CIDR 계산기 웹사이트(cidr.xyz)를 북마크하면 편리합니다.
  • 서브넷 CIDR은 VPC CIDR 범위 내에 있어야 하며, 겹치면 안 됩니다.

3. VPC 생성 및 IP 대역 설정

이론은 이제 충분합니다. 김개발 씨는 실제로 VPC를 생성해보기로 했습니다.

CTO님이 요청한 "개발 환경용 VPC"를 만들려면 어떤 IP 대역을 선택해야 할까요?

VPC 생성 시 가장 중요한 결정은 CIDR 블록 선택입니다. RFC 1918에서 정의한 프라이빗 IP 대역(10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) 중에서 선택해야 합니다.

일반적으로 10.x.x.x 대역을 선호하며, VPC는 /16 크기로 만드는 것이 표준입니다.

다음 코드를 살펴봅시다.

import boto3

# VPC 생성 및 설정 전체 과정
ec2 = boto3.resource('ec2', region_name='ap-northeast-2')
ec2_client = boto3.client('ec2', region_name='ap-northeast-2')

# 1. VPC 생성 - 10.0.0.0/16 대역 사용
vpc = ec2.create_vpc(CidrBlock='10.0.0.0/16')
vpc.wait_until_available()  # VPC가 사용 가능해질 때까지 대기

# 2. VPC에 이름 태그 추가
vpc.create_tags(Tags=[{"Key": "Name", "Value": "dev-vpc"}])

# 3. DNS 설정 활성화 - 인스턴스에 DNS 이름 할당
vpc.modify_attribute(EnableDnsSupport={'Value': True})
vpc.modify_attribute(EnableDnsHostnames={'Value': True})

# 4. VPC 정보 확인
print(f'VPC ID: {vpc.id}')
print(f'VPC CIDR: {vpc.cidr_block}')
print(f'VPC State: {vpc.state}')

김개발 씨는 박시니어 씨와 함께 VPC 설계를 시작했습니다. "선배님, 어떤 IP 대역을 사용하면 좋을까요?" 박시니어 씨는 회사의 네트워크 설계 문서를 꺼냈습니다.

"우리 회사는 10.x.x.x 대역을 표준으로 사용하기로 했어요. 개발 환경은 10.0.0.0/16, 스테이징은 10.1.0.0/16, 프로덕션은 10.2.0.0/16으로 할 거예요." IP 대역을 선택할 때는 몇 가지 고려사항이 있습니다.

먼저 프라이빗 IP 대역을 사용해야 합니다. 인터넷에서는 공인 IP만 라우팅되므로, VPC 내부에서는 사설 IP를 사용합니다.

RFC 1918에서는 세 가지 대역을 정의합니다. 10.0.0.0/8은 가장 큰 대역으로, 1,600만 개 이상의 IP를 제공합니다.

대기업이나 복잡한 네트워크 구조에 적합합니다. 172.16.0.0/12는 중간 크기로, 100만 개 이상의 IP를 제공합니다.

192.168.0.0/16은 가장 작은 대역으로, 가정용 공유기에서 흔히 사용합니다. 실무에서는 대부분 10.x.x.x 대역을 선호합니다.

충분히 크고, 다른 네트워크와 겹칠 가능성이 적기 때문입니다. 192.168.x.x는 집이나 카페 와이파이에서 자주 사용되므로, VPN 연결 시 충돌할 수 있습니다.

VPC 크기는 어떻게 정해야 할까요? AWS는 /16부터 /28까지 지원합니다.

하지만 실무에서는 거의 항상 /16을 사용합니다. 65,536개의 IP는 충분히 크지만, 관리하기 어려울 정도로 크지도 않습니다.

작은 스타트업이라도 /16을 권장합니다. 처음에는 서버가 몇 대 없어도, 서비스가 성장하면서 서버가 늘어납니다.

오토스케일링을 사용하면 순간적으로 수백 대의 인스턴스가 생성될 수 있습니다. /24로 만들었다가 나중에 IP가 부족해지면, VPC를 새로 만들어야 합니다.

위의 코드를 단계별로 살펴봅시다. 첫 번째 단계는 VPC 객체를 생성하는 것입니다.

create_vpc 메서드에 CIDR 블록을 전달하면, AWS가 VPC를 생성합니다. 하지만 생성 직후에는 아직 사용할 수 없는 상태입니다.

wait_until_available 메서드는 VPC가 완전히 생성될 때까지 기다립니다. 이것은 중요합니다.

VPC가 준비되지 않은 상태에서 서브넷을 만들려고 하면 오류가 발생합니다. 다음으로 태그를 추가합니다.

AWS에서 태그는 매우 중요합니다. 리소스가 수백 개로 늘어나면, 태그 없이는 관리가 불가능합니다.

'dev-vpc'라는 이름을 붙여서, 이것이 개발 환경용 VPC임을 명확히 합니다. DNS 설정은 두 가지가 있습니다.

EnableDnsSupport는 AWS DNS 서버를 사용할지 결정합니다. 이것은 기본적으로 활성화되어 있습니다.

EnableDnsHostnames는 EC2 인스턴스에 DNS 이름을 자동으로 할당할지 결정합니다. 예를 들어 EnableDnsHostnames를 활성화하면, 인스턴스가 'ip-10-0-1-5.ap-northeast-2.compute.internal' 같은 DNS 이름을 받습니다.

이 이름으로 다른 인스턴스에서 접근할 수 있어 편리합니다. 실제 프로젝트에서는 어떻게 적용할까요?

C사는 마이크로서비스 아키텍처를 사용합니다. 주문 서비스, 결제 서비스, 배송 서비스가 각각 독립적으로 운영됩니다.

모든 서비스를 하나의 VPC(10.0.0.0/16)에 배치하고, 각 서비스마다 별도의 서브넷을 할당합니다. 이렇게 하면 서비스 간 통신은 VPC 내부 네트워크로 이루어져 빠르고 안전합니다.

외부 인터넷을 거치지 않으므로 데이터 전송 비용도 절약됩니다. 주의할 점도 있습니다.

VPC를 생성한 후에는 CIDR 블록을 변경할 수 없습니다. 보조 CIDR 블록을 추가할 수는 있지만, 메인 CIDR 블록은 절대 변경되지 않습니다.

따라서 처음부터 신중하게 선택해야 합니다. 또한 다른 네트워크와의 연결을 고려해야 합니다.

만약 회사 사무실 네트워크가 10.0.0.0/16을 사용한다면, VPC도 같은 대역을 사용하면 안 됩니다. VPN이나 Direct Connect로 연결할 때 IP가 충돌합니다.

김개발 씨는 성공적으로 첫 VPC를 생성했습니다. AWS 콘솔에 접속해보니 'dev-vpc'가 정상적으로 만들어져 있었습니다.

"생각보다 간단하네요!" 하지만 VPC만으로는 아무것도 할 수 없습니다. 이 안에 서브넷을 만들어야 실제로 인스턴스를 배치할 수 있습니다.

서브넷에도 퍼블릭과 프라이빗이라는 중요한 구분이 있습니다.

실전 팁

💡 - VPC는 항상 /16 크기로 만드세요. 나중에 확장이 불가능합니다.

  • 10.x.x.x 대역을 사용하되, 온프레미스 네트워크와 겹치지 않는지 확인하세요.
  • 태그는 반드시 붙이세요. Name, Environment, Team 등의 태그가 유용합니다.

4. 퍼블릭 서브넷 vs 프라이빗 서브넷

김개발 씨는 VPC를 만들었지만, 아직 서버를 띄울 수 없습니다. "선배님, 서브넷을 만들라는데, 퍼블릭과 프라이빗 중 어떤 걸 만들어야 하나요?" 박시니어 씨는 서브넷의 차이를 설명하기 시작했습니다.

퍼블릭 서브넷은 인터넷과 직접 통신할 수 있는 서브넷이고, 프라이빗 서브넷은 인터넷과 직접 통신할 수 없는 서브넷입니다. 퍼블릭 서브넷에는 웹 서버나 로드 밸런서를 배치하고, 프라이빗 서브넷에는 데이터베이스나 애플리케이션 서버를 배치합니다.

차이는 인터넷 게이트웨이로의 라우팅 여부에 있습니다.

다음 코드를 살펴봅시다.

import boto3

ec2 = boto3.resource('ec2', region_name='ap-northeast-2')
ec2_client = boto3.client('ec2', region_name='ap-northeast-2')

# 기존 VPC 가져오기
vpc = ec2.Vpc('vpc-xxxxx')

# 1. 퍼블릭 서브넷 생성 (웹 서버용)
public_subnet = vpc.create_subnet(
    CidrBlock='10.0.1.0/24',
    AvailabilityZone='ap-northeast-2a'
)
public_subnet.create_tags(Tags=[{"Key": "Name", "Value": "public-subnet-2a"}])

# 2. 프라이빗 서브넷 생성 (DB용)
private_subnet = vpc.create_subnet(
    CidrBlock='10.0.11.0/24',
    AvailabilityZone='ap-northeast-2a'
)
private_subnet.create_tags(Tags=[{"Key": "Name", "Value": "private-subnet-2a"}])

# 3. 인터넷 게이트웨이 생성 및 VPC 연결
igw = ec2.create_internet_gateway()
vpc.attach_internet_gateway(InternetGatewayId=igw.id)

# 4. 퍼블릭 라우팅 테이블 생성
public_route_table = vpc.create_route_table()
public_route_table.create_route(
    DestinationCidrBlock='0.0.0.0/0',  # 모든 인터넷 트래픽
    GatewayId=igw.id  # 인터넷 게이트웨이로 라우팅
)
public_route_table.associate_with_subnet(SubnetId=public_subnet.id)

print(f'퍼블릭 서브넷: {public_subnet.id}')
print(f'프라이빗 서브넷: {private_subnet.id}')

박시니어 씨는 다시 화이트보드를 꺼냈습니다. "김개발 씨, 회사 건물을 생각해봐요.

1층 로비는 누구나 들어올 수 있죠. 하지만 서버실은 허가받은 사람만 들어갈 수 있어요." 서브넷도 마찬가지입니다.

퍼블릭 서브넷은 1층 로비처럼 외부에 개방되어 있고, 프라이빗 서브넷은 서버실처럼 내부 전용 공간입니다. 이 두 가지를 적절히 조합하면 안전하면서도 기능적인 네트워크를 만들 수 있습니다.

그렇다면 무엇이 서브넷을 퍼블릭 또는 프라이빗으로 만들까요? 많은 사람들이 착각하는데, 서브넷 자체에 '퍼블릭' 플래그가 있는 것은 아닙니다.

중요한 것은 라우팅 테이블입니다. 서브넷의 라우팅 테이블에 인터넷 게이트웨이로 향하는 경로가 있으면 퍼블릭, 없으면 프라이빗입니다.

인터넷 게이트웨이는 VPC와 인터넷 사이의 관문입니다. 마치 공항의 출입국 심사대와 같습니다.

퍼블릭 서브넷의 인스턴스가 인터넷에 접속하려면, 트래픽이 반드시 인터넷 게이트웨이를 거쳐야 합니다. 언제 퍼블릭 서브넷을 사용할까요?

웹 서버는 사용자의 HTTP 요청을 받아야 하므로 퍼블릭 서브넷에 배치합니다. 로드 밸런서도 외부 트래픽을 받아야 하므로 퍼블릭입니다.

NAT 게이트웨이도 퍼블릭 서브넷에 위치합니다. 하지만 대부분의 리소스는 프라이빗 서브넷에 배치하는 것이 좋습니다.

데이터베이스는 절대 인터넷에 노출되면 안 됩니다. 해커가 직접 공격할 수 있기 때문입니다.

애플리케이션 서버도 로드 밸런서 뒤에 숨기는 것이 안전합니다. 위의 코드를 자세히 분석해봅시다.

먼저 두 개의 서브넷을 생성합니다. 하나는 10.0.1.0/24 대역으로 퍼블릭용이고, 다른 하나는 10.0.11.0/24 대역으로 프라이빗용입니다.

이때 중요한 파라미터가 AvailabilityZone입니다. AWS의 각 리전은 여러 개의 가용 영역으로 구성됩니다.

서울 리전(ap-northeast-2)에는 2a, 2b, 2c, 2d 네 개의 영역이 있습니다. 서브넷은 반드시 하나의 가용 영역에 속해야 합니다.

고가용성을 위해서는 여러 영역에 걸쳐 서브넷을 만들어야 합니다. 다음으로 인터넷 게이트웨이를 생성합니다.

VPC 하나당 하나의 인터넷 게이트웨이만 연결할 수 있습니다. 게이트웨이를 만든 후 attach_internet_gateway로 VPC에 연결합니다.

핵심은 라우팅 테이블입니다. create_route로 '0.0.0.0/0'으로 향하는 트래픽을 인터넷 게이트웨이로 보내도록 설정합니다.

0.0.0.0/0은 '모든 IP 주소'를 의미합니다. 즉, VPC 내부가 아닌 모든 트래픽은 인터넷 게이트웨이로 보냅니다.

마지막으로 associate_with_subnet으로 이 라우팅 테이블을 퍼블릭 서브넷에 연결합니다. 이 순간, 서브넷이 퍼블릭이 됩니다.

프라이빗 서브넷은 기본 라우팅 테이블을 사용하므로, 인터넷 게이트웨이 경로가 없습니다. 실무에서는 어떻게 구성할까요?

D사는 3-tier 아키텍처를 사용합니다. 웹 티어는 퍼블릭 서브넷에, 애플리케이션 티어와 데이터베이스 티어는 프라이빗 서브넷에 배치합니다.

사용자는 웹 티어에만 접근할 수 있고, 웹 티어가 내부적으로 애플리케이션 티어를 호출하고, 애플리케이션 티어가 데이터베이스에 접근합니다. 이렇게 계층을 나누면 보안이 크게 향상됩니다.

해커가 웹 서버를 공격해도, 데이터베이스까지 도달하기 어렵습니다. 각 계층마다 보안 그룹으로 트래픽을 제어하기 때문입니다.

주의해야 할 점이 있습니다. 프라이빗 서브넷의 인스턴스도 때로는 인터넷에 접속해야 합니다.

예를 들어 소프트웨어 업데이트를 다운로드하거나, 외부 API를 호출할 수 있습니다. 하지만 직접 인터넷 게이트웨이를 사용하면 퍼블릭 IP가 노출됩니다.

이럴 때는 NAT 게이트웨이를 사용합니다. NAT 게이트웨이는 퍼블릭 서브넷에 배치되고, 프라이빗 서브넷의 트래픽을 대신 전달합니다.

프라이빗 인스턴스는 NAT 게이트웨이를 통해 외부에 접속하지만, 외부에서는 프라이빗 인스턴스에 직접 접근할 수 없습니다. 김개발 씨는 이제 퍼블릭과 프라이빗의 차이를 명확히 이해했습니다.

"웹 서버는 퍼블릭에, 데이터베이스는 프라이빗에 두면 되는 거네요!" 올바른 서브넷 구성은 보안의 첫걸음입니다. 다음 단계에서는 VPC를 어떻게 여러 서브넷으로 효율적으로 나눌지 알아보겠습니다.

실전 팁

💡 - 웹 서버와 로드 밸런서는 퍼블릭 서브넷에, 나머지는 모두 프라이빗 서브넷에 배치하세요.

  • 프라이빗 서브넷도 NAT 게이트웨이를 통해 인터넷 접속이 가능합니다.
  • 고가용성을 위해 각 타입의 서브넷을 최소 2개 이상의 가용 영역에 만드세요.

5. 서브넷 분할 전략

김개발 씨는 VPC 10.0.0.0/16을 받았습니다. 이제 이것을 여러 서브넷으로 나눠야 하는데, 어떤 기준으로 나눠야 할까요?

박시니어 씨는 회사의 서브넷 설계 문서를 보여주며 설명했습니다.

서브넷 분할은 네트워크 설계에서 가장 중요한 단계입니다. 가용 영역별, 계층별(웹/앱/DB), 환경별(개발/스테이징/프로덕션)로 나누는 것이 일반적입니다.

/24 크기(256개 IP)가 대부분의 용도에 적합하며, 확장성을 고려해 충분한 여유를 두어야 합니다.

다음 코드를 살펴봅시다.

import ipaddress

# VPC CIDR 블록
vpc_cidr = ipaddress.IPv4Network('10.0.0.0/16')

# 서브넷 분할 전략 - 2개 가용 영역, 3계층 아키텍처
subnets = {
    # 가용 영역 A - 퍼블릭
    'public-2a': '10.0.1.0/24',      # 웹 서버, 로드 밸런서
    'app-2a': '10.0.11.0/24',        # 애플리케이션 서버
    'db-2a': '10.0.21.0/24',         # 데이터베이스

    # 가용 영역 C - 퍼블릭
    'public-2c': '10.0.2.0/24',      # 웹 서버, 로드 밸런서
    'app-2c': '10.0.12.0/24',        # 애플리케이션 서버
    'db-2c': '10.0.22.0/24',         # 데이터베이스

    # 관리용 서브넷
    'mgmt-2a': '10.0.31.0/24',       # 배스천 호스트, VPN
    'reserved': '10.0.40.0/21',      # 미래 확장용 (2048개 IP)
}

# 각 서브넷의 사용 가능 IP 계산
for name, cidr in subnets.items():
    if 'reserved' not in name:
        network = ipaddress.IPv4Network(cidr)
        usable_ips = network.num_addresses - 5  # AWS 예약 5개
        print(f'{name}: {cidr} - 사용 가능 IP {usable_ips}개')

박시니어 씨는 화이트보드에 큰 사각형을 그렸습니다. "이게 우리 VPC예요.

10.0.0.0/16, 즉 65,536개의 IP 주소가 있습니다. 이제 이것을 효율적으로 나눠야 해요." "마치 아파트 단지를 설계하는 것과 비슷해요.

동마다 용도가 다르고, 각 동의 크기도 다르죠. 서브넷도 마찬가지예요." 서브넷을 나누는 첫 번째 기준은 가용 영역입니다.

AWS는 각 리전을 여러 개의 물리적으로 분리된 데이터센터로 구성합니다. 서울 리전에는 네 개의 가용 영역이 있습니다.

한 영역에 장애가 발생해도 다른 영역은 정상 작동합니다. 따라서 서비스의 고가용성을 위해서는 최소 두 개 이상의 영역에 서브넷을 만들어야 합니다.

예를 들어 2a 영역과 2c 영역에 각각 퍼블릭 서브넷을 만들고, 로드 밸런서가 두 영역의 서버를 모두 사용하도록 설정합니다. 두 번째 기준은 계층입니다.

3-tier 아키텍처에서는 프레젠테이션 계층, 애플리케이션 계층, 데이터 계층을 분리합니다. 각 계층마다 별도의 서브넷을 만들면, 보안 그룹으로 계층 간 트래픽을 세밀하게 제어할 수 있습니다.

예를 들어 웹 계층은 인터넷에서 80/443 포트로 접근 가능하지만, 앱 계층은 웹 계층에서만 접근 가능합니다. 데이터베이스 계층은 앱 계층에서만 3306 포트(MySQL)로 접근 가능합니다.

세 번째 기준은 퍼블릭/프라이빗입니다. 앞에서 배운 것처럼, 인터넷에 노출되는 리소스는 퍼블릭 서브넷에, 내부 전용 리소스는 프라이빗 서브넷에 배치합니다.

일반적으로 웹 계층만 퍼블릭이고, 나머지는 모두 프라이빗입니다. 서브넷 크기는 어떻게 정할까요?

실무에서는 대부분 /24를 사용합니다. 256개의 IP는 대부분의 용도에 충분합니다.

AWS가 5개를 예약하므로, 실제로는 251개를 사용할 수 있습니다. 오토스케일링으로 인스턴스가 늘어나도 충분히 감당할 수 있습니다.

만약 특별히 큰 서브넷이 필요하다면 /23(512개) 또는 /22(1024개)를 사용할 수 있습니다. 반대로 관리용 서브넷처럼 인스턴스가 몇 개 안 되는 곳은 /26(64개) 또는 /28(16개)을 사용할 수 있습니다.

위의 코드에서 사용한 전략을 분석해봅시다. 2a와 2c 두 개의 가용 영역을 사용합니다.

각 영역마다 퍼블릭, 앱, DB 서브넷을 만들어서 총 6개의 주요 서브넷이 생깁니다. 관리용 서브넷은 2a에만 만듭니다.

배스천 호스트나 VPN 서버는 고가용성이 덜 중요하기 때문입니다. IP 대역을 보면 패턴이 있습니다.

퍼블릭은 10.0.x.0, 앱은 10.0.1x.0, DB는 10.0.2x.0을 사용합니다. 이렇게 규칙적으로 만들면 나중에 IP 주소만 봐도 어떤 서브넷인지 알 수 있습니다.

중요한 것은 예약 공간입니다. 10.0.40.0/21은 2,048개의 IP를 제공하며, 미래 확장을 위해 남겨둡니다.

처음부터 모든 IP를 할당하면, 나중에 새로운 서브넷을 만들 공간이 없어집니다. 실무 사례를 살펴봅시다.

E사는 MSA(마이크로서비스 아키텍처)를 채택했습니다. 서비스가 20개 이상 있고, 각 서비스마다 개발, 스테이징, 프로덕션 환경이 있습니다.

모든 것을 하나의 VPC에 넣으면 복잡해집니다. 따라서 환경별로 VPC를 분리했습니다.

개발 VPC는 10.0.0.0/16, 스테이징은 10.1.0.0/16, 프로덕션은 10.2.0.0/16입니다. 각 VPC 내부에서는 서비스별로 서브넷을 나눕니다.

또 다른 회사 F사는 서브넷을 기능별로 나눕니다. 웹 서브넷, API 서브넷, 백그라운드 작업 서브넷, 데이터 처리 서브넷 등으로 세밀하게 분리합니다.

각 서브넷마다 다른 보안 정책과 네트워크 ACL을 적용할 수 있습니다. 흔한 실수를 피해야 합니다.

초보자들은 서브넷을 너무 작게 만들거나, 너무 많이 만드는 경향이 있습니다. /28로 만들면 16개 IP밖에 없어서, 조금만 확장해도 부족합니다.

반대로 서브넷을 수십 개 만들면 관리가 복잡해집니다. 적절한 균형이 중요합니다.

대부분의 경우, 가용 영역당 3-4개의 서브넷(퍼블릭, 앱, DB, 관리)이면 충분합니다. 서비스가 커지면 VPC를 추가로 만드는 것이 서브넷을 무한정 늘리는 것보다 낫습니다.

김개발 씨는 이제 서브넷 설계의 원칙을 이해했습니다. "가용 영역별, 계층별로 나누고, /24 크기로 만들고, 확장 여유를 남겨두는 거네요!" 체계적인 서브넷 분할은 나중에 네트워크 문제를 해결할 때 큰 도움이 됩니다.

IP 주소만 봐도 어느 계층의 어느 영역인지 바로 알 수 있기 때문입니다.

실전 팁

💡 - 서브넷은 /24 크기가 가장 실용적입니다. 251개의 사용 가능한 IP를 제공합니다.

  • IP 대역을 규칙적으로 할당하세요. 10.0.1x.0은 앱, 10.0.2x.0은 DB 같은 패턴을 만드세요.
  • 전체 IP의 30-40%는 미래 확장을 위해 예약해두세요.

6. 서브넷 생성 실습

드디어 실전입니다. 김개발 씨는 앞서 배운 내용을 바탕으로 실제 서브넷을 생성해보기로 했습니다.

CTO님의 요구사항은 "2개 가용 영역에 걸친 고가용성 웹 서비스 네트워크"입니다.

서브넷 생성은 VPC 내에서 IP 대역을 분할하고, 각 영역에 배치하고, 라우팅 테이블을 설정하는 과정입니다. 퍼블릭 서브넷은 인터넷 게이트웨이로, 프라이빗 서브넷은 NAT 게이트웨이로 라우팅됩니다.

모든 서브넷은 반드시 하나의 가용 영역에 속하며, 고가용성을 위해 여러 영역에 중복 배치합니다.

다음 코드를 살펴봅시다.

import boto3

ec2 = boto3.resource('ec2', region_name='ap-northeast-2')
ec2_client = boto3.client('ec2', region_name='ap-northeast-2')

# VPC 가져오기
vpc = ec2.Vpc('vpc-xxxxx')

# 인터넷 게이트웨이 생성 및 연결
igw = ec2.create_internet_gateway()
vpc.attach_internet_gateway(InternetGatewayId=igw.id)

# 서브넷 생성 - 2개 가용 영역 x 3계층
subnets = {}

# 가용 영역 A
subnets['public-2a'] = vpc.create_subnet(CidrBlock='10.0.1.0/24', AvailabilityZone='ap-northeast-2a')
subnets['app-2a'] = vpc.create_subnet(CidrBlock='10.0.11.0/24', AvailabilityZone='ap-northeast-2a')
subnets['db-2a'] = vpc.create_subnet(CidrBlock='10.0.21.0/24', AvailabilityZone='ap-northeast-2a')

# 가용 영역 C
subnets['public-2c'] = vpc.create_subnet(CidrBlock='10.0.2.0/24', AvailabilityZone='ap-northeast-2c')
subnets['app-2c'] = vpc.create_subnet(CidrBlock='10.0.12.0/24', AvailabilityZone='ap-northeast-2c')
subnets['db-2c'] = vpc.create_subnet(CidrBlock='10.0.22.0/24', AvailabilityZone='ap-northeast-2c')

# 퍼블릭 라우팅 테이블 생성
public_rt = vpc.create_route_table()
public_rt.create_route(DestinationCidrBlock='0.0.0.0/0', GatewayId=igw.id)

# 퍼블릭 서브넷에 라우팅 테이블 연결
public_rt.associate_with_subnet(SubnetId=subnets['public-2a'].id)
public_rt.associate_with_subnet(SubnetId=subnets['public-2c'].id)

# NAT 게이트웨이 생성 (프라이빗 서브넷용)
# 먼저 Elastic IP 할당
eip_response = ec2_client.allocate_address(Domain='vpc')
nat_gw_response = ec2_client.create_nat_gateway(
    SubnetId=subnets['public-2a'].id,  # NAT는 퍼블릭 서브넷에 배치
    AllocationId=eip_response['AllocationId']
)

print('서브넷 생성 완료!')
for name, subnet in subnets.items():
    print(f'{name}: {subnet.id} ({subnet.cidr_block})')

김개발 씨는 노트북을 열고 코드를 작성하기 시작했습니다. 박시니어 씨가 옆에서 지켜보며 조언을 해줍니다.

"자, 차근차근 해봅시다." 첫 번째 단계는 인터넷 게이트웨이를 만드는 것입니다. VPC는 기본적으로 완전히 격리되어 있습니다.

외부와 통신하려면 게이트웨이가 필요합니다. create_internet_gateway로 게이트웨이를 만들고, attach_internet_gateway로 VPC에 연결합니다.

VPC 하나당 게이트웨이는 하나만 연결할 수 있습니다. 인터넷 게이트웨이는 두 가지 역할을 합니다.

첫째, VPC 내부에서 외부로 나가는 트래픽을 전달합니다. 둘째, 외부에서 퍼블릭 IP를 가진 인스턴스로 들어오는 트래픽을 전달합니다.

마치 회사 건물의 정문과 같습니다. 두 번째 단계는 서브넷을 생성하는 것입니다.

create_subnet 메서드에 두 가지 중요한 파라미터를 전달합니다. CidrBlock은 서브넷의 IP 대역이고, AvailabilityZone은 물리적 위치입니다.

CIDR 블록은 VPC의 CIDR 범위 내에 있어야 하며, 다른 서브넷과 겹치면 안 됩니다. 가용 영역을 명시하는 것이 중요합니다.

지정하지 않으면 AWS가 임의로 선택하는데, 나중에 서브넷 간 데이터 전송 비용이 예상과 달라질 수 있습니다. 명시적으로 지정하면 네트워크 구조를 정확히 제어할 수 있습니다.

코드를 보면 2a와 2c 영역에 각각 3개씩, 총 6개의 서브넷을 만듭니다. 퍼블릭, 앱, DB 서브넷이 각 영역마다 하나씩 있습니다.

이렇게 하면 한 영역에 장애가 발생해도 다른 영역이 서비스를 계속할 수 있습니다. 세 번째 단계는 라우팅 테이블을 설정하는 것입니다.

라우팅 테이블은 네트워크 트래픽의 목적지를 결정합니다. "10.0.x.x로 가는 트래픽은 VPC 내부에서 처리하고, 그 외의 모든 트래픽은 인터넷 게이트웨이로 보내라"와 같은 규칙을 정의합니다.

create_route_table로 새 라우팅 테이블을 만들고, create_route로 경로를 추가합니다. '0.0.0.0/0'은 모든 IP를 의미하므로, VPC 외부의 모든 트래픽이 인터넷 게이트웨이로 향합니다.

마지막으로 associate_with_subnet으로 라우팅 테이블을 서브넷에 연결합니다. 이 순간, 서브넷이 퍼블릭이 됩니다.

두 개의 퍼블릭 서브넷(2a, 2c)이 같은 라우팅 테이블을 공유합니다. 네 번째 단계는 NAT 게이트웨이를 만드는 것입니다.

프라이빗 서브넷의 인스턴스도 소프트웨어 업데이트나 외부 API 호출을 위해 인터넷에 접속해야 할 때가 있습니다. 하지만 퍼블릭 IP를 주면 보안상 위험합니다.

NAT 게이트웨이가 이 문제를 해결합니다. NAT 게이트웨이는 퍼블릭 서브넷에 배치됩니다.

프라이빗 인스턴스의 요청을 받아서, 자신의 퍼블릭 IP로 변환해서 인터넷에 전달합니다. 응답이 오면 다시 프라이빗 IP로 변환해서 돌려줍니다.

외부에서는 NAT 게이트웨이의 IP만 보이고, 프라이빗 인스턴스의 IP는 보이지 않습니다. NAT 게이트웨이를 만들려면 먼저 Elastic IP가 필요합니다.

Elastic IP는 AWS에서 제공하는 고정 퍼블릭 IP입니다. allocate_address로 할당받고, create_nat_gateway로 NAT 게이트웨이를 생성할 때 이 IP를 연결합니다.

실무에서는 추가 설정이 필요합니다. 프라이빗 서브넷용 라우팅 테이블을 만들어야 합니다.

퍼블릭과 비슷하지만, 인터넷 게이트웨이 대신 NAT 게이트웨이로 향하는 경로를 추가합니다. 그리고 이 라우팅 테이블을 프라이빗 서브넷(app-2a, db-2a, app-2c, db-2c)에 연결합니다.

고가용성을 위해서는 각 가용 영역마다 NAT 게이트웨이를 만드는 것이 좋습니다. 위 코드는 2a에만 만들었지만, 실제로는 2c에도 만들어야 합니다.

그렇지 않으면 2a 영역에 장애가 발생했을 때 2c의 프라이빗 인스턴스도 인터넷에 접속할 수 없습니다. 주의할 점도 있습니다.

NAT 게이트웨이는 시간당 요금이 부과됩니다. 개발 환경에서는 비용 절감을 위해 NAT 인스턴스(EC2 기반)를 사용하거나, 필요할 때만 NAT 게이트웨이를 켤 수도 있습니다.

하지만 프로덕션에서는 관리형 NAT 게이트웨이가 안정적이고 편리합니다. 또한 서브넷의 퍼블릭 IP 자동 할당 설정도 중요합니다.

퍼블릭 서브넷의 인스턴스는 자동으로 퍼블릭 IP를 받아야 외부와 통신할 수 있습니다. modify_subnet_attribute로 MapPublicIpOnLaunch를 활성화하면 됩니다.

김개발 씨는 코드를 실행했습니다. 몇 분 후, AWS 콘솔에 6개의 서브넷이 깔끔하게 생성되어 있었습니다.

"성공했어요! 이제 여기에 인스턴스를 띄우면 되는 거죠?" 박시니어 씨는 고개를 끄덕였습니다.

"맞아요. 이제 웹 서버는 퍼블릭 서브넷에, 애플리케이션은 앱 서브넷에, 데이터베이스는 DB 서브넷에 배치하면 됩니다.

보안 그룹으로 트래픽을 제어하면 안전한 네트워크가 완성되는 거예요." VPC와 서브넷 설계는 클라우드 인프라의 기초입니다. 제대로 설계하면 보안이 강화되고, 확장이 쉬워지며, 문제 발생 시 빠르게 대응할 수 있습니다.

처음에는 복잡해 보이지만, 원리를 이해하면 논리적이고 체계적인 구조임을 알 수 있습니다.

실전 팁

💡 - 서브넷 생성 직후 Name 태그를 붙여서 콘솔에서 쉽게 식별하세요.

  • 퍼블릭 서브넷은 MapPublicIpOnLaunch를 활성화해서 인스턴스가 자동으로 퍼블릭 IP를 받게 하세요.
  • NAT 게이트웨이는 각 가용 영역마다 하나씩 만들어야 고가용성을 보장합니다.

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#AWS#VPC#CIDR#Subnet#Network#AWS,VPC,Network

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

함께 보면 좋은 카드 뉴스