나는 C로 작은 HTTP 서버를 만들어 웹이 실제로 어떻게 작동하는지 이해했다
Source: Dev.to
What I Wanted (and What I Didn’t)
이 프로젝트는 절대 프로덕션 서버를 만드는 것이 목적이 아니었습니다.
Nginx와 경쟁하려는 것도 아니었고, 아주 기본적인 질문들에 대한 답만 원했습니다:
- HTTP 요청은 실제로 어떻게 생겼을까?
- 서버는 어떤 파일을 반환할지 어떻게 결정할까?
accept()와 브라우저가 페이지를 렌더링하기 사이에 무슨 일이 일어날까?
그게 전부였습니다.
What This Server Actually Does
정확히 이 정도만 합니다 — 더도 덜도 아니죠:
- 포트 8080에서 리스닝
- GET 요청만 처리
- 정적 파일(HTML, 텍스트, 이미지) 제공
- POSIX 스레드 사용(클라이언트 1명당 1스레드)
- 올바른
Content-Type헤더 전송 - 파일이 없을 경우 간단한 404 반환
한 번에 이해할 수 있을 정도로 작습니다.
Code Structure (Because One File Is a Nightmare)
모든 코드를 main.c에 몰아넣고 싶지는 않아서 파일을 나눴습니다:
main.c→ 소켓 설정,bind,listen, accept 루프request.c→ 요청을 읽고 클라이언트를 처리http.c→ HTTP 응답을 생성utils.c→ MIME 타입 및 URL 디코딩
특별한 트릭은 없습니다. 책임을 분리했을 뿐이죠. 이 덕분에 디버깅이 훨씬 수월해졌습니다.
The First “Oh Damn” Moment
처음으로 원시 HTTP 요청을 터미널에 출력했을 때, 드디어 깨달았습니다:
GET /index.html HTTP/1.1
Host: localhost:8080
그게 전부였습니다. 객체가 없죠. 그 순간 HTTP가 더 이상 신비롭게 느껴지지 않았습니다.
Parsing Without Overthinking
전체 HTTP 파서를 만들지는 않았습니다. 작은 정규식을 사용해 GET / 뒤의 파일 경로를 추출하고, 디코딩한 뒤 바로 진행했습니다.
취약한가요? 네. 과도한 설계는 이 프로젝트의 핵심 목적을 무너뜨렸을 겁니다.
Building the Response Manually
또 하나 간과하기 쉬운 사실: 유효한 응답은 문자 그대로:
status line
headers
(empty line)
body
이 흐름을 하드코딩하니 모든 것이 이해됐습니다. 파일을 찾지 못했을 때는 간단한 404를 반환했습니다.
MIME Types: Small Detail, Big Lesson
한때 HTML은 잘 보였지만 이미지가 안 보였던 적이 있습니다. 브라우저가 Content-Type을 정말 신경 쓴다는 사실을 깨달았습니다. 그래서 작은 확장자 → MIME 매핑을 추가했죠:
.html→text/html.jpg→image/jpeg.png→image/png
그때부터 모든 것이 정상적으로 렌더링되었습니다. 이 사소한 버그가 수십 개의 블로그 포스트보다 더 큰 교훈을 주었습니다.
Why C?
C는 아무것도 숨기지 않기 때문입니다. 동작한다면, 여러분이 직접 만들었기 때문이죠. C로 구현하면서:
- 소켓을 제대로 이해하게 됨
- 메모리를 신중히 관리하게 됨
- 추상화가 아닌 실제 시스템 콜을 읽게 됨
그리고 솔직히 현대 프레임워크에 대한 감탄이 더 커졌습니다.
What This Server Is Bad At (On Purpose)
분명히 해야 할 점 — 이 서버는 다음 기능을 지원하지 않습니다:
POST요청- HTTPS
- Keep‑alive 연결
- 보안
- 실제 트래픽 처리
그리고 괜찮습니다. 학습용 프로젝트였고, 스타트업이 아니니까요.
Final Thoughts
프레임워크 없이 서버를 만든 적이 없다면, 한 번 시도해 보길 권합니다. 도구를 대체하려는 목적이 아니라, 완성하려는 압박도 없습니다. 저에게 이 프로젝트는 HTTP에 대한 많은 혼란을 해소해 주었고, 그 자체만으로도 충분히 가치 있었습니다.
Thanks for reading 🙌