웹은 어떻게 진화했을까?
유행의 교체가 아닌, 시대의 한계를 돌파해 온 웹의 생존기.

웹의 역사를 이야기할 때 우리는 종종 과거를 너무 쉽게 "레거시"라고 부릅니다. 하지만 PHP, JSP, ASP 같은 기술은 단순히 낡은 기술이 아니라, 그 시대의 현실적인 문제를 풀기 위해 등장했습니다.
결국 중요한 질문은 이겁니다. "무엇이 최신인가?"가 아니라 "왜 지금 이 선택이 타당한가?"
웹 기술의 진화는 직선이 아닙니다. 오히려 한 방향으로 달렸다가, 다음 병목을 만나면 다른 방향으로 다시 돌아오는 왕복 운동에 가깝습니다. 서버에서 시작해 클라이언트로 갔다가, 다시 서버로 돌아오는 흐름이 바로 그 증거입니다.
이 글은 그 흐름을 따라가며, 각 시대가 웹을 통해 어떤 문제를 해결하고자 했고, 무엇이 웹의 진화를 이끌었는지 살펴봅니다.
1. 고전 웹 (1990s ~ 2000s 초반)
먼저, 웹이 등장하던 시기의 목적은 지금과 많이 달랐습니다. World Wide Web은 본래 연구자들이 문서를 공유하기 위한 시스템이었고, 핵심은 “정보를 연결하고 읽는다”는 데 있었습니다. 이 구조를 정의한 HTML 역시 문서를 표현하는 데 초점이 맞춰져 있었죠. 애초에 “상호작용”보다는 “열람”이 중심이었습니다.
이러한 목적은 당시의 기술적 환경과도 맞물려 있었습니다. 초기 웹에서 사용되던 HTTP는 매우 단순한 요청–응답 구조를 가지고 있었고, 한 번의 연결이 끝나면 상태를 유지하지 않는 특징을 갖고 있었습니다.
즉, 서버는 사용자가 누구인지, 이전에 어떤 행동을 했는지 기억할 수 없었고, 그 결과 사용자마다 다른 화면을 보여주거나 복잡한 상호작용을 처리하는 일은 구조적으로 어려웠습니다.
여기에 제한적인 서버 성능과 느린 네트워크 환경까지 더해지면서, 요청이 들어올 때마다 내용을 새로 만들어내기보다는 이미 준비된 문서를 그대로 전달하는 방식이 훨씬 효율적인 선택이었습니다. 결국 초기 웹은 ‘정적일 수밖에 없는 조건’ 위에서 자연스럽게 형성된 구조였다고 볼 수 있습니다.

이 단순한 구조는 생각보다 오랫동안 웹의 표준으로 유지되었습니다. 하지만 웹이 점점 “문서”를 넘어 “서비스”가 되면서 상황이 달라지기 시작했습니다.
쇼핑, 커뮤니티, 포털, 이메일 서비스처럼 사용자마다 다른 데이터를 보여줘야 하는 웹이 등장하면서, 단순한 정적 HTML만으로는 요구를 충족할 수 없게 된 것입니다.
2. 초기 SSR (1990s 후반 ~ 2000s 중반)
웹이 단순한 문서 전달을 넘어 “서비스”로 확장되기 시작하면서, 정적인 HTML만으로는 한계를 드러내기 시작했습니다. 사용자마다 다른 데이터를 보여주고, 로그인 상태를 유지하며, 게시판처럼 지속적으로 변하는 정보를 다루기 위해서는 새로운 방식이 필요했습니다.
이 요구를 해결한 것이 초기 서버 사이드 렌더링(SSR) 모델입니다.
대표적인 기술이 바로 PHP, JSP, ASP였습니다. 그중에서도 PHP는 가장 널리 사용되며 SSR 패러다임을 대중화한 기술 중 하나입니다.
PHP는 어떻게 문제를 해결했을까?
PHP의 핵심 아이디어는 단순합니다.
“HTML 안에 서버 로직을 섞어버리자”
예를 들어 다음과 같은 코드가 가능합니다:
<h1>안녕하세요</h1>
<p>
오늘 시간: <?php echo date("Y-m-d H:i:s"); ?>
</p>이 코드는 서버에서 실행된 뒤, 최종적으로 이런 HTML로 변환됩니다:
<h1>안녕하세요</h1>
<p>
오늘 시간: 2026-04-29 12:34:56
</p>즉, 브라우저는 PHP 코드를 전혀 보지 못하고, 항상 “완성된 HTML”만 받습니다.
이 모델의 장점은 분명했습니다.
- 브라우저가 아무리 단순해도 화면을 보여줄 수 있었다.
- 검색 엔진이 이해할 수 있는 형태의 HTML을 바로 받을 수 있었다.
- 서버는 데이터와 템플릿을 한곳에서 제어할 수 있었다.
하지만 비용도 분명했습니다.
- 링크를 누를 때마다 전체 페이지가 다시 렌더링되어 깜빡임이 발생.
- 페이지 전환마다 서버가 템플릿을 다시 조립해야 해서 부하가 증가.
- 브라우저는 매 요청마다 HTML 파싱, 스타일 계산, 레이아웃, 페인트를 다시 수행.
당시에는 이 비용이 꽤 자연스러운 대가였습니다. 웹 자체가 아직 "앱"이 아니었기 때문입니다.
3. jQuery의 등장 (2006 ~ 2013)
AJAX는 기술적으로 더 이전에도 존재했지만, 대중화에는 시간이 필요했습니다.
왜냐하면 당시 웹의 기본 UX 자체가 "클릭하면 전체 새로고침"이었고, 개발자와 사용자 모두 그 한계를 어느 정도 당연하게 받아들이고 있었기 때문입니다.
여기에 브라우저 파편화가 겹쳤습니다.
- IE6/7/8, Firefox, Chrome의 DOM API 차이
- 이벤트 처리 방식 불일치
- 같은 기능을 브라우저별 분기문으로 중복 구현
jQuery는 이 혼란을 $ 하나로 추상화했습니다.
- 선택:
$(selector) - 이벤트:
.on() - AJAX:
$.ajax() - 애니메이션:
.fadeIn(),.slideUp()
즉, jQuery는 단지 문법을 쉽게 만든 도구가 아니었습니다. 브라우저라는 조각난 환경에서 부분 업데이트를 팀 단위 생산성으로 끌어올린 추상화 계층이었습니다.
하지만 여기서 또 다른 문제가 드러납니다. DOM을 직접 건드리는 코드가 많아질수록, 화면 상태와 코드 상태를 사람이 계속 맞춰야 한다는 점입니다.
특히 브라우저 렌더링 엔진은 생각보다 민감합니다. DOM을 읽고 쓰는 순서가 섞이면 Layout(Reflow)와 Paint(Repaint)가 연쇄적으로 발생할 수 있습니다. 한 번의 클릭이 아니라, 하나의 프레임 안에서 여러 번의 재계산을 유발하는 순간 성능은 급격히 흔들립니다.
이 시점부터 중요한 화두는 "어떻게 더 많이 조작할까"가 아니라, 어떻게 덜 깨뜨릴까로 바뀝니다.
브라우저별 분기 코드가 jQuery 추상화로 얼마나 줄어드는지, 그리고 왜 명령형 제어가 커질수록 유지보수 비용이 올라가는지 직접 확인해보세요.
Browser Fragmentation Lab
if (window.attachEvent) { el.attachEvent('onclick', handler) }\n// browser-specific branch continues...브라우저별 원시 코드
42 lines
jQuery 적용 코드
12 lines
중복 제거율
71%
4. SPA의 시대: 웹이 애플리케이션이 되다 (2013 ~ 현재)
AJAX는 축복이었지만, 규모가 커질수록 다른 문제가 터졌습니다.
"데이터가 바뀔 때마다 어떤 DOM을 어떻게 바꿀지"를 개발자가 직접 명령하는 방식은 작은 기능에는 빠르지만, 대규모 화면에서는 곧 스파게티 코드가 됩니다.
여기서 SPA 프레임워크의 핵심 철학이 등장합니다.
명령하지 말고 선언하라. State가 바뀌면 UI는 다시 계산된다.
Angular, React, Vue가 각자 푼 문제
- Angular
- 엔터프라이즈 친화적 올인원 프레임워크
- 양방향 바인딩, DI, 라우팅 등 체계적 제공
- React
- 컴포넌트 단위 사고를 대중화
- Virtual DOM 기반의 효율적 갱신
- Vue
- 낮은 진입장벽과 높은 생산성의 균형
- 템플릿 친화성과 반응성의 절충
SPA의 성과는 분명했습니다.
- 전환이 부드럽고 앱 같은 UX
- 상태 관리와 UI 표현의 관심사가 분리됨
- 서버는 JSON 중심으로 가벼워짐
하지만 비용도 생겼습니다.
- 큰 JS 번들로 인한 초기 로딩 지연
- 빈 HTML에서 시작하는 렌더 경로로 SEO 불리
- 하이드레이션이 끝나기 전까지는 버튼이 보이지만 앱은 아직 "깨어나지" 않은 상태가 될 수 있음
사용자가 느끼는 체감 성능은 단순히 JS 파일 크기만으로 결정되지 않습니다. 네트워크 전송 시간, 브라우저의 HTML 파싱, JS 실행, 메인 스레드 점유, 하이드레이션 완료 시점이 합쳐져서 "느리다"는 인상을 만듭니다.
같은 액션을 해도 사고방식이 어떻게 달라지는지, 명령형과 선언형을 나란히 비교해봅시다.
Imperative vs Declarative
총 2개 중 완료 0개
jQuery 스타일
DOM 직접 조작 횟수: 0
- 액션을 실행하면 로그가 쌓입니다.
React 스타일
State 기반 갱신 횟수: 0
- 액션을 실행하면 로그가 쌓입니다.
그리고 실제 갱신 범위가 어떻게 달라지는지 Virtual DOM diff 관점에서 확인할 수 있습니다.
Virtual DOM Diff Visualizer
이번 업데이트에서 실제 반영된 노드: 1 / 5
Fiber: 렌더링을 작업 스케줄링으로 바꾸다
Virtual DOM이 렌더 결과를 계산하는 방식이라면, Fiber는 그 계산을 언제, 어떻게, 얼마만큼 쪼개서 실행할지를 다루는 아키텍처입니다.
이 차이가 중요한 이유는 브라우저 메인 스레드 때문입니다.
- 입력 처리
- 이벤트 루프
- 스타일 계산
- 레이아웃
- 페인트
- JavaScript 실행
이 모든 작업이 같은 메인 스레드 위에서 경쟁합니다. React가 한 번에 너무 많은 작업을 실행하면 화면은 멈추고 입력은 밀립니다.
Fiber는 렌더 작업을 작은 단위로 나눕니다.
- 우선순위를 판단하고
- 중간에 멈출 수 있고
- 다시 이어서 처리할 수 있게 하고
- 사용자 입력을 끼워 넣을 틈을 남깁니다
이건 단순 최적화가 아니라, 렌더링을 선점 가능한 스케줄링 문제로 바꾸는 일입니다.
청크 단위로 작업이 쪼개질 때와 한 번에 몰아칠 때의 차이를 직접 보세요.
Fiber Time Slicer
프레임 1
현재 작업: 가상 DOM 계산
예산: 16.7ms
누적 처리: 0ms
남은 작업
6개
완료 비율
0%
현재 모드
분할
예산 초과
NO
렌더 청크
스케줄 로그
- 작업을 시작하면 렌더 청크 분할이 보입니다.
브라우저는 무엇부터 그리는가
웹 성능을 이야기할 때 많은 사람들이 JS 번들 크기만 봅니다. 하지만 실제 체감은 더 복합적입니다.
브라우저는 대략 다음 경로를 따라 화면을 만듭니다.
- HTML 파싱
- CSS 파싱
- DOM과 CSSOM 생성
- Render Tree 구성
- Layout 계산
- Paint
- Composite
이 흐름은 단순한 순서가 아니라, 서로를 강하게 의존하는 파이프라인입니다. 특히 DOM 읽기와 쓰기가 섞이면 Layout이 반복적으로 강제될 수 있고, 이는 곧 Reflow 폭탄으로 이어집니다.
이때 중요한 것은 "한 번에 많은 일을 하는 것"이 아니라, 브라우저가 싫어하는 읽기/쓰기 패턴을 만들지 않는 것입니다. 그래서 batching과 스케줄링이 중요해집니다.
이 차이는 jQuery 시절의 직접 조작 코드가 왜 커질수록 유지보수와 성능 둘 다 어려워지는지 설명해 줍니다.
5. 다시 SSR로의 회귀: Isomorphic/Hybrid의 시대 (현재)
SPA가 승리한 것처럼 보이던 시점에도, 제품 관점에선 숙제가 남아 있었습니다.
- 첫 진입 시 빈 화면 대기
- SEO와 소셜 미리보기(OG) 품질 문제
- 저사양/저속 네트워크 환경에서 체감 성능 저하
Next.js 같은 현대 프레임워크는 여기서 절충이 아니라 재해석을 택했습니다.
- 서버에서 먼저 의미 있는 HTML을 제공(SSR/SSG)
- 클라이언트에서 하이드레이션 후 SPA처럼 상호작용
- 페이지/요청 특성에 따라 렌더 전략을 혼합
과거 SSR의 "전체 새로고침" 단점은 버리고, SSR의 "빠른 첫 화면/검색 친화성" 장점을 현대적으로 복구한 셈입니다.
하지만 현대 SSR에서 진짜 어려운 지점은 따로 있습니다. 화면은 서버가 먼저 그렸는데, 사용자는 그 화면이 언제 진짜 살아나는지를 체감하기 때문입니다.
이 간극이 바로 Hydration의 문제입니다. 서버가 미리 만든 DOM에 JavaScript가 붙기 전까지는, 사용자는 버튼이 보이는데 눌리지 않는 순간을 경험할 수 있습니다. 화면은 존재하지만 상호작용은 아직 오지 않은 상태, 이것이 Hydration의 uncanny valley입니다.
아래 보드에서 네트워크 환경별로 SPA와 Modern SSR의 초기 경험 차이를 비교해볼 수 있습니다.
Rendering Race Board
SPA
TTFB
180ms
FCP
1200ms
TTI
2100ms
Modern SSR
TTFB
220ms
FCP
680ms
TTI
1300ms
서버가 맡을 수 있는 일을 서버로 옮기면, 클라이언트 번들은 더 얇아질 수 있습니다. 그리고 이 경계 조정은 단순한 기술 취향이 아니라 런타임 비용의 구조를 바꾸는 일입니다.
아래 저울은 같은 화면이라도 서버와 클라이언트가 맡는 책임을 어디에 두느냐에 따라 초기 페이로드와 하이드레이션 부담이 어떻게 달라지는지를 보여줍니다.
RSC Bundle Balance
Server Payload
93 KB
Client Bundle
111 KB
Hydration
91 ms
FMP
716 ms
책임 분배
Server
55%
Client
45%
Hydration pressure
낮음
해석 포인트
서버 책임이 올라갈수록 초기 HTML은 더 의미 있게 채워지고, 클라이언트 번들과 하이드레이션 부담은 줄어듭니다. 다만 모든 것을 서버로 보내는 것이 답은 아닙니다. 상호작용이 많은 구간은 여전히 클라이언트가 더 자연스럽습니다.
6. 결론: 기술 스택은 취향이 아니라 문맥이다
유행은 빠르게 바뀌지만, 문제의 본질은 반복됩니다.
- SSR은 "개인화된 콘텐츠"라는 문제를 해결했다.
- jQuery는 "브라우저 혼돈과 생산성" 문제를 해결했다.
- SPA는 "복잡한 인터랙션과 상태 관리" 문제를 해결했다.
- Fiber는 "메인 스레드 차단" 문제를 더 잘 다루게 했다.
- 현대 SSR과 RSC는 "성능과 SEO, 그리고 초기 상호작용"의 균형을 다시 맞췄다.
그래서 기술 선택의 질문도 바뀌어야 합니다.
"요즘 뭐 써요?"보다 먼저, "우리 서비스의 병목은 어디에 있고, 어떤 렌더링 전략이 그 병목을 줄이는가?"
정체성 있는 스택 선택은 거창한 철학이 아니라, 이 질문을 팀이 꾸준히 던지는 습관에서 시작됩니다.
덧붙임: 실무에서 바로 쓰는 선택 전략
새 프로젝트를 시작할 때 아래 4가지만 먼저 체크해도 의사결정 품질이 크게 올라갑니다.
- 유입 구조: 검색 유입 중심인가, 로그인 후 반복 사용 중심인가?
- 첫 화면 목표: 빠른 콘텐츠 노출이 중요한가, 풍부한 상호작용이 중요한가?
- 팀 역량: 프레임워크 학습 비용과 운영 복잡도를 감당 가능한가?
- 운영 환경: 저사양 기기나 불안정 네트워크 사용자 비중이 높은가?