웹 개발/Front End

리액트 상태관리

L3m0n S0ju 2025. 5. 31. 20:44

 

 

 

 

 

 

리액트에서 상태관리를 할 떄는 여러 방법이 있지만 기본적으로 React Context API 가 있고 Redux, Recoil, Zustand, Jotai 정도가 있는데 React Context API와 Jotai만 간단하게 알아보겠습니다.

 

 

 

React Context API

기본적으로 React Context Api는 값이 거의 변경될 일이 없는 경우에 많이 사용합니다. 이유는 굳이 많은기능이 필요하지 않은 상황에서는 최대한 라이브러리는 지양하는 것이 좋기 때문입니다. 

'use client';
import * as React from 'react';
import { useQuery } from '@tanstack/react-query';
import { fetch } from '../../shared/lib/fetch/client';
export enum ProductType {
  BASIC = 'BASIC',
  PLUS = 'PLUS',
}

// Context의 값 타입 정의
interface ProductTypeContextValues {
  productType: ProductType | undefined;
  error: string | null;
}

interface Response<T> {
  status?: number;
  code?: number;
  message?: string;
  error?: boolean;
  errorMessage?: string;
  errorDescription?: string;
  data?: T;
}

interface CompanyInfoDto {
  productType: ProductType;
}

const fetchProductType = async () => {
  const response = await fetch(
    `/api/company-info/...`,
    {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
      },
    },
  );

  if (!response.ok) {
    const errorData = (await response.json()) as Response<void>;
    throw new Error(errorData.message);
  }

  const responseData =
    (await response.json()) as Response<CompanyInfoDto>;

  return responseData.data?.productType;
};

// Context 생성 (기본값 `undefined`로 설정)
const ProductTypeContext = React.createContext<
  ProductTypeContextValues | undefined
>(undefined);

export function ProductTypeProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  const { data: productType, error } = useQuery({
    queryKey: ['productType'],
    queryFn: fetchProductType,
  });

  const contextValues: ProductTypeContextValues = {
    productType,
    error: error ? error.message : null,
  };

  return (
    <ProductTypeContext.Provider value={contextValues}>
      {children}
    </ProductTypeContext.Provider>
  );
}

export const useProductType = (): ProductTypeContextValues => {
  const context = React.useContext(ProductTypeContext);

  if (!context) {
    throw new Error('useProductType must be used within a ProductTypeProvider');
  }

  return context;
};

 

위 코드는 Basic, Plus 중에 어떤 제품을 사용하는지 전역에서 사용할 수 있도록 하는 Context 코드입니다. 위에서 부터 순서대로 설명하면 제품의 Basic/Plus 값을 api로 가져와서 context 파라미터에 넣어서 Provider를 만듭니다.

 

 

 

 

const Layout = ({ children }: Props) => {
  return (
    <ProductTypeProvider>
      <div className="dop_container">{children}</div>
    </ProductTypeProvider>
  );
};

 

 

그래서 사용하는 쪽에서는 ProductTypeProvider안에 코드에서는 제공되는 context를 사용할 수 있습니다. Context 안에는 Provider 그리고 Consumer 개념이 있습니다. 

 

 

const context = React.useContext(ProductTypeContext);

 

Consumer은 전통적인 사용방식이 있었지만 현재는 대부분 위 코드처럼 useContext로 값을 가져오는 방식을 사용합니다.

 

 

const { productType } = useProductType();

 

자식 컴포넌트에서 사용할 때는 useProductType()을 해주면 바로 제품 타입을 가져올 수 있습니다.

 

 

 

Jotai

2번째로 Jotai입니다. 전역으로 상태를 관리해야할 값들이 많아지거나 많은 변경? 변화가 예상될때는 Jotai와 같은 상태관리 라이브러리를 사용하는 것이 좋습니다. 값이 많아지다보면 React Context Api로 관리하기에 복잡해질 수 있기 때문입니다.

 

'use client';

import * as React from 'react';
import * as Jotai from 'jotai';
import { formatDate } from '@dop-ui/react/shared/lib/date';
import { UserJobTenureDto } from './types';
const store = Jotai.createStore();

export function WorkStatusDateProvider({ children }: React.PropsWithChildren) {
  return <Jotai.Provider store={store}>{children}</Jotai.Provider>;
}

// 초기값
export const accrualDate = Jotai.atom<string>(
  formatDate({ date: new Date(), format: 'YYYY-MM-DD' }),
);
export const jobTenureDate = Jotai.atom<UserJobTenureDto>({
  joinDate: formatDate({ date: new Date(), format: 'YYYY-MM-DD' }), // 조회가능한 첫번째 날 (입사일 )
  resignationDate: formatDate({ date: new Date(), format: 'YYYY-MM-DD' }), // 조회 가능한 마지막날 ( 퇴사일 or 최대 3년)
});
export const clickDateAtom = Jotai.atom<string | null>(null);

 

 

사용법은 간단합니다. store를 생성하고 Provider로 제공하면 Provider 아래에 있는 값들은 모두 제공되는 변수들을 사용할 수 있습니다. 

 

 

 

const accrualDateAtom = useAtomValue(accrualDate);

 

사용할 때는 useAtomValue를 사용하면 됩니다.

 

 

 

const setDate = useSetAtom(accrualDate);
setDate(currentDate);

 

값을 입력할 때는 위코드와 같이 useSetAtom을 사용하여 함수를 하나 만들고 만든 함수에 파라미터를 넘겨주면 전역 변수를 수정할 수 있습니다.

 

 

 

지금까지 리액트에서 상태관리 하는 방법에 대해 알아봤습니다. 사용해보니 생각보다 간단한 것 같습니다.