서브넷과 CIDR
클래스 기반 주소 체계의 가장 큰 문제는 유연성이 없다는 것이었습니다. 호스트가 300대 필요한 조직에 클래스 C(254대)를 주면 부족하고, 클래스 B(65,534대)를 주면 65,000개 이상의 주소가 낭비됩니다.
이 문제를 해결하기 위해 등장한 것이 서브넷(Subnet)과 CIDR(Classless Inter-Domain Routing)입니다.
서브넷 마스크의 역할
서브넷 마스크(Subnet Mask)는 IP 주소에서 어디까지가 네트워크부이고 어디서부터가 호스트부인지를 알려주는 값입니다.
IP 주소와 서브넷 마스크를 비트 단위로 AND 연산하면 네트워크 주소가 나옵니다. 반대로 마스크에서 0으로 남은 비트가 호스트부이며, 이 범위 안에서 호스트 주소와 브로드캐스트 주소가 결정됩니다.
CIDR 표기법
CIDR은 클래스에 얽매이지 않고, 네트워크부의 비트 수를 자유롭게 지정하는 방식입니다.
192.168.1.0/24는 앞 24비트가 네트워크부라는 뜻입니다. 서브넷 마스크 255.255.255.0과 같은 의미이지만, 표기가 훨씬 간결합니다.
| CIDR | 서브넷 마스크 | 호스트부 비트 | 사용 가능 호스트 | 용도 예시 |
|---|---|---|---|---|
| /8 | 255.0.0.0 | 24 | 16,777,214 | 대형 ISP |
| /16 | 255.255.0.0 | 16 | 65,534 | 기업 VPC |
| /20 | 255.255.240.0 | 12 | 4,094 | 대규모 서브넷 |
| /24 | 255.255.255.0 | 8 | 254 | 일반 LAN |
| /25 | 255.255.255.128 | 7 | 126 | 중간 서브넷 |
| /26 | 255.255.255.192 | 6 | 62 | 소규모 서브넷 |
| /27 | 255.255.255.224 | 5 | 30 | 서버 팜 |
| /28 | 255.255.255.240 | 4 | 14 | 소규모 DMZ |
| /30 | 255.255.255.252 | 2 | 2 | 포인트투포인트 링크 |
| /31 | 255.255.255.254 | 1 | 2 | 포인트투포인트 링크 |
| /32 | 255.255.255.255 | 0 | 1 | 단일 호스트 |
일반적인 IPv4 브로드캐스트 서브넷에서 호스트 수에서 2를 빼는 이유는, 호스트부가 모두 0인 주소(네트워크 주소)와 모두 1인 주소(브로드캐스트 주소)는 호스트에 할당할 수 없기 때문입니다.
은 프리픽스 길이입니다. 다만 /31은 포인트투포인트 링크에서 두 주소를 모두 사용할 수 있고, /32는 단일 호스트 경로를 나타내므로 위 공식의 예외로 다룹니다.
서브넷 분할 계산
192.168.1.0/24 네트워크를 4개의 서브넷으로 나누어야 합니다.
4개의 서브넷이 필요하므로 , 즉 호스트부에서 2비트를 빌려 네트워크부를 확장합니다.
| 서브넷 | 네트워크 주소 | 호스트 범위 | 브로드캐스트 | 게이트웨이 (관례) |
|---|---|---|---|---|
| 1 | 192.168.1.0/26 | .1 ~ .62 | .63 | .1 |
| 2 | 192.168.1.64/26 | .65 ~ .126 | .127 | .65 |
| 3 | 192.168.1.128/26 | .129 ~ .190 | .191 | .129 |
| 4 | 192.168.1.192/26 | .193 ~ .254 | .255 | .193 |
"""서브넷 분할 계산기"""
import ipaddress
def subnet_divide(network_str, new_prefix):
"""네트워크를 지정된 프리픽스로 분할"""
network = ipaddress.ip_network(network_str, strict=False)
subnets = list(network.subnets(new_prefix=new_prefix))
print(f"원래 네트워크: {network}")
print(f"서브넷 마스크: {network.netmask}")
print(f"사용 가능 호스트: {len(list(network.hosts()))}\n")
print(f"분할 결과 ({len(subnets)}개 /{new_prefix} 서브넷):")
print(f"{'서브넷':<22} {'호스트 범위':<35} {'BC':<16} {'호스트수'}")
print("-" * 90)
for i, subnet in enumerate(subnets, 1):
hosts = list(subnet.hosts())
if hosts:
first, last = hosts[0], hosts[-1]
print(f"{str(subnet):<22} {str(first)} ~ {str(last):<20} "
f"{str(subnet.broadcast_address):<16} {len(hosts)}")
# 실습
subnet_divide("192.168.1.0/24", 26) # 4개 분할
print()
subnet_divide("10.0.0.0/16", 20) # 16개 분할
# 두 IP가 같은 서브넷에 있는지 확인
def same_subnet(ip1, ip2, prefix):
"""두 IP가 같은 서브넷에 있는지 확인"""
net = ipaddress.ip_network(f"{ip1}/{prefix}", strict=False)
return ipaddress.ip_address(ip2) in net
print(f"\n같은 서브넷? {same_subnet('192.168.1.100', '192.168.1.200', 24)}")
print(f"같은 서브넷? {same_subnet('192.168.1.100', '192.168.1.200', 25)}")슈퍼넷팅 (경로 집약)
서브넷 분할이 큰 네트워크를 작게 나누는 것이라면, 슈퍼넷팅(Supernetting)은 반대로 여러 네트워크를 하나로 합치는 것입니다.
경로 집약은 아무 네트워크나 합칠 수 있는 것이 아닙니다. 네트워크들이 연속된 주소 범위여야 하고, 합친 범위가 프리픽스 경계에 맞게 정렬되어 있어야 합니다. 또한 집약 대상 경로가 같은 방향으로 나가거나 같은 정책으로 처리될 수 있어야 합니다. 예를 들어 192.168.0.0/24부터 192.168.3.0/24까지 네 개의 연속된 네트워크는 192.168.0.0/22 하나로 표현할 수 있습니다.
클라우드 VPC 서브넷 설계
AWS VPC를 설계할 때 CIDR 계산이 직접적으로 필요합니다.
| 설계 원칙 | 이유 |
|---|---|
| VPC CIDR은 넉넉하게 선택 | 기본 CIDR 크기는 나중에 변경할 수 없음 |
| 보조 CIDR 확장 가능성 고려 | 필요 시 secondary IPv4 CIDR을 추가 가능 |
| AZ별로 균등 분할 | 가용성 확보 |
| Public/Private/DB 분리 | 보안 계층화 |
| 서브넷당 예약 IP 5개 고려 | AWS가 첫 4개와 마지막 1개 주소를 예약 |
| EKS/컨테이너 IP 예산 확보 | 노드와 Pod ENI/IP 소진 방지 |
AWS에서 IPv4 서브넷 크기는 /16부터 /28 범위 안에서 선택합니다. 각 서브넷에서는 첫 네 개 주소와 마지막 주소를 사용할 수 없으므로, 예를 들어 /24는 256개 주소가 있어도 실제 할당 가능한 주소는 251개입니다.
# Linux에서 서브넷 정보 확인
ip addr show eth0 | grep inet
# 네트워크 주소 계산 (ipcalc)
ipcalc 192.168.1.100/26
# 출력: Network, HostMin, HostMax, Broadcast, Hosts
# Docker 네트워크의 서브넷 확인
docker network inspect bridge | grep -A 5 IPAM다음 절에서는 사설 IP를 사용하는 장치들이 어떻게 인터넷과 통신하는지, NAT와 DHCP의 동작 원리를 살펴보겠습니다.