🌍 네트워크

Subnet

서브넷

IP 네트워크의 논리적 분할. CIDR로 표기. VPC 구성 요소.

📖 상세 설명

Subnet(서브넷)은 하나의 큰 IP 네트워크를 더 작은 논리적 네트워크 세그먼트로 분할하는 기술입니다. 예를 들어, 회사에서 10.0.0.0/16 네트워크를 할당받았다면, 이를 10.0.1.0/24(개발팀), 10.0.2.0/24(운영팀), 10.0.3.0/24(DB서버)처럼 분리하여 네트워크 관리 효율성과 보안성을 높일 수 있습니다.

서브넷은 CIDR(Classless Inter-Domain Routing) 표기법으로 표현합니다. 10.0.1.0/24에서 /24는 서브넷 마스크를 의미하며, 앞의 24비트가 네트워크 주소, 나머지 8비트(2^8 = 256개, 실제 사용 가능 254개)가 호스트 주소입니다. /24는 255.255.255.0과 동일하며, /16은 65,536개, /28은 16개의 IP를 할당받습니다.

클라우드 환경에서 서브넷은 VPC(Virtual Private Cloud)의 핵심 구성 요소입니다. AWS, GCP, Azure 모두 Public Subnet(인터넷 직접 연결)과 Private Subnet(내부 통신 전용)을 구분합니다. 웹 서버는 Public Subnet에, 데이터베이스는 Private Subnet에 배치하여 보안을 강화합니다. Private Subnet의 아웃바운드 인터넷 접근은 NAT Gateway를 통해 처리합니다.

고가용성(HA) 아키텍처에서는 최소 2개 이상의 가용영역(AZ)에 서브넷을 분산 배치합니다. 예를 들어, ap-northeast-2a와 ap-northeast-2c에 각각 Public/Private Subnet을 만들어 총 4개의 서브넷을 구성합니다. 이렇게 하면 한 AZ에 장애가 발생해도 서비스가 중단되지 않습니다. CIDR 블록은 향후 확장을 고려해 /24보다 여유 있는 /20 ~ /22 크기로 설계하는 것이 권장됩니다.

💻 코드 예제

# AWS VPC와 서브넷 구성 (Terraform)
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = { Name = "production-vpc" }
}

# Public Subnet (AZ-a) - 웹 서버, ALB 배치
resource "aws_subnet" "public_a" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.1.0/24"    # 254개 IP
  availability_zone       = "ap-northeast-2a"
  map_public_ip_on_launch = true

  tags = { Name = "public-subnet-a", Type = "public" }
}

# Public Subnet (AZ-c) - HA 구성용
resource "aws_subnet" "public_c" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.2.0/24"
  availability_zone       = "ap-northeast-2c"
  map_public_ip_on_launch = true

  tags = { Name = "public-subnet-c", Type = "public" }
}

# Private Subnet (AZ-a) - DB, 애플리케이션 서버
resource "aws_subnet" "private_a" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.10.0/24"
  availability_zone = "ap-northeast-2a"

  tags = { Name = "private-subnet-a", Type = "private" }
}

# Private Subnet (AZ-c) - HA 구성용
resource "aws_subnet" "private_c" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.11.0/24"
  availability_zone = "ap-northeast-2c"

  tags = { Name = "private-subnet-c", Type = "private" }
}

# Internet Gateway - Public Subnet용
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
  tags   = { Name = "main-igw" }
}

# NAT Gateway - Private Subnet 아웃바운드용
resource "aws_eip" "nat" {
  domain = "vpc"
}

resource "aws_nat_gateway" "main" {
  allocation_id = aws_eip.nat.id
  subnet_id     = aws_subnet.public_a.id  # Public Subnet에 배치

  tags = { Name = "main-nat-gateway" }
}
import boto3
import ipaddress

# CIDR 계산 유틸리티
def calculate_cidr(cidr_block):
    """CIDR 블록 정보 계산"""
    network = ipaddress.ip_network(cidr_block, strict=False)

    print(f"네트워크 주소: {network.network_address}")
    print(f"브로드캐스트: {network.broadcast_address}")
    print(f"서브넷 마스크: {network.netmask}")
    print(f"총 IP 수: {network.num_addresses}")
    print(f"사용 가능 IP: {network.num_addresses - 5}")  # AWS는 5개 예약
    print(f"IP 범위: {list(network.hosts())[0]} ~ {list(network.hosts())[-1]}")

    return network

# 예제: /24 블록 분석
calculate_cidr("10.0.1.0/24")
# 네트워크 주소: 10.0.1.0
# 총 IP 수: 256
# 사용 가능 IP: 251 (AWS는 5개 예약: .0, .1, .2, .3, .255)

# boto3로 VPC 서브넷 생성
ec2 = boto3.client('ec2', region_name='ap-northeast-2')

# VPC 생성
vpc_response = ec2.create_vpc(
    CidrBlock='10.0.0.0/16',
    TagSpecifications=[{
        'ResourceType': 'vpc',
        'Tags': [{'Key': 'Name', 'Value': 'production-vpc'}]
    }]
)
vpc_id = vpc_response['Vpc']['VpcId']

# Public Subnet 생성
public_subnet = ec2.create_subnet(
    VpcId=vpc_id,
    CidrBlock='10.0.1.0/24',
    AvailabilityZone='ap-northeast-2a',
    TagSpecifications=[{
        'ResourceType': 'subnet',
        'Tags': [
            {'Key': 'Name', 'Value': 'public-subnet-a'},
            {'Key': 'Type', 'Value': 'public'}
        ]
    }]
)

# Private Subnet 생성
private_subnet = ec2.create_subnet(
    VpcId=vpc_id,
    CidrBlock='10.0.10.0/24',
    AvailabilityZone='ap-northeast-2a',
    TagSpecifications=[{
        'ResourceType': 'subnet',
        'Tags': [
            {'Key': 'Name', 'Value': 'private-subnet-a'},
            {'Key': 'Type', 'Value': 'private'}
        ]
    }]
)

print(f"Public Subnet ID: {public_subnet['Subnet']['SubnetId']}")
print(f"Private Subnet ID: {private_subnet['Subnet']['SubnetId']}")
# AWS CLI로 VPC 서브넷 관리

# VPC 생성
aws ec2 create-vpc \
    --cidr-block 10.0.0.0/16 \
    --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=production-vpc}]'

# VPC ID 확인
VPC_ID=$(aws ec2 describe-vpcs \
    --filters "Name=tag:Name,Values=production-vpc" \
    --query 'Vpcs[0].VpcId' --output text)

# Public Subnet 생성 (AZ-a)
aws ec2 create-subnet \
    --vpc-id $VPC_ID \
    --cidr-block 10.0.1.0/24 \
    --availability-zone ap-northeast-2a \
    --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=public-subnet-a},{Key=Type,Value=public}]'

# Private Subnet 생성 (AZ-a)
aws ec2 create-subnet \
    --vpc-id $VPC_ID \
    --cidr-block 10.0.10.0/24 \
    --availability-zone ap-northeast-2a \
    --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=private-subnet-a},{Key=Type,Value=private}]'

# 서브넷 목록 조회
aws ec2 describe-subnets \
    --filters "Name=vpc-id,Values=$VPC_ID" \
    --query 'Subnets[*].{ID:SubnetId,CIDR:CidrBlock,AZ:AvailabilityZone,Public:MapPublicIpOnLaunch}' \
    --output table

# Internet Gateway 생성 및 연결
IGW_ID=$(aws ec2 create-internet-gateway \
    --query 'InternetGateway.InternetGatewayId' --output text)
aws ec2 attach-internet-gateway --vpc-id $VPC_ID --internet-gateway-id $IGW_ID

# CIDR 계산 (Linux ipcalc)
ipcalc 10.0.1.0/24
# Network:   10.0.1.0/24
# Netmask:   255.255.255.0
# HostMin:   10.0.1.1
# HostMax:   10.0.1.254
# Hosts/Net: 254

# 서브넷 내 사용 가능 IP 수 확인
aws ec2 describe-subnets \
    --subnet-ids subnet-xxxxx \
    --query 'Subnets[0].AvailableIpAddressCount'

🗣️ 실무에서 이렇게 말하세요

💬 VPC 설계 회의에서
"프로덕션 VPC는 10.0.0.0/16으로 잡고, Public Subnet은 /24로 2개 AZ에 배치합시다. Private Subnet은 RDS Multi-AZ 때문에 최소 2개 필요하고, EKS 노드용으로 /20 크기 서브넷을 별도로 만들어야 Pod IP가 부족하지 않습니다. 총 6개 서브넷 구성이 적절해 보입니다."
💬 보안 리뷰 미팅에서
"DB 서버가 Public Subnet에 있으면 안 됩니다. Private Subnet으로 이동하고, 웹 서버에서만 3306 포트 접근 가능하도록 Security Group을 설정해야 합니다. 외부 API 호출이 필요하면 NAT Gateway를 통해 나가도록 라우팅 테이블을 수정하겠습니다."
💬 트러블슈팅 상황에서
"EC2에서 인터넷 연결이 안 된다고요? 먼저 서브넷이 Public인지 Private인지 확인해주세요. Private Subnet이라면 NAT Gateway가 있는지, 라우트 테이블에 0.0.0.0/0 → NAT 경로가 있는지 체크해야 합니다. Public이면 IGW 연결과 퍼블릭 IP 할당 여부를 확인하세요."

⚠️ 흔한 실수 & 주의사항

❌
CIDR 블록을 너무 작게 설정

/28(11개 IP)로 만들었다가 서비스 확장 시 IP가 부족합니다. 서브넷 CIDR은 나중에 변경 불가하므로 처음부터 /24 이상으로 여유 있게 잡으세요. EKS 사용 시 /20 권장입니다.

❌
단일 AZ에만 서브넷 배치

ap-northeast-2a에만 서브넷을 만들면, 해당 AZ 장애 시 전체 서비스가 중단됩니다. RDS Multi-AZ, ALB는 최소 2개 AZ의 서브넷이 필수입니다. 항상 짝수 개로 구성하세요.

❌
Public/Private 구분 없이 모든 리소스를 Public에 배치

데이터베이스, 백엔드 서버를 Public Subnet에 두면 외부 공격에 노출됩니다. 인터넷 직접 연결이 필요한 ALB, Bastion만 Public에 두고, 나머지는 반드시 Private Subnet에 배치하세요.

✅
올바른 서브넷 설계 패턴

VPC /16 → Public /24 x 2 AZ + Private /24 x 2 AZ 구성이 표준입니다. CIDR 블록은 겹치지 않게 계획하고(VPC Peering 대비), 용도별로 태그를 명확히 붙이세요. 서브넷당 AWS 예약 IP 5개를 감안한 용량 산정이 필요합니다.

🔗 관련 용어

📚 더 배우기