문법을 외우기 전에, 리액트가 어떤 문제를 풀려고 나왔는지부터 알면 훨씬 쉬워요. 이번 장은 코드보다 큰 그림과 이야기 중심입니다. 편하게 읽어 내려가세요.
리액트는 "화면(UI)을 만들기 위한 자바스크립트 라이브러리"예요. 페이스북(현 Meta)이 만들었고, 복잡한 화면을 작은 부품(컴포넌트)으로 나눠 조립하듯 만들 수 있게 해줍니다.
라이브러리를 쉽게 말하면, 누군가가 미리 만들어 놓은 유용한 코드들의 모음집이에요. 예를 들어 계산기를 만들 때 제곱근·난수 같은 걸 직접 구현하지 않고, 자바스크립트가 미리 준 Math 기능을 그냥 가져다 씁니다.
Math.sqrt(25); // 5 (제곱근을 직접 안 만들어도 됨)
Math.random(); // 0~1 사이 난수
이 Math도 누군가 미리 만들어 둔 기능이에요. 이런 게 바로 라이브러리랍니다.
리액트도 똑같아요. 웹 화면을 만들 때 필요한 기능들을 이미 만들어 놓은 라이브러리예요. 예를 들어 버튼을 눌렀을 때 숫자 증가 → 화면 다시 그리기 → 바뀐 부분만 업데이트하는 귀찮은 일을 리액트가 대신 해줘요. 우리는 이 정도만 쓰면 됩니다.
function Counter() {
const [count, setCount] = useState(0);
return (
<>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>증가</button>
</>
);
}
나머지 화면 변경 · DOM 조작 · 이벤트 연결은 리액트가 알아서 처리해요. 즉 내 코드 안에서 리액트가 제공하는 기능을 가져다 쓰는 거예요.
const [count, setCount] = useState(0) 에서 const는 자바스크립트 문법, count는 내가 만든 변수, useState()는 리액트가 준 기능이에요. 이렇게 내 코드 + 리액트 코드가 한데 섞여 돌아갑니다.함수(function)는 하나의 기능이에요. 예를 들어 add(a, b)는 두 수를 더하는 함수 하나죠.
function add(a, b) {
return a + b;
}
반면 라이브러리(library)는 함수·클래스·객체 등을 잔뜩 모아 놓은 것이에요. 리액트 안에는 useState(), useEffect(), useMemo(), createContext() 같은 수많은 기능이 들어 있어요.
import { useState } from "react" = react라는 공구상자에서 useState라는 도구를 꺼내 쓰는 것.
그럼 프레임워크(framework)는 뭐가 다를까요? 라이브러리는 내가 필요할 때 불러서 쓰니까 내 코드가 중심이에요. 반대로 프레임워크는 프레임워크가 흐름을 정하고, 내가 그 규칙에 맞춰 코드를 채워 넣죠 — 프레임워크가 중심이에요.
| 구분 | 누가 부르나 | 중심 |
|---|---|---|
| 라이브러리 (React) | "필요하면 내가 부른다" | 내 코드 |
| 프레임워크 (Angular 등) | "필요할 때 프레임워크가 내 코드를 부른다" | 프레임워크 |
아까 이 한 줄, 사실 세 가지 일을 동시에 하고 있어요.
const [count, setCount] = useState(0);
// 1. count 라는 상태를 저장한다
// 2. setCount() 로 값을 바꿀 수 있게 한다
// 3. 값이 바뀌면 화면을 자동으로 다시 그린다
리액트를 안 쓰면 이 세 번째(화면 갱신)를 내가 직접 해야 해요. 버튼으로 숫자를 올리는 기능을 순수 자바스크립트로 만들면 이렇게 됩니다.
<h1 id="number">0</h1>
<button id="btn">증가</button>
let count = 0;
const number = document.getElementById("number");
const button = document.getElementById("btn");
button.addEventListener("click", () => {
count++;
number.textContent = count; // ← 화면을 내가 직접 바꿔줘야 함
});
변수도 직접 만들고, 값도 직접 바꾸고, 화면(textContent)까지 내가 직접 갱신해야 해요. 리액트는 바로 이 마지막 작업을 자동으로 해줘요.
const [count, setCount] = useState(0);
return (
<>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>증가</button>
</>
);
setCount(count + 1) 딱 이것만 하면, 리액트가 "count가 바뀌었네? 그럼 h1도 다시 그려야지" 하고 알아서 화면을 갱신해요. number.textContent = count 같은 코드를 쓸 필요가 없어요.
let count = 0 으로 만들고 버튼을 누르면 count는 0 → 1 → 2 로 증가는 해요. 하지만 화면은 계속 0만 보여요.
리액트는 일반 변수(let)가 바뀐 사실을 모르기 때문이에요. 그래서 화면과 연결된 상태(state)가 필요한 거예요.
| 순수 자바스크립트 | 리액트 | |
|---|---|---|
| 값 변경 | count++ | setCount(count + 1) |
| 화면 갱신 | number.textContent = count (내가 직접) | 리액트가 자동으로 |
useState를 단순히 "변수를 만드는 함수"로 오해하면 안 돼요. 정확히는 "리액트가 관리하는 특별한 변수(상태)를 만들고, 값이 바뀌면 화면도 함께 업데이트되도록 연결해 주는 기능"이에요. (자세한 건 CH 04에서 깊게 다뤄요.)2000년대 후반, 웹 화면이 점점 복잡해지면서 "어디를 바꾸면 어디가 어떻게 변하는지"를 사람이 일일이 챙기기가 너무 어려워졌어요. 페이스북도 뉴스피드처럼 실시간으로 바뀌는 화면 때문에 골머리를 앓았죠. 그 해결책으로 조던 워크(Jordan Walke)라는 페이스북 엔지니어가 리액트를 만들었어요.
| 용어 | 의미 |
|---|---|
| Reconciliation (재조정) | 가상 DOM을 비교해서 "무엇이 바뀌었는지" 알아내는 전체 알고리즘/과정 |
| Diffing (diff) | 재조정 안에서 이전/이후를 비교하는 구체적인 비교 작업 |
| Rendering | state/props 변화로 새 가상 DOM을 "계산"하는 단계 (그림의 1→2 사이) |
| Commit | 실제로 DOM에 반영하는 단계 (그림의 3→4) |
SPA(Single Page Application, 단일 페이지 애플리케이션)는 페이지 하나를 불러온 뒤, 화면 전환이 필요할 때 전체를 새로 받지 않고 필요한 부분만 자바스크립트로 바꿔치기하는 방식이에요.
| 주변 도구 | 역할 |
|---|---|
| Vite / CRA | 리액트 프로젝트를 만들고 개발 서버로 띄워주는 빌드 도구. (실습에서 Vite를 썼죠.) |
| React Router | SPA에서 페이지 전환(라우팅)을 담당. |
| 상태관리(Zustand·Redux) | 여러 컴포넌트가 공유하는 전역 상태를 관리. |
| Next.js | 리액트 기반의 풀스택 프레임워크(서버 렌더링 등). |