Frontend

[Frontend] Web Fundamentals 정리

teddy bear 2026. 7. 3. 20:14
반응형

🌍 웹 기초 지식 완전 정리

"프레임워크는 몇 년마다 바뀌지만, 브라우저와 네트워크의 기본 원리는 변하지 않는다."

프론트엔드 면접에서는 프레임워크 지식만큼이나 웹의 근본 동작 원리를 자주 묻습니다. 이 글에서는 URL 입력부터 화면 렌더링까지의 전체 과정을 정리합니다.


📌 목차

  1. 브라우저에 URL을 입력하면 일어나는 일
  2. 렌더링 과정: Critical Rendering Path
  3. 리플로우 vs 리페인트
  4. HTTP/HTTPS와 캐싱
  5. CORS
  6. 브라우저 저장소 비교

1️⃣ 브라우저에 URL을 입력하면 일어나는 일

면접 단골 질문입니다. 전체 흐름을 단계별로 정리하면:

1. URL 파싱
2. DNS 조회 (도메인 → IP 주소)
3. TCP 연결 (3-way Handshake)
4. TLS 핸드셰이크 (HTTPS인 경우)
5. HTTP 요청 전송
6. 서버 응답 수신
7. HTML 파싱 → DOM 트리 생성
8. CSS 파싱 → CSSOM 트리 생성
9. DOM + CSSOM → Render Tree 생성
10. Layout (요소 위치/크기 계산)
11. Paint (화면에 실제로 그리기)

TCP 3-way Handshake

클라이언트 ──SYN──▶ 서버
클라이언트 ◀──SYN-ACK── 서버
클라이언트 ──ACK──▶ 서버
        (연결 완료, 데이터 전송 시작)

💡 면접 포인트: 단순히 단계를 나열하는 것보다, "DNS 조회 결과는 캐싱되어 다음 요청부터는 생략될 수 있다"거나 "HTTPS는 TCP 연결 후 TLS 핸드셰이크가 추가로 필요해 초기 연결이 더 느리다" 같은 디테일을 곁들이면 좋습니다.


2️⃣ 렌더링 과정: Critical Rendering Path

브라우저가 HTML/CSS를 받아 화면에 그리기까지의 과정을 Critical Rendering Path라고 부릅니다.

HTML ──파싱──▶ DOM Tree
                          ╲
                           ╲──▶ Render Tree ──▶ Layout ──▶ Paint ──▶ Composite
                          ╱
CSS  ──파싱──▶ CSSOM Tree

왜 <script> 태그 위치가 중요한가?

<head>
  <script src="app.js"></script> <!-- ❌ 파싱을 막음(Render Blocking) -->
</head>
<body>
  ...
  <script src="app.js" defer></script> <!-- ✅ HTML 파싱을 막지 않고, 파싱 완료 후 실행 -->
</body>

속성동작

없음 스크립트를 만나는 즉시 다운로드+실행, HTML 파싱 중단
async 다운로드는 비동기, 완료되면 즉시 실행 (순서 보장 안 됨)
defer 다운로드는 비동기, HTML 파싱 완료 후 순서대로 실행

3️⃣ 리플로우 vs 리페인트

Reflow (Layout)Repaint

발생 조건 요소의 크기, 위치가 변경될 때 색상, 배경 등 시각적 스타일만 변경될 때
비용 높음 (레이아웃 전체 재계산 가능) 상대적으로 낮음
예시 width, height, display 변경 color, background-color 변경
// ❌ 나쁜 예: 매 반복마다 강제 Reflow 발생
for (let i = 0; i < 100; i++) {
  element.style.width = element.offsetWidth + 1 + 'px'; // 읽기+쓰기 반복
}

// ✅ 좋은 예: 읽기와 쓰기를 분리
const width = element.offsetWidth;
element.style.width = width + 100 + 'px';

최적화 팁

  • transform, opacity는 Compositor 단계에서만 처리되어 Reflow/Repaint 없이 애니메이션 가능 → GPU 가속 활용
  • will-change: transform 힌트로 브라우저에 최적화 유도 가능 (남용은 오히려 메모리 낭비)

4️⃣ HTTP/HTTPS와 캐싱

HTTP 캐싱 헤더

Cache-Control: max-age=3600, public
ETag: "abc123"
[브라우저]                          [서버]
    │                                  │
    │──1차 요청──────────────────────▶│
    │◀───응답 (ETag: abc123)──────────│
    │                                  │
    │──재요청 (If-None-Match: abc123)─▶│
    │◀───304 Not Modified (본문 없음)──│  ← 캐시 재사용

캐시 전략 요약

헤더역할

Cache-Control: max-age 지정 시간 동안 재검증 없이 캐시 사용
ETag 리소스의 고유 해시값, 변경 여부 확인용
Last-Modified 마지막 수정 시각 기반 검증

HTTP/1.1 vs HTTP/2

HTTP/1.1HTTP/2

연결 방식 요청마다 별도 연결 필요 (또는 Keep-Alive) 하나의 연결에서 다중 요청 처리 (Multiplexing)
Head-of-Line Blocking 발생 완화
헤더 압축 없음 HPACK 압축

5️⃣ CORS (Cross-Origin Resource Sharing)

브라우저의 Same-Origin Policy로 인해, 다른 출처(도메인/포트/프로토콜이 다른)의 리소스에 대한 요청은 기본적으로 차단됩니다. CORS는 서버가 "이 출처는 허용한다"고 명시적으로 알려주는 메커니즘입니다.

[프론트엔드: https://my-app.com]
        │
        │  fetch('https://api.other.com/data')
        ▼
[서버: https://api.other.com]
        │
        │ 응답 헤더: Access-Control-Allow-Origin: https://my-app.com
        ▼
브라우저가 헤더 확인 후 응답을 JS에 전달 (없으면 차단)

Simple Request vs Preflight Request

  • Simple Request: GET, POST(특정 Content-Type)는 바로 요청
  • Preflight Request: PUT, DELETE, 커스텀 헤더 사용 시 브라우저가 먼저 OPTIONS 요청으로 서버에 허용 여부를 확인
클라이언트 ──OPTIONS(preflight)──▶ 서버
클라이언트 ◀──허용 헤더 응답────── 서버
클라이언트 ──실제 요청(PUT)──────▶ 서버

💡 면접 포인트: CORS 에러는 서버가 해결해야 하는 문제입니다. 프론트엔드에서 아무리 코드를 바꿔도 서버가 Access-Control-Allow-Origin을 설정하지 않으면 해결되지 않는다는 점을 명확히 설명할 수 있어야 합니다.


6️⃣ 브라우저 저장소 비교

용량만료서버 전송용도

Cookie ~4KB 설정 가능 매 요청마다 자동 전송 인증 토큰, 세션
localStorage ~5-10MB 없음 (수동 삭제 전까지) 안 됨 사용자 설정, 캐시 데이터
sessionStorage ~5-10MB 탭 종료 시 안 됨 일시적 폼 데이터
IndexedDB 매우 큼 (수백MB+) 없음 안 됨 대용량 구조화된 데이터

⚠️ 보안 팁: 인증 토큰을 localStorage에 저장하면 XSS 공격에 취약합니다(JS로 직접 접근 가능). 민감한 토큰은 httpOnly 쿠키에 저장하는 것이 더 안전합니다.


🔚 마무리

웹 기초 지식은 프레임워크가 아무리 바뀌어도 변하지 않는 가장 근본적인 자산입니다. "왜 이렇게 동작하는가"를 스스로 설명할 수 있을 때까지 원리를 파고드는 것이, 프레임워크 트렌드를 따라가는 것보다 장기적으로 더 큰 경쟁력이 됩니다.