ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [ToyProject-Todomate] ํˆฌ๋‘๋ฉ”์ดํŠธ ํด๋ก  ํ”„๋กœ์ ํŠธ 35
    WEB Dev/ToyProject 2022. 2. 8. 15:44
    728x90

    728x90

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

     

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

     


     

     

    ํ•  ์ผ(to-do) ๋งŒ๋“ค๊ธฐ๋ฅผ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ๋‹ค.

    ์ €๋ฒˆ์ฃผ ํ›„๋ฐ˜๋ถ€ํ„ฐ ์ง‘์ค‘ํ•˜๊ณ  ์žˆ๋Š”๋ฐ ๋ชฉํ‘œ์— ๋งž๊ฒŒ ํ•  ์ผ ๊ฐ’์„ ๋ฟŒ๋ ค ์ฃผ๋Š” ๊ฒƒ์ด ์ƒ๊ฐ๋ณด๋‹ค ์–ด๋ ค์› ๋‹ค. 

    ์—ฌ๋Ÿฌ๋ชจ๋กœ ์ค‘์‹ฌ์ด ๋˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค๋ณด๋‹ˆ state ๋‹ค๋ฃจ๋Š” ๊ฒƒ๋„ ๋ญ”๊ฐ€ ๋” ๊ณ ๋„ํ™” ๋œ ๊ธฐ๋ถ„์ด๋‹ค.

     

     

     

    1. Feed์— ๋ชฉํ‘œ ๋ฟŒ๋ฆฌ๊ธฐ

     

    atom์œผ๋กœ ์„ค์ •ํ•ด ๋‘” goal ๋”๋ฏธ ๋ฐ์ดํ„ฐ๋ฅผ Main - Feed ์—์„œ๋„ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

     

     

    export const goalsData = atom({ // ๋กœ๋”ฉ ์‹œ ๋ชจ๋“  ๋ชฉํ‘œ ๋ฟŒ๋ฆฌ๊ธฐ
      key: "goalsData",
      default: [
        {
          "goal_id": 0,
          "next_goal_id": 1,
          "title": "์ฒซ ๋ฒˆ์งธ ๋ชฉํ‘œ",
          "privacy": "PUB",
          "box_color": "",
          "title_color": "#3CB371"
        },
        {
          "goal_id": 1,
          "next_goal_id": 2,
          "title": "๋‘ ๋ฒˆ์งธ ๋ชฉํ‘œ",
          "privacy": "PRI",
          "box_color": "",
          "title_color": "#C71585"
        }
      ]
    })

     

     

    import { useRecoilState, useRecoilValue } from "recoil";
    
    import { goalsData } from "../atoms/todoData";
    
    export default function Feed() {
    
    
    /* Hook ์„ ์–ธ ์‹œ์ž‘ */
    
    /* atom ์‹œ์ž‘ */
        let goal = useRecoilValue(goalsData);
        
        
        return ();
        
     }

     

     

    useRecoilState๋ฅผ ์“ฐ์ง€ ์•Š๊ณ  useRecoilValue๋ฅผ ์“ด ์ด์œ ๋Š” ๋ชฉํ‘œ์˜ ์ƒ์„ฑ, ์ˆ˜์ •, ์‚ญ์ œ๋Š” ๋ณ„๋„์˜ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ง„ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 

    useRecoilValue๋กœ ๊ฐ’๋งŒ ๋ฐ›์•„์™€ ๋ฟŒ๋ ค์ค€ ๋‹ค์Œ ํŽ˜์ด์ง€์— ๋งˆํฌ์—… ๋  ์ˆ˜ ์žˆ๋„๋ก goal.map์œผ๋กœ ํ™”๋ฉด์— ๋ฟŒ๋ ค์ค€๋‹ค.

    ๊ทธ๋ฆฌ๊ณ  ํด๋ฆญํ•˜๋ฉด ๊ธฐ๋Šฅ๋“ค์ด ๋™์ž‘ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— <Button> UI ์ปดํฌ๋„ŒํŠธ๋กœ ๊ฐ์‹ธ์ค€๋‹ค.

     

    return (
            <Box className='feed-box'>
            <h2 className="feed-title">Feed</h2>
                <Box className="feed-goals-list-box">
                    <List className="goals-list-wrap" >
                        {
                        goal.map((goal, index) => {
                        return ( <ListItem className="goals-listItem" id={goal.goal_id} key={goal.goal_id} > 
                            <Button className="goals-listItem-text-wrap" isselected={isGoalSelected[index]} id={goal.goal_id} onClick={(e)=>{(clickTodoHandler(e))}} data-index={index} >
                            <LibraryBooksIcon className="goals-listItem-icon" />
                                    <ListItemText className="goals-listItem-text" id={goal.goal_id} name={goal.goal_id} sx={{ color:goal.title_color }}  >{goal.title}</ListItemText>
                                <ListItemText className="goals-listItem-add-icon" ><span>+</span></ListItemText>
                            </Button>
                            )
                         }   
                        )
                       }
                       
                    </List>
                </Box>
            </Box>
            
            </>
       )

     

     

     

     

     

     

    2. ๊ฐœ๋ณ„ ๋ชฉํ‘œ ํด๋ฆญํ–ˆ์„ ๋•Œ ํ•  ์ผ ์ž…๋ ฅ

     

    ํ™”๋ฉด์— ๋ฟŒ๋ ค์ง„ ๋ชฉํ‘œ๋ฅผ ํด๋ฆญํ–ˆ์„ ๋•Œ ํ•ด๋‹น ํด๋ฆญ ๋ถ€๋ถ„ ํ•˜๋‹จ์— textfield๊ฐ€ ๋‚˜ํƒ€๋‚˜๊ณ 

    ํ•  ์ผ์„ ์ž…๋ ฅํ–ˆ์„ ๋•Œ ์ƒˆ๋กœ์šด todo state๊ฐ€ ๋งŒ๋“ค์–ด ์งˆ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

    ์—ฌ๊ธฐ์„œ๋Š” InputBase๋ฅผ ์‚ฌ์šฉํ•  ๊ฑด๋ฐ ๋ชฉํ‘œ ๋งŒ๋“ค ๋•Œ์ฒ˜๋Ÿผ ๊ฒฝํ—˜ํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ readonly์†์„ฑ์— ๋”ฐ๋ผ true์ผ ๋•Œ๋Š” ์ˆ˜์ •์ด ์•ˆ๋˜๊ณ  false์ผ ๋•Œ๋Š” ์ˆ˜์ •์ด ๊ฐ€๋Šฅํ•˜๋„๋กํ•˜๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค. 

     

    ๋ชฉํ‘œ๋ฅผ ํด๋ฆญํ•˜๋ฉด CreateInput์ด๋ผ๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ชฉํ‘œ ์•„๋ž˜ ๋ถ™์„ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด ๋ชฉํ‘œ์— onClick ์ด๋ฒคํŠธ๋ฅผ ์ƒ์„ฑํ–ˆ๊ณ  CreateInput ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ•˜๋‹จ์— ๋ณ„๋„๋กœ ๋งŒ๋“ค์—ˆ๋‹ค.

    ์ด CreateInput ์ปดํฌ๋„ŒํŠธ์—์„œ ํ•  ์ผ์„ ์ž…๋ ฅํ•˜๊ณ  ์—”ํ„ฐ๋ฅผ ์น˜๋ฉด (submit ๋ฒ„ํŠผ ๋…ธ์ถœํ•˜์ง€ ์•Š์Œ)

    ์•„๋ž˜ createTodoState์— ๋‹ด๊ธฐ๊ณ  react form hook์„ ํ†ตํ•ด ์ œ์ถœ๋˜๊ฒŒ ๋œ๋‹ค.

     

    // input ์ƒ์„ฑ
    export function CreateInput(props) {
    
    
        let id = props.id;
        let defaultDate = new Date().toJSON().substring(0,10);
        const isGoalSelected = props.isGoalSelected;
        const setIsGoalSelected = props.setIsGoalSelected;
        console.log("isGoalSelected" , isGoalSelected)
        /* Hook ์„ ์–ธ ์‹œ์ž‘ */
    
        /* atom ์‹œ์ž‘ */
        const { register, handleSubmit, errors, watch } = useForm({ mode: "onChange" });
        let [todo, setTodo] = useRecoilState(todoData);
        let [createTodoState, setCreactTodoState] = useState({
            "todo_id": "",
            "goal_id": "", //๋ฌถ์—ฌ์žˆ๋Š” goal id
            "next_todo_id": "", //๋‹ค์Œ todo id (์ˆœ์„œ์ง€์ •์šฉ)
            "title": "",
            "date": "",
            "end_repeat_date": "", //๋ฐ˜๋ณต ์ข…๋ฃŒ ์ผ์ž. ๋ฐ˜๋ณต ์—†์œผ๋ฉด date ์™€ ๊ฐ’์ด ๊ฐ™๊ฑฐ๋‚˜ ์—†์Œ
            "repeat_days": {
                "sun": "N", //y ๋ฉด ์ผ์š”์ผ ๋ฐ˜๋ณต, n ์ด๋ฉด ๋ฐ˜๋ณต x
                "mon": "N",
                "tue": "N",
                "wed": "N",
                "thu": "N",
                "fri": "N",
                "sat": "N",
            },
            "check_yn" : "N" //๋‹ฌ์„ฑ์—ฌ๋ถ€
          });
    
        /* Hook ์„ ์–ธ ๋ */
    
        /* ํ•จ์ˆ˜ ์„ ์–ธ ์‹œ์ž‘ */
    
        //input ํ•จ์ˆ˜
        const onInputChange = (e) => {
            setCreactTodoState({...createTodoState, title: e.target.value})
            // console.log(createTodoState)
        }
        
        //todo ์ถ”๊ฐ€ ํ•จ์ˆ˜
        const addTodo = (data, id) => {
            const copy_todo_state = [...todo];
            //์ถ”๊ฐ€๋˜๋Š” state 'todo_id', 'goal_id', 'next_todo_id', 'date', 'end_repeat_date'
            // 'repeat_days'์™€ 'check_yn'์€ default ๊ฐ’ "N" , 
            data.todo_id = copy_todo_state.length;
            data.goal_id = parseInt(id); //key๋ฅผ ์œ„ํ•œ id ์ถ”๊ฐ€
            data.next_todo_id = copy_todo_state.length+1;
            data.date = defaultDate;
            data.end_repeat_date = defaultDate;
            copy_todo_state.push(data);
            setTodo(copy_todo_state, console.log(copy_todo_state))
        }
        
        // console.log(goal)//react-form-hook submit ํ•จ์ˆ˜
        const onSubmit =  (data) => { 
            console.log(data.goal_id)
            const index = data.goal_id
            const newArr = [...isGoalSelected];
            setIsGoalSelected(newArr[index] = false)
             setCreactTodoState(JSON.stringify(createTodoState));
             addTodo(createTodoState, index);
        }
    
        const onError = (error) => {
            console.log(error);
            };
    
    
    
        /* ํ•จ์ˆ˜ ์„ ์–ธ ๋ */
    
        return  (
                <Box className="goals-todo-input-create-Box">
                    <form onSubmit={handleSubmit(onSubmit,onError)}>
                        <div className="goals-todo-input-create-wrap">
                        <CheckBoxOutlineBlankIcon className="goals-todo-input-create-check-icon"/>
                        <InputBase {...register("title")} id="todo-input" className="goals-todo-input-create-field"  placeholder="ํ•  ์ผ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”." onChange={onInputChange} /> 
                        <InputBase {...register("goal_id")} id="todo-goal-id-input" className="todo-goal-create-id" type="hidden" value={id} /> 
                        <Button type="submit" className="goals-todo-input-btn"><MoreHorizIcon className="goals-todo-list-input-btn-icon" /></Button>
                        </div>
                    </form>
                </Box>
                )
    }

     

     

     

    ์—ฌ๊ธฐ์„œ ๋งต์œผ๋กœ ๋ฟŒ๋ ค์ง€๋Š” ํƒœ๊ทธ๋ฅผ ๊ฐœ๋ณ„ ์„ ํƒํ•˜๋Š” ํด๋ฆญ ์ด๋ฒคํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ ๊ต‰์žฅํžˆ ์• ๋ฅผ ๋จน์—ˆ๋‹ค. 

    ๊ทธ๋Ÿฌ๋‹ค ์•„๋ž˜ ๋ธ”๋กœ๊ทธ์˜ ๋„์›€์„ ๊ต‰์žฅํžˆ ๋งŽ์ด ๋ฐ›์•˜๋‹ค.

     

     

     

     

    react) mapํ•จ์ˆ˜ ํด๋ฆญ ์ด๋ฒคํŠธ์—์„œ ํ•œ๊ฐœ์˜ ๊ฐ’๋งŒ ์Šคํƒ€์ผ ๋ณ€๊ฒฝํ•˜๊ธฐ

    ์ด์ „์—๋Š” ํด๋ฆญ์‹œ ์Šคํƒ€์ผ๋งŒ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ž‘์„ฑํ•˜์˜€๋‹ค. ํ•˜์ง€๋งŒ ํ™ˆํŽ˜์ด์ง€๋ฅผ ๋ณด๋ฉด ํ•˜๋‚˜๋งŒ ํด๋ฆญํ–ˆ์„์‹œ ํ•˜๋‚˜๋งŒ ๋ถˆ์ด ๋“ค์–ด์˜จ๋‹ค๊ฑฐ๋‚˜ ํ•˜๋‚˜๋งŒ ์Šคํƒ€์ผ์ด ๋ณ€๊ฒฝ๋˜์–ด์•ผ ํ•œ๋‹ค.

    velog.io

     

     

    1. ๋ชฉํ‘œ ๋ฐฐ์—ด๋งŒํผ boolean ๊ฐ’์œผ๋กœ ์ฑ„์›Œ์ง„ ๋ฐฐ์—ด ์ƒ์„ฑํ•˜๊ธฐ

    let [isGoalSelected, setIsGoalSelected] = useState(Array(goal.length).fill(false) );

    ๋กœ ๋ณ„๋„์˜ ๋ฐฐ์—ด์„ ํ•˜๋‚˜ ์ƒ์„ฑํ•ด์ค€๋‹ค. 

     

     

    2. ๊ฐœ๋ณ„ ๋ชฉํ‘œ ๋ฒ„ํŠผ์— ํ• ๋‹นํ•  ์ด๋ฒคํŠธ ์ƒ์„ฑ

    <Button className="goals-listItem-text-wrap" isselected={isGoalSelected[index]} id={goal.goal_id} onClick={(e)=>{(clickCreateTodoHandler(e))}} data-index={index} ></Button>
    //๋ชฉํ‘œ ํด๋ฆญ ์‹œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
    const clickCreateTodoHandler = (e) => {
        const index = e.currentTarget.dataset.index;
        const newArr = Array(goal.length).fill(false) ;
        newArr[index] = true;
        setIsGoalSelected(newArr)
    }

    ์œ„์™€ ๊ฐ™์ด ๊ฐœ๋ณ„ ๋ชฉํ‘œ ๋ฒ„ํŠผ์— onClick ์ด๋ฒคํŠธ๋ฅผ ๋„ฃ์–ด์ค€ ๋‹ค์Œ ํด๋ฆญ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.

    ๊ฐœ๋ณ„ Button์— dataset์œผ๋กœ ์„ค์ •๋œ index ๊ฐ’์„ ๋ฐ›์•„์˜ค๊ณ  (goal ๋ฐฐ์—ด์˜ ์ธ๋ฑ์Šค์™€ ๊ฐ™์Œ)

    newArr๋กœ ๊ธฐ์กด ๋ฐฐ์—ด๊ณผ ๊ฐ™์€ ๋ฐฐ์—ด์„ ์ƒ์„ฑํ•œ ๋‹ค์Œ ํ•ด๋‹น ์ˆœ์„œ์˜ false ๊ฐ’์„ true๋กœ ๋ฐ”๊ฟ”์ค€๋‹ค์Œ isGoalSelected ๊ฐ’์„ ๋ณ€๊ฒฝํ•ด์ค€๋‹ค. (์ด ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ์“ธ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— recoil์— ๋„ฃ์ง€ ์•Š๊ณ  useState๋กœ ์‚ฌ์šฉํ–ˆ๋‹ค.

     

    3. ์กฐ๊ฑด๋ฌธ์œผ๋กœ CreateInput ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ

     

    ํ•ด๋‹น ์ธ๋ฑ์Šค์˜ isGoalSelected๊ฐ€ true ์ธ ๊ฒฝ์šฐ CreateInput ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ๋„๋ก ์กฐ๊ฑด๋ฌธ์„ ์„ค์ •ํ•œ๋‹ค.

     

     {isGoalSelected[index] ? <CreateInput />: null}

     

    ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ•ด๋‹น ์ธ๋ฑ์Šค ๋ถ€๋ถ„์˜ isGoalSelected์˜ ๊ฐ’์— ๋”ฐ๋ผ CreateInput ์ด ๋“ฑ์žฅํ•˜๊ฒŒ ๋œ๋‹ค.

     

     

     

     

    3. todo state ๊ฐ ๋ชฉํ‘œ์— ๋งž๊ฒŒ ๋ฟŒ๋ฆฌ๊ธฐ

     

     

    ์ด์ œ ์ƒ์„ฑ๋œ ํ•  ์ผ ๋“ค์„ ๋ชฉํ‘œ ์•„๋ž˜์— ๋…ธ์ถœ๋˜๋„๋ก ํ•ด์ค€๋‹ค.

    ๋ชฉํ‘œ์˜ id์™€ ํ•  ์ผ์ด ๊ฐ€์ง€๊ณ  ์žˆ๋Š” goal_id๊ฐ€ ๋งค์นญ์ด ๋˜์–ด์•ผ ํ•˜๊ณ  ํ•ด๋‹น ๋ชฉํ‘œ ์•„๋ž˜์— ํ•  ์ผ์ด ๋ฟŒ๋ ค์ ธ์•ผ ํ•œ๋‹ค.

     

    ๋ชฉํ‘œ์™€ ๋™์ผํ•˜๊ฒŒ recoil๋กœ ๋”๋ฏธ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค๊ณ  atom์œผ๋กœ ๊ฐ€์ง€๊ณ  ์˜จ๋‹ค.

     

    import { todoData } from "../atoms/todoData";
    
    export default function Feed() {
    
    
    /* Hook ์„ ์–ธ ์‹œ์ž‘ */
    
    /* atom ์‹œ์ž‘ */
    
        let todo = useRecoilValue(todoData);
        
        
        return (<>
            
            <Box className='feed-box'>
            <h2 className="feed-title">Feed</h2>
                <Box className="feed-goals-list-box">
                    <List className="goals-list-wrap" >
                        {
                        goal.map((goal, index) => {
                        return ( <ListItem className="goals-listItem" id={goal.goal_id} key={goal.goal_id} > 
                            <Button className="goals-listItem-text-wrap" isselected={isGoalSelected[index]} id={goal.goal_id} onClick={(e)=>{(clickCreateTodoHandler(e))}} data-index={index} >
                            <LibraryBooksIcon className="goals-listItem-icon" />
                                    <ListItemText className="goals-listItem-text" id={goal.goal_id} name={goal.goal_id} sx={{ color:goal.title_color }}  >{goal.title}</ListItemText>
                                <ListItemText className="goals-listItem-add-icon" ><span>+</span></ListItemText>
                            </Button>
                            {todo.map((todo,index)=>{
                              
                                return (
                                    <Box className="goals-todo-input-list-Box" key={index} onClick={clickTodoModalHandler} data-key={index}>
                                  
                                    {goal.goal_id === parseInt(todo.goal_id) ? (<>
                                    <div className="goals-todo-input-list-check-wrap">
                                        <CheckBoxOutlineBlankIcon className="goals-todo-list-input-check-icon"/>
                                        <InputBase key={`todo${index}`} inputProps={{readOnly: todoReadOnly[index].toString(),}} type="text" name={todo.title} id="todo-input" value={todo.title} onChange={todoEditEventHandler} className="goals-todo-list-input"></InputBase> 
                                        { 
                                    
                                     }
                                    </div>
                                    <Button className="goals-todo-list-input-btn" ><MoreHorizIcon className="goals-todo-list-input-btn-icon" /></Button>
                                    </>
                                    ) : null} 
                                    </Box>
                                )
                            })}
                            {isGoalSelected[index] ? <CreateInput id={goal.goal_id} isGoalSelected={isGoalSelected} setIsGoalSelected={setIsGoalSelected} />: null}
                        </ListItem>
                                )
                            })
                        }
                        
    
                    </List>
                </Box>
            </Box>
    
            {/* ๋ชจ๋‹ฌ ์ƒ์„ฑ */}
             <TodoModal modalOpen={modalOpen} handleTodoModalClose={handleTodoModalClose}selectedTodo={selectedTodo} clickTodoEditHandler={clickTodoEditHandler} clickTodoDeleteHandler={clickTodoDeleteHandler} selectedInputIndex={selectedInputIndex}  />
            </>
        );
    }

     

     

    ๊ทธ๋ฆฌ๊ณ  goal.map ์•ˆ์— todo๋ฅผ ๋ฟŒ๋ ค์ฃผ๊ฒŒ ๋˜๋Š”๋ฐ goal์˜ goal_id์™€ todo ์˜ goal_id๊ฐ€ ๊ฐ™์€ ๊ฒฝ์šฐ๋งŒ ์ถ”๋ ค์ ธ map์œผ๋กœ ๋ฟŒ๋ ค์งˆ ์ˆ˜ ์žˆ๊ฒŒ ํ•˜์˜€๋‹ค. 

     

     

    ์ด์ œ ๊ฐ ํ•  ์ผ์„ ํด๋ฆญํ•˜๋ฉด ๋ชจ๋‹ฌ์ด ์ƒ์„ฑ๋˜๊ณ  ํ•ด๋‹น ๋ชจ๋‹ฌ์—์„œ 5๊ฐ€์ง€ ๊ธฐ๋Šฅ (์ˆ˜์ •, ์‚ญ์ œ, ๋‚ด์ผ ํ•˜๊ธฐ, ๋‚ ์งœ ๋ฐ”๊พธ๊ธฐ, ์ˆœ์„œ๋ณ€๊ฒฝ) ๋‹ค์„ฏ ๊ฐœ์˜ ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•˜๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค.

     

    ๊ทธ๋Ÿฐ๋ฐ ์ด๊ฒŒ ์ƒ์„ฑ๋ณด๋‹ค ๋” ์–ด๋ ค์šด ๊ฑฐ ๊ฐ™์•„....

    728x90

    ๋Œ“๊ธ€