사전 종단 간 원칙
패턴

종단 간 원칙

gabury1고친 사람 github-actions[bot]

주고받는 데이터를 책임지는 일을 중간 장비가 아니라 양 끝에 맡기기로 하는 결정입니다. 중간은 데이터를 목적지 쪽으로 넘기는 일만 합니다. 빠진 것 없이 온전하게 닿았는지는 보내는 호스트와 받는 호스트가 직접 확인합니다.

쉽고 빠른 이해

데이터가 온전히 닿았는지 확인하는 일을 중간에 맡기지 않고 보내는 쪽과 받는 쪽이 직접 하는 설계입니다. 파일을 보냈다면 받은 쪽이 파일 전체를 훑어 보낸 것과 같은지 맞춰 봅니다.

중간에서 아무리 잘 챙겨도 양 끝은 다시 확인해야 합니다. 디스크를 잘못 읽었거나 보내는 프로그램이 데이터를 망가뜨린 경우는 중간 구간의 검사가 못 봅니다.

어떻게 도는가:

  1. 중간은 데이터를 목적지 쪽으로 넘기기만 합니다
  2. 양 끝이 도착한 데이터를 확인하고, 어긋나면 다시 보내 달라고 합니다
  3. 중간이 거드는 장치는 속도를 위한 보탬으로만 둡니다

대가로 양 끝의 소프트웨어가 할 일이 늘어납니다. 중간 장비는 문제를 알아채도 대신 고쳐 줄 수 없습니다.

상세

택배로 유리컵을 보냅니다. 구간마다 기사가 조심히 다뤘다고 해도, 컵이 성한지는 상자를 연 받는 사람만 압니다.

종단은 데이터를 처음 만든 쪽과 마지막으로 받는 쪽, 곧 통신하는 양 끝을 말합니다. 사이에 놓인 계층과 장비는 데이터를 넘기는 일만 맡습니다.

이 결정은 물음 하나에 답합니다. 빠진 데이터 다시 보내기·순서 맞추기·암호화 같은 기능을 어느 계층에 넣을 것인가입니다. 완전한 보장은 양 끝에만 둘 수 있습니다.

구간 검사가 못 보는 구간

파일 하나가 건너가는 길을 따라가 봅니다. 보내는 프로그램이 디스크에서 파일을 읽어 메모리에 담고 구간을 건너 보냅니다. 받는 쪽은 그것을 다시 메모리와 디스크를 거쳐 프로그램에 넘깁니다.

한 구간은 장비와 장비 사이 한 걸음입니다. 그 한 걸음마다 데이터가 뒤집혔는지 보는 것이 구간 검사입니다.

flowchart TD
    subgraph T["양 끝 검사가 덮는 범위"]
        A["보내는 프로그램"] --> B["디스크에서 읽기"]
        B --> C["보내는 쪽 메모리"]
        C --> D["첫 구간"]
        D --> E["중간 장비"]
        E --> F["둘째 구간"]
        F --> G["받는 쪽 메모리"]
        G --> H["디스크에 쓰기"]
        H --> I["받는 프로그램"]
        subgraph S["구간 검사가 덮는 범위"]
            D
            E
            F
        end
    end

안쪽 네모로 묶인 세 단계가 구간 검사가 덮는 범위입니다. 바깥 네모는 양 끝의 검사가 덮는 범위입니다. 안쪽 네모 밖에서 망가진 데이터는 구간 검사를 모두 통과하고도 어긋난 채 도착합니다.

안쪽 네모 밖에서 망가지는 경우는 드물지 않습니다. 디스크가 잘못 읽을 수 있고, 메모리에서 비트가 뒤집힐 수 있고, 보내는 프로그램이 버그로 엉뚱한 바이트를 넣을 수 있습니다.

이런 것까지 잡으려면 보낸 파일과 받은 파일을 끝에서 끝까지 맞춰 봐야 합니다. 체크섬이 그 도구입니다. 체크섬은 데이터 전체를 훑어 만든 짧은 값입니다. 두 값이 다르면 데이터가 달라진 것입니다.

보내는 쪽이 원본 파일에서 체크섬을 구해 함께 보내고, 받는 쪽이 도착한 파일에서 다시 구해 견줍니다. 이 검사는 파일이 지나온 모든 구간을 한 번에 덮습니다.

지금까지 중간 장비·구간 검사라 부른 것이 곧 양 끝의 프로그램 아래에 깔린 계층입니다. 그래서 양 끝의 검사는 아래 계층이 무엇을 하든 없앨 수 없습니다. 아래 계층이 같은 기능을 갖춰도 양 끝은 한 번 더 확인합니다. 데이터가 온전히 닿았다는 신뢰성을 끝내 책임지는 쪽은 양 끝입니다.

아래 계층이 거들 수 있는 몫

원칙은 아래 계층이 아무것도 하지 말라는 말이 아닙니다. 데이터가 자주 깨지는 구간이라면 그 구간에서 바로 다시 보내 양 끝이 나설 일을 줄입니다.

무선 구간을 생각해 봅시다. 구간에서 바로 복구하지 않으면 양 끝이 무엇이 잘못됐는지 알아챌 때까지 기다렸다가 처음부터 다시 보내게 됩니다. 한 구간의 재전송이 그 기다림을 줄여 줍니다.

flowchart TD
    subgraph S1["아래 계층이 한 구간에서 되돌릴 때"]
        A1["보내는 호스트"] -->|첫 구간| A2["중간 장비"]
        A2 -->|무선 구간| A3["받는 호스트"]
        A3 -. 무선 구간만 다시 .-> A2
    end
    subgraph S2["양 끝이 되돌릴 때"]
        B1["보내는 호스트"] -->|첫 구간| B2["중간 장비"]
        B2 -->|무선 구간| B3["받는 호스트"]
        B3 -. 처음부터 다시 .-> B1
    end

점선이 다시 보내는 범위입니다. 위는 한 걸음이고 아래는 지나온 길 전체입니다.

그래서 아래 계층의 장치는 속도를 위한 보탬으로 둡니다. 보장으로 여기지는 않습니다. 이 선을 넘으면 양 끝이 검사를 빼먹습니다. 그러면 아래 계층이 못 본 오류가 그대로 통과합니다.

완전한 보장은 언제나 양 끝에 둡니다. 아래 계층에는 그 보장을 대신하지 않는 선에서 속도를 위한 장치만 얹습니다.

이 결정이 치르는 대가

양 끝에 기능을 몰아 둔 대가는 네 가지입니다.

대가 무슨 일이 일어나나
양 끝이 일을 떠안는다 재전송·순서 맞추기·중복 걸러내기를 끝단 프로그램이 직접 한다
중간이 못 거든다 한 구간이 깨져도 양 끝이 알아챌 때까지 복구가 미뤄진다
같은 검사가 두 번 돈다 아래 계층의 검사와 양 끝의 검사가 겹친다
중간이 내용을 모른다 중간에서 대신 답하거나 줄여 보내는 최적화를 못 한다

표의 마지막 줄은 대가이자 이 결정이 노린 것입니다. 중간이 내용을 모르면 중간을 고치지 않고도 양 끝의 프로그램을 바꿀 수 있습니다.

인터넷이 이 원칙을 따른 결과

보낼 데이터는 한 덩어리로 가지 않고 작은 조각으로 잘려 갑니다. 그 조각이 패킷입니다. 인터넷에서 패킷을 나르는 일은 IP(Internet Protocol, 인터넷 프로토콜)가 맡습니다.

IP 는 패킷을 목적지 쪽으로 넘기기만 할 뿐 도착을 약속하지 않습니다. 잃어버려도, 순서가 바뀌어도, 두 번 닿아도 책임지지 않습니다. 이런 전달을 최선형 전달이라고 부릅니다.

빠진 것을 채우는 일은 그 위에서 TCP(Transmission Control Protocol, 전송 제어 프로토콜)가 합니다. TCP 는 양 끝의 호스트에서만 돕니다. 패킷을 다음 구간으로 넘기는 장비인 라우터는 TCP 가 무슨 일을 하는지 모릅니다.

flowchart TD
    subgraph H1["보내는 호스트"]
        A1["응용 프로그램"] --- A2["TCP"] --- A3["IP"]
    end
    subgraph R["라우터"]
        R1["IP"]
    end
    subgraph H2["받는 호스트"]
        C3["IP"] --- C2["TCP"] --- C1["응용 프로그램"]
    end
    A3 --> R1
    R1 --> C3
    A2 -. 양 끝끼리만 .-> C2

실선은 패킷이 실제로 지나는 길입니다. 점선은 양 끝의 TCP 끼리만 주고받는 확인입니다. 라우터 칸에는 IP 만 있고 TCP 가 없습니다.

덕분에 새 응용 프로그램을 만들 때 가운데를 고치지 않아도 됩니다. 양 끝의 프로그램만 바꾸면 됩니다. 인터넷이 넓어지는 동안 가운데를 갈아엎지 않아도 됐던 까닭이 여기에 있습니다.

원칙을 깨뜨리는 중간 장비

양 끝의 일에 끼어드는 중간 장비도 있습니다. 이런 장비를 미들박스라고 부릅니다.

NAT(Network Address Translation, 네트워크 주소 변환)은 지나가는 패킷의 주소를 바꿉니다. 양 끝이 서로의 주소를 그대로 안다는 전제가 깨집니다. 바깥에서 먼저 연결을 걸기도 어려워집니다.

flowchart TD
    N1["보내는 호스트 · 출발지 주소 A"] --> N2["NAT · 출발지를 B 로 바꿔 내보냄"]
    N2 --> N3["받는 호스트 · 출발지가 B 로 보인다"]

A 와 B 는 주소가 놓이는 칸을 가리키는 이름입니다. 받는 쪽은 보내는 호스트의 주소인 A 를 끝내 보지 못합니다.

방화벽은 중간에서 연결을 끊습니다. 프록시는 양 끝을 대신해 답하기도 합니다.

이 장비들은 그 연결이 어디까지 왔는지를 스스로 기억합니다. 양 끝에만 있어야 할 것을 중간이 들고 있는 셈입니다. 장비가 죽으면 양 끝이 멀쩡해도 연결이 함께 끊깁니다.

통신 밖으로 옮겨 간 같은 생각

메시지 앱의 종단 간 암호화는 같은 생각을 내용 보호에 옮긴 것입니다. 양 끝에서만 내용을 풀 수 있게 해 두면 중간을 몇 군데나 지나든 내용이 새지 않습니다.

저장에서도 같습니다. 파일을 여러 단계에 걸쳐 옮길 때는 각 단계의 검사를 믿는 대신 처음 만든 쪽과 마지막에 읽는 쪽에서 체크섬을 맞춰 봅니다.

관련 항목

양 끝이 직접 맡는 통신 기능

재전송 · 흐름 제어 · 혼잡 제어 · 신뢰성 · 체크섬 · 다중화

중간이 약속하는 전달 방식

최선형 전달 · 패킷 · 데이터그램 · 라우팅 · 패킷 손실 · MTU

이 원칙 위에 선 계층과 프로토콜

전송 계층 · TCP · UDP · QUIC · IP · 링크 계층

양 끝에 서는 참여자와 그 접점

호스트 · 소켓 · 포트 · IP 주소

이 원칙을 깨뜨리는 중간 장비

미들박스 · NAT · 방화벽 · 프록시 · 로드 밸런서 · CDN

같은 생각을 옮겨 쓴 기법

종단 간 암호화 · TLS · HTTPS · 해시

이 원칙과 나란히 인용되는 설계 지침

견고성 원칙 · 최소 권한 · 계층 · 상태 없는 전달

다른 이름: end-to-end principle · end-to-end argument · 종단간 원칙 · 엔드 투 엔드 원칙