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

2022. 1. 13. 00:25ใ†WEB Dev/ToyProject

728x90


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

 

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

 


 

์–ด์ œ ๋งŒ๋“ค์–ด๋‘” ์ˆ˜์ • ํ•จ์ˆ˜๋ฅผ ์ด์–ด์„œ ๊ฐœ๋ฐœํ•˜๋ ค๊ณ  ํ•˜๋Š”๋ฐ ๋ณต์žกํ•œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋ ค๊ณ  ํ•˜๋‹ˆ ๋งŽ์ด ๋ง‰๋ง‰ํ•ด์กŒ๋‹ค.

ํ•˜๋‚˜์”ฉ ์ •๋ฆฌํ•ด์„œ ํ’€์–ด๋‚˜๊ฐ€๋ณด์ž

 

 

๋ชฉํ‘œ ์ˆ˜์ • ๊ธฐ๋Šฅ์„ ์œ„ํ•ด ๊ตฌํ˜„ํ•ด์•ผ ํ•  ๊ฒƒ

 

- ํ˜„์žฌ ํ‘œ์‹œ๋œ ๋ชฉํ‘œ๋ฅผ ํด๋ฆญํ•˜๋ฉด ํ•ด๋‹น state์˜ title๊ณผ key๊ฐ€ GoalForm์— ์ „๋‹ฌ๋  ๊ฒƒ

- GoalForm์˜ textfield์— state์˜ title์ด ์ ํž ๊ฒƒ

- title์„ ์ˆ˜์ •ํ•˜๊ณ  ๋‹ค์‹œ ๋“ฑ๋ก๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๊ฐ™์€ key์˜ state๊ฐ€ ์—…๋ฐ์ดํŠธ ๋  ๊ฒƒ

 

 

๊ทธ๋ฆฌ๊ณ  ๊ธฐ์กด์— ์ž‘์„ฑํ•œ ๋ชฉํ‘œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณด๋ฉด InputBase๋กœ ์ ํ˜€์žˆ๋Š”๋ฐ ์ด๊ฒŒ ๊ทธ๋ƒฅ list ๊ฐ™์€ ํƒœ๊ทธ๊ฐ€ ์•„๋‹ˆ๋ผ์„œ

readOnly๊ฐ€ ํ•„์š”ํ–ˆ๋˜ ๊ฒƒ์ด์—ˆ๋‹ค.

 

๊ธฐ์กด์— ๋งŒ๋“ค์—ˆ๋˜ ์ˆ˜์ • ํ•จ์ˆ˜์™€ readOnly state๋ฅผ ์‚ญ์ œํ•˜๊ณ  ๋‹ค์‹œ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด ๋ณด์ž.

๊ทธ๋ฆฌ๊ณ  ListItem ์•ˆ์— InputBase๋ฅผ Label๋กœ ๋ฐ”๊ฟ”์„œ ์ฒดํฌ๋ฐ•์Šค๊ฐ€ ์ฒดํฌ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์ž.

 

 

 

 

์œ„์˜ Label ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ™•์ธํ•˜๊ณ  ๊ฐ™์€ ๋ชจ์–‘์œผ๋กœ Goals.js ๋ฅผ ๋ฐ”๊ฟ”์ค€๋‹ค.

Label์„ ์„ ํƒํ•œ ์ด์œ ๋Š” ๋‚˜์ค‘์— todomate์ฒ˜๋Ÿผ ๋ชฉํ‘œ๋ฅผ ํด๋ฆญํ•˜๋ฉด ์ฒดํฌ๋ฐ•์Šค๊ฐ€ ์ฒดํฌ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค.

label ์†์„ฑ์— item.title์ด ๋“ค์–ด๊ฐ€๋ฉด ๋˜๊ฒ ๋‹ค.

 

checked ์†์„ฑ์€ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณ€๊ฒฝํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— item.done์˜ ๊ฐ’๋„ checked๋กœ ๋ณ€๊ฒฝํ•ด์ค€๋‹ค.

 

 

<Checkbox
  checked={checked}
  onChange={handleChange}
  inputProps={{ 'aria-label': 'controlled' }}
/>

 

  // ๋ชฉํ‘œgoals ํŽ˜์ด์ง€ goal ์•„์ดํ…œ
  let [goal, goalChange] = useState(
     { 
       items: [{
       id: '0',
       title: '์ฒซ๋ฒˆ์งธ',
       done: 'checked',
        },
        { id: '1',
          title: '๋‘๋ฒˆ์งธ',
          done: '',
      }]
    }
  )

 

 

state๋ฅผ ์œ„์™€ ๊ฐ™์ด ๋ฐ”๊พธ๊ณ  Goals.js ์ปดํฌ๋„ŒํŠธ๋„ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณ€๊ฒฝํ–ˆ๋‹ค.

 

 

import React from "react";
import { Checkbox, FormControlLabel, FormGroup, 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);
  let goalItems = props.goal;
  let addGoal = props.addGoal;
  let deleteGoal = props.deleteGoal;
  let checkboxEventHandler = props.checkboxEventHandler;


  //๋ชฉํ‘œ ์‚ญ์ œ ์ด๋ฒคํŠธํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜

    function deleteEventHandler(e) {
      console.log( 'id : ',  e.target.id)
      deleteGoal(e.target.id, goalItems)
    }


  return (
    <FormGroup>
    <GoalForm addGoal={addGoal} />

        {
        goalItems.map((item, idx) => {
         
        return ( <ListItem className="goals-wrap" key={item.id} > { console.log('item: ', item) }
                    <ListItemText>
                    <FormControlLabel control={<Checkbox checked={item.done} onChange={checkboxEventHandler} />}
                    id={item.id}
                    name={item.id}
                    label={item.title}
                    fullWidth={true}
                    
                    />
                    </ListItemText>
                    <ListItemSecondaryAction>
                    <IconButton aria-label="Delete Todo" id={item.id} onClick={deleteEventHandler} sx={{ fontSize: '14px' }}>
                        ์‚ญ์ œ
                    </IconButton>
                    </ListItemSecondaryAction>
                </ListItem>
                );
            })
        }
    </FormGroup>
  );
}

 

 

๋ชจ์–‘์€ ์˜ˆ์˜๊ฒŒ ์ž˜ ๋‚˜์™”๋Š”๋ฐ... ์ •์‹ ์ฐจ๋ ค๋ณด๋‹ˆ ์šฐ๋ฆฌ๊ฐ€ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋Š” '๋ชฉํ‘œ' ๋Š” ๊ทธ์•ผ๋ง๋กœ ๋ชฉํ‘œ๊ณ  

์‹ค์ œ๋กœ๋Š” ์ฒดํฌ๋ฐ•์Šค์˜ ๊ธฐ๋Šฅ์„ ์ „ํ˜€ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์žˆ์—ˆ๋‹ค.

 

 

ํด๋ก ํ•˜๋Š” ์›๋ณธ์˜ ๊ธฐ๋Šฅ๋„ ์ œ๋Œ€๋กœ ํŒŒ์•…์„ ๋ชปํ–ˆ๋‹ค๋Š”๊ฒŒ ๋„ˆ๋ฌด ์–ด์ด์—†์—ˆ๋‹ค.

 

 

์œ„์˜ Goals.js ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ๋ณ€๊ฒฝํ•ด๋ณด์ž

 

 

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

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

  /* ํ•จ์ˆ˜ ์‹œ์ž‘ */

  //๋ชฉํ‘œ ์‚ญ์ œ ์ด๋ฒคํŠธํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜

    function deleteEventHandler(e) {
      console.log( 'id : ',  e.target.id)
      deleteGoal(e.target.id, goalItems)
    }

/* ํ•จ์ˆ˜ ๋ */


  return (
    <>
    <GoalForm addGoal={addGoal} />
    <List>
        {
        goalItems.map((item, idx) => {
         
        return ( <ListItem className="goals-wrap" id={item.id} key={item.id}  > 
                    <ListItemText>{item.title} </ListItemText>
                    <IconButton aria-label="Delete Todo" id={item.id} onClick={deleteEventHandler} sx={{ fontSize: '14px' }}>
                        ์‚ญ์ œ
                    </IconButton>
                </ListItem>
                );
            })
        }
    </List>
    </>
  );
}

 

 

์ด๋ ‡๊ฒŒ ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌํ•ด์ฃผ์—ˆ๋‹ค.

์‚ญ์ œ ๋ฒ„ํŠผ์˜ onClick ํ•ธ๋“ค๋Ÿฌ๋Š” ๊ทธ๋Œ€๋กœ ์ž‘๋™ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†๊ณ , props๋กœ ๋‚ด์šฉ์€ ์ž˜ ๊ฐ€์ง€๊ณ  ์˜ค๊ณ  ์žˆ์œผ๋‹ˆ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

Goal state์—์„œ done์„ ์‚ญ์ œํ•ด์ฃผ๋ฉด ๋˜๊ฒ ๋‹ค!

 

 

  // ๋ชฉํ‘œgoals ํŽ˜์ด์ง€ goal ์•„์ดํ…œ
  let [goal, goalChange] = useState(
     { 
       items: [{
       id: '0',
       title: '์ฒซ๋ฒˆ์งธ',
        },
        { id: '1',
          title: '๋‘๋ฒˆ์งธ',
      }]
    }
  )

 

 

๊ทธ๋Ÿผ ์ž…๋ ฅ๋„ ์ •์ƒ์ ์œผ๋กœ ๋  ์ˆ˜ ์žˆ๋„๋ก addGoal ํ•จ์ˆ˜๋„ ์ˆ˜์ •ํ•œ๋‹ค.

item.done์ด ์ƒ์„ฑ๋˜์ง€ ์•Š๋„๋ก ์ง€์›Œ์ฃผ๊ณ , readOnly ๊ด€๋ จ ํ•จ์ˆ˜๋„ ์‚ญ์ œํ•ด App.js ๋ฅผ ์ „์ฒด ์ˆ˜์ •ํ•œ๋‹ค.

 

 

 

import React, { useState } from "react";
import "../stylesheets/App.css";
import { Button  } from "@mui/material";
import { ThemeProvider, createTheme } from "@mui/material/styles";
import { Switch, Route, useHistory } from "react-router-dom";
import Signin from "./Signin";
import Join from "./Join";
import BasicNavBar from "./BasicNavBar";
import Goals from "./Goals";
import Main from "./Main";


const theme = createTheme({
  components: {
    MuiTypography: {
      defaultProps: {
        variantMapping: {
          h1: 'h2',
          h2: 'h2',
          h3: 'h2',
          h4: 'h2',
          h5: 'h2',
          h6: 'h2',
          subtitle1: 'h2',
          subtitle2: 'h2',
          body1: 'span',
          body2: 'span',
        },
      },
    },
  },
  typography: {
    Link: {
      color: '#222',
    },
    bolder: {
      fontWeight: 600,
    },
    button: {

    },
  },
  palette: {
    btn: {
      main: "#f2f2f2",
      contrastText: "#111",
    },
    basicnav: {
      main: "#fefefe",
      contrastText: "#111",
    },
    mainnav: {
      main: '#FAFAFA'
    },
    main: {
      main: '#e4f2b4'
    },
    content: {
      main: '#23fa43'
    },
    explore: {
      main: '#2a5dc9'
    },
    feed: {
      main: '#f66a55'
    },
    calendar: {
      main: '#9f4cd6'
    },
  },
});

function App() {
  
  /* Hook ์„ ์–ธ ์‹œ์ž‘ */
  
  let history = useHistory();

  /* Hook ์„ ์–ธ ๋ */


  /* Dummy State ์‹œ์ž‘ */

  // ๋ชฉํ‘œgoals ํŽ˜์ด์ง€ goal ์•„์ดํ…œ
  let [goal, goalChange] = useState(
     { 
       items: [{
       id: '0',
       title: '์ฒซ๋ฒˆ์งธ',
        },
        { id: '1',
          title: '๋‘๋ฒˆ์งธ',
      }]
    }
  )

  /* Dummy State ๋ */


  /* ํ•จ์ˆ˜ ์„ ์–ธ ์‹œ์ž‘ */


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

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

  //๋ชฉํ‘œ ์‚ญ์ œ ํ•จ์ˆ˜
  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})
  }

  /* ํ•จ์ˆ˜ ์„ ์–ธ ๋ */


  return (
    <ThemeProvider theme={theme}>
      <Switch>
        <Route exact path="/">
          <div className="App">
            <div className="header">
              <img className="main_img" src="images/todomate.jpg" />
              <h1>todo mate</h1>
              <h3>ํ•  ์ผ์„ ์ž‘์„ฑ, ๊ณ„ํš, ๊ด€๋ฆฌํ•˜์„ธ์š”.</h3>
            </div>
            <div className="start_btn">
              <Button 
                className="join_btn" 
                color="btn"
                variant="contained" 
                sx={{ boxShadow: 'none'}}
                onClick={() => {
                  history.push("/join");
                }}
              >
                ๊ฐ€์ž…ํ•˜๊ธฐ
              </Button>
              <Button
                className="signin_btn"
                color="btn"
                variant="contained"
                sx={{ boxShadow: 'none'}}
                onClick={() => {
                  history.push("/signin");
                }}
              >
                ๋กœ๊ทธ์ธ
              </Button>
            </div>
          </div>
        </Route>
        <Route exact path="/main">
          <Main/>
        </Route>
        <Route exact path="/join">
        <BasicNavBar/>
          <Join />
        </Route>
        <Route exact path="/signin">
        <BasicNavBar/>
          <Signin />
        </Route>
        <Route exact path="/goals">
        <BasicNavBar/>
          <Goals goal={goal.items} addGoal={addGoal} deleteGoal={deleteGoal} />
        </Route>
        </Switch>
    </ThemeProvider>
  );
}

export default App;

 

 

 

์ง„์ž‘ ์ด๋ ‡๊ฒŒ ํ• ๊ฑธ ^^

 

 

๊ทธ๋Ÿผ ์ด์ œ ๋“œ๋””์–ด ์ˆ˜์ •์„ ๊ตฌํ˜„ํ•ด๋ณด์ž;

 

728x90