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

[RTK] Typescript todoList 만들기 - (4) 컴포넌트 분리하기

by 그레이웅 2022. 10. 28. 02:00
반응형

이전 포스팅 글

- 2022.10.25 - [개발 공부 시리즈/Redux-ToolKit] - [RTK] Typescript todoList 만들기 - (1)

- 2022.10.26 - [개발 공부 시리즈/Redux-ToolKit] - [RTK] Typescript todoList 만들기 - (2) 기본적인 디자인하기

- 2022.10.27 - [개발 공부 시리즈/Redux-ToolKit] - [RTK] Typescript todoList 만들기 - (3) 삭제 기능 만들기

 

 

[RTK] Typescript todoList 만들기 - (4)  컴포넌트 분리하기

 

이번 포스팅에서는 RTK TODO APP의 컴포넌트 분리를 해보겠다.

컴포넌트 분리 기준은 개발자마다 각각 달라서 자신이 편한 대로 쓰는 게 가장 좋기는 하지만, 

같이 개발하는 사람과 어떻게 분리할 것인지 계획하고, 아니면 많은 사람들의 코드를 보고 따라 하는 것이 가장 좋기는 하다.

 

 

 

1. 기능 단위 컴포넌트 분리

 

RTK 공식 홈페이지에서 제공하는 CRA(Create-react-app) 템플릿에서는 다음과 같은 폴더 구조로 되어있다.

 

공식 템플릿 버전에서는 app폴더에는 hook과 store 가 존재하고 있다.

features라는 폴더 안에는 기능 단위로 컴포넌트, 스타일, Redux 등의 코드를 같이 넣는 것을 권장한다.

 

무엇보다 개인과 협업하는 사람들의 의견을 조율해야 하지만 나는 RTK에서 권장하는 방식으로 구현해야겠다.

 

2. todoList.tsx 컴포넌트 생성 및 module.css, App.tsx 변경

 

features/todos 폴더에 todoList.tsx 컴포넌트를 생성하고, 현재 App.tsx의 코드를 가져온다.

 

todoList.tsx 

 

import React, {useState} from 'react'

import { AppDispatch, RootState } from "../../app/store";
import { useSelector, useDispatch } from "react-redux";

import { addTodo, deleteTodo } from "./todoSlice";

//meterial Ui
import CssBaseline from "@mui/material/CssBaseline";
import Container from "@mui/material/Container";
import Box from "@mui/material/Box";

import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";

import List from "@mui/material/List";
import { ListItem } from "@mui/material";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";

import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";

//css modules import
import styles from './Todo.module.css'

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

  // console.log(dispatch);

  let [txt, setTxt] = useState("");
  return (
    <CssBaseline>
      <Container fixed>
        <Box sx={{ height: "100vh" }}>
          <h1 className={styles.center}>Todo List</h1>
          <div className={styles.center}>
            <TextField
              type="text"
              onChange={(e) => setTxt(e.target.value)}
              id="standard-basic"
              label="todo Input"
              variant="standard"
              value={txt}
            ></TextField>
            <Button
              style={{ marginTop: "10px", marginLeft: "10px" }}
              variant="outlined"
              onClick={(e) => {
                dispatch(addTodo(txt));
                setTxt('')
                console.log(e);
              }}
            >
              추가
            </Button>
          </div>
          <div className={styles.center}>
            <h3>할일 내용</h3>

            <List
              dense
              sx={{
                margin: "0 auto",
                width: "100%",
                maxWidth: 360,
                bgcolor: "background.paper",
              }}
            >
              {todoList.map((item) => (
                <Card className={styles.card} style={{ marginTop: "20px" }}>
                  <CardContent>
                    <ListItem
                      style={{ justifyContent: "space-between" }}
                      disableGutters
                      key={item.id}
                      // dispatch 추가
                      onClick={() => {
                        dispatch(deleteTodo(item.id));
                      }}
                    >
                      {item.text}{" "}
                      <DeleteOutlineIcon style={{ float: "right" , cursor: 'pointer' }} />
                    </ListItem>
                  </CardContent>
                </Card>
              ))}
            </List>
          </div>
        </Box>
      </Container>
    </CssBaseline>
  )
}


export default TodoList;

 

App.tsx 의 코드를 가져와서 위의 예제와 같이 경로를 변경하였다.

기존의 App.module.css 의 파일도 TodoList.module.css를 생성하여 새로운 경로를 붙여주었다.

 

TodoList.module.css

 


.wrapper {
    background-color: blanchedalmond;
}

.center {
    text-align: center;
}

.margin-auto {
    margin : 0 auto
}

.card:hover {
    background-color: aliceblue;
}

 

App.tsx 파일은 다음과 같이 변경하였다.

features/todos/TodoList.tsx를 import 해주었다.

 

App.tsx

 

import React from "react";

import ToodList from './features/todos/TodoList';

function App() {
  
  return (
    <>
    <ToodList />
    </>
  );
}

export default App;

 

3. TodoItem.tsx 생성

 

todoList의 해당 카드 각각의 할 일 요소가 되는 항목도 TodoItem.tsx의 컴포넌트 파일로 분리해주겠다.

 

 

TodoItem.tsx

import React from "react";

import { AppDispatch } from "../../app/store";
import { useDispatch } from "react-redux";

import { deleteTodo } from "./todoSlice";

import { ListItem } from "@mui/material";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";


import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";

import styles from './TodoItem.module.css';

interface Todo {
    id : string;
    text : string;
    completed : boolean
}
function TodoItem({todo } : {todo: Todo}) {

  const dispatch = useDispatch<AppDispatch>();
  return (
    <>
        <Card className={styles.card} style={{ marginTop: "20px" }}>
          <CardContent>
            <ListItem
              style={{ justifyContent: "space-between" }}
              disableGutters
              key={todo.id}
              // dispatch 추가
              onClick={() => {
                dispatch(deleteTodo(todo.id));
              }}
            >
              {todo.text}{" "}
              <DeleteOutlineIcon
                style={{ float: "right", cursor: "pointer" }}
              />
            </ListItem>
          </CardContent>
        </Card>
    </>
  );
}

export default TodoItem;

 

사실 여기서 엄청 헤맸다.

TodoList와 TodoItem의 todo의 값을 props로 넘겨 주려 했지만, 타입 스크립트 형태에서 어떻게 사용할지 몰라서 계속 오류가 났었다.

 

interface Todo {
    id : string;
    text : string;
    completed : boolean
}
function TodoItem({todo } : {todo: Todo}) {

..///

interface로 store.ts 에 있는 Todo객체와 똑같이 설정해주고, 아래 props 받는 방식이 원래는 ({props}) 로 사용하였는데 타입을 어떻게 붙일지 몰라서 한참을 헤맸다.

위와 같이 구현하니 잘 작동되었다.

 

TodoItem.module.css 도 새로 생성해주었다.

 

TodoItem.module.css 

 

.card:hover {
    background-color: aliceblue;
}

TodoList.tsx 에는 TodoItem.tsx를 추가하고, TodoItem에 props로 todo들을 내보내 준다.

 

TodoList.tsx

//todoItem 컴포넌트 추가
import TodoItem from './TodoItem'

//... 일부 코드 발췌

 	<div className={styles.center}>
            <h3>할일 내용</h3>
            <List
              dense
              sx={{
                margin: "0 auto",
                width: "100%",
                maxWidth: 360,
                bgcolor: "background.paper",
              }}
            >
              {todoList.map((item) => (
                <TodoItem todo={item}/>
              ))}
            </List>
          </div>
          
          
  ///...

 

 

 

4. 완성된 폴더구조

 

 

전체적인 디렉터리 구조는 이러하다. Todo관련 기능을 담당하는 것을 fetures/todos 폴더에 몰아서 넣었다.

 

타입 스크립트로 react를 사용하는 것이 너무 미숙하여 더 많은 공부를 해야겠다.

간단한 props를 넘기는 것조차 사용하기가 어려운 느낌이 있다.

직접 부딪히며, 더 많이 알아가야지!!

 

전체 적인 코드는 github 에도 올려놓았다.

 

https://github.com/ijw9209/rtk-ts-todo-app

 

GitHub - ijw9209/rtk-ts-todo-app

Contribute to ijw9209/rtk-ts-todo-app development by creating an account on GitHub.

github.com

 

반응형

댓글