본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 5. · 12 Views
IP 주소 체계와 서브네팅 완벽 가이드
네트워크의 기본이 되는 IP 주소 체계와 서브네팅에 대해 알아봅니다. 클래스풀과 클래스리스 주소 체계부터 서브넷 마스크, CIDR, NAT, DHCP까지 실무에서 반드시 알아야 할 네트워크 개념을 쉽게 설명합니다.
목차
- 네트워크_주소와_호스트_주소
- 클래스풀_주소_체계
- 클래스리스_주소_체계
- 서브넷_마스크와_CIDR_표기법
- 서브네팅_계산_방법
- 공인_IP_vs_사설_IP와_NAT
- DHCP_동적_IP_할당
1. 네트워크 주소와 호스트 주소
김개발 씨는 회사에서 처음으로 서버 인프라 구축 프로젝트에 투입되었습니다. 선배가 건넨 네트워크 구성도에는 192.168.1.0/24라는 표기가 있었는데, 이게 대체 무슨 의미인지 감이 오지 않았습니다.
"이 숫자들이 어떻게 조합되어 네트워크가 구성되는 거지?"
IP 주소는 네트워크 주소와 호스트 주소 두 부분으로 구성됩니다. 마치 우편 주소에서 도시 이름과 상세 주소가 분리되어 있는 것과 같습니다.
네트워크 주소는 어떤 네트워크에 속하는지를, 호스트 주소는 그 네트워크 안에서 어떤 장치인지를 식별합니다.
다음 코드를 살펴봅시다.
# IP 주소 구조 분석 예제
ip_address = "192.168.1.100"
subnet_mask = "255.255.255.0"
# IP 주소를 이진수로 변환
ip_binary = '.'.join(format(int(x), '08b') for x in ip_address.split('.'))
mask_binary = '.'.join(format(int(x), '08b') for x in subnet_mask.split('.'))
# 네트워크 주소 계산 (IP AND 서브넷 마스크)
ip_parts = [int(x) for x in ip_address.split('.')]
mask_parts = [int(x) for x in subnet_mask.split('.')]
network = [ip_parts[i] & mask_parts[i] for i in range(4)]
print(f"IP 주소: {ip_address}")
print(f"서브넷 마스크: {subnet_mask}")
print(f"네트워크 주소: {'.'.join(map(str, network))}") # 192.168.1.0
print(f"호스트 부분: {ip_parts[3]}") # 100
김개발 씨는 입사 6개월 차 주니어 개발자입니다. 백엔드 개발만 하다가 처음으로 인프라 업무를 맡게 되었는데, 네트워크 설정 화면을 보니 머리가 복잡해졌습니다.
IP 주소, 서브넷 마스크, 게이트웨이... 이 숫자들은 대체 어떤 규칙으로 정해지는 걸까요?
선배 개발자 박시니어 씨가 화이트보드 앞으로 김개발 씨를 불렀습니다. "IP 주소를 이해하려면 먼저 그 구조를 알아야 해요.
아파트 주소를 떠올려 보세요." IP 주소는 마치 아파트 주소와 같습니다. "서울시 강남구 역삼동 OO아파트"가 네트워크 주소라면, "101동 1501호"가 호스트 주소입니다.
택배 기사가 먼저 아파트 단지를 찾고, 그 다음 동과 호수를 찾는 것처럼, 네트워크에서도 먼저 어떤 네트워크인지 확인하고 그 안에서 특정 장치를 찾습니다. IPv4 주소는 총 32비트로 구성되어 있습니다.
우리가 흔히 보는 192.168.1.100 같은 형태는 32비트를 8비트씩 4개로 나눠서 10진수로 표현한 것입니다. 각 8비트 덩어리를 **옥텟(Octet)**이라고 부릅니다.
이 32비트 중 앞부분은 네트워크 주소로, 뒷부분은 호스트 주소로 사용합니다. 문제는 어디까지가 네트워크 주소이고 어디부터가 호스트 주소인지를 구분하는 것인데, 여기서 서브넷 마스크가 등장합니다.
위 코드를 살펴보겠습니다. 서브넷 마스크 255.255.255.0을 이진수로 변환하면 11111111.11111111.11111111.00000000이 됩니다.
1인 부분은 네트워크 주소, 0인 부분은 호스트 주소를 나타냅니다. IP 주소와 서브넷 마스크를 AND 연산하면 네트워크 주소를 구할 수 있습니다.
192.168.1.100에서 앞의 세 옥텟 192.168.1이 네트워크 부분이고, 마지막 100이 호스트 부분이 되는 것입니다. 실무에서 이 개념이 왜 중요할까요?
서버가 다른 서버와 통신할 때, 먼저 상대방이 같은 네트워크에 있는지 확인합니다. 같은 네트워크라면 직접 통신하고, 다른 네트워크라면 라우터(게이트웨이)를 통해 통신합니다.
이 판단의 기준이 바로 네트워크 주소입니다. 흔히 하는 실수 중 하나는 호스트 주소를 0이나 255로 설정하는 것입니다.
호스트 부분이 모두 0인 주소는 네트워크 자체를 나타내고, 모두 1인 주소(255)는 브로드캐스트 주소로 예약되어 있습니다. 따라서 실제 장치에 할당할 수 없습니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 후 네트워크 구성도가 조금씩 눈에 들어오기 시작했습니다.
"아, 그래서 같은 대역의 IP를 써야 서로 통신이 되는 거군요!"
실전 팁
💡 - 호스트 주소 0은 네트워크 주소, 255는 브로드캐스트 주소로 예약되어 있어 장치에 할당할 수 없습니다
- 같은 네트워크에 있는 장치들끼리는 라우터 없이 직접 통신이 가능합니다
2. 클래스풀 주소 체계
박시니어 씨가 이어서 설명했습니다. "예전에는 IP 주소를 클래스로 나눠서 관리했어요.
지금은 잘 안 쓰지만, 네트워크 기초를 이해하려면 꼭 알아야 하는 개념이에요." 김개발 씨는 고개를 끄덕이며 필기 준비를 했습니다.
클래스풀(Classful) 주소 체계는 IP 주소를 A, B, C, D, E 다섯 개의 클래스로 나누는 방식입니다. 각 클래스는 네트워크 크기가 정해져 있어서 마치 옷 사이즈처럼 S, M, L로 구분한 것과 같습니다.
대규모 조직은 A 클래스, 중간 규모는 B 클래스, 소규모는 C 클래스를 사용했습니다.
다음 코드를 살펴봅시다.
# 클래스풀 IP 주소 클래스 판별
def get_ip_class(ip_address):
first_octet = int(ip_address.split('.')[0])
if 1 <= first_octet <= 126:
return "Class A", "255.0.0.0", 16777214 # 호스트 수
elif 128 <= first_octet <= 191:
return "Class B", "255.255.0.0", 65534
elif 192 <= first_octet <= 223:
return "Class C", "255.255.255.0", 254
elif 224 <= first_octet <= 239:
return "Class D", "멀티캐스트용", 0
else:
return "Class E", "실험용", 0
# 테스트
test_ips = ["10.0.0.1", "172.16.0.1", "192.168.1.1", "224.0.0.1"]
for ip in test_ips:
ip_class, mask, hosts = get_ip_class(ip)
print(f"{ip}: {ip_class}, 기본 마스크: {mask}, 최대 호스트: {hosts}")
인터넷 초창기에는 IP 주소 관리가 상대적으로 단순했습니다. 네트워크에 연결되는 장치 수가 많지 않았기 때문입니다.
당시 설계자들은 IP 주소를 효율적으로 분배하기 위해 클래스라는 개념을 도입했습니다. 클래스풀 주소 체계를 이해하려면 옷 사이즈를 떠올리면 됩니다.
백화점에서 옷을 살 때 S, M, L, XL 사이즈가 있듯이, IP 주소도 조직의 규모에 따라 적절한 클래스를 선택했습니다. Class A는 대기업용 특대 사이즈입니다.
첫 번째 옥텟이 1부터 126 사이인 주소가 여기에 해당합니다. 첫 번째 옥텟만 네트워크 주소로 사용하고 나머지 세 옥텟은 호스트 주소로 사용합니다.
무려 1,677만 개의 호스트를 수용할 수 있어서 IBM, AT&T 같은 거대 기업이 할당받았습니다. Class B는 중견 기업용입니다.
첫 번째 옥텟이 128부터 191 사이입니다. 앞의 두 옥텟이 네트워크 주소이고, 뒤의 두 옥텟이 호스트 주소입니다.
약 6만 5천 개의 호스트를 수용할 수 있습니다. Class C는 소규모 조직용입니다.
192부터 223 사이의 첫 번째 옥텟을 가집니다. 앞의 세 옥텟이 네트워크 주소이고, 마지막 옥텟만 호스트 주소입니다.
254개의 호스트만 수용할 수 있습니다. 위 코드에서 10.0.0.1은 Class A로 판별됩니다.
첫 번째 옥텟이 10이기 때문입니다. 반면 192.168.1.1은 Class C입니다.
우리가 가정이나 소규모 사무실에서 흔히 보는 192.168.x.x 대역이 바로 Class C에 해당합니다. 그런데 이 체계에는 심각한 문제가 있었습니다.
바로 주소 낭비입니다. 1000개의 호스트가 필요한 조직이 있다고 해봅시다.
Class C로는 254개밖에 안 되니 부족하고, Class B를 할당받으면 6만 5천 개 중 겨우 1000개만 사용하게 됩니다. 나머지 6만 4천 개는 그냥 버려지는 것입니다.
이런 비효율성 때문에 인터넷이 급성장하면서 IPv4 주소가 빠르게 고갈되기 시작했습니다. 그래서 등장한 것이 바로 클래스리스 주소 체계입니다.
127로 시작하는 주소는 특별합니다. 127.0.0.1은 루프백(Loopback) 주소로, 자기 자신을 가리킵니다.
개발할 때 localhost라고 부르는 바로 그 주소입니다.
실전 팁
💡 - 첫 번째 옥텟만 보면 클래스를 바로 알 수 있습니다: 1-126은 A, 128-191은 B, 192-223은 C
- 현재는 클래스풀 체계를 거의 사용하지 않지만, 네트워크 기초 이해와 자격증 시험에서 자주 출제됩니다
3. 클래스리스 주소 체계
"클래스풀의 문제점을 해결하기 위해 나온 게 클래스리스예요." 박시니어 씨의 설명이 이어졌습니다. 김개발 씨는 클래스 체계의 비효율성을 이해한 터라 새로운 방식이 어떻게 이 문제를 해결했는지 궁금해졌습니다.
클래스리스(Classless) 주소 체계는 클래스의 경계를 무시하고 필요한 만큼만 네트워크를 나누는 방식입니다. 마치 맞춤복처럼 조직에 딱 맞는 크기의 네트워크를 할당할 수 있습니다.
**CIDR(Classless Inter-Domain Routing)**이라고도 불리며, 1993년에 도입되어 현재까지 사용되고 있습니다.
다음 코드를 살펴봅시다.
import ipaddress
# 클래스리스 방식으로 네트워크 분석
def analyze_cidr_network(cidr_notation):
network = ipaddress.ip_network(cidr_notation, strict=False)
return {
"네트워크 주소": str(network.network_address),
"브로드캐스트 주소": str(network.broadcast_address),
"서브넷 마스크": str(network.netmask),
"사용 가능 호스트 수": network.num_addresses - 2,
"호스트 범위": f"{list(network.hosts())[0]} ~ {list(network.hosts())[-1]}"
}
# 다양한 CIDR 블록 분석
examples = ["192.168.1.0/24", "10.0.0.0/22", "172.16.0.0/20"]
for cidr in examples:
print(f"\n{cidr} 분석:")
for key, value in analyze_cidr_network(cidr).items():
print(f" {key}: {value}")
클래스풀 체계가 기성복이라면, 클래스리스 체계는 맞춤복입니다. 기성복은 S, M, L처럼 정해진 사이즈만 있어서 딱 맞지 않을 수 있습니다.
하지만 맞춤복은 내 몸에 맞게 만들어지니 낭비가 없습니다. 1993년, 인터넷 엔지니어들은 IP 주소 고갈 위기에 직면했습니다.
클래스풀 체계의 낭비가 너무 심했기 때문입니다. 그래서 클래스의 경계를 없애고, 비트 단위로 네트워크 크기를 조절할 수 있는 CIDR를 도입했습니다.
CIDR의 핵심은 **가변 길이 서브넷 마스크(VLSM)**입니다. 클래스풀에서는 Class C면 무조건 /24(255.255.255.0)를 사용해야 했습니다.
하지만 CIDR에서는 /25, /26, /27 등 원하는 크기로 네트워크를 나눌 수 있습니다. 위 코드를 보면 세 가지 다른 크기의 네트워크를 분석하고 있습니다.
192.168.1.0/24는 254개의 호스트를, 10.0.0.0/22는 1022개의 호스트를, 172.16.0.0/20은 4094개의 호스트를 수용할 수 있습니다. /22라는 표기가 의미하는 것은 무엇일까요?
32비트 IP 주소 중 앞의 22비트가 네트워크 주소라는 뜻입니다. 나머지 10비트가 호스트 주소가 되므로, 2의 10승인 1024개에서 네트워크 주소와 브로드캐스트 주소 2개를 빼면 1022개의 호스트를 사용할 수 있습니다.
1000개의 호스트가 필요한 조직을 다시 생각해봅시다. 클래스풀에서는 Class B를 받아 6만 4천 개를 낭비했습니다.
하지만 CIDR에서는 /22 블록을 할당받으면 됩니다. 1022개를 사용할 수 있으니 거의 딱 맞습니다.
클라우드 환경에서 이 개념은 더욱 중요합니다. AWS VPC를 생성할 때 10.0.0.0/16 같은 CIDR 블록을 지정하고, 그 안에서 서브넷을 /24, /26 등으로 나누어 사용합니다.
효율적인 IP 주소 설계가 인프라 비용과 직결되기 때문입니다. 라우팅 테이블 크기도 줄어들었습니다.
클래스풀 시절에는 각 네트워크마다 별도의 라우팅 엔트리가 필요했습니다. 하지만 CIDR에서는 여러 네트워크를 하나의 큰 블록으로 묶어서 표현할 수 있습니다.
이것을 경로 집약(Route Aggregation) 또는 슈퍼네팅이라고 합니다. 김개발 씨는 AWS 콘솔에서 VPC를 생성할 때 왜 CIDR 블록을 지정해야 하는지 이제야 이해가 되었습니다.
단순한 숫자가 아니라 네트워크 설계의 핵심이었던 것입니다.
실전 팁
💡 - CIDR 블록 크기를 결정할 때는 향후 확장성을 고려하여 여유 있게 설계하세요
- 클라우드 환경에서는 VPC CIDR 블록을 나중에 변경하기 어려우므로 처음부터 신중하게 결정해야 합니다
4. 서브넷 마스크와 CIDR 표기법
"자, 이제 실무에서 가장 많이 마주치는 서브넷 마스크와 CIDR 표기법을 자세히 알아볼까요?" 박시니어 씨가 노트북을 열었습니다. 김개발 씨는 AWS 문서에서 수없이 봤지만 정확히 이해하지 못했던 /24, /16 같은 표기들이 드디어 명확해지길 기대했습니다.
서브넷 마스크는 IP 주소에서 네트워크 부분과 호스트 부분을 구분하는 32비트 값입니다. CIDR 표기법은 이를 간단하게 슬래시와 숫자로 표현합니다.
예를 들어 /24는 앞의 24비트가 네트워크 주소라는 의미이며, 255.255.255.0과 동일합니다.
다음 코드를 살펴봅시다.
import ipaddress
# 서브넷 마스크와 CIDR 변환
def mask_to_cidr(subnet_mask):
"""서브넷 마스크를 CIDR 표기로 변환"""
binary = ''.join(format(int(x), '08b') for x in subnet_mask.split('.'))
return binary.count('1')
def cidr_to_mask(cidr):
"""CIDR 표기를 서브넷 마스크로 변환"""
mask = '1' * cidr + '0' * (32 - cidr)
octets = [int(mask[i:i+8], 2) for i in range(0, 32, 8)]
return '.'.join(map(str, octets))
# 변환 테스트
print("서브넷 마스크 → CIDR:")
for mask in ["255.255.255.0", "255.255.255.128", "255.255.240.0"]:
print(f" {mask} = /{mask_to_cidr(mask)}")
print("\nCIDR → 서브넷 마스크:")
for cidr in [24, 25, 20, 16]:
print(f" /{cidr} = {cidr_to_mask(cidr)}")
서브넷 마스크를 처음 접하면 255.255.255.0 같은 숫자가 왜 이렇게 생겼는지 의아합니다. 하지만 이진수로 바꿔보면 그 의미가 명확해집니다.
255를 이진수로 바꾸면 11111111입니다. 8비트가 모두 1입니다.
따라서 255.255.255.0은 이진수로 11111111.11111111.11111111.00000000이 됩니다. 앞의 24비트가 1이고 뒤의 8비트가 0입니다.
서브넷 마스크에서 1인 부분은 네트워크 주소, 0인 부분은 호스트 주소를 나타냅니다. IP 주소와 서브넷 마스크를 비트 AND 연산하면 네트워크 주소만 남게 됩니다.
마치 체로 거르는 것과 같습니다. CIDR 표기법은 이 서브넷 마스크를 더 간단하게 표현합니다.
1이 몇 개인지만 세면 되니까요. 255.255.255.0은 1이 24개이므로 /24로 표기합니다.
훨씬 간결하죠? 위 코드에서 mask_to_cidr 함수는 서브넷 마스크를 이진수로 변환한 뒤 1의 개수를 셉니다.
반대로 cidr_to_mask 함수는 CIDR 숫자만큼 1을 채우고 나머지를 0으로 채운 뒤 10진수로 변환합니다. 실무에서 자주 만나는 CIDR 값들을 정리해보겠습니다.
/8은 Class A 크기로 약 1677만 개의 호스트를 수용합니다. /16은 Class B 크기로 6만 5천 개, /24는 Class C 크기로 254개입니다.
/24보다 큰 숫자도 자주 사용됩니다. /25는 126개, /26은 62개, /27은 30개, /28은 14개의 호스트를 수용합니다.
소규모 서브넷을 만들 때 유용합니다. AWS에서 보안 그룹이나 네트워크 ACL을 설정할 때 이 지식이 필요합니다.
특정 IP 범위에서만 접근을 허용하려면 CIDR 블록으로 지정해야 합니다. 단일 IP만 허용하려면 /32를 사용합니다.
192.168.1.100/32는 192.168.1.100 단 하나의 IP만을 의미합니다. 0.0.0.0/0은 특별한 의미를 가집니다.
네트워크 비트가 0개라는 것은 모든 IP를 포함한다는 뜻입니다. 라우팅 테이블에서 기본 경로(Default Route)를 지정할 때 사용합니다.
김개발 씨는 이제 CIDR 표기를 보면 머릿속에서 자동으로 호스트 수가 계산됩니다. /24면 254개, /25면 126개.
이 감각이 네트워크 설계의 기초가 됩니다.
실전 팁
💡 - 자주 쓰는 CIDR을 외워두세요: /24=254개, /25=126개, /26=62개, /27=30개, /28=14개
- 단일 IP를 지정할 때는 /32를 사용하고, 모든 IP를 의미할 때는 0.0.0.0/0을 사용합니다
5. 서브네팅 계산 방법
"이제 직접 서브네팅을 해볼까요?" 박시니어 씨가 문제를 냈습니다. "192.168.1.0/24 네트워크를 4개의 서브넷으로 나누면 각각 어떤 범위가 될까요?" 김개발 씨는 잠시 생각에 잠겼습니다.
이론은 알겠는데 직접 계산하려니 막막했습니다.
**서브네팅(Subnetting)**은 하나의 큰 네트워크를 여러 개의 작은 네트워크로 나누는 작업입니다. 네트워크 비트를 늘려서 더 많은 서브넷을 만들 수 있으며, 그만큼 각 서브넷의 호스트 수는 줄어듭니다.
2의 거듭제곱으로 계산하는 것이 핵심입니다.
다음 코드를 살펴봅시다.
import ipaddress
def subnet_calculator(base_network, num_subnets):
"""네트워크를 주어진 수의 서브넷으로 분할"""
network = ipaddress.ip_network(base_network)
# 필요한 서브넷 비트 수 계산
import math
subnet_bits = math.ceil(math.log2(num_subnets))
new_prefix = network.prefixlen + subnet_bits
# 서브넷 생성
subnets = list(network.subnets(prefixlen_diff=subnet_bits))
print(f"원본 네트워크: {base_network}")
print(f"서브넷 비트 추가: {subnet_bits}비트 → /{new_prefix}")
print(f"\n생성된 서브넷 ({len(subnets)}개):")
for i, subnet in enumerate(subnets):
hosts = list(subnet.hosts())
print(f" 서브넷 {i+1}: {subnet}")
print(f" 사용 가능 IP: {hosts[0]} ~ {hosts[-1]} ({len(hosts)}개)")
# 192.168.1.0/24를 4개의 서브넷으로 분할
subnet_calculator("192.168.1.0/24", 4)
서브네팅은 네트워크 관리자의 핵심 기술입니다. 하나의 큰 네트워크를 부서별, 기능별로 나누면 보안과 관리가 용이해집니다.
마치 큰 사무실을 파티션으로 나누는 것과 같습니다. 서브네팅의 기본 원리는 간단합니다.
네트워크 비트를 늘리면 서브넷 수가 증가하고, 호스트 수가 감소합니다. 비트 하나를 추가할 때마다 서브넷 수는 2배가 됩니다. 구체적인 예를 들어보겠습니다.
192.168.1.0/24 네트워크가 있습니다. 이를 4개의 서브넷으로 나누고 싶습니다.
4개를 만들려면 몇 비트가 필요할까요? 2비트입니다.
2의 2승이 4이기 때문입니다. 원래 /24였으니 2비트를 더하면 /26이 됩니다.
새로운 서브넷 마스크는 255.255.255.192가 됩니다. 각 서브넷은 64개의 IP를 가지며, 네트워크 주소와 브로드캐스트 주소를 제외하면 62개의 호스트를 수용할 수 있습니다.
위 코드의 실행 결과를 살펴보겠습니다. 192.168.1.0/26은 첫 번째 서브넷으로, 192.168.1.1부터 192.168.1.62까지 사용 가능합니다.
두 번째 서브넷 192.168.1.64/26은 192.168.1.65부터 192.168.1.126까지 사용합니다. 이런 식으로 네 개의 서브넷이 만들어집니다.
실무 시나리오를 생각해봅시다. 회사에 개발팀, 마케팅팀, 영업팀, 경영지원팀 네 개의 부서가 있습니다.
각 부서에 별도의 서브넷을 할당하면 부서 간 트래픽을 제어하고, 보안 정책을 다르게 적용할 수 있습니다. 서브네팅 계산을 빠르게 하는 팁이 있습니다.
/24에서 시작해서 비트를 하나씩 늘려보세요. /25는 2개의 서브넷(각 126개 호스트), /26은 4개(각 62개), /27은 8개(각 30개), /28은 16개(각 14개)입니다.
반대로 슈퍼네팅도 가능합니다. 여러 개의 작은 네트워크를 하나로 합치는 것입니다.
192.168.0.0/24와 192.168.1.0/24를 합치면 192.168.0.0/23이 됩니다. 라우팅 테이블을 간소화할 때 사용합니다.
주의할 점도 있습니다. 서브넷 경계는 2의 거듭제곱 단위로만 나눌 수 있습니다.
3개나 5개로 정확히 나누는 것은 불가능합니다. 3개가 필요하면 4개로 나누고 하나를 비워두어야 합니다.
김개발 씨는 펜을 들고 직접 계산해보았습니다. "아, 생각보다 규칙이 있네요.
2의 거듭제곱만 기억하면 되는군요!"
실전 팁
💡 - 서브넷 수 = 2의 (추가 비트 수)승, 호스트 수 = 2의 (남은 호스트 비트 수)승 - 2
- 가장 흔한 서브넷: /24(254개), /25(126개), /26(62개), /27(30개), /28(14개)
6. 공인 IP vs 사설 IP와 NAT
김개발 씨가 궁금한 것이 생겼습니다. "선배, 제 노트북 IP가 192.168로 시작하는데, 인터넷에서는 다른 IP로 보인다고 하더라고요.
이게 어떻게 가능한 거예요?" 박시니어 씨가 웃으며 답했습니다. "아, 그건 NAT 때문이에요.
아주 중요한 개념이죠."
**공인 IP(Public IP)**는 인터넷에서 고유하게 식별되는 주소이고, **사설 IP(Private IP)**는 내부 네트워크에서만 사용되는 주소입니다. **NAT(Network Address Translation)**는 사설 IP를 공인 IP로 변환하여 인터넷 통신을 가능하게 합니다.
이를 통해 부족한 IPv4 주소를 효율적으로 사용할 수 있습니다.
다음 코드를 살펴봅시다.
import ipaddress
def check_ip_type(ip_addr):
"""IP 주소가 공인인지 사설인지 확인"""
ip = ipaddress.ip_address(ip_addr)
# 사설 IP 대역
private_ranges = [
ipaddress.ip_network('10.0.0.0/8'), # Class A 사설
ipaddress.ip_network('172.16.0.0/12'), # Class B 사설
ipaddress.ip_network('192.168.0.0/16'), # Class C 사설
ipaddress.ip_network('127.0.0.0/8'), # 루프백
]
for private in private_ranges:
if ip in private:
return f"{ip_addr}: 사설 IP ({private})"
return f"{ip_addr}: 공인 IP"
# 테스트
test_ips = ["192.168.1.100", "10.0.0.1", "172.16.50.1",
"8.8.8.8", "203.252.1.1", "127.0.0.1"]
for ip in test_ips:
print(check_ip_type(ip))
IPv4 주소는 약 43억 개입니다. 숫자만 보면 많아 보이지만, 전 세계 인터넷 사용 장치 수를 생각하면 턱없이 부족합니다.
스마트폰, 노트북, 태블릿, IoT 기기까지 한 사람이 여러 개의 장치를 사용하니까요. 이 문제를 해결하기 위해 사설 IP라는 개념이 등장했습니다.
인터넷에서는 사용하지 않고 내부 네트워크에서만 쓰는 주소입니다. 마치 회사 내선 번호와 같습니다.
내선 번호 100번은 A회사에도 있고 B회사에도 있지만, 회사 안에서만 통하니까 충돌이 없습니다. RFC 1918에서 정의한 사설 IP 대역은 세 가지입니다.
10.0.0.0/8은 가장 큰 대역으로 대기업이나 클라우드에서 많이 사용합니다. 172.16.0.0/12 (172.16.0.0 ~ 172.31.255.255)는 중간 크기입니다.
192.168.0.0/16은 가정이나 소규모 사무실에서 흔히 사용합니다. 집에서 공유기에 연결된 노트북의 IP가 192.168.0.10이라고 해봅시다.
이 IP로 구글에 접속하려면 어떻게 될까요? 구글 서버는 192.168.0.10이라는 주소로 응답을 보낼 수 없습니다.
인터넷에서 이 주소는 의미가 없기 때문입니다. 여기서 NAT가 등장합니다.
공유기가 노트북의 요청을 받으면, 출발지 주소를 사설 IP에서 공인 IP로 바꿔서 인터넷으로 보냅니다. 응답이 오면 다시 원래의 사설 IP로 바꿔서 노트북에 전달합니다.
마치 통역사처럼 두 세계 사이에서 주소를 번역해주는 것입니다. NAT에는 여러 종류가 있습니다.
**SNAT(Source NAT)**는 출발지 주소를 바꾸고, **DNAT(Destination NAT)**는 목적지 주소를 바꿉니다. 가장 흔한 PAT(Port Address Translation) 또는 NAPT는 포트 번호까지 함께 변환하여 하나의 공인 IP로 여러 사설 IP 장치가 동시에 인터넷을 사용할 수 있게 합니다.
위 코드를 보면 8.8.8.8(구글 DNS)과 203.252.1.1은 공인 IP로 판별됩니다. 반면 10.0.0.1, 172.16.50.1, 192.168.1.100은 사설 IP입니다.
127.0.0.1은 루프백 주소로 특수한 용도입니다. AWS에서 EC2 인스턴스를 생성하면 기본적으로 사설 IP만 할당됩니다.
인터넷과 통신하려면 NAT Gateway를 설정하거나 **Elastic IP(공인 IP)**를 할당해야 합니다. 비용과 보안을 고려한 설계가 필요합니다.
김개발 씨는 집에서 쓰는 공유기가 이렇게 복잡한 일을 하고 있다는 것에 놀랐습니다. "와, 공유기가 그냥 와이파이만 쏘는 게 아니었네요!"
실전 팁
💡 - 사설 IP 대역을 외워두세요: 10.x.x.x, 172.16-31.x.x, 192.168.x.x
- 클라우드에서 비용 절감을 위해 NAT Gateway 대신 NAT Instance를 사용하기도 합니다
7. DHCP 동적 IP 할당
"그런데 선배, IP 주소를 어떻게 장치마다 자동으로 할당하는 거예요? 제가 노트북을 연결할 때마다 수동으로 설정한 적이 없거든요." 김개발 씨의 질문에 박시니어 씨가 마지막 주제를 꺼냈습니다.
"그게 바로 DHCP 덕분이에요."
**DHCP(Dynamic Host Configuration Protocol)**는 네트워크에 연결된 장치에 자동으로 IP 주소를 할당하는 프로토콜입니다. IP 주소뿐만 아니라 서브넷 마스크, 게이트웨이, DNS 서버 주소까지 자동으로 설정해줍니다.
마치 호텔에서 방 배정을 받는 것처럼, 장치가 네트워크에 연결되면 DHCP 서버가 적절한 IP를 할당해줍니다.
다음 코드를 살펴봅시다.
# DHCP 동작 과정 시뮬레이션
class DHCPServer:
def __init__(self, network, start, end):
self.available_ips = [f"192.168.1.{i}" for i in range(start, end + 1)]
self.leased_ips = {} # MAC -> IP 매핑
self.lease_time = 86400 # 24시간 (초)
def discover(self, mac_address):
"""DHCP Discover: 클라이언트가 서버 탐색"""
print(f"[DISCOVER] {mac_address}: 'DHCP 서버 계신가요?'")
return self.offer(mac_address)
def offer(self, mac_address):
"""DHCP Offer: 서버가 IP 제안"""
if self.available_ips:
offered_ip = self.available_ips[0]
print(f"[OFFER] 서버: '{offered_ip} 사용하시겠어요?'")
return offered_ip
return None
def request(self, mac_address, requested_ip):
"""DHCP Request: 클라이언트가 IP 요청"""
print(f"[REQUEST] {mac_address}: '{requested_ip} 주세요!'")
return self.acknowledge(mac_address, requested_ip)
def acknowledge(self, mac_address, ip):
"""DHCP Acknowledge: 서버가 IP 확정"""
self.available_ips.remove(ip)
self.leased_ips[mac_address] = ip
print(f"[ACK] 서버: '{ip} 할당 완료! (24시간 임대)'")
return ip
# DHCP 과정 시뮬레이션
dhcp = DHCPServer("192.168.1.0/24", 100, 200)
dhcp.discover("AA:BB:CC:DD:EE:FF")
dhcp.request("AA:BB:CC:DD:EE:FF", "192.168.1.100")
컴퓨터를 카페 와이파이에 연결했을 때를 생각해보세요. 비밀번호만 입력하면 바로 인터넷이 됩니다.
IP 주소를 직접 설정한 적이 없는데 어떻게 통신이 가능할까요? 바로 DHCP 덕분입니다.
DHCP가 없던 시절에는 네트워크 관리자가 모든 장치의 IP를 수동으로 설정해야 했습니다. 직원이 100명인 회사라면 100개의 IP를 일일이 입력해야 했습니다.
장치가 바뀌거나 새로운 직원이 입사할 때마다 관리자를 불러야 했죠. 정말 비효율적이었습니다.
DHCP는 이 과정을 자동화합니다. 동작 방식은 DORA라는 4단계로 이루어집니다.
이 이름은 각 단계의 첫 글자를 딴 것입니다. 첫 번째는 Discover입니다.
새 장치가 네트워크에 연결되면 "DHCP 서버 계신가요?"라고 브로드캐스트 메시지를 보냅니다. 아직 IP가 없으니 모든 장치에게 물어보는 것입니다.
두 번째는 Offer입니다. DHCP 서버가 이 메시지를 받으면 "192.168.1.100 어때요?"라며 사용 가능한 IP를 제안합니다.
여러 DHCP 서버가 있다면 여러 개의 Offer가 올 수 있습니다. 세 번째는 Request입니다.
클라이언트는 받은 Offer 중 하나를 선택하여 "네, 그 IP 주세요!"라고 요청합니다. 이때도 브로드캐스트를 사용하여 다른 서버들에게도 선택 결과를 알립니다.
네 번째는 Acknowledge입니다. 서버가 "알겠습니다.
24시간 동안 쓰세요."라며 확정합니다. 이제 클라이언트는 해당 IP를 사용할 수 있습니다.
위 코드는 이 DORA 과정을 간단히 시뮬레이션합니다. DHCPServer 클래스가 IP 풀을 관리하고, 각 단계별로 메시지를 출력합니다.
실제 DHCP는 UDP 67번(서버), 68번(클라이언트) 포트를 사용합니다. **임대 시간(Lease Time)**이라는 개념도 중요합니다.
IP는 영구적으로 할당되는 것이 아니라 일정 시간 동안만 빌려주는 것입니다. 임대 시간이 끝나기 전에 갱신하지 않으면 IP는 다시 풀에 반환됩니다.
카페에서 잠깐 사용하는 장치에 영구적으로 IP를 주면 낭비가 되니까요. 클라우드 환경에서도 DHCP가 활발히 사용됩니다.
AWS VPC에서 EC2 인스턴스를 시작하면 자동으로 사설 IP가 할당되는데, 내부적으로 DHCP가 동작하기 때문입니다. AWS는 기본적으로 VPC에 DHCP 옵션 세트를 제공합니다.
**고정 IP(Static IP)**가 필요한 경우도 있습니다. 서버나 프린터처럼 항상 같은 IP를 가져야 하는 장치는 DHCP 예약(Reservation) 기능을 사용합니다.
MAC 주소와 IP를 미리 매핑해두면 해당 장치는 항상 같은 IP를 받습니다. 김개발 씨는 감탄했습니다.
"와, 그냥 자동으로 되는 줄 알았는데 이런 과정이 있었군요. 네트워크가 정말 체계적이네요!"
실전 팁
💡 - DHCP 과정은 DORA로 외우세요: Discover - Offer - Request - Acknowledge
- 서버나 중요 장치는 DHCP 예약 또는 고정 IP를 사용하여 IP가 변경되지 않도록 합니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.
AWS Organizations 완벽 가이드
여러 AWS 계정을 체계적으로 관리하고 통합 결제와 보안 정책을 적용하는 방법을 실무 스토리로 쉽게 배워봅니다. 초보 개발자도 바로 이해할 수 있는 친절한 설명과 실전 예제를 제공합니다.
AWS KMS 암호화 완벽 가이드
AWS KMS(Key Management Service)를 활용한 클라우드 데이터 암호화 방법을 초급 개발자를 위해 쉽게 설명합니다. CMK 생성부터 S3, EBS 암호화, 봉투 암호화까지 실무에 필요한 모든 내용을 담았습니다.
AWS Secrets Manager 완벽 가이드
AWS에서 데이터베이스 비밀번호, API 키 등 민감한 정보를 안전하게 관리하는 Secrets Manager의 핵심 개념과 실무 활용법을 배워봅니다. 초급 개발자도 쉽게 따라할 수 있도록 실전 예제와 함께 설명합니다.