OSI 참조 모델을 확실하게 이미지화하기 ── HTTP 요청 1개를 7계층으로 해부한다
· 小村 豪 · OSI 참조 모델, TCP/IP, 네트워크, Wireshark, Ethernet, TCP, HTTP, C#, .NET, Socket, 기술 상담
OSI 참조 모델은 네트워크 입문서라면 반드시 처음에 나오는데도, “7계층의 이름은 말할 수 있지만, 솔직히 이미지가 잘 안 잡힌다”는 말을 자주 듣습니다. 물리 계층・데이터 링크 계층・네트워크 계층……이라고 암기는 했지만, 자신이 작성하고 있는 C# 코드나, 지금 눈앞에서 일어나고 있는 통신 장애와 그 7단 도표가 어떻게 연결되는지 알 수 없는 채로 남아 있기 쉽습니다.
이 블로그에서는 지금까지 TCP로 Send한 단위마다 Receive할 수 있다는 오해나 TCP 재전송으로 산업용 카메라 통신이 멈추는 원인과 분리 등 L4(트랜스포트 계층)의 동작을 다룬 글을 써 왔지만, 그 토대가 되는 ‘계층’이라는 사고방식 자체를 정리한 글은 없었습니다.
이 글의 접근 방식은 단순합니다. HTTP GET 요청 1개를 운반하는 Ethernet 프레임을 실제로 하나 만들어서, 바깥쪽부터 순서대로 해부합니다. 7계층은 개념도 안에만 있는 이야기가 아니라, 네트워크를 흐르는 171바이트 안에 물리적으로 겹쳐진 형태로 존재합니다. 그것을 헥스 덤프와 Wireshark로 직접 눈으로 확인하고 나면, OSI 참조 모델은 더 이상 암기의 대상이 아니게 됩니다.
이 글에 등장하는 코드는, 빌드・실행할 수 있는 샘플 일체(프레임 조립・해부 라이브러리, Wireshark로 열 수 있는 pcap 내보내기, 단위 테스트)로 GitHub에 공개하고 있습니다.
osi-model-packet-anatomy - komurasoft-blog-samples (GitHub)
1. 먼저 결론
- OSI 참조 모델은 ‘구현’이 아니라 ‘어휘’입니다. 현실의 인터넷에서 동작하는 것은 TCP/IP 프로토콜 스위트(실질적으로 4계층)이며, OSI의 7계층 프로토콜 자체는 보급 경쟁에서 패배해 쓰이지 않고 있습니다. 살아남은 것은 ‘L2에서 나눠서 본다’, ‘L7의 문제다’ 같은 공통 언어로서의 모델입니다.12
- 7계층은 바이트열로서 물리적으로 겹쳐져 있습니다. HTTP GET을 운반하는 프레임이라면, 앞의 14바이트가 Ethernet 헤더(L2), 다음 20바이트가 IPv4 헤더(L3), 다음 20바이트가 TCP 헤더(L4), 나머지가 HTTP 텍스트(L7)입니다. 각 계층의 헤더가 앞 계층 바로 다음부터 시작하는 모습은 본문의 헥스 덤프와 Wireshark로 그대로 확인할 수 있습니다.
- 당신의 C# 코드가 직접 쓰는 것은 L7의 바이트열뿐입니다.
Socket.Send에 넘기는 것은 애플리케이션 데이터뿐이며, TCP・IP 헤더는 OS의 프로토콜 스택이, Ethernet 헤더와 전기 신호는 NIC 쪽이 붙입니다. 그렇기 때문에HttpClient/SslStream/Socket중 무엇을 쓸지는 ‘어느 계층 아래를 OS에 맡길 것인가’의 선택이 됩니다.3 - L5(세션 계층)・L6(표현 계층)는, 현실의 스택에는 독립된 계층으로 존재하지 않습니다. TLS나 문자 인코딩이 그 역할의 일부를 담당하고 있지만, TCP/IP 모델에서는 통틀어 애플리케이션 계층입니다. ‘L6가 뭔지 모르겠다’는 것은 당신의 잘못이 아니라, 모델과 현실이 어긋나 있는 부분이기 때문입니다.2
- OSI 모델이 실무에서 진짜로 도움이 되는 것은 장애 분리와 대화 상황입니다. ‘ping은 통하는데 HTTP가 실패한다’면 L3까지는 살아 있으니 L4 이상을 의심한다는, 계층의 어휘를 이용한 좁혀 나가기는 네트워크・인프라・앱 개발자 사이에서 통하는 공통 언어가 됩니다.
2. 왜 OSI 참조 모델은 암기로 끝나는가
많은 해설이 이런 표로 시작합니다.
| 계층 | 이름 | 설명 |
|---|---|---|
| L7 | 애플리케이션 계층 | 애플리케이션에 통신 서비스를 제공한다 |
| L6 | 표현 계층 | 데이터의 표현 형식을 변환한다 |
| L5 | 세션 계층 | 통신의 시작・종료를 관리한다 |
| L4 | 트랜스포트 계층 | 신뢰성 있는 데이터 전송을 제공한다 |
| L3 | 네트워크 계층 | 경로 선택과 어드레싱을 수행한다 |
| L2 | 데이터 링크 계층 | 인접 노드 간의 데이터 전송을 수행한다 |
| L1 | 물리 계층 | 비트를 전기 신호로 변환한다 |
이 표는 옳지만, 모든 행이 추상적인 말로 쓰여 있어서 읽어도 이미지가 잡히지 않습니다. ‘데이터의 표현 형식을 변환한다’는 말을 듣고, 그것이 자기 코드의 어느 줄을 가리키는지 아는 사람은 없을 것입니다.
하나 더, 솔직하게 적어 둬야 할 역사적 사실이 있습니다. OSI 참조 모델은 ISO(국제표준화기구)와 ITU-T가 정한 표준(ISO/IEC 7498-1, ITU-T X.200)으로, 원래는 7계층 각각에 대응하는 OSI 프로토콜군과 세트를 이루는 거대한 구상이었습니다.1 그러나 1990년대의 보급 경쟁에서, 먼저 동작하는 것이 있었던 TCP/IP가 사실상의 표준이 되었고, OSI 프로토콜 자체는 거의 쓰이지 않은 채 끝났습니다. 인터넷의 기초를 정한 RFC 1122는 링크 계층・인터넷 계층(IP)・트랜스포트 계층・애플리케이션 계층이라는 실질적으로 4계층으로 세계를 설명하며, L5・L6에 해당하는 독립된 계층은 없습니다.2
즉, 지금 우리가 OSI 참조 모델을 배우는 의미는 ‘이대로 구현되어 있기 때문’이 아니라, ‘계층으로 생각한다’는 도구와, 장애 분리를 위한 공통 어휘를 손에 넣기 위해서입니다. 이 전제에 서면 ‘L5와 L6의 실물을 찾을 수 없다’고 고민할 필요가 없어지고, 시야가 단번에 넓어집니다.
3. 본론: HTTP 요청 1개를 해부한다
서론은 여기까지 하고, 실물을 봅니다. 다음 헥스 덤프는 GET /index.html HTTP/1.1이라는 HTTP 요청 1개를 운반하는 Ethernet 프레임(전체 171바이트)입니다. 앞서 소개한 샘플 코드가 조립한 것으로, IPv4 헤더 체크섬과 TCP 체크섬 모두 정확히 계산되어 있기 때문에, 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
이것이 이 글에서 가장 보여 드리고 싶은 도표입니다. 포인트는 3가지입니다.
- 각 계층은 ‘앞 계층 바로 다음’부터 시작합니다. Ethernet 헤더가 0~13번째 바이트, IPv4 헤더가 14~33번째 바이트, TCP 헤더가 34~53번째 바이트, HTTP가 54번째 바이트부터. 계층은 개념상의 분류가 아니라, 프레임 앞부분부터의 바이트 범위로 가리킬 수 있습니다.
- 안쪽 계층은 바깥쪽 계층의 ‘페이로드(짐)’입니다. Ethernet 입장에서 보면 IPv4 이후는 그냥 짐이며, 내용이 TCP인지 아닌지는 관여하지 않습니다. IP 입장에서 보면 TCP 이후가 짐, TCP 입장에서 보면 HTTP가 짐입니다. 각 계층은 자신의 헤더만 읽고, 짐에는 손을 대지 않은 채 위로 넘깁니다. 이 ‘관여하지 않는’ 구조야말로 캡슐화입니다.
- L1(물리 계층)과 L5・L6는 이 도표에 등장하지 않습니다. L1은 이 바이트열을 전기 신호・빛・전파로 변환하는 일이라서 덤프에는 나타나지 않고, L5・L6는 앞 장에서 본 대로 평문 HTTP/1.1에서는 독립된 실체가 없습니다. ‘7계층 중 덤프에 나타나는 것은 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에서 웹 서버로 보내는 프레임의 목적지 MAC은 웹 서버가 아니라 기본 게이트웨이(라우터)의 MAC이 됩니다.
여기서 많은 사람이 한 번쯔음 궁금해하는 것이 ‘IP 주소가 있는데 왜 MAC 주소가 필요한가’입니다. 답은 계층의 역할 분담에 있습니다. IP 주소는 ‘최종 목적지’를 가리키는 주소이고, MAC 주소는 ‘다음 한 구간을 운반할 상대’를 가리키는 수신인입니다. 택배에 비유하면, 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 헤더 체크섬
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 헤더를 다시 붙여서 내보냅니다.
TTL(Time To Live)은 이 ‘라우터를 넘는’ 움직임을 눈으로 보이게 해 주는 필드입니다. 라우터를 하나 넘을 때마다 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 체크섬
L3까지로 패킷은 목적지 ‘호스트(머신)’에 도착했습니다. 하지만 그 머신에서는 웹 서버도 데이터베이스도, 그 밖에 수많은 프로세스도 동시에 통신하고 있습니다. L4의 역할 중 하나는, 포트 번호를 이용해 ‘어느 프로세스(의 소켓)에 넘길지’를 분배하는 것입니다. 목적지 포트 80은 ‘HTTP 서버가 대기하고 있는 번호’라는 관습으로, OS는 이 번호를 보고 해당 프로세스의 수신 버퍼에 데이터를 쌓습니다.
TCP의 또 다른 큰 역할이, 시퀀스 번호・확인 응답・재전송・흐름 제어에 의한 ‘신뢰성 있는 바이트 스트림‘의 제공입니다.4 이 부근의 동작과 다루는 법은 각각 한 편의 글이 될 만큼 깊은 내용이므로, TCP의 메시지 경계 이야기와 TCP 재전송 이야기로 넘기겠습니다. 여기서는 ‘L4 위부터는 더 이상 네트워크의 모양을 하고 있지 않고, 프로세스와 프로세스를 잇는 파이프처럼 보인다’는 감각을 잡아 두세요.
한 가지, 모델의 이상과 현실의 어긋남을 보여주는 재미있는 사실이 있습니다. TCP의 체크섬은, TCP 세그먼트 단독이 아니라, L3의 정보(송신지・목적지 IP 주소)를 나열한 ‘가상 헤더’를 앞에 붙여서 계산하도록 정의되어 있습니다.4 계층이 깔끔하게 독립되어 있다면, L4의 계산에 L3의 주소가 섞일 리가 없습니다. OSI 참조 모델은 어디까지나 정리를 위한 지도일 뿐이고, 현실의 프로토콜은 필요에 따라 계층을 넘나든다는 좋은 예입니다.
7. L5・L6 ── 모델과 현실이 어긋나는 부분
자, 해부 도표에는 L5(세션 계층)・L6(표현 계층)이 없었습니다. 이것이 ‘OSI를 모르겠다’고 느끼는 가장 큰 원인이므로, 분명히 적어 둡니다.
L5・L6는 현실의 TCP/IP 스택에는 독립된 계층으로 존재하지 않습니다. RFC 1122의 인터넷 모델에서는, TCP보다 위는 모두 ‘애플리케이션 계층’입니다.2 OSI 모델이 상정한 역할은, 현실에는 다음과 같이 흩어져서 흡수되어 있습니다.
| OSI의 구상 | 현실에 흡수된 곳의 예 |
|---|---|
| L5: 대화(세션)의 확립・관리 | TLS의 세션・핸드셰이크, HTTP의 쿠키/토큰, 앱 자체의 로그인 관리 |
| 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 페이로드가 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 / 케이블 / 공간 |
이 표에서 2가지를 읽을 수 있습니다.
첫째, 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);
}
데모를 실행하면, 헥스 덤프와 겹친 구조 뷰의 표시에 더해, 이 프레임을 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장의 겹친 구조 뷰와 같은 것을, 업계 표준 도구가 보여 주는 셈입니다. 여기까지 확인했다면, 다음은 실제 트래픽입니다. Wireshark로 캡처를 시작하고, 필터에 tcp.port == 443 정도를 넣고 브라우저로 어딘가의 사이트를 열면, 실제 통신이 전부 이 겹친 구조로 되어 있음을 확인할 수 있습니다.
또한, 해부기(PacketDissector) 쪽의 구현에도 실무적인 배울 점을 넣어 두었습니다. 예를 들어 IPv4의 짐을 잘라낼 때, 수신 버퍼의 나머지 전체가 아니라 헤더의 TotalLength 필드를 신뢰해서 잘라내야 합니다. Ethernet에는 최소 프레임 길이(60바이트)가 있어서, 짧은 패킷에는 의미 없는 패딩이 끝에 붙기 때문입니다. ‘바깥쪽 계층의 사정(패딩)을, 안쪽 계층의 길이 정보로 제거한다’ ── 이것도 계층의 역할 분담이 구현에 나타나는 한 예로, 단위 테스트로 검증하고 있습니다.
10. 실무에서 OSI 모델을 쓴다 ── 계층의 어휘로 나눈다
서두에서 ‘OSI는 어휘로서 살아남았다’고 썼습니다. 그 어휘가 실제로 도움이 되는 상황을 2가지 들어 봅니다.
장애 분리. ‘서버에 연결되지 않는다’는 보고는, 계층의 어휘로 번역하면 체계적으로 좁힐 수 있습니다. 아래부터 순서대로 확인하는 것이 정석입니다.
| 확인 | 사용하는 것 | 살아 있다고 알 수 있는 계층 |
|---|---|---|
| 링크 램프・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는 설명과 대화를 위한 참조 모델입니다.2
- ‘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라는 보장은 없습니다. 포트 번호는 관습이며, 실제로 무엇이 흐르고 있는지는 페이로드를 봐야 알 수 있습니다. 샘플의 해부기가 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로 Send한 단위마다 Receive할 수 있다는 오해 ── 바이트 스트림으로 다루기 위한 수신 설계
- TCP 재전송으로 산업용 카메라 통신이 멈추는 원인과 분리
- C# async/await 실무 판단표 - Task.Run과 ConfigureAwait
관련 상담 분야
합동회사 코무라소프트는 TCP/IP 통신을 하는 Windows 애플리케이션의 설계・구현, 산업용 장비와의 통신에서 ‘가끔 끊긴다・느려진다’ 같은 통신 결함의 원인 조사, 패킷 캡처를 이용한 장애 분리 지원을 다루고 있습니다.
참고 링크
-
ITU-T, X.200 : Information technology - Open Systems Interconnection - Basic Reference Model: The basic model. OSI 기본 참조 모델의 원전(ISO/IEC 7498-1과 같은 내용). 7계층 각각의 정의에 대해. ↩ ↩2
-
IETF, RFC 1122 - Requirements for Internet Hosts – Communication Layers. 인터넷 호스트가 구현해야 할 요구사항을 정한 RFC. 링크 계층・IP 계층・트랜스포트 계층・애플리케이션 계층의 4계층으로 프로토콜 스위트를 정리하고 있는 것에 대해. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Socket Class (System.Net.Sockets). .NET의 소켓 API. 애플리케이션이 페이로드를 넘기고, 프로토콜 헤더 생성은 OS의 프로토콜 스택이 담당하는 것에 대해. ↩
-
IETF, RFC 9293 - Transmission Control Protocol (TCP). TCP의 현행 사양. 시퀀스 번호・확인 응답에 의한 신뢰성 제공과, 체크섬 계산에 IP 주소를 포함하는 가상 헤더를 쓰는 것에 대해. ↩ ↩2
-
Microsoft Learn, SslStream Class (System.Net.Security). 기존 스트림(보통 TCP의
NetworkStream)을 감싸서 TLS에 의한 암호화・인증을 제공하는 클래스에 대해. ↩ -
Wireshark Foundation, Wireshark User’s Guide. 캡처 파일을 여는 방법, 패킷 상세 패널과 바이트열 패널의 대응 표시, 표시 필터 사용법에 대해. ↩
관련 기사
같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.
Windows의 세션 분리를 어떻게 이해할까 ── Session 0・RDP・다중 사용자 동시 실행
Windows 앱 개발자가 혼란스러워하기 쉬운 '세션' 개념을 정리합니다. 서비스가 UI를 표시할 수 없는 Session 0 분리의 이유, RDP 접속 시 세션의 동작, 명명된 개체의 세션 분리, 공유 PC・RDS 환경에서 흔히 발생하는 설계 실...
Windows 앱의 중복 실행 방지 ── 네임드 Mutex와 재실행 시 창 활성화
업무용 Windows 앱의 정석 요건인 「같은 앱을 두 번 실행시키지 않는다」를 네임드 Mutex로 구현하는 방법을 정리합니다. Global\ 과 Local\ 네임스페이스의 차이로 인한 RDP 환경의 함정, 소유 스레드 제약과 AbandonedM...
C#에서 Win32 API를 안전하게 호출하기 — P/Invoke 실무 가이드(DllImport / LibraryImport / CsWin32)
C#에서 P/Invoke로 Win32 API나 네이티브 DLL을 호출할 때의 실무 포인트를 정리합니다. DllImport와 LibraryImport의 차이, CsWin32를 이용한 시그니처 자동 생성, 문자열 마샬링의 함정, SafeHandle을...
WinForms/WPF 앱에 Entra ID 인증 넣기 ── MSAL.NET과 WAM 브로커 실무 구성
WinForms/WPF 데스크톱 앱에 Entra ID(구 Azure AD) 인증을 도입하는 절차를 실무 관점에서 정리합니다. 퍼블릭 클라이언트의 개념, ROPC 폐지 현황, 앱 등록, MSAL.NET의 AcquireTokenSilent 패턴, W...
업무 앱의 날짜/시간과 시간대 ── DateTime의 함정부터 UTC 저장 원칙, 테스트 설계까지
서버 이전 시 시간이 9시간 어긋나거나, 해외 지사에서만 날짜가 하루 전날이 되는 문제──날짜/시간 사고의 원인을 DateTime의 Kind와 암묵적 변환부터 정리합니다. DateTimeOffset과의 구분 사용, UTC 저장과 ISO 8601 ...
관련 토픽
이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.
Windows 기술 토픽
Windows 개발, 장애 조사, 기존 자산 활용에 관한 KomuraSoft LLC 기사를 모은 토픽 허브입니다.
장애 조사 & 장기 가동 장애
간헐적 장애, 통신 진단, 장기 가동 크래시, 실패 경로 테스트 기반을 정리한 토픽 페이지입니다.
이 주제와 연결되는 서비스
이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.
Windows 앱 개발
상주 처리, 장비 연동, 운영 로그, 유지 보수 가능한 구조가 필요한 Windows 데스크톱 애플리케이션을 지원합니다.
기술 상담 & 설계 리뷰
설계 방향, 아키텍처 경계, 수명 관리, 기존 Windows 자산 처리 방법을 정리하는 데 도움을 드립니다.
자주 묻는 질문
이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.
- OSI 참조 모델의 7계층은 실제 인터넷에서 쓰이고 있나요?
- 쓰이지 않고 있습니다. 현실의 인터넷에서 동작하는 것은 TCP/IP 프로토콜 스위트(실질적으로 4계층)이며, OSI의 7계층 프로토콜 자체는 1990년대의 보급 경쟁에서 패배해 거의 쓰이지 않은 채 끝났습니다. 인터넷의 기초를 정한 RFC 1122는 링크 계층・IP 계층・트랜스포트 계층・애플리케이션 계층의 4계층으로 세계를 설명합니다. 살아남은 것은 'L2에서 나눠서 본다', 'L7의 문제다' 같은 장애 분리와 대화를 위한 공통 어휘로서의 모델입니다.
- 세션 계층(L5)과 표현 계층(L6)의 실물은 어디에 있나요?
- 현실의 TCP/IP 스택에는 독립된 계층으로 존재하지 않습니다. OSI 모델이 상정한 역할은 현실에서는 흩어져서 흡수되어 있으며, L5(대화의 확립・관리)는 TLS의 핸드셰이크나 HTTP의 쿠키/토큰에, L6(데이터 표현의 변환・암호화)는 TLS에 의한 암호화・문자 인코딩・JSON 같은 직렬화 형식에 해당합니다. 'TLS는 몇 계층인가'를 하나로 확정하는 것에는 실무상 의미가 없고, L4 위 L7 아래에서 L5・L6에 해당하는 역할을 함께 갖고 있다고 설명할 수 있다는 점이 더 중요합니다.
- IP 주소가 있는데 왜 MAC 주소가 필요한가요?
- 계층의 역할 분담이 다르기 때문입니다. IP 주소(L3)는 최종 목적지를 가리키는 주소로, 통신의 처음부터 끝까지 바뀌지 않습니다. MAC 주소(L2)는 '다음 한 구간을 운반할 상대'를 가리키는 수신인으로, 라우터를 넘을 때마다 다시 붙여집니다. 사내 PC에서 웹 서버로 보내는 프레임의 목적지 MAC은 웹 서버가 아니라 기본 게이트웨이(라우터)의 MAC이 됩니다. IP 주소로부터 인접 노드의 MAC 주소를 조회하는 것이 ARP이며, arp -a 명령으로 대응표를 확인할 수 있습니다.
- OSI 참조 모델은 실무에서 무엇에 쓸모가 있나요?
- 장애 분리와 팀 간의 대화입니다. '서버에 연결되지 않는다'는 보고는, 링크 램프 목측(L1~L2), 게이트웨이로의 ping(L3), 상대 포트로의 TCP 연결 확인(L4), HTTP 요청 전송(L7)처럼 아래 계층부터 하나씩 생존 확인을 함으로써 체계적으로 좁힐 수 있습니다. 예를 들어 ping은 통하는데 TCP 연결이 실패한다면, L4의 포트를 막는 무언가(서비스 중지, 방화벽)로 의심 범위가 좁혀집니다. 또한 'L2 문제니까 스위치를 봐 달라'처럼 책임 분계점을 짧게 전달하기 위한 업계 공통의 좌표로도 기능합니다.
저자 프로필
기사 저자의 프로필 페이지입니다.
Go Komura
합동회사 코무라소프트 대표
Windows 소프트웨어 개발, 기술 상담, 장애 조사를 중심으로 재현이 어려운 장애 조사와 기존 자산이 남아 있는 프로젝트에 강점이 있습니다.
공개 링크