Skip to Content
ReactBasic1. React의 탄생과 기본 철학

React는 왜 태어났을까?

이 문서의 목표 — React가 등장한 이유를 역사 순서로 이해하고, 4대 핵심 개념(컴포넌트 · 상태 · Virtual DOM · 선언형)과 JSX가 화면에 그려지기까지의 전체 흐름을 잡습니다.

1995 — 정적인 웹 + JavaScript 탄생

초기 웹은 읽기만 가능한 문서였습니다. 넷스케이프의 브렌던 아이크가 만든 JavaScript 덕분에 버튼 클릭 같은 간단한 상호작용이 비로소 가능해졌습니다.

<button onclick="alert('Hello!')">눌러보세요</button>

버튼을 누르면 onclick에 적힌 JS가 실행되어 경고창이 뜹니다. 페이지 전체를 다시 불러오지 않고 반응하는 최초의 “동적인 웹” 패턴입니다.

2000년대 중반 — AJAX와 jQuery

AJAX는 페이지 전체 새로고침 없이 필요한 부분만 갱신하게 해줬고, jQuery는 브라우저마다 다른 DOM API 차이를 숨겨 짧은 코드로 DOM을 다루게 해줬습니다.

fetch("/user") .then(res => res.json()) .then(data => { document.querySelector("#name").textContent = data.name; });

서버에 비동기 요청 → 응답(JSON)을 받아 #name의 텍스트 교체합니다.

2010년대 — 대형 웹앱과 “상태 일관성의 위기”

페이스북·넷플릭스처럼 수많은 상태가 실시간으로 변하는 앱이 등장합니다. jQuery식으로 DOM을 직접 조작하면 규모가 커질수록 변경 경로가 얽혀서 “데이터는 바뀌었는데 화면은 안 바뀌는” 상태-UI 불일치 버그가 폭증했습니다.

2013 — Facebook의 해법, React

“상태를 선언적으로 기술하면, 화면 갱신은 React가 알아서”

이를 위해 4가지 핵심 설계가 등장합니다: 컴포넌트 · 상태 · Virtual DOM · 선언형

비유 — 과거의 방식은 벽돌을 직접 쌓는 목수(명령형), React는 설계도(JSX/VDOM)만 주면 시공팀(ReactDOM)이 최적으로 시공하는 방식(선언형)입니다.

React의 4대 기둥

① 컴포넌트 (Component)

UI를 독립된 기능 단위로 나눈, 재사용 가능한 함수

function Button({ kind = "primary", children }) { const className = kind === "primary" ? "btn-primary" : "btn-ghost"; return <button className={className}>{children}</button>; } function App() { return ( <div> <Button>좋아요</Button> <Button kind="ghost">공유하기</Button> </div> ); }

Button은 입력(props)에 따라 모양만 달라지는 독립 부품이고, App은 그 부품을 조립합니다. 중복이 사라지고 유지보수가 쉬워집니다.

흔한 오해 — “컴포넌트 = 파일 1개”가 아닙니다. 핵심은 역할 단위로 나누는 것입니다.

② 상태 (State)

시간에 따라 변하는 데이터. UI가 반드시 따라야 하는 진실의 원천(source of truth)

핵심 원칙: UI는 상태의 함수입니다. 상태가 바뀌면 UI는 자동으로 일관되게 갱신됩니다.

function Counter() { const [count, setCount] = React.useState(0); return ( <> <p>{count}번 클릭됨</p> {/* ⚠️ 비권장: 같은 렌더의 count 값을 재사용 */} <button onClick={() => setCount(count + 1)}>+1</button> {/* ✅ 권장: 함수형 업데이트 → 직전 상태를 안전하게 참조 */} <button onClick={() => { setCount(c => c + 1); setCount(c => c + 1); // 같은 이벤트 안에서도 +2 보장 }}>+2</button> </> ); }

setCount(count + 1)을 연속 두 번 호출하면 +1만 반영될 수 있습니다. React가 여러 업데이트를 묶어 처리(배치 업데이트)하기 때문인데, 함수형 업데이트 setCount(c => c + 1)은 항상 최신 값을 받아 안전합니다.

흔한 오해 — 상태 ≠ 전역 상태. 로컬 상태가 기본이고, 정말 필요할 때만 Context/Redux/Zustand 같은 전역 도구를 씁니다.

③ Virtual DOM

UI를 JS 객체(설계도)로 만들고, 이전 설계도와 새 설계도를 비교(diff) 해서 달라진 부분만 실제 DOM에 반영(패치)하는 전략

(이전 VDOM) vs (새 VDOM) │ diff 최소 변경 목록(패치) 실제 DOM에 적용
const title = document.createElement("h1"); title.textContent = "Hello!"; document.body.appendChild(title); title.textContent = "Hi!"; // 시점·순서를 전부 개발자가 직접 관리

흔한 오해 — VDOM이 DOM을 대체하는 게 아닙니다. 최종 결과물은 언제나 실제 DOM이고, VDOM은 “무엇이 바뀌었나”를 계산하는 용도입니다.

④ 선언형 UI

무엇을 보여줄지(상태 → UI 규칙)만 선언하면, 어떻게 DOM을 조작할지는 React가 알아서 처리

// 절차를 하나하나 직접 지시 const btn = document.querySelector('#btn'); const panel = document.querySelector('#panel'); btn.addEventListener('click', () => { panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; });

DOM의 현재 상태를 개발자가 계속 추적해야 합니다.

흔한 오해 — “선언형은 느리다”? 추상화 비용은 있지만, 규모가 커질수록 유지보수 효율과 버그 감소 이점이 훨씬 큽니다.

렌더 파이프라인: JSX가 화면이 되기까지

JSX (사람 친화적 표기) └─ Babel (변환) └─ React.createElement(...) → Virtual DOM (설계도) └─ ReactDOM.createRoot / render → 실제 DOM 최소 패치

JSX 작성

JS 안에서 HTML처럼 UI를 쓰는 문법 설탕. {}로 JS 표현식을 넣을 수 있습니다.

const name = "홍길동"; const el = <h1>Hello, {name.toUpperCase()}!</h1>;

JSX는 문자열도 아니고, 브라우저가 직접 이해하지 못합니다. 반드시 변환이 필요합니다.

Babel이 변환

JSX와 최신 JS 문법을 브라우저가 아는 코드로 바꿔주는 통역사입니다.

// 입력 const el = <h1>Hello</h1>; // Babel 출력 (개념) const el = React.createElement("h1", null, "Hello");

createElement가 VDOM 객체 생성

실제 DOM이 아닌 평범한 JS 객체를 만듭니다. 이 객체 트리가 곧 설계도입니다.

{ type: "h1", key: null, ref: null, props: { children: "Hello" } }

ReactDOM이 실제 DOM에 반영

createRoot로 React가 관리할 루트를 만들고(React 18+), render가 VDOM을 비교해 필요한 부분만 실제 DOM에 그립니다.

직접 돌려보기 — CDN + Babel 최소 실행

빌드 도구 없이 HTML 한 장으로 원리를 체험할 수 있습니다.

    • server.js
      • index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Hello React with Babel</title> <!-- React & ReactDOM: VDOM 생성 + DOM 패치 엔진 --> <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script> <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script> <!-- Babel: JSX → 브라우저 호환 코드 --> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> </head> <body> <div id="root"></div> <!-- type="text/babel"이 없으면 브라우저가 JSX를 해석하지 못해 실패! --> <script type="text/babel"> function Counter() { const [count, setCount] = React.useState(0); return ( <section> <p>Count: {count}</p> <button onClick={() => setCount(c => c + 1)}>+1</button> </section> ); } const root = ReactDOM.createRoot(document.getElementById("root")); root.render(<Counter />); </script> </body> </html>

학습 경로 — ① CDN + Babel Standalone으로 원리 체득 → ② Vite/CRA로 핫리로드·빌드·TS까지 갖춘 실무 환경으로 이동. Babel Standalone은 런타임에 매번 변환하므로 개발 학습용이며, 실무 번들링과는 다릅니다.

언제 React가 좋은 선택일까?

상황판단
상태 변화가 잦은 대규모 앱 (대시보드, SNS, 커머스)✅ React가 거의 필수
실시간 데이터 갱신, 복잡한 사용자 인터랙션✅ 적합
정적이고 작은 페이지, 수명이 짧은 프로젝트⚠️ 과할 수 있음

용어 정리

핵심 용어 글로서리 펼치기

용어한 줄 정의
JSXJS 안에 HTML처럼 UI를 쓰는 확장 문법. {}로 표현식 삽입
BabelJSX/최신 문법을 브라우저 호환 코드로 바꾸는 트랜스파일러
Virtual DOM화면 구조의 JS 객체 설계도. diff 계산 후 최소 패치용
Diffing이전/다음 VDOM 비교. 같은 타입은 재사용, 다르면 교체
Batching여러 상태 변경을 묶어 렌더 횟수를 줄이는 최적화
Component재사용 가능한 UI 단위 (함수 컴포넌트 중심)
Props부모 → 자식으로 전달하는 읽기 전용 입력값
State컴포넌트 내부의 변할 수 있는 데이터
createRoot / renderReact 루트 생성(18+) 후 VDOM을 실제 DOM에 반영
Bundler모듈 묶기 + HMR/빌드/최적화 (Webpack, Vite)
CDN라이브러리를 네트워크로 제공하는 분산 전송망
ExpressNode 기반 경량 서버 (정적 파일·간단 API)

자가 점검 체크리스트

  • 역사 → 문제 → 해결(React) 흐름을 1분 안에 설명할 수 있다
  • JSX → Babel → createElement → VDOM → render 흐름을 그림으로 그릴 수 있다
  • 명령형 vs 선언형 차이를 코드로 시연할 수 있다
  • CDN+Babel 최소 실행 → Vite/CRA 실무 환경 경로를 설명할 수 있다
  • “언제 React가 과한가”를 기준을 들어 설명할 수 있다
Last updated on