더 이상 Redux가 필요 없는 이유

·2023년 4월 9일
0
post-thumbnail

Adhithi Ravichandram 의 글을 번역하여 작성한 글입니다. 많은 의역이 들어갔으므로 원문 Why You Don’t Need Redux Anymore?를 읽어보시는 것을 추천드립니다.


최근 몇 년간 많은 리액트 프로젝트들이 Redux를 써 왔다. 심지어 아래 이미지처럼, React 관련 구인 공고에서 Redux 경험을 요구하는 경우도 봤다.

(이미지)

위 구인 공고에서는 React.js / Redux 사용 경험을 요구 된다고 명시해 두었다. 두 개가 같은 게 아닌데, 왜 이 일을 하기 위해서 Redux 경험이 꼭 필요한 걸까? 나는 잘 모르겠지만... 뭐, 이게 이번 글의 핵심은 아니니 넘어가자.

핵심은 이 산업 자체에서 Redux를 React의 동의어처럼 쓴다는 거고, 당신이 React 코드를 짜기 위해서는 Redux를 알아야 한다는 거다. 사실이 아닌데도 말이다.

잠시 뒤로 물러나서, Redux가 무엇인지 그리고 어떤 문제를 해결하는 놈인지 찬찬히 알아보자.


Redux가 해결하는 문제들

2022년에 실시한 JS 실태 조사에 따르면, 응답자 중 43%(3위)가 State를 다루는 게 JavaScript에서 가장 어려운 것 중 하나라고 답 했다.

Redux 같은 라이브러리는 이런 특정 어려운 점을 해결하기 위해 사용된다.

react-redux는 매우 유명한 state 관리 라이브러리이다. 이는 당신의 애플리케이션의 전체 state를 관리하는 추가 저장 공간을 제공한다.

state란 뭘까?

state는 바뀔 수 있는 data이다.

React 컴포넌트는 store저장공간에서 데이터를 읽고, action을 dispatch하고 store의 데이터를 업데이트한다.

Redux의 store는 전체 애플리케이션의 현재 state를 포함한 진짜 소스들을 가지고 있다.

Redux는 사이즈가 큰 React 애플리케이션의 state 관리 문제를 덜어주지만, 많은 팀들은 실제 Redux가 필요해지기 전부터 Redux를 사용하기 시작한다. 이 때문에 애플리케이션의 사이즈가 (필요 없이) 커지기도 한다.


여전히 Redux가 필요할까?

지난 몇 년 간 React 생태계는 엄청나게 성장했고, 이에 여전히 Redux가 필요할까?하는 질문이 나오기 시작했다. 그리고 대부분의 경우, 이 질문에 대한 답은 아니오이다.

다른 대안들도 있기 때문에, 더이상 React에서 state를 관리하기 위해서 Redux를 꼭 써야할 필요는 없다.

Redux 같이 외부에서 state를 관리하는 라이브러리를 쓰기 전에 state를 관리할 수 있는 다른 방법들을 먼저 고려해 볼 수 있다. 여기에 React 앱만을 사용해서 state를 관리하는 몇 가지 단계를 정리해 보겠다.


1. React의 locate state로 시작하기

대부분의 경우에는 외부 라이브러리 없이, 리액트 자체의 state hook을 사용해도 리액트를 쓰기에 충분하다.
어떤 폼을 작성한다고 치면, 버튼 몇 개를 클릭하고, 당신이 보고 있는 페이지의 UI를 통해 소통하게 될 것이다. 이를 위해서 상태 관리 라이브러리가 필요하진 않다.

당신은 useState hook을 사용해 상태 관리를 할 수 있다.
다른 무엇보다도 local state를 사용하는 걸 첫 번째로 생각하자.

import { useState} from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>현재 {count}번 클릭했습니다</p>
	  <button onClick={() => setCount(count + 1)}>클릭해보세요!</button>
	</div>
  );
}

2. props 내려 보내기

local state로 시작했다면, 아마 이 state를 가져다 써야 하는 자식 컴포넌트들이 생겼을 것이다. 어떻게 하면 될까?
바로 지금이 prop으로 내려 보낼 때이다. props를 사용해서 자식 컴포넌트가 필요한 state들을 쭉쭉 내려보내주면 된다.

import { useState} from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>현재 {count}번 클릭했습니다</p>
	  <button onClick={() => setCount(count + 1)}>클릭해보세요!</button>
	  <CounterInfo count={count}/>
	</div>
  );
}

function CounterInfo({ count }) {
  return (
    <div>
      <p>현재 {count}번 클릭했어요</p>
	</div>
  );
}

위 예시에는 CounterCounteInfo라는 두 개의 컴포넌트가 등장한다. CounterInfo는 부모 컴포넌트인 Counter에서 count를 prop으로 받는다. 이는 리액트에서 상위/부모 컴포넌트가 하위/자식 컴포넌트에게 값을 내려주는 일반적인 방식이다.

이 방식은 간단한 케이스에서는 완벽하게 동작하지만, 애플리케이션이 커지게 되면 컴포넌트들끼리 엮어서 prop으로 값을 내려주는 방식이 매우 성가시고 번거로워진다. 이럴 때는 코드는 읽기에도, 관리하기에도 더 어려워진다.

(역주) 이럴 때 사용하는 것이 redux 같은 전역 상태 관리 툴이기도 하다 ^^

3. React Context

React Context는 리액트 컴포넌트 구조의 전역에서 데이터를 공유하기 위해 만들어졌다. 만약, 애플리케이션의 특정 state를 애플리케이션 트리 전체에서 사용할 필요가 있다면 React Context를 쓰면 된다.

예를 들어, 사용자 정보, 테마 정보, 언어 설정 등은 애플리케이션 전체에 적용되는, 전역에서 사용되는 데이터이다. React Context 는 이러한 전역 데이터를 저장하는데에 매우 유용하다. 이를 사용하면 몇 겹의 컴포넌트를 거쳐서 prop으로 전달하지 않아도 된다.

// 이 컴포넌트는 useContext Hook을 설명하기 위한 것입니다.
import React, { useContext } from "react";

const blogInfo = {
  React: {
    post: "Learn useContext Hooks",
    author: "Adhithi Ravichandran"
  }, 
  GraphQL: {
    post: "Learn GraphQL Mutations",
    author: "Adhithi Ravichandran"
  }
};

const blogInfoContext = React.createContext(blogInfo);

function ParentComponent() {
  return (
    <blogInfoContext.Provider value={blogInfo}>
      <h2></h2>
	  <BlogDetailComponent />
      <MyOtherBlogInfoComponent />
    </blogInfoContext.Provider>
  );
}

function BlogDetailComponent() {
  const blogDetails = useContext(blogInfoContext);
  
  return (
    <div>
      <h3>React Blog Details</h3>
	  <p>Topic: {blogDeatils.React.post}</p>
      <p>Author: {blogDeatils.React.author}</p>
    </div>
  );
}

function MyOtherBlogInfoComponent() {
  const blogDetails = useContext(blogInfoContext);
  
  return (
    <div>
      <h3>GraphQL Blog Details</h3>
	  <p>Topic: {blogDeatils.GraphQL.post}</p>
      <p>Author: {blogDeatils.GraphQL.author}</p>
    </div>
  );
}

export default ParentComponent;

Context API는 꽤 간단하다.
먼저 함수 createContext를 사용해서 state context를 만들고, providerconsumer를 반환한다. provider는 자식 컴포넌트가 사용할 state가 있는 컴포넌트 트리를 감싼다.
위 예시에서는, blogInfoContext를 만들었다. 이는 필요한 정보인 blogInfo(state)를 가지고 있다.


자식 컴포넌트 내에서, useContext 훅을 사용하면 필요한 state에 쉽게 접근할 수 있다.

Context API에 대한 더 많은 정보가 필요하다면 내가 전에 쓴 포스트를 참고하면 된다. React의 useContext hook을 배워보자


매번 Context를 찾을 필요 없다는 사실을 기억하자. 이는 정말, 꼭 필요할 때만 쓰면 된다.

Redux와 비교했을 때, Context API는 더 간편하고 덜 복잡하다.


4. React Query

자, 그동안 어떻게 React만 사용해서 애플리케이션의 state를 관리하는지 살펴봤다. local state와 prop 내리기, context를 활용해서 말이다.


React Qeury는 data fetching을 관리할 때 사용할 수 있는 라이브러리이다. 이는 서버 상태 라이브러리로, 서버와 클라이언트 사이에서 일어나는 비동기적 작업을 관리할 수 있게 도와준다.

Redux는 클라이언트 상태 라이브러리므로, React Query와는 다르다. React Query는 클라이언트 상태에서 캐시 데이터를 관리하는 데에 사용되는 보일러플레이트 코드(변경 없이 재사용하는 코드)와 관련된 연결 작업을 몇 줄의 코드로 대체해 준다.

대부분 React Query를 사용하게 되면 모든 비동기 코드를 통합하고 애플리케이션 내에 남아 있는 클라이언트 상태를 Redux와 같은 툴을 쓰지 않고도 쉽게 관리할 수 있다.

리액트 쿼리에 대한 글을 찾아보다, 카카오페이 프론트엔드 개발팀에서 작성한 글을 찾아 첨부합니다.
카카오페이 프론트엔드 개발자들이 React Query를 선택한 이유


React Query와 관련된 간단한 예시는 다음과 같다.

function Todos() {
  const { isLoading, isError, data, error } = useQuery('todos', fetchTodoList)
  
  if (isLoading) {
    return <span>Loading...</span>
  }
  
  if (isError) {
    return <span>Error: {error.message}</span>
  }
  
  // 여기까지 if문을 통과하면 isSuccess == true라는 걸 알 수 있다.
  return (
    <ul>
      {data.map(todo => (
         <li key={todo.id}>{todo.title}</li>
      ))}
	</ul>
  )
}

그 외 방법들

React Query의 대체 라이브러리로는 비슷한 기능을 가지고 있는 Vercel의 SWR이나 Apollo Client 등이 있다. Apollo Client 역시 local state를 관리하는 기능이 옵션으로 들어 있으므로, Apollo를 선택해 state를 함께 관리할 수도 있다.

마무리

이제 마무리할 시간이다. 즐겁게 읽었길 바란다.
아래에는 리액트를 쓰며 읽어 볼만 한 몇몇 글과 최근 내가 오픈한 Next.js 13 코스 링크이다.

Next.js 13 Fundamentals

Prop Drilling

(링크 하나는 안 돼서 제외했습니다)



영어 표현

  • be bloated
  • cumbersome 번거로운, 성가신
  • boilerplate 변경 없이 재사용한
profile
성장형 개발자

0개의 댓글