OSI 참조 모델을 선명하게 그려 보기 ── HTTP 요청 1개를 7계층으로 해부한다

· 업데이트: · · OSI 참조 모델, TCP/IP, 네트워크, Wireshark, Ethernet, TCP, HTTP, C#, .NET, Socket, 기술 상담

수정 이력(5건, 최종 수정 2026년 09월 03일)

이 글에 적용한 변경 사항의 기록입니다. 보관해 둔 수정 전 버전은 DOI가 부여된 고정 URL에서 읽을 수 있습니다.

Codex 리뷰에 따라 상담·문의 링크에 /ko/ 로케일 접두를 붙였습니다. 본문의 기술적인 주장은 바꾸지 않았습니다.
permalink·저자 표기·지식 맵 래퍼·깨진 내부 링크 등 CI가 지적한 표시용 수정을 반영했습니다. 본문의 기술적인 주장은 바꾸지 않았습니다.
글 맨 앞에 「이 글의 지식 맵」 절을 추가했습니다. 본문에서 다루는 개념과 그 관계를 요약·그림·상세 페이지 링크로 정리한 것입니다. 본문의 주장은 바꾸지 않았습니다.
외부 리뷰(1283건)에 대응해 본문을 갱신했습니다. 개별 변경 내용은 아래 이력을 참조해 주십시오.
맨 앞에 대상 독자와, 직접 따라 할 때 필요한 것을 추가했습니다. 라우터를 넘을 때 L2만 다시 붙고 L3의 목적지 IP는 바뀌지 않음을 보이는 그림과, Wireshark의 패킷 상세 창과 바이트열 창의 대응을 보이는 그림을 추가하고, 인접해 있던 각주를 각각이 뒷받침하는 주장 바로 뒤로 나누어 배치했습니다.
최초 공개
이 글을 인용하기(DOI(등록된 아카이브): 10.5281/zenodo.21635380)

아래 DOI는 이전에 등록된 아카이브를 가리키며 현재 본문과 다를 수 있습니다. 현재 본문을 참조할 때는 이 페이지의 URL을 사용하세요.

Go Komura (2026). 「OSI 참조 모델을 선명하게 그려 보기 ── HTTP 요청 1개를 7계층으로 해부한다」. 합동회사 코무라소프트. https://comcomponent.com/ko/blog/osi-model-packet-anatomy/

DOI(등록된 아카이브)
10.5281/zenodo.21635380
DOI(마지막 등록 버전)
10.5281/zenodo.21635381

OSI 참조 모델은 네트워크 입문서라면 반드시 맨 앞에 나오는데, 「7계층 이름은 말할 수 있지만, 솔직히 그림이 잡히지 않는다」는 말을 자주 듣습니다. 물리 계층·데이터 링크 계층·네트워크 계층…… 하고 외워 두었는데도, 자신이 작성 중인 C# 코드나 눈앞에서 일어나는 통신 장애와 그 7단 그림이 어떻게 이어지는지 모르는 채로 남기 쉽습니다.

이 글은 Windows 앱 개발자와 네트워크 입문자에게 맞춰 썼습니다. C# 코드 조각과 Windows 명령(ping / tracert / arp 등)이 나오지만, 패킷이나 프로토콜에 대한 사전 지식은 필요하지 않습니다. 읽기만 해도 따라갈 수 있게 구성했지만, 직접 손으로 확인해 보고 싶다면 .NET SDK(dotnet 명령)와 Wireshark가 동작하는 환경이 있으면 글의 내용을 그대로 자신의 화면에서 재현할 수 있습니다.

이 블로그에서는 지금까지 TCP에서 Send한 단위마다 Receive할 수 있다는 오해나 TCP 재전송으로 산업용 카메라 통신이 멈추는 원인과 분리처럼 L4(트랜스포트 계층)의 동작을 다룬 글을 써 왔지만, 그 토대가 되는 「계층」이라는 사고방식 자체를 정리한 글은 없었습니다.

이 글의 접근은 단순합니다. HTTP GET 요청 1개를 실어 나르는 Ethernet 프레임을 실제로 1개 만들어, 바깥쪽부터 차례로 해부합니다. 7계층은 개념도 안의 이야기만이 아니라, 네트워크를 흐르는 171바이트 안에 물리적으로 중첩되어 존재합니다. 그것을 hex dump와 Wireshark로 직접 눈으로 보고 나면, OSI 참조 모델은 암기 대상이 아니게 됩니다.

이 글에 나오는 코드는 빌드·실행할 수 있는 샘플 세트(프레임 조립·해부 라이브러리, Wireshark에서 열 수 있는 pcap 내보내기, 단위 테스트)로 GitHub에 공개해 두었습니다.

osi-model-packet-anatomy - komurasoft-blog-samples (GitHub)

1. 먼저 결론

  • OSI 참조 모델은 「구현」이 아니라 「어휘」입니다. 현실 인터넷에서 동작하는 것은 TCP/IP 프로토콜 스위트(실질 4계층)입니다.1 OSI의 7계층 프로토콜 자체는 보급 경쟁에서 밀려 거의 쓰이지 않은 채 끝났습니다.2 살아남은 것은 「L2에서 원인을 분리한다」「L7 문제다」라는 공통 언어로서의 모델입니다.
  • 7계층은 바이트열로서 물리적으로 중첩되어 있습니다. HTTP GET을 실어 나르는 프레임이라면, 맨 앞 14바이트가 Ethernet 헤더(L2), 다음 20바이트가 IPv4 헤더(L3), 다음 20바이트가 TCP 헤더(L4), 나머지가 HTTP 텍스트(L7)입니다. 각 계층의 헤더가 앞 계층 바로 뒤에서 시작되는 모습은 본문의 hex dump와 Wireshark에서 그대로 확인할 수 있습니다.
  • 여러분이 작성하는 C# 코드가 직접 쓰는 것은 L7 바이트열뿐입니다. Socket.Send에 넘기는 것은 애플리케이션 데이터만이며, TCP·IP 헤더는 OS의 프로토콜 스택이, Ethernet 헤더와 전기 신호는 NIC 쪽이 붙입니다. 그래서 HttpClient / SslStream / Socket 중 무엇을 쓸지는 「어느 계층부터 아래를 OS에 맡길지」의 선택이 됩니다.3
  • L5(세션 계층)·L6(프레젠테이션 계층)은 현실 스택에는 독립된 계층으로 존재하지 않습니다. TLS나 문자 인코딩이 그 역할의 일부를 맡고 있지만, TCP/IP 모델에서는 묶어 애플리케이션 계층입니다. 「L6이 무엇인지 모르겠다」는 여러분의 탓이 아니라, 모델과 현실이 어긋난 지점이기 때문입니다.1
  • OSI 모델이 실무에서 정말 도움이 되는 것은 장애 원인 분리와 대화의 순간입니다. 「ping은 통하는데 HTTP가 실패한다」면 L3까지는 살아 있으므로 L4 이상을 의심한다, 처럼 계층 어휘로 범위를 좁히는 일은 네트워크·인프라·앱 개발자 사이에서 통하는 공통 언어가 됩니다.

그림의 실선은 항상 성립하는 관계, 점선은 조건이 붙는 관계입니다(성립 조건은 상세 페이지의 관계별 설명에 적혀 있습니다). 관계 전체 목록(총 20건, 근거와 확신도 포함)과 주요 개념의 정의는 지식 맵 상세 페이지에 정리되어 있습니다(일본어). 데이터: JSON-LD / Turtle

2. OSI 참조 모델은 왜 암기에서 끝나는가

많은 해설이 이런 표에서 시작합니다.

계층 이름 설명
L7 애플리케이션 계층 애플리케이션에 통신 서비스를 제공한다
L6 프레젠테이션 계층 데이터의 표현 형식을 변환한다
L5 세션 계층 통신의 시작·종료를 관리한다
L4 트랜스포트 계층 신뢰성 있는 데이터 전송을 제공한다
L3 네트워크 계층 경로 선택과 주소 지정을 수행한다
L2 데이터 링크 계층 인접 노드 간 데이터 전송을 수행한다
L1 물리 계층 비트를 전기 신호로 변환한다

이 표는 맞습니다만, 모든 행이 추상적인 말로 쓰여 있어 읽어도 그림이 잡히지 않습니다. 「데이터의 표현 형식을 변환한다」는 말을 듣고, 자기 코드의 어느 줄인지 아는 사람은 없을 것입니다.

하나 더, 솔직히 적어 두어야 할 역사적 사실이 있습니다. OSI 참조 모델은 ISO(국제표준화기구)와 ITU-T가 정한 표준(ISO/IEC 7498-1, ITU-T X.200)이며, 원래는 7계층 각각에 대응하는 OSI 프로토콜 군과 세트로 된 원대한 구상이었습니다.2 그러나 1990년대 보급 경쟁에서, 이미 동작하는 구현이 있던 TCP/IP가 사실상의 표준이 되었고, OSI 프로토콜 자체는 거의 쓰이지 않은 채 끝났습니다. 인터넷의 기초를 정한 RFC 1122는 링크 계층·인터넷 계층(IP)·트랜스포트 계층·애플리케이션 계층이라는 실질 4계층으로 세계를 설명하며, L5·L6에 해당하는 독립된 계층은 없습니다.1

즉, 지금 우리가 OSI 참조 모델을 배우는 의미는 「이대로 구현되어 있기 때문」이 아니라, 「계층으로 생각한다」는 도구와 장애 원인 분리의 공통 어휘를 얻기 위해서입니다. 이 전제에 서면 「L5와 L6의 실물을 찾을 수 없다」고 고민할 필요가 없어지고, 전체 그림이 한눈에 들어옵니다.

3. 본론: HTTP 요청 1개를 해부한다

앞부분은 여기까지로 하고, 실물을 봅니다. 다음 hex dump는 GET /index.html HTTP/1.1이라는 HTTP 요청 1개를 실어 나르는 Ethernet 프레임(전체 171바이트)입니다. 맨앞에서 소개한 샘플 코드가 조립한 것이며, IPv4 헤더 checksum과 TCP checksum도 올바르게 계산되어 있어 Wireshark로 열어도 정상 패킷으로 표시됩니다.

0000  02 00 00 00 00 01 02 00  00 00 00 02 08 00 45 00  ..............E.
0010  00 9d 12 34 40 00 40 06  3b 99 c0 00 02 0a c6 33  ...4@.@.;......3
0020  64 50 cb 84 00 50 00 00  03 e8 00 00 07 d0 50 18  dP...P........P.
0030  ff ff a3 05 00 00 47 45  54 20 2f 69 6e 64 65 78  ......GET /index
0040  2e 68 74 6d 6c 20 48 54  54 50 2f 31 2e 31 0d 0a  .html HTTP/1.1..
0050  48 6f 73 74 3a 20 65 78  61 6d 70 6c 65 2e 63 6f  Host: example.co
0060  6d 0d 0a 55 73 65 72 2d  41 67 65 6e 74 3a 20 4b  m..User-Agent: K
0070  6f 6d 75 72 61 53 6f 66  74 44 65 6d 6f 2f 31 2e  omuraSoftDemo/1.
0080  30 0d 0a 41 63 63 65 70  74 3a 20 74 65 78 74 2f  0..Accept: text/
0090  68 74 6d 6c 0d 0a 43 6f  6e 6e 65 63 74 69 6f 6e  html..Connection
00a0  3a 20 63 6c 6f 73 65 0d  0a 0d 0a                 : close....

오른쪽 ASCII 표시를 보면, 중간(offset 0x36)부터 GET /index.html HTTP/1.1이라는 사람이 읽을 수 있는 텍스트가 시작되는 것을 알 수 있습니다. 그렇다면 그 앞의 54바이트는 무엇인가. 샘플의 해부기에 넣으면 이렇게 보고됩니다.

[ L2 Ethernet II | offset   0 |  14 bytes | dst=02:00:00:00:00:01 src=02:00:00:00:00:02 type=0x0800 (IPv4)
  [ L3 IPv4        | offset  14 |  20 bytes | 192.0.2.10 -> 198.51.100.80 TTL=64 proto=6 (TCP) checksum=OK
    [ L4 TCP         | offset  34 |  20 bytes | 52100 -> 80 [Psh, Ack] seq=1000 win=65535
      [ L7 HTTP        | offset  54 | 117 bytes | GET /index.html HTTP/1.1

이게 이 글에서 가장 봐 주었으면 하는 그림입니다. 포인트는 세 가지입니다.

  1. 각 계층은 「앞 계층의 바로 뒤」에서 시작합니다. Ethernet 헤더는 0~13번째 바이트, IPv4 헤더는 14~33번째 바이트, TCP 헤더는 34~53번째 바이트, HTTP는 54번째 바이트부터입니다. 계층은 개념상의 분류가 아니라, 프레임 맨 앞부터의 바이트 범위로 가리킬 수 있습니다.
  2. 안쪽 계층은 바깥 계층의 「payload(적재물)」입니다. Ethernet에서 보면 IPv4 이후는 그냥 적재물이고, 내용이 TCP인지는 관여하지 않습니다. IP에서 보면 TCP 이후가 적재물, TCP에서 보면 HTTP가 적재물입니다. 각 계층은 자기 헤더만 읽고, 적재물에는 손대지 않은 채 위로 넘깁니다. 이 「관여하지 않는」 구조야말로 캡슐화입니다.
  3. L1(물리 계층)과 L5·L6은 이 그림에 등장하지 않습니다. L1은 이 바이트열을 전기 신호·빛·전파로 변환하는 일이므로 dump에는 비치지 않고, L5·L6은 앞 장에서 본 대로 평문의 HTTP/1.1에서는 독립된 실체가 없습니다. 「7계층 중 dump에 비치는 것은 4계층」──이것이 현실의 모습입니다.

반복하지만, 이것은 HTTP가 특별해서가 아닙니다. 데이터베이스 연결이든, gRPC든, 산업용 카메라의 GigE Vision이든, Ethernet 위의 TCP/IP 통신이라면 모든 패킷이 이 같은 중첩 구조를 합니다. 바뀌는 것은 L7 적재물의 내용뿐입니다.

4. L2 데이터 링크 계층 ── 「옆」까지 전달한다

해부 결과를 바깥쪽부터 차례로 봅니다. 맨 앞 14바이트가 Ethernet II 헤더입니다.

02 00 00 00 00 01   목적지 MAC 주소(6바이트)
02 00 00 00 00 02   출발지 MAC 주소(6바이트)
08 00               EtherType = 0x0800(적재물은 IPv4)

L2의 일은 「같은 네트워크 세그먼트 안의 옆 노드까지 전달하는 것」입니다. 목적지 지정에는 MAC 주소를 사용합니다. MAC 주소는 NIC에 할당된 식별자이며, 원칙적으로 라우터를 넘어 상대에게 도달하지는 않습니다. 사무실 PC에서 Web 서버로 보내는 프레임의 목적지 MAC은 Web 서버가 아니라 기본 게이트웨이(라우터)의 MAC이 됩니다.

여기서 많은 사람이 한 번은 의문을 갖는 것이 「IP 주소가 있는데 왜 MAC 주소가 필요한가」입니다. 답은 계층의 역할 분담에 있습니다. IP 주소는 「최종 목적지」를 가리키는 주소이고, MAC 주소는 「다음 1구간을 실어 나를 상대」를 가리키는 수신인입니다. 택배에 비유하면, IP 주소는 송장의 배송지 주소이고, MAC 주소는 「다음 중계 센터」의 이름입니다. 송장(L3)은 끝까지 바뀌지 않지만, 수신인(L2)은 구간마다 다시 붙습니다. IP 주소에서 인접 노드의 MAC 주소를 알아내는 것이 ARP이며, arp -a 명령으로 PC가 기억하고 있는 대응표를 확인할 수 있습니다.

장비로 말하면 L2를 보고 전달하는 것이 스위치(스위칭 허브)입니다. 스위치는 목적지 MAC 주소만 보고 포트를 고르며, 적재물의 IP 주소는 보지 않습니다.

5. L3 네트워크 계층 ── 세상 끝까지 전달한다

다음 20바이트가 IPv4 헤더입니다. 주요 필드를 뽑아 냅니다.

45          버전=4, 헤더 길이=5워드(20바이트)
00 9d       전체 길이 157바이트(IP 헤더+TCP+HTTP. Ethernet의 14바이트는 포함하지 않음)
40 06       TTL=64, 프로토콜=6(적재물은 TCP)
3b 99       헤더 checksum
c0 00 02 0a 출발지 IP   192.0.2.10
c6 33 64 50 목적지 IP     198.51.100.80

L3의 일은 「라우터를 몇 대를 거치더라도 최종 목적지의 호스트까지 전달하는 것」입니다. L2가 1구간(1홉)만 나르는 데 비해, L3의 목적지 IP 주소는 통신의 처음부터 끝까지 바뀌지 않습니다. 라우터는 받은 패킷의 목적지 IP를 보고 「다음은 어느 라우터에 넘길지」를 판단하고, L2 헤더를 다시 붙여 내보냅니다.

이 「L2만 다시 붙는」 동작을 그림으로 나타내면 다음과 같습니다. 구간마다 바뀌는 것은 목적지·출발지 MAC 주소(L2)뿐이며, 목적지 IP 주소(L3)는 3구간 모두 같은 값입니다.

목적지 MAC: 라우터1목적지 IP: 198.51.100.80목적지 MAC: 라우터2목적지 IP: 198.51.100.80목적지 MAC: Web 서버목적지 IP: 198.51.100.80송신 PC192.0.2.10라우터1라우터2Web 서버198.51.100.80

그림 1: L2의 수신인은 구간마다 다시 붙고, L3의 목적지 IP는 끝까지 바뀌지 않는다. TTL은 라우터를 넘을 때마다 1씩 줄어든다.

TTL(Time To Live)은 이 「라우터를 넘는」 동작을 눈에 보여 주는 필드입니다. 라우터를 1개 넘을 때마다 1씩 줄어들고, 0이 된 패킷은 폐기되어 송신 측에 오류가 돌아갑니다. tracert(Windows의 경로 조사 명령)는 TTL을 일부러 1, 2, 3……으로 늘리며 보냄으로써 중간 라우터를 하나씩 드러냅니다. 즉 tracert 출력에 늘어선 한 줄 한 줄이 「L3의 홉」의 실물입니다.

6. L4 트랜스포트 계층 ── 어느 프로세스에 전달할 것인가

다음 20바이트가 TCP 헤더입니다.

cb 84       출발지 포트 52100
00 50       목적지 포트 80
00 00 03 e8 시퀀스 번호
00 00 07 d0 확인 응답 번호
50 18       헤더 길이=5워드, 플래그 [PSH, ACK]
ff ff       윈도우 크기
a3 05       checksum

L3까지로 패킷은 목적지의 「호스트(머신)」에 도착했습니다. 그러나 그 머신에서는 Web 서버도 데이터베이스도, 그 밖의 수많은 프로세스도 동시에 통신하고 있습니다. L4의 일 중 하나는 포트 번호를 써서 「어느 프로세스(의 소켓)에 넘길지」를 나누는 것입니다. 목적지 포트 80은 「HTTP 서버가 대기하는 번호」라는 관례이며, OS는 이 번호를 보고 해당 프로세스의 수신 버퍼에 데이터를 넣습니다.

TCP의 또 하나의 큰 일은 시퀀스 번호·확인 응답·재전송·흐름 제어에 의한 「신뢰성 있는 바이트 스트림」의 제공입니다.4 이 부근의 동작과 다루는 법은 각각 글 한 편이 될 깊이이므로, TCP의 메시지 경계 이야기와 TCP 재전송 이야기에 맡깁니다. 여기서는 「L4부터 위는 더 이상 네트워크의 형태가 아니라, 프로세스와 프로세스를 잇는 파이프로 보인다」는 감각을 잡아 주십시오.

하나, 모델의 이상과 현실의 어긋남을 보여 주는 흥미로운 사실이 있습니다. TCP의 checksum은 TCP 세그먼트 단일이 아니라, L3 정보(출발지·목적지 IP 주소)를 늘어놓은 「의사 헤더」를 앞에 붙여 계산한다고 정의되어 있습니다.4 계층이 깔끔하게 독립되어 있다면 L4 계산에 L3 주소가 섞일 리는 없습니다. OSI 참조 모델은 어디까지나 정리를 위한 지도이고, 현실의 프로토콜은 필요에 따라 계층을 가로지른다는 좋은 예입니다.

7. L5·L6 ── 모델과 현실이 어긋나는 지점

자, 해부도에는 L5(세션 계층)·L6(프레젠테이션 계층)이 없었습니다. 여기가 「OSI를 모르겠다」고 느끼는 가장 큰 원인이므로, 분명히 적습니다.

L5·L6은 현실의 TCP/IP 스택에는 독립된 계층으로 존재하지 않습니다. RFC 1122의 인터넷 모델에서는 TCP보다 위는 모두 「애플리케이션 계층」입니다.1 OSI 모델이 가정한 역할은 현실에서는 다음과 같이 흩어져 흡수되어 있습니다.

OSI의 가정 현실에서 흡수한 장소의 예
L5: 대화(세션)의 수립·관리 TLS의 세션·핸드셰이크, HTTP의 Cookie/토큰, 앱 자신의 로그인 관리
L6: 데이터 표현의 변환·암호화 TLS에 의한 암호화, 문자 인코딩(UTF-8), JSON 등의 직렬화 형식

예를 들어 HTTPS의 경우, TCP(L4) 위에 TLS가 끼어들고, HTTP는 그 안을 흐릅니다. 「TLS는 몇 계층인가?」는 시험 문제로 만들고 싶어지는 지점이지만, 정답을 하나로 정하는 것에는 실무상 의미가 없습니다. L4 위이면서 L7 아래에서, 세션 관리(L5에 해당하는 역할)와 암호화(L6에 해당하는 역할)를 함께 가진다 ── 그렇게 설명할 수 있는 편이 「L6입니다」라고 바로 답할 수 있는 것보다 중요합니다.

.NET 코드에서는 이 관계가 그대로 클래스를 겹쳐 쓰는 방식에 나타납니다. TcpClient.GetStream()으로 얻은 L4 스트림을 SslStream으로 감싸면, Write한 평문은 TLS 레코드로 암호화된 뒤에 TCP로 넘어갑니다.5

using var client = new TcpClient("example.com", 443);
// L4의 바이트 스트림을 TLS(L5/L6 해당)로 감싼다
using var ssl = new SslStream(client.GetStream());
await ssl.AuthenticateAsClientAsync("example.com");
// 여기에 쓰는 것은 L7(HTTP)의 평문. 암호화는 SslStream의 일
await ssl.WriteAsync(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n..."));

스트림을 스트림으로 감싸는 이 구도는 캡슐화의 코드 판입니다. Wireshark에서 443번 포트 통신을 보면 TCP payload가 Application Data라는 불투명한 덩어리로만 보이며, 「L7이 L6에 해당하는 포장에 들어갔다」는 것을 반대 방향에서 확인할 수 있습니다.

8. 여러분의 C# 코드는 어느 계층을 만지고 있는가

여기까지를 Windows 앱 개발자의 도구에 대응시킵니다.

계층 실물의 예 .NET API의 예 누가 헤더를 붙이는가
L7 애플리케이션 HTTP, gRPC, 자체 프로토콜 HttpClient, Grpc.Net.Client, 직접 만든 전문 조립 여러분의 코드 / 라이브러리
(L5/L6 해당) TLS, 직렬화, 문자 코드 SslStream, JsonSerializer, Encoding 라이브러리
L4 트랜스포트 TCP, UDP Socket, TcpClient, UdpClient(헤더 생성은 OS) OS의 프로토콜 스택
L3 네트워크 IP, 라우팅, ICMP Ping, NetworkInterface(참조만) OS의 프로토콜 스택
L2 데이터 링크 Ethernet, Wi-Fi, ARP (일반적인 앱에서는 직접 만지지 않음) NIC 드라이버 / NIC
L1 물리 전기 신호, 빛, 전파 ── NIC / 케이블 / 공간

이 표에서 두 가지를 읽을 수 있습니다.

첫째, API 선택은 「어느 계층부터 아래를 맡길지」의 선택입니다. HttpClient를 쓰면 L7 조립까지 맡길 수 있고, Socket을 쓰면 L7은 전부 직접 쓰게 됩니다. 반대로, 일반적인 Windows 앱에서 L3 이하를 직접 조작하는 일은 거의 없습니다(raw 소켓에는 관리자 권한이나 강한 제약이 붙습니다).

둘째, Socket.Send에 넘기는 것은 L7 바이트열「뿐」입니다. 샘플의 Part 2는 루프백으로 실제로 HTTP 요청을 보내는 데모이지만, 애플리케이션이 준비하는 것은 60바이트의 HTTP 텍스트뿐이며, 제3장에서 본 54바이트분의 헤더들은 OS와 NIC가 붙입니다. 여러분의 앱 네트워크 코드는 실은 7계층 중 가장 안쪽 적재물을 만드는 일만 한다 ── 이것이 OSI 모델과 여러분의 코드의 정확한 위치 관계입니다.

using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
await socket.ConnectAsync(IPAddress.Loopback, port);

byte[] request = Encoding.ASCII.GetBytes(
    $"GET / HTTP/1.1\r\nHost: localhost:{port}\r\nConnection: close\r\n\r\n");

// 넘기는 것은 L7 바이트열뿐. TCP/IP/Ethernet 헤더는
// 이 호출 너머(OS와 NIC)에서 붙는다
int sent = await socket.SendAsync(request);

9. 손으로 확인한다 ── 프레임을 조립해 Wireshark에서 연다

「읽어서 알았다」를 「보고 알았다」로 바꾸기 위해, 샘플 코드의 실행 방법을 소개합니다.

샘플의 중심은 제3장의 프레임을 L7에서 L2로 감싸 가는 순서 그대로 조립하는 SampleFrameBuilder입니다. 송신 시 각 계층에서 일어나는 일이 그대로 메서드 순서입니다.

public static byte[] BuildHttpGetFrame()
{
    // L7: 애플리케이션이 Send에 넘기는 것은 이 바이트열「뿐」
    byte[] http = Encoding.ASCII.GetBytes(HttpRequest);

    // L4: OS의 프로토콜 스택이 TCP 헤더를 앞에 붙인다
    byte[] tcp = WrapInTcp(http);

    // L3: 여기에 IPv4 헤더를 앞에 붙인다
    byte[] ip = WrapInIpv4(tcp);

    // L2: NIC 드라이버 앞에서 Ethernet 헤더를 앞에 붙인다
    return WrapInEthernet(ip);
}

데모를 실행하면 hex dump와 중첩 뷰 표시에 더해, 이 프레임을 pcap 파일로 내보냅니다.

dotnet run --project samples/Demo

생성된 sample-http-get.pcap을 Wireshark에서 열어 보십시오.6 가운데 창(패킷 상세 창)에 Ethernet II / Internet Protocol Version 4 / Transmission Control Protocol / Hypertext Transfer Protocol의 4행이 세로로 늘어서고, 하나를 클릭하면 아래 창(바이트열 창)의 해당 바이트 범위만 하이라이트됩니다. 이 대응 관계를 그림으로 나타내면 다음과 같으며, 제3장의 중첩 뷰와 같은 것을 업계 표준 도구가 보여 주는 셈입니다.

바이트열 창 ── 하이라이트되는 범위패킷 상세 창 ── 위에서부터 차례로 4행클릭클릭클릭클릭0~13번째 바이트14~33번째 바이트34~53번째 바이트54~170번째 바이트Ethernet IIInternet Protocol Version 4Transmission Control ProtocolHypertext Transfer Protocol

그림 2: 상세 창의 행과 바이트열 창의 대응. 행을 고르면 그 계층 헤더가 차지하는 바이트 범위만 아래에서 반전 표시된다.

여기까지 확인했다면 다음은 실제 트래픽입니다. Wireshark에서 캡처를 시작하고, 필터에 tcp.port == 443 정도를 넣은 뒤 브라우저로 아무 사이트나 열면, 진짜 통신이 전부 이 중첩으로 이루어져 있음을 확인할 수 있습니다.

덧붙여, 해부기 쪽(PacketDissector) 구현에도 실무적인 배울 점을 넣어 두었습니다. 예를 들어 IPv4 적재물을 잘라 낼 때는 수신 버퍼의 남은 전부가 아니라 헤더의 TotalLength 필드를 신뢰하고 잘라 낼 필요가 있습니다. Ethernet에는 최소 프레임 길이(60바이트)가 있어, 짧은 패킷에는 의미 없는 패딩이 끝에 붙기 때문입니다. 「바깥 계층의 사정(패딩)을 안쪽 계층의 길이 정보로 걷어 낸다」── 이것도 계층의 역할 분담이 구현에 나타나는 한 예이며, 단위 테스트로 검증하고 있습니다.

10. 실무에서 OSI 모델을 쓴다 ── 계층 어휘로 원인을 분리한다

맨앞에서 「OSI는 어휘로 살아남았다」고 적었습니다. 그 어휘가 실제로 도움이 되는 장면을 두 가지 듭니다.

장애 원인 분리. 「서버에 연결되지 않는다」는 보고는 계층 어휘로 옮기면 체계적으로 범위를 좁힐 수 있습니다. 아래부터 차례로 확인하는 것이 정석입니다.

확인 사용하는 것 살아 있다고 알 수 있는 계층
링크 램프·Wi-Fi 연결 표시 육안 L1~L2
동일 세그먼트의 게이트웨이에 ping ping 192.168.x.1 자기 주변의 L3
상대 호스트에 ping ping <상대> 경로 전체의 L3
상대 포트에 TCP 연결 Test-NetConnection <상대> -Port 443 L4(+중간 방화벽)
HTTP 요청을 보낸다 curl -v나 앱 본체 L7(+TLS)

예를 들어 「ping은 통하는데 Test-NetConnection이 실패한다」면 L3까지는 정상이고, L4 포트를 막는 무언가(서비스 중지, 방화벽, 포트 번호 설정 실수)로 혐의가 좁혀집니다. 「TCP 연결은 되는데 HTTP가 400을 반환한다」면 네트워크가 아니라 L7(요청 내용)의 문제입니다. 무작정 케이블을 뽑았다 꽂는 대신 어느 계층까지 살아 있는지를 한 단계씩 확정한다 ── 이것이 OSI 모델의 실무에서의 쓰임입니다.

대화의 공통 언어. 「L2 문제 같으니 스위치 포트를 봐 달라」「그건 L7 이야기이니 앱 팀에」라는 대화는 네트워크 담당·인프라 담당·앱 개발자 사이에서 정확하게 통합니다. 계층 번호는 책임 분계점을 짧게 말하기 위한 업계 공통 좌표입니다.

11. 흔한 오해를 바로잡는다

마지막으로 OSI 참조 모델 주변에서 자주 보는 오해를 모아 바로잡습니다.

  • 「인터넷은 OSI의 7계층으로 동작한다」 ── 동작하지 않습니다. 구현되어 있는 것은 TCP/IP(실질 4계층)이며, OSI는 설명과 대화를 위한 참조 모델입니다.1
  • 「TCP/IP의 4계층과 OSI의 7계층은 깔끔하게 대응한다」 ── L5~L7의 대응은 본질적으로 모호합니다. 「TCP/IP의 애플리케이션 계층 = OSI의 L5+L6+L7」이라는 표를 자주 보지만, 제7장에서 본 대로 L5·L6의 역할은 현실에서는 TLS나 직렬화 형식에 흩어져 있습니다.
  • 「TLS는 제6계층(또는 제5계층)의 프로토콜이다」 ── 하나의 계층으로 확정할 의미는 없습니다. L4 위, L7 아래에서 L5·L6에 해당하는 역할을 함께 가진다, 가 정확한 설명입니다.
  • 「포트 번호를 보면 프로토콜을 알 수 있다」 ── 포트 80이라고 해서 HTTP인 것은 아닙니다. 포트 번호는 관례이며, 실제로 무엇이 흐르는지는 payload를 보기 전까지 알 수 없습니다. 샘플의 해부기가 HTTP 판정을 포트 번호가 아니라 내용의 앞부분 문자열로 하는 것은 이 때문입니다.
  • 「스위치는 L2, 라우터는 L3 장비다」 ── 출발점으로는 맞지만, 현실에는 L3 스위치, L4에서 나누는 로드 밸런서, L7을 검사하는 WAF처럼 여러 계층에 걸친 장비가 흔히 있습니다. 「이 장비가 어느 계층까지 읽는가」로 잡는 것이 정확합니다.

12. 정리

  • OSI 참조 모델은 구현이 아니라, 계층으로 생각하기 위한 어휘. 현실에서 동작하는 것은 TCP/IP(실질 4계층)
  • 7계층은 개념도가 아니라, 프레임 1개의 바이트열 안에 물리적으로 중첩되어 있다(L2: 0~13, L3: 14~33, L4: 34~53, L7: 54~)
  • 각 계층은 자기 헤더만 읽고, 적재물(안쪽 계층)에는 관여하지 않는다 ── 이것이 캡슐화
  • L5·L6은 현실 스택에는 독립해 존재하지 않으며, TLS나 인코딩이 역할을 흡수하고 있다
  • 여러분이 작성하는 C# 코드가 쓰는 것은 L7 바이트열뿐. TCP/IP 헤더는 OS, Ethernet은 NIC가 붙인다
  • 실무에서의 쓰임은 아래 계층부터 한 단계씩 생존 확인하는 장애 원인 분리와, 팀 간의 공통 언어
  • 샘플 코드로 프레임을 조립해 Wireshark에서 열면, 이 글의 내용은 모두 자신의 눈으로 확인할 수 있다

관련 글

관련 상담 영역

合同会社小村ソフト에서는 TCP/IP 통신을 하는 Windows 애플리케이션의 설계·구현, 산업 기기와의 통신에서 「가끔 끊긴다·느려진다」와 같은 통신 장애의 원인 조사, 패킷 캡처를 이용한 장애 원인 분리 지원을 다루고 있습니다.

참고 링크

  1. IETF, RFC 1122 - Requirements for Internet Hosts – Communication Layers. 인터넷 호스트가 구현해야 할 요건을 정한 RFC. 링크 계층·IP 계층·트랜스포트 계층·애플리케이션 계층의 4계층으로 프로토콜 스위트를 정리하고 있다는 점에 대해. ↩ ↩2 ↩3 ↩4 ↩5

  2. ITU-T, X.200 : Information technology - Open Systems Interconnection - Basic Reference Model: The basic model. OSI 기본 참조 모델의 원전(ISO/IEC 7498-1과 같은 내용). 7계층 각각의 정의에 대해. ↩ ↩2

  3. Microsoft Learn, Socket Class (System.Net.Sockets). .NET의 소켓 API. 애플리케이션은 payload를 넘기고, 프로토콜 헤더 생성은 OS의 프로토콜 스택이 맡는다는 점에 대해. ↩

  4. IETF, RFC 9293 - Transmission Control Protocol (TCP). TCP의 현행 사양. 시퀀스 번호·확인 응답에 의한 신뢰성 제공과, checksum 계산에 IP 주소를 포함하는 의사 헤더를 쓴다는 점에 대해. ↩ ↩2

  5. Microsoft Learn, SslStream Class (System.Net.Security). 기존 스트림(보통은 TCP의 NetworkStream)을 감싸 TLS에 의한 암호화·인증을 제공하는 클래스에 대해. ↩

  6. Wireshark Foundation, Wireshark User’s Guide. 캡처 파일을 여는 방법, 패킷 상세 창과 바이트열 창의 대응 표시, 표시 필터 사용법에 대해. ↩

같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.

이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.

이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.

자주 묻는 질문

이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.

OSI 참조 모델의 7계층은 실제 인터넷에서 사용되고 있습니까?
사용되지 않습니다. 현실 인터넷에서 동작하는 것은 TCP/IP 프로토콜 스위트(실질 4계층)이며, OSI의 7계층 프로토콜 자체는 1990년대 보급 경쟁에서 밀려 거의 쓰이지 않은 채 끝났습니다. 인터넷의 기초를 정한 RFC 1122는 링크 계층·IP 계층·트랜스포트 계층·애플리케이션 계층의 4계층으로 세계를 설명합니다. 살아남은 것은 「L2에서 원인을 분리한다」「L7 문제다」라는 장애 원인 분리와 대화를 위한 공통 어휘로서의 모델입니다.
세션 계층(L5)과 프레젠테이션 계층(L6)의 실물은 어디에 있습니까?
현실의 TCP/IP 스택에는 독립된 계층으로 존재하지 않습니다. OSI 모델이 가정한 역할은 현실에서는 흩어져 흡수되어 있으며, L5(대화의 수립·관리)는 TLS 핸드셰이크나 HTTP의 Cookie/토큰에, L6(데이터 표현의 변환·암호화)는 TLS에 의한 암호화·문자 인코딩·JSON 등의 직렬화 형식에 해당합니다. 「TLS는 몇 계층인가」를 하나로 확정하는 것에는 실무상 의미가 없고, L4 위이면서 L7 아래에서 L5·L6에 해당하는 역할을 함께 가진다고 설명할 수 있는 편이 더 중요합니다.
IP 주소가 있는데 왜 MAC 주소가 필요합니까?
계층의 역할 분담이 다르기 때문입니다. IP 주소(L3)는 최종 목적지를 가리키는 주소이며, 통신의 처음부터 끝까지 바뀌지 않습니다. MAC 주소(L2)는 「다음 1구간을 실어 나를 상대」를 가리키는 수신인이며, 라우터를 넘을 때마다 다시 붙습니다. 사무실 PC에서 Web 서버로 보내는 프레임의 목적지 MAC은 Web 서버가 아니라 기본 게이트웨이(라우터)의 MAC이 됩니다. IP 주소에서 인접 노드의 MAC 주소를 알아내는 것이 ARP이며, arp -a 명령으로 대응표를 확인할 수 있습니다.
OSI 참조 모델은 실무에서 어디에 도움이 됩니까?
장애 원인 분리와 팀 간 대화입니다. 「서버에 연결되지 않는다」는 보고는 링크 램프 육안 확인(L1~L2), 게이트웨이에 ping(L3), 상대 포트에 TCP 연결 확인(L4), HTTP 요청 전송(L7)처럼 아래 계층부터 한 단계씩 생존 확인을 하면 체계적으로 범위를 좁힐 수 있습니다. 예를 들어 ping은 통하는데 TCP 연결이 실패한다면, L4 포트를 막는 무언가(서비스 중지, 방화벽)로 혐의가 좁혀집니다. 또한 「L2 문제이니 스위치를 봐 달라」처럼 책임 분계점을 짧게 전달하는 업계 공통 좌표로도 기능합니다.

저자 프로필

기사 저자의 프로필 페이지입니다.

Go Komura

합동회사 코무라소프트 대표

Windows 소프트웨어 개발, 기술 상담, 장애 조사를 중심으로 재현이 어려운 장애 조사와 기존 자산이 남아 있는 프로젝트에 강점이 있습니다.

블로그 목록으로 돌아가기