
NAT: 사설 IP와 공인 IP 변환
공유기 하나로 온 가족이 인터넷을 쓰는 비결. 하나의 공인 IP 뒤에 숨은 수많은 사설 IP들. 네트워크 주소 변환의 마법.

공유기 하나로 온 가족이 인터넷을 쓰는 비결. 하나의 공인 IP 뒤에 숨은 수많은 사설 IP들. 네트워크 주소 변환의 마법.
내 서버는 왜 걸핏하면 뻗을까? OS가 한정된 메모리를 쪼개 쓰는 처절한 사투. 단편화(Fragmentation)와의 전쟁.

미로를 탈출하는 두 가지 방법. 넓게 퍼져나갈 것인가(BFS), 한 우물만 팔 것인가(DFS). 최단 경로는 누가 찾을까?

이름부터 빠릅니다. 피벗(Pivot)을 기준으로 나누고 또 나누는 분할 정복 알고리즘. 왜 최악엔 느린데도 가장 많이 쓰일까요?

매번 3-Way Handshake 하느라 지쳤나요? 한 번 맺은 인연(TCP 연결)을 소중히 유지하는 법. HTTP 최적화의 기본.

스타트업을 하면서 AWS EC2 인스턴스를 띄우다가 문득 궁금했다. 우리 회사 노트북 10대, 휴대폰 15대가 전부 같은 공유기를 쓰는데, IP 주소는 어떻게 관리되는 거지? 외부에서 보면 우리 회사는 딱 하나의 IP 주소만 갖고 있는데, 그럼 패킷이 들어왔을 때 공유기는 어떻게 "아 이건 김 대리 노트북으로 보내야 하는구나"를 알까?
더 이상했던 건, ifconfig 쳐보면 내 맥북은 192.168.0.15인데, 집에 가서 또 쳐보면 거기도 192.168.0.15였다. IP 주소가 중복되면 큰일 나는 거 아니야? 그런데 왜 세상은 멀쩡하지?
이 의문이 나를 NAT(Network Address Translation)라는 개념으로 이끌었다. 결국 이거였다. 공유기가 하는 일의 핵심은 "주소 번역"이었다.
처음엔 "공유기가 그냥 스위치처럼 패킷만 전달하는 거 아닌가?"라고 생각했다. 그런데 스위치는 L2(데이터 링크 계층)에서 MAC 주소 보고 일하는 친구고, 공유기는 L3(네트워크 계층)에서 IP 주소를 건드린다. 전혀 다른 레벨이었다.
그 다음 의문은 "그럼 공유기가 내부 디바이스마다 다른 공인 IP를 할당해주나?"였다. 근데 아니었다. 통신사(ISP)가 우리 집에 주는 공인 IP는 딱 하나다. 그럼 이걸 어떻게 나눠 쓰지?
여기서 "아, 공유기가 Port 번호를 이용해서 구분하는구나!"라는 깨달음이 왔다. 같은 IP 주소를 쓰더라도, 포트 번호가 다르면 다른 연결로 인식된다. 공유기는 이 포트 번호를 이용해서 "이 포트는 김 대리 노트북", "저 포트는 이 사원 휴대폰" 이렇게 매핑 테이블을 만들어둔다.
아파트 비유가 완전히 와닿았다.
아파트 건물 = 공인 IP 각 호수 = 사설 IP + 포트 번호우편물이 "서울시 강남구 테헤란로 123번지"로 오면, 이건 아파트 건물 전체의 주소다. 하지만 우편물엔 "305호 앞으로"라는 상세 주소가 붙어 있다. 관리실(공유기)이 이걸 보고 305호로 배달한다.
외부에서 보면 아파트 전체는 "테헤란로 123번지" 하나로 보이지만, 내부에는 101호부터 1505호까지 수백 개의 호수가 있다. 이게 바로 NAT의 본질이다.
IPv4 주소는 32비트다. 2^32 = 약 43억 개. 언뜻 많아 보이지만, 전 세계 인구가 80억이고, 한 사람이 스마트폰, 노트북, 태블릿, 스마트워치, IoT 냉장고까지 가지면 절대 부족하다.
1990년대 중반, "이대로 가면 10년 안에 IP 주소가 고갈된다"는 경고가 나왔다. 해결책은 두 가지였다.
IPv6가 정답이긴 한데, 전 세계 인프라를 바꾸는 건 시간이 너무 오래 걸렸다. 그래서 NAT가 임시방편으로 등장했고, 그게 30년째 현역이다. 결국 NAT는 "IPv4 수명 연장 패치"였다.
IANA(국제 인터넷 주소 관리 기관)가 정한 사설 IP 대역은 딱 세 가지다.
10.0.0.0 ~ 10.255.255.255 (10.0.0.0/8) - A 클래스, 대기업용
172.16.0.0 ~ 172.31.255.255 (172.16.0.0/12) - B 클래스, 중소기업용
192.168.0.0 ~ 192.168.255.255 (192.168.0.0/16) - C 클래스, 가정용
이 주소들은 "인터넷에 직접 나가지 않는다"는 전제 하에, 전 세계 누구나 마음대로 쓸 수 있다. 내 집 공유기가 192.168.0.1을 쓰고, 옆집도 192.168.0.1을 쓴다. 하지만 각 집 공유기가 다른 공인 IP를 가지고 있으니 충돌이 안 난다.
나는 이렇게 이해했다. 사설 IP는 "집 안 닉네임"이고, 공인 IP는 "주민등록증 상의 주소"다. 집 안에서 아빠를 "여보"라고 부르든, 옆집에서 남편을 "여보"라고 부르든 상관없다. 하지만 세금 고지서는 주민등록증 주소로 온다.
하나의 사설 IP를 하나의 고정된 공인 IP로 매핑한다. 주로 웹서버처럼 "외부에서 접속 가능해야 하는 서버"에 쓴다.
192.168.0.10 (웹 서버) <-> 211.45.123.100 (고정 공인 IP)
AWS Elastic IP가 이 방식이다. EC2 인스턴스에 고정 공인 IP를 붙여서, 재부팅해도 IP가 안 바뀌게 한다.
공인 IP 풀을 만들어두고, 필요할 때마다 하나씩 할당한다. 내부 디바이스가 100대인데 공인 IP가 10개만 있으면, 동시에 10명만 외부 통신 가능하다.
Pool: 211.45.123.100 ~ 211.45.123.110 (총 11개)
내부 디바이스 100대가 경쟁해서 사용
요즘은 잘 안 쓴다. IP 부족할 때 임시로 쓰던 방식.
진짜 핵심은 이거다. 우리 집 공유기, 회사 공유기가 쓰는 방식. 하나의 공인 IP를 포트 번호로 쪼개서 여러 디바이스가 공유한다.
사설 IP:Port 공인 IP:Port
192.168.0.10:54321 <-> 211.45.123.100:10001
192.168.0.15:49152 <-> 211.45.123.100:10002
192.168.0.20:33000 <-> 211.45.123.100:10003
하나의 공인 IP(211.45.123.100)에 포트만 바꿔가며 수만 개 연결을 처리한다. 이론상 포트는 0~65535 범위니까, 하나의 공인 IP로 6만 개 동시 연결이 가능하다.
나는 이걸 "전화 교환원 비유"로 받아들였다. 옛날 회사들은 대표번호 하나(02-1234-5678)로 전화를 받고, 교환원이 "몇 번 부서세요?"라고 물어서 내선 번호(포트)로 연결해줬다. NAT가 하는 일도 똑같다.
공유기는 NAT 테이블(매핑 테이블)을 메모리에 들고 있다. 실제로 리눅스 서버에서 NAT를 켜면 커널이 이 테이블을 관리한다.
# 리눅스에서 NAT 테이블 확인
sudo iptables -t nat -L -v -n
예시 테이블:
| 내부 IP:Port | 외부 IP:Port | 목적지 IP | 상태 | 타임아웃 |
|---|---|---|---|---|
| 192.168.0.10:54321 | 211.45.123.100:10001 | 8.8.8.8:80 | 활성 | 120초 |
| 192.168.0.15:49152 | 211.45.123.100:10002 | 142.250.1.1:443 | 활성 | 120초 |
| 192.168.0.20:33000 | 211.45.123.100:10003 | 13.124.199.1:443 | 종료 대기 | 10초 |
내 노트북 (192.168.0.10:54321) -> 공유기
"나 구글(8.8.8.8:80)에 HTTP 요청 보낼게"
공유기:
192.168.0.10:54321211.45.123.100:10001 (사용 가능한 포트 자동 할당)211.45.123.100:10001로 변경구글 (8.8.8.8:80) -> 공유기
"211.45.123.100:10001로 보낸 요청에 대한 응답이야"
공유기:
10001을 NAT 테이블에서 검색192.168.0.10:54321 발견192.168.0.10:54321로 변경결국 이거였다. 공유기는 패킷의 IP 주소와 포트 번호를 실시간으로 고쳐 쓴다. 마치 편지봉투의 "받는 사람" 주소를 수정액으로 지우고 다시 쓰는 것처럼.
리눅스 서버를 공유기처럼 쓰려면 iptables로 NAT를 설정한다.
# IP 포워딩 활성화 (패킷 전달 허용)
sudo sysctl -w net.ipv4.ip_forward=1
# NAT 규칙 추가 (SNAT: Source NAT)
# eth0는 공인 IP가 붙은 인터페이스, eth1은 사설망 인터페이스
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# 확인
sudo iptables -t nat -L -v -n
MASQUERADE는 "내 공인 IP가 동적으로 바뀔 수 있으니, 현재 인터페이스의 IP로 자동 변환해줘"라는 뜻이다. 집 공유기처럼 ISP가 DHCP로 IP를 할당하는 환경에서 쓴다.
만약 공인 IP가 고정이라면:
sudo iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 211.45.123.100
회사에 웹 서버(192.168.0.50:8080)가 있고, 외부에서 211.45.123.100:80으로 접속하면 내부 서버로 연결되게 하고 싶다.
# DNAT (Destination NAT): 목적지 주소 변환
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.0.50:8080
# 응답 패킷도 변환 (SNAT)
sudo iptables -t nat -A POSTROUTING -j MASQUERADE
이게 바로 "포트 포워딩"이다. 공유기 설정에서 "외부 포트 80 -> 내부 192.168.0.50:8080"으로 규칙 추가하는 게 이 원리다.
Docker도 똑같은 방식으로 작동한다. docker run -p 8080:80 하면, 호스트의 8080 포트로 들어오는 패킷을 컨테이너 내부의 80 포트로 NAT 한다.
NAT는 "내부에서 외부로 나가는 요청"은 완벽히 처리하지만, "외부에서 내부로 들어오는 요청"은 기본적으로 막힌다. NAT 테이블에 매핑 정보가 없으면, 공유기는 "이 패킷을 누구한테 줘야 하지?"를 모른다.
이게 문제가 되는 상황:
공유기가 UPnP를 지원하면, 내부 디바이스가 "포트 포워딩 규칙 좀 자동으로 추가해줘"라고 요청할 수 있다.
# Python miniupnpc 라이브러리 예시
import miniupnpc
u = miniupnpc.UPnP()
u.discover()
u.selectigd()
# 외부 포트 12345 -> 내부 포트 12345로 매핑 추가
u.addportmapping(12345, 'TCP', u.lanaddr, 12345, 'My Game', '')
print(f"External IP: {u.externalipaddress()}")
하지만 UPnP는 보안 구멍이 크다. 악성코드가 UPnP로 포트를 열어버릴 수 있어서, 요즘은 끄는 게 권장된다.
STUN 서버에 "나 지금 공인 IP:Port가 뭐야?"라고 물어본다. STUN 서버가 "너 211.45.123.100:10001이야"라고 알려주면, 그 정보를 상대방에게 전달해서 P2P 연결한다.
WebRTC가 이 방식을 쓴다. Zoom 같은 화상회의 서비스도 초기 연결에 STUN을 쓴다.
// WebRTC STUN 설정 예시
const configuration = {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' }
]
};
const peerConnection = new RTCPeerConnection(configuration);
STUN으로도 안 되는 극악의 NAT 환경(Symmetric NAT)이 있다. 이 경우 중계 서버(TURN)를 거쳐서 통신한다. P2P가 아니라 서버를 경유하는 거라 느리지만, 확실한 방법이다.
STUN, TURN, 직접 연결 등을 전부 시도해보고, 가장 빠른 경로를 자동으로 선택한다. WebRTC의 기술.
나는 이렇게 정리해본다. NAT Traversal = "NAT라는 벽에 구멍 뚫기"다. UPnP는 정문 열쇠, STUN은 뒷문 찾기, TURN은 담 넘어서 우회하기.
IPv6에선 모든 디바이스가 고유한 공인 IP를 받는다. 사설 IP가 필요 없다. NAT도 필요 없다.
# IPv6 주소 예시
2001:0db8:85a3:0000:0000:8a2e:0370:7334
IPv6 주소는 128비트라서, 지구상의 모래알 하나하나에 IP를 할당해도 남는다. "IP 부족" 개념 자체가 사라진다.
하지만 현실은? 2025년 현재도 전 세계 트래픽의 60%는 여전히 IPv4다. 인터넷의 절반 이상이 아직도 NAT에 의존한다. 왜?
결국 NAT는 "임시방편"으로 시작했지만, 너무 잘 작동해서 영구 솔루션이 됐다. 이게 기술 부채(Technical Debt)의 교과서적 사례다.
Docker 컨테이너도 NAT를 쓴다. 호스트 서버가 "공유기" 역할을 하고, 컨테이너들은 사설 IP(172.17.0.0/16)를 받는다.
# 컨테이너 내부 IP 확인
docker run -it --rm alpine ip addr show
# 출력 예시:
# eth0: 172.17.0.2/16
# 호스트에서 Docker NAT 규칙 확인
sudo iptables -t nat -L -n | grep DOCKER
Docker는 docker0라는 가상 브릿지를 만들고, 여기에 NAT를 걸어서 컨테이너들이 외부와 통신하게 한다. -p 8080:80 옵션은 "호스트 8080 포트를 컨테이너 80 포트로 포트 포워딩"이다.
Kubernetes도 비슷하다. Pod마다 사설 IP를 주고, NodePort 서비스가 NAT로 외부 트래픽을 Pod로 전달한다.
회사 공유기 설정 화면을 열어보니, "포트 포워딩" 메뉴가 있었다. 거기서 내가 직접 규칙을 추가해봤다. 외부 포트 22 → 내 맥북 192.168.0.15:22로 SSH 접속 허용. 집에서 회사 맥북으로 SSH 접속이 됐다. 이제 NAT가 정확히 뭘 하는지 체감했다. 결국 이거였다. 공유기는 단순한 신호 중계기가 아니라, 능동적으로 패킷을 고쳐 쓰는 주소 번역기였다.