인턴

Jotai

choijming21 2025. 3. 25. 14:55

Jotai란?

  • React 상태관리 라이브러리 중 하나로 비교적 매우 간단하고 작은 단위로 전역 상태 관리가 가능
  • 매우 작은 3kb의 번들 사이즈와 타입스크립트에 지향적
  • Jotai는 React.js, Next.js, React Native에서 사용이 가능

 

 

jotai의 기본 사용법

기본적으로 Jotai는 React 내의 state, useState와 유사한 모양이기 때문에 쉽게 입문할 수 있습니다. 전역 관리 atom을 생성하고, 생성된 atom을 컴포넌트 내에 불러와 사용한다고 생각하면 됩니다.

 

 

1. atom

: atom을 생성하고 괄호 안에 초기값을 넣을 수 있습니다.

const counter = atom(0);

 

 

2. useAtom(read/write)

: 생성된 atom을 불러와서 useState와 동일하게 사용이 가능합니다.

const [count, setCount] = useAtom(counter);

 

 

3. useSetAtom(write)

: 생성된 atom의 값을 update만 할 때 사용합니다.

const counterWriteOnly = useSetAtom(counter);

 

 

4. useAtomValue(read)

: 생성된 atom의 값을 read만 할 때 사용합니다.

const counterReadOnly = useAtomValue(counter);

 

useSetAtom, useAtomValue는 각각 읽기, 쓰기만 가져와서 사용하기 때문에 useAtom과 다르게 재랜더링 하지 않는 장점이 있습니다.

 

 

< 실습 > 

// store/counterAtom.tsx
import { atom } from "jotai";

export const counter = atom(0);


// components/CounterControl.tsx
import { counter } from "@/store/counterAtom";
import { useAtom } from "jotai";
import * as S from "./counterControl.style";

const CounterControl = () => {
  const [count, setCount] = useAtom(counter);

  const handlePlus = () => {
    setCount((prev) => prev + 1);
  };

  const handleMinus = () => {
    setCount((prev) => prev - 1);
  };

  const handleReset = () => {
    setCount(0);
  };

  return (
    <S.MainContainer>
      <h1>{count}</h1>
      <S.ButtonContainer>
        <S.Button onClick={handleMinus}>-</S.Button>

        <S.Button onClick={handleReset}>reset</S.Button>
        <S.Button onClick={handlePlus}>+</S.Button>
      </S.ButtonContainer>
    </S.MainContainer>
  );
};

export default CounterControl;


// components/CounterDisplay.tsx
import { counter } from "@/store/counterAtom";
import { useAtomValue, useSetAtom } from "jotai";
import * as S from "./counterControl.style";

const CounterDisplay = () => {
  const counterReadOnly = useAtomValue(counter);

  const counterWriteOnly = useSetAtom(counter);

  const handleTenPlus = () => {
    counterWriteOnly((prev) => prev + 10);
  };

  const handleTenMinus = () => {
    counterWriteOnly((prev) => prev - 10);
  };

  return (
    <S.MainContainer>
      <h1>{counterReadOnly}</h1>

      <S.ButtonContainer>
        <S.Button onClick={handleTenMinus}>- 10</S.Button>
        <S.Button onClick={handleTenPlus}>+ 10</S.Button>
      </S.ButtonContainer>
    </S.MainContainer>
  );
};

export default CounterDisplay;


// pages/index.tsx
import Head from "next/head";
import Image from "next/image";
import { Geist, Geist_Mono } from "next/font/google";
import styles from "@/styles/Home.module.css";
import CounterControl from "@/components/CounterControl";
import CounterDisplay from "@/components/CounterDisplay";

export default function Home() {
  return (
    <>
      <Head>
    // ....
      </Head>
      <div
        className={`${styles.page} ${geistSans.variable} ${geistMono.variable}`}
      >
        <main className={styles.main}>
          <CounterControl />
          <CounterDisplay />
        </main>
        <footer className={styles.footer}>
         // ...
        </footer>
      </div>
    </>
  );
}