Why is React declarative?

aken·2025년 2월 28일
1
post-thumbnail

React를 공부하면 한 번쯤은 "React는 선언적이다."를 들어본 적 있으실 겁니다.
왜 React는 선언적이라고 말하는걸까요?
이유를 찾기 전에 선언형 프로그래밍이 무엇인지 살펴봅시다.

선언형 vs 명령형 프로그래밍

declarative(선언형)이랑 같이 언급되는 imperative(명령형)이 있습니다. 이 둘은 서로 반대되는 개념으로 설명하는데요.

선언형 프로그래밍은 두 가지 뜻으로 통용되고 있다.
한 정의에 따르면, 프로그램이 어떤 방법으로 해야 하는지를 나타내기보다 무엇과 같은지를 설명하는 경우에 "선언형"이라고 한다.
또 다른 정의에 따르면, 프로그램이 함수형 프로그래밍 언어, 논리형 프로그래밍 언어, 혹은 제한형 프로그래밍 언어로 쓰인 경우에 "선언형"이라고 한다. - wiki

명령형 프로그래밍은 선언형 프로그래밍과 반대되는 개념으로, 프로그래밍의 상태와 상태를 변경시키는 구문의 관점에서 연산을 설명하는 프로그래밍 패러다임의 일종이다.
자연 언어에서의 명령법이 어떤 동작을 할 것인지를 명령으로 표현하듯이, 명령형 프로그램은 컴퓨터가 수행할 명령들을 순서대로 써 놓은 것이다. - wiki

즉, 명령형은 어떻게 해야 할 지(how to do)를, 선언형은 무엇을 할 지(what to do)를 나타냅니다.

저는 개념이 추상적으로 느껴져서 확 와닿지 않았습니다. 그래서, 예시를 보면서 둘의 차이를 이해하려 했습니다.

stackoverflow에서 '집사가 저녁 식사로 치킨을 만든다'를 가정으로 들었는데요.

명령형 세계에서는 집사에게 치킨을 만드는 방법을 단계별로 알려줘야 합니다.

1. 주방으로 간다.
2. 냉장고를 연다.
3. 냉장고에서 닭고기를 꺼낸다.

...

?. 음식을 테이블 위에 놓는다.

반면, 선언형 세계에서는 먹고싶은 음식만 말하면 됩니다.

저녁으로 치킨 먹고 싶다.

만약 집사가 치킨 만드는 방법을 모른다면, 치킨을 만들 수 없습니다.
집사가 치킨 만드는 방법을 알고 있기에 "저녁으로 치킨 먹고 싶다."를 말할 수 있던 겁니다.

선언형 세계에서도 집사가 주방으로 가서 냉장고를 열고 닭고기를 꺼내야 합니다. 다만, 치킨 만드는 방법(절차적인 단계)이 집사에게 추상화되어 있어 저희는 신경 쓰지 않아도 됩니다.

즉, 선언형 프로그래밍에서 명령형 방식의 how가 추상화되어 있습니다. 그럼, what를 기술하면 내부적으로 알아서 처리됩니다.

만약, 배열에 있는 요소들 중 짝수만 필터링 하는 함수를 JS로 어떻게 구현하실 건가요?
저는 크게 2가지가 떠오르는데요.

첫 번째로, for문을 이용한 방식입니다.

function getEvens(arr) {
  const evens = [];

  for (let i = 0; i < arr.length; i++) {
    if (arr[i] % 2 === 0) evens.push(arr[i]);
  }

  return evens;
}
  1. 반복문을 돌면서,
  2. 요소가 짝수이면 evens 배열에 요소를 push하고 있습니다.

어떻게 짝수인 요소만 필터링하는지 코드에 명시되어있습니다.
하지만, 한 눈에 어떤 값을 반환하는 건지 파악하기 힘드네요.

두 번째로, 배열의 filter 메서드를 이용하는 방식입니다.

function getEvens(arr) {
  return arr.filter((value) => value % 2 === 0);
}

filter 내부적으로 필터링하는 작업이 추상화되어 있어 어떤 요소를 얻고 싶은지 조건만 알려주면 자동으로 필터링됩니다.
for문보다 가독성이 좋아져서 어떤 결과를 반환하는지 쉽게 예측할 수 있습니다.

React is declarative

공식 문서에서 "React는 UI를 선언적인 방식으로 조작합니다."라고 나와있습니다.

React provides a declarative way to manipulate the UI.

만약 HTML과 JS로 카운터를 구현한다면,

<h1 id="count">0</h1>
<button id="increment">+</button>
<button id="decrement">-</button>

<script>
  let count = 0;
  const countElement = document.getElementById("count");
  document.getElementById("increment").addEventListener("click", () => {
    count++;
    countElement.textContent = count;
  });
  document.getElementById("decrement").addEventListener("click", () => {
    count--;
    countElement.textContent = count;
  });
</script>
  1. 직접 DOM에 접근하고 (getElementById)
  2. 이벤트 리스너를 등록하고 (addEventListener)
  3. 상태가 변경될 때마다 UI를 직접 업데이트합니다. (countElement.textContent = count)

즉, 어떻게 UI를 업데이트하는지 하나하나 지시해야 합니다.
여기서 UI 또는 핸들러를 추가한다면, 건드려야 할 부분을 찾기 위해 모든 코드를 유심히 살펴봐야 합니다. React에서는 이런 걱정을 하지 않아도 되는데요!

React로 구현한다면,

import { useState } from "react";

export default function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>+</button>
      <button onClick={() => setCount(count - 1)}>-</button>
    </div>
  );
}

DOM을 직접 조작하는 대신, 무엇을 보여주고 싶은지에 집중하면 됩니다.
React 내부에 DOM 업데이트 과정이 추상화되어있기 때문에 무엇을 보여주고 싶은지 선언만 하면, React가 이를 해석하고 자동으로 DOM을 변경합니다.
이런 이유로 "React is declarative."하다고 말합니다.

선언형 프로그래밍이 React 개발자에게 어떤 이점을 줄까?

React가 선언적(declarative)인 특징 덕분에 개발자는 UI를 직접 조작할 필요 없이 무엇을 보여주고 싶은지에 집중하면 된다고 했는데요.
따라서, 디버깅하기 쉬워지고 유지보수하기도 좋습니다.

만약 React가 없다면 getElementById와 같은 메서드로 DOM 찾고, addEventListener에서 상태가 변경되는지 또는 UI 업데이트하는지 일일이 확인해야 했네요 🤪

참고

Difference between declarative and imperative in React.js? - stackoverflow

2개의 댓글

comment-user-thumbnail
2025년 4월 7일

리액트를 통해 HTML을 어떻게 효율적으로 그릴 수 있는지에 대한 공부를 하다가 글을 참고하게 되었는데, 많은 도움이 됐습니다! 글 잘 봤습니다 :)

1개의 답글