[ToyProject-Todomate] ํˆฌ๋‘๋ฉ”์ดํŠธ ํด๋ก  ํ”„๋กœ์ ํŠธ 22

2022. 1. 5. 00:10ใ†WEB Dev/ToyProject

728x90


๐Ÿ”ท CloneTodo โ˜‘ - Todomate Clone Project  |  Team CloneMate

 

CloneTodo : ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์‚ฌ๋ž‘ํ•˜๋Š” ํˆฌ๋‘๋ฉ”์ดํŠธ๋ฅผ ํด๋ก ํ•˜์—ฌ ์›น ์„œ๋น„์Šค๋ฅผ ๋ฐฐํฌํ•ด๋ณด๋Š” ํ”„๋กœ์ ํŠธ

 


 

์˜ค๋Š˜ ํ•  ์ผ์€ ์ฑ…์— ๋‚˜์˜จ๋Œ€๋กœ Todo๋ฅผ ์‚ญ์ œํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณธ๋‹ค. ํˆฌ๋‘๋ฉ”์ดํŠธ์—์„œ์˜ ๋ชฉํ‘œ ์‚ญ์ œ ๋ฒ„ํŠผ์€ ํ•˜๋‹จ์— ์žˆ๊ณ , ํด๋ฆญํ•˜๋ฉด ์ž‘์„ฑํ•œ ๋ชฉํ‘œ๊ฐ€ ์‚ญ์ œ๋œ๋‹ค. 

์š”๊ฒƒ์€ ๋‚˜์ค‘์˜ ์ž๋ฆฌ๋ฐฐ์น˜์˜ ๋ฌธ์ œ์ผ ๊ฒƒ ๊ฐ™์•„์„œ ์šฐ์„  ํ›„๋”ฑ ์‚ญ์ œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด๋ณธ๋‹ค.

 

MUI์˜ ListItemSecondaryAction์„ ์ด์šฉํ•ด์„œ ์‚ญ์ œ ๋ฒ„ํŠผ์„ ๊ตฌํ˜„ํ•œ๋‹ค. ์ด ๋•Œ ์‚ญ์ œ ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋“ค์–ด๊ฐˆ ๊ณณ์€ ์ด์ „์— ๋งŒ๋“  Goals.js ์ปดํฌ๋„ŒํŠธ์˜ <ListItem> </ListItem> ์‚ฌ์ด๊ฐ€ ๋˜๊ฒ ๋‹ค. 

๊ทธ๋ฆฌ๊ณ  InputBase์— id์™€ name ์†์„ฑ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. ์ด id์™€ name์ด ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•  ๋•Œ ์–ด๋–ค ๋ชฉํ‘œ๋ฅผ ์ง€์ •ํ–ˆ๋Š”์ง€ ํ™•์ธํ•ด ์ค„ ์ˆ˜ ์žˆ๋Š” ํ‚ค๊ฐ€ ๋œ๋‹ค.

import React from "react";
import { Checkbox, IconButton, InputBase, ListItem, ListItemText } from "@mui/material";
import GoalForm from "./GoalForm";
import { ListItemSecondaryAction } from "@material-ui/core";

export default function Goals(props) {
  console.log(props.goal);
  let goalItems = props.goal;
  let add = props.add;
  return (
    <>
    <GoalForm add={add} />

        {
        goalItems.map((item, idx) => {
        return (
                <ListItem className="goals-wrap">
                    <Checkbox checked={item.done} />
                    <ListItemText>
                    <InputBase
                        inputProps={{ "aria-label": "naked" }}
                        type="text"
                        id={item.id}
                        name={item.id}
                        value={item.title}
                        multiline={true}
                        fullWidth={true}
                    />
                    </ListItemText>
                    <ListItemSecondaryAction>
                    <IconButton aria-label="Delete Todo" sx={{ fontSize: '14px' }}>
                        ์‚ญ์ œ
                    </IconButton>
                </ListItemSecondaryAction>
                </ListItem>
                );
            })
        }
    </>
  );
}

 

์›๋ž˜ ์•„์ด์ฝ˜์„ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, ๋‚˜๋Š” ์‚ญ์ œ ๋ผ๊ณ  ์ ํžŒ ๊ธด ๋ฒ„ํŠผ์œผ๋กœ ๋ณ€ํ™˜ํ•  ์˜ˆ์ •์ด๋ผ ์‚ญ์ œ๋ผ๋Š” ํ…์ŠคํŠธ๋กœ ๋ฒ„ํŠผ์„ ๊ตฌํ˜„ํ–ˆ๋‹ค. 

 

๋ฒ„ํŠผ์„ ๊ตฌํ˜„ํ–ˆ์œผ๋‹ˆ ์ด์ œ ์‚ญ์ œ ์ด๋ฒคํŠธ๋ฅผ ๊ตฌํ˜„ํ•  ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด์•ผํ•œ๋‹ค. delete() ๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค ๊ฑด๋ฐ, ํ˜„์žฌ state๋ฅผ ์‚ญ์ œํ•ด์•ผํ•˜๊ณ , state๋Š” app.js ์— ์žˆ์œผ๋‹ˆ app.js๋กœ ๊ฐ€์„œ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•˜๊ณ  props๋กœ ๋„˜๊ฒจ์ค˜์•ผ ํ•œ๋‹ค. add์™€ ๊ฐ™๋‹ค. 

delete ๋ผ๊ณ  ํ•จ์ˆ˜์ด๋ฆ„์„ ์ž‘์„ฑํ•˜๋ฉด ์˜ˆ์•ฝ์–ด๋ผ๊ณ  ์•ˆ๋œ๋‹ค๊ณ  ํ•œ๋‹ค. deleteGoal๋ผ๋Š” ํ•จ์ˆ˜์ด๋ฆ„์œผ๋กœ ๋ฐ”๊ฟ”์ฃผ๊ณ , add๋„ ๋™์ผํ•˜๊ณ  addGoal ๋กœ ํ•จ์ˆ˜ ์ด๋ฆ„์„ ๋ฐ”๊ฟ”์ค€๋‹ค.

 

//app.js ์— ์ž‘์„ฑ


  //๋ชฉํ‘œ ์ถ”๊ฐ€ ํ•จ์ˆ˜
  function addGoal(item){

    const thisItems = goal.items; // goal State ์›๋ณธ ์นดํ”ผ
    item.id = 'ID-' + thisItems.length; //key๋ฅผ ์œ„ํ•œ id ์ถ”๊ฐ€
    item.done = false; // done false๋กœ ์ดˆ๊ธฐํ™”
    thisItems.push(item) // ์นดํ”ผํ•œ goal ๋ฆฌ์ŠคํŠธ์— ์•„์ดํ…œ ์ถ”๊ฐ€
    goalChange({items: thisItems}); //goalChange๋ฅผ ์ด์šฉํ•ด state ๋ณ€๊ฒฝ
    console.log('items :', goal.items)
  }

  //๋ชฉํ‘œ ์‚ญ์ œ ํ•จ์ˆ˜
  function deleteGoal(item){

    const thisItems = goal.items;
    console.log('Before Update Items :', goal.items );
    const newItems = thisItems.filter(e => e.id !== item.id)
    goalChange({items: newItems},()=>{
      console.log('Update Items : ', goal.items)
    })
  }

 

๋ชฉํ‘œ ์‚ญ์ œ ํ•จ์ˆ˜๋Š” Goals ์ปดํฌ๋„ŒํŠธ์— ๋ณด๋‚ด์ ธ์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— Goals ์ปดํฌ๋„ŒํŠธ์— props๋กœ ๋ณด๋‚ด์ค€๋‹ค.

        <Route exact path="/goals">
        <BasicNavBar/>
          <Goals goal={goal.items} add={addGoal} delete={deleteGoal} key={goal.items.id} />
        </Route>

 

์ด์ „์— add ํ•จ์ˆ˜๋Š” Goals ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฐ›์•„์„œ GoalForm์œผ๋กœ ํ•œ ๋ฒˆ ๋” ์ „๋‹ฌํ–ˆ๋Š”๋ฐ, ์ด๋ฒˆ์—” ๋ฐ”๋กœ props๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. Goals.js์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์ค€๋‹ค. 

 

    function deleteEventHandler() {
        props.delete(goalItems)
    }

//์—ฌ๊ธฐ์„œ goalItems๋Š” ์ €๋ฒˆ์— props.goal์„ ๋ฐ›์•„๋‘” ๋ณ€์ˆ˜์ด๋‹ค.

 

์ด์ œ ์ด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์•„๊นŒ ๋งŒ๋“ค์–ด ๋‘” ์‚ญ์ œ ๋ฒ„ํŠผ์— ์—ฐ๊ฒฐํ•ด์ฃผ๋ฉด ๋œ๋‹ค. 

 

//Goals.js

import React from "react";
import { Checkbox, IconButton, InputBase, ListItem, ListItemText } from "@mui/material";
import GoalForm from "./GoalForm";
import { ListItemSecondaryAction } from "@material-ui/core";

export default function Goals(props) {
  console.log(props.goal);
  let goalItems = props.goal;
  let add = props.add;

    function deleteEventHandler() {
        props.delete(goalItems)
    }

  return (
    <>
    <GoalForm add={add} />

        {
        goalItems.map((item, idx) => {
        return (
                <ListItem className="goals-wrap">
                    <Checkbox checked={item.done} />
                    <ListItemText>
                    <InputBase
                        inputProps={{ "aria-label": "naked" }}
                        type="text"
                        id={item.id}
                        name={item.id}
                        value={item.title}
                        multiline={true}
                        fullWidth={true}
                    />
                    </ListItemText>
                    <ListItemSecondaryAction>
                    <IconButton aria-label="Delete Todo" onClick={deleteEventHandler} sx={{ fontSize: '14px' }}>
                        ์‚ญ์ œ
                    </IconButton>
                </ListItemSecondaryAction>
                </ListItem>
                );
            })
        }
    </>
  );
}

 

๋‹ค ๋งŒ๋“ค์–ด๋†“๊ณ  ์‹คํ–‰ํ•ด๋ณด๋‹ˆ ๋ชฉํ‘œ๊ฐ€ ์‚ญ์ œ๋„ ์•ˆ๋˜๊ณ  ์ฝ˜์†”์—๋Š” 

index.js:1 Warning: State updates from the useState() and useReducer() Hooks don't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect().

 

์ด๋Ÿฐ ๋ฉ”์‹œ์ง€๊ฐ€ ๋–ด๋‹ค. useState์˜ ๋ณ€๊ฒฝ ํ•จ์ˆ˜์— ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ์“ฐ์ง€ ๋ง๋ผ๋Š” ์ด์•ผ๊ธฐ ๊ฐ™์€๋ฐ (...) ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‚ด๋ง˜๋Œ€๋กœ ๋ณ€ํ™˜ํ•ด์„œ ๊ทธ๋Ÿฐ๊ฐ€๋ณด๋‹ค. goalChange ํ•จ์ˆ˜์—์„œ ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ๋นผ์ฃผ์ž. ์›๋ž˜ ์ฝœ๋ฐฑํ•จ์ˆ˜๋„ ๋””๋ฒ„๊น… console.log๋ฅผ ์ฐ๋Š”๊ฑฐ๋ผ ํฐ ๋ฌธ์ œ๋Š” ์—†์„ ๋“ฏ ํ•˜๋‹ค. 

 

๊ทธ๋ฆฌ๊ณ  ์‚ญ์ œ๊ฐ€ ์•ˆ๋˜๋Š”๊ฑด ....

์™œ์ผ๊นŒ? ํ•จ์ˆ˜ ์ด๋ฒคํŠธ๋Š” ์ž˜ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์•„ ๋ฐ”์ธ๋”ฉ์—๋Š” ๋ฌธ์ œ๊ฐ€ ์—†๋Š”๋ฐ, id ๊ฐ’์„ ๋น„๊ตํ•˜๋Š”๋ฐ์—์„œ ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ๊ฒƒ ๊ฐ™๋‹ค. ๋‹ค๋ฅธ ๋ฌธ์„œ๋“ค์„ ๋” ์ฐพ์•„๋ณด๊ณ  ํ•จ์ˆ˜๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ํ•  ๊ฒƒ ๊ฐ™๋‹ค. ์•„๋‹˜ ๋‚ด๊ฐ€ ๋ญ”๊ฐ€ ์ž˜๋ชป ์ž‘์„ฑํ•œ๊ฑธ๊นŒใ… ใ… 

 

  //๋ชฉํ‘œ ์‚ญ์ œ ํ•จ์ˆ˜
  function deleteGoal(item){

    const thisItems = goal.items; // goal State ์›๋ณธ ์นดํ”ผ
    console.log('Before Update Items :', goal.items );
    const newItems = thisItems.filter(e => e.id !== item.id)
    goalChange({items: newItems}, console.log('update : ' , newItems)
    )
  }

 

์–ด๋”” ๋ฌผ์–ด๋ณผ๋ฐ๊ฐ€ ์—†์œผ๋‹ˆ ๋‹ต๋‹ตํ•˜๊ธฐ๋งŒ ํ•˜๋‹คใ… ใ… 

 

 

 

 

e.id๋ž‘ item.id๋ฅผ ์ฐ์–ด๋ณด๋‹ˆ ๋‘˜ ๋‹ค undefined๋‹ค..

๋ญ”๊ฐ€ e.id๊ฐ€ ์ œ๋Œ€๋กœ ์•ˆ ๋“ค์–ด๊ฐ€๊ณ  ์žˆ๋˜์ง€ item.id๊ฐ€ ์•ˆ๋“ค์–ด๊ฐ€๋Š”๊ฑฐ ๊ฐ™์€๋ฐ ์šฐ์„  add ํ•จ์ˆ˜ ๋•Œ ํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ onClick์ด๋ฒคํŠธ์— deleteEventHandler์— e๋ฅผ ์ „๋‹ฌํ•ด์ฃผ๊ธฐ ์œ„ํ•ด์„œ ํ•ด๋‹น IconButton์— id ์†์„ฑ์„ ๋„ฃ์–ด์ฃผ๊ณ , id๋ฅผ deleteGoal ํ•จ์ˆ˜๋กœ ๋ณด๋‚ด์ฃผ์—ˆ๋‹ค.

 

<IconButton aria-label="Delete Todo" id={item.id} onClick={deleteEventHandler} sx={{ fontSize: '14px' }}>
์‚ญ์ œ
</IconButton>
    function deleteEventHandler(e) {
      console.log( 'id : ',  e.target.id)
      props.deleteGoal(e.target.id, goalItems)
    }

 

๊ทธ๋ฆฌ๊ณ  deleteGoal ํ•จ์ˆ˜์— ์ธ์ž๋ฅผ ๋‘ ๊ฐœ ๋ฐ›๋„๋ก ํ•ด์„œ ํด๋ฆญํ•œ list์˜ id ๊ฐ’๊ณผ goal state๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ๋‹ค.

  //๋ชฉํ‘œ ์‚ญ์ œ ํ•จ์ˆ˜
  function deleteGoal(targetId, item){

    const thisItems = item; // goal State ์›๋ณธ ์นดํ”ผ
    console.log('Before Update Items :', item );
    const newItems = thisItems.filter(e => e.id !== targetId)
    goalChange({items: newItems})
  }

 

์ด๋ ‡๊ฒŒ ํ•˜๋‹ˆ ์‚ญ์ œ๊ฐ€ ๋œ๋‹ค... ๊ฐ๋™ ใ… ใ… ใ… ใ… ใ… ใ… 

์ •ํ™•ํ•˜๊ฒŒ ๋ญ๊ฐ€ ๋”ฑ ํ‹€๋ ค์„œ ๊ทธ๋žฌ๋‹ค! ์„ค๋ช…ํ•˜๊ธฐ๋Š” ์–ด๋ ค์šด๋ฐ ์ด๋ ‡๊ฒŒ ํ•ด๊ฒฐํ•˜๊ณ  ๋‚˜๋‹ˆ ๋ญ”๊ฐ€ ๊ตฌ์กฐ๋ฅผ ์•Œ๊ฒŒ๋œ ๊ฑฐ ๊ฐ™๊ณ  ๋ฟŒ๋“ฏํ•˜๊ธฐ๋งŒ ํ•˜๋‹ค.... ์˜ค๋Š˜์€ ํŽธํ•˜๊ฒŒ ์ž ๋“ค ์ˆ˜ ์žˆ๊ฒ ๋‹ค...

 

 

728x90