Frontend 02
VS Code와 웹: 개발 환경 속 Client/Server의 숨겨진 이야기
VS Code에서 파일을 열고 브라우저에서 확인하는 일이 실제 웹의 Client/Server 구조와 어떻게 이어지는지 모르겠다.
근거 · 교안 p7-p10
1장: 브라우저가 건네는 첫마디: ‘요청’해요, ‘응답’받아요
1. 브라우저가 건네는 첫마디: ‘요청’해요, ‘응답’받아요
솔라의 손가락이 키보드 위에서 경쾌하게 움직였다. 화면 한쪽을 차지한 VS Code 편집기에서 <h1> 태그 안의 텍스트를 ‘나의 첫 페이지’에서 ‘나의 멋진 페이지!’로 바꾼 참이었다. Ctrl+S. 저장. 곧바로 화면 다른 한쪽의 웹 브라우저로 시선을 옮기고는 새로고침 아이콘을 눌렀다. 방금 수정한 문구가 즉시 화면에 나타났다.
“됐어!”
솔라는 만족스러운 미소를 지었다. 코드를 쓰고, 저장하고, 브라우저에서 확인하는 이 단순하고 빠른 흐름이 마음에 들었다. VS Code는 글을 쓰는 작업실, 브라우저는 완성된 글을 보여주는 액자. 솔라에게 이 둘의 관계는 명확해 보였다.
하지만 솔라의 미간에 작은 주름이 잡혔다. 얼마 전 강의에서 들었던 한 문장이 머릿속을 맴돌았다. ‘웹은 클라이언트가 서버에 무언가를 요청하고, 서버가 그에 응답하는 방식으로 작동합니다.’
클라이언트, 서버, 요청, 응답… 솔라는 자신의 모니터를 다시 훑어보았다. VS Code, 브라우저, 그리고 내 컴퓨터. 여기서 클라이언트는 누구고 서버는 누구일까? 브라우저가 클라이언트라고 했던 것 같은데, 그럼 내 컴퓨터에 있는 파일을 보여달라고 ‘요청’이라도 보낸다는 걸까? 누구에게? 내 컴퓨터가 서버인가? 스스로에게 요청을 보내고 스스로 응답하는 건가? 생각할수록 이상했다.
“그냥 내 컴퓨터에 있는 index.html 파일을 브라우저로 열어서 보는 거잖아. 워드 문서를 더블클릭해서 여는 거랑 크게 다를 게 없는 것 같은데…”
솔라가 혼잣말처럼 중얼거리는 소리를 들었는지, 옆에서 조용히 책을 읽던 루나가 고개를 들었다. 솔라의 시선이 VS Code 창과 브라우저 창 사이를 불안하게 오가는 것을 본 루나는 잠시 생각하더니 입을 열었다.
“솔라, 잠깐 다른 웹사이트 하나만 열어볼래? 아무거나, 자주 가는 포털 사이트 같은 거.”
“응? 갑자기?”
솔라는 고개를 갸웃했지만, 이내 새 탭을 열고 익숙한 포털 사이트 주소를 입력했다. 화려한 이미지와 뉴스 기사들이 화면을 가득 채웠다.
“그 상태에서 F12 키 한번 눌러봐.”
솔라가 F12 키를 누르자, 화면 한쪽에 복잡한 코드와 탭들로 가득한 ‘개발자 도구’ 창이 나타났다. 솔라는 몇 번 본 적은 있었지만, 볼 때마다 위압감이 드는 창이었다.
“거기서 ‘Network’라고 된 탭을 눌러줘.”
루나의 말에 따라 솔라는 ‘Network’ 탭을 클릭했다. 처음엔 텅 비어 있던 창에 ‘페이지를 새로고침하여 요청 기록 시작’이라는 안내 문구가 보였다.
“이제 그 상태로 페이지를 새로고침해 봐. 그리고 저 창에 뭐가 나타나는지 잘 봐.”
솔라는 반신반의하며 브라우저의 새로고침 버튼을 다시 눌렀다. 그 순간, 놀라운 일이 벌어졌다. 텅 비어 있던 네트워크 탭이 순식간에 수십 개의 알 수 없는 이름들로 채워지기 시작했다. 위에서부터 아래로 줄줄이 목록이 생겨나며, 각 항목 옆의 막대그래프가 분주하게 그려졌다.
“우와, 이게 다 뭐야? 그냥 새로고침 한번 했을 뿐인데…”
솔라의 눈이 동그래졌다. 루나는 당황하는 솔라의 옆으로 다가와 모니터를 가리켰다.
“맨 위에 있는 항목 한번 클릭해볼래? 주소창에 입력한 주소랑 이름이 제일 비슷한 걸로.”
솔라가 목록 맨 위에 있는 항목을 클릭하자, 오른쪽에 더 복잡한 정보창이 나타났다. ‘Headers’, ‘Payload’, ‘Preview’, ‘Response’ 같은 낯선 단어들이 보였다. 루나는 그중 ‘Headers’ 탭을 손가락으로 가리켰다.
“여기 봐봐. 스크롤 조금만 내려보면 ‘Request Headers’랑 ‘Response Headers’라는 두 구역이 보일 거야.”
솔라는 천천히 스크롤을 내렸다. 정말로 ‘요청 헤더’와 ‘응답 헤더’라는 제목 아래 알아보기 힘든 영어와 숫자들이 가득했다.
“네가 아까 주소창에 주소를 치고 엔터를 누른 순간, 네 브라우저가 저 포털 사이트의 컴퓨터(서버)에게 말을 건 거야. 그게 바로 이 ‘Request’ 부분이야. ‘이 페이지를 나에게 보여주세요’ 하고 편지를 보낸 거지.”
루나의 설명에 솔라는 ‘Request Headers’ 부분을 유심히 들여다보았다. GET / HTTP/2, Host: www.portal.com 같은 문구들이 눈에 들어왔다.
“그리고… 그 편지를 받은 서버가 ‘알겠어, 여기 네가 요청한 페이지야’ 하고 답장을 보내준 게 바로 아래 ‘Response’ 부분이고.”
루나의 손가락이 ‘Response Headers’로 옮겨갔다. HTTP/2 200 OK 라는 문구가 보였다.
솔라는 잠시 아무 말도 하지 못했다. 그저 화면 속 ‘Request’와 ‘Response’라는 두 단어와 그 아래의 빼곡한 내용들을 번갈아 쳐다볼 뿐이었다. 지금까지 브라우저가 웹사이트를 보여주는 것은 그저 마법 같은 일, 혹은 멀리 있는 파일을 가져와 ‘여는’ 행위라고 막연하게 생각했다. 하지만 지금 눈앞의 증거는 전혀 다른 이야기를 하고 있었다. 이건 일방적인 ‘열기’가 아니었다. 명백한 ‘대화’였다. 내 브라우저가 먼저 말을 걸고(요청), 저 멀리의 서버가 대답을 하는(응답) 쌍방향 통신.
“아… ‘요청’하고 ‘응답’한다는 게… 이런 거였구나. 진짜로 내 브라우저가 저쪽에 메시지를 보내고, 저쪽에서 답장을 보내주는 거였어.”
솔라는 방금 전까지 자신의 개발 환경을 보며 가졌던 의문을 떠올렸다. ‘클라이언트가 서버에 요청한다’는 추상적인 문장이 이제야 피부에 와 닿는 듯했다.
깨달음의 순간도 잠시, 솔라의 시선은 다시 자신의 로컬 프로젝트가 열려 있는 다른 브라우저 탭으로 향했다. 포털 사이트에서는 명백한 ‘요청-응답’의 흔적을 찾았다. 그렇다면 이제 진짜 질문이 남았다.
솔라는 자신의 index.html 파일이 열린 탭을 손가락으로 가리키며 입을 열었다.
“좋아, 포털 사이트는 알겠어. 내 브라우저가 클라이언트가 되어서 저쪽 서버에 요청을 보낸 거네. 그런데 이건? 내 컴퓨터에 있는 이 파일을 열었을 때도, 내 브라우저는 방금처럼 ‘요청’이라는 걸 했을까? 만약 했다면… 도대체 누구한테?”
2장: file:// vs. http://: 주소창의 작은 차이가 만드는 큰 세상
솔라의 질문이 공중에 맴돌았다. “만약 했다면… 도대체 누구한테?”
루나는 솔라의 질문에 바로 답하는 대신, 조용히 의자를 끌어 솔라의 옆에 앉았다. 그리고는 말없이 솔라의 모니터를 손가락으로 가리켰다. 하나는 방금 네트워크 로그를 확인했던 포털 사이트 탭, 다른 하나는 솔라의 ‘멋진 페이지’가 열려 있는 탭이었다. 루나의 손가락은 두 탭을 번갈아 가리키더니, 이내 각각의 브라우저 주소창 위에서 멈췄다.
솔라는 루나의 손가락 끝을 따라 시선을 옮겼다. 두 개의 주소. 너무나 익숙해서 평소에는 아무 생각 없이 지나쳤던 부분이었다. 하지만 지금, 두 개를 나란히 놓고 보니 미묘하지만 결정적인 차이가 눈에 들어왔다.
포털 사이트 탭:
https://www.portal-site.com
내 프로젝트 탭:
file:///C:/Users/솔라/projects/my-first-web/index.html
솔라는 자기도 모르게 입을 열었다.
“하나는… https로 시작하고, 다른 하나는… file로 시작하네.”
물론 그 뒤에 따라오는 주소의 형태도 완전히 달랐다. 하나는 익숙한 웹사이트 주소였고, 다른 하나는 솔라의 컴퓨터 폴더 경로 그대로였다. 지금까지는 그저 ‘웹사이트를 열었을 때’와 ‘내 파일을 열었을 때’의 자연스러운 차이라고만 생각했다.
“맞아. 바로 그 시작 부분이, 네 질문에 대한 거의 모든 답을 담고 있어.”
루나는 처음으로 입을 열었다.
“솔라, 아까 포털 사이트에서 ‘요청’과 ‘응답’이 일어나는 걸 봤지? 그 대화는 어떤 규칙, 즉 약속(Protocol)을 따라서 이뤄져. http나 https는 바로 그 약속의 이름이야. ‘HyperText Transfer Protocol’. 하이퍼텍스트를 전송하기 위한 약속이라는 뜻이지.”
루나는 ‘Transfer(전송)’라는 단어를 살짝 강조했다.
“전송…” 솔라는 단어를 되뇌었다. “내 컴퓨터에서 저쪽 서버 컴퓨터로 뭔가를 보내고, 저쪽에서 다시 나한테 보내주는 것… 그게 전송이니까. ‘요청-응답’이랑 딱 맞는 말이네.”
“정확해.” 루나가 고개를 끄덕였다. “브라우저는 주소창에 http나 https가 있는 걸 보면, ‘아, 지금부터 나는 클라이언트 역할을 해야겠구나. 네트워크 저편의 서버에게 말을 걸어서 페이지를 전송받아야지’ 하고 마음의 준비를 하는 거야.”
솔라는 다시 자신의 두 탭을 바라보았다. 클라이언트 모드로 작동 중인 포털 사이트 탭. 그리고…
“그럼… file은?”
솔라의 질문에 루나는 대답 대신 되물었다.
“글쎄. file은 무슨 뜻일까? ‘전송’처럼 거창한 의미가 있는 단어는 아니지?”
“파일… 그냥 파일이잖아.”
순간 솔라의 머릿속에서 뭔가 번쩍했다. 너무나 당연해서 오히려 생각지 못했던 사실이었다. http가 ‘전송’이라는 행위를 담은 약속이라면, file은 그저 ‘파일’이라는 대상을 가리키는 팻말에 불과했다.
“설마… file://은 ‘네트워크로 뭔가를 전송해서 받아와라’는 뜻이 아니라, 그냥… ‘네 컴퓨터에 있는 이 경로의 파일을 열어서 보여줘’라는 뜻이야?”
“바로 그거야. file://은 네트워크 통신을 위한 약속이 아니야. 브라우저에게 ‘너는 지금부터 웹 클라이언트가 아니라, 그냥 문서 뷰어(Document Viewer) 역할을 해. 네가 설치된 컴퓨터의 파일 시스템에 직접 접근해서 저 파일을 읽어 와’라고 지시하는 명령에 가까워.”
루나는 솔라의 index.html 탭을 가리켰다.
“그러니 네가 아까 던진 질문, ‘누구에게 요청을 보냈을까?’에 대한 답은…”
솔라는 허탈한 웃음과 함께 말을 이었다.
“아무에게도 보내지 않았다… 구나. 요청 자체가 없었어. 서버도 없었고, 클라이언트 역할도 없었던 거야. 그냥 내 브라우저가 워드 프로그램처럼 내 컴퓨터의 파일을 열어준 것뿐이네.”
솔라는 의자 깊숙이 등을 기댔다. 혼란스럽던 머릿속이 명쾌하게 정리되는 느낌이었다. 브라우저는 항상 ‘클라이언트’인 줄 알았다. 하지만 아니었다. 브라우저는 주소창에 주어진 약속(프로토콜)에 따라 역할을 바꾸는 카멜레온 같은 존재였다. http://를 만나면 통신을 준비하는 ‘웹 클라이언트’가 되고, file://을 만나면 얌전히 파일을 읽어오는 ‘문서 뷰어’가 되는 것이다.
자신의 개발 방식이 웹의 핵심인 ‘요청-응답’ 구조와는 전혀 상관없는 방식이었다는 사실에 조금 김이 빠지기도 했다.
“그럼 내가 VS Code에서 코드를 고치고 브라우저에서 새로고침해서 보는 건, 진짜 웹 개발이라기보다는 그냥 문서 확인에 가까운 거였네. 클라이언트-서버 구조를 전혀 흉내 내고 있지 않은 거잖아.”
솔라의 시선이 VS Code 창의 왼쪽 탐색기 패널로 향했다. 수많은 개발자들이 추천하고 설치했던 ‘Live Server’라는 확장 프로그램이 눈에 띄었다. 솔라는 그저 저장하면 자동으로 새로고침이 되는 편리한 기능이라고만 생각하고 있었다.
하지만 이제 새로운 질문이 떠올랐다.
“언니, 그런데 이상해. 내 컴퓨터의 파일을 브라우저가 그냥 ‘뷰어’처럼 열 수 있다면, 개발자들은 왜 굳이 ‘Live Server’ 같은 로컬 서버를 켜서 개발하라고 하는 거야? 그냥 파일 열면 되는데, 왜 귀찮게 내 컴퓨터에 서버를 만들어서까지 이걸 보는 거지?”
3장: Live Server의 마법: 내 컴퓨터 안의 작은 웹 서버
솔라의 질문은 스스로를 향한 것이기도 했다. ‘왜 귀찮게 내 컴퓨터에 서버를 만들어서까지 이걸 보는 거지?’
그녀는 자신의 VS Code 편집기 화면을 내려다보았다. 창의 오른쪽 하단, 상태 표시줄에 자리 잡은 ‘Go Live’ 버튼이 눈에 들어왔다. ‘Live Server’ 확장 프로그램을 설치한 뒤로 수없이 눌렀지만, 그저 ‘자동 새로고침 시작’ 정도로만 여겼던 버튼이었다. 솔라는 마우스 커서를 그 위로 가져가 잠시 망설였다. 그리고 결심한 듯 버튼을 클릭했다.
익숙한 효과음과 함께, 곧바로 새 브라우저 탭이 번쩍 열리며 솔라의 ‘나의 멋진 페이지!’가 나타났다. 모든 것이 file:// 경로로 파일을 열었을 때와 똑같아 보였다. 하지만 단 하나, 결정적인 부분이 달랐다. 솔라의 시선은 곧장 주소창으로 향했다.
http://127.0.0.1:5500/index.html
“주소가… file://이 아니네.”
솔라는 http라는 네 글자를 뚫어져라 쳐다봤다. 이전 장에서 루나와 함께 확인했던 바로 그 약속의 이름이었다. 브라우저를 ‘문서 뷰어’가 아닌 ‘웹 클라이언트’로 행동하게 만드는 신호탄.
“흐음.” 솔라는 시험 삼아 VS Code로 돌아가 <h1> 태그의 내용을 다시 ‘나의 첫 페이지’로 바꾸고 저장했다. 그러자 마법처럼 브라우저 탭이 스스로 새로고침되며 내용이 바뀌었다. “역시 그냥 새로고침 편하게 해주는 거였어. http로 시작하긴 하지만, 결국 내 컴퓨터에서 돌아가는 건 똑같잖아.”
솔라가 혼잣말로 중얼거렸다. 그녀의 결론은 간단했다. Live Server는 개발의 편의를 위해 file:// 경로를 http://처럼 보이게 포장하고, 자동 새로고침 기능을 덤으로 얹어주는 도구일 뿐이라고.
옆에서 지켜보던 루나가 조용히 입을 열었다.
“솔라, 아까 포털 사이트에서 했던 거, 여기서도 한번 해볼래?”
“포털 사이트에서 했던 거라면… 아, 개발자 도구?”
솔라는 고개를 끄덕이며 F12 키를 눌러 개발자 도구를 열고, 망설임 없이 ‘Network’ 탭으로 이동했다. 이전과 달리 이제 그 창이 낯설지 않았다. 솔라는 새로고침 버튼을 눌렀다.
순간, 텅 비어 있던 네트워크 탭에 몇 줄의 목록이 나타났다.
솔라의 입이 살짝 벌어졌다.
“요청이… 있어! file:// 때랑은 달라!”
목록의 맨 위에는 index.html이라는 이름이 선명했고, 그 상태(Status)는 200 OK였다. 포털 사이트에서 봤던 것과 똑같은 성공의 신호였다. 솔라는 해당 항목을 클릭하고 ‘Headers’ 탭을 확인했다. 그곳에는 분명히 ‘Request Headers’와 ‘Response Headers’가 존재했다. 브라우저가 클라이언트로서 누군가에게 ‘요청’을 보냈고, 그 누군가가 ‘응답’을 해준 명백한 증거였다.
“잠깐만… 이상하잖아.” 솔라의 미간이 다시 좁혀졌다. “분명히 내 브라우저가 http 요청을 보냈어. 클라이언트 역할을 한 거지. 그런데… 누구한테? 포털 사이트처럼 저 멀리 있는 서버 컴퓨터는 아닐 거 아냐. 이 파일은 내 컴퓨터에 있으니까.”
솔라는 ‘Request URL’ 항목을 유심히 들여다봤다. http://127.0.0.1:5500/index.html.
“127.0.0.1… 이게 도대체 누구야?”
“그게 바로 ‘나 자신’을 가리키는 특별한 주소야.”
루나가 설명했다. “네트워크 세상에서 모든 컴퓨터가 쓸 수 있는 약속된 번호지. ‘localhost’라고 부르기도 하고. 네 브라우저는 지금 저 멀리 어딘가로 편지를 보낸 게 아니라, 바로 자기 자신이 실행되고 있는 컴퓨터, 즉 네 컴퓨터에게 편지를 보낸 거야.”
솔라는 잠시 생각에 잠겼다. 내 브라우저가 내 컴퓨터에게 요청을 보냈다. 그럼 그 요청을 받고 응답을 해준 건 누구일까? 컴퓨터 스스로?
그때, 솔라의 머릿속에서 모든 조각이 맞춰졌다. ‘Go Live’ 버튼, http:// 주소, 그리고 네트워크 탭의 요청 기록.
“아! 그럼 Live Server라는 게… 그냥 새로고침 도구가 아니라, 내 컴퓨터 안에서 잠깐 돌아가는 꼬마 서버 같은 거구나!”
솔라의 목소리에 흥분이 실렸다.
“Go Live를 누르는 순간, VS Code가 내 컴퓨터에 작은 웹 서버 프로그램을 실행시키는 거야. 그리고 그 서버는 127.0.0.1:5500이라는 주소에서 귀를 기울이고 있다가… 내 브라우저가 그 주소로 ‘index.html 파일 주세요’ 하고 http 요청을 보내면, ‘네, 여기 있습니다’ 하고 파일을 찾아서 http 응답으로 보내주는 거지!”
솔라는 이제야 모든 것을 이해했다. file://로 파일을 여는 것은 단순한 ‘파일 열기’였다. 하지만 Live Server를 통하는 순간, 개발 환경은 진짜 웹의 동작 방식인 ‘클라이언트-서버’ 구조를 완벽하게 흉내 내기 시작하는 것이었다. 자동 새로고침은 그 과정에서 제공되는 편리한 부가 기능일 뿐, 핵심은 내 컴퓨터 안에 ‘서버’의 역할을 해주는 존재를 만드는 것이었다.
솔라는 다시 한번 자신의 개발 환경을 천천히 둘러보았다. 이제는 각 요소의 역할이 명확하게 보였다.
그녀는 VS Code 창을 손가락으로 가리켰다. “이건 개발자인 내가 코드를 만드는 작업실.”
그리고 Live Server로 열린 브라우저 탭을 가리켰다.
“이건 진짜 웹처럼 http로 요청을 보내는 클라이언트.”
마지막으로 그녀는 자신의 컴퓨터 본체를 툭툭 치며 미소 지었다. “그리고 이 안 어딘가에서, Live Server라는 임시 서버가 내 클라이언트의 요청을 성실하게 받아주고 있는 거고.”
더 이상 VS Code와 브라우저는 그저 분리된 편집기와 뷰어가 아니었다. 솔라의 책상 위에는 이제 코드 작업실(VS Code)과, 그 작업실의 결과물을 요청하고 전시하는 클라이언트(브라우저), 그리고 그 둘을 이어주는 작은 서버(Live Server)로 이루어진 완벽한 소우주가 펼쳐져 있었다.
“내 책상 위에 작은 인터넷을 통째로 만들어두고 있었던 거네.” 솔라가 나지막이 중얼거렸다. 그 말에는 자신의 개발 환경을 완전히 새로운 눈으로 보게 된 경이로움이 담겨 있었다.