⭐️ Today's summary
오늘은 전역 상태를 간단하게 관리할 수 있는 경량 상태관리 라이브러리인 zustand를 배웠다.
이를 통해 간단한 API로 전역 상태를 관리할 수 있고, 사용법이 직관적이며, Provider, 액션 타입 등 복잡한
구조도 불필요하다.
처음 접하는거기에 사용법을 정리해 보겠다.
⭐️ Problem
zustand의 기본 문법과 사용법에 익숙하지 않아 코드로 정리해보겠다.
⭐️ Try
[ App.jsx ]
** App.jsx **
import './App.css';
import { styled } from 'styled-components';
import CounterDisplay from './components/zustand/CounterDisplay';
import CounterControls from './components/zustand/CounterControls';
import TodoList from './components/zustand/TodoList';
function App() {
const AppContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
width: 100vw;
padding: 24px;
text-align: center;
transition: all 0.3s;
`;
const Section = styled.section`
width: 100%;
max-width: 800px;
margin: 0 auto;
padding: 18px;
border-radius: 8px;
margin-bottom: 20px;
`;
return (
<>
<AppContainer>
<Section>
<h2>Zustand 전역 상태 관리</h2>
<CounterDisplay />
<CounterControls />
</Section>
<Section>
<h2>Zustand TodoList</h2>
</Section>
<Section>
<TodoList />
</Section>
</AppContainer>
</>
);
}
export default App;
[ TodoList.jsx ]
** TodoList.jsx **
import React from 'react';
import styled from 'styled-components';
import useTodoStore from '../../store/useTodoStore';
const ListContainer = styled.div`
width: 100%;
max-width: 600px;
margin: 0 auto;
`;
const TodoItem = styled.div`
display: flex;
align-items: center;
padding: 12px;
margin: 8px 0;
border-radius: 4px;
`;
const Checkbox = styled.input`
margin-right: 12px;
width: 18px;
height: 18px;
cursor: pointer;
`;
const TodoText = styled.span`
flex: 1;
text-decoration: ${(porps) => (porps.completed ? 'line-through' : 'none')};
`;
const DeleteButton = styled.button`
padding: 6px 12px;
color: white;
background: red;
border-radius: 4px;
cursor: pointer;
&:hover {
opacity: 0.9;
}
`;
const FilterContainer = styled.div`
display: flex;
gap: 10px;
margin-bottom: 20px;
justify-content: center;
`;
const FilterButton = styled.button`
padding: 6px 12px;
color: white;
background: blue;
border-radius: 4px;
cursor: pointer;
&:hover {
opacity: 0.9;
}
`;
const TodoList = () => {
const { getFilteredTodos, toggleTodo, deleteTodo, setFilter } = useTodoStore();
const todos = getFilteredTodos();
return (
<ListContainer>
<FilterContainer>
<FilterButton onClick={() => setFilter('all')}>전체</FilterButton>
<FilterButton onClick={() => setFilter('active')}>진행중</FilterButton>
<FilterButton onClick={() => setFilter('completed')}>완료</FilterButton>
</FilterContainer>
{todos.map((todo) => (
<TodoItem key={todo.id}>
<Checkbox type="checkbox" checked={todo.completed} onChange={() => toggleTodo(todo.id)} />
<TodoText completed={todo.completed}>{todo.text}</TodoText>
<DeleteButton onClick={() => deleteTodo(todo.id)}>삭제</DeleteButton>
</TodoItem>
))}
</ListContainer>
);
};
export default TodoList;
[ useTodoStore.js ]
import { create } from 'zustand';
const useTodoStore = create((set, get) => ({
todos: [
{
id: 1,
text: '밥먹기',
completed: false,
},
{
id: 2,
text: '잠자기',
completed: false,
},
{
id: 3,
text: '숨쉬기',
completed: false,
},
],
filter: 'all', // all, active, completed
toggleTodo: (id) =>
set((state) => ({
todos: state.todos.map((todo) => (todo.id === id ? { ...todo, completed: !todo.completed } : todo)),
})),
deleteTodo: (id) =>
set((state) => ({
todos: state.todos.filter((todo) => todo.id !== id),
})),
setFilter: (filter) => set({ filter }), // {filter : filter}
getFilteredTodos: () => {
const { todos, filter } = get();
switch (filter) {
case 'active':
return todos.filter((todo) => !todo.completed);
case 'completed':
return todos.filter((todo) => todo.completed);
default:
return todos;
}
},
}));
export default useTodoStore;
위 코드에서 헷갈렸던 부분은 zustand의 전역 상태와 액션(함수) 부분을 구분하여 사용하는 방법과
set과 get의 역할, 그리고 어느 값을 전역에서 사용할지 말지가 헷갈렸다.
위 코드는 렌더링시에 useTodoStore에서 값을 가져오고 getFilterdTodos()로 필터링된 배열을 불러오고 있다.
이부분에서 필터링해서 가져오는 코드가 TodoList.jsx에 작성해야하는지, useTodoStrore.js에 작성해야하는지
저 필터링 된 부분을 어디서만 사용할지에 따라서 다르게 생각하면 됬었다.
현재 코드에서는 다른 부분에서도 필터링을 사용할 수 있다고 생각하여 전역으로 관리하는 useTodoStore에 작성했다.
이제 전역 상태와 액션을 구분하는 방식은 zustand는 create(()=>{}) 안에 key : value 형식으로 저장되어있는데,
이때 value가 일반 값이면 상태가 되고, 함수가 들어가면 액션이 된다.
set()을 사용하게 되면 콜백함수로 state를 사용할 수 있게되는데, 이건 필드( 상태 + 액션 )내용 전부에 접근할 수 있다.
get()또한 set() 안에서 사용할 수 있으며 차이는 state. 하고 접근하나 get(). 하고 접근하나의 차이가 끝이다.
zustand는 정말 간편하면서도 강력한 상태관리 라이브러리이다.
'Weekly TIL' 카테고리의 다른 글
Weekly TIL - Day 16 (2) | 2025.04.30 |
---|---|
Weekly TIL - Day 15 (2) | 2025.04.29 |
Weekly TIL - Day 13 (0) | 2025.04.27 |
Weekly TIL - Day 12 (0) | 2025.04.25 |
Weekly TIL - Day 11 (1) | 2025.04.25 |