Intro
지난 게시글에서 클라이언트의 요청 진행 과정과 서버의 응답이 네트워크를 통해 어떻게 클라이언트로 도달하는지에 대해 알아봤다.
그러면 다음 주제는 자연스럽게 서버에서 전달 받은 HTML 문서
를 브라우저가 어떻게 그려내는지(rendering
)일 것이다.
하지만 그전에, 클라이언트에게 전달되는 콘텐츠(HTML
, CSS
, JS
)가 어떤 과정을 거쳐 현재의 모습으로 발전해 왔는지 그 역사를 먼저 알아보자.
인터넷의 등장과 웹 브라우저의 출현
웹과 인터넷을 구분하고 해당 주제를 시작하자.
인터넷: 전 세계 컴퓨터들을 연결하는 물리적인 네트워크 인프라(통신 프로토콜(TCP/IP)을 사용하여 서로 정보를 주고받을 수 있게 연결)
웹: 인터넷을 기반으로 HTML, HTTP, URL 등의 기술을 사용하여 정보를 공유하는 시스템(인터넷 위에서 동작하는 시스템)
팀 버너스 리는 1990년 최초의 웹 서버
인 CERN httpd
, 웹 브라우저
인 WorldWideWeb
, HTML 1.0
을 개발, 1991년 8월 6일, 세계 최초의 웹사이트인 http://info.cern.ch 공개한다.
그 후, 1993년에 웹의 소스 코드를 공개하고, 1994년에는 W3C(World Wide Consortium - HTML, CSS, XML 등 웹 기술의 표준을 개발하고 관리)재단을 창설한다.
이렇게 웹 서 비스의 시대가 개막 되었다!
하지만 이 시점에서의 컨텐츠들은 정적 웹페이지(static)
로 단순한 정보제공에 가까웠다.
정적 웹페이지
는 서버에 미리 저장된 HTML, CSS, JavaScript 등의 파일로 구성(이 시점에서는 JS 없었음)
모든 사용자에게 동일한 콘텐츠 제공(사용자의 요청이나 상태에 따라 콘텐츠가 달라지지 않음)
동적 웹페이지와 서버 사이드 렌더링의 등장
사실 위의 스태틱 페이지들은 굉장히 한정적인 목적에서만 적합하다.(변할 일이 없는 정보성 페이지...)
사용자의 요청에 따라 다른 내용을 보여주려면 사전에 해당하는 케이스들의 페이지를 전부 만들어야 하며 사용자가 입력한 값들은 반영하기 어려울 것이다.
이러한 상황에서 등장한게 CGI(Common Gateway Interface)
이다.
- 웹 서버는 요청 URL을 분석하여 해당 요청이 CGI 프로그램에 대한 것인지 확인
- 맞다면 프로세스를 생성 후, HTTP 요청의 정보를 환경 변수에 세팅 및 POST 요청의 경우, body CGI 프로그램의 표준 입력으로 전달
- CGI 프로그램은 처리 결과를
HTTP response
로 출력 - 그 결과를 사용자에게 전달하는 방식으로 동작
이 방식으로 최초의 SSR(server side rendering)
이 탄생한다.
여기서 SSR
과 CSR
의 차이점을 짚자면 포인트는 HTML이 어디서 완성되냐이다.
사용자의 요청이 있을 때마다 서버에서 HTML을 동적으로 생성하여 완성된 상태로 사용자에게 전달하는게 SSR이다.
반면에 CSR은 빈 혹은 최소한의 내용의 HTML만 전달받은 후, 주로 JavaScript를 사용하여 클라이언트 측에서 HTML 요소들을 동적으로 생성해 완성한다.
다만 React
는 전체 페이지를 React로 구성하는 것이 아닌 페이지의 일부분에서 React를 적용하는게 가능하고, 그렇기에 사용자 인터페이스를 만들기 위한 JavaScript 라이브러리라고 공식홈페이지에 적혀있다.
이는 SSR
과 CSR
은 상호 배타적인 개념이 아니며, 하나의 웹 애플리케이션 내에서 혼합하여 사용될 수 있다는 것이다.
다시 돌아와서 최초의 동적 페이지생성을 위한 Interface가 생겼지만, 매 요청마다 프로세스를 생성하고 실행 해야 하는 성능 상의 문제가 있었고, 이를 개선한 서버 사이드 스크립팅 언어들이 등장했다.
대표적인 서버 사이드 스크립팅 언어로는 PHP
, ASP
, JSP
등이 있다.
이들은 웹 서버와 긴밀하게 통합되어 동작하며, CGI
와 달리 매 요청마다 새로운 프로세스를 생성하지 않고 웹 서버 프로세스 내에서 스크립트를 실행한다.
-
PHP(PHP: Hypertext Preprocessor)
- 1995년 출시
- 오픈 소스로 시작
- 웹 서버 모듈로 동작하여 빠른 응답 속도 제공
- 다양한 운영 체제와 웹 서버에서 동작
- WordPress, Drupal 등 많은 CMS에서 사용
-
ASP(Active Server Pages)
- 1996년 출시
- Microsoft에서 개발
- Windows 서버와 IIS 웹 서버에서 동작
- VBScript나 JScript를 사용하여 동적 웹 페이지 생성
- 후에 ASP.NET으로 발전
-
JSP (JavaServer Pages)
- 1999년 출시
- Java 기반
- Java의 강력한 기능과 개발 도구를 웹 개발에 활용
- 서블릿 컨테이너에서 동작
- 높은 성능과 확장성 제공
이러한 서버 사이드 스크립팅 언어들은 데이터베이스와의 연동, 세션 관리, 사용자 인증 등 다양한 기능을 내장하여 웹 개발을 더욱 쉽고 효율적으로 만들었으며, 동적이고 데이터 중심적인 웹 애플리케이션 개발을 가능하게 했다.
MVC(Model-View-Controller) 아키텍처의 도입
1990년대 후반부터 웹 개발 분야에서는 복잡성이 증가하고 대규모 프로젝트가 많아짐에 따라, 코드의 유지보수성과 확장성이 중요한 이슈로 부각되었고, 이에 MVC 패턴이 웹 개발에 도입된다.
MVC
는 사용자 인터페이스, 데이터 및 논리 제어를 구현하는데 널리 사용되는 소프트웨어 디자인 패턴으로, 소프트웨어의 비즈니스 로직과 화면을 구분하는데 중점을 둔다. 관심을 분리하여 결합도를 낮추는 것이다.
어플리케이션을 세 가지 주요 컴포넌트(Model
, Controller
, View
)로 분리하는데, 웹 개발에서는 서버 사이드(Model과 Controller)와 클라이언트 사이드(View)로 분리된다.
-
Model(모델)
- 애플리케이션의 데이터와 비즈니스 로직을 담당
- 데이터베 이스와 상호작용하고, 데이터 유효성 검사, 데이터 처리 등을 수행
- 사용자 인터페이스(View)나 컨트롤러에 독립적이어야 함
-
Controller(컨트롤러)
- 사용자의 요청을 받아 처리하는 부분
- 모델과 뷰 사이의 상호작용을 관리
- 사용자의 입력을 받아 모델을 업데이트하고, 업데이트된 모델을 뷰에 전달
- 애플리케이션의 flow를 제어
-
View(뷰)
- 사용자 인터페이스를 담당
- 모델로부터 데이터를 받아와 사용자에게 표시
- 사용자의 입력을 받아 컨트롤러에 전달
- HTML, CSS, JavaScript 등을 사용하여 구현
오늘날에도 MVC는 웹 애플리케이션 개발에서 널리 사용되는 아키텍처 패턴으로, 다양한 웹 프레임워크들이 MVC 패턴을 기반으로 설계되었고, MVP(Model-View-Presenter), MVVM(Model-View-ViewModel)같은 여러 변형 패턴들이 생길 만큼 표준적인 아키텍처 패턴으로 자리 잡았다.
CSS & Javascript 등장!
기왕 웹 페이지를 제공한다면, 멋진 페이지를 제공하고 싶을 것이다. 하지만 쌩 HTML로는 한계가 명확한데 심지어 초기에는 Tag와 태그 속성만으로 스타일링을 했다고 한다.
참고로 HTML에서 style 속성을 사용한 inline 스타일링과 style 태그를 사용한 내부 스타일시트가 지원된건 HTML4부터 라고 한다. 어우...
1996년 12월에 발표된 CSS(Cascading Style Sheets)은 문서와 스타일을 분리하는 것을 목표로 하였고
CSS를 사용하면 HTML 문서의 구조와 콘텐츠는 그대로 유지하면서, 별도의 스타일 시트에서 스타일을 선언하고, 태그를 선택해 적용할 수 있었다.
주요 기능은 selector 문법, box model이다.
CSS가 디자인을 위해서 나왔다면 javascript는 페이지내에 동적인 요소를 넣기 위해 개발 되었다.
당시 넷스케이프는 웹 브라우저 시장을 주도하고 있었고, 1995년 12월에 출시한 웹 브라우저 넷스케이프 네비게이터2.0에 자바스크립트를 최초로 탑재했다.
자바스크립트를 사용하여 간단한 폼 검증, 이미지 롤오버 효과등을 구현할 수 있었고 그 시기에 굉장히 성공했다 한다.
그러자 Microsoft에서는 자바스크립트와 호환되는 자체 구현체인 JScript를 개발하여 1996년 8월에 출시된 Internet Explorer3.0에 탑재하였다.
모든 브라우저가 JS로 통일 되었고, SPEC도 잘 지원하는 지금도 개발하다보면 크로스 브라우징 이슈를 만날 때가 있는데, 브라우저마다 언어부터 다르다면 생각만 해도 끔찍하다.
1996년 11월, 넷스케이프는 ECMA 인터내셔널에 자바스크립트의 표준화를 제안했고, 이를 통해 1997년 6월에 첫 번째 ECMA Script 사양인 ECMAScript 1이 출시되었다.
그 후, Firefox, Opera, Safari 같은 다양한 브라우저가 생겨나는데, IE같은 경우 점유율이 높다는 이유로 ECMA Script를 따라가지 않았고, 그외 브라우저들도 스펙을 지원하는게 천차만별이었다.
이 시기에 자바스크립트의 표준화는 요원한 것 처럼 보였다. 크롬
과 ES5
전 까지의 JS의 표준은 1999년 12월에 발표된 ES3
이다.
JQuery의 등장
이후 파이어폭스, 오페라, 사파리 등 다양한 브라우저가 등장한다.
하지만 당시 높은 점유율을 차지하던 인터넷 익스플로러는 ECMAScript 표준을 완전히 준수하지 않았고, 자체적인 비표준 기능들을 구현하기도 했다.
다른 브라우저들 역시 ECMAScript 스펙 지원 수준이 제각각이었다. 이로 인해 웹 개발자들은 브라우저별로 다른 코드를 작성해야 했고, 크로스 브라우징 이슈를 해결하느라 고통 받았다(받았을 것이다)...
이는 한 시대를 풍미한 JQuery
의 등장 배경이 된다.
2006년에 John Resig이 발표한 jQuery는 간단한 문법(dom 조작, ajax)으로도 각광 받았지만,
브라우저 간 호환성 문제 해결 - 동일한 동작 보장이라는 부분이 그 시대 웹 개발자들이 사랑할 수 밖에 없는 이유 였을 것이다.
현 시점에서는 javascript의 발전, 브라우저의 표준화, SPA 라이브러리, 프레임워크(react, vue)의 등장으로 레거시로 전락했다(그럼에도 2023년 9월 기준으로 전체 웹 사이트의 77.6%는 jQuery를 사용한다 함).
jquery와 react를 비교하자면 jquery는 dom을 직접적(명령형)으로 UI의 값을 변경 해야하고, React는 virtual dom을 사용하고 선언적으로 컴포넌트 패턴기반의 UI의 상태를 업데이트한다.
바닐라 자바스크립트의 중요성을 강조한 하는 주범이기도 하다.
JQuery의 쉬운 문법으로 JavaScript에 대한 이해 없이도 웹 개발을 할 수 있었다(그만큼 쉽고 직관적이었다는걸 의미). 그리고 브라우저 간 호환성때문에 js보다 jquery를 선호 할 수 밖에 없었다.
본인은 바닐라 자바스크립트가 중요하냐고 물은 질문에, 현재 웹 개발 실무에서는 별로 중요하지 않다고 말한 적이 있다(바닐라 자바스크립트는 일반적으로 프레임워크, 라이브러리를 사용하지 않고 웹 개발 하는 것을 의미).
프론트엔드 개발자가 js와 web api를 사용할 줄 아는 것은 당연하다 생각했고, 대부분의 회사들이 프로젝트를 바닐라로 진행하지 않고, React,vue 같은 툴을 실무에 사용하기에 해당 툴에 대한 사용경험과 이해도가 더 중요하다는 의미에서 한 말이었다.
예를 들어, 태그를 선택하거나, 특정 값을 변경할 때, web api로도 구현할 수 있지만, react를 사용한다면 리액트의 방식으로 해당 부분들을 작성해야하는 것처럼 말이다.
그렇다고 리액트를 사용한다는 것이 javascript를 사용하지 않는다는 말도, web api를 사용할 수 없다는 말이 아니고 몰라도 된다는 말은 아니다.
javascript를 사용한다면 관련 문서를 찾아가면서 할 수 있는 것이다.
자바 백엔드 개발자를 채용한다고 할 때, 스프링부트 사용경험은 물어봐도 java 사용할 줄 아는지, 프레임워크 없이 http 서버를 구축하고 사용하는 것을 중요하게 생각하는지를 묻지 않는 것처럼 말이다.
그때는 이렇게 풀어서 말하지 못했다. 원론적으로 기본은 중요하고 앞으로 기본이 중요하다고 답해야겠다.
AJAX(Asynchronous JavaScript and XML)
아작스, 에이작스는 브라우저가 웹 페이지는 전체를 다시 로드하지 않고도 비동기적으로 서버와 데이터를 교환할 수 있게 해준다.
기존에는 form
, submit
으로 데이터를 보내면, 결과가 포함된 완성된 HTML 문서 전체를 받아야 했다(동기적으로 동작).
반면에 AJAX
를 사용하면 응답(주로 JSON
)만 받아서 ui를 수정하면 끝인 것이다. SPA
의 기반 기술이기도 하다.
web 환경에서는 XMLHttpRequest
객체(이벤트 방식)를 기반으로, node에서는 http
모듈을 기반으로 구현된다.
XMLHttpRequest
객체는 IE 브라우저에서 최초로 도입되었다 한다.
그 후, 각 브라우저들에서 지원되고, 구글의 gmail, google map등에 활용되면서 알려졌다.
ajax요청을 위해 XMLHttpRequest
객체를 직접 다루기 보다는 axios
라이브러리(내부적으로 XMLHttpRequest 사용)나, fetch api
(web native api, promise 기반), 그외 라이브러리를 사용한다.
Chrome(V8)의 등장
2008년 구글에서 공개한 크롬 브라우저는 혁신이었다. C++로 작성 된 V8이라는 자바스크립트엔진을 탑재 했는데, 기존의 한줄 한줄 실행되던 자바스크립트 코드를 필요한 경우, 최적화 된 네이티브 머신 코드로 동적으로 컴파일하여 실행하는 JIT 컴파일러를 사용했다.
프로그래밍 언어는 컴파일 언어, 인터프리터 언어로 나뉜다. 인터프리터 언어는 컴파일 단계 없이 코드를 바로 한줄 한줄 실행할 수 있는데, 자바스크립트는 인터프리터 언어이다.
인터프리터 언어는 일반적으로 컴파일 언어에 비해 속도가 느린데, 실행 속도를 향상 시키기 위해 최적 화 기능이 있는 JIT 컴파일러를 도입한 것이다.
- 코드를 AST(추상구문트리)로 변환 후, 인터프리터에서 byte code -> machine code 과정을 거쳐 실행(인터프리터 모드)
- 자주 실행되는 코드 경로와 함수들을 식별해(hot spot), 해당 코드들의 byte code를 최적화한 machine code 생성
- 최적화된 machine code를 캐시에 저장 - (js는 동적타이핑 언어로 실행전까지 타입을 알 수 없어 최적화가 어렵다)
- 동일한 코드가 실행될 때, 인터프리터를 거치지 않고 직접 실행된다(JIT 모드).
- 최적화된 코드가 더 이상 자주 사용되지 않는다면, 캐시에서 제거된다.
이는 자바스크립트의 실행 속도를 크게 향상시켰다. 참고로 해당 최적화와 관련하여 코드를 작성하려면 타입이 중요하다.
js 특성상 AST에서 바이트코드로 변환 할때 오류방지를 위한 타입핸들링에 관한 코드가 생성된다. 최적화 아이디어중에 프로파일링을 통해 타입이 일정하다는 가정하에 타입 관련 예외처리 코드들을 제거함으로써 성능을 높이는 방식있기에, 특히 배열에 타입을 섞어 넣지 말자.
크롬과 V8의 등장은 웹 브라우저 시장에 큰 변화를 가져왔고, 자바스크립트의 가능성에 대한 영역을 확장했다.
2009년 Ryan Dahl은 V8 엔진을 브라우저 외부에서 독립적으로 실행할 수 있도록 포팅하고, 여기에 libuv
를 이용한 크로스 플랫폼 비동기 I/O, 이벤트 루프 등의 기능을 추가하여 Node.js의 초기 버전을 개발했다.
자바스크립트는 원래 브라우저에서 실행되기 위해 개발된 것이었지만, Node.js
를 통해 서버 사이드에서도 자바스크립트를 사용할 수 있게 된다.
SPA, CSR
2010년 전후 스마트폰과 앱의 등장으로 사용자들은 모바일 앱에 익숙해졌고, app-like한 동작들을 웹 애플리케이션에서도 원하게 되었다.
이는 SPA
의 등장 배경이 되는데 SPA
는 하나의 페이지로 구성된 웹 애플리케이션(single page app)을 의미하며 네이티브 앱과 유사한 인터랙티브한 웹 애플리케이션 구축을 가능하게 한다.
전통적인 웹앱(MPA:multi page app)에서는 다양한 화면의 페이지가 다르고, 페이지가 다르면 생기는 문제(깜빡임, 데이터 전체 새로 받아옴)등이 있는데, SPA
는 해당 부분에서 해법을 제시했다.
SPA는 초기에 필요한 모든 리소스를 한 번에 로드하고, 이후 사용자의 인터랙션에 따라 동적으로 페이지를 업데이트한다. 그후 유저 인터렉션에 따라 AJAX를 통해 비동기적으로 데이터를 주고받으며, 받아온 데이터를 기반으로 클라이언트 측에서 UI를 렌더링한다.
이를 통해 페이지 전환 없이도 다양한 화면을 구현할 수 있게 되었다.
SPA의 등장은 CSR(Client Side Rendering)과 밀접한 관련이 있다.
CSR은 서버에서 최소한의 HTML과 데이터만 받아온 후, 클라이언트 측에서 JavaScript를 사용하여 동적으로 UI를 렌더링하는 방식이다. SPA는 CSR을 기반으로 동작하는 것이다.
이를 위한 라이브러리들이 등장하는데, 대표적인 삼총사 Angular
, React
, Vue.js
가 있다.
이렇게 SPA 라이브러리들의 등장으로 프론트엔드 영역이 백엔드의 영역이 분리되고, 프론트엔드 개발자라는 롤도 생겨나게 된다.
Webpack, Babel, Polyfill
JS module system
위의 상황으로 웹 애플리케이션의 규모가 커지고 복잡해지면서 모듈화와 번들링의 필요성이 대두되는데, 번들링을 알아보기 전 모듈시스템을 한번 짚고 가자.
- node에서의 필요성으로 생겨난 CommonJS module 시스템(CJS)
- JS 최초의 모듈시스템(2009년)
- 브라우저에서 뿐만 아니라, 서버사이드 애플리케이션에서도 사용하기 위한 방법
- 동기적 load
- AMD(Asynchronous Module Definition)
- CommonJS에서 합의점을 찾지 못해 분리 됌
- 브라우저를 위한 비동기적 load(브라우저에 중점)
- UMD (Universal Module Definition)
- AMD와 CommonJS 두 그룹으로 나누어지다 보니 서로 호환되지 않는 문제가 발생
- UMD는 AMD와 CommonJS, window에 추가하는 방식까지 모두 가능한 모듈을 작성하는 방식
- exports와 module이 존재하면 CJS,define이 함수이고 define.amd가 존재할 경우 AMD, 그것도 아니라면 window 객체에 모듈을 내보냄
- ESM
- ECMAScript 2015 (ES6)에서 공식적인 모듈 시스템
- import와 export 키워드를 사용
- 현재 대부분의 모던 브라우저와 Node.js에서 ES6 모듈을 지원
참조
- https://yozm.wishket.com/magazine/detail/1261/
- https://velog.io/@yesbb/%EB%AA%A8%EB%93%88-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%98-%EC%97%AD%EC%82%AC-%EA%B7%B8%EB%A6%AC%EA%B3%A0-ESM
- https://velog.io/@minidoo/JavaScript-Module
웹 환경에서는 웹 애플리케이션의 규모가 커지고 모듈화가 필요해지자, 각 모듈들을 어떻게 불러와야에 대한 문제가 생겼다.
웹 사이트의 로딩 속도는 빠를 수록 좋은데, 서버에 파일을 많이 요청할 수록 속도는 느려질 확률이 높을 것이다.
이에 대한 해법으로 모든 모듈을 한 파일에 집어넣는 번들링이 등장한다.
module bundler(webpack)
웹팩 === 모듈번들러 수준으로 웹 개발에서 성공한 웹팩(Webpack)은 프로젝트의 구조를 분석하고, 모듈을 비롯한 관련 리소스들을 찾은 다음, 이를 브라우저에서 이용할 수 있는 번들로 묶는 작업을 수행한다.
웹팩의 주요 목적은 자바스크립트 파일들을 하나(또는 여러 개)의 파일로 묶어내는 것이지만, 로더(Loader)를 이용하면 CSS, 이미지 등 다양한 타입의 파일도 모듈로 취급하여 번들에 포함 시킬 수 있다.
webpack.config.js
파일로 설정을 관리하며, 플러그인을 통해 babel load, 번들 최적화, 환경 변수 주입 등의 추가적인 작업도 가능하다.
그외 번들러로는 rollup, esbuild, vite등이 있다. 트렌드를 볼때 vite가 차기 점유율을 많이 가져오는 것 같다.
https://webpack.kr/ https://joshua1988.github.io/webpack-guide/guide.html https://bepyan.github.io/blog/2023/bundlers
transpiler(Babel), Polyfill
위의 웹팩 처럼 transpiler === Babel 수준으로 성공한 바벨이다. 바벨과 폴리필은 크로스 브라우징 문제를 해결주는 도구이다.
바벨(Babel)의 설정파일은 .babelrc
이고, 구버전 브라우저와의 호환성을 위해 주로 ES6 이상의 코드를 이전 버전의 자바스크립트 코드(ES5)로 변환하는데 사용된다.
그리고 Polyfill은 브라우저에서 지원하지 않는 최신 기능을 Polyfill(기능 구현체)을 통해 사용할 수 있게 해주는데, babel에서 폴리필을 제공해 준다.
바벨도 플러그인 기반으로 동작하며, Preset 혹은 다양한 플러그인을 조합하여 필요한 변환 작업을 수행한다.