🔥 Subnet: Public과 Private의 진짜 차이

1519자
18분

서울 리전(ap-northeast-2)을 덮는 큰 VPC 컨테이너가 부드러운 푸른 직사각형으로 그려져 있고, 그 안이 네 개의 색이 다른 슬라이스로 갈라져 각각의 가용영역(apne2-az1~az4)을 나타낸다. 각 슬라이스 위에 'Subnet'이라고 적힌 작은 둥근 사각형이 한두 개씩 떠 있어 Subnet이 AZ 단위로 잘리는 한 칸이라는 점을 보여 준다

처음 만든 Subnet에서 EC2가 30분 동안 인터넷에 닿지 않은 적이 있다. 이름은 친절하게 public-subnet으로 지어 두었고, 보안 그룹도 풀어 놓았으며, 인스턴스에는 공인 IP까지 붙어 있었다. 그런데도 패키지 매니저가 한 번도 응답하지 않았다. 한참을 찾고 나서야 라우팅 테이블에 0.0.0.0/0 행이 없다는 걸 알아챘다. 그날 나는 이름이나 태그가 아니라 라우팅 테이블의 한 줄이 Public과 Private를 결정한다는 사실을 30분 만에 배웠다. 이제 그 줄이 들어가는 위치와 Subnet, CIDR 산수, 두 토글이 서로 어떻게 맞물리는지 차례로 설명한다.

Subnet은 'AZ 한 칸'이라는 단위다

VPC는 리전 전체를 덮는 가상 네트워크의 경계고, 그 안에 들어 있는 Subnet은 한 가용영역(AZ)에 묶여 있는 IP 대역의 한 칸이다. 한 Subnet이 두 AZ에 걸칠 수 없다. 같은 VPC 안에 여러 Subnet을 두고, 각 Subnet을 서로 다른 AZ에 흩뿌려서 AZ 장애로부터 격리되는 단위를 만드는 것이 Subnet의 첫 번째 존재 이유다.

서울 리전(ap-northeast-2)에는 네 개의 AZ가 있고, 그래서 가용성을 진지하게 생각하는 워크로드는 적어도 두 AZ, 보통 세 AZ에 Subnet을 깔아 둔다. 한 AZ가 정전이 나도 다른 AZ의 Subnet에 떠 있는 인스턴스가 트래픽을 받아 줄 수 있게 하는 물리적 격벽을 IP 레벨에서 가져가는 셈이다.

ec2:CreateSubnet API 시그니처만 봐도 Subnet이 어떤 단위인지 바로 드러난다. 공식 레퍼런스는 VpcId만 Required로 표시하지만, 실제 요청은 IPv4 CidrBlock(또는 IPAM·IPv6 대응 필드)과 AvailabilityZone(혹은 AvailabilityZoneId)을 함께 받아야 한다(verified 2026-04-25). 즉 Subnet을 만들 때는 어느 VPC에 둘지와 어느 AZ에 둘지를 요청 안에 함께 적는다. 한 번 정한 AZ나 CIDR은 사실상 바꿀 수 없고, 다르게 가져가고 싶으면 새 Subnet을 만들고 자원을 옮겨야 한다.

CIDR 분할: VPC를 더 작은 칸으로 자르는 법

VPC가 10.0.0.0/16이면 65,536개의 사설 IP가 그 안에 산다. Subnet은 그 중 일부만 가져간다. 보통 /24로 자르면 256개, /20이면 4,096개. 작게는 /28까지 자를 수 있고(IP 16개), 크게는 VPC와 같은 /16까지 가능하다(verified 2026-04-25, aws ec2 create-subnet 공식 API 레퍼런스 기준).

산수보다 한 발 먼저 알아 둘 것이 있다. AWS가 띄우는 표준 IPv4 Subnet에서는 IP 주소 다섯 개가 예약돼 있다. 10.0.0.0/16 VPC 안의 /24 Subnet 10.0.1.0/24을 예로 들면(verified 2026-04-25, Subnet sizing 가이드):

  • 10.0.1.0: 네트워크 주소
  • 10.0.1.1: VPC 라우터용 예약
  • 10.0.1.2: DNS 매핑용 예약 (실제 Amazon Provided DNS의 IP는 VPC CIDR의 base+2, 즉 이 예에서는 10.0.0.2)
  • 10.0.1.3: 미래 사용을 위한 예약
  • 10.0.1.255: 브로드캐스트 주소(VPC는 브로드캐스트를 지원하지 않지만 그래도 예약)

직접 IP를 가져오는 BYOIP 같은 일부 케이스에서는 이 다섯 개의 예약 규칙이 일부 다르게 적용되지만, 콘솔이 만들어 주는 표준 Subnet은 모두 이 다섯 개를 비워 둔다고 보면 된다. 그래서 /24는 256개 같지만 실제로 인스턴스가 받아 갈 수 있는 IP는 251개고, /28처럼 작은 Subnet이면 이 다섯 개가 16개 중 31%를 차지한다. 보안상 일부러 작게 자른 관리용 Subnet에서 "왜 인스턴스를 11개째에서 더 못 띄우지" 하고 한참을 헤맨 적이 있다. 이 다섯 개 때문이었다.

10.0.0.0/16 VPC를 10.0.1.0/24와 10.0.2.0/24 같은 작은 Subnet으로 잘랐을 때, 한 Subnet 256개의 IP 중 첫 네 개와 마지막 한 개가 회색으로 칠해져 'reserved by AWS'라는 라벨이 붙고 나머지 251개가 옅은 푸른 점으로 그려져 사용 가능 IP를 시각화한 그림. 다섯 개 예약 IP의 위치를 한 장으로 보여 주는 다이어그램이다

CIDR을 어떻게 나눌지는 나중에 고치기 어려운 결정이라 처음에 한 번 그릴 때 의도가 들어가야 한다. 한 VPC당 Subnet은 기본 200개까지 만들 수 있고, 필요하면 Service Quotas로 더 늘릴 수 있다. 라우팅 테이블도 같은 한도가 따로 있어서 먼저 바닥나는 건 보통 200이라는 숫자보다 IP 풀이다. /16을 잡으면 계산이 단순하지만, /20이나 /22처럼 작게 시작하면 Subnet을 더 나눌 때 직접 다시 계산할 일이 빠르게 늘어난다.

Public과 Private을 가르는 것은 '이름'이 아니라 '라우팅'이다

자주 보는 오해 한 줄이 있다. "Subnet 이름을 public-subnet이라고 지으면 public Subnet이다." 첫 줄에 적은 30분짜리 사고가 정확히 이 오해에서 출발한다. AWS 공식 문서는 Subnet 종류를 라우팅 설정으로 정한다고 분명히 적는다(verified 2026-04-25, Configure subnets 가이드). 이름도 태그도 색깔도 아니다. 라우팅 테이블이 0.0.0.0/0을 어디로 보내는지가 Public과 Private를 나눈다.

  • 어떤 Subnet의 라우팅 테이블에 0.0.0.0/0 → igw-xxxx (Internet Gateway) 항목이 있으면 그 Subnet은 public이다.
  • 0.0.0.0/0이 IGW가 아닌 곳(예: NAT Gateway nat-xxxx)으로 빠지거나 그 항목 자체가 없으면 그 Subnet은 private이다. 외부로 나가는 길이 아예 없는 형태를 AWS는 isolated라고 따로 부르기도 하는데, 운영 현장에서는 통째로 'private'이라고 묶어 부른다.

Subnet이 public인지 private인지는 Subnet 자체 속성이 아니라 연결된 라우팅 테이블의 한 줄이 정한다. 연결 글 라우팅 테이블: 패킷이 어디로 가는지에서는 그 줄이 실제로 어떻게 동작하는지 단계별로 따라간다.

두 개의 Subnet 박스가 나란히 놓여 있고 위에는 각각 라우팅 테이블 카드가 떠 있다. 왼쪽은 0.0.0.0/0이 Internet Gateway로 향하면서 'Public Subnet'이라는 초록색 라벨을, 오른쪽은 0.0.0.0/0이 NAT Gateway로 향하면서 'Private Subnet'이라는 주황색 라벨을 받는다. 아래에 '라우팅이 결정한다, 이름이 아니다'라고 적혀 있어 이름이 아니라 라우팅이 Public/Private을 결정한다는 사실을 한 장으로 시각화한 그림

인스턴스에 공인 IP가 자동으로 붙는지는 또 다른 토글이다

두 번째 함정은 라우팅과 분리되어 있다. Public Subnet에 인스턴스를 띄우면 자동으로 공인 IPv4가 붙는가는, 위의 라우팅 결정과는 별개의 토글이다. Subnet에는 MapPublicIpOnLaunch라는 속성이 있고, 이게 true면 그 Subnet에서 새로 띄우는 ENI(네트워크 인터페이스)에 자동으로 공인 IPv4 주소를 붙여 준다(verified 2026-04-25).

지난 편에서 한 줄로 짚고 넘어갔던 default subnet의 "기본적으로 public이고 자동 공인 IPv4를 부여한다"는 사실은 두 개의 설정이 같이 켜져 있다는 뜻이다.

  1. 라우팅 테이블에 0.0.0.0/0 → IGW가 있다 (이게 public의 정의).
  2. MapPublicIpOnLaunch = true (이게 공인 IP 자동 부여).

둘을 늘 함께 켤 필요는 없다. 라우팅은 IGW로 보내되 자동 공인 IP 부여는 끄고, 필요할 때만 인스턴스에 EIP(Elastic IP)를 붙이는 운영 패턴이 흔하다. 이렇게 두면 코드 대신 IP 할당만 바꿔서 어느 인스턴스를 인터넷에 노출할지 결정할 수 있다.

3-AZ 표준 패턴: 머릿속 구성도 한 장

많은 AWS 입문 자료는 AZ 세 곳에 Public/Private Subnet을 한 쌍씩 배치해 여섯 개 Subnet으로 설명을 시작한다.

  • ap-northeast-2a: Public /24 + Private /24
  • ap-northeast-2b: Public /24 + Private /24
  • ap-northeast-2c: Public /24 + Private /24

Internet Gateway: 외부로 나가는 유일한 길은 위쪽에 한 개 둔다. 외부에서 오는 트래픽은 ALB(Application Load Balancer) 같은 로드밸런서가 세 AZ의 Public Subnet에 한 발씩 걸쳐 받아 내고, 실제 EC2와 RDS는 Private Subnet에 두어서 직접 인터넷에서는 보이지 않게 만든다. Private Subnet이 외부로 패치를 받아 와야 할 때는 NAT Gateway: Private Subnet의 외부 연결을 한 AZ당 한 개씩(또는 비용을 줄이려고 한 개만) 둔다.

세 개의 가용영역(apne2-az1, az2, az3)에 각각 Public Subnet과 Private Subnet 한 쌍씩이 배치된 표준 3-AZ 토폴로지 다이어그램. 위쪽에는 Internet Gateway가 한 개, 그 아래 ALB가 세 Public Subnet에 발을 걸치고 있다. Private Subnet 안에는 EC2와 RDS가 보이며, az1의 Public Subnet에 놓인 NAT Gateway가 세 Private Subnet의 외부 트래픽을 중계한다. 모던 플랫 스타일로 정리된 AWS 표준 아키텍처 그림이다

이 구성에서는 Security Group: 인스턴스 레벨 방화벽Network ACL: Subnet 레벨 방화벽과의 차이이 각각 인스턴스와 Subnet 경계에서 트래픽을 통제하고, 라우팅 테이블이 패킷의 목적지를 정한다.

'관리형'이라는 단어가 감추는 비용: Public을 남발하지 않는 이유

모든 자원을 Public Subnet에 두면 설계 자체는 가장 단순하다. 라우팅 테이블 한 장에 0.0.0.0/0 → IGW 한 줄을 두고, 인스턴스마다 공인 IPv4(또는 EIP)를 한 개씩 붙이면 모든 인스턴스가 인터넷에 닿는다. 이 구성에서는 NAT Gateway도 따로 두지 않는다. 정확히는 공인 IP가 없는 인스턴스는 Public Subnet에 두더라도 인터넷에 못 나가므로, 이 설계는 "모두에게 공인 IP를 부여한다"는 결정과 한 묶음이다.

문제는 공격 표면과 사고의 폭발 반경이다. Public Subnet에 둔 EC2의 Security Group: 인스턴스 레벨 방화벽 한 줄이 잘못 풀어지면, 22번 포트가 0.0.0.0/0에 열린 채로 그 인스턴스는 전 세계에서 시도하는 자동화 스캔의 표적이 된다. Private Subnet에 두면, 보안 그룹의 사고가 나도 그 자원에 외부에서 직접 닿을 수 있는 길 자체가 없다. 폭발 반경이 작아진다.

이 선택에는 비용도 바로 붙는다. NAT Gateway는 시간당 요금과 처리한 트래픽 GB당 요금을 함께 청구한다(verified 2026-04-25, VPC 가격 페이지). 모든 자원을 Private Subnet에 두면 구조는 단정해지지만, 패키지 매니저가 외부에서 패치를 가져오는 트래픽까지 NAT를 지나면서 AWS가 청구서 항목을 빠르게 추가한다. 한쪽 끝이 정답이 아니라, 외부에 보일 필요가 있는 자원만 Public으로 두고 나머지는 모두 Private에 묶는 단순한 규칙이 답이다. 이 규칙을 따르려면 처음 그릴 때 Subnet을 의도적으로 둘로 나눠 두어야 한다. 이름이 아니라 라우팅이 정의한다는 사실은, 처음 그릴 때 그 라우팅을 직접 그려야 한다는 뜻이기도 하다.

언제 Subnet 분리를 얕게만 하는가

Subnet은 사실상 빼고 설계할 수 없다. VPC 안에 자원을 두려면 반드시 어떤 Subnet 안에 넣어야 한다. 그래서 질문은 '쓸지 말지'가 아니라 'Public/Private 분리를 어디까지 둘지'다.

학습용 계정이라면, 30분짜리 데모라면, 한두 인스턴스로 끝나는 일회성 PoC라면, default VPC의 default subnet 하나에 그냥 띄워도 된다. default VPC가 미리 만들어 둔 환경의 정확한 용도다.

운영에 한 번이라도 닿을 가능성이 있다면 처음부터 두 갈래로 자르는 쪽이 안전하다. 한 AZ에 Public/Private 한 쌍을 두고, 가능하면 두 번째 AZ에도 같은 한 쌍을 둔다. 처음에 4분만 더 써 두면 나중에 네트워크 구성을 다시 바꾸는 비용을 줄일 수 있다. VPC끼리 CIDR이 겹치면 VPC Peering과 Transit Gateway: VPC를 잇는 두 방법을 연결할 때 라우트가 막히므로, 처음부터 겹치지 않는 대역을 좁게 잡아 두는 편이 충돌 위험을 미리 막는다.

다음 편 예고

Public과 Private를 나누는 기준은 결국 라우팅 테이블의 한 줄에 있다. 라우팅 테이블: 패킷이 어디로 가는지에서는 그 줄을 실제 예시와 함께 따라간다. 기본 라우트와 명시 라우트가 어떻게 우선순위를 다투는지, 한 라우팅 테이블이 여러 Subnet에 붙을 수 있다는 사실이 어떻게 Public Subnet끼리 한 장의 라우팅 테이블을 공유한다는 흔한 패턴으로 이어지는지를 짚는다.

참고 자료

YouTube 영상

채널 보기
트라이(Trie)를 이용한 자동 완성 알고리즘 | Trie 자료구조 이야기
투영과 예측, 그리고 선형 결합 | 선형대수학
트라이(Trie)에서 단어를 삭제하는 방법 | Trie 자료구조 이야기
스칼라 곱셈과 내적의 기하학적 의미 | 선형대수학
AI는 왜 수백 차원의 벡터를 사용할까? 고차원 공간과 행렬 | 선형대수학
직교성과 벡터 투영 | 선형대수학
AI 추천 시스템의 원리, 벡터 사이의 각도와 코사인 유사도 | 선형대수학
벡터의 정의와 덧셈 연산 | 선형대수학