본문 바로가기
개발 공부 시리즈/Redux-ToolKit

[RTK] Typescript todoList 만들기 - (1)

by 그레이웅 2022. 10. 25. 02:30
반응형

[RTK] Typescript todoList 만들기

 

Redux-Toolkit과 Typescirpt를 사용한 TodoList를 정리하며, 만들어 보는 시간을 가져보겠다.

 

1. 프로젝트 설치

 

typescript 템플릿을 이용하여 다운로드하도록 하겠다.

자신이 원하는 디렉터리로 cmd 창을 열고 다음과 같은 명령어를 입력하면 된다.

 

npx create-react-app rtk-todo-list --template typescript

 

설치가 완료되면 만들어진 디렉토리로 이동하여 vsCode로 프로젝트를 연다.

 

자세한 Redux-Toolkit 설치방법은 이 포스팅을 참고하면 된다.

2022.10.03 - [개발 공부 시리즈/Redux-ToolKit] - [React] Redux-Toolkit 초기 설치

 

[React] Redux-Toolkit 초기설치

Redux-Toolkit Redux-toolkit은 기존 Redux를 편리하고 간편하게 사용할 수 있는 라이브러리이다. Redux코드를 보다 더 간결하게 작성할 수 있고, Redux 보다 효율적이다. 기존 Redux의 스토어 구성, 많은 패키

ijw9209.tistory.com

 

 


 

 

2. 프로젝트 실행해보기

 

vsCode 터미널 창에서 => npm start 명령어로 실행해보면 다음과 같이 구동되는 것을 확인할 수 있다.

 

 

 


 

 

3. App.tsx 수정하기

 

프로젝트의 App.tsx의 내용을 지우고, 다음과 같이 작성한다.

 

import React from 'react';
import './App.css';

function App() {
  return (
    <>
      <h1>할일 관리</h1>
    </>
  );
}

export default App;

 

기존의 React 로고가 나오는 코드들을 지우고, 할 일 관리에 대한 내용을 작성할 것이다.

 

 


 

4. 필요한 패키지 설치하기

 

- redux toolkit과 react-redux를 다운로드한다.

 

npm install @reduxjs/toolkit react-redux

 

- 디자인에 사용할 material UI를 다운로드한다.

npm install @mui/material @emotion/react @emotion/styled

https://mui.com/material-ui/getting-started/installation/

 

Installation - Material UI

Install Material UI, the world's most popular React UI framework.

mui.com

 

- uuid를 사용하여 고유 아이디를 생성한다. uuid의 라이브러리를 다운로드한다.

 

npm install uuid @types/uuid

https://www.npmjs.com/package/uuid

 

uuid

RFC4122 (v1, v4, and v5) UUIDs. Latest version: 9.0.0, last published: 2 months ago. Start using uuid in your project by running `npm i uuid`. There are 48973 other projects in the npm registry using uuid.

www.npmjs.com

 

5. 폴더 구조 만들기

 

src 폴더 하위에 app 폴더와 features 폴더를 만든다.

app 폴더는 Redux의 store나 hook 등의 내용이 들어가는 폴더이고,

features 폴더는 하위에 slice 및 component가 들어갈 폴더이다.

 

 

 

6. store.ts , hook.ts 작성하기

 

RTK 공식 문서를 참고하여 app/store.ts 와 app/hook.ts를 작성해준다.

 

app/store.ts

//store.ts

import { configureStore } from '@reduxjs/toolkit'
import todoReducer from '../features/todos/todoSlice'
// ...

export const store = configureStore({
  reducer: {
    todos : todoReducer,
  }
})

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch

파일 이름 그대로 store는 저장되는 공간이다.

 

app/hook.ts

//hook.ts


import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from './store'

// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch: () => AppDispatch = useDispatch
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

hook.ts는 RootState 및 AppDispatch의 타입을 가져올 수 있다.

 

 


7. Slice 작성


features/todos/todoSlice.ts

import { createSlice , PayloadAction } from '@reduxjs/toolkit'
import type { RootState } from '../../app/store'
import { v4 as uuidv4 } from 'uuid';

// Define a type for the slice state
interface Todo {
  id : string;
  text : string;
  completed : boolean
}

// Define the initial state using that type
const initialState: Todo[] = [
    {
        id : uuidv4(),
        text : '테스트',
        completed : false
    }
];

export const todoSlice = createSlice({
  name: 'todos',
  // `createSlice` will infer the state type from the `initialState` argument
  initialState,
  reducers: {
    //할일 추가
    addTodo: (state , action: PayloadAction<string>) => {
        // const { text } = action.payload;
        state.push({id : uuidv4(), text : action.payload, completed : false });
    },
  },
})

export const { addTodo } = todoSlice.actions

// Other code such as selectors can use the imported `RootState` type
export const selectTodos = (state: RootState) => state.todos

export default todoSlice.reducer

어기서 타입 스크립트 사용으로 인해 작성방법을 굉장히 헤매었다.

설명하자면 Todo라는 객체의 타입을 먼저 설정해주고,  초기값을 initialState로 선언해준다.

 

CreateSlice는 사용할 reducers를 정의하는 공간이다. 일단 할 일 추가를 추가하였다 action.paload에는 dispatch로 사용자가 보내는 값이 담아서 들어오게 된다. 

할일 추가 기능에는 사용자가 입력한 텍스트가 들어오게 되며, id 값은 uuid()로 난수가 생성되고, completed(할 일 완료할 때 true로 바뀐다.) false를 기본값으로 설정해 주었다.

 

 


8. Provider 선언

 

루트 경로에 있는 index.tsx의 Provider를 선언하고 store를 연결해준다.

 

index.tsx

 

//index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { store } from './app/store'
import { Provider } from 'react-redux'

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <Provider store={store}>
        <App />
    </Provider>,
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

Provider 연결을 깜빡하고 계속 오류가 어디서 났는지 찾고 있었다...

하지만 오류 코드를 제대로 보니 Provider 문제라고 떡하니 써져있었는데, 오류코드를 잘 봐야 하는 중요성에 대해 다시 한번 깨닫게 된다. store를 연결해준다.

 


9. App.tsx 작성

 

App.tsx를 다음과 같이 변경해준다.

 

App.tsx

import React,{ useState } from 'react';
import { AppDispatch, RootState } from './app/store'
import { useSelector, useDispatch } from 'react-redux'
import { addTodo } from './features/todos/todoSlice';


function App() {
  const todoList = useSelector((state: RootState) => state.todos)
  const dispatch = useDispatch<AppDispatch>()

  console.log(dispatch)

  let [ txt , setTxt ] = useState('');

  return (
    <>
      <h1>할일 관리</h1>

      <div>
        <input 
        type="text"
        onChange={(e) => setTxt(e.target.value)}
        ></input>
        <button
          onClick={() => {
            dispatch(addTodo(txt));
          }}
        >추가</button>
      </div>

      <div>
        <h3>할일 내용</h3>
        <>
        {todoList.map((item) =>(
          <p key={item.id}>{item.text}</p>
        ))}          
        </>        
      </div>
    </>
  );
}

export default App;

 

일단 기능 먼저 구현하도록 하였다.

설명하자면 useSelector로 현재 전역 state에 있는 todos를 가져와서 보여주게 되며, 

dispatch함수를 이용해 추가 버튼을 눌렀을 때 할 일이 추가되도록 만들었다.

 

나는 여기서 한참 동안 헤매었다. 

dispatch 함수에서 자동으로 reducer를 매칭 해주는지 알고 addTodo함수를 계속 찾을 수 없다길래 설정에 오류사항이 있는 줄 알았다. 

하지만 알고 보니 Slice를 import 하여 사용하는 것이었다..ㅠ

그래도 그 덕분에 redux-toolkit에 대한 이해도는 많이 올라간 것 같다.

 

 


현재까지 구현 내용

 

할일 입력 전 초기상태

 

할일 입력 후 상태

 

매번 다른 사람의 코드를 따라서 치다가 내가 이번엔 직접 부딪히려고 구현하였는데, 생각보다 시간이 더 오래 걸리게 되었지만, 한두 번 계속 헤매는 것이 내 실력에 정말 많은 도움이 된다는 것을 다시 한번 깨닫는다. 

늦은 시간까지 하느라 멈출까도 생각하였지만, 참고 계속하려고 한 것이 잘한 것 같다.

 

디자인 및 삭제 기능은 다음 포스팅으로 이어가도록 하겠다.

반응형

댓글