React 전역 데이터 다루기

Jeris·2023년 4월 26일
0

코드잇 부트캠프 0기

목록 보기
65/107

1. Context란?

전역 데이터(Global data)

  • Language처럼 프로젝트 전체적으로 사용하는 데이터
  • 전역 데이터를 다룰 때 props와 state만 사용하면 Prop Drilling 문제가 발생한다.

Prop Drilling

  • 드릴로 땅을 파듯이 상위 컴포넌트에서 하위 컴포넌트로 반복해서 Prop을 내려주는 상황

React Conext

  • React Context는 React 애플리케이션 전체에서 전역적으로 사용될 데이터를 관리하기 위한 기능입니다.
  • Context를 사용하면, 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하지 않아도 하위 컴포넌트에서 해당 데이터에 접근할 수 있습니다.
  • <Context.Provider /> 컴포넌트로 범위를 정해 줄 수 있습니다.

2. Context로 데이터 내려주기

// contexts/LocaleContext.js
import { createContext } from "react";

const LocaleContext = createContext();

export default LocaleContext;


// components/App.js
import LocaleContext from "../contexts/LocaleContext";
...
function App() {
  ...
  return (
  <LocaleContext.Provider value="ko">
    ...
  </LocaleContext.Provider>
  );
}


// components/ReviewList.js
import LocaleContext from "../contexts/LocaleContext";

function ReviewListItem({ item, onDelete, onEdit }) {
  const locale = useContext(LocaleContext);
  ...
  return ( 
    ...
    <p>현재 언어: {locale}</p>
    ...
  );
}
  • useContext()는 React Hook 중 하나로, React Context에서 값을 읽어오는 데 사용됩니다. Context 객체와 함께 사용되며, 해당 Context 객체가 제공하는 값을 리턴합니다.
  • createContext() 함수는 아규먼트로 Context가 제공할 기본 값을 받습니다.
  • <LocaleContext.Provider> 안의 컴포넌트에서는 어디서나 LocaleContext가 제공하는 값을 쓸 수 있습니다.
  • 공유할 데이터를 Provider의 value prop으로 내려줄 수 있습니다.
  • Provider는 태그로 감싸지만 Fragment처럼 아무 것도 렌더링하지 않습니다.

3. Context 값에 State 사용하기

  1. LocaleSelect.js 컴포넌트를 만듭니다.
// components/LocaleSelect.js
function LocaleSelect({ value, onChange }) {
  const handleChange = (e) => onChange(e.target.value);

  return (
    <select value={value} onChange={handleChange}>
      <option value="ko">한국어</option>
      <option value="en">English</option>
    </select>
  );
}

export default LocaleSelect;
  1. App 컴포넌트에 locale state를 만듭니다.
// components/App.js
function App() {
  const [locale, setLocale] = useState("ko");
  ...
  return (
    ...
    <LocaleSelect value={locale} onChange={setLocale} />
    ...
  );
}

4. Context 코드 분리하기

Locale Context에서 상태를 관리하는 것의 이점

  • State를 컴포넌트 외부에서 관리하여, 상태 변경에 따른 리렌더링이 불필요한 컴포넌트에서 발생하지 않습니다. 이는 성능 향상에 도움이 됩니다.
  • 상태를 컴포넌트 외부에서 관리하면, 다른 컴포넌트에서도 해당 상태에 접근할 수 있으므로, 코드의 중복을 줄일 수 있습니다.
  • 언어 설정과 관련된 상태는 애플리케이션 전역에서 사용되므로, 이를 하나의 중앙화된 장소에서 관리할 수 있어서 유지보수가 용이합니다.

Locale Context에서 state 관리하기

  1. locale state를 LocaleContext.js로 옮겨줍니다.
// contexts/LocaleContext.js
import { createContext, useState } from "react";

const LocaleContext = createContext();

export function LocaleProvider(defaultValue = "ko", { children }) {
  const [locale, setLocale] = useState(defaultValue);
  return (
    <LocaleContext.Provider value={{ locale, setLocale }}>
      {children}
    </LocaleContext.Provider>
  );
}

export default LocaleContext;
  1. locale 값을 전달하고 가져오기 위한 커스텀 hook을 만듭니다.
// contexts/LocaleContext.js
...
export function useLocale() {
  const context = useContext(LocaleContext);

  if (!context) {
    throw new Error("반드시 LocaleProvider 안에서 사용해야 합니다");
  }

  const { locale } = context;

  return locale;
}

export function useSetLocale() {
  const context = useContext(LocaleContext);

  if (!context) {
    throw new Error("반드시 LocaleProvider 안에서 사용해야 합니다");
  }

  const { setLocale } = context;

  return setLocale;
}
...
  1. 컴포넌트에서 관리하던 상태들을 커스텀 훅으로 전부 바꿔줍니다.

5. 다국어 기능 완성하기

  1. useTranslate.js 커스텀 훅을 만듭니다.
// hooks/useTranslate.js
import { useLocale } from '../contexts/LocaleContext';

const dict = {
  ko: {
    'confirm button': '확인',
    'cancel button': '취소',
    'edit button': '수정',
    'delete button': '삭제',
  },
  en: {
    'confirm button': 'OK',
    'cancel button': 'Cancel',
    'edit button': 'Edit',
    'delete button': 'Delete',
  },
};

function useTranslate() {
  const locale = useLocale();
  const translate = (key) => dict[locale][key] || '';
  return translate;
}

export default useTranslate;
  1. 컴포넌트에서 커스텀 훅을 사용합니다.
import useTranslate from "../hooks/useTranslate";
function component () {
  ...
  const t = useTranslate();
  ...
  return (
    ...
    <>{t("edit button")}</>
    <>{t("delete button")}</>
    <>{t("cancel button")}</>
    <>{t("confirm button")}</>
  );
}

6. 상태 관리의 짧은 역사

초기 버전의 React

  • 초기 버전의 React에서는 상태 관리를 위해 컴포넌트 내부에 state를 정의하고, 이를 업데이트하는 방식으로 처리했습니다.
  • 그러나 React 애플리케이션의 규모가 커질수록 컴포넌트 간의 데이터 흐름이 복잡해지고, 상태 관리가 어려워졌습니다. 이에 따라 React 생태계에서는 여러 가지 상태 관리 라이브러리가 등장하게 되었습니다.
  • 2013년, Flux 아키텍처가 처음으로 Facebook에서 발표되었습니다.

Flux

  • Flux는 단방향 데이터 흐름을 따르는 아키텍처 패턴으로, 상태를 변경하는 모든 액션(action)이 중앙 집중적으로 관리되는 스토어(store)에서 처리됩니다.
  • 이는 데이터가 어디에서 변경되었는지 추적하기 쉽고, 예측 가능한 데이터 흐름을 제공하여 React 애플리케이션의 복잡성을 줄입니다.
    • Dispatcher 애플리케이션 내에서 발생한 모든 액션(action)을 처리합니다. Dispatcher는 단 하나만 존재하며, 모든 액션을 처리하기 위한 등록된 콜백 함수를 갖고 있습니다.
    • Store 애플리케이션의 상태를 저장하고 관리합니다. Store는 Dispatcher로부터 전달받은 액션을 처리하여 상태를 업데이트합니다.
    • Action 애플리케이션에서 발생한 이벤트를 나타냅니다. 액션은 일반적으로 액션 타입과 데이터를 포함합니다.
    • View 상태를 표시하는 사용자 인터페이스입니다. View는 상태를 변경할 수 없으며, 상태 변경을 위해 액션을 디스패치합니다.
  • 2015년 Dan Abramov는 Flux를 단순화하고 개선한 Redux 라이브러리를 만들었습니다.

Redux

  • Redux는 React 애플리케이션에서 상태(state)를 관리하기 위한 JavaScript 라이브러리로, Flux 아키텍처를 기반으로 하며, 단방향 데이터 흐름을 따릅니다.
  • Redux는 간단한 디자인과 순수한 함수를 사용하여 Flux 구현의 복잡성을 줄이고 불필요한 코드들을 단순화하였습니다.
  • Redux는 다음과 같은 핵심 개념으로 이루어져 있습니다.
    • Store 애플리케이션의 상태를 저장하고 관리하는 객체입니다. Redux에서는 하나의 Store만 존재하며, 모든 상태가 Store에 저장됩니다.
    • Action 상태를 변경하는 객체입니다. 액션은 일반적으로 타입(type)과 데이터(payload)를 포함합니다.
    • Reducer 액션을 받아서 상태를 업데이트하는 함수입니다. Reducer는 이전 상태와 액션을 입력받아 새로운 상태를 반환합니다.
    • Dispatch 액션을 Store에 전달하는 함수입니다. Dispatch를 호출하면 액션이 Reducer에 전달되어 상태가 업데이트됩니다.
  • Redux는 Flux와 비슷한 아키텍처를 사용하지만, 상태 변경을 위해 불변성(immutability)을 강조하고, 컴포넌트와 스토어 간의 연결을 위해 컨테이너(component)를 도입하는 등의 차이가 있습니다.

React Context API

  • React Context API는 기존의 있던 내장 Context 기능을 업데이트하여 2018년 React 16.3에서 새롭게 도입되었습니다.
  • useContext Hook을 사용하여 값에 대한 참조를 가져올 수 있게 되었습니다.
  • 여러 개의 Context 객체를 중첩해서 사용할 수 있게 되었습니다.
  • React Context API는 Redux와 함께 React 상태 관리에 많이 사용되었습니다.
  • 하지만 Redux는 Store에 모든 상태를 저장하기 때문에 데이터 하나를 추가할 때마다 무의미한 Redux 코드를 작성해야 했고, 비동기를 처리를 하려면 코드가 복잡해지고 가독성이 떨어지는 문제가 있었습니다.
  • 그래서 데이터의 출처로 Client-state와 Server-state를 분리하려는 시도가 생겼습니다.

React Query

  • 2020년에 Tanner Linsley가 React Table의 개발 경험에 영감을 받아 React Query 라이브러리를 개발했습니다.
  • React 애플리케이션에서 서버에서 가져온 데이터를 관리하는 라이브러리입니다.
  • Client-state와 Server-state를 분리하지는 않지만, 클라이언트 측에서 데이터 캐싱 및 자동 업데이트를 처리하므로 클라이언트 상태 관리에 유용한 라이브러리입니다.
  • React Query는 다음과 같은 특징을 가지고 있습니다.
    • Data caching React Query는 데이터를 캐싱하여, 동일한 요청이 반복될 때 서버에서 데이터를 다시 가져오지 않고 캐시된 데이터를 사용합니다. 이를 통해 네트워크 대역폭을 절약하고, 더 빠른 데이터 요청을 처리할 수 있습니다.
    • Automatic data updates React Query는 캐싱된 데이터를 주기적으로 업데이트하여, 최신 데이터를 유지합니다. 이를 통해 UI를 업데이트하는데 필요한 데이터가 항상 최신 상태인 것을 보장할 수 있습니다.
    • Error handling React Query는 네트워크 오류나 서버 오류 등의 오류를 처리하고, 오류 발생 시 UI를 업데이트합니다. 이를 통해 사용자가 오류를 인지하고, 적절한 대처를 할 수 있습니다.
    • Ease of use React Query는 간단한 구현과 사용성을 가지고 있으며, 다른 상태 관리 라이브러리와 함께 사용할 수 있습니다.

SWR

  • SWR은 2020년에 Zeit(지트) 팀에서 개발한 React Hooks 기반의 React 라이브러리입니다.
  • Zeit 팀은 Next.js와 같은 프레임워크와 서비스를 제공하고 있는 회사로, SWR은 Zeit 팀에서 개발한 Next.js의 핵심 라이브러리 중 하나입니다.
  • SWR은 데이터 요청과 관리를 효율적으로 처리하기 위해 개발되었습니다.
  • SWR은 Next.js와 함께 사용할 수 있으며, React Native, Vue.js 등 다양한 프레임워크에서도 사용할 수 있습니다.
  • SWR은 다음과 같은 특징을 가지고 있습니다.
    • Data caching, Automatic data updates, Error handling
    • Server-side Rendering SWR은 서버 사이드 렌더링을 지원합니다. 이를 통해 초기 로딩 속도를 개선하고, SEO에 유리한 페이지를 생성할 수 있습니다.
  • 이후에는 MobX, Recoil, Zustand 등의 다양한 상태 관리 라이브러리가 등장하면서, React 애플리케이션의 상태 관리 방식이 다양화되었습니다.

Recoil

  • Recoil은 2020년 페이스북이 개발한 React 상태 관리 라이브러리입니다.
  • Recoil은 상태를 작은 조각으로 분할하고, 상태 간의 의존성을 처리하는 방식으로 상태 관리를 간편하게 처리할 수 있도록 도와줍니다.
  • Recoil은 다음과 같은 특징을 가지고 있습니다.
    • Atomic state management Recoil은 상태를 작은 조각으로 분할하여 원자적으로 관리합니다. 이를 통해 상태 관리를 간단하게 처리할 수 있습니다.
    • Asynchronous data handling Recoil은 비동기 데이터를 처리하기 위한 기능을 제공합니다. 이를 통해 비동기 데이터를 간편하게 처리할 수 있습니다.
    • State selectors Recoil은 상태 선택자를 제공하여, 상태를 읽고 변경하는 방법을 편리하게 처리할 수 있습니다.
    • TypeScript support Recoil은 TypeScript를 지원하며, 타입 안정성을 보장합니다.

Feedback

  • 상태 관리 section은 더 보충해서 작성하자.
  • 상태 관리 라이브러리를 더 자세하게 찾아보자.
  • Language 외에 전역 데이터로 관리해야할 데이터는 무엇이 있을까?

Reference

profile
job's done

0개의 댓글