본문 바로가기

Frontend

프론트엔드에서 상태란?

웹 프론트엔드에서 핵심적으로 논의되는 주제인 상태는 무엇이며 왜 관리해야하는 대상인지 그리고 관리하는 방법에는 어떤 것들이 있는지 알아봅니다.

프로그래밍에서 상태란?

프로그래밍에서 상태에 대해 이야기해보기 전에 저의 현재 상태에 대해 이야기해보겠습니다.

저는 현재 의자에 앉아서 책상에 올려져있는 뜨거운 커피를 마시고 있습니다.

현재 저의 상태는 앉아 있다는 것이고 커피의 상태는 뜨거운 상태입니다. 몇시간이 지났습니다.

저는 운동을 하러 나갔고 책상에 있던 커피는 식었습니다.

이제 제 상태는 운동 중인 상태이고 책상에 있던 커피는 차가워진 상태입니다. 이것이 현실 세계에서의 상태입니다.

현실 세계에서의 상태와 비슷하게 프로그래밍에서의 상태는 데이터로 표현합니다. 컴퓨터 프로그램에서 상태란 시간이 지남에 따라서 변할 수 있는 데이터입니다.

간단한 카운터 프로그램을 만들어서 시간이 지남에 따라 카운터 변수의 변화를 지켜보도록 하겠습니다. 아래 코드를 브라우저 콘솔 창에 입력한 후에 실행해보세요.

 

let counter = 0;


const increment = () => {
 counter = counter + 1;
};


const print = () => {
 console.log(counter);
};


setInterval(() => {
 increment();
 print();
}, 1000);

 

이 프로그램에서 counter는 상태라고 할 수 있습니다. 처음에 counter 변수는 0이라는 상태를 가지고 있다가 counter의 상태가 1씩 증가합니다. 이렇게 시간이 지남에 따라서 변경될 수 있는 데이터를 상태라고 합니다. 따라서 상태는 문자열, 객체, 배열등의 다양한 데이터 타입이 될 수 있습니다.

프로그래밍에서 상태란 주어진 시간에 대해 시스템을 표현한 것입니다. 상태는 애플리케이션에 존재하는 언제든지 변경될 수 있는 데이터(문자열, 불린값, 배열, 객체등)라고 할 수 있습니다.

웹 프론트엔드에서 상태란?

프론트엔드에서 상태란 웹 애플리케이션을 구성하는 데이터라고 할 수 있습니다. 이러한 데이터는 웹 애플리케이션의 사용자에 의해 시간에 따라서 생성되고, 읽히고, 수정되고, 삭제될 수 있습니다.(CRUD)

자바스크립트로 웹 애플리케이션을 만든다고 생각해보겠습니다. 웹 애플리케이션은 도메인 로직과 관련된 상태, 브라우저의 History API와 관련된 네비게이션 상태, 컴포넌트들의 상태등으로 구성됩니다. 이런 상태 자체는 프로그래밍 언어의 데이터로 표현되므로 상태를 사용자에게 보여주기 위해서는 브라우저의 DOM에 표현해야합니다.

자바스크립트에서는 상태를 DOM을 조작하여 아래와 같이 표현할 수 있습니다.

 

let count = 1;

const box = document.getElementById('box');
box.innerText = count; 

 

사용자의 행동으로 인해(ex: 버튼 클릭) count 상태가 1 증가하면 UI에도 마찬가지로 count의 상태가 1 증가해서 보여져야 합니다.

이런 경우에는 updateCount라는 함수를 만들어서 해결할 수 있습니다.

 

const updateCount = () => {
    count = count + 1
      box.innerText = count
}

 

updateCount는 사용자의 행위를 입력으로 받아서 count를 증가시키고 box의 UI에 반영합니다. updateCount함수가 두가지 역할(count 증가, UI에 반영)을 하므로 리팩토링해보겠습니다.

 

const updateCount = () => {
    count = count + 1
      updateBoxUI(count)
}

const updateBoxUI = (count) => {
      box.innerText = count    
}

 

위와 같이 상태를 업데이트하는 함수와 UI를 업데이트하는 함수를 분리할 수 있습니다. 하지만 그럼에도 불구하고 상태를 업데이트하는 하는 함수에서 UI를 업데이트하는 함수를 호출할 수 밖에 없습니다.

정리해보면 사용자와 웹 페이지의 인터랙션으로 인해 이벤트가 발생하고 이는 상태를 업데이트합니다. 그런데 상태를 업데이트할 때 UI도 업데이트해줘야 하기 때문에 연쇄적인 함수 호출이 발생합니다. 이게 MVC 방식으로 구성된 애플리케이션의 문제입니다.(Cascading Effects) 이런 방식의 문제점은 상태 업데이트가 얽히거나 상태를 업데이트하는 코드와 UI를 업데이트하는 코드를 각각 분리시켜서 테스트하기 어렵게 만들 뿐만 아니라 코드를 관리하기 어렵게 만듭니다.

Frontend Library to Rescue

상태가 업데이트되면 화면에 반영해야합니다. 상태가 업데이트 될 때마다 개발자가 직접 DOM을 업데이트하는 방식이 문제를 가지고 있기 때문에 많은 웹 프론트엔드 라이브러리 및 프레임워크들은 DOM을 업데이트하는 방식을 해결했습니다.

개발자가 상태를 업데이트하면 프론트엔드 라이브러리가 상태 업데이트를 감지하고 이를 UI에 반영시킵니다. 다시 말하면, 프론트엔드 라이브러리의 역할은 상태가 업데이트되면 이를 DOM 노드에 반영시키는 것입니다. 따라서 개발자는 DOM 노드를 업데이트하는 부분은 신경쓰지 않고 상태를 업데이트하는 부분에 집중할 수 있습니다.

따라서 대다수의 프론트엔드 라이브러리들은 명확한 책임을 가지고 있습니다.

개발자가 직접 DOM을 조작(Manipulation)하는 것은 어려우니 개발자는 상태를 업데이트하는 코드를 작성하고 상태가 업데이트되면 UI를 업데이트하는 부분은 프론트엔드 라이브러리가 담당한다.

상태 관리(in React)

프로그램을 만들 때 데이터가 많아지면 데이터를 개발자가 다루기 쉽게 관리해야 합니다. 웹 애플리케이션에서도 마찬가지로 애플리케이션이 커지면 커질수록 상태는 많아지고 관리하기 어려워집니다.

웹에서의 상태는 시간에 따라서 변하고 시간에 따라 변하는 상태는 UI에 직접적으로 영향을 미치기 때문에 개발자가 애플리케이션을 제어하려면 상태를 예측 할 수 있어야합니다.

상태를 예측 가능하게 만들기 위한 상태 관리 방법에는 다양한 방법이 존재합니다.

 

상태의 변경이 아무곳에서나 일어나면 상태의 변경을 관리하기 어려워질 수 있으므로 상태의 업데이트가 한 곳에서 처리하기도 하고 상태를 직접 변경하거나 상태를 간접적으로 변경할 수도 있습니다.(Event)

위 그림에서 왼쪽 방식은 직접적인(Direct) 방식을 사용하여 상태를 직접 수정하는 방식이고 대표적인 예로 Recoil, Jotai가 있습니다.

오른쪽 방식은 간접적인(Indirect) 방식을 사용하여 상태를 간접적으로 수정하는 방식이고 대표적인 예로 Redux, Zustand가 있습니다.

직접 또는 간접적으로 상태를 업데이트하는 API는 리액트에도 존재합니다. 직접 상태를 업데이트하는 API는 useState를 사용하는 것이고 간접적으로 상태를 업데이트하는 것은 useReducer를 사용해서 이벤트를 dispatch하는 것입니다.

리액트에서 상태를 관리할 수 있는 라이브리리 또는 내장 API는 다양합니다. 이들은 상태의 특성에 따라 어떤 방법을 사용할지 구분할 수 있습니다.

 

 

 

위와 같이 상태의 특성에 따라 사용할 API를 정할 수는 있지만 결국 상태 관리의 목표는 시간에 따라서 변화하는 상태를 좀 더 효율적으로 관리함에 있습니다. 그리고 이러한 효율성은 개발자의 생산성으로 이어집니다. 상태 관리 방법에 정답이 있다면 정답은 개발자가 상태를 예측하고 다루기 편한 방법일 것입니다.