2022. 1. 13. 00:25ใWEB Dev/ToyProject
๐ท 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;
์ง์ ์ด๋ ๊ฒ ํ ๊ฑธ ^^
๊ทธ๋ผ ์ด์ ๋๋์ด ์์ ์ ๊ตฌํํด๋ณด์;
'WEB Dev > ToyProject' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[ToyProject-Todomate] ํฌ๋๋ฉ์ดํธ ํด๋ก ํ๋ก์ ํธ 27 (0) | 2022.01.15 |
---|---|
[ToyProject-Todomate] ํฌ๋๋ฉ์ดํธ ํด๋ก ํ๋ก์ ํธ 26 (0) | 2022.01.13 |
[ToyProject-Todomate] ํฌ๋๋ฉ์ดํธ ํด๋ก ํ๋ก์ ํธ 24 (0) | 2022.01.11 |
[ToyProject-Todomate] ํฌ๋๋ฉ์ดํธ ํด๋ก ํ๋ก์ ํธ 23 (0) | 2022.01.06 |
[ToyProject-Todomate] ํฌ๋๋ฉ์ดํธ ํด๋ก ํ๋ก์ ํธ 22 (0) | 2022.01.05 |