C-3: 웹이 동작하는 원리
목차 29
안녕하세요, 홍순구 튜터입니다. CS 기초 아홉 번째 시간, 네트워크 파트의 마지막 시간이에요.
지난 세 시간 동안 우리는 네트워크의 조각들을 하나씩 손에 쥐었습니다. C-1에서는 네트워크를 계층으로 펼치고, TCP로 연결을 맺는 3-way handshake와 IP·포트 주소 체계를 배웠어요. C-2에서는 그 위에서 도는 HTTP의 요청·응답, 메서드, 상태 코드, 그리고 통신을 암호로 지키는 TLS 핸드셰이크까지 봤죠. 조각은 충분히 모였습니다. 그런데 한 가지, 아직 안 한 게 있어요. 이 조각들이 실제로 어떤 순서로 맞물려 돌아가는지는 따로따로 봤지 한 줄기로 꿰어본 적이 없습니다.
그래서 오늘은 흩어진 조각을 하나의 여정으로 잇습니다. 여러분이 브라우저 주소창에 www.instagram.com을 치고 엔터를 누른 그 한순간 — 화면에 사진이 뜨기까지 컴퓨터 안에서, 그리고 인터넷 너머에서 실제로 무슨 일들이 순서대로 일어나는가. 이게 오늘의 주제입니다.
이 질문은 우연히 고른 게 아니에요. "URL을 치면 무슨 일이 일어나나요?" 는 신입 개발자 기술 면접에서 가장 자주, 가장 깊게 묻는 단골 질문입니다. 면접관이 이 하나로 컴퓨터 구조·운영체제·네트워크를 한꺼번에 떠보거든요. DNS만 답하면 거기서 "그다음은요?"가 꼬리를 물고, 끝까지 막힘없이 풀어내는 사람과 중간에 멈추는 사람이 확연히 갈립니다. 오늘 그 전체 여정을 한 호흡에 정리해둡시다.
오늘 우리가 걸을 길은 이렇습니다.
오늘의 여정 — 웹이 동작하는 원리
① 전체 여정 한눈에 — "URL 을 치면 일어나는 일" (최고 빈출)
② DNS — 주소를 IP 로 바꾸는 인터넷 전화번호부 (면접 빈출)
③ 연결을 맺는다 — TCP 3-way + TLS 핸드셰이크 (지난 시간 회수)
④ 요청과 응답 — 서버는 그동안 무엇을 하나
⑤ 렌더링 — 받은 HTML 이 화면이 되기까지
⑥ 서버는 한 대가 아니다 — 로드밸런싱·CDN·캐싱 (면접 빈출)
⑦ REST API — 웹 API 를 설계하는 약속 (면접 빈출)
⑧ 웹소켓 한 입 — HTTP 가 못 하는 양방향
①에서 전체 여정의 큰 그림을 통째로 잡고, ②~⑤에서 그 여정을 단계별로 깊이 파고듭니다(주소 찾기 → 연결 → 요청·응답 → 화면 그리기). 그다음 ⑥에서 "사실 서버는 한 대가 아니다"로 시야를 넓히고, ⑦~⑧에서 그 위에서 앱이 대화하는 두 방식(REST와 웹소켓)을 봅니다.
💡 오늘 수업의 핵심 — "URL을 치면, DNS로 IP를 찾고 → TCP·TLS로 연결을 맺고 → HTTP로 요청·응답을 주고받고 → 받은 HTML을 브라우저가 렌더링한다. 실제 서비스는 로드밸런서·CDN·캐시로 수많은 서버에 흩어져 있고, 그 위에서 앱은 REST(요청-응답)나 웹소켓(양방향)으로 대화한다" 🎯
🎯 학습 목표
- "URL을 치면 일어나는 일" 전체 여정(DNS → TCP → TLS → HTTP → 서버 처리 → 응답 → 렌더링)을 한 호흡에 설명하고, 각 단계가 지난 시간 배운 어느 조각인지 짚어냅니다.
- DNS가 도메인을 IP로 바꾸는 계층 구조(루트·TLD·권한 서버)와 재귀 질의를 이해하고, 로드밸런싱·CDN·캐싱이 왜 필요한지 개념으로 설명합니다(깊은 운영은 인프라 과목의 몫).
- REST API(자원·메서드·무상태)가 웹 API를 설계하는 약속임을 알고, HTTP의 한계를 넘는 웹소켓의 양방향 통신이 언제 필요한지 구분합니다.
Step 1: "전체 여정 한눈에 — 'URL을 치면 일어나는 일'"
면접에서 "URL을 치면 무슨 일이 일어나나요?"를 받으면, 잘하는 사람은 먼저 전체 골격을 한 호흡에 던지고 그다음 면접관이 파고드는 단계를 깊이 설명합니다. 그래서 우리도 똑같이 갑니다. 오늘 배울 모든 것을 일단 한 장의 지도로 통째로 보고, Step 2부터 한 칸씩 확대할게요.
여러분이 주소창에 www.instagram.com/p/abc를 치고 엔터를 누르면, 대략 이런 순서로 일이 벌어집니다.
URL 을 치면 일어나는 일 — 한 페이지가 뜨기까지
① 주소창에 URL 입력
│ www.instagram.com/p/abc → 무슨 프로토콜·도메인·경로인지 분해
▼
② DNS 조회 도메인 이름 → IP 주소 (Step 2)
│
▼
③ TCP 3-way handshake 그 IP 의 서버와 연결 통로 열기 (Step 3)
│
▼
④ TLS 핸드셰이크 https 면 암호 통로까지 얹기 (Step 3)
│
▼
⑤ HTTP 요청 전송 "이 페이지(자원) 줘" (Step 4)
│
▼
⑥ 서버 처리 라우팅 → 앱 로직 → DB 조회 → 응답 생성 (Step 4·6)
│
▼
⑦ HTTP 응답 수신 HTML·CSS·JS·이미지가 돌아옴 (Step 4)
│
▼
⑧ 브라우저 렌더링 글자 덩어리(HTML) → 눈에 보이는 화면 (Step 5)
이 여덟 단계가 오늘의 전부예요. 하나씩 한 줄로만 풀어볼게요. ① 브라우저는 먼저 입력한 주소를 분해합니다 — https라는 프로토콜, www.instagram.com이라는 도메인, /p/abc라는 경로로요. ② 도메인은 사람이 읽는 이름일 뿐이고 실제 통신은 숫자 IP로 하니까, DNS에게 "이 도메인의 IP가 뭐야?"를 물어 IP를 받아옵니다. ③ IP를 알았으니 그 서버와 TCP 3-way handshake로 연결 통로를 엽니다(지난 C-1에서 배운 그 세 번 인사예요). ④ https라면 그 위에 TLS 핸드셰이크로 암호 통로까지 얹어요(C-2에서 배운 그 과정이죠). ⑤ 이제 안전한 통로로 HTTP 요청을 보냅니다("이 페이지 줘"). ⑥ 서버는 요청을 받아 내부에서 처리하고(필요하면 데이터베이스도 뒤져), ⑦ 결과를 HTTP 응답으로 돌려줍니다. ⑧ 브라우저는 받은 HTML을 해석해 화면으로 그려냅니다.
지금은 각 단계를 "아, 이런 게 순서대로 있구나" 정도로만 머리에 담으면 충분해요. 중요한 건 이 여덟 칸의 순서입니다. 순서만 외워둬도 면접에서 절반은 먹고 들어가요. Step 2부터 이 칸들을 하나씩 열어 안을 들여다보겠습니다.
🎯 면접에서는 이렇게 답하세요
"주소창에 URL을 치면, 먼저 브라우저가 주소를 프로토콜·도메인·경로로 분해합니다. 그다음 DNS로 도메인을 IP 주소로 바꾸고, 그 IP의 서버와 TCP 3-way handshake로 연결을 맺어요. HTTPS라면 그 위에 TLS 핸드셰이크로 암호화 통로를 추가하고요. 이제 HTTP 요청을 보내면 서버가 라우팅·로직·DB 조회를 거쳐 HTTP 응답을 돌려주고, 브라우저가 받은 HTML을 파싱해 화면으로 렌더링합니다. 한마디로 DNS → TCP → TLS → HTTP 요청·응답 → 렌더링의 순서예요."
🙋 학생 질문 — "튜터님, 방금 들어갔던 사이트를 또 열면 이 과정을 매번 처음부터 다 하나요?"
좋은 질문이에요. 결론부터 말하면, 상당 부분을 건너뜁니다. 매번 전부 다 하면 너무 느릴 테니까요.
여러 군데에 캐시(미리 기억해두기)가 깔려 있어요. ② DNS 조회는 한 번 찾은 IP를 브라우저와 운영체제가 한동안 기억하니(이걸 TTL이라고 해요, Step 2에서 봐요) 다시 묻지 않고 바로 씁니다. ③ TCP 연결도 한 번 맺은 걸 잠깐 재활용하고(keep-alive), ⑧ 화면을 그릴 때 쓰는 이미지·CSS 같은 파일도 브라우저가 캐시에 저장해뒀다가 재요청 없이 꺼내 써요. 그래서 두 번째 방문은 첫 방문보다 훨씬 빠릅니다. "여정 전체가 매번 똑같이 반복된다"가 아니라, "캐시가 있는 단계는 건너뛰고 없는 단계만 실행한다"가 정확한 그림이에요. 캐싱은 Step 6에서 더 다룹니다.
Step 2: "DNS — 주소를 IP로 바꾸는 인터넷 전화번호부"
여정의 첫 실제 통신은 DNS(Domain Name System, 도메인 이름 체계)입니다. 왜 이게 필요한지부터 잡을게요. 우리는 www.instagram.com 같은 이름을 기억하지만, 정작 컴퓨터끼리의 통신은 C-1에서 배운 IP 주소(예: 142.250.x.x 같은 숫자)로 이뤄집니다. 사람은 숫자 수십 개를 외우기 어렵고, 컴퓨터는 이름으로 길을 못 찾아요. 그 사이를 번역해주는 게 DNS입니다.
비유하면 전화번호부예요. 우리는 "재훈이에게 전화해야지"라고 이름으로 생각하지만, 실제로 전화기가 거는 건 번호죠. 전화번호부가 이름을 번호로 바꿔주듯, DNS는 도메인 이름을 IP 주소로 바꿔줍니다. 그래서 DNS를 흔히 "인터넷의 전화번호부"라고 불러요.
그런데 전 세계 도메인이 수십억 개입니다. 이걸 전화번호부 한 권에 다 담아 한 곳에서 관리하면, 그 한 곳이 멈추는 순간 인터넷 전체가 멈추고, 너무 느려서 감당도 안 돼요. 그래서 DNS는 계층 구조로, 책임을 위에서 아래로 나눠 갖습니다. 도메인을 점(.)으로 끊어 오른쪽부터 좁혀 가며 찾아요.
DNS 계층 구조 — 위에서 아래로 좁혀 가며 IP 를 찾는다
브라우저: "www.instagram.com 의 IP 가 뭐죠?"
│
▼
[ 재귀 리졸버 ] 나 대신 끝까지 물어봐 주는 대리인 (보통 통신사·공용 DNS)
│
├─▶ [ 루트 서버 (.) ] "com 은 저쪽 TLD 서버한테 물어봐"
│
├─▶ [ TLD 서버 (.com) ] "instagram.com 은 저쪽 권한 서버한테"
│
└─▶ [ 권한 서버 ] "instagram.com 의 IP 는 142.250.x.x 야"
│
▼
IP 주소를 받아 브라우저에 전달 (그리고 한동안 기억 = 캐시)
흐름을 따라가 볼게요. 브라우저는 직접 전 세계를 헤매지 않고, 재귀 리졸버(recursive resolver)라는 대리인에게 질문을 통째로 맡깁니다. 보통 통신사가 운영하거나 8.8.8.8 같은 공용 DNS 서버죠. 이 대리인이 나 대신 계층을 타고 다니며 답을 찾아옵니다. 먼저 맨 위 루트 서버에게 묻습니다. 루트는 IP를 직접 모르지만 ".com을 담당하는 TLD 서버(Top-Level Domain, 최상위 도메인)는 저기야"라고 알려줘요. 그 TLD 서버에게 다시 물으면 "instagram.com을 책임지는 권한 서버(authoritative server)는 저기"라고 안내합니다. 마지막으로 그 권한 서버가 비로소 실제 IP 주소를 내놓아요. 대리인은 이 IP를 브라우저에게 돌려줍니다.
핵심은 두 가지예요. 첫째, 아무도 전부를 알지 않고 각자 자기 단계만 책임진다 — 루트는 TLD가 어디인지만, TLD는 권한 서버가 어디인지만 압니다. 이렇게 나눠 가져야 수십억 도메인을 감당할 수 있어요. 둘째, 한 번 찾은 답은 캐시됩니다. 각 단계가 결과를 일정 시간(TTL, Time To Live) 동안 기억해뒀다가 재사용해요. 그래서 자주 가는 사이트는 DNS 조회를 거의 건너뜁니다(Step 1의 질문 답이 여기서 완성되죠).
🎯 면접에서는 이렇게 답하세요
"DNS는 사람이 읽는 도메인 이름을 컴퓨터가 통신에 쓰는 IP 주소로 바꿔주는, '인터넷의 전화번호부'입니다. 전 세계 도메인이 너무 많아 한 곳에서 관리할 수 없어서 계층 구조로 나뉘어 있어요. 브라우저의 질문을 받은 재귀 리졸버가 루트 → TLD(.com) → 권한 서버 순으로 도메인을 오른쪽부터 좁혀 가며 물어 최종 IP를 찾아옵니다. 각 단계는 자기 책임 범위만 알고, 한 번 찾은 결과는 TTL 동안 캐시해 다음 조회를 빠르게 합니다."
🙋 학생 질문 — "튜터님, 재귀 질의랑 반복 질의는 뭐가 다른 거예요?"
이름이 비슷해서 헷갈리는데, 누가 발품을 파느냐의 차이예요.
재귀 질의(recursive)는 브라우저와 재귀 리졸버 사이의 관계입니다. 브라우저는 "그냥 최종 답만 줘"라고 통째로 맡기고, 리졸버가 끝까지 책임지고 IP를 찾아다 줘요. 발품을 리졸버가 대신 파는 거죠. 반면 반복 질의(iterative)는 그 리졸버가 루트·TLD·권한 서버를 상대하는 방식입니다. 리졸버가 루트에게 물으면 루트는 답 대신 "다음은 저기로 가봐"라고 방향만 알려줘요. 그럼 리졸버가 그 방향으로 또 묻고, 또 다음 안내를 받아 이동하고… 이렇게 한 단계씩 직접 옮겨 다니며 묻는 게 반복 질의예요.
정리하면 브라우저→리졸버는 "다 해다 줘"(재귀), 리졸버→상위 서버들은 "방향만 알려줘, 내가 갈게"(반복)입니다. 깊은 동작까지 외울 필요는 없고, "리졸버가 나 대신 계층을 타고 다닌다"는 큰 그림이면 면접에서 충분해요.
Step 3: "연결을 맺는다 — TCP 3-way + TLS 핸드셰이크"
DNS로 IP를 알아냈으니, 이제 그 서버와 연결을 맺을 차례예요. 여기서 지난 두 시간에 배운 조각 둘이 한 여정으로 합류합니다. C-1의 TCP 3-way handshake(통로 열기)와 C-2의 TLS 핸드셰이크(암호 통로 얹기)예요. 둘은 따로 배웠지만, 실제 HTTPS 통신에서는 위아래로 포개져 차례로 일어납니다.
먼저 TCP 3-way handshake입니다. HTTP는 한 글자도 틀리면 안 되는 통신이라 신뢰성을 보장하는 TCP 위에서 돈다고 했죠(C-2). 그 TCP는 데이터를 보내기 전에 "우리 연결할 준비됐지?"를 세 번 인사로 확인합니다. 그게 끝나면 데이터가 안전하게 오갈 통로가 열려요. 여기에 더해 https라면, 그 통로 위에 TLS 핸드셰이크로 암호 통로를 한 겹 더 얹습니다. 인증서로 상대가 진짜인지 확인하고, 비대칭키로 대칭키를 합의한 뒤, 그 빠른 대칭키로 본 통신을 암호화하는 과정이었죠(C-2). 순서가 중요해요 — TCP가 먼저(통로), TLS가 그 위에(암호) 입니다.
TCP 로 통로를 열고(3-way), 그 위에 TLS 로 암호 통로를 얹는다
Client Server
│ │
│─────────────────── SYN ───────────────────▶│ 연결할까요?
│◀──────────────── SYN + ACK ────────────────│ 좋아요, 그쪽도 준비됐죠?
│─────────────────── ACK ───────────────────▶│ 준비 끝 — 여기서 TCP 통로 완성
│ │
│─────────────── ClientHello ───────────────▶│ 암호화 시작, 인증서 주세요
│◀──────── Certificate + KeyExchange ────────│ 인증서 확인 + 대칭키 합의
│──────────────── Finished ─────────────────▶│ 대칭키 확정 — 암호 통로 완성
│ │
위쪽 세 줄(SYN → SYN+ACK → ACK)이 TCP 3-way handshake고, 아래 세 줄이 TLS 핸드셰이크예요. 둘 다 끝나야 비로소 안전하게 암호화된 통로가 완성됩니다. 그제야 그 위로 진짜 데이터, 즉 HTTP 요청이 오갈 수 있어요(Step 4).
눈치챘겠지만, 연결 한 번 맺는 데 인사가 꽤 많죠. TCP에 세 번, TLS에 또 몇 번. 그래서 연결 수립에는 시간(왕복)이 든다는 게 중요한 포인트예요. 이 비용을 줄이려고 HTTP는 keep-alive로 연결을 재활용하고, HTTP/2는 한 연결로 여러 요청을 멀티플렉싱하고, HTTP/3는 아예 더 가벼운 QUIC로 갈아탔다는 게 — 바로 지난 시간 C-2 마지막에 본 진화의 이유입니다.
🎯 면접에서는 이렇게 답하세요
"IP를 알아내면 그 서버와 먼저 연결을 맺습니다. HTTP는 신뢰성이 필요해 TCP 위에서 돌기 때문에, TCP 3-way handshake(SYN → SYN+ACK → ACK)로 데이터를 보낼 통로를 먼저 열어요. HTTPS라면 그 위에 TLS 핸드셰이크를 추가해서, 인증서로 상대를 확인하고 대칭키를 합의한 암호화 통로를 얹습니다. 즉 TCP 통로 → TLS 암호 통로 순서로 포개져요. 이 과정에 왕복이 여러 번 들어서 연결 수립 비용이 있고, 그래서 keep-alive나 HTTP/2·3 같은 최적화가 나온 겁니다."
🙋 학생 질문 — "튜터님, 그럼 페이지 하나 열 때마다 이 핸드셰이크를 매번 다 하나요? 그럼 너무 느릴 것 같은데요."
맞아요, 매번 처음부터 다 하면 정말 느립니다. 그래서 여러 장치로 이 비용을 아껴요.
먼저 연결 재사용(keep-alive)이 있습니다. 한 번 맺은 TCP·TLS 통로를 끊지 않고 잠깐 열어둔 채로, 그 사이 들어오는 여러 요청을 같은 통로로 처리해요. 한 페이지를 열 때 이미지·CSS·JS를 수십 개 받아야 하는데, 그때마다 새로 핸드셰이크하면 끔찍하니까요. 또 TLS에는 세션 재개(session resumption)가 있어서, 전에 합의한 키 정보를 살려 핸드셰이크를 줄입니다. 더 나아가 지난 시간 배운 HTTP/2는 연결 하나로 여러 요청을 동시에 실어 보내고(멀티플렉싱), HTTP/3는 QUIC라는 더 가벼운 방식으로 연결 비용 자체를 크게 낮췄어요. 그래서 실제로는 "페이지마다 처음부터 전부"가 아니라 "한 번 맺어 최대한 재활용"이 기본입니다.
Step 4: "요청과 응답 — 서버는 그동안 무엇을 하나"
암호화된 통로가 열렸으니, 드디어 HTTP 요청을 보냅니다. C-2에서 배운 그 메시지예요 — GET /p/abc HTTP/1.1 같은 시작줄에 헤더가 붙은 글자 덩어리죠. 이 요청이 서버에 도착하면, 그동안 서버 안에서는 무슨 일이 벌어질까요? 우리가 클라이언트 쪽만 보다 보니 서버는 "요청 넣으면 답 나오는 까만 상자"처럼 느껴지는데, 그 상자 안을 한 겹 열어볼게요.
요청 한 건이 서버 안에서 거치는 길
[ HTTP 요청 도착 ] GET /p/abc
│
▼ 라우팅 — 이 요청은 어느 코드가 처리? (/p/abc → 게시물 핸들러)
│
▼ 애플리케이션 로직 — 로그인 됐나? 권한 있나? 비즈니스 규칙 처리
│
▼ 데이터베이스 조회 — 게시물·작성자·좋아요 수를 꺼내 옴 (다음 카테고리 D 의 주인공)
│
▼ 응답 생성 — 꺼낸 데이터를 HTML 또는 JSON 으로 빚어
│
[ HTTP 응답 반환 ] 200 OK + 본문
순서대로 보면, 요청이 도착하면 서버는 먼저 라우팅을 합니다. "/p/abc라는 경로의 요청은 어느 코드가 맡지?"를 정하는 단계예요. 그다음 그 코드(애플리케이션 로직)가 실제 일을 처리합니다 — 로그인 여부와 권한을 확인하고, 필요한 규칙을 적용하죠. 거의 항상 여기서 데이터베이스를 조회합니다. 게시물 내용, 작성자, 좋아요 수 같은 진짜 데이터는 서버 코드가 아니라 데이터베이스에 저장돼 있으니까요. 꺼내온 데이터를 HTML이나 JSON 같은 형태로 빚어서, 마지막에 상태 코드(200·404 등, C-2)와 함께 HTTP 응답으로 돌려줍니다.
여기서 잠깐 멈춰볼게요. 방금 "데이터베이스를 조회한다"고 했죠. 그 데이터베이스가 정확히 뭐고, 어떻게 데이터를 안전하게(트랜잭션) 그리고 빠르게(인덱스) 다루는지는 바로 다음 카테고리 D의 주인공입니다. 오늘은 "서버 처리의 한복판에 DB 조회가 있다"는 것만 짚고 넘어갈게요. 면접에서 "이 API가 느려요, 어디부터 보겠어요?"를 물으면, 바로 이 그림 — 네트워크 왕복인지, DB 조회인지, 응답 생성인지 — 을 떠올려 가설을 세우는 게 시작입니다.
🎯 면접에서는 이렇게 답하세요
"요청이 서버에 도착하면, 먼저 라우팅으로 어느 코드가 이 요청을 처리할지 정합니다. 그 코드가 애플리케이션 로직(인증·권한·비즈니스 규칙)을 실행하고, 대부분 그 안에서 데이터베이스를 조회해 필요한 데이터를 꺼냅니다. 마지막으로 그 데이터를 HTML이나 JSON으로 응답을 생성해 상태 코드와 함께 돌려줘요. 그래서 'API가 느리다'는 문제를 만나면 네트워크 왕복·DB 조회·응답 생성 중 어디가 병목인지 단계별로 의심하는 게 출발점입니다."
🙋 학생 질문 — "튜터님, 이미지나 CSS 파일 같은 건 서버가 그때마다 DB를 뒤져서 만들어 주는 건가요?"
아니에요, 거기서 정적 콘텐츠와 동적 콘텐츠가 갈립니다. 좋은 구분이에요.
동적 콘텐츠는 요청마다 내용이 달라지는 것 — 예를 들어 "내 피드", "이 게시물의 좋아요 수" 같은 거죠. 이건 방금 본 것처럼 서버가 DB를 조회해 그때그때 빚어서 만들어 줍니다. 반면 정적 콘텐츠는 누가 언제 요청하든 똑같은 파일 — 로고 이미지, CSS, JS 파일 같은 거예요. 이런 건 DB를 뒤질 필요 없이 저장된 파일을 그대로 내보내면 끝입니다. 그래서 정적 파일은 처리가 가볍고, 아예 사용자 가까운 곳에 미리 복사해두면 더 빠른데, 그게 Step 6에서 볼 CDN이에요. "매번 만들어야 하는 것(동적)"과 "한 번 만들어두면 되는 것(정적)"을 나눠서 다루는 게 웹 성능의 큰 줄기 중 하나입니다.
Step 5: "렌더링 — 받은 HTML이 화면이 되기까지"
서버가 응답으로 HTML을 돌려줬어요. 그런데 HTML은 결국 <div>...</div> 같은 글자 덩어리입니다. 이게 어떻게 우리 눈에 보이는 사진과 버튼이 될까요? 그 마지막 변환을 렌더링(rendering, 화면에 그리기)이라고 하고, 브라우저가 담당합니다. 여정의 마지막 칸이에요.
브라우저는 받은 글자 덩어리를 다음 단계로 가공합니다.
받은 HTML 이 화면이 되기까지 (브라우저 안에서)
HTML 문서 ──파싱──▶ DOM 트리 문서의 구조 (요소들의 부모-자식 관계)
CSS 문서 ──파싱──▶ CSSOM 트리 스타일 규칙 (무엇을 어떻게 꾸밀지)
│
둘을 │ 합쳐서
▼
Render Tree 실제로 화면에 그릴 것만 추린 트리
│
▼
Layout 각 요소의 위치·크기를 계산 (어디에 얼마나)
│
▼
Paint 계산된 대로 픽셀을 칠해 화면에 표시
순서를 따라가 볼게요. 브라우저는 HTML을 파싱(parsing, 해석)해서 DOM 트리를 만듭니다. 문서가 어떤 요소들로 어떻게 중첩돼 있는지의 구조예요. 동시에 CSS를 파싱해 CSSOM 트리를 만드는데, 이건 "어떤 요소를 무슨 색·크기로 꾸밀지"의 스타일 규칙이죠. 이 둘(구조 + 스타일)을 합쳐 렌더 트리를 만듭니다. 화면에 실제로 그려질 것만 추린 트리예요(예: display:none인 요소는 여기서 빠져요). 그다음 레이아웃 단계에서 각 요소가 화면 어디에, 얼마만 한 크기로 놓일지를 계산하고, 마지막 페인트 단계에서 그 계산대로 픽셀을 칠해 비로소 화면에 나타납니다.
한 가지 더. 페이지 안에 이미지·CSS·JS 파일 링크가 있으면, 브라우저는 그걸 받으려고 여정을 또 돕니다(Step 1~4를 그 파일에 대해 다시!). 그래서 한 페이지를 여는 데 실제로는 수십 번의 요청이 오가요. 또 자바스크립트가 나중에 DOM을 바꾸면 레이아웃·페인트를 다시 하는데(리플로우·리페인트), 이게 잦으면 화면이 버벅입니다. 다만 렌더링을 빠르게 하는 최적화의 깊은 부분은 웹 과목(html-css-js 등)에서 본격적으로 다뤄요. 여기선 "받은 HTML이 DOM → 렌더 트리 → 레이아웃 → 페인트를 거쳐 화면이 된다"는 큰 흐름이면 충분합니다.
🎯 면접에서는 이렇게 답하세요
"브라우저는 받은 HTML을 파싱해 DOM 트리(문서 구조)를, CSS를 파싱해 CSSOM 트리(스타일)를 만들고, 둘을 합쳐 화면에 그릴 것만 추린 렌더 트리를 구성합니다. 그다음 각 요소의 위치·크기를 계산하는 레이아웃, 픽셀을 칠하는 페인트를 거쳐 화면에 나타나요. 이 과정을 흔히 Critical Rendering Path라고 부릅니다. 페이지 안의 이미지·CSS·JS는 각각 또 요청해서 받아오고, 자바스크립트가 DOM을 바꾸면 레이아웃·페인트가 다시 일어나(리플로우·리페인트) 성능에 영향을 줍니다."
🙋 학생 질문 — "튜터님, CSS는 <head>에 위에 두고 JS는 <body> 끝에 두라고 배웠는데, 렌더링이랑 관련이 있나요?"
정확히 이 렌더링 과정 때문이에요. 아주 좋은 연결이에요.
렌더 트리를 만들려면 DOM과 CSSOM이 둘 다 필요하죠. 그래서 CSS를 다 받기 전엔 화면을 못 그려요(CSS는 렌더링을 막는 자원이에요). CSS를 <head> 위쪽에 두면 브라우저가 일찍 받아 빨리 화면을 그릴 준비를 합니다. 반대로 자바스크립트는 DOM을 멈춰 세워요 — 브라우저가 HTML을 파싱하다 <script>를 만나면, 그 스크립트가 DOM을 건드릴 수도 있으니 일단 파싱을 멈추고 스크립트부터 실행합니다. 그래서 무거운 JS를 <head>에 두면 화면이 늦게 떠요. JS를 <body> 끝에 두면 DOM을 거의 다 만든 뒤에 실행되니, 화면이 먼저 뜨고 스크립트가 나중에 도는 거죠. (요즘은 defer·async 속성으로 더 깔끔하게 푸는데, 그 실무 디테일은 웹 과목에서 다뤄요.) 원리는 결국 "렌더 트리에 뭐가 필요한가"에서 나옵니다.
Step 6: "서버는 한 대가 아니다 — 로드밸런싱·CDN·캐싱"
지금까지 우리는 "브라우저 하나가 서버 한 대와 대화한다"는 그림으로 여정을 그렸어요. 깔끔하지만, 현실은 다릅니다. 인스타그램에 동시 접속하는 사람이 수억 명인데, 서버 한 대가 그걸 다 받아낼 수 있을까요? 절대 못 합니다. 그래서 실제 서비스는 수많은 서버에 일을 흩뿌려 놓아요. 이 Step에서는 그 흩뿌림을 가능하게 하는 세 장치 — 로드밸런싱·CDN·캐싱 — 을 개념만 잡습니다. (깊은 운영은 인프라 과목의 영역이에요.)
먼저 로드밸런서(load balancer, 부하 분산기)입니다. 똑같은 코드를 돌리는 서버 여러 대 앞에 분배기를 하나 세우고, 들어오는 요청을 그 뒤의 서버들에 골고루 나눠줘요.
한 대로 못 받으니, 분배기 뒤에 똑같은 서버를 여러 대
Client ──▶ Load Balancer ──┬──▶ [ Server 1 ]
├──▶ [ Server 2 ]
└──▶ [ Server 3 ]
· 분배 방식: 라운드로빈(번갈아) · 최소 연결 수 등
· 헬스체크: 죽은 서버는 분배에서 빼고 살아있는 서버로만
· 무상태(C-2)라 어느 서버가 받아도 결과가 같다 ← 그래서 확장이 쉽다
분배 방식은 라운드로빈(번갈아 한 대씩)이나 "지금 가장 한가한 서버에게" 같은 규칙을 써요. 그리고 헬스체크로 죽은 서버는 분배 대상에서 빼버립니다. 여기서 지난 시간 배운 무상태(stateless, C-2)가 빛을 발해요 — HTTP가 무상태라 서버가 손님을 기억하지 않으니, 1번 요청은 Server 1이, 2번 요청은 Server 3이 받아도 아무 문제가 없습니다. "기억하지 않음"이 "아무 서버나 받아도 됨"을 만들고, 그게 곧 수평 확장(서버를 늘려 감당하기)의 힘이에요. 참고로 로드밸런서가 IP·포트만 보고 빠르게 분배하면 L4, URL·헤더 같은 내용까지 보고 똑똑하게 분배하면 L7이라고 부릅니다(L4/L7 운영의 깊은 부분은 인프라 과목에서 다뤄요).
두 번째는 CDN(Content Delivery Network, 콘텐츠 전송망)입니다. 서울 사용자가 미국에 있는 서버까지 매번 갔다 오면 거리가 멀어 느려요. 그래서 로고·이미지·CSS 같은 정적 콘텐츠(Step 4에서 본 그것)의 복사본을 전 세계 곳곳의 엣지 서버에 미리 뿌려둡니다. 사용자는 자기와 가장 가까운 엣지에서 받아 빠르고, 원본 서버(오리진)는 부담을 던 거죠. 세 번째 캐싱은 이 모든 단계에 깔린 "한 번 가져온 건 기억해두고 재사용"이라는 아이디어예요. 브라우저 캐시 → CDN 캐시 → 서버 앞단 캐시(예: 메모리 캐시) → 데이터베이스 순으로 여러 겹으로 쌓여, 뒤로 갈수록 느리고 비싼 곳까지 안 가도 되게 막아줍니다. A-2에서 본 "자주 쓰는 데이터를 가까운 빠른 곳에 둔다"는 캐시의 정신이, 컴퓨터 한 대를 넘어 인터넷 규모로 똑같이 반복되는 거예요.
🎯 면접에서는 이렇게 답하세요
"로드밸런서는 똑같은 서버 여러 대 앞에서 요청을 골고루 분배하는 장치예요. 라운드로빈 같은 규칙으로 나누고, 헬스체크로 죽은 서버를 빼며, IP·포트만 보면 L4, URL·헤더까지 보면 L7입니다. HTTP가 무상태라 어느 서버가 받아도 되니 이런 수평 확장이 가능해요. CDN은 정적 콘텐츠를 사용자 가까운 엣지 서버에 복사해 지연을 줄이고 원본 부하를 더는 장치고, 캐싱은 브라우저·CDN·서버·DB로 이어지는 여러 겹에서 '한 번 가져온 건 재사용'으로 느린 뒷단까지 안 가게 막는 공통 원리입니다."
🙋 학생 질문 — "튜터님, 로드밸런서랑 CDN이 둘 다 '여러 서버'를 쓰는 것 같은데 뭐가 다른 거예요?"
목적이 달라요. 한쪽은 부하를 나누고, 다른 쪽은 거리를 줄입니다.
로드밸런서가 푸는 문제는 "한 대로는 트래픽을 못 받는다"예요. 그래서 같은 데이터센터 안에 똑같은 서버를 여러 대 두고 일감을 나눠 줍니다. 핵심은 일감 분산이에요. 반면 CDN이 푸는 문제는 "사용자가 서버에서 너무 멀다"입니다. 서울 사용자가 미국 서버까지 가면 물리적 거리 때문에 느리니까, 콘텐츠 복사본을 서울 근처 엣지 서버에 미리 둬서 가까운 데서 받게 해요. 핵심은 거리 단축이고, 그래서 주로 어디서든 똑같은 정적 콘텐츠(이미지·CSS)에 잘 맞습니다. 정리하면 로드밸런서는 "누가 처리할지"(부하), CDN은 "어디서 가져올지"(거리)를 푸는 장치예요. 실제 서비스는 둘을 함께 씁니다.
Step 7: "REST API — 웹 API를 설계하는 약속"
이제 여정에서 한 걸음 물러나, 그 위에서 앱들이 데이터를 주고받는 방식을 봅니다. 브라우저와 서버, 혹은 서버와 서버가 HTTP로 대화할 때 — 무작정 아무렇게나 주소를 정하면 서로 혼란스럽겠죠. 그래서 "HTTP를 이렇게 쓰자"는 널리 합의된 약속이 생겼는데, 그게 REST(Representational State Transfer)입니다. 이 약속대로 만든 창구를 REST API라고 불러요.
REST의 핵심은 세 가지예요. 첫째, 세상의 모든 것을 자원(resource)으로 보고 주소(URI)로 표현합니다. 둘째, 그 자원에 무엇을 할지는 HTTP 메서드로 밝혀요(C-2에서 배운 그 메서드들이죠). 셋째, 통신은 무상태(C-2)입니다. 표로 보면 한눈에 들어와요.
| 하고 싶은 일 | 메서드 | 주소(URI) | 멱등성 |
|---|---|---|---|
| 게시물 42번 조회 | GET |
/posts/42 |
O |
| 새 게시물 생성 | POST |
/posts |
X |
| 게시물 42번 전체 수정 | PUT |
/posts/42 |
O |
| 게시물 42번 일부 수정 | PATCH |
/posts/42 |
△ |
| 게시물 42번 삭제 | DELETE |
/posts/42 |
O |
보이는 패턴이 있죠. 주소는 "무엇"(자원)을, 메서드는 "어떻게"(행위)를 나타냅니다. /posts/42라는 주소는 "42번 게시물"이라는 대상만 가리키고, 그걸 조회할지 지울지는 GET·DELETE 같은 메서드가 정해요. 그래서 좋아요를 누르는 건 POST /posts/42/likes처럼 표현합니다("42번 게시물의 좋아요를 하나 생성"이죠). 이렇게 자원과 행위를 깔끔하게 나눈 설계를 RESTful 하다고 말해요. 그리고 각 요청은 무상태라 그 자체로 완결돼야 합니다 — 필요한 정보(인증 토큰 등)를 매 요청에 담아 보내고, 서버는 이전 요청을 기억하지 않아요. 바로 이 무상태 덕분에 Step 6의 로드밸런서가 아무 서버에나 요청을 던질 수 있는 거고요.
REST는 지금도 웹 API의 가장 흔한 스타일이지만, 유일한 정답은 아니에요. 자원 중심이 안 맞는 곳도 있고, 더 유연한 방식(GraphQL 등)이나 곧 볼 양방향 방식(웹소켓)을 쓰기도 합니다. 실무 API 설계의 깊은 디테일은 백엔드·웹 과목에서 다루고, 여기선 "자원을 주소로, 행위를 메서드로, 무상태로"라는 REST의 골격이면 면접에 충분해요.
🎯 면접에서는 이렇게 답하세요
"REST는 HTTP를 이용해 웹 API를 설계하는 약속이에요. 핵심은 셋입니다. 자원을 URI로 표현하고(
/posts/42), 그 자원에 할 행위는 HTTP 메서드로 밝히며(GET 조회·POST 생성·PUT/PATCH 수정·DELETE 삭제), 통신은 무상태로 합니다. 즉 주소는 '무엇'을, 메서드는 '어떻게'를 나타내요. 이 원칙을 잘 지킨 설계를 RESTful 하다고 하고, 무상태이기 때문에 로드밸런서로 여러 서버에 자유롭게 분산할 수 있습니다. 다만 REST가 유일한 정답은 아니고, GraphQL이나 웹소켓 같은 다른 선택지도 있어요."
🙋 학생 질문 — "튜터님, 그냥 주소에 /getPost?id=42처럼 동사를 쓰면 안 되나요? 그게 더 직관적인데요."
동작은 해요. 하지만 REST의 정신과는 어긋납니다. 왜인지 보면 REST가 왜 그렇게 설계됐는지가 이해돼요.
REST에서 주소는 명사(자원), 행위는 메서드가 규칙이에요. /getPost처럼 주소에 동사를 넣으면, 같은 게시물 하나를 두고 /getPost·/deletePost·/updatePost처럼 주소가 행위마다 제각각 늘어나요. 반면 /posts/42 하나로 자원을 고정해두고 메서드(GET·DELETE·PUT)로 행위를 바꾸면, 주소 체계가 일관되고 예측 가능해집니다. 처음 보는 API라도 "아, /posts/42에 GET 하면 조회, DELETE 하면 삭제겠구나"가 바로 읽혀요. 게다가 메서드에는 C-2에서 배운 멱등성·안전성 같은 약속된 의미가 붙어 있어서, GET은 캐시해도 되고 DELETE는 여러 번 보내도 안전하다는 걸 중간 장치들(캐시·프록시)도 알아챕니다. 동사를 주소에 넣으면 이 약속이 다 깨져요. 그래서 "자원은 주소, 행위는 메서드"가 REST의 핵심 규칙입니다.
Step 8: "웹소켓 한 입 — HTTP가 못 하는 양방향"
마지막으로 HTTP의 근본적인 한계 하나와, 그걸 푸는 웹소켓을 가볍게 한 입 봅니다. C-2에서 배운 HTTP의 규칙을 떠올려보세요 — 항상 클라이언트가 먼저 묻고, 서버가 답한다였죠. 서버는 절대 먼저 말을 걸 수 없어요. 평소엔 이게 문제가 안 되는데, 딱 한 종류의 상황에서 곤란해집니다. 서버에서 일어난 일을 클라이언트에게 곧바로 알려야 할 때예요.
예를 들어 실시간 채팅을 생각해봐요. 상대가 메시지를 보내면 내 화면에 즉시 떠야 하는데, HTTP는 서버가 먼저 못 보내니 방법이 마땅찮아요. 그래서 옛날엔 클라이언트가 "새 메시지 있어요? …없어요? 지금은요?"를 1초마다 계속 물어봤어요(폴링, polling). 답이 대부분 "없음"인데도 계속 묻느라 낭비가 크죠. 이 문제를 제대로 푸는 게 웹소켓(WebSocket)입니다.
HTTP 요청-응답 vs 웹소켓 양방향
[HTTP] Client ──질문──▶ Server 한 번 묻고
Client ◀──답변── Server 한 번 답하면 끝. 서버가 먼저 못 보냄.
[폴링] Client ──있어?──▶ Server "없음"인데도
Client ──지금은?▶ Server 계속 물어봐야 함 (낭비)
[웹소켓] Client ◀══════▶ Server 한 번 연결하면 통로가 계속 열려,
양쪽 다 아무 때나 먼저 보낼 수 있음
웹소켓은 처음엔 평범한 HTTP 요청으로 시작해서 "이 연결을 웹소켓으로 바꾸자"고 업그레이드(upgrade)한 뒤, 그때부터는 양방향으로 계속 열린 통로를 유지합니다. 그래서 서버에 새 메시지가 도착하면 묻지 않아도 서버가 먼저 내 화면에 밀어줄 수 있어요. 실시간 채팅, 알림, 라이브 댓글, 온라인 게임처럼 서버가 능동적으로, 즉시 알려야 하는 곳에 딱 맞습니다. 반대로 그냥 페이지를 조회하거나 데이터를 한 번 가져오는 일은 굳이 통로를 계속 열어둘 필요가 없으니 REST(요청-응답)가 더 낫고요. "양방향 실시간이면 웹소켓, 단방향 조회면 REST"가 고르는 기준이에요. 깊은 구현은 백엔드·웹 과목에서 다루니, 오늘은 "HTTP가 못 하는 양방향을 웹소켓이 푼다"는 한 입이면 됩니다.
🎯 면접에서는 이렇게 답하세요
"HTTP는 항상 클라이언트가 먼저 요청하고 서버가 응답하는 단방향 구조라, 서버가 먼저 말을 걸 수 없어요. 그래서 실시간 채팅·알림처럼 서버가 즉시 알려야 하는 경우엔 예전엔 클라이언트가 계속 물어보는 폴링을 썼는데 낭비가 큽니다. 웹소켓은 HTTP로 시작해 연결을 업그레이드한 뒤 양방향으로 계속 열린 통로를 유지해서, 서버도 아무 때나 먼저 데이터를 보낼 수 있어요. 그래서 실시간 양방향이 필요하면 웹소켓, 단순 조회면 REST를 씁니다."
🙋 학생 질문 — "튜터님, 그냥 폴링을 아주 빠르게(0.1초마다) 하면 웹소켓이랑 똑같지 않나요?"
비슷해 보여도 비용이 완전히 달라요. 그래서 웹소켓이 따로 생긴 거예요.
폴링을 0.1초마다 하면, 새 메시지가 없어도 매번 HTTP 요청을 새로 보내야 합니다. 요청마다 헤더가 붙고, 연결을 재활용하더라도 "있어요? 없음. 있어요? 없음"을 끝없이 반복하니 서버와 네트워크에 헛일이 쌓여요. 그리고 아무리 빨라도 0.1초의 틈이 있어 진짜 '즉시'는 아니죠. 그 중간 단계로 롱 폴링(long polling)이라는 것도 있어요 — 서버가 답을 바로 안 주고 "새 메시지가 생길 때까지" 응답을 붙들고 있다가 생기면 그제야 돌려주는 방식이라 낭비를 좀 줄입니다. 하지만 근본 해법은 역시 웹소켓이에요. 한 번 통로를 열어두면 새 요청을 계속 만들 필요 없이, 데이터가 생긴 그 순간 양쪽이 바로 주고받으니까요. "빠른 폴링"은 흉내고, 웹소켓은 구조 자체가 양방향이라는 게 차이입니다.
마무리
오늘 배운 핵심 세 가지
🎯 하나, "URL을 치면 일어나는 일"의 전체 여정을 한 줄기로 꿰었습니다. 주소를 분해하고 → DNS로 도메인을 IP로 바꾸고 → TCP 3-way handshake로 통로를 열고 → TLS 핸드셰이크로 암호 통로를 얹고 → HTTP 요청을 보내면 → 서버가 라우팅·로직·DB 조회로 처리해 → HTTP 응답을 돌려주고 → 브라우저가 렌더링해 화면을 그립니다. 지난 시간 따로 배운 3-way handshake·TLS·HTTP가 여기서 하나의 순서로 합류했어요.
🎯 둘, 그 여정을 단계별로 깊이 봤습니다. DNS는 도메인을 IP로 바꾸는 계층적 전화번호부(루트 → TLD → 권한 서버, 재귀 질의)고, 렌더링은 HTML·CSS를 DOM·CSSOM으로 파싱해 렌더 트리 → 레이아웃 → 페인트로 화면을 그리는 과정이에요. 그리고 현실의 서비스는 서버 한 대가 아니라 로드밸런서(부하 분산)·CDN(거리 단축)·캐싱(재사용)으로 수많은 서버에 흩어져 있고, 무상태이기에 그 분산이 가능했습니다.
🎯 셋, 그 위에서 앱이 대화하는 두 방식을 잡았어요. REST는 "자원은 주소(URI)로, 행위는 메서드로, 통신은 무상태로"라는 웹 API 설계 약속이고, 웹소켓은 HTTP가 못 하는 양방향 실시간을 위해 연결을 계속 열어두는 방식입니다. 단방향 조회면 REST, 양방향 실시간이면 웹소켓 — 이 기준으로 고릅니다.
다음 시간 예고
이걸로 네트워크 카테고리(C-1~C-3)가 마무리됐어요. 계층과 TCP/UDP(C-1), HTTP와 HTTPS(C-2), 그리고 오늘 그 전부를 꿴 웹의 동작 원리까지 — 이제 "내가 친 요청이 어떻게 서버에 닿고 화면에 뜨나"를 한 호흡에 설명할 수 있게 됐습니다.
그런데 오늘 Step 4에서 슬쩍 미뤄둔 게 하나 있었죠. 서버가 요청을 처리할 때 거의 항상 데이터베이스를 조회한다고 했어요. 그 데이터베이스가 정확히 무엇이고, 어떻게 데이터를 안전하게(트랜잭션·ACID) 그리고 빠르게(인덱스) 다루는지 — 다음 시간(D-1)에는 바로 이 면접에 나오는 데이터베이스로 넘어갑니다. "트랜잭션이 뭔가요", "인덱스는 왜 조회를 빠르게 하나요", "SQL과 NoSQL은 언제 무엇을 쓰나요" 같은, 네트워크만큼이나 단골인 질문들을 원리부터 잡을 거예요. "이 API가 왜 느려요?"라는 물음의 답을 한 칸 더 깊이 파고드는 시간이 됩니다. 다음 시간에 만나요.
과제
[기초] URL 여정을 직접 그려보기
연필과 종이만 있으면 됩니다. 오늘 배운 전체 순서를 손으로 한 번 정리해봐요.
https://www.example.com/photos/7을 주소창에 치고 엔터를 눌렀다고 합시다. 화면에 페이지가 뜨기까지 일어나는 일을 순서대로 여덟 단계로 적어보세요. 각 단계를 한 줄로만 설명하면 됩니다(DNS·TCP·TLS·HTTP 요청·서버 처리·응답·렌더링이 들어가야 해요).- 위 여덟 단계 중, 지난 두 시간(C-1·C-2)에 이미 배운 단계가 어느 것인지 짚고, 각각 C-1에서 배운 건지 C-2에서 배운 건지 표시해보세요.
- (선택) 브라우저 개발자 도구의 Network 탭을 열고 아무 사이트나 새로고침해보세요. 한 페이지를 여는 데 요청이 몇 개나 오가는지 세어보고, 왜 하나가 아니라 여러 개인지 한 줄로 설명해보세요(힌트: Step 5).
[응용] 시나리오마다 어떤 장치가 일하는지 판단하기
아래 상황에서 오늘 배운 장치(DNS·로드밸런서·CDN·캐시) 중 무엇이 관련되는지 고르고, 한 줄씩 이유를 설명해보세요.
- 어떤 사이트를 두 번째로 방문했더니 첫 방문보다 훨씬 빨리 떴습니다. 이걸 빠르게 만든 장치들을 떠올려보고, 각각 무엇을 건너뛰거나 재사용한 건지 설명해보세요.
- 서울과 뉴욕에 있는 두 사용자가 같은 로고 이미지를 받았는데, 둘 다 빨랐습니다. 어떻게 둘 다 가까운 곳에서 받았을지, 어떤 장치 덕분인지 설명해보세요.
- 한 서버가 갑자기 죽었는데도 사용자들은 아무 문제 없이 서비스를 계속 썼습니다. 이게 가능하려면 어떤 장치가 있어야 하고, HTTP의 어떤 성질(C-2)이 이걸 가능하게 하는지 연결해 설명해보세요.
[심화] "URL을 치면 일어나는 일" 면접 답변 만들기
오늘 배운 전체를 엮어, 면접 최고 빈출 질문에 대한 자기만의 답변을 직접 써보는 과제입니다.
- "URL을 치면 무슨 일이 일어나나요?"에 대한 답변을, DNS → TCP → TLS → HTTP 요청 → 서버 처리 → 응답 → 렌더링 순서로 한 호흡에 말하듯 정리해보세요(소리 내어 한 번 말해보면 더 좋아요).
- 면접관이 파고들 꼬리 질문에 대비해보세요. "DNS는 구체적으로 어떻게 IP를 찾나요?"에 대한 답을 루트·TLD·권한 서버·재귀 리졸버를 써서 두세 문장으로 준비하고, "서버가 응답을 보낸 뒤 브라우저는 뭘 하나요?"에 대한 답을 렌더링 과정(DOM·CSSOM·렌더 트리·레이아웃·페인트)으로 준비하세요.
- (선택) "실제 서비스는 서버 한 대로 이걸 다 하나요?"라는 질문이 들어오면, 로드밸런서·CDN·캐싱을 끌어와 "서버는 한 대가 아니다"를 설명하는 답을 한 단락으로 준비해보세요. 무상태(C-2)가 왜 이 확장을 가능하게 하는지도 함께 엮으면 좋습니다.
생각해볼 주제
1. 왜 DNS는 한곳에서 관리하지 않고 계층으로 쪼갰을까?
전 세계 도메인-IP 짝을 거대한 표 하나에 담아 한 기관이 관리하면, 구조가 훨씬 단순할 것 같습니다. 찾을 때 한 군데만 물어보면 되니까요. 그런데 DNS는 굳이 루트·TLD·권한 서버로 책임을 잘게 나눠 가졌어요. 만약 그 중앙 표 하나가 멈추면 어떤 일이 벌어질지, 그리고 전 세계에서 1초에 수백만 번 쏟아지는 조회를 한 곳이 감당할 수 있을지 떠올려보세요. "한곳에 모으는 단순함"과 "여러 곳에 나누는 견고함·확장성" 사이의 트레이드오프를, A·B 카테고리에서 본 다른 분산·계층 구조(메모리 계층, 캐시)와 같은 원리로 생각해보세요.
2. "기억하지 않는" 무상태가 어떻게 "무한히 늘리는" 확장의 힘이 될까?
언뜻 생각하면 서버가 손님을 기억하는 편이 더 똑똑하고 편리할 것 같습니다. 그런데 오늘 본 로드밸런서는 무상태이기 때문에 비로소 아무 서버에나 요청을 던질 수 있었어요. 만약 서버가 각자 손님을 기억한다면, 같은 손님의 다음 요청은 반드시 그 손님을 기억하는 서버로만 보내야 하고, 서버를 늘리거나 한 대가 죽었을 때 그 기억은 어떻게 될지 생각해보세요. "기억하지 않음"이라는 불편을 감수한 대가로 무엇을 얻었는지, 그리고 그럼에도 로그인 같은 상태를 유지해야 할 때 그 상태를 어디에 두면 좋을지(힌트: 서버 바깥의 공용 저장소)를 함께 저울에 올려보세요.
3. REST가 늘 정답일까 — 웹소켓·다른 방식은 언제 더 나을까?
REST는 웹 API의 가장 흔한 스타일이고, 깔끔한 자원 중심 설계라는 큰 장점이 있습니다. 하지만 오늘 본 실시간 채팅에서는 "클라이언트가 먼저 묻는다"는 REST의 전제가 오히려 걸림돌이 됐죠. 모든 통신을 REST 하나로 밀어붙이면 어떤 상황에서 불편해질지, 반대로 모든 걸 웹소켓으로 만들면 단순 조회에 무엇이 과한지 생각해보세요. "널리 쓰이는 표준"이 곧 "모든 상황의 정답"은 아니라는 점을, 통신의 성격(단방향 조회냐, 양방향 실시간이냐)에 따라 도구를 고르는 관점에서 풀어보세요.
✅ 예시 답안정답 보기
이 문서는 C-3 「웹이 동작하는 원리」의 과제와 생각해볼 주제에 대한 예시답안입니다. 정답을 외우는 용도가 아니라, 원리를 어떻게 풀어 답하는지 흐름을 참고하는 용도로 보세요.
과제 예시답안
🎯 [과제 1 예시답안] URL 여정을 직접 그려보기
채점 포인트
| 항목 | 배점 | 기준 |
|---|---|---|
| 여덟 단계 순서 | 45% | DNS·TCP·TLS·HTTP 요청·서버 처리·응답·렌더링이 올바른 순서로 들어갔는가 |
| 지난 시간 단계 짚기 | 30% | 이미 배운 단계를 C-1/C-2로 정확히 표시했는가 |
| 요청 개수 관찰 (선택) | 25% | 한 페이지에 요청이 여러 개인 이유를 설명했는가 |
풀이 예시
1. https://www.example.com/photos/7 여정 여덟 단계
- URL 분해 —
https(프로토콜)·www.example.com(도메인)·/photos/7(경로)로 나눈다. - DNS 조회 —
www.example.com의 IP 주소를 DNS에 물어 받아온다. - TCP 3-way handshake — 그 IP의 서버와 연결 통로를 연다(SYN → SYN+ACK → ACK).
- TLS 핸드셰이크 —
https이므로 인증서 확인 + 대칭키 합의로 암호 통로를 얹는다. - HTTP 요청 전송 —
GET /photos/7로 "이 자원 줘"를 보낸다. - 서버 처리 — 라우팅 → 앱 로직 → DB 조회 → 응답 생성.
- HTTP 응답 수신 — 상태 코드와 함께 HTML·CSS·JS·이미지가 돌아온다.
- 브라우저 렌더링 — HTML을 파싱해 DOM·CSSOM → 렌더 트리 → 레이아웃 → 페인트로 화면을 그린다.
2. 지난 시간에 배운 단계
- 3번 TCP 3-way handshake → C-1(계층 모델과 TCP/UDP)에서 배운 연결 수립.
- 4번 TLS 핸드셰이크 → C-2(HTTP와 웹 통신)에서 배운 HTTPS 암호화.
- 5·7번 HTTP 요청·응답 → C-2에서 배운 메시지 구조·메서드·상태 코드.
- 2번 DNS, 6번 서버 처리, 8번 렌더링은 오늘(C-3) 새로 배운 단계입니다.
3. (선택) 요청 개수 관찰
한 페이지를 여는데 Network 탭에 요청이 수십 개씩 잡힙니다. 첫 요청으로 받은 HTML 안에 이미지·CSS·JS 링크가 들어 있고, 브라우저가 그것들을 받으려고 각각 또 요청을 보내기 때문이에요(Step 5). 그래서 "한 페이지 = 요청 하나"가 아니라 "한 페이지 = 수십 번의 작은 여정"입니다.
💡 튜터의 한마디 — 이 여덟 단계의 순서만 몸에 익히면 면접의 절반은 끝납니다. 외울 때 "주소 찾고(DNS) → 연결 맺고(TCP·TLS) → 주고받고(HTTP) → 그린다(렌더링)"의 네 덩어리로 묶으면 중간에 빠뜨리지 않아요.
🎯 [과제 2 예시답안] 시나리오마다 어떤 장치가 일하는지 판단하기
채점 포인트
| 항목 | 배점 | 기준 |
|---|---|---|
| 캐시로 빨라진 재방문 | 35% | DNS·연결·정적 파일 캐시 중 무엇이 재사용·생략됐는지 짚었는가 |
| CDN 거리 단축 | 30% | 엣지 서버 복사본으로 가까운 곳에서 받는다는 점을 설명했는가 |
| 무중단 + 무상태 연결 | 35% | 로드밸런서·헬스체크를 들고, 무상태(C-2)와 연결했는가 |
풀이 예시
1. 두 번째 방문이 빠른 이유 — 캐싱
여러 단계가 재사용되거나 생략됩니다. ① DNS 조회는 한 번 찾은 IP를 TTL 동안 기억해둬서 다시 묻지 않고 바로 씁니다. ② TCP·TLS 연결도 잠깐 열어둔 걸 재활용(keep-alive)할 수 있고요. ③ 무엇보다 로고·CSS·JS 같은 정적 파일을 브라우저가 캐시에 저장해뒀다가, 재요청 없이 그대로 꺼내 씁니다. 그래서 "여정 전체"가 아니라 "캐시 없는 단계만" 실행돼 훨씬 빨라요.
2. 서울·뉴욕 둘 다 빠른 이유 — CDN
로고 이미지 같은 정적 콘텐츠의 복사본을 전 세계 엣지 서버에 미리 뿌려둔 덕분입니다(CDN). 서울 사용자는 서울 근처 엣지에서, 뉴욕 사용자는 뉴욕 근처 엣지에서 받으니 둘 다 가까운 곳에서 받아 빠릅니다. 원본 서버까지 멀리 갈 필요가 없어요.
3. 서버가 죽어도 무중단 — 로드밸런서 + 무상태
여러 서버 앞에 로드밸런서가 있고, 헬스체크로 죽은 서버를 분배 대상에서 빼면 됩니다. 살아있는 서버로만 요청을 보내니 사용자는 장애를 못 느껴요. 이게 가능한 결정적 이유는 HTTP가 무상태(C-2)라는 점입니다. 서버가 손님을 기억하지 않으니, 죽은 서버가 받던 손님의 다음 요청을 다른 서버가 받아도 아무 문제가 없거든요.
💡 튜터의 한마디 — 세 장치를 한 줄로 구분하면 헷갈리지 않아요. 캐싱은 "한 번 가져온 걸 재사용", CDN은 "가까운 곳에서 받기"(거리), 로드밸런서는 "여러 서버에 나눠 받기"(부하). 그리고 이 분산을 떠받치는 토대가 무상태입니다.
🎯 [과제 3 예시답안] "URL을 치면 일어나는 일" 면접 답변 만들기
채점 포인트
| 항목 | 배점 | 기준 |
|---|---|---|
| 한 호흡 전체 답변 | 40% | DNS→TCP→TLS→요청→처리→응답→렌더링을 끊김 없이 정리했는가 |
| 꼬리 질문 대비 | 40% | DNS 동작과 렌더링 과정을 각각 원리로 답할 수 있는가 |
| 확장 답변 (선택) | 20% | 로드밸런서·CDN·캐싱을 무상태와 엮어 설명했는가 |
풀이 예시
1. 한 호흡 답변
"주소창에 URL을 치면, 먼저 브라우저가 주소를 프로토콜·도메인·경로로 분해합니다. 그다음 DNS로 도메인을 IP 주소로 바꾸고, 그 서버와 TCP 3-way handshake로 연결을 맺어요. HTTPS면 그 위에 TLS 핸드셰이크로 암호화 통로를 더하고요. 이제 HTTP 요청을 보내면 서버가 라우팅·로직·DB 조회를 거쳐 HTTP 응답을 돌려주고, 브라우저가 받은 HTML을 파싱해 렌더링으로 화면을 그립니다. 한마디로 DNS → TCP → TLS → HTTP 요청·응답 → 렌더링입니다."
2. 꼬리 질문 대비
- "DNS는 어떻게 IP를 찾나요?" — "브라우저가 재귀 리졸버에게 질문을 맡기면, 리졸버가 루트 → TLD(.com) → 권한 서버 순으로 도메인을 오른쪽부터 좁혀 가며 물어 최종 IP를 찾아옵니다. 각 단계는 자기 책임 범위만 알고, 결과는 TTL 동안 캐시돼요."
- "응답을 받은 뒤 브라우저는 뭘 하나요?" — "HTML을 파싱해 DOM 트리(구조)를, CSS를 파싱해 CSSOM 트리(스타일)를 만들고, 둘을 합쳐 렌더 트리를 구성합니다. 그다음 위치·크기를 계산하는 레이아웃, 픽셀을 칠하는 페인트를 거쳐 화면에 나타나요."
3. (선택) 확장 답변
"실제 서비스는 서버 한 대가 아니라, 로드밸런서로 똑같은 서버 여러 대에 요청을 분산하고, 정적 콘텐츠는 CDN으로 사용자 가까운 엣지에서 받게 하며, 캐싱을 여러 겹으로 깔아 느린 뒷단까지 안 가게 합니다. 이 분산이 가능한 건 HTTP가 무상태라 어느 서버가 받아도 결과가 같기 때문이에요."
💡 튜터의 한마디 — 이 질문은 면접관이 꼬리를 물며 깊이를 떠보는 단골입니다. 그래서 한 호흡 골격을 먼저 던지고, 면접관이 파고드는 단계(보통 DNS나 렌더링)를 한 단계 더 깊이 답하는 전략이 좋아요. 골격이 흔들리지 않으면 어느 단계로 들어와도 받아칠 수 있습니다.
생각해볼 주제 예시답안
🤔 [생각해볼 주제 1 예시답안] 왜 DNS는 한곳에서 관리하지 않고 계층으로 쪼갰을까?
[문제 상황 요약]
전 세계 도메인-IP 짝을 거대한 표 하나에 담아 한 기관이 관리하면 구조가 단순해 보인다. 그런데 DNS는 굳이 루트·TLD·권한 서버로 책임을 잘게 나눴다. 중앙 집중의 단순함과 계층 분산의 견고함·확장성 사이의 트레이드오프를 저울에 올려보는 문제다.
[튜터의 가이드 및 해설]
중앙 집중 표 하나의 문제를 두 가지로 짚으면 됩니다. 첫째는 단일 장애점(single point of failure)이에요. 그 한 곳이 멈추는 순간 전 세계 인터넷이 도메인을 못 찾아 통째로 멈춥니다. 둘째는 부하입니다. 전 세계에서 1초에 수백만 번 쏟아지는 조회를 한 곳이 감당하는 건 불가능에 가까워요.
계층으로 쪼개면 이 둘이 동시에 풀립니다. 책임이 루트·TLD·권한 서버로 나뉘어 있으니 어느 한 부분이 흔들려도 전체가 멈추지 않고(루트 서버도 전 세계에 여러 벌로 복제돼 있어요), 조회 부하도 여러 단계와 수많은 서버로 분산됩니다. 게다가 한 번 찾은 결과를 각 단계가 TTL 동안 캐시하니, 실제로는 매번 루트까지 가지도 않아요.
여기서 중요한 통찰은, 이게 이 과목에서 본 다른 구조들과 같은 결이라는 점입니다. A·B에서 본 메모리 계층(자주 쓰는 건 가까운 빠른 곳에)이나 캐시도 똑같이 "한곳에 다 모으지 않고, 계층으로 나눠 가까운 데서 빠르게"라는 아이디어였죠. "중앙 집중은 단순하지만 깨지기 쉽고, 분산·계층은 복잡하지만 견고하고 확장된다"는 트레이드오프는 컴퓨터 시스템 전반을 관통합니다.
🎯 면접관을 홀리는 핵심 멘트
"DNS를 계층으로 나눈 건 단일 장애점을 없애고 부하를 분산하기 위해서입니다. 표 하나에 다 모으면 그 한 곳이 멈출 때 인터넷 전체가 멈추고, 전 세계 조회를 혼자 감당할 수도 없어요. 루트·TLD·권한 서버로 책임을 나누고 TTL 캐시를 더하면 견고함과 확장성을 동시에 얻습니다. 사실 이건 메모리 계층이나 캐시와 같은 결 — '한곳에 모으는 단순함'과 '나누는 견고함'의 트레이드오프죠."
🤔 [생각해볼 주제 2 예시답안] "기억하지 않는" 무상태가 어떻게 "무한히 늘리는" 확장의 힘이 될까?
[문제 상황 요약]
서버가 손님을 기억하는 편이 똑똑하고 편리해 보인다. 그런데 로드밸런서는 무상태이기 때문에 비로소 아무 서버에나 요청을 던질 수 있었다. "기억하지 않음"의 불편을 감수한 대가로 무엇을 얻는지, 그럼에도 로그인 상태는 어디에 둬야 하는지 저울에 올려보는 문제다.
[튜터의 가이드 및 해설]
서버가 손님을 각자 기억한다고 가정해보면 문제가 선명해집니다. 재훈이가 로그인한 정보를 Server 1이 기억하고 있다면, 재훈이의 다음 요청은 반드시 Server 1로만 보내야 해요. 그럼 로드밸런서는 "이 손님은 어느 서버가 기억하지?"를 일일이 추적해야 하고(끈적한 연결, sticky session), 그 Server 1이 죽으면 재훈이의 기억도 함께 날아가 다시 로그인해야 합니다. 서버를 새로 늘려도 기존 손님들은 자기를 기억하는 옛 서버에 묶여 있어 부하가 고르게 안 퍼져요.
무상태는 이 모든 걸 풀어버립니다. 서버가 아무것도 기억하지 않으니, 재훈이의 요청을 1번 서버가 받든 3번 서버가 받든 똑같이 처리돼요. 그래서 로드밸런서가 자유롭게 분산하고, 서버를 늘리거나 줄이거나 한 대가 죽어도 손님 경험이 끊기지 않습니다. "기억하지 않는다"는 불편이 곧 "아무 서버나 받아도 된다"는 자유가 되고, 그게 수평 확장의 힘이에요.
그럼 로그인 같은 상태는 어디에 둘까요? 핵심은 상태를 개별 서버 바깥으로 빼는 거예요. 모든 서버가 함께 보는 공용 저장소(예: 세션을 담는 외부 캐시나 DB)에 상태를 두면, 어느 서버가 요청을 받든 그 공용 저장소를 조회해 "아, 로그인한 재훈이구나"를 알 수 있습니다. 서버 자신은 여전히 무상태이고, 상태는 공용 공간에 모아두는 거죠. 무상태의 확장성을 지키면서 로그인도 유지하는 실무의 표준 패턴입니다.
🎯 면접관을 홀리는 핵심 멘트
"무상태는 서버가 손님을 기억하지 않기 때문에, 어느 서버가 요청을 받아도 결과가 같습니다. 그래서 로드밸런서가 자유롭게 분산하고, 서버를 늘리거나 한 대가 죽어도 서비스가 안 끊겨요 — 이게 수평 확장의 토대입니다. 로그인 같은 상태는 개별 서버가 아니라 모든 서버가 함께 보는 공용 저장소에 둬서, 서버는 무상태로 유지하면서도 상태를 잃지 않게 합니다."
🤔 [생각해볼 주제 3 예시답안] REST가 늘 정답일까 — 웹소켓·다른 방식은 언제 더 나을까?
[문제 상황 요약]
REST는 웹 API의 가장 흔한 스타일이고 깔끔한 자원 중심 설계라는 장점이 있다. 하지만 실시간 채팅에서는 "클라이언트가 먼저 묻는다"는 전제가 걸림돌이 됐다. 통신의 성격(단방향 조회냐, 양방향 실시간이냐)에 따라 도구를 고르는 관점을 풀어보는 문제다.
[튜터의 가이드 및 해설]
REST의 전제는 요청-응답, 즉 "클라이언트가 먼저 묻고 서버가 답한다"입니다. 게시물 조회, 글 작성, 프로필 수정처럼 클라이언트가 행동을 시작하는 일에는 이 모델이 더없이 잘 맞아요. 자원을 주소로, 행위를 메서드로 깔끔하게 표현하고, 무상태라 확장도 쉽고요.
문제는 서버가 먼저 알려야 하는 상황입니다. 실시간 채팅에서 상대 메시지가 도착했을 때, REST로는 서버가 먼저 못 보내니 클라이언트가 계속 물어봐야(폴링) 하는데 이건 낭비가 크고 진짜 '즉시'도 아니에요. 여기선 양방향 통로를 계속 열어두는 웹소켓이 자연스럽습니다. 반대로 모든 통신을 웹소켓으로 만들면, 한 번 받고 끝날 단순 조회에도 연결을 계속 유지하는 비용이 들어 과합니다.
그래서 핵심은 통신의 성격으로 도구를 고르는 거예요. 단방향 조회·생성·수정처럼 클라이언트가 시작하는 일은 REST, 채팅·알림·라이브처럼 서버가 능동적으로 즉시 알려야 하는 일은 웹소켓. 실제 서비스는 둘을 함께 씁니다 — 피드 조회는 REST로, 새 알림 푸시는 웹소켓으로요. 더 나아가 클라이언트가 원하는 데이터만 골라 받는 GraphQL처럼 다른 선택지도 있고요. "널리 쓰이는 표준"이 "모든 상황의 정답"은 아니라는 것, 그리고 상황에 맞는 도구를 고를 줄 아는 것이 한 단계 높은 안목입니다.
🎯 면접관을 홀리는 핵심 멘트
"REST는 클라이언트가 먼저 묻는 요청-응답에 최적이라, 조회·생성·수정 같은 대부분의 통신에 잘 맞습니다. 하지만 서버가 먼저 알려야 하는 실시간 채팅·알림에서는 폴링의 낭비를 피하려고 양방향 통로를 유지하는 웹소켓이 낫고요. 핵심은 통신이 단방향 조회냐 양방향 실시간이냐로 도구를 고르는 거예요. 실무에선 피드는 REST, 알림은 웹소켓처럼 둘을 함께 씁니다."
