앞선 글에서 URL과 HTTP라는 두 축을 다뤘다. 이번에는 그 조각들을 합쳐 본다. 초기 웹 아키텍처는 한 문장으로 요약된다. 브라우저는 원격지에 있는 문서를 가져와 화면에 그려 주는 뷰어였고, 그 위에 꾸밈과 움직임과 기능이 차례로 얹히며 지금의 웹이 됐다. 이 글은 그 진화의 순서를 학습 지도처럼 따라간다. 순서를 알면 오늘의 복잡한 웹도 결국 같은 골격 위에 있다는 것이 보인다.
요청과 응답은 클라이언트와 서버 사이를 오간다
웹은 서비스를 요청하는 클라이언트와 제공하는 서버로 나뉜다. 우리가 쓰는 브라우저가 클라이언트다. 둘 다 인터넷에 연결되어 있고, 그 사이를 요청과 응답이 오간다. 전체 흐름은 다음과 같다
[브라우저] [웹 서버] │ │ (먼저 실행되어 접속 대기) │ ① URL 입력 → DNS 서버에 질의 → IP 주소 획득 │ │ ② TCP/IP 연결 │ │ ────────────────────────────────────────▶ │ │ ③ HTTP 요청 (GET /index.html) │ │ ────────────────────────────────────────▶ │ │ │ ④ 저장된 HTML 문서를 찾는다 │ ⑤ HTTP 응답 (200 OK + 문서) │ │ ◀───────────────────────────────────────── │ │ ⑥ 구문 분석 → 렌더링 → 화면 표시 │
순서를 짚어 보면 이렇다. 서버는 클라이언트보다 먼저 실행되어 접속을 기다린다. 사용자가 주소창에 URL을 입력하면 브라우저는 먼저 DNS 서버에 도메인 네임을 물어 IP 주소를 알아낸다. 그 IP로 TCP/IP 연결을 맺고, 그 연결 위에서 HTTP 요청을 보낸다. 가장 많이 쓰는 요청 메서드는 GET이며 “이 경로의 문서를 보내 달라”는 뜻이다. 서버는 2차 메모리(하드디스크 등)에 저장해 둔 HTML 문서를 찾아 200 OK 응답과 함께 돌려준다
실제로 클라이언트와 인터넷 사이에는 L2 스위치와 라우터가, 서버 앞단에는 라우터와 함께 서버를 보호하는 보안 장치들이 자리한다. 입문 단계에서는 그런 구성 요소가 존재한다는 정도만 알아 두고, 핵심 흐름인 요청과 응답에 집중하면 된다
브라우저는 받은 HTML을 구문 분석하고 렌더링한다
문서를 받은 브라우저는 곧바로 화면에 뿌리지 않는다. 두 단계를 거친다
첫 단계는 구문 분석(parsing)이다. HTML 문서는 일반 텍스트와 달리 태그와 텍스트가 섞여 있다. 텍스트는 내용이고, 태그는 그 텍스트가 어떤 속성을 갖는지 규정하는 정보다. 파서는 이 둘을 분리해 문서의 구조를 파악한다. 둘째 단계는 렌더링(rendering)이다. 분석한 구조와 속성에 따라 화면에 실제로 내용을 그린다
이 두 단계를 합치면 브라우저의 본질이 드러난다. 초기 브라우저는 문서 뷰어였다. 다만 보통의 문서 뷰어와 달리, 내 컴퓨터가 아니라 인터넷 너머 다른 컴퓨터에 저장된 파일을 가져와 보여 준다. 즉 원격지 문서 뷰어다. GET 요청은 그 문서를 읽기 위한 다운로드에 가깝고, 문서가 제대로 전달되면 200 OK와 함께 렌더링된다. 봐야 할 문서가 여러 개면 이 과정을 반복할 뿐이다. 이것이 초창기 웹의 전부였다
정적 문서에 꾸밈을 더하다: CSS가 분리된 이유
문서를 원격으로 편하게 보게 되자 곧 다음 욕심이 생긴다. 내용뿐 아니라 꾸밈도 중요해진 것이다. 글꼴을 바꾸고 특정 단어를 굵게 만드는 식이다. 처음에는 이 꾸밈 정보를 HTML 문서 안에 직접 넣었다
여기서 소프트웨어 설계의 대원칙을 짚어야 한다. 유지보수하기 좋은 코드의 기본은 기능을 독립적으로 분리하는 것이다. 흔히 셋으로 나눈다. 화면을 다루는 UI, 처리를 담당하는 로직, 그리고 데이터다. 이 셋이 섞이면 의존 관계가 생긴다. 데이터 하나를 바꿨을 뿐인데 UI는 물론 로직까지 같이 고쳐야 하는 상황이 그렇다. 한 곳을 수정하면 여러 곳이 딸려 바뀌는 구조는 유지보수성이 떨어진다. 그래서 떼어 놓는다. 이 원칙을 프로그래밍 언어 차원에 녹인 것이 객체 지향이다
HTML 안에 꾸밈을 넣은 방식은 바로 이 원칙을 어긴 것이었다. 데이터(내용)와 UI(표현)가 한 파일에 섞였다. 그래서 꾸밈 정보를 별도 파일로 떼어 냈고, 그렇게 등장한 것이 CSS(Cascading Style Sheets)다. 1994년 Håkon Wium Lie가 구조와 표현을 분리하자고 제안했고, W3C는 1996년 12월 첫 명세인 CSS1을 발표했다. 이후 HTML이 내용을, CSS가 표현을 맡으면서 문서는 한층 미려해졌다
내용은 글자만이 아니다. 버너스 리가 웹으로 보려던 논문에도 사진과 그림이 들어 있었다. 그래서 이미지 같은 자료까지 함께 전송되어 하나의 문서로 조립됐다. 단순 텍스트를 넘어 복합적인 문서 표현이 가능해진 것이다
이때 서버가 하는 일은 단순하다. HTML, CSS, 이미지 파일을 미리 2차 메모리에 저장해 두었다가 요청이 오면 그대로 보낸다. 도서관의 책이 누가 읽는다고 내용이 바뀌지 않듯, 이런 문서는 요청에 따라 내용이 달라지지 않는다. 이를 정적 문서(static document)라고 부른다
정적 문서에 움직임을 더하다: 자바스크립트의 탄생
다음 욕심은 움직임이었다. 화면에 동작을 주려면 그 동작을 기술할 규칙, 즉 스크립트가 필요했다. 초기 브라우저 회사였던 넷스케이프(Netscape)가 이 결정을 내렸다
넷스케이프가 만든 스크립트 언어는 이름을 세 번 바꿨다. 1995년 5월 Mocha라는 이름으로 시작해, 같은 해 9월 Navigator 2.0과 함께 LiveScript로 공개됐고, 그해 12월 JavaScript로 최종 개명됐다. 마지막 이름은 당시 인기를 끌던 Java의 명성에 편승하려는 마케팅 선택이었다. 언어 자체는 Java와 직접적인 관계가 없다
자바스크립트가 흥미로운 점은 실행 위치에 있다. 자바스크립트는 코드, 즉 프로그램이다. 그런데 이 코드도 처음에는 서버에 저장되어 있다. HTML, CSS와 함께 클라이언트로 다운로드된 뒤, 실행은 클라이언트 쪽(브라우저)에서 이루어진다. 코드가 사는 곳과 실행되는 곳이 다른 셈이다. 이 클라이언트 사이드 실행이 자바스크립트의 최초 형태였다
자바스크립트가 돌아가려면 이를 해석해 실행하는 엔진이 필요하다. 대표적인 것이 구글이 만든 V8 엔진이다. V8은 넷스케이프 시절보다 한참 뒤(2008년)에 나온 현대적 엔진이지만, 자바스크립트 엔진의 역할을 보여 주는 대표 사례다. 정리하면 브라우저는 세 요소로 이루어진다
| 구성 요소 | 역할 |
|---|---|
| 파서 | HTML·CSS의 태그와 텍스트를 분리해 구조를 분석 |
| 렌더링 엔진 | 분석한 구조를 화면에 그림 |
| 자바스크립트 엔진 | 내려받은 자바스크립트 코드를 실행 (예: V8) |
문서 요청에서 서비스 호출로 – CGI의 등장
여기까지가 초기 웹의 모습이다. 그런데 GET 요청이 늘 정적 문서만 가져온 것은 아니다. 요청은 점차 서버의 기능 실행까지 포함하게 됐다
처음에 웹 서버의 목표는 데이터 송수신이었다. 저장된 파일을 찾아 보내는 일이다. 여기에 기능 요청이 덧붙는다. 함수에 비유할 수 있다. 입력을 받아 연산하고 결과를 돌려주는 동작이다. 이 기능을 일종의 서비스로 볼 수 있다. 그 결과, 클라이언트에도 로직(자바스크립트)이 있고 서버에도 로직이 존재하는 구조가 됐다
서버 쪽 로직은 과거에 CGI(Common Gateway Interface)로 구현했다. 요청이 들어오면 CGI가 실행 코드를 돌려 새 프로세스를 만들고, 연산한 결과를 응답으로 내보낸다. 즉 서버의 역할이 “저장된 문서를 보내는 것”에서 “요청을 받아 코드를 실행하고 그 결과를 보내는 것”으로 확장됐다. 정적 문서를 넘어 동적인 결과를 만들어 내기 시작한 분기점이다.
정리
한 줄로 요약하면, 초기 웹 아키텍처는 원격지 문서 뷰어 위에 꾸밈(CSS), 움직임(자바스크립트), 기능(CGI)이 차례로 얹힌 구조다. 새로운 기술을 만날 때는 그것이 이 네 층 중 어디를 확장한 것인지부터 짚으면 전체 지도 위에서 위치를 잡을 수 있다
출처 – 인프런 강의 중 [모든 웹 개발자가 봐야 할 단 한 장의 지도]