네트워크 통신은 생각보다 많은 약속들이 함께 움직인다.
브라우저에서 서버로 요청을 보낸다고 할 때, 그 안에는 단순히 “데이터를 보낸다”는 말로 끝나지 않는 여러 단계가 숨어 있다.
웹에서는 HTTP가 필요하고, 데이터를 잘게 나누고 순서를 맞추는 데는 TCP가 관여하고, 목적지까지 찾아가는 데는 IP가 필요하고, 실제 같은 네트워크 안에서 장비끼리 주고받을 때는 이더넷 같은 규칙이 필요하다.
문제는 이걸 전부 한 번에 이해하려고 하면 머릿속이 너무 복잡해진다는 것이다.
그래서 네트워크는 보통 계층 구조로 나누어 이해한다.
계층 구조는 쉽게 말하면 역할 분담이다.
택배를 보낼 때를 생각해보면, 내가 직접 물류센터 위치를 계산하고, 배송 트럭을 고르고, 도로 상황을 판단하고, 받는 사람 집 앞까지 찾아가지는 않는다.
나는 물건을 포장하고 주소를 적는다.
택배사는 배송 경로를 정한다.
기사님은 실제로 물건을 이동시킨다.
받는 사람은 포장을 뜯고 내용물을 확인한다.
각 단계마다 관심사가 다르다.
네트워크도 비슷하다.
애플리케이션은 “무슨 요청을 보낼까?”에 관심이 있고,
전송 계층은 “이 데이터를 어떻게 안정적으로 전달할까?”에 관심이 있고,
네트워크 계층은 “어느 목적지까지 보내야 할까?”에 관심이 있고,
데이터 링크 계층은 “지금 연결된 네트워크 안에서 다음 장비에게 어떻게 넘길까?”에 관심이 있고,
물리 계층은 “0과 1을 어떤 신호로 바꿔 보낼까?”에 관심이 있다.
여기서 중요한 건, 각 계층은 자기 역할에 집중한다는 점이다.
HTTP가 케이블의 전기 신호까지 신경 쓰지 않고, IP가 웹 요청의 의미까지 해석하지 않는다.
각자 맡은 일을 하고, 아래 계층 또는 위 계층에 넘겨준다.
네트워크를 설명할 때 가장 자주 등장하는 모델이 OSI 7계층이다.
OSI 7계층은 네트워크 통신을 7개의 역할로 나누어 설명하는 모델이다.
위에서부터 보면 대략 이런 흐름이다.
처음 보면 외워야 할 표처럼 느껴진다.
나도 처음에는 “응표세전네데물” 같은 식으로 외우는 게 먼저 떠올랐다.
그런데 지금 단계에서 중요한 건 이름을 줄줄 외우는 게 아니다.
중요한 건 패킷이 이동할 때 각 계층이 어떤 역할을 맡는지다.
웹 요청을 예로 들면, 애플리케이션 계층에서는 HTTP 요청이 만들어진다.
전송 계층에서는 이 데이터를 어느 프로그램으로 보낼지, 어떻게 전달할지 다룬다.
네트워크 계층에서는 목적지 IP를 보고 어느 네트워크로 갈지 판단한다.
데이터 링크 계층에서는 같은 네트워크 안에서 다음 장비로 보내기 위한 정보를 붙인다.
물리 계층에서는 결국 이 데이터를 전기 신호, 빛 신호, 전파 같은 형태로 바꿔 보낸다.
즉 OSI 7계층은 실제 장비나 코드가 반드시 딱 7개로 나뉘어 동작한다는 뜻이라기보다,
복잡한 네트워크 통신을 역할별로 나누어 보기 위한 설명 도구에 가깝다.
실제 인터넷 통신을 설명할 때는 TCP/IP 4계층도 많이 사용한다.
TCP/IP 4계층은 보통 이렇게 나눈다.
OSI 7계층보다 덜 세분화되어 있다.
OSI의 애플리케이션, 표현, 세션 계층을 TCP/IP에서는 애플리케이션 계층으로 묶어서 보는 식이다.
그리고 데이터 링크 계층과 물리 계층도 네트워크 액세스 계층으로 묶어 설명하기도 한다.
개발자 입장에서는 TCP/IP 4계층이 더 현실적으로 느껴질 때가 많다.
예를 들어 웹 요청을 보낼 때 머릿속으로는 이렇게 생각할 수 있다.
“HTTP 요청이 만들어지고, TCP가 전달 방식을 잡고, IP가 목적지를 찾고, 이더넷이나 와이파이를 통해 실제 네트워크로 나간다.”
이 정도 흐름이 잡히면, 네트워크 문제를 볼 때도 어디쯤에서 문제가 생겼는지 나누어 생각할 수 있다.
HTTP 응답 코드 문제인지,
TCP 연결 문제인지,
IP 라우팅 문제인지,
같은 네트워크 안에서 전달이 안 되는 문제인지,
아니면 아예 물리적으로 연결이 안 된 문제인지 구분할 수 있게 된다.
계층 구조에서 핵심이 되는 개념이 캡슐화다.
캡슐화는 데이터를 아래 계층으로 내려보내면서, 각 계층이 자기 역할에 필요한 정보를 헤더로 붙이는 과정이다.
처음에는 애플리케이션에서 보낼 데이터가 있다.
예를 들어 브라우저가 서버에 이런 요청을 만든다고 해보자.
GET /index.html
이 데이터는 먼저 애플리케이션 계층의 데이터다.
하지만 이 데이터만 덜렁 보내면 네트워크는 처리할 수 없다.
받는 서버의 어떤 프로그램으로 보내야 하는지,
목적지 컴퓨터는 어디인지,
같은 네트워크 안에서 다음 장비는 누구인지,
실제로 신호로 어떻게 보낼지에 대한 정보가 필요하다.
그래서 아래 계층으로 내려가면서 포장이 계속 추가된다.
전송 계층에서는 TCP 헤더가 붙는다.
여기에는 출발지 포트, 목적지 포트 같은 정보가 들어간다.
네트워크 계층에서는 IP 헤더가 붙는다.
여기에는 출발지 IP, 목적지 IP 같은 정보가 들어간다.
데이터 링크 계층에서는 이더넷 헤더와 트레일러가 붙는다.
여기에는 같은 네트워크 안에서 다음 장비에게 전달하기 위한 MAC 주소 정보 등이 들어간다.
비유하면 이런 느낌이다.
처음 데이터는 편지 내용이다.
TCP는 “어느 방, 어느 담당자에게 전달할지” 적는 과정이고,
IP는 “어느 건물, 어느 지역으로 보낼지” 적는 과정이고,
이더넷은 “지금 이 구간에서 다음 배달 지점은 어디인지” 붙이는 과정이다.
계층을 내려갈수록 데이터는 점점 더 여러 겹으로 포장된다.
[HTTP 데이터]
[TCP 헤더 [HTTP 데이터]]
[IP 헤더 [TCP 헤더 [HTTP 데이터]]]
[이더넷 헤더 [IP 헤더 [TCP 헤더 [HTTP 데이터]]] 이더넷 트레일러]
여기서 중요한 건, 각 계층은 위에서 내려온 데이터를 자신의 payload처럼 본다는 점이다.
TCP 입장에서는 HTTP 데이터가 payload다.
IP 입장에서는 TCP 헤더와 HTTP 데이터 전체가 payload다.
이더넷 입장에서는 IP 헤더부터 그 안의 데이터 전체가 payload다.
즉, 아래 계층으로 갈수록 이전 계층의 데이터 전체를 감싸는 구조가 된다.
데이터를 받는 쪽에서는 반대 과정이 일어난다.
이 과정을 역캡슐화라고 한다.
수신 측 장비는 물리 계층에서 신호를 0과 1의 데이터로 해석한다.
그다음 데이터 링크 계층에서 이더넷 헤더를 확인한다.
“이 프레임이 나에게 온 게 맞나?”
“오류는 없나?”
“그다음에는 위의 어떤 프로토콜로 넘겨야 하나?”
이런 판단을 한 뒤, 데이터 링크 계층의 헤더와 트레일러를 제거하고 위로 넘긴다.
네트워크 계층에서는 IP 헤더를 확인한다.
“목적지 IP가 나인가?”
“위로는 TCP에게 넘기면 되나?”
이런 식으로 확인하고 IP 헤더를 제거한다.
전송 계층에서는 TCP 헤더를 확인한다.
“이 데이터는 몇 번 포트로 가야 하지?”
“순서는 맞나?”
“어느 애플리케이션으로 넘겨야 하지?”
그다음 TCP 헤더를 제거하고 애플리케이션으로 넘긴다.
마지막으로 애플리케이션은 HTTP 요청 내용을 해석한다.
즉, 보내는 쪽에서는 헤더를 하나씩 붙이고,
받는 쪽에서는 헤더를 하나씩 확인한 뒤 제거한다.
네트워크 장비 입장에서 보면 이런 시뮬레이션처럼 생각할 수 있다.
“일단 이더넷 헤더를 보자. 이 프레임을 내가 처리해야 하나?”
“맞다면 안쪽의 IP 패킷을 확인하자. 목적지 IP는 어디지?”
“내가 최종 목적지라면 TCP 정보를 확인하자. 몇 번 포트로 넘겨야 하지?”
“이제 애플리케이션에게 실제 데이터를 넘기자.”
이 흐름이 잡히면 캡슐화와 역캡슐화가 단순한 암기 용어가 아니라, 실제 데이터가 이동하는 방식으로 보이기 시작한다.
네트워크를 공부하다 보면 PDU라는 말도 나온다.
PDU는 Protocol Data Unit의 약자다.
쉽게 말하면, 각 계층에서 다루는 데이터 단위를 의미한다.
같은 데이터라도 어느 계층에서 바라보느냐에 따라 이름이 달라진다.
전송 계층에서는 보통 세그먼트라고 부르고,
네트워크 계층에서는 패킷이라고 부르고,
데이터 링크 계층에서는 프레임이라고 부른다.
물리 계층에서는 비트 단위로 다룬다.
처음에는 이 용어들이 헷갈린다.
“패킷이라고 했다가, 세그먼트라고 했다가, 프레임이라고 했다가 왜 이름이 계속 바뀌지?”
이건 데이터 자체가 완전히 다른 것이 되었다기보다, 어느 계층의 관점에서 보고 있느냐가 달라졌기 때문이다.
전송 계층이 TCP 헤더를 붙여서 바라보면 세그먼트다.
네트워크 계층이 IP 헤더를 붙여서 바라보면 패킷이다.
데이터 링크 계층이 이더넷 헤더와 트레일러를 붙여서 바라보면 프레임이다.
비유하면 같은 물건이라도 상황에 따라 이름이 달라지는 것과 비슷하다.
내가 물건을 살 때는 상품이고,
상자에 담겨 배송되면 택배이고,
물류센터에서는 화물이고,
받는 사람 입장에서는 주문한 물건이다.
대상은 이어져 있지만, 보는 위치와 역할이 다르기 때문에 이름이 달라진다.
지금까지의 내용을 하나의 흐름으로 다시 보면 이렇다.
클라이언트가 서버에 요청을 보내려고 한다.
애플리케이션 계층에서 HTTP 요청 데이터가 만들어진다.
전송 계층에서 TCP 헤더가 붙는다.
네트워크 계층에서 IP 헤더가 붙는다.
데이터 링크 계층에서 이더넷 헤더와 트레일러가 붙는다.
물리 계층에서 이 데이터는 신호로 바뀌어 네트워크를 타고 이동한다.
서버 쪽에서는 반대로 진행된다.
신호를 데이터로 해석하고,
프레임을 확인하고,
IP 패킷을 확인하고,
TCP 세그먼트를 확인하고,
마지막으로 HTTP 요청 내용을 애플리케이션이 읽는다.
여기서 중요한 건, 네트워크 통신을 하나의 덩어리로 보면 너무 복잡하지만, 계층으로 나누면 각 단계의 역할이 보인다는 점이다.
“요청이 서버로 간다”는 말 안에는 사실 여러 계층의 포장과 해석 과정이 들어 있다.
캡슐화는 보내는 쪽에서 포장하는 과정이고,
역캡슐화는 받는 쪽에서 포장을 뜯고 해석하는 과정이다.
이 관점이 생기면 앞으로 MAC 주소, IP 주소, 포트, TCP, HTTP 같은 개념을 볼 때도 조금 덜 흩어져 보인다.
각 개념이 어느 계층에서 어떤 판단을 하기 위해 필요한지 연결해서 볼 수 있기 때문이다.