CIDR — 왜 존재하는가, IP 주소 고갈 문제의 해결책

AWS VPC를 처음 만들 때 10.0.0.0/16을 아무 생각 없이 입력했다.

동작은 했다. 그런데 나중에 두 VPC를 Peering으로 연결하려 했더니 CIDR이 겹쳐서 실패했다. Secondary CIDR을 붙이는 지저분한 작업을 했다. VPC CIDR은 나중에 바꿀 수 없다.

CIDR은 처음에 제대로 이해하지 않으면 나중에 반드시 피를 보는 개념이다. 처음부터 파고들자.


IPv4가 43억 개인데 왜 부족한가?

2^32 = 4,294,967,296. 약 43억 개다. 지구 인구보다 많다.

처음 들으면 충분해 보인다. 왜 부족할까?

첫 번째 이유 — NAT 이전에는 기기마다 공인 IP가 필요했다

지금은 공유기 뒤에 스마트폰, 노트북, TV, 냉장고가 동시에 붙어도 공인 IP는 하나다. NAT(Network Address Translation) 덕분이다. 하지만 1993년 CIDR이 등장했을 때는 NAT가 없었다. 인터넷에 연결된 모든 기기가 공인 IP를 요구했다.

두 번째 이유 — 주소 할당 방식 자체가 낭비를 강제했다

이게 CIDR이 해결하려 한 진짜 문제다.

1981년 IPv4 설계 당시 주소를 클래스(Class)로 나눴는데, 이 구조가 천문학적인 낭비를 만들었다.

세 번째 이유 — 예약된 주소들

0.0.0.0/8, 127.0.0.0/8(루프백), 169.254.0.0/16(Link-local), 멀티캐스트 블록 등 수억 개가 특수 용도로 예약되어 있다. 공인 IP로 쓸 수 있는 실제 주소는 43억에서 한참 줄어든다.


Class A/B/C 체계가 어떤 낭비를 만들었나?

1981년 RFC 791(IPv4)은 주소를 클래스로 구분했다.

클래스첫 비트범위서브넷 마스크호스트 수
A00.0.0.0 ~ 127.255.255.255/816,777,214
B10128.0.0.0 ~ 191.255.255.255/1665,534
C110192.0.0.0 ~ 223.255.255.255/24254

선택지가 딱 세 가지다. /8, /16, /24 중 하나를 골라야 한다.

1,000명 규모 회사가 IP를 신청하면:

  • Class C(/24, 254개): 부족하다
  • Class B(/16, 65,534개): 받는다

Class B 하나를 받으면 65,534개 주소 중 1,000개만 쓰고 64,534개는 버려진다. 할당됐지만 아무도 못 쓰는 주소가 된다.

1992년 분석에 따르면 Class B 블록이 당시 추세대로라면 2년 안에 고갈된다는 예측이 나왔다.

실제 사례: MIT는 한때 18.0.0.0/8을 통째로 보유했다 — 약 1,600만 개. 실제로 쓰는 건 수만 개 수준이었고 나머지는 잠겨 있었다. 결국 2017년에 이 블록의 약 절반을 매각했다(Amazon 등에 양도). 지금은 그 일부만 보유한다. 큰 블록을 받아도 대부분 못 쓰고 잠긴다는 낭비 구조를 보여주는 사례다.

이 낭비 구조를 깨야 했다.


CIDR이 뭔데 이 낭비를 어떻게 줄이나?

CIDR(Classless Inter-Domain Routing) — 1993년 RFC 1519로 표준화됐다.

핵심 아이디어는 한 문장이다: 클래스라는 고정 경계를 없애고, 비트 단위로 주소 블록을 자유롭게 나눠 쓰자.

클래스풀 체계에서는 /8, /16, /24 중에서만 선택했다. CIDR에서는 /9, /14, /22, /27 — 1부터 32까지 어디서도 자를 수 있다.

1,000개 IP가 필요한 회사에게 /22(1,022개 사용 가능)를 준다. Class B의 65,534개 대신 1,022개. 낭비가 98% 줄었다. 같은 Class B 주소 블록에서 이제 64개 회사에 IP를 줄 수 있다.

라우터 입장에서도 이점이 생겼다. CIDR에서는 라우팅 테이블 엔트리에 프리픽스 길이를 명시한다.

프리픽스 집계(Route Aggregation) — 여러 연속된 엔트리를 하나로 묶어 라우팅 테이블 크기를 줄일 수 있다.

// 집계 전
203.0.113.0/26
203.0.113.64/26
203.0.113.128/26
203.0.113.192/26

// 집계 후
203.0.113.0/24  (위 4개를 하나로)

라우터가 기억해야 하는 엔트리가 줄어든다.


/24가 뭔가? 256개인데 왜 254개만 쓸 수 있나?

192.168.1.0/24를 이진수로 펼쳐보자.

192      . 168      .  1       .  0
11000000 . 10101000 . 00000001 . 00000000

/24: 앞 24비트 = 네트워크 주소
     뒤  8비트 = 호스트 주소

[네트워크 (24비트)       ][호스트(8비트)]
[11000000.10101000.00000001][00000000]

프리픽스 길이가 24라는 말은 앞 24비트가 네트워크를 식별하고, 나머지 8비트가 개별 호스트를 식별한다는 뜻이다.

사용 가능한 IP 수 계산:

  • 총 주소: 2^(32-24) = 2^8 = 256
  • 네트워크 주소(192.168.1.0): 1개 빼기 — 서브넷 자체를 식별
  • 브로드캐스트 주소(192.168.1.255): 1개 빼기 — 서브넷 내 전체 전송
  • 사용 가능: 254개

공식: 2^(32 - prefix_length) - 2

CIDR총 주소사용 가능용도
/1665,53665,534VPC 전체
/24256254일반 서브넷
/266462소규모 서브넷
/281614최소 서브넷
/3042포인트-투-포인트 링크
/3211단일 호스트 (Security Group 규칙 등)

서브넷 마스크 표기와 CIDR 표기는 같은 정보의 다른 표현이다:

  • /24 = 255.255.255.0 (앞 24비트가 1, 나머지 0)
  • /26 = 255.255.255.192 (앞 26비트가 1)

서브넷을 더 쪼개려면 어떻게 하나?

부서별로 IP를 나눠야 할 때, /24/26 4개로 분할해보자.

/26은 앞 26비트가 네트워크, 뒤 6비트가 호스트다. /24에서 2비트를 더 빌린 셈이다. 2비트로 4가지 조합(00, 01, 10, 11)이 가능하므로 서브넷 4개가 생긴다.

192.168.1.0/26
  범위: 192.168.1.0 ~ 192.168.1.63
  네트워크:   192.168.1.0
  브로드캐스트: 192.168.1.63
  사용 가능: 192.168.1.1 ~ 192.168.1.62 (62개)

192.168.1.64/26
  범위: 192.168.1.64 ~ 192.168.1.127
  사용 가능: 62개

192.168.1.128/26
  범위: 192.168.1.128 ~ 192.168.1.191
  사용 가능: 62개

192.168.1.192/26
  범위: 192.168.1.192 ~ 192.168.1.255
  사용 가능: 62개

서브넷 경계 정렬 규칙 — 반드시 지켜야 한다:

/26(64개 주소)의 시작 주소는 반드시 64의 배수여야 한다(0, 64, 128, 192). 192.168.1.10/26 같은 표기는 잘못된 것이다. 이걸 어기면 라우터가 경로를 제대로 계산하지 못한다.

VLSM(Variable Length Subnet Masking) — CIDR의 진짜 힘:

하나의 주소 블록에서 서브넷을 서로 다른 크기로 잘라 쓸 수 있다.

10.0.0.0/16 (부모 블록)
├── 개발팀: 10.0.1.0/24   → 254개 (서버가 많음)
├── 마케팅: 10.0.2.0/27   → 30개  (PC만)
├── 운영팀: 10.0.3.0/26   → 62개  (중간)
└── DMZ:    10.0.4.0/28   → 14개  (외부 공개 서버 소수)

필요한 만큼만 쓴다. 나머지는 낭비되지 않는다.


AWS VPC에서 CIDR을 어떻게 설계해야 하나?

VPC CIDR은 나중에 바꿀 수 없다. 처음에 제대로 잡아야 한다.

잘못 설계하면:

  • 서비스 성장으로 IP가 부족해진다. 서브넷 크기는 변경 불가
  • VPC Peering 시 CIDR이 겹치면 연결 자체가 불가능하다
  • Secondary CIDR을 붙이면 라우팅 테이블이 복잡해진다

VPC CIDR 선택 기준:

RFC 1918 사설 주소 블록 중에서 선택한다:

  • 10.0.0.0/8 — 가장 큰 블록, 다중 VPC 환경에 적합
  • 172.16.0.0/12 — 중간 크기
  • 192.168.0.0/16 — 소규모 환경, 가정용 공유기와 겹칠 수 있어 주의

실무 권장 패턴 — VPC 간 CIDR이 겹치지 않도록:

서비스 A: 10.0.0.0/16  (65,534 IP)
서비스 B: 10.1.0.0/16  (65,534 IP)
서비스 C: 10.2.0.0/16  (65,534 IP)

서브넷 설계 — Public/Private/Data 3계층:

VPC: 10.0.0.0/16

Public 서브넷 (인터넷 게이트웨이 직접 연결)
├── 10.0.1.0/24  (ap-northeast-2a) → ALB, NAT Gateway
└── 10.0.2.0/24  (ap-northeast-2b) → ALB, NAT Gateway

Private 서브넷 (WAS 계층)
├── 10.0.101.0/24 (ap-northeast-2a) → Spring Boot
└── 10.0.102.0/24 (ap-northeast-2b) → Spring Boot

Data 서브넷 (DB 계층)
├── 10.0.201.0/24 (ap-northeast-2a) → RDS, ElastiCache
└── 10.0.202.0/24 (ap-northeast-2b) → RDS, ElastiCache

Spring Boot를 Private 서브넷에 두는 이유는 단 하나 — 외부에서 WAS에 직접 접근하는 경로를 없애는 것이다.

AWS가 서브넷에서 예약하는 주소 5개:

/24(256개) 서브넷에서 AWS가 5개를 예약한다:

  • .0 — 네트워크 주소
  • .1 — VPC 라우터
  • .2 — AWS DNS 서버
  • .3 — 미래 사용 예약
  • .255 — 브로드캐스트

사용 가능한 IP = 256 - 5 = 251개. /28(16개) 서브넷은 예약 5개를 빼면 11개뿐이다. 작은 서브넷일수록 낭비 비율이 높다.


Private 서브넷이 인터넷에 나가려면 어떻게 하나? NAT가 왜 필요한가?

Private 서브넷의 Spring Boot는 외부 API를 호출하거나 Maven 저장소에서 패키지를 받아야 할 때 인터넷이 필요하다.

사설 IP(10.0.101.10)로는 인터넷 라우팅이 되지 않는다. 외부에서는 사설 IP를 모른다.

일반 NAT/PAT가 뭔가 — 먼저 개념부터:

NAT(Network Address Translation)는 사설 IP를 공인 IP로 바꿔 인터넷에 내보내는 기술이다. RFC 1918 사설 대역(10.x, 172.16~31.x, 192.168.x)은 공인 인터넷에서 라우팅되지 않으므로, 외부로 나가는 패킷의 출발지 사설 IP를 공인 IP로 치환해야 한다. 핵심은 **PAT(Port Address Translation, NAT 오버로드)**다 — 출발지 IP뿐 아니라 포트까지 같이 바꿔서, 여러 내부 호스트가 공인 IP 하나를 공유한다. NAT 장비는 내부 IP:포트 ↔ 공인 IP:포트 변환 테이블을 유지하고, 응답이 돌아오면 이 테이블을 역참조해 원래 내부 호스트로 돌려준다. 가정용 공유기 뒤의 스마트폰·노트북·TV가 공인 IP 하나로 동시에 인터넷을 쓰는 것이 정확히 이 방식이다. 앞서 본 CIDR/사설망 설계 위에서, 사설 대역의 내부 호스트들이 외부와 통신할 수 있게 해주는 다리가 NAT다.

NAT Gateway가 중간에서 주소를 변환한다:

Spring Boot (10.0.101.10)
  → NAT Gateway (Elastic IP: 52.x.x.x)  ← 주소 변환
  → 외부 API 서버

외부 API 서버: 52.x.x.x에서 요청이 왔다고 본다
NAT Gateway: 52.x.x.x:포트 ↔ 10.0.101.10:포트 테이블 관리
응답이 오면 원래 IP로 돌려줌

외부 API의 IP 화이트리스트에는 Elastic IP(52.x.x.x)를 등록한다. 이게 Elastic IP를 고정으로 쓰는 이유다.

NAT Gateway 비용 주의:

시간당 과금 + 데이터 처리량(GB)당 과금이다.

S3나 같은 리전 내 AWS 서비스로 대용량 데이터를 보낼 때 NAT Gateway를 거치면 비용이 폭발한다.

VPC Endpoint로 NAT Gateway를 우회하라:

NAT Gateway 우회 대상:
- S3: Gateway Endpoint (무료)
- DynamoDB: Gateway Endpoint (무료)
- CloudWatch, SQS, SNS, ECR: Interface Endpoint (시간당 과금, 데이터 비용 없음)

S3로 대용량 파일을 자주 보내는 서비스라면 Gateway Endpoint 설정 하나로 NAT Gateway 비용을 크게 줄일 수 있다.


VPC Peering을 할 때 CIDR이 겹치면 어떻게 되나?

VPC A(10.0.0.0/16)와 VPC B(10.0.0.0/16)를 Peering으로 연결하려 하면 — 실패한다.

AWS가 명시적으로 거부한다. 겹치는 CIDR끼리는 VPC Peering, Transit Gateway 연결 모두 안 된다.

왜 안 되는가?

라우팅 테이블 때문이다. VPC A 내부 라우터가 10.0.1.5로 패킷을 보낼 때 이게 VPC A의 서브넷인지 VPC B로 가야 하는지 구분할 수 없다. 모호성을 라우팅 프로토콜이 해결할 방법이 없다.

온프레미스-AWS 연결 시 같은 문제:

VPN이나 Direct Connect로 회사 사무실 네트워크와 VPC를 연결할 때도 마찬가지다. 회사 사내 네트워크가 192.168.0.0/16이고 VPC도 192.168.0.0/16이라면 연결 불가능이다. 사내 네트워크가 192.168.x.x 대역인 경우가 많으니 VPC는 10.x.x.x 대역을 쓰는 게 안전하다.

Longest Prefix Match — 겹치지 않지만 포함 관계인 경우:

라우팅 테이블:
10.0.0.0/8  → Transit Gateway (넓은 범위)
10.0.1.0/24 → local (좁은 범위)

패킷이 10.0.1.5로 가면 /24(더 구체적)를 선택한다. 더 긴(구체적인) 프리픽스가 우선순위가 높다. 라우터는 항상 가장 구체적인 경로를 선택한다.


IPv6는 왜 /64가 기본 단위인가?

IPv6는 128비트 주소다. 2^128 ≈ 3.4 × 10^38. 처음 이 숫자를 보면 황당하다. 지구의 모래알보다 많다.

IPv6 서브넷의 기본 단위는 /64다. 왜 하필 64인가?

SLAAC(Stateless Address Autoconfiguration) 때문이다.

IPv6 호스트는 DHCP 서버 없이 스스로 주소를 만들 수 있다.

  • 상위 64비트(네트워크 프리픽스): 라우터에서 받는다
  • 하위 64비트(인터페이스 식별자): 자신의 MAC 주소(EUI-64 형식)나 랜덤값으로 생성한다

이 자동 생성이 정확히 하위 64비트를 쓰기 때문에, 서브넷 크기를 /64로 맞춰야 SLAAC가 작동한다.

/64 서브넷 하나에 호스트 주소가 2^64 ≈ 18.4 × 10^18개다. 지구 전체 기기를 다 넣어도 하나의 서브넷에 들어간다.

AWS VPC에서 IPv6:

VPC IPv6 CIDR: /56 블록 (AWS에서 자동 할당)
서브넷 IPv6 CIDR: /64 (서브넷 하나당 고정)

현재 대부분의 AWS 서비스는 IPv4와 IPv6를 동시에 지원하는 듀얼 스택 방식을 권장한다.


운영 중 IP가 부족해지면 어떻게 하나?

경험해봤다.

서비스가 성장해서 EC2 인스턴스, RDS, ElastiCache, ENI(Elastic Network Interface), Lambda VPC 연결이 쌓이다 보면 서브넷 IP가 바닥난다.

AWS 서브넷은 크기를 변경할 수 없다. 한번 만든 서브넷의 CIDR은 고정이다.

선택지:

방법 1 — VPC Secondary CIDR 추가

기존 VPC CIDR: 10.0.0.0/16
Secondary CIDR 추가: 10.1.0.0/16

Secondary CIDR로 새 서브넷을 만들어 사용한다. 라우팅 테이블이 복잡해진다. 임시방편이다.

방법 2 — 인스턴스를 새 서브넷으로 이전

새 서브넷을 만들고 인스턴스를 순차적으로 옮긴다. 무중단으로 하려면 Blue-Green 방식이 필요하다. 공수가 가장 크지만 가장 깔끔하다.

방법 3 — 처음부터 넉넉하게 (근본적 해결)

VPC /16, 서브넷 /20(4,096개) 이상으로 넉넉하게 시작한다. 사설 IP는 사용에 비용이 들지 않는다. IP를 아낀다고 이득이 없다. 나중에 부족해서 고통받는 것보다 처음에 크게 잡는 게 낫다.

처음부터 설계를 제대로 해야 하는 이유가 바로 이것이다.


최적화

서브넷 크기 선택 원칙:

  • VPC: /16 이상 (절대 /24로 VPC를 만들지 말 것)
  • WAS 서브넷: /20/22 (1,0244,096 IP)
  • DB 서브넷: /24~/26 (많은 인스턴스 불필요)
  • ALB/NAT 서브넷: /24 (8개 이상 권장)

NAT Gateway 비용 최적화:

  • S3, DynamoDB → Gateway Endpoint 사용 (무료)
  • ECR(컨테이너 이미지 풀) → Interface Endpoint 사용
  • 같은 AZ 내 트래픽은 NAT Gateway를 같은 AZ에 배치해 Inter-AZ 전송 비용 절감

튜닝

VPC DNS 설정:

enableDnsSupport: true   # VPC 내부 DNS 활성화 (기본값)
enableDnsHostnames: true # 퍼블릭 IP에 DNS 호스트명 부여

Private Hosted Zone을 사용하는 경우 enableDnsSupport가 반드시 켜져 있어야 한다. Spring Boot에서 spring.datasource.url에 RDS 엔드포인트(DNS명)을 사용하는데, 이 설정이 꺼져 있으면 DNS 조회가 실패한다.

서브넷 라우팅 테이블 분리:

Public 서브넷과 Private 서브넷은 반드시 별도 라우팅 테이블을 써야 한다. 기본 라우팅 테이블(main route table)에 IGW 경로를 추가하면 Private 서브넷도 자동으로 Public이 되어버린다. 이 실수가 보안 사고로 이어진다.

Public 서브넷 Route Table:
  10.0.0.0/16 → local
  0.0.0.0/0   → Internet Gateway

Private 서브넷 Route Table:
  10.0.0.0/16 → local
  0.0.0.0/0   → NAT Gateway

운영상 주의사항

CIDR 겹침 사전 검증

새 VPC를 만들거나 Peering을 추가하기 전에 기존 CIDR 목록을 확인한다. 여러 계정, 리전에 걸쳐 관리하면 엑셀 시트나 IP 관리 도구(phpIPAM, NetBox)가 필요하다.

Security Group은 CIDR보다 Security Group 참조를 선호하라

WAS Security Group에서 DB 인바운드를 10.0.101.0/24(서브넷 CIDR)로 열면, 그 서브넷에 추가되는 모든 인스턴스가 DB에 접근 가능해진다. WAS Security Group ID를 직접 참조하면 해당 Security Group이 붙은 인스턴스만 접근 가능해 더 정밀하다.

Lambda VPC 배치의 IP 소비

Lambda가 VPC 내에서 실행될 때 ENI를 생성하면서 IP를 소비한다. Lambda 동시 실행 수가 갑자기 늘어나면 서브넷 IP가 빠르게 고갈된다. Lambda 함수는 가능하면 넓은 서브넷에 배치하거나, VPC 외부에서 실행하고 VPC Endpoint로 AWS 서비스에 접근하는 방식을 고려한다.

RDS 다중 AZ 배포 시 서브넷 그룹

RDS 서브넷 그룹에는 최소 2개 AZ의 서브넷이 필요하다. 한 AZ만 가져가면 다중 AZ 옵션이 활성화되지 않는다. 처음부터 각 AZ별 DB 서브넷을 만들어두는 습관을 들여라.