브라우저 렌더링 과정
1. HTML 파일 다운로드 및 파싱
-
브라우저는 서버에서 HTML 파일을 받아옵니다.
-
HTML 파일을 받아오면, 브라우저의 렌더링 엔진이 HTML 파싱을 시작합니다.
-
이때 HTML을 분석하며 DOM (Document Object Model) 트리를 만듭니다. DOM은 HTML 요소들을 노드 형태로 표현한 트리 구조입니다.
-
HTML에 포함된 <link>, <script>, <img>
등 외부 리소스는 별도로 다운로드 요청을 보내며, CSS와 JavaScript 파일이 이 과정에서 로드됩니다.
2. CSS 파일 다운로드 및 파싱
-
브라우저는 HTML 파싱 중 태그를 만나면, CSS 파일을 서버에서 다운로드받아 파싱합니다.
-
CSS 파일은 브라우저의 렌더링 엔진에 의해 분석되어 CSSOM (CSS Object Model) 트리로 변환됩니다.
-
CSSOM은 각 HTML 요소에 적용할 스타일을 정의하며, DOM과 결합하여 최종 화면에 표시될 구조를 만듭니다.
3. DOM과 CSSOM 결합 (Render Tree 생성)
-
브라우저는 파싱된 DOM 트리와 CSSOM 트리를 결합하여 렌더 트리 (Render Tree) 를 만듭니다.
-
렌더 트리는 화면에 실제로 그려질 요소들만 포함하며, display: none 속성이 있는 요소나 태그 내부의 내용은 제외됩니다.
-
이 렌더 트리는 각 요소의 위치와 스타일 정보를 포함한 시각적 구조입니다.
4. 레이아웃 단계 (Layout)
- 레이아웃 단계에서는 렌더 트리를 기반으로 각 요소의 정확한 위치와 크기를 계산합니다. 이 과정을 "reflow"라고도 부릅니다.
- 브라우저는 페이지의 뷰포트 크기, 콘텐츠 크기, 그리고 CSS 스타일에 따라 각 요소가 어디에 배치될지를 계산합니다.
5. 페인팅 (Painting)
- 페인팅 단계에서는 각 요소의 스타일(색상, 배경 이미지, 텍스트 등)을 렌더 트리에 적용하여 화면에 그려질 픽셀 데이터를 생성합니다.
- 이 단계에서 요소들은 layers (레이어)로 나뉘어 처리되며, 각 레이어는 페인팅 작업을 통해 색상, 그림자, 텍스트, 경계선 등의 시각적 스타일을 적용받습니다.
6. 컴포지팅 (Compositing)
- 페인팅 작업이 끝난 후, 여러 레이어로 나뉘어진 요소들은 최종적으로 합쳐져 화면에 렌더링됩니다. 이 과정을 컴포지팅 (Compositing)이라고 합니다.
- 브라우저는 각 레이어를 GPU를 통해 빠르게 렌더링하고, 화면에 그려질 최종 이미지를 사용자에게 표시합니다.
7. JavaScript 실행 (자바스크립트의 영향)
- HTML 파싱 중
- JavaScript는 DOM을 동적으로 변경할 수 있기 때문에, 브라우저는 DOM이나 CSSOM을 다시 파싱하거나 레이아웃, 페인팅, 컴포지팅을 다시 해야 하는 경우가 있습니다. 이를 reflow나 repaint라고 합니다.
- 예를 들어, DOM에 새로운 요소를 추가하거나 CSS 스타일을 변경하면 브라우저는 레이아웃을 다시 계산하고, 그에 따라 화면을 다시 그립니다.
8. 최종 렌더링
- 모든 과정이 끝나면, 브라우저는 화면에 최종적으로 렌더된 페이지를 사용자에게 보여줍니다.
- 이 모든 과정은 매우 짧은 시간 안에 이루어지며, 브라우저는 빠르게 사용자가 웹 페이지와 상호작용할 수 있도록 처리합니다.
CSR (Client-Side Rendering) vs SSR (Server-Side Rendering)
1. Client-Side Rendering (CSR)
- CSR에서는 클라이언트(브라우저)가 HTML, CSS, JavaScript 파일을 받아서 클라이언트 측에서 렌더링을 수행합니다.
- 초기 페이지 로드 시 서버는 기본 HTML만 제공하고, JavaScript 파일을 다운로드한 후, 브라우저가 이 파일을 실행하여 필요한 데이터를 가져와 화면에 동적으로 그립니다.
장점
- 빠른 사용자 인터랙션: 페이지 내에서 빠른 네비게이션과 동적 콘텐츠 업데이트가 가능해 사용자 경험이 부드러워집니다.
- 더 적은 서버 부담: 서버는 기본 HTML과 JavaScript 파일만 제공하며, 나머지 처리는 클라이언트에서 이루어집니다. 서버 자원을 아낄 수 있습니다.
- 애플리케이션과 같은 사용자 경험: CSR은 SPA(Single Page Application) 방식으로 페이지 전환 없이 데이터를 업데이트해 앱처럼 사용할 수 있습니다.
단점
- 초기 로딩 속도 느림: 처음에 빈 HTML을 받고, JavaScript 파일을 로드하고 실행한 후에야 콘텐츠가 나타나므로 초기 로딩이 느릴 수 있습니다.
- SEO(Search Engine Optimization) 문제: 클라이언트 측에서만 렌더링이 이루어지기 때문에, 검색 엔진 크롤러가 제대로 콘텐츠를 인덱싱하지 못할 가능성이 있습니다. 일부 검색 엔진은 JavaScript 실행을 지원하지 않기 때문입니다.
- JavaScript 의존성: 브라우저에서 JavaScript가 비활성화된 경우에는 사이트가 제대로 작동하지 않습니다.
2. Server-Side Rendering (SSR)
- SSR에서는 서버에서 HTML을 모두 렌더링한 후에 클라이언트로 전송합니다. 클라이언트는 렌더링된 HTML을 받아 바로 화면에 표시할 수 있습니다.
- 서버에서 미리 HTML을 준비하여 전달하므로, 클라이언트는 초기 로드에서 완전한 페이지를 받아 볼 수 있습니다.
장점
-
빠른 초기 로딩: 서버에서 완전한 HTML을 바로 제공하므로, 초기 화면이 빠르게 표시됩니다.
-
SEO 친화적: 검색 엔진 크롤러가 서버에서 완전한 HTML을 받아 바로 인덱싱할 수 있어 SEO가 잘 동작합니다.
-
첫 페이지 유저 경험 향상: 사용자는 즉시 페이지의 콘텐츠를 확인할 수 있어 더 나은 사용자 경험을 제공합니다.
단점
- 서버 부하 증가: 매번 페이지를 요청할 때마다 서버가 HTML을 생성하고 렌더링해야 하므로, 트래픽이 많아질 경우 서버에 부담이 가중됩니다.
- 느린 사용자 인터랙션: 클라이언트가 서버에서 새로운 페이지를 다시 받아야 하므로, SPA처럼 부드러운 사용자 경험을 제공하기 어려울 수 있습니다.
- 복잡한 구현: 서버에서 렌더링과 상태 관리를 해야 하므로, CSR에 비해 구현이 복잡하고 더 많은 리소스가 필요합니다.
RESTful API의 원칙
1. 리소스 기반 설계
- RESTful API에서는 리소스(Resource)를 기반으로 설계됩니다. 각 리소스는 고유한 URI(Uniform Resource Identifier)를 통해 식별됩니다.
- 리소스는 일반적으로 명사를 사용해 식별하며, URI는 해당 리소스를 표현하는 경로를 나타냅니다.
2. HTTP 메서드 사용
- HTTP 메서드를 사용하여 리소스에 대한 작업을 정의합니다. 각 메서드는 CRUD(Create, Read, Update, Delete) 작업을 표현하는 데 사용됩니다.
- GET: 리소스 조회 (Read)
- POST: 리소스 생성 (Create)
- PUT: 리소스 전체 수정 (Update)
- PATCH: 리소스 부분 수정 (Partial Update)
- DELETE: 리소스 삭제 (Delete)
3. 상태 없음 (Stateless)
- RESTful API는 Stateless(상태 비저장성)를 가져야 합니다. 즉, 서버는 각 클라이언트의 요청을 독립적으로 처리하며, 요청 간의 상태를 서버가 저장하지 않습니다.
- 클라이언트의 모든 요청은 필요한 정보를 전부 포함해야 하고, 서버는 해당 요청을 통해 모든 처리를 수행할 수 있어야 합니다.
예시: 클라이언트가 API를 호출할 때, 매 요청마다 인증 토큰 등을 포함하여 서버가 별도의 세션 상태를 기억하지 않도록 설계해야 합니다.
4. 캐시 가능 (Cacheable)
- RESTful API는 응답을 캐시할 수 있어야 합니다. 적절한 캐싱 전략을 통해 네트워크 트래픽을 줄이고, 성능을 개선할 수 있습니다.
- 서버는 응답에 캐시 관련 헤더(
Cache-Control
, Expires
, ETag
등)를 포함하여, 클라이언트나 프록시 서버가 결과를 캐싱할 수 있도록 합니다.
5. 계층화된 시스템 (Layered System)
- RESTful API는 계층화된 아키텍처를 가져야 합니다. 클라이언트는 중간 서버(프록시, 게이트웨이 등)를 통해 최종 서버에 접근할 수 있으며, 이러한 계층을 명확히 인식할 필요는 없습니다.
- 이 원칙을 통해 시스템은 확장성 및 보안 측면에서 이점을 얻을 수 있습니다.
예시: 클라이언트는 직접적인 서버가 아닌 프록시 서버를 통해 API 요청을 처리할 수 있고, 클라이언트는 이러한 중간 단계를 알 필요가 없습니다.
6. 일관된 인터페이스 (Uniform Interface)
-
RESTful API는 일관된 인터페이스를 제공해야 합니다. 클라이언트와 서버가 서로 독립적으로 발전할 수 있도록, 인터페이스 설계가 일관되고 자주 변경되지 않아야 합니다.
-
RESTful API에서는 URI를 통해 리소스를 식별하고, HTTP 메서드를 통해 리소스에 대한 작업을 일관되게 처리합니다.
RESTful API를 설계할 때의 이점
1. 확장성
- 서버와 클라이언트가 독립적으로 동작할 수 있으므로, 시스템 확장성이 좋아집니다. API 자체가 리소스에 대한 명확한 인터페이스를 제공하기 때문에 서버 확장, 클라이언트 확장 모두 용이합니다.
2. 유지 보수성 (Maintainability)
- 일관된 인터페이스와 리소스 기반 설계를 따름으로써, API가 쉽게 이해되고 유지보수할 수 있습니다. 특히, 여러 팀이 협력할 때 유용합니다.
- 클라이언트-서버 분리 (Client-Server Separation)
- 클라이언트와 서버가 명확히 분리되므로, 클라이언트는 서버의 내부 구현을 몰라도 되고, 서버는 클라이언트의 상태를 유지할 필요가 없습니다. 이렇게 독립적인 개발이 가능해집니다.
- 재사용성 (Reusability)
- 리소스 중심 설계로, 동일한 API를 다양한 용도로 재사용할 수 있습니다. 예를 들어, 여러 서비스가 동일한 사용자 정보를 활용할 때 동일한 API를 사용하면 됩니다.
CORS (Cross-Origin Resource Sharing)
CORS가 무엇인지, 그리고 브라우저에서 어떻게 동작하는지 설명
CORS(Cross-Origin Resource Sharing)는 웹 애플리케이션이 다른 출처의 리소스에 접근할 수 있도록 허용하는 보안 메커니즘입니다. CORS는 동일 출처 정책(Same-Origin Policy, SOP)을 완화하기 위해 사용됩니다.
동일 출처 정책(Same-Origin Policy): 브라우저에서 보안상의 이유로, 스크립트가 자신이 로드된 출처(Origin)와 다른 출처의 리소스에 접근하는 것을 제한하는 보안 기능입니다.
CORS의 동작 방식
CORS는 클라이언트(주로 브라우저)와 서버 간의 요청 및 응답에 포함된 헤더를 통해 동작합니다. CORS 요청은 두 가지 형태로 나뉩니다: 단순 요청(Simple Requests)과 사전 요청
(Preflight Requests).
1. 단순 요청 (Simple Requests)
단순 요청은 다음 조건을 모두 만족할 때 발생
-
HTTP 메서드가 GET, POST, 또는 HEAD일 때.
-
요청 헤더에 허용된 헤더만 포함될 때 (Accept, Accept-Language, Content-Language, Content-Type 등이 허용됨).
이 경우, 클라이언트는 요청을 보내고, 서버는 응답에 CORS 헤더를 포함시켜 클라이언트가 요청이 허용되었는지 판단할 수 있게 합니다.
2. 사전 요청 (Preflight Requests)
단순 요청이 아닌 경우(예: 메서드가 PUT
, DELETE
이거나 사용자 정의 헤더가 포함된 경우), 브라우저는 실제 요청을 보내기 전에 사전 요청(Preflight Request)을 보냅니다. 이 요청은 서버에 특정 요청이 허용되는지 확인하기 위한 안전장치입니다.
사전 요청은 HTTP 메서드 OPTIONS를 사용하여 전송되며, 서버는 요청을 허용할지 여부를 헤더로 응답합니다.
-
Access-Control-Allow-Methods: 서버가 허용하는 HTTP 메서드를 지정합니다.
-
Access-Control-Allow-Headers: 클라이언트가 사용할 수 있는 요청 헤더 목록을 지정합니다.
-
Access-Control-Max-Age: 클라이언트가 사전 요청 결과를 얼마나 오래 캐시할 수 있는지 지정합니다.