안동민 개발노트

본문 시작

왜 계층이 필요한가

통신 책임을 계층과 인터페이스로 분리하는 이유를 이해하고 변경 영향과 네트워크 장애 범위를 계층별로 좁힙니다.

1장에서 우리는 인터넷이 전 세계를 연결하는 거대한 네트워크이며, 데이터는 패킷이라는 단위로 쪼개져 전달된다는 것을 확인했습니다.

그런데 잠시 생각해 보면, 이게 정말 복잡한 일이라는 걸 알 수 있습니다.

전기·무선 신호를 비트로 변환하고, 그 비트들을 프레임으로 묶고, 프레임에 로컬 목적지 주소를 붙이고, IP 주소를 기준으로 경로를 결정하고, 필요한 경우 손실된 데이터를 재전송하고, 마지막으로 애플리케이션이 이해할 수 있는 형태로 해석해야 합니다.

이 모든 과정을 하나의 거대한 프로토콜로 처리하려고 하면 어떻게 될까요?


모놀리식 통신의 문제점

만약 네트워크 통신을 하나의 프로토콜이 전부 담당한다고 가정해 봅시다.

비트를 물리 신호로 변환하는 것부터, IP 주소를 기반으로 경로를 결정하는 것, 연결과 재전송을 관리하는 것, HTTP 요청을 파싱하는 것까지 모두 하나의 거대한 규격 안에 들어 있습니다.

이런 구조에서는 어떤 문제가 생길까요?

유선 Ethernet을 Wi-Fi로 바꿀 때, 책임과 인터페이스가 나뉘어 있지 않다면 다른 기능까지 변경 영향을 검토해야 합니다.

링크 전송 방식을 바꾸는 일이 경로 결정·재전송 로직과 강하게 묶여 있으면 부분 교체가 어려워집니다.

소프트웨어 공학에서 이런 구조를 모놀리식(Monolithic)이라고 부릅니다.

하나의 거대한 덩어리가 모든 책임을 지는 구조입니다.

그리고 소프트웨어 엔지니어링이 그랬듯, 네트워크도 이 문제를 계층화(Layering)로 해결했습니다.


계층화의 장점

계층화란 전체 통신 과정을 역할별로 나누어 독립적인 계층으로 분리하는 것입니다.

각 계층은 자기 역할에만 집중하고, 다른 계층의 내부 동작에는 관여하지 않습니다.

모듈성(Modularity): 각 계층은 독립적으로 설계하고 구현할 수 있습니다.

물리 계층을 설계하는 엔지니어가 HTTP 스펙을 알 필요가 없고, 웹 개발자가 전기 신호의 변조 방식을 이해할 필요가 없습니다.

대체 가능성(Replaceability): 특정 계층만 교체할 수 있습니다.

Ethernet을 Wi-Fi로 바꿔도 IP·TCP의 핵심 규약은 재사용할 수 있습니다. 다만 실제 환경의 주소 설정·MTU·손실·처리량 변화까지 없어지는 것은 아닙니다.

표준화(Standardization): 각 계층별로 명확한 인터페이스를 정의하면, 서로 다른 제조사의 장비와 소프트웨어가 호환됩니다.

아래 코드는 호출 인터페이스를 설명하는 모형입니다. 다음 홉과 MAC은 고정 문자열이며 ARP·라우팅 테이블 조회나 실제 프레임 송신을 수행하지 않습니다. b"Hello"의 5바이트를 40비트로 표시합니다.

layering_analogy.py
"""
계층화는 프로그래밍의 추상화와 동일한 원리입니다.
"""

class PhysicalLayer:
    """물리 계층: 비트를 전기 신호로 변환"""
    def send(self, bits):
        print(f"[물리] {len(bits) * 8} 비트를 전기 신호로 전송")

class DataLinkLayer:
    """데이터 링크 계층: MAC 주소로 로컬 전달"""
    def __init__(self, physical):
        self.physical = physical

    def send(self, frame, dst_mac):
        print(f"[데이터링크] MAC {dst_mac}로 프레임 전송")
        self.physical.send(frame)  # 하위 계층은 어떻게 전송하든 상관없음

class NetworkLayer:
    """네트워크 계층: IP 주소로 경로 결정"""
    def __init__(self, datalink):
        self.datalink = datalink

    def send(self, packet, dst_ip):
        # IP 계층은 물리 매체가 이더넷이든 Wi-Fi든 상관 안 함
        next_hop_ip = self.route(dst_ip)
        next_hop_mac = self.resolve_mac(next_hop_ip)
        print(f"[네트워크] IP {dst_ip}로 패킷 전송 (다음 홉: {next_hop_ip})")
        self.datalink.send(packet, next_hop_mac)

    def route(self, ip):
        return "192.168.1.1"  # 라우팅 테이블이 다음 홉을 결정

    def resolve_mac(self, ip):
        return "AA:BB:CC:DD:EE:FF"  # ARP/ND가 다음 홉의 L2 주소를 해결

# 이더넷 → Wi-Fi로 바꿔도 NetworkLayer 코드는 수정 불필요
phy = PhysicalLayer()
dl = DataLinkLayer(phy)
net = NetworkLayer(dl)
net.send(b"Hello", "10.0.0.1")

이 예시에서 NetworkLayer는 DataLinkLayer가 이더넷인지 Wi-Fi인지 모릅니다.

오직 send(frame, mac) 인터페이스만 알면 됩니다.

이것이 계층화의 핵심입니다.


소프트웨어 설계와의 연결

프로그래머라면 계층화가 이미 익숙할 것입니다.

소프트웨어 아키텍처에서 같은 원리를 매일 사용하고 있기 때문입니다.

네트워크 계층화소프트웨어 아키텍처
응용 계층Controller / UI
전송 계층Service / Use Case
네트워크 계층Repository / Adapter
데이터 링크·물리 계층드라이버 / 런타임 경계

이 표는 정확한 1:1 대응표가 아니라, 책임을 나누고 인터페이스 뒤에 구현을 숨긴다는 계층화의 목적을 보여줍니다.

하위 계층 구현이 바뀌어도 상위 계층이 같은 추상화 위에서 동작하도록 경계를 나누는 것이 핵심입니다.


편지 보내기 비유로 계층 이해하기

계층의 동작을 직관적으로 이해하기 위해, 회사에서 다른 회사로 공식 문서를 보내는 과정을 생각해 봅시다.

피어 관계와 실제 전달 경로

피어 관계와 실제 전달 경로

논리적 피어와 실제 전달 순서점선은 동일 계층의 논리적 관계입니다. 실선 화살표는 송신 측에서 하위 계층으로 내려간 데이터가 통신 경로를 거쳐 수신 측 상위 계층으로 올라가는 순서를 나타냅니다.송신 측수신 측응용응용전송전송IPIP링크·물리링크·물리중간 링크·라우터 경로는 생략
논리적 피어와 실제 전달 순서점선은 동일 계층의 논리적 관계입니다. 실선 화살표는 송신 측에서 하위 계층으로 내려간 데이터가 통신 경로를 거쳐 수신 측 상위 계층으로 올라가는 순서를 나타냅니다.송신 측수신 측응용응용전송전송IPIP링크·물리링크·물리중간 링크·라우터 경로는 생략

점선은 같은 계층의 규약 관계, 실선 화살표는 한 방향의 데이터 전달 순서입니다.

작성자가 문서의 내용을 작성합니다.

이것은 애플리케이션 계층에 해당합니다.

비서가 문서를 묶음으로 나누고, 순서와 담당자 정보를 붙여 분실되면 다시 요청할 수 있게 관리합니다.

이것은 전송 계층입니다.

우편실에서 회사 주소와 우편번호를 확인하고, 어느 지역의 우체국으로 넘길지 결정합니다.

이것은 네트워크 계층입니다.

링크와 물리 매체가 다음 홉까지 비트열을 전달합니다.

이것은 데이터 링크 계층과 물리 계층에 해당합니다.

중요한 것은 각 계층이 다른 계층의 내부 구현을 몰라도 정해진 인터페이스를 통해 동작한다는 점입니다.

애플리케이션은 링크의 전송 방식까지 알 필요 없고, 링크 계층은 HTTP 메시지의 의미를 해석하지 않습니다.

또 하나 주목할 점은 피어 간 논리적 통신입니다.

회사 A의 작성자와 회사 B의 수신자는 마치 직접 대화하는 것처럼 느끼지만, 실제로는 여러 계층을 거쳐 물리적으로 전달됩니다.

네트워크에서도 마찬가지입니다.

예를 들어 HTTP over TCP를 Ethernet으로 보내는 경로에서는 HTTP 모듈이 TCP·IP·링크 서비스를 통해 상대 HTTP 모듈과 통신합니다.


계층 간 서비스와 인터페이스

각 계층은 상위 계층에 서비스를 제공하고, 하위 계층의 서비스를 사용합니다.

이 관계를 명확히 이해하는 것이 중요합니다.

관계설명예시
서비스 제공하위 → 상위IP가 TCP에게 패킷 전달 서비스 제공
서비스 사용상위 → 하위TCP가 IP의 패킷 전달 서비스 사용
인터페이스계층 사이의 규약소켓 API (응용↔전송 계층 인터페이스)

프로그래밍에서 함수를 작게 나누고 각 함수가 하나의 책임만 가지게 설계하는 것처럼, 네트워크도 통신 과정을 계층으로 나누어 각 계층이 하나의 역할만 담당하게 만든 것입니다.

이것이 바로 관심사의 분리(Separation of Concerns)입니다.


문제 진단에서의 계층 활용

계층화의 실질적 가치는 문제를 격리할 수 있다는 데 있습니다.

아래는 도구와 eth0 인터페이스가 있는 Linux 환경의 명령 예시입니다. 목적지와 장치 이름은 환경에 맞게 바꿔야 하며 실행 결과를 관측한 것이 아닙니다.

layer_debugging.sh
# 1계층 (물리): 케이블이 연결되어 있나?
ip -br link show eth0
# UP, LOWER_UP = 인터페이스와 물리 링크 상태 확인

# 2계층 (데이터 링크): 같은 네트워크 내 통신이 되나?
arping -c 3 192.168.1.1
# 같은 L2 구간에서 ARP 응답 확인

# 3계층 (네트워크): 다른 네트워크로 도달 가능한가?
ping -c 4 8.8.8.8
# ICMP가 허용된 환경에서 IP 도달성 확인

# 4계층 (전송): 포트가 열려있나?
nc -zv example.com 443
# TCP 연결 가능 여부 (호스트명을 쓰면 DNS도 함께 필요)

# 7계층 (응용): HTTP 응답이 정상인가?
curl -v https://example.com
# HTTP 상태 코드와 헤더 확인

같은 시점·같은 목적지 IP의 ping은 성공하지만 curl이 실패한다면 DNS·TCP·TLS·HTTP 조건을 나누어 살펴볼 수 있습니다. 위 코드처럼 8.8.8.8과 example.com이 다르면 한쪽 ping 성공이 다른 목적지의 도달성을 보장하지 않습니다. 또한 ICMP와 TCP는 서로 다르게 필터링될 수 있습니다.

이렇게 아래에서 위로 하나씩 확인하면 문제의 위치를 더 빠르게 좁힐 수 있습니다.

다음 절에서는 이 계층화 원리를 가장 체계적으로 정리한 모델인 OSI 7계층을 살펴보겠습니다.