[주간 회고] 네트워크를 읽는 방식이 바뀐 일주일
🧭 이번 주를 한 줄로 정리하면
책으로만 훑고 지나가던 네트워크를, Tiny와 Proxy를 직접 붙잡고 디버깅하며 '내가 실제로 제어하고 확인할 수 있는
흐름'으로 바꿔 낸 시간이었다.
이번 주 학습 축
1. 네트워크 개념의 시스템적 재정의
클라이언트-서버 모델을 기반으로, 추상적인 도메인 이름이
getaddrinfo
계열의 시스템 콜을 통해 어떻게 구체적인 소켓 주소 구조체로 번역되는지 시스템 수준에서 이해했다.
2. 소켓과 커널의 내부 흐름
listen과
accept의 분리 구조를 파악하고, 커널 관점의 통신 끝점(Endpoint)이 애플리케이션 관점에서 어떻게 파일 디스크립터(FD)로
추상화되는지 체감했다.
3. Tiny 웹 서버와 CGI 메커니즘
HTTP 요청 파싱 및 정적/동적 서빙의 분기 처리를 코드로 확인하고,
fork,
execve,
dup2를 활용해 서버가 외부 프로세스의 출력을 가로채는 CGI의 원리를 해체했다.
4. Proxy Lab: 서버 앞단으로의 확장
브라우저와 엔드 서버 사이에서 능동적 중개자 역할을 하는 Proxy를 직접 구현하며, 요청을 뜯어보고 다시 조립하여 전달하는 네트워크 실전 감각을 길렀다.
이번 주에 한 일 체크리스트
이번 주 작업을 한 번 더 분명하게 짚고 넘어가기 위해 체크리스트로 정리했다. 아래 항목들은 단순 요약이 아니라, 코드를 직접 만지고 흐름을 확인한 범위를 기준으로 기록한 것이다.
getaddrinfo가 호스트 이름을 실제 연결 가능한 주소 정보로 바꾸는 과정 이해
listen과
accept의 분리 구조를 코드 레벨에서 추적
cgi-bin
기준 정적/동적 콘텐츠 분기 원리 확인
fork,
setenv,
dup2,
execve를 통한 CGI 동작 원리 해부
10대 핵심역량
이 섹션은 단순히 점수를 매기는 자기평가표가 아니라, 이번 주에 무엇이 실제로 달라졌는지를 역량이라는 언어로 다시 풀어 쓴 기록이다. CSAPP 11장을 읽고 Tiny와 Proxy를 붙잡고 씨름했던 시간은 개념 암기를 넘어, 내가 어떤 방식으로 이해하고 문제를 해결해 나가는지 여실히 보여준 시간이었다.
1. 문제 정의
4.0 / 5
막히는 장면을 대하는 방식이 달라졌다. 예전에는 막히면 "네트워크가 어렵다"고 뭉뚱그렸지만, 이번 주에는 지금
막히는 부분이 주소 해석인지, HTTP 헤더 파싱인지,
listen과
accept의 역할 분리인지 명확하게 구분하여 문제를 잘게 쪼개어 접근할 수 있게 되었다.
2. 개념 이해
4.5 / 5
소켓을 '커널 입장에선 통신의 끝점, 애플리케이션 입장에선 파일 디스크립터'로 보는 설명은 큰 인식의 전환을
가져왔다.
getaddrinfo가 주소를 해석하고, 소켓이 통신 경로를 묶고, 프로세스가 이를 FD로 다루는 일련의 과정이 운영체제의 I/O 문맥
안에서 확고하게 정리되었다.
3. 시스템적 사고
4.0 / 5클라이언트, 서버, 커널, 소켓, 파일 디스크립터, 자식 프로세스가 파편화된 지식이 아니라 하나의 거대한 데이터 파이프라인으로 연결되었다. Tiny를 통해 서버 내부의 동작을, Proxy를 통해 서버 앞단에서의 트래픽 제어를 경험하며 다각적인 시스템 시점을 확보했다.
4. 코드 독해
3.5 / 5
추상적인 HTTP 프로토콜이 C 코드 안에서 문자열 파싱, 조건문, 파일 I/O로 치환되는 과정을 직접 확인했다. URI 내
cgi-bin
포함 여부에 따라 실행 경로가 갈리는 코드를 추적하며, 코드를 두려워하기보다 배운 개념이 어디에 구현되어 있는지
찾는 독해 습관이 생겼다.
5. 디버깅 감각
4.0 / 5Docker와 DevContainer 환경에서 브레이크포인트를 걸고 버퍼의 상태와 소켓의 흐름을 눈으로 직접 확인했다. 눈으로 읽은 문장을 맹신하지 않고, 프로그램의 제어 흐름이 신뢰할 수 있는 단위까지 잘게 쪼개어 내려가는 실증적인 디버깅 감각을 익혔다.
6. 구현 연결력
4.0 / 5Proxy Lab을 진행하며 요청 파싱, 헤더 재조립, 응답 중계라는 교재의 짧은 설명이 실제 구현에서는 얼마나 촘촘한 메모리 및 소켓 처리 로직을 요구하는지 체감했다. 이론을 코드로, 다시 코드를 구현 과제로 연결하는 능력이 예전보다 훨씬 자연스러워졌다.
7. 실험·검증
3.5 / 5바이너리 데이터를 일반 문자열 함수로 처리했을 때 이미지가 깨지는 현상을 직접 겪으며, 왜 네트워크에서 바이너리 안전한 I/O(Robust I/O)가 필수적인지 몸소 깨달았다. 추상적인 경고문보다 실패하는 코드가 훨씬 강력한 검증 도구임을 배웠다.
8. 설명 가능성
4.0 / 5
웹 서버의 동작 방식을 "대충 그런 구조"가 아니라, 왜
accept가 별도의 소켓을 반환하는지, CGI 프로그램의
printf
출력이 어떻게
dup2를 통해 클라이언트로 흘러가는지 등 구체적인 시스템 콜의 흐름을 곁들여 설명할 수 있게 되었다.
9. 질문 확장
4.0 / 5순차적 구조의 Tiny 서버를 구현하고 나니 자연스럽게 "동시성(Concurrency)은 어떻게 달성하는가?", "select나 epoll 같은 멀티플렉싱 기법은 왜 필요한가?"와 같은 다음 단계의 질문들이 솟아났다. 학습의 끝이 아니라 확장의 시작점에 서 있음을 느낀다.
10. 학습 지속력
4.0 / 5이해가 막힐 때 쉽게 포기하거나 요약본을 찾지 않았다. 교재를 읽다가 막히면 코드로 가고, 코드에서 막히면 디버거를 켜고, 다시 교재로 돌아오는 끈질긴 왕복 달리기를 계속했다. 끊기는 지점을 포기하지 않고 끝까지 이어 붙이려 한 점이 가장 큰 성과다.
이번 주에 실제로 붙잡고 있었던 일
이번 회고를 너무 교재 중심의 이론 요약으로만 남기고 싶지는 않았다. 실제로 이번 주 대부분의 시간은 "CSAPP 11장을 읽었다"는 활자 독해보다 "읽은 내용을 코드와 디버거로 확인했다"는 실천에 가까웠기 때문이다. 책에서 본 클라이언트-서버 모델과 소켓 인터페이스를 단순히 머리로만 넘기지 않고, Tiny 코드 안에서 어디서 요청을 읽고, 어디서 URI를 나누며, 어디서 응답을 써 내려가는지 집요하게 따라갔다. 막히면 책으로 돌아가고, 다시 코드로 내려오는 식의 거친 왕복이 계속됐다.
실습 레포지토리 환경을 Docker와 VSCode DevContainer로 일관되게 정리해 둔 것도 큰 도움이 되었다. 환경 설정에 에너지를 낭비하지 않고, 즉시 Tiny를 8000번 포트에, Proxy를 4500번 포트에 띄운 다음 브레이크포인트를 걸어 변수의 상태를 추적하는 데 시간을 쏟을 수 있었다. 이번 주 내가 얻은 지식은 잘 정돈된 글이 아니라, 요청이 흘러가고 되돌아오는 경로를 수없이 손으로 짚어가며 얻어낸 체득의 결과물이다.
처음에는 네트워크를 이렇게 생각했다
이번 주차 전까지 나에게 네트워크란 거대한 블랙박스였습니다. 브라우저 주소창에 URL을 입력하면 어딘가에 있는 마법 같은 인프라를 타고 HTML과 이미지가 화면으로 쏟아지는 과정 정도로만 여겼습니다. HTTP 헤더의 형태나 GET 요청의 의미 정도는 알고 있었지만, 그 텍스트들이 내 컴퓨터의 랜카드를 벗어나 상대방 컴퓨터의 애플리케이션에 도달하기까지의 '실제 구현체'는 전혀 상상하지 못했습니다.
네트워크를 공부한다고 했을 때, 단순히 TCP/IP 4계층이나 OSI 7계층 같은 패킷의 추상적인 이동 경로를 외우는 일인 줄 알았습니다. 하지만 CSAPP 11장을 펴는 순간, 저자는 네트워크를 철저히 운영체제와 시스템 프로그래밍의 연장선상에서 풀어냈습니다. 모든 네트워크 애플리케이션은 결국 클라이언트 프로세스와 서버 프로세스 간의 트랜잭션이며, 이를 잇는 다리가 바로 소켓(Socket) 인터페이스라는 사실을 마주하며 흩어져 있던 지식의 조각들이 하나로 맞춰졌습니다.
소켓을 파일 디스크립터로 본 순간부터 달라진 것
"커널 관점에서 소켓은 통신의 끝점(Endpoint)이다. 그러나 애플리케이션 관점에서 소켓은 해당하는 식별자(Descriptor)를 가진 열린 파일일 뿐이다."
- CSAPP Chapter 11
이번 장을 통틀어 제 관점을 가장 강하게 뒤흔든 문장입니다. Unix의 "모든 것은 파일이다"라는 철학이 네트워크 통신에까지
예외 없이 적용된다는 점은 꽤나 충격적이면서도 명쾌했습니다. 네트워크 밖으로 데이터를 보내는 과정은 특별한 마법이
아니라, 그저 커널이 할당해 준 파일 디스크립터에
read와
write
연산을 수행하는 익숙한 I/O 행위에 불과했던 것입니다.
특히 서버 측에서
listen과
accept가 분리되어 있다는 사실을 인지하며 많은 의문이 해소되었습니다. 처음에는 서버가 하나의 소켓으로 모든 클라이언트와
대화한다고 막연히 추측했지만, 실제로는 문지기 역할을 하는 듣기 소켓(Listening Socket)과 실제 데이터를 주고받는 연결
소켓(Connected Socket)이 철저히 분리되어 동작했습니다.
accept
함수가 클라이언트의 연결 요청을 낚아채 새로운 식별자를 생성하는 흐름을 코드 레벨에서 확인하고 나서야, 서버가 어떻게
여러 클라이언트와 데이터를 뒤섞임 없이 처리할 수 있는지 그 구조적 기반을 납득할 수 있었습니다.
클라이언트-서버 모델과 시스템 콜
소켓의 FD 추상화, listen/accept
Tiny 웹 서버와 CGI 동적 분기
Proxy Lab의 요청/응답 중계
Tiny를 읽으면서 HTTP가 코드가 되는 장면
추상적인 이론이 구체적인 C 코드로 눈앞에 렌더링된 것은 CSAPP에서 제공하는 Tiny 웹 서버를 분석하면서부터입니다. Tiny는 한 번에 하나의 요청만 처리하는 순차적(Sequential) 서버에 불과하지만, HTTP 규약의 핵심 뼈대를 완벽하게 구현하고 있었습니다.
코드를 한 줄씩 파헤치며 가장 인상 깊었던 지점은, 거창해 보이던 웹 서버의 라우팅이 결국 '문자열 파싱과 단순한
조건문'으로 귀결된다는 점이었습니다. 클라이언트가 보낸
GET / HTTP/1.0
형태의 텍스트를 읽어 URI를 분리하고, 그 안에
cgi-bin이라는 문자열이 포함되어 있으면 동적 콘텐츠로, 그렇지 않으면 파일 시스템에서 정적 파일을 찾아 그대로 소켓 버퍼에
밀어 넣습니다. 마법 같던 웹 서버의 동작이 이토록 직관적이고 투명한 로직으로 짜여 있다는 사실이 무척 흥미로웠습니다.
CGI를 이해하면서 서버를 보는 시선이 바뀐 지점
단순히 파일을 읽어 전송하는 정적 서빙과 달리, 동적 서빙은 서버를 바라보는 제 시각을 한 단계 더 끌어올렸습니다. 처음에 "서버가 요청에 맞게 동적 페이지를 생성한다"는 개념을 접했을 때는, 서버 프로그램 내부에 엄청나게 복잡한 비즈니스 로직이나 템플릿 엔진이 단단히 결합되어 있어야 하는 줄 알았습니다. 그러나 Tiny가 동적 콘텐츠를 처리하기 위해 사용하는 CGI(Common Gateway Interface) 메커니즘을 뜯어보며, 이것이 웹만의 고유 기술이 아니라 철저히 '운영체제 프로세스 제어'의 응용이라는 것을 깨달았습니다.
Tiny는 동적 요청이 인입되면
fork()를 호출해 자식 프로세스를 생성합니다. 그리고
setenv()를 이용해 클라이언트의 파라미터를
QUERY_STRING
환경변수에 주입합니다. 여기서 시스템 프로그래밍의 백미는 단연
dup2(fd, STDOUT_FILENO)
시스템 콜이었습니다. 자식 프로세스의 표준 출력(모니터)을 클라이언트와 연결된 소켓 식별자로 조용히 덮어씌운 뒤,
execve()로 목적 프로그램을 실행합니다. 결과적으로 외부의 CGI 프로그램(예: adder)은 네트워크 소켓의 존재조차 모른 채
평소처럼
printf를 호출할 뿐이지만, 그 결과는 커널의 파이프라인을 타고 고스란히 클라이언트 브라우저로 전송됩니다.
Proxy Lab에서 비로소 손에 잡힌 것
Tiny가 웹 서버의 '내부 로직'을 낱낱이 보여주었다면, 실습 과제인 Proxy Lab은 브라우저와 엔드 서버 사이의 트래픽을 직접 다루며 '서버 앞단'의 통제력을 쥐어 주었습니다.
프록시는 결코 데이터를 멍청하게 흘려보내는 단순한 파이프가 아니었습니다. 클라이언트로부터 연결을 수락하고, HTTP 헤더를 철저히 파싱하며, URI에서 Hostname과 Path를 발라내어 표준 포맷으로 재조립합니다. 그 후 엔드 서버(Tiny)를 향해 스스로 능동적인 소켓을 열어 요청을 전송하고, 반환되는 응답을 빠짐없이 읽어 들여 다시 클라이언트에게 중계(Relaying)합니다.
이 복잡다단한 과정을 직접 C 코드로 작성하고, 디버거를 통해 버퍼에 패킷이 차오르는 것을 확인하는 순간, 네트워크는 비로소 '읽고 외워야 할 이론'에서 '내가 직접 조립하고 통제하는 데이터의 흐름'으로 탈바꿈했습니다.
이번 주에 내가 조금 성장했다고 느낀 지점
예전에는 코딩하다 막히면 구글링을 하거나 개념 문서를 다시 훑어보는 데 그쳤다면, 이번에는 개념과 코드 사이를 훨씬 밀도 있게 오갔다. 코드가 막히면 문서를 다시 읽고, 문서를 읽다 헷갈리면 즉시 디버거를 켜서 메모리와 소켓의 상태를 눈으로 확인했다. 이러한 집요한 반복 덕분에, 네트워크를 뜬구름 잡는 이론으로 두지 않고 운영체제와 코드 레벨에서 완벽하게 증명 가능한 대상으로 끌어내렸다는 점이 이번 주 가장 큰 성장이었다.
이번 주에 자꾸 걸린 장면
물론 모든 과정이 순탄하지만은 않았습니다. 추상적인 개념을 날것의 코드로 옮기는 과정에서 수많은 암초를 만났습니다.
-
문자열 파싱의 늪: HTTP GET 요청의 URL을 파싱하여
hostname, port, path로 정교하게 분리하는 작업은 상당한 포인터 제어 능력을 요구했습니다.
sscanf와strstr을 활용해 HTTP 헤더의 끝을 알리는\r\n\r\n을 찾는 과정에서 메모리 침범(Segfault)을 수없이 마주했습니다. -
텍스트와 바이너리의 경계: 프록시에서 응답 데이터를
중계할 때, 초기에는 텍스트 기반의 문자열 함수로 무작정 버퍼를 읽어 넘겼습니다. HTML 텍스트는 정상 출력되었지만
이미지 파일이 심각하게 깨지는 참사가 벌어졌습니다. 널 스트링(
\0)에 의존하지 않는 바이너리 안전한 I/O 함수(Robust I/O)의 필요성을 뼈저리게 체감했습니다. -
연결 종료의 책임: 클라이언트, 프록시, 서버가 맞물려
있는 구조에서 소켓을 언제
close해주어야 하는지 타이밍을 놓쳐, 프로세스가 좀비 상태로 남거나 브라우저 탭이 무한 로딩의 늪에 빠지는 경험을 겪어야 했습니다.
이번 장을 지나며 생긴 관점 변화
| 처음에는 네트워크를 이렇게 생각했다 | 지금은 네트워크를 이렇게 본다 |
|---|---|
| 웹 서버는 동적 페이지를 만들기 위한 거대하고 복잡한 프레임워크 덩어리다. | 웹 서버는 소켓으로 텍스트를 파싱하고, 조건에 따라 파일을 읽어 내리거나 외부 프로세스의 표준 출력을 가로채서(CGI) 반환하는 직관적인 프로그램이다. |
| 소켓은 네트워크 프로그래밍에서만 쓰이는 매우 이질적이고 특수한 객체다. |
소켓은 커널이 제공하는 통신의 끝점이며, 애플리케이션
입장에서는 그저
read,
write
할 수 있는 익숙한 파일 디스크립터일 뿐이다.
|
| 프록시는 데이터를 양방향으로 대충 전달해주는 수동적인 통로다. | 프록시는 클라이언트에겐 서버처럼, 서버에겐 클라이언트처럼 영리하게 행동하며 패킷을 재구성하는 능동적 중개자(Middleman)다. |
앞으로 더 공부할 것
이번 주 학습은 네트워크와 웹 서버의 가장 튼튼한 뼈대를 세우는 과정이었습니다. 하나의 요청을 순차적으로 묵묵히 처리하는 기초적인 서버를 바닥부터 구현해 보았으니, 이제 자연스럽게 다음 아키텍처에 대한 갈증이 생깁니다.
첫째는 동시성(Concurrency) 제어입니다. 현재 구현한 Tiny와
Proxy는 앞선 클라이언트의 처리가 완벽히 끝나기 전까지 다음 클라이언트가 하염없이 대기해야 하는 블로킹 구조를
가집니다. 이를 타파하기 위해 프로세스/스레드 기반의 동시성 모델이나,
epoll
혹은
select를 활용한 I/O 멀티플렉싱(Multiplexing) 기반의 고성능 서버 구조로 학습을 확장해야 합니다.
둘째는 프록시 캐싱(Caching)의 도입입니다. 빈번하게 발생하는 정적 리소스 요청을 굳이 엔드 서버까지 보내지 않고 프록시 계층에서 즉각 반환하기 위해, 메모리 최적화 구조와 LRU(Least Recently Used) 교체 알고리즘을 결합한 캐시 로직을 직접 완성해 볼 계획입니다.
마무리하며
CSAPP 11장과 Proxy Lab을 관통하며 얻은 가장 값진 수확은, 네트워크 인프라와 운영체제 시스템이 맞닿아 있는 그
흐릿했던 경계선을 또렷하게 응시할 수 있게 되었다는 점입니다. 브라우저에서 날아온 작은 텍스트 덩어리가 커널 소켓을
통과해 파일 디스크립터로 변하고,
dup2
'크래프톤 정글 > TIl _ WILL' 카테고리의 다른 글
| WIL 10주차: Pintos Project 2에서 배운 것은 (0) | 2026.05.07 |
|---|---|
| 크래프톤 정글 · Week 9 WIL · Pintos Project 1 Threads (0) | 2026.04.30 |
| 크래프톤 정글 — Week 7 WIL (2) | 2026.04.16 |
| 크래프톤 정글 — Week 6 WIL (1) | 2026.04.09 |
| 크래프톤 정글 — Week 5 WIL (0) | 2026.04.02 |