개인적으로 세팅 연습을 해보며 간단한 투두리스트도 함께 만들어보았다. 공부해가는 과정이라 틀린 부분이 있을 수 있음.
※ Next.js 공식 사이트에 정리된 CLI툴로 설치했을 때의 이점
1) npx create-next-app --ts [폴더이름]
2) npm i redux react-redux @types/react-redux
3) npm i --save-dev redux-devtools-extension next-redux-wrapper
// store/action/todos.ts
export const ADD_TODO = "ADD_TODO"
export const DELETE_TODO = "DELETE_TODO"
let id = 1
interface TodoType {
title: string,
isComplete: boolean
}
export const addTodo = (todo: TodoType) => {
return {
type: ADD_TODO,
payload: {
todo: {
id: id++,
title: todo.title,
isComplete: todo.isComplete,
}
}
}
}
export const deleteTodo = (id: number) => {
return {
type: DELETE_TODO,
payload: {
id,
}
}
}
export type ActionsType = ReturnType<typeof addTodo> | ReturnType<typeof deleteTodo>
// store/reducer/todoReducer.ts
import { ActionsType, ADD_TODO, DELETE_TODO } from "../actions/todos";
interface todoType {
title: string,
isComplete: boolean,
}
interface InitialStateType {
todos: todoType[]
}
const initialState: InitialStateType = {
todos: [],
}
export default function TodoReducer(state = initialState, action: ActionsType) {
switch (action.type) {
case ADD_TODO:
return {
todos: [...state.todos, action.payload.todo]
}
case DELETE_TODO:
return {
todos: [...state.todos.filter((todo) => todo.id !== action.payload.id)]
}
default:
return state
}
}
// store/reducer/main.ts
import { combineReducers } from "redux";
import TodoReducer from "./todoReducer"
const rootReducer = combineReducers({ TodoReducer })
type ReducerType = ReturnType<typeof rootReducer>
export default rootReducer
// pages/app.tsx
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
const store = createStore(rootReducer, composeWithDevTools())
export default function App({ Component, pageProps }: AppProps) {
return <Provider store={store}>
<Component {...pageProps} />
</Provider>
}
// 메인 페이지
// pages/index.tsx
import InputForm from '@/components/InputForm'
import TodoList from '@/components/TosoList'
export default function Home() {
return (
<>
<h1>todo</h1>
<InputForm />
<TodoList />
</>
)
}
// InputForm 컴포넌트
// components/InputForm.tsx
import { addTodo } from "@/store/actions/todos"
import { useState } from "react"
import { useDispatch } from "react-redux"
const InputForm = () => {
const dispatch = useDispatch()
const [text, setText] = useState('')
const handleChange = (e) => {
setText(e.target.value)
}
const handleSubmit = (e) => {
e.preventDefault()
const todo = {
title: text,
isComplete: false,
}
dispatch(addTodo(todo))
console.log(todo);
setText('')
}
return (
<div>
<form onSubmit={handleSubmit}>
<input type="text" value={text} onChange={handleChange} />
<button>추가</button>
</form>
</div>
)
}
export default InputForm
// TodoList 컴포넌트
// components/TodoList.tsx
import { useSelector } from "react-redux"
import TodoItem from "./TodoItem"
const TodoList = () => {
const todos = useSelector(state => state.TodoReducer.todos)
return (
<div>{todos.map(todo => <TodoItem key={todo.id} todo={todo} />)}</div>
)
}
export default TodoList
// TodoItem 컴포넌트
// components/TodoItem.tsx
import { deleteTodo } from "@/store/actions/todos"
import { useDispatch } from "react-redux"
const TodoItem = ({ todo }) => {
const dispatch = useDispatch()
const handleDelete = () => {
dispatch(deleteTodo(todo.id))
}
return (
<div>
<span>{todo.title}</span>
<span>{todo.isComplete ? '완료' : '미완료'}</span>
<button onClick={handleDelete}>삭제</button>
</div>
)
}
export default TodoItem
참고 사이트
https://velog.io/@gyutato/Next.js%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-create-next-app