에듀테크 프로젝트 대시보드
라우터와 라우팅 테이블
IP 주소와 서브넷으로 네트워크를 나눴다면, 이제 궁금한 건 "서로 다른 네트워크에 있는 두 컴퓨터는 어떻게 패킷을 주고받는가"다. 그 일을 하는 장비가 라우터(Router)이고, 라우터가 "이 목적지는 이쪽으로 보내면 된다"를 적어 둔 표가 바로 라우팅 테이블(Routing Table)이다. 아래에서 라우터가 매 패킷마다 이 표를 어떻게 검색하는지 직접 돌려 보자.
라우터는 서로 다른 네트워크(서브넷)를 연결하고, 도착한 패킷의 목적지 IP 주소를 보고 "다음으로 어느 장비에게 넘겨야 목적지에 가까워지는가"—즉 다음 홉(Next Hop)—를 결정해서 전달하는 장비다. 이 판단을 위해 참고하는 표가 라우팅 테이블이고, 패킷이 라우터를 하나 거칠 때마다 이 조회가 한 번씩 반복된다.
같은 네트워크 안에서만 동작한다. MAC 주소를 보고 어느 포트로 내보낼지 정하며, IP나 서브넷 개념을 몰라도 된다. 자세한 내용은 스위치와 브리지 참고.
서로 다른 네트워크를 연결한다. IP 주소(목적지 네트워크)를 보고 어느 인터페이스·다음 홉으로 넘길지 정하며, 그 판단 근거가 라우팅 테이블이다.
라우터 한 대가 들고 있는 라우팅 테이블은 대략 이런 모습이다. 각 행은
"이 대역(목적지 네트워크/CIDR)으로 가려면 이 다음 홉으로,
이 인터페이스로 내보내라"는 규칙 하나를 뜻한다.
| 목적지 네트워크 | 서브넷 마스크 | 다음 홉(게이트웨이) | 인터페이스 | 메트릭 |
|---|---|---|---|---|
| 0.0.0.0/0 | 0.0.0.0 | 203.0.113.1 | eth0 | 1 |
| 10.0.0.0/8 | 255.0.0.0 | 10.0.0.1 | eth1 | 1 |
| 10.1.0.0/16 | 255.255.0.0 | 10.1.0.1 | eth1 | 1 |
| 10.1.2.0/24 | 255.255.255.0 | 10.1.2.1 | eth2 | 1 |
0.0.0.0/0은 "이 표의 다른 행에 하나도 안 걸리면 여기로 보내라"는
기본 경로(Default Route)다. 나머지는 목적지가 구체적일수록 프리픽스(/8 → /24)가
길어진다 — CIDR 표기법은 서브넷 마스크와 CIDR 페이지 참고.
목적지 IP 하나는 라우팅 테이블의 여러 행에 동시에 걸릴 수 있다. 예를 들어
10.1.2.55는 0.0.0.0/0에도, 10.0.0.0/8에도,
10.1.0.0/16에도, 10.1.2.0/24에도 전부 해당한다.
이럴 때 라우터는 그중 서브넷 마스크가 가장 긴(가장 구체적인) 행을 고른다.
이게 최장 일치 검색이다 — 구체적인 경로가 있다면 뭉뚱그린 기본 경로보다 항상 우선한다.
| 목적지 네트워크 | 다음 홉 | 인터페이스 |
|---|
라우팅 테이블을 채우는 방법은 크게 두 가지다.
| 정적 라우팅 (Static) | 동적 라우팅 (Dynamic) |
|---|---|
| 관리자가 경로를 직접 입력 | 라우터끼리 프로토콜로 경로 정보를 자동 교환 |
| 단순하고 예측 가능, 소규모망에 적합 | 네트워크가 크고 자주 바뀌는 환경에 적합 |
| 회선 장애가 나도 테이블이 스스로 안 바뀜 → 수동 수정 전까지 불통 | 장애 발생 시 이웃 라우터들이 새 경로를 다시 계산해 자동 우회 |
| 대표 사례: 집 공유기의 고정 게이트웨이 설정 | 대표 프로토콜: RIP, OSPF (기관 내부), BGP (기관 간) |
패킷은 목적지까지 가는 전체 경로를 미리 알지 못한다. 각 라우터는 오직
"다음 홉이 누구인가"만 판단해서 넘길 뿐이고, 그 다음 라우터가 또 자기
테이블로 다음 홉을 판단하는 과정이 목적지에 도착할 때까지 반복된다.
출발지 192.168.1.10에서 목적지 10.1.2.50까지,
라우터 A→B→C→D를 거치는 과정을 재생해 보자.
# 라우팅 테이블: list of dict, prefix가 길수록 더 구체적인 경로 def longest_prefix_match(dest_ip: str, table: list) -> dict | None: dest = ip_to_int(dest_ip) candidates = [] for route in table: mask = (0xFFFFFFFF << (32 - route["prefix"])) & 0xFFFFFFFF network = ip_to_int(route["network"]) if (dest & mask) == (network & mask): # 이 행에 걸리는가? candidates.append(route) if not candidates: return None # 기본 경로(0.0.0.0/0)가 없다면 도달 불가 # prefix가 가장 큰(=가장 구체적인) 후보를 채택 — 최장 일치 candidates.sort(key=lambda r: r["prefix"], reverse=True) return candidates[0] # table.sort(key=lambda r: r["prefix"]) 로 미리 정렬해 두면 # 매 패킷마다 정렬을 반복하지 않아도 되어 실제 라우터는 이 방식을 쓴다.