프론트엔드

[개발지식]"Redux가 실제로 작동하는 방식과 의도하는 방식을 구별해야 한다"

tlsghwn 2025. 2. 13. 11:52

[개발지식] "Redux가 실제로 작동하는 방식과 의도하는 방식을 구별해야 한다"

 

들어가며

리액트에서 useState 훅을 사용해서 상태값을 관리했는데 새로고침하면 초기값으로 돌아갔다.
새로고침시에도 바뀐 상태값을 유지시키고 싶어 useState 초기값에 함수로직을 작성해봤다.
검색해보니 이걸 '함수형 초기값' 또는 '레이지 초기값(lazy initial state)'라 함.

const state값유지함수 = () => {
    // 로직 작성
    return ~~ ;
  };

  const [state, setState] = useState(state값유지함수);

//혹은

const [state, setState] = useState(() => {
    return ~~ ;
});

이런식으로...

나는 개인플젝을 만들며 함수형 초기값, redux로 상태관리를 시도해봤는데 redux가 아주 유용했다. (당연함)


그래서 소개해본다 이미 다 알고있는 리덕스!

 



Redux

React와 함께 사용되는 상태 관리 라이브러리

 

리액트는 컴포넌트 기반으로 UI를 만들어 나가는데, 컴포넌트는 서로 간에 데이터를 주고 받아야 한다.
이 때 컴포넌트 간의 데이터 흐름이 복잡해지면서 관리가 어려워질 수 있다.
이 문제를 해결하기 위해 리덕스는 전역 상태 관리 를 제공한다.

아래 그림처럼 애플리케이션의 상태를 중앙에서 관리하고, 상태 변경에 대한 모든 로직을 한 곳에서 처리하기 때문에 개발자들 사이에서 매우 인기있는 상태 관리 라이브러리이다.

그 근거로 npm trends 에서 확인한 최근 5년간 상태관리 라이브러리 다운로드 추세를 비교한 그래프를 가져왔음

보면 redux 사용률이 압도적으로 높은 걸 볼 수 있다.

사용률이 높은것과는 반대로 리덕스 사용자들의 만족도는 낮다는 것을
the software house (소프트웨어 개발사인데 자료가 신뢰할만한것인지 확실하지 않아서 링크만 걸어둠)에서 확인해볼수있다.
리덕스의 단점들을 보완, 리액트 환경에 최적화되어 나온게 Recoil. 다음에 건드려보겠다.

 


프론트엔드 데이터흐름 방식의 역사

  • mvc 🔜 flux 🔜 redux 순으로 진화(?)해옴

바야흐로... 양방향 MVC 패턴

Model : 데이터를 읽고 저장하며, 데이터의 상태를 관리
View : 사용자 인터페이스(UI)를 담당, 데이터를 시각적으로 표현
Controller : 사용자 입력과 이벤트 처리

 

ex) 사용자가 웹페이지에서 "로그인" 버튼을 클릭하면
Controller는 "로그인" 클릭 이벤트를 감지하고 이벤트 작업을 이해
Model은 사용자의 로그인 정보를 확인하고, db와 통신하여 인증 처리
View는 사용자에게 로그인 완료 및 실패 메세지를 표시

만약 프로젝트 규모가 커지면?
View와 Model의 수가 증가하게 되면서 무수히 많은 양방한 통신이 발생하게 된다.
때문에 데이터의 흐름을 파악하기 어려워지고, 많은 버그의 원인이 된다.

 

Flux의 탄생

Flux는 기존 MVC패턴의 단점을 보완하기 위해 페이스북에서 발표한 아키텍처이다.

양방향인 MVC와 달리 Flux는 단방향으로 데이터가 흐른다.
Action이 발생하면 Dispatcher에 의해 변경된 데이터가 Store에 저장되고 해당 데이터를 보여주는 View가 변경되는 흐름이다.
데이터 변화가 단반향으로 흐르기 때문에 흐름을 파악하기 쉽고 예측 가능 하여 버그를 줄일 수 있다.

 

📣 "예측 가능"
개발을 위협하는 눈에 보이지 않는 복잡성을 낮춰 작성한 코드가 어떤 결과를 가져올지 예측한다.

Flux의 탄생과 구성요소에 대해 재밌게 설명하는 안내서가 있습니다.
Flux를 더 깊게 이해하고 싶다면 읽어보세요! Flux로의 카툰 안내서

 

Redux 등장!

Redux는 Flux보다 조금 더 단순화되어 사용이 간편해진 Flux 구현체이다.
React + Flux 에 Reducer 를 결합한 Redux가 탄생한다.
Redu(cer) + (Fl)ux = Redux

 



 

리덕스를 효율적으로 쓰는 방향 ?

 

"Redux가 실제로 작동하는 방식과
Redux가 의도하는 방식을 구별해야 한다"

출처

 

 

"작동하는 방식"은 리덕스의 내부 메커니즘 및 동작 방식을 설명하고,
"의도하는 방식"은 리덕스의 설계 원칙과 목표를 나타내는 것으로 보이는데

이 두 측면을 이해하면 리덕스를 효율적으로 사용할 수 있지 않을까?

 

 


Redux 작동방식

우선 Redux는 Store라는 단일 데이터 저장소를 갖는다.

상태를 변화시키는 유일한 방법은 action을 발생시키는 것.
상태의 변경은 action을 전달받은 Reducer만 할 수 있다.
Reducer는 action과 이전상태를 전달받아 새로운 상태를 반환하는 역할을 한다.

뭐가 다른지 비유하자면
액션은 발생한 사건을 나타내고
리듀서는 사건에 대한 처리 방법을 정의한다.
ex) 액션은 편지나 메시지와 유사, 리듀서는 편지를 받고 어떻게 대응할지를 결정하는 사람
만약 편지가 "생일 축하 메시지"라면, 사람은 생일 축하 파티를 준비할 것이고, "공과금 청구서"라면 지불 방법을 고려할 것.

✅ Reducer 2가지 특징

  • 리듀서는 반드시 이전의 상태와 action을 매개변수(입력값)로 받는다
  • 리듀서는 이전상태를 변경하는 것이 아닌, 새로운 상태를 생성해서 반환한다.

 

앱에서 어떤 일이 발생하면

  • Store(저장소)는 Reducer를 실행하고 발생한 상황에 따라 상태가 업데이트된다.
  • Store는 상태가 변경되었음을 UI에게 알린다.

 

새로운 상태에 따라 UI가 재렌더링 되는 구조이다.

 

 


Redux 의도방식

Redux의 주요 목표는 상태 관리를 예측 가능하고 중앙 집중화된 방식으로 처리하는 것

  • 불변성: Redux는 상태 변경을 불변성을 유지하면서 처리한다. 이것은 상태 변경을 추적하고 예측 가능하게 만드는 데 도움이 된다. 이 내용은 아래 불변성을 유지하는 이유에서 이어진다
  • 예측 가능성: 상태가 변경될 때마다 새로운 상태 객체가 생성되는데, 이전 상태와 새로운 상태가 서로 다르기 때문에 상태 변경이 예측 가능하게 되는것이다. 이렇게 하면 상태 변경을 쉽게 추적할 수 있으며, 디버깅이나 테스트 시에도 도움이 된다.
  • 중앙 집중화: Redux는 상태를 단 하나의 스토어에 집중 저장하므로 애플리케이션의 상태를 일관되게 관리할 수 있다.
  • 확장성: Redux는 중대형 애플리케이션에서도 사용할 수 있으며, 여러 컴포넌트 및 모듈 간에 상태를 공유하기 쉽다.
 
 

Redux 불변성을 유지하는 이유

불변성(Immutanility)이란?
React, Redux, js 등에서 등장 하는 개념. 극단적으로 얘기하자면 현재의 변수들을 유지하는 것이 아니라 지속적으로 새로운 값들을 만들어내고 이전 값을 대체하는 것을 의미.

 

Redux가 새로운 상태를 반환하는 방법
Redux는 기존의 상태 객체와 return 되는 상태객체를 비교하여 return 되는 상태 객체가 새로운 객체일 경우 이를 인지하여 return 되는 상태값을 추적하여 변경시킨다.
이게 새로운 상태값을 반환하는 방법이자 불변성을 유지해야하는 이유이다.

 

🙋‍♀️ 만약 새로운 객체가 아닌 기존의 상태값을 참조하게 되면요?
Redux에서는 기존의 상태와 차이를 인식할 수 없기 때문에 변경된 값이 적용되지 못하고 기존의 값을 유지하게 된다.

불변성을 유지함으로써
다른 코드나 컴포넌트에서 의도치 않게 상태가 수정되는 것을 방지할 수 있다.
이로써 데이터의 무결성을 보장하며, React와 같은 UI 라이브러리에서 변경된 부분만 다시 렌더링하는 최적화 작업도 간편하게 수행할 수 있다!

 
 

Redux를 사용하는 이유

state를 props 전송없이 공유

useState는 다른 컴포넌트에서 사용시 props 를 통해 데이터값을 전송해야한다.
하나의 상위 컴포넌트에서 state(데이터) 변경을 하면 이 상태에 의존하는 다른 모든 구성요소에 전파되면서 관계가 굉장히 복잡해진다.

여러 컴포넌트를 거쳐 props를 전달하는 것은 매우 비효율적이며, 불필요한 props의 수만 늘어나게 된다.

이는 리액트에서 단반향 데이터 흐름을 구현할 때 발생하는 props drilling(과도한 prop 전달) 이슈도 해결할 수 있게 된다.

 

useState() / Redux 비교
                                                                                                                                              
  useState  Redux 
저장소 state Store
상태변화 setState Reducer
상태 값 변경 setState에 담긴 인자 값 dispatch에 담긴 payload에 따라

렌더링 최적화

리액트에서 렌더링 조건

  • 상태값이 변경되었을 때
  • props의 값이 변경되었을 때
  • 상위컴포넌트가 렌더링 될 때. 등

특정 컴포넌트에서 상태변화가 일어나면 하위 컴포넌트에서도 자동으로 리렌더링(로딩시간이 더 길어짐) 이 이루어진다.

리덕스는 상태값을 Store 객체 내부에 담아 관리한다고 말했다.
이 Store에 저장되어 있는 값이 변경되면 해당 상태값을 보여주는 컴포넌트로 바로 전달된다.
이 흐름을 따르면 기존의 props를 전달하는 작업은 생략하게 되는 것.

즉, 하위컴포넌트에서 불필요한 리렌더링은 일어나지 않는다.

 

 


 

그래서 Redux 무조건 써야할까?

리덕스를 사용하기 전에 현재 프로젝트에 리덕스의 사용이 적절한지 고민해봐야할 것이다

사례를 들어
페이스북의 경우, 애플리케이션의 서비스와 규모가 커지고 사용자와 인터렉션이 많아지면서 방대한 데이터흐름을 MVC패턴으로 유지하기가 힘들었다. 그 배경으로 flux와 redux를 사용하게 된 것인데 페이스북처럼 개발규모가 크다면 Redux를 사용하기에 적절하다고 생각된다.

하지만 상태관리에 있어 복잡성이 높지 않다면 반드시 리덕스를 사용할 필요는 전혀 없어보인다.
불필요한 상태에서 라이브러리를 사용하는것은 애플케이션 번들 사이즈만 증가시킬 것이다.

만약, React로 단반향 데이터 흐름을 구현하면서 prop drilling 이슈가 생기거나 디버깅에 어려움이 생긴다면 그때 Redux를 사용해보자!
🫡

 

 

참조

 

Idiomatic Redux: The Tao of Redux, Part 1 - Implementation and Intent

Thoughts on what Redux requires, how Redux is intended to be used, and what is possible with Redux

blog.isquaredsoftware.com