본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 9. · 16 Views
Jotai 시작하기 - 설치부터 첫 실행까지
React 상태 관리의 새로운 패러다임, Jotai를 소개합니다. 원자 단위의 상태 관리로 복잡한 전역 상태를 간단하게 다루는 방법을 배워보세요. 설치부터 첫 예제 실행까지 초급 개발자도 쉽게 따라할 수 있도록 안내합니다.
목차
1. Jotai란 무엇인가
김개발 씨는 React로 쇼핑몰 프로젝트를 진행하던 중 고민에 빠졌습니다. 전역 상태를 관리하려니 보일러플레이트 코드가 너무 많았습니다.
Redux는 배우기 어렵고, Context API는 리렌더링이 걱정됩니다.
Jotai는 React를 위한 원자 기반 상태 관리 라이브러리입니다. 마치 레고 블록처럼 작은 상태 조각(Atom)을 조립해서 큰 상태를 만듭니다.
불필요한 리렌더링을 최소화하면서도 간결한 코드로 전역 상태를 관리할 수 있습니다.
다음 코드를 살펴봅시다.
import { atom, useAtom } from 'jotai'
// 상태를 원자(Atom) 단위로 정의합니다
const countAtom = atom(0)
function Counter() {
// useState처럼 간단하게 사용합니다
const [count, setCount] = useAtom(countAtom)
return (
<div>
<h1>카운트: {count}</h1>
<button onClick={() => setCount(count + 1)}>
증가
</button>
</div>
)
}
김개발 씨는 입사 3개월 차 주니어 개발자입니다. 오늘도 열심히 React 프로젝트를 진행하던 중, 여러 컴포넌트에서 공유해야 하는 사용자 정보 때문에 고민에 빠졌습니다.
Props를 여러 단계로 전달하자니 코드가 복잡해지고, Redux를 도입하자니 러닝커브가 만만치 않았습니다. 선배 개발자 박시니어 씨가 다가와 코드를 살펴봅니다.
"요즘에는 Jotai라는 라이브러리도 많이 쓰는데, 한번 써볼래요? 정말 간단해요." 그렇다면 Jotai란 정확히 무엇일까요?
쉽게 비유하자면, Jotai는 마치 원자 단위로 물질을 다루는 화학과 같습니다. 큰 분자를 만들 때 작은 원자들을 조합하듯이, Jotai에서는 작은 상태 조각(Atom)들을 조합해서 복잡한 애플리케이션 상태를 만듭니다.
각 원자는 독립적이면서도 필요할 때 다른 원자와 결합할 수 있습니다. Jotai라는 이름 자체가 일본어로 '상태'를 뜻하는 '状態(じょうたい)'에서 유래했습니다.
개발자들이 React의 상태 관리를 더 쉽고 직관적으로 할 수 있도록 만들어진 라이브러리입니다. 전역 상태 관리의 고민 React로 개발하다 보면 필연적으로 전역 상태 관리 문제에 직면하게 됩니다.
로그인한 사용자 정보, 장바구니 데이터, 테마 설정 등은 여러 컴포넌트에서 공유해야 합니다. 기존에는 이런 문제를 해결하려면 Redux를 배워야 했습니다.
하지만 Redux는 action, reducer, store를 모두 설정해야 해서 초보자에게는 진입 장벽이 높았습니다. 간단한 카운터 하나 만드는데도 파일을 여러 개 만들어야 했습니다.
Context API를 쓰면 어떨까요? 간단해 보이지만 Provider 안의 모든 컴포넌트가 상태 변경 시 리렌더링될 수 있다는 성능 문제가 있었습니다.
Jotai의 핵심 철학 바로 이런 문제를 해결하기 위해 Jotai가 등장했습니다. Jotai의 핵심 철학은 **"최소한의 API로 최대한의 유연성을"**입니다.
atom()으로 상태를 만들고, useAtom()으로 사용하는 것이 전부입니다. useState의 전역 버전이라고 생각하면 됩니다.
또한 원자적 업데이트가 가능합니다. 특정 atom을 구독하는 컴포넌트만 리렌더링되므로 성능이 뛰어납니다.
Redux처럼 하나의 거대한 스토어를 관리하는 대신, 필요한 만큼 작은 atom들을 만들어 조합합니다. 코드로 보는 Jotai의 매력 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 atom(0)으로 초기값이 0인 상태를 만듭니다. 이것이 바로 원자, 즉 Atom입니다.
다음으로 컴포넌트 안에서 useAtom(countAtom)을 호출하면 useState와 똑같은 형태로 [값, 설정함수]를 받습니다. 이후에는 일반적인 React 코드와 동일하게 사용하면 됩니다.
Provider 설정도, 복잡한 설정 파일도 필요 없습니다. 그냥 atom을 만들고 useAtom으로 쓰면 끝입니다.
언제 Jotai를 선택해야 할까 실제 현업에서는 어떻게 활용할까요? 예를 들어 쇼핑몰 서비스를 개발한다고 가정해봅시다.
사용자 정보, 장바구니, 상품 필터 등 여러 전역 상태가 필요합니다. Jotai를 사용하면 각각을 독립적인 atom으로 만들고, 필요한 컴포넌트에서만 구독할 수 있습니다.
장바구니가 변경되어도 사용자 정보를 보여주는 컴포넌트는 리렌더링되지 않습니다. 특히 TypeScript와의 궁합이 좋습니다.
atom의 타입이 자동으로 추론되어 별도의 타입 정의가 거의 필요 없습니다. 학습 곡선의 완만함 초보 개발자가 걱정하는 것 중 하나가 바로 러닝커브입니다.
Jotai는 useState를 쓸 줄 안다면 바로 사용할 수 있습니다. 새로운 개념이나 패러다임을 배울 필요가 없습니다.
공식 문서도 친절하고, 예제가 풍부합니다. 정리 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "와, 정말 간단하네요!" Jotai는 React 상태 관리의 새로운 가능성을 보여줍니다.
간결하면서도 강력하고, 배우기 쉬우면서도 실전에서 유용합니다. 여러분도 오늘부터 Jotai를 시작해 보세요.
실전 팁
💡 - useState를 전역으로 쓴다고 생각하면 이해가 쉽습니다
- 작은 프로젝트부터 시작해서 점진적으로 확장해보세요
- TypeScript를 사용하면 타입 안정성까지 얻을 수 있습니다
2. 왜 만들어졌는가
박시니어 씨가 커피를 마시며 이야기를 이어갑니다. "Jotai가 왜 만들어졌는지 알면 더 깊이 이해할 수 있어요.
Redux나 Zustand를 만든 것과 같은 개발자가 만들었답니다."
Jotai는 Recoil의 영향을 받아 더 간단하고 React답게 만든 라이브러리입니다. Zustand를 만든 Daishi Kato가 개발했으며, 보일러플레이트를 최소화하고 React의 Concurrent Mode와 완벽하게 호환되도록 설계되었습니다.
다음 코드를 살펴봅시다.
// Recoil 스타일의 영향을 받았지만 더 간결합니다
import { atom, useAtom } from 'jotai'
// atom 정의가 매우 간단합니다
const textAtom = atom('hello')
const uppercaseAtom = atom(
(get) => get(textAtom).toUpperCase()
)
function TextDisplay() {
// 파생 상태도 동일한 방식으로 사용합니다
const [uppercase] = useAtom(uppercaseAtom)
return <h1>{uppercase}</h1> // HELLO
}
김개발 씨가 궁금해하며 물었습니다. "그런데 이미 Redux도 있고 Recoil도 있는데, 왜 Jotai를 새로 만들었을까요?" 박시니어 씨가 웃으며 대답합니다.
"좋은 질문이에요. 사실 이 이야기를 알면 Jotai를 더 깊이 이해할 수 있어요." React 생태계의 상태 관리 여정 React가 처음 등장했을 때는 상태 관리가 간단했습니다.
컴포넌트 안에서 setState를 쓰면 됐으니까요. 하지만 애플리케이션이 커지면서 문제가 생겼습니다.
전역 상태가 필요해졌고, 개발자들은 Flux 패턴을 고안했습니다. 그리고 Redux가 등장했습니다.
2015년부터 2020년까지 Redux는 사실상 표준이었습니다. 하지만 개발자들은 불만이 있었습니다.
"왜 이렇게 복잡하지? 단순히 숫자 하나 저장하는데 파일을 세 개나 만들어야 해?" Recoil의 등장과 새로운 패러다임 2020년 Facebook(현 Meta)이 Recoil을 공개했습니다.
완전히 다른 접근이었습니다. 하나의 거대한 스토어 대신 원자(Atom) 단위로 상태를 관리하자는 것이었습니다.
개발자들은 환호했습니다. "이거야!
이게 진정한 React 스타일이야!" useState처럼 간단하면서도 전역 상태를 관리할 수 있었습니다. 하지만 문제가 있었습니다.
Recoil은 아직 실험 단계였고, API가 자주 변경되었습니다. atom을 정의할 때 고유한 key 문자열을 써야 하는 것도 번거로웠습니다.
Daishi Kato의 도전 바로 이때 Daishi Kato라는 개발자가 나타났습니다. 그는 이미 Zustand라는 간단한 상태 관리 라이브러리를 만든 경험이 있었습니다.
"Recoil의 원자 개념은 훌륭해. 하지만 더 간단하게 만들 수 있어.
key도 필요 없고, Provider도 선택적으로 만들 수 있어." 그렇게 2020년 말, Jotai가 세상에 나왔습니다. 일본어로 '상태'를 뜻하는 이름답게, 상태 관리의 본질에 집중한 라이브러리였습니다.
Jotai가 해결하려는 문제 Jotai는 세 가지 핵심 문제를 해결하려고 만들어졌습니다. 첫째, 보일러플레이트 최소화입니다.
Redux처럼 action, reducer를 만들 필요가 없습니다. Recoil처럼 key를 쓸 필요도 없습니다.
atom을 만들고 useAtom으로 쓰면 끝입니다. 둘째, React 철학과의 일치입니다.
React는 컴포넌트 중심, 선언적 UI를 지향합니다. Jotai도 마찬가지입니다.
atom은 React 컴포넌트처럼 조합 가능하고, useAtom은 useState처럼 자연스럽습니다. 셋째, 미래 지향성입니다.
React 18의 Concurrent Mode, Suspense와 완벽하게 작동합니다. 비동기 상태 관리도 자연스럽습니다.
코드로 보는 철학 위의 코드를 보면 Jotai의 철학이 드러납니다. uppercaseAtom은 파생 상태(derived state)입니다.
다른 atom의 값을 읽어서 계산합니다. Recoil의 selector와 비슷하지만 더 간단합니다.
key도 없고, 그냥 함수만 전달하면 됩니다. get 함수로 다른 atom을 읽습니다.
의존성 추적이 자동으로 됩니다. textAtom이 변경되면 uppercaseAtom을 구독하는 컴포넌트만 리렌더링됩니다.
커뮤니티의 반응 Jotai가 공개되자 React 커뮤니티는 열광했습니다. "드디어 나왔다!
Recoil의 장점에 Zustand의 간결함을 더한 라이브러리!" GitHub 스타가 빠르게 증가했고, 많은 기업에서 도입하기 시작했습니다. Vercel, Shopify 같은 유명 기업들도 프로젝트에 사용했습니다.
특히 Next.js와의 궁합이 좋아서 SSR 프로젝트에서도 인기를 끌었습니다. 계속되는 발전 Daishi Kato는 지금도 Jotai를 꾸준히 개선하고 있습니다.
atom effects, atom families, 디버깅 도구 등 유용한 기능들이 계속 추가되고 있습니다. 하지만 핵심 API는 변하지 않습니다.
간결함이 Jotai의 정체성이기 때문입니다. 정리 김개발 씨가 감탄하며 말했습니다.
"와, 이런 철학으로 만들어졌군요. 그래서 이렇게 간단한 거였네요!" Jotai는 단순히 새로운 라이브러리가 아닙니다.
React 상태 관리의 진화 과정에서 나온 결과물입니다. 선배 라이브러리들의 장점을 배우고 단점을 개선한 현대적인 해결책입니다.
실전 팁
💡 - Jotai는 Recoil보다 가볍고 간단합니다(번들 크기 약 3KB)
- Zustand와 함께 사용할 수도 있습니다(상황에 따라 선택)
- React Suspense와 함께 사용하면 로딩 처리가 매우 쉬워집니다
3. 다른 라이브러리와의 비교
김개발 씨가 노트북을 열며 물었습니다. "그럼 Redux, Zustand, Recoil 중에서 언제 Jotai를 써야 하나요?" 박시니어 씨가 화이트보드를 가리키며 설명을 시작합니다.
Jotai는 Redux보다 간결하고, Zustand보다 세밀하며, Recoil보다 가벼운 특징을 가집니다. 각 라이브러리는 서로 다른 철학과 용도를 가지고 있으며, 프로젝트 규모와 팀의 선호도에 따라 선택하면 됩니다.
다음 코드를 살펴봅시다.
// Redux: 액션과 리듀서가 필요합니다
// const INCREMENT = 'INCREMENT'
// function counterReducer(state = 0, action) { ... }
// Zustand: 스토어를 만들어야 합니다
// const useStore = create((set) => ({ count: 0, inc: () => set(...) }))
// Recoil: key가 필요합니다
// const countState = atom({ key: 'count', default: 0 })
// Jotai: 가장 간결합니다
import { atom, useAtom } from 'jotai'
const countAtom = atom(0)
function Counter() {
const [count, setCount] = useAtom(countAtom)
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
김개발 씨는 선택의 기로에 섰습니다. 팀 회의에서 상태 관리 라이브러리를 결정해야 했습니다.
"다들 다르다고 하는데, 정확히 뭐가 다른 거죠?" 박시니어 씨가 경험담을 들려줍니다. "저도 처음에는 헷갈렸어요.
각 라이브러리를 실제로 써보고 나서야 차이를 알게 됐죠." Redux: 검증된 거인 Redux는 가장 오래되고 검증된 라이브러리입니다. 마치 대형 백화점과 같습니다.
필요한 것은 다 있지만, 물건 하나 사려고 해도 여러 층을 거쳐야 합니다. action을 정의하고, reducer를 만들고, store에 연결하고, Provider로 감싸야 합니다.
장점은 명확합니다. 상태 변경 과정이 예측 가능하고, 디버깅 도구가 훌륭하며, 미들웨어 생태계가 풍부합니다.
대규모 프로젝트에서 여러 개발자가 협업할 때 강력합니다. 하지만 단점도 분명합니다.
학습 곡선이 가파르고, 보일러플레이트가 많으며, 간단한 상태 하나 추가하는데도 여러 파일을 수정해야 합니다. Zustand: 미니멀리스트 Zustand는 Jotai와 같은 개발자가 만든 형제 라이브러리입니다.
마치 편의점과 같습니다. 필요한 것만 빠르게 가져갈 수 있습니다.
스토어를 만들고 바로 사용하면 됩니다. Provider도 필요 없고, Context API처럼 간단합니다.
장점은 극도의 단순함입니다. 번들 크기가 작고, 러닝커브가 거의 없으며, React 외부에서도 사용할 수 있습니다.
하지만 중앙 집중식 스토어 방식입니다. 하나의 큰 스토어에 모든 상태를 담습니다.
상태가 많아지면 관리가 복잡해질 수 있습니다. Recoil: Facebook의 실험 Recoil은 Facebook이 만든 공식적인 라이브러리입니다.
마치 모듈형 가구와 같습니다. 필요한 부품을 조립해서 원하는 형태를 만듭니다.
atom을 조합해서 복잡한 상태 구조를 만들 수 있습니다. 장점은 강력한 기능입니다.
원자 단위 업데이트, selector로 복잡한 파생 상태, Suspense 완벽 지원, 시간 여행 디버깅이 가능합니다. 하지만 아직 실험적 단계입니다.
API가 변경될 수 있고, 각 atom마다 고유한 key 문자열을 관리해야 하며, 번들 크기가 상대적으로 큽니다. Jotai: 균형잡힌 선택 그렇다면 Jotai는 어떤 위치일까요?
Jotai는 마치 스마트한 자판기와 같습니다. 편의점처럼 간단하면서도, 필요한 기능은 다 있습니다.
Recoil의 원자 개념을 가져오되, key 없이 더 간결하게 만들었습니다. 구체적인 비교 코드를 보면 차이가 명확합니다.
Redux는 action과 reducer를 정의해야 합니다. Zustand는 create 함수로 스토어를 만듭니다.
Recoil은 atom에 key를 써야 합니다. Jotai는 그냥 atom(0)이면 끝입니다.
번들 크기도 중요합니다. Redux는 약 14KB, Recoil은 약 15KB, Zustand는 약 1KB, Jotai는 약 3KB입니다.
Jotai는 작으면서도 기능이 풍부합니다. 언제 무엇을 선택할까 실제 프로젝트에서는 어떻게 선택해야 할까요?
Redux를 선택하는 경우: 대규모 팀 프로젝트, 상태 변경 추적이 중요한 경우, 이미 Redux 경험이 많은 팀, 풍부한 미들웨어가 필요한 경우입니다. Zustand를 선택하는 경우: 작은 프로젝트, 빠른 프로토타이핑, 최소한의 설정만 원하는 경우, React 외부에서도 상태를 쓰고 싶은 경우입니다.
Recoil을 선택하는 경우: Facebook 생태계를 쓰는 경우, 복잡한 파생 상태가 많은 경우, 실험적 기능을 써볼 의향이 있는 경우입니다. Jotai를 선택하는 경우: Recoil의 간결한 대안을 원하는 경우, TypeScript를 쓰는 경우, 원자 단위 관리가 필요한 중소규모 프로젝트, Next.js와 함께 쓰는 경우입니다.
실전 사례 박시니어 씨가 경험담을 들려줍니다. "우리 팀은 처음에 Redux를 썼어요.
하지만 간단한 UI 상태 하나 추가할 때마다 파일 세 개를 수정하는 게 피곤했죠. 그래서 Zustand로 갈아탔는데, 프로젝트가 커지니 스토어 하나가 너무 비대해졌어요." "결국 Jotai로 정착했습니다.
필요한 만큼 atom을 만들고, 관련 있는 것끼리 조합하니 코드가 깔끔해졌어요. 무엇보다 팀원들이 금방 배워서 쓸 수 있었죠." 성능 비교 리렌더링 최적화 측면에서도 차이가 있습니다.
Redux는 연결된 컴포넌트가 많으면 최적화가 어렵습니다. Context API는 Provider 하위 전체가 리렌더링될 수 있습니다.
Zustand는 selector로 최적화해야 합니다. Jotai는 atom 구독 방식이라 자동으로 최적화됩니다.
특정 atom을 쓰는 컴포넌트만 리렌더링됩니다. 정리 김개발 씨가 정리하듯 말했습니다.
"그러니까 Redux는 대기업용, Zustand는 스타트업용, Jotai는 밸런스형이네요!" 박시니어 씨가 웃으며 답했습니다. "그렇게 생각해도 좋아요.
정답은 없어요. 프로젝트와 팀에 맞는 걸 선택하면 됩니다." 중요한 것은 각 라이브러리의 철학을 이해하고, 프로젝트 요구사항에 맞게 선택하는 것입니다.
실전 팁
💡 - 작은 프로젝트라면 Zustand, 중간 규모는 Jotai, 대규모는 Redux를 고려하세요
- 이미 팀이 익숙한 라이브러리가 있다면 그것을 쓰는 것도 좋은 선택입니다
- Jotai와 Zustand를 함께 써도 됩니다(글로벌 설정은 Zustand, 컴포넌트 상태는 Jotai)
4. 설치 방법
김개발 씨가 손을 걷어붙이며 말했습니다. "이제 직접 해보고 싶어요!" 박시니어 씨가 터미널을 열며 설치 과정을 보여주기 시작합니다.
Jotai 설치는 npm, yarn, pnpm 중 원하는 패키지 매니저로 할 수 있습니다. React 16.8 이상이 필요하며, TypeScript를 사용하면 별도의 타입 정의 없이도 완벽한 타입 지원을 받을 수 있습니다.
다음 코드를 살펴봅시다.
# npm을 사용하는 경우
npm install jotai
# yarn을 사용하는 경우
yarn add jotai
# pnpm을 사용하는 경우 (Next.js 프로젝트에서 많이 사용)
pnpm add jotai
# 설치 확인 - package.json을 열어보세요
# "dependencies": {
# "jotai": "^2.6.0",
# "react": "^18.2.0"
# }
김개발 씨는 새 프로젝트 폴더를 만들었습니다. 이제 Jotai를 설치할 차례입니다.
설치 과정은 다른 npm 패키지와 똑같이 간단합니다. 박시니어 씨가 먼저 확인합니다.
"React 프로젝트가 준비됐죠? 없다면 먼저 Create React App이나 Vite로 프로젝트를 만들어야 해요." 사전 준비 사항 Jotai를 설치하기 전에 확인해야 할 것들이 있습니다.
첫째, React 버전입니다. Jotai는 React Hooks를 사용하므로 React 16.8 이상이 필요합니다.
요즘 프로젝트라면 대부분 18 버전을 쓰므로 문제없을 것입니다. 둘째, Node.js 버전입니다.
최신 LTS 버전(18 이상)을 권장합니다. 터미널에서 node --version으로 확인할 수 있습니다.
셋째, 패키지 매니저입니다. npm, yarn, pnpm 중 하나를 선택하면 됩니다.
프로젝트에서 이미 사용 중인 것을 그대로 쓰면 됩니다. React 프로젝트 생성 아직 프로젝트가 없다면 먼저 만들어야 합니다.
가장 빠른 방법은 Vite를 사용하는 것입니다. Vite는 빠른 개발 서버와 빌드를 제공합니다.
터미널에서 다음 명령어를 실행하세요. bash npm create vite@latest my-jotai-app -- --template react cd my-jotai-app npm install Next.js를 사용한다면 이렇게 시작합니다.
bash npx create-next-app@latest my-jotai-app cd my-jotai-app 기존 프로젝트가 있다면 그 폴더로 이동하면 됩니다. Jotai 설치하기 이제 본격적으로 Jotai를 설치할 차례입니다.
터미널에서 프로젝트 폴더로 이동한 후 위의 명령어 중 하나를 실행하면 됩니다. npm을 쓴다면 npm install jotai, yarn이면 yarn add jotai, pnpm이면 pnpm add jotai입니다.
설치에는 보통 몇 초밖에 걸리지 않습니다. Jotai는 매우 가벼운 라이브러리이기 때문입니다.
설치 확인 설치가 제대로 됐는지 확인해 봅시다. 프로젝트 폴더의 package.json 파일을 열어보세요.
dependencies 항목에 jotai가 추가되어 있어야 합니다. 버전은 2.x 이상이면 최신 버전입니다.
또 다른 확인 방법은 간단한 import를 테스트하는 것입니다. 아무 파일에서나 import { atom } from 'jotai'를 작성했을 때 에러가 나지 않으면 성공입니다.
TypeScript 설정 (선택사항) TypeScript를 사용한다면 추가 설정이 필요할까요? 좋은 소식이 있습니다.
Jotai는 TypeScript로 작성되었고, 타입 정의가 패키지에 포함되어 있습니다. @types/jotai 같은 별도 패키지를 설치할 필요가 없습니다.
그냥 사용하면 타입이 자동으로 추론됩니다. atom(0)을 만들면 TypeScript는 이것이 number 타입이라는 것을 알아냅니다.
개발 도구 설치 (추천) Jotai를 더 편하게 쓰려면 개발 도구도 설치하는 것이 좋습니다. bash npm install jotai-devtools --save-dev 이 도구를 쓰면 브라우저 개발자 도구에서 atom 값을 실시간으로 볼 수 있습니다.
Redux DevTools처럼 상태 변경을 추적할 수 있어서 디버깅이 편해집니다. 추가 유틸리티 (선택사항) Jotai는 확장 라이브러리도 제공합니다.
localStorage와 연동하려면 jotai/utils, Immer와 함께 쓰려면 jotai/immer, React Query와 함께 쓰려면 jotai/query를 설치할 수 있습니다. 하지만 처음에는 핵심 jotai만 설치하고 시작하는 것이 좋습니다.
일반적인 설치 오류 가끔 설치 중 문제가 생길 수 있습니다. "peer dependency 경고"가 나타날 수 있습니다.
React 버전이 요구사항에 맞지 않는다는 뜻입니다. React를 최신 버전으로 업데이트하면 해결됩니다.
네트워크 오류가 나면 npm 캐시를 지우고 다시 시도해 보세요. npm cache clean --force 명령어를 사용합니다.
프로젝트 구조 세팅 설치가 끝났다면 프로젝트 구조를 정리해 봅시다. atoms 폴더를 만들어서 atom 정의들을 관리하는 것이 좋습니다.
예를 들어 src/atoms/userAtom.js 같은 식으로 만듭니다. src/ atoms/ userAtom.js cartAtom.js components/ Header.jsx Cart.jsx App.jsx 이렇게 하면 어디에 어떤 상태가 있는지 쉽게 파악할 수 있습니다.
정리 김개발 씨가 package.json을 확인하며 미소지었습니다. "생각보다 정말 간단하네요!" 박시니어 씨가 답했습니다.
"그렇죠? 이제 진짜 재밌는 부분이 시작됩니다.
첫 번째 코드를 작성해 볼까요?" Jotai 설치는 정말 간단합니다. 복잡한 설정 파일도, 추가 도구도 필요 없습니다.
패키지 하나만 설치하면 바로 사용할 수 있습니다.
실전 팁
💡 - Vite를 사용하면 개발 서버가 매우 빠릅니다
- jotai-devtools를 설치하면 디버깅이 편해집니다
- atoms 폴더를 만들어서 상태를 체계적으로 관리하세요
5. 첫 번째 예제 실행하기
드디어 코드를 작성할 시간입니다. 김개발 씨가 에디터를 열고 손가락을 움직입니다.
"가장 간단한 카운터부터 만들어볼까요?" 박시니어 씨가 옆에서 단계별로 안내합니다.
Jotai의 첫 예제는 카운터 앱으로 시작하는 것이 좋습니다. atom으로 상태를 정의하고, useAtom으로 읽고 쓰는 기본 패턴을 익힐 수 있습니다.
useState와 거의 동일한 사용법이라 학습 곡선이 완만합니다.
다음 코드를 살펴봅시다.
// src/atoms/counterAtom.js
import { atom } from 'jotai'
// 초기값 0인 atom을 생성합니다
export const counterAtom = atom(0)
// src/App.jsx
import { useAtom } from 'jotai'
import { counterAtom } from './atoms/counterAtom'
function App() {
// useState와 똑같은 방식으로 사용합니다
const [count, setCount] = useAtom(counterAtom)
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h1>카운트: {count}</h1>
<button onClick={() => setCount(count + 1)}>증가</button>
<button onClick={() => setCount(count - 1)}>감소</button>
<button onClick={() => setCount(0)}>리셋</button>
</div>
)
}
export default App
김개발 씨가 에디터를 열고 새 파일을 만들었습니다. 첫 번째 Jotai 코드를 작성할 시간입니다.
손끝에서 약간의 긴장감이 느껴집니다. 박시니어 씨가 진심 어린 조언을 합니다.
"긴장 풀어요. useState 쓸 줄 알면 이미 90% 아는 거예요." 파일 구조 만들기 먼저 atoms 폴더를 만듭니다.
src 폴더 안에 atoms라는 새 폴더를 생성하고, 그 안에 counterAtom.js 파일을 만듭니다. 이 파일에 첫 번째 atom을 정의할 것입니다.
왜 별도 파일로 분리할까요? atom은 여러 컴포넌트에서 공유되는 전역 상태입니다.
한 곳에서 정의하고 여러 곳에서 import해서 쓰는 것이 관리하기 좋습니다. 첫 번째 Atom 만들기 counterAtom.js 파일을 열고 코드를 작성합니다.
import { atom } from 'jotai'로 atom 함수를 가져옵니다. 그리고 atom(0)으로 초기값이 0인 상태를 만듭니다.
정말 간단하죠? 이 atom은 이제 애플리케이션 어디서든 사용할 수 있는 전역 상태가 됩니다.
Provider로 감쌀 필요도 없고, Context를 만들 필요도 없습니다. 컴포넌트에서 사용하기 이제 App.jsx를 열어봅시다.
먼저 useAtom을 import합니다. 그리고 방금 만든 counterAtom도 import합니다.
컴포넌트 안에서 useAtom(counterAtom)을 호출하면 [값, 설정함수] 배열을 받습니다. 이 부분이 핵심입니다.
useState와 정확히 똑같은 방식입니다. const [count, setCount] = useState(0) 대신 const [count, setCount] = useAtom(counterAtom)을 쓰는 것뿐입니다.
버튼에 기능 연결하기 이제 버튼을 만들어 봅시다. 증가 버튼은 onClick={() => setCount(count + 1)}로 현재 값에 1을 더합니다.
감소 버튼은 1을 빼고, 리셋 버튼은 0으로 설정합니다. 일반적인 React 코드와 완전히 동일합니다.
Jotai만의 특별한 문법이나 패턴이 없습니다. 이것이 Jotai의 강점입니다.
실행해보기 코드를 저장하고 개발 서버를 실행해 봅시다. 터미널에서 npm run dev (Vite) 또는 npm start (CRA)를 실행합니다.
브라우저가 열리고 카운터 앱이 나타납니다. 증가 버튼을 클릭해 보세요.
숫자가 올라갑니다. 감소 버튼을 눌러보세요.
숫자가 내려갑니다. 리셋을 누르면 0으로 돌아갑니다.
완벽하게 작동합니다! 여러 컴포넌트에서 공유하기 이제 Jotai의 진가를 보여줄 차례입니다.
새로운 컴포넌트를 만들어 봅시다. CountDisplay.jsx라는 파일을 만들고 같은 counterAtom을 import합니다.
jsx // src/components/CountDisplay.jsx import { useAtom } from 'jotai' import { counterAtom } from '../atoms/counterAtom' function CountDisplay() { const [count] = useAtom(counterAtom) return <p>다른 컴포넌트에서 본 카운트: {count}</p> } export default CountDisplay 이 컴포넌트를 App.jsx에 추가하면 어떻게 될까요? 놀랍게도 같은 값을 공유합니다.
한쪽에서 버튼을 클릭하면 다른 컴포넌트의 값도 자동으로 업데이트됩니다. 읽기만 하는 경우 값만 읽고 싶고 변경할 필요가 없다면 어떻게 할까요?
useAtomValue를 사용하면 됩니다. 설정 함수 없이 값만 반환합니다.
jsx import { useAtomValue } from 'jotai' const count = useAtomValue(counterAtom) 반대로 값은 필요 없고 설정 함수만 필요하다면 useSetAtom을 씁니다. jsx import { useSetAtom } from 'jotai' const setCount = useSetAtom(counterAtom) 이렇게 하면 값이 변경되어도 이 컴포넌트는 리렌더링되지 않습니다.
성능 최적화에 유용합니다. 함수형 업데이트 setCount에 함수를 전달할 수도 있습니다.
jsx setCount(prev => prev + 1) 이 방식은 이전 값을 기반으로 새 값을 계산할 때 더 안전합니다. 비동기 상황에서 특히 유용합니다.
브라우저 개발자 도구로 확인하기 jotai-devtools를 설치했다면 더 재미있는 경험을 할 수 있습니다. 개발자 도구를 열면 Jotai 탭이 보입니다.
여기서 모든 atom의 현재 값을 실시간으로 볼 수 있습니다. 값을 직접 수정해서 테스트할 수도 있습니다.
정리 김개발 씨가 화면에 나타난 카운터를 보며 감탄했습니다. "정말 간단하네요!
useState와 거의 똑같은데 전역으로 쓸 수 있다니!" 박시니어 씨가 웃으며 답했습니다. "그렇죠?
이제 더 복잡한 예제를 만들어 볼까요?" 첫 번째 예제는 성공했습니다. Jotai의 기본 개념인 atom과 useAtom을 이해했습니다.
이제 여러분은 Jotai 개발자입니다.
실전 팁
💡 - useState를 Jotai로 바꾸려면 상태 정의만 atom으로 옮기면 됩니다
- useAtomValue와 useSetAtom을 활용하면 불필요한 리렌더링을 줄일 수 있습니다
- 개발자 도구로 상태 변화를 실시간으로 모니터링하세요
6. 프로젝트 구조 살펴보기
카운터 앱이 작동하는 것을 본 김개발 씨가 물었습니다. "실제 프로젝트에서는 어떻게 구조를 잡아야 할까요?" 박시니어 씨가 자신의 프로젝트 폴더를 열어 보여줍니다.
Jotai 프로젝트는 atoms 폴더에서 상태를 관리하고, hooks 폴더에서 커스텀 로직을 분리하며, components 폴더에서 UI를 구성하는 패턴이 일반적입니다. 규모가 커지면 도메인별로 폴더를 나누어 관리합니다.
다음 코드를 살펴봅시다.
// 추천 프로젝트 구조
src/
atoms/
userAtom.js // 사용자 관련 상태
cartAtom.js // 장바구니 상태
themeAtom.js // 테마 설정
hooks/
useCart.js // 장바구니 로직
useAuth.js // 인증 로직
components/
Header.jsx
Cart.jsx
ProductList.jsx
pages/
Home.jsx
Product.jsx
App.jsx
// atoms/userAtom.js 예시
import { atom } from 'jotai'
export const userAtom = atom(null)
export const isLoggedInAtom = atom((get) => get(userAtom) !== null)
김개발 씨는 이제 본격적인 프로젝트를 시작하려고 합니다. 하지만 어디에 무엇을 두어야 할지 막막합니다.
폴더 구조가 엉망이 되면 나중에 찾기 어렵습니다. 박시니어 씨가 자신의 경험을 공유합니다.
"처음에는 저도 막 만들었어요. 나중에 atom이 어디 있는지 찾느라 시간을 낭비했죠." 기본 폴더 구조 먼저 가장 기본적인 구조부터 살펴봅시다.
atoms 폴더는 모든 atom 정의를 담습니다. 마치 도서관의 서고처럼, 모든 상태가 여기에 정리되어 있습니다.
userAtom, cartAtom처럼 의미 있는 이름으로 파일을 만듭니다. components 폴더는 React 컴포넌트를 담습니다.
Header, Footer, Button 같은 UI 요소들이 여기에 있습니다. 이 컴포넌트들이 atoms를 import해서 사용합니다.
hooks 폴더는 커스텀 훅을 담습니다. useCart, useAuth처럼 비즈니스 로직을 캡슐화한 훅들이 있습니다.
작은 프로젝트의 구조 프로젝트가 작다면 간단하게 유지하는 것이 좋습니다. src/ atoms.js // 모든 atom을 한 파일에 App.jsx components/ Counter.jsx atom이 5개 이하라면 굳이 파일을 나눌 필요가 없습니다.
atoms.js 하나에 다 넣어도 됩니다. 중간 규모 프로젝트의 구조 프로젝트가 커지면 도메인별로 나눕니다.
src/ features/ user/ atoms/ userAtom.js components/ UserProfile.jsx hooks/ useAuth.js cart/ atoms/ cartAtom.js components/ CartItem.jsx hooks/ useCart.js 이렇게 하면 user 관련 코드는 user 폴더에, cart 관련 코드는 cart 폴더에 모입니다. 나중에 찾기 쉽고, 협업할 때도 충돌이 적습니다.
Atom 파일 작성 패턴 atoms 파일을 어떻게 작성하는 것이 좋을까요? 한 파일에 관련된 atom들을 모아두는 것이 좋습니다.
예를 들어 userAtom.js에는 사용자 관련 atom을 모두 넣습니다. ```jsx // atoms/userAtom.js import { atom } from 'jotai' // 기본 상태 export const userAtom = atom(null) // 파생 상태 export const isLoggedInAtom = atom( (get) => get(userAtom) !== null ) export const userNameAtom = atom( (get) => get(userAtom)?.name ??
'Guest' ) ``` 기본 atom과 파생 atom을 함께 정의하면 관리하기 편합니다. 커스텀 훅 패턴 복잡한 로직은 커스텀 훅으로 분리합니다.
jsx // hooks/useAuth.js import { useAtom, useSetAtom } from 'jotai' import { userAtom } from '../atoms/userAtom' export function useAuth() { const [user, setUser] = useAtom(userAtom) const login = async (email, password) => { const userData = await api.login(email, password) setUser(userData) } const logout = () => { setUser(null) } return { user, login, logout } } 이렇게 하면 컴포넌트는 useAuth만 import하면 됩니다. atom을 직접 다룰 필요가 없어서 코드가 깔끔해집니다.
Next.js 프로젝트 구조 Next.js를 사용한다면 구조가 조금 다릅니다. src/ app/ // Next.js 13+ App Router page.jsx layout.jsx atoms/ userAtom.js components/ Header.jsx hooks/ useAuth.js Next.js의 app 폴더와 atoms 폴더를 분리해서 관리합니다.
타입 정의 (TypeScript 사용 시) TypeScript를 쓴다면 타입도 함께 정의합니다. typescript // atoms/userAtom.ts import { atom } from 'jotai' type User = { id: string name: string email: string } export const userAtom = atom<User | null>(null) 제네릭으로 타입을 명시하면 자동완성과 타입 체크가 완벽하게 작동합니다.
환경별 분리 개발과 프로덕션 환경을 분리할 수도 있습니다. ```jsx // atoms/configAtom.js import { atom } from 'jotai' const isDev = process.env.NODE_ENV === 'development' export const apiUrlAtom = atom( isDev ?
'http://localhost:3000' : 'https://api.example.com' ) ``` 환경 변수를 atom으로 관리하면 전역에서 쉽게 접근할 수 있습니다. 테스트 파일 위치 테스트 파일은 어디에 둘까요?
각 파일 옆에 .test.js로 두는 것이 좋습니다. atoms/ userAtom.js userAtom.test.js hooks/ useAuth.js useAuth.test.js 이렇게 하면 테스트 대상 파일과 테스트 파일이 함께 있어서 관리하기 편합니다.
실전 팁 박시니어 씨가 중요한 조언을 합니다. "처음부터 완벽한 구조를 만들려고 하지 마세요.
프로젝트가 커지면서 자연스럽게 리팩토링하면 됩니다. 중요한 것은 일관성입니다." atom 파일명은 항상 ~Atom.js로 끝나게 하세요.
훅은 use~로 시작하세요. 이런 네이밍 규칙을 지키면 나중에 찾기 쉽습니다.
정리 김개발 씨가 자신의 프로젝트 구조를 정리하기 시작했습니다. atoms 폴더를 만들고, 관련 atom들을 모으고, 커스텀 훅을 분리했습니다.
박시니어 씨가 만족스럽게 말했습니다. "좋아요!
이제 진짜 프로젝트 같네요. 이 구조를 유지하면서 개발하면 나중에 유지보수하기 훨씬 쉬울 거예요." 좋은 프로젝트 구조는 개발 속도를 높이고, 버그를 줄이며, 팀 협업을 원활하게 만듭니다.
시간을 들여서 잘 설계하세요.
실전 팁
💡 - 작을 때는 간단하게, 커지면 도메인별로 분리하세요
- atom 파일명은 일관된 규칙을 사용하세요 (예: ~Atom.js)
- 복잡한 로직은 커스텀 훅으로 분리하면 테스트하기 쉽습니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
서비스 메시 완벽 가이드
마이크로서비스 간 통신을 안전하고 효율적으로 관리하는 서비스 메시의 핵심 개념부터 실전 도입까지, 초급 개발자를 위한 완벽한 입문서입니다. Istio와 Linkerd 비교, 사이드카 패턴, 실무 적용 노하우를 담았습니다.
EFK 스택 로깅 완벽 가이드
마이크로서비스 환경에서 로그를 효과적으로 수집하고 분석하는 EFK 스택(Elasticsearch, Fluentd, Kibana)의 핵심 개념과 실전 활용법을 초급 개발자도 쉽게 이해할 수 있도록 정리한 가이드입니다.
Grafana 대시보드 완벽 가이드
실시간 모니터링의 핵심, Grafana 대시보드를 처음부터 끝까지 배워봅니다. Prometheus 연동부터 알람 설정까지, 초급 개발자도 쉽게 따라할 수 있는 실전 가이드입니다.
분산 추적 완벽 가이드
마이크로서비스 환경에서 요청의 전체 흐름을 추적하는 분산 추적 시스템의 핵심 개념을 배웁니다. Trace, Span, Trace ID 전파, 샘플링 전략까지 실무에 필요한 모든 것을 다룹니다.
CloudFront CDN 완벽 가이드
AWS CloudFront를 활용한 콘텐츠 배포 최적화 방법을 실무 관점에서 다룹니다. 배포 생성부터 캐시 설정, HTTPS 적용까지 단계별로 알아봅니다.