React 기초1편

React란 무엇인가 — 선언형 UI와 컴포넌트라는 발상

React가 해결하려 한 문제는 무엇이고, '명령형에서 선언형으로'라는 전환은 왜 중요한가. 웹 개발 경험에 빗대어 React의 핵심 발상을 짚습니다.

웹을 조금이라도 만져봤다면 React라는 이름은 익숙할 것입니다. 채용 공고에도, 튜토리얼에도, 남의 프로젝트 의존성 목록에도 늘 등장하니까요. 그런데 막상 “React가 뭐냐”고 물으면 “음… 프론트엔드 프레임워크?” 정도에서 말이 막히곤 합니다.

이 시리즈는 React의 핵심 개념을 웹 개발 경험에 빗대어 하나씩 짚어보려고 합니다. 이미 HTML·CSS·JavaScript로 무언가를 만들어본 사람을 전제로 하고, “기존 방식으로는 이런 게 불편했는데 React는 이렇게 풀더라”라는 식으로 이야기를 풀어갈 예정입니다. 그 첫걸음으로, 오늘은 React가 대체 무엇을 해결하려고 등장했는지부터 봅니다.


React는 ‘프레임워크’가 아니라 ‘라이브러리’다

먼저 사소해 보이지만 중요한 구분 하나. React는 스스로를 “사용자 인터페이스를 만들기 위한 라이브러리”라고 소개합니다. 프레임워크가 아니라 라이브러리라는 점이 핵심인데, 라우팅이나 데이터 관리 같은 것을 React가 다 정해주지 않는다는 뜻이기 때문입니다.

React가 책임지는 것은 딱 하나, 화면(View)을 그리는 일입니다. 주소에 따라 페이지를 바꾸는 일, 서버와 통신하는 일, 전역 상태를 관리하는 일은 별도의 라이브러리를 골라 조합합니다. 그래서 React는 가볍지만, 대신 “어떻게 조립할지”는 개발자의 몫으로 남습니다. 이 성격을 기억해두면 뒤에 나올 여러 선택지들이 왜 존재하는지 이해하기 쉬워집니다.


명령형에서 선언형으로

React를 이해하는 가장 빠른 길은 선언형(Declarative)이라는 단어를 붙잡는 것입니다. 이게 무슨 말인지는 기존 방식과 비교하면 선명해집니다.

예전 방식으로 “버튼을 누르면 숫자가 1 올라가는” 화면을 만든다고 해봅시다. 우리는 대략 이렇게 했습니다.

let count = 0;
const el = document.querySelector('#count');
button.addEventListener('click', () => {
  count += 1;
  el.textContent = count; // 화면을 '직접' 고쳐준다
});

여기서 개발자가 하는 일은 “값이 바뀌었으니 화면의 이 부분을 이렇게 고쳐라”라고 일일이 지시하는 것입니다. 이것이 명령형(Imperative)입니다. 화면이 복잡해질수록 “무엇이 바뀌면 어디를 고쳐야 하는가”의 경우의 수가 폭발하고, 여기서 대부분의 버그가 태어납니다.

React는 발상을 뒤집습니다. 개발자는 화면을 고치는 절차를 쓰는 대신, “이 상태일 때 화면은 이렇게 생겼다”만 선언합니다.

function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

count가 바뀌면 화면의 어디를 어떻게 고칠지는 React가 알아서 계산합니다. 개발자는 “상태가 이러면 결과는 이것”이라는 관계만 적어두면 됩니다. 요리에 비유하면, 명령형이 “냄비를 불에 올리고 3분 뒤 저어라”라는 조리 순서라면, 선언형은 “완성된 요리는 이런 모습이다”라는 완성 사진을 건네는 쪽에 가깝습니다.


컴포넌트 — 화면을 조립 가능한 단위로

두 번째 핵심은 컴포넌트(Component)입니다. 위 예시의 Counter처럼, React에서는 화면의 조각을 함수 하나로 정의합니다. 이 함수는 화면(정확히는 화면을 그리는 설계도)을 반환하고, 우리는 이 조각들을 레고 블록처럼 조합해 전체 화면을 만듭니다.

function App() {
  return (
    <div>
      <Header />
      <Counter />
      <Footer />
    </div>
  );
}

한 번 잘 만든 Counter를 여러 곳에서 재사용할 수 있고, 각 조각은 자기 안의 일만 책임지면 됩니다. 화면을 ‘하나의 거대한 문서’가 아니라 ‘작은 부품들의 조합’으로 바라보는 이 관점은, 사실 React뿐 아니라 Vue·Svelte 같은 현대 프레임워크가 공유하는 공통 언어이기도 합니다.


React는 어떻게 화면을 최소한으로만 고치는가

“상태가 바뀌면 화면을 다시 그린다”라고 하면, 매번 전체를 새로 그려서 느리지 않을까 하는 의문이 듭니다. 여기서 등장하는 것이 가상 DOM(Virtual DOM)입니다.

React는 실제 화면(DOM)을 곧바로 건드리지 않습니다. 대신 화면의 모습을 가벼운 자바스크립트 객체로 한 벌 들고 있다가, 상태가 바뀌면 바뀐 새 버전과 이전 버전을 비교해서 실제로 달라진 부분만 골라 진짜 DOM에 반영합니다. 이 비교 과정을 재조정(Reconciliation)이라고 부릅니다. 덕분에 우리는 “전체를 다시 선언”하는 편한 방식으로 코드를 쓰면서도, 실제 화면 갱신은 최소한으로 유지되는 이점을 함께 얻습니다.

이 원리를 지금 깊이 알 필요는 없습니다. 다만 “React는 내가 선언한 결과와 현재 화면의 차이를 스스로 계산해 메운다”는 감각만 가지고 있으면, 앞으로 나올 State나 렌더링 이야기가 훨씬 자연스럽게 이어집니다.


이 시리즈에서 다룰 것 — 그리고 React Native

정리하면 React의 발상은 세 문장입니다. 화면을 상태의 함수로 선언한다. 화면은 재사용 가능한 컴포넌트로 조립한다. 실제 갱신은 React가 알아서 최소화한다. 앞으로 이어질 글에서 JSX, Props, State, useEffect, Context 같은 개념을 하나씩 이 발상 위에 얹어갈 것입니다.

한 가지 미리 밝혀둘 것이 있습니다. 이 시리즈의 1부는 웹에서의 React를 다룹니다. 흔히 함께 묶여 언급되는 React Native는 방금 이야기한 React의 사고방식을 웹이 아니라 모바일 네이티브 화면으로 옮긴 것으로, 별도의 2부에서 다룰 예정입니다. React를 제대로 이해해두면 React Native는 그 위에 얹히는 이야기라 훨씬 수월해집니다. 그러니 우선은 이 선언형이라는 감각에 익숙해지는 것부터 시작해봅시다.