์ฝ๋์บ ํ
MDN
https://medium.com/w-bs-log/history-push%EC%99%80-replace%EC%9D%98-%EC%B0%A8%EC%9D%B4-ed5f2f7db7dc
https://www.zerocho.com/category/HTML&DOM/post/599d2fb635814200189fe1a7
https://e-juhee.tistory.com/entry/router-push-router-replace
โ ๋ผ์ฐํฐ(router) ๊ฐ์ฒด๋โ
ํ์ด์ง ์ด๋๊ณผ ๊ด๋ จ๋ ๊ธฐ๋ฅ์ ๊ฐ์ง๊ณ ์๋ ๊ฐ์ฒด๋ก, ๋ผ์ฐํ ์ด๋ ํ์ด์ง์ด๋์ด๋ค.
์ด ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด A ํ์ด์ง์์ B ํ์ด์ง๋ก ์ด๋ํ ๋, "B ํ์ด์ง๋ก ๋ผ์ฐํ ํ๋ค" ๊ณ ๋งํ๋ค.
ํ์ด์ง๋ฅผ ์ด๋ํ๊ธฐ ์ํด useRouter() ๋ผ๋ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๋ค.const router = useRouter() const rounter.push("์ด๋ํ์ด์ง")
โ ์ ์ ๋ผ์ฐํ
login ํ์ด์ง๋ ๋๊ฐ ์ธ์ ์ ์ํด๋ ํญ์ ๋ก๊ทธ์ธ ํ์ด์ง๊ฐ ๋์ต๋๋ค.
์ด๋ฌํ ํ์ด์ง๋ก ์ด๋ํ๋ ๊ฒ์"์ ์ ๋ผ์ฐํ ํ๋ค"
๊ณ ํ๋ค.
โผ ์ค์ต
- ํ์ด์ง ์กฐํํ๊ธฐ
//05-01-static-routing import { useRouter } from "next/router"; export default function StaticRoutingPage() { const router = useRouter(); const onClickMove = () => { router.push("/section05/05-01-static-routing-moved"); }; return <button onClick={onClickMove}>ํ์ด์ง ์ด๋</button>; } ; // 05-01-static-routing-moved export default function StaticRoutingPage() { return <div>์ด๋์๋ฃ</div>; }
- ํ์ด์ง ์ด๋ํ๊ธฐ
//05-02-static-routing-board import { useRouter } from "next/router"; export default function StaticRoutingPage() { const router = useRouter(); const onClickMove1 = () => { router.push("/section05/05-02-static-routing-board-moved/1"); }; const onClickMove2 = () => { router.push("/section05/05-02-static-routing-board-moved/2"); }; const onClickMove3 = () => { router.push("/section05/05-02-static-routing-board-moved/3"); }; return ( <div> <button onClick={onClickMove1}>1๋ฒ ๊ฒ์๊ธ ์ด๋</button>; <button onClick={onClickMove2}>2๋ฒ ๊ฒ์๊ธ ์ด๋</button>; <button onClick={onClickMove3}>3๋ฒ ๊ฒ์๊ธ ์ด๋</button>; </div> ); }; //05-02-static-routing-board-moved/1 export default function StaticRoutingPage() { return <div>1๋ฒ ์ด๋์๋ฃ</div>; } //05-02-static-routing-board-moved/2 export default function StaticRoutingPage() { return <div>2๋ฒ ์ด๋์๋ฃ</div>; } //05-02-static-routing-board-moved/3 export default function StaticRoutingPage() { return <div>3๋ฒ ์ด๋์๋ฃ</div>; }
- ํ์ด์ง ์ ํํ๊ธฐ
//05-03-static-routing-board-query import { useRouter } from "next/router"; export default function StaticRoutingPage() { const router = useRouter(); const onClickMove1 = () => { router.push("/section05/05-03-static-routing-board-query-moved/1"); }; const onClickMove2 = () => { router.push("/section05/05-03-static-routing-board-query-moved/2"); }; const onClickMove3 = () => { router.push("/section05/05-03-static-routing-board-query-moved/3"); }; return ( <div> <button onClick={onClickMove1}>1๋ฒ ๊ฒ์๊ธ ์ด๋</button>; <button onClick={onClickMove2}>2๋ฒ ๊ฒ์๊ธ ์ด๋</button>; <button onClick={onClickMove3}>3๋ฒ ๊ฒ์๊ธ ์ด๋</button>; </div> ); } //05-03-static-routing-board-query-moved/2 import { useQuery, gql } from "@apollo/client"; const FETCH_BOARD = gql` query { fetchBoard(number: 2) { number writer title contents } } `; export default function StaticRoutingPage() { const { data } = useQuery(FETCH_BOARD); return ( <div> <div>2๋ฒ ์ด๋์๋ฃ</div> <div>์์ฑ์ : {data.fetchBoard.writer}</div> <div>์ ๋ชฉ : {data.fetchBoard.title}</div> <div>๋ด์ฉ : {data.fetchBoard.contents}</div> </div> ); }
โ ๋์ ๋ผ์ฐํ
๊ฒ์ํ ์์ธ๋ณด๊ธฐ์ ๊ฐ์ ๊ฒฝ์ฐ ๊ธ ๋ฒํธ์ ๋ฐ๋ผ ์ฃผ์๊ฐ ๋ณ๊ฒฝ๋๋ค.
๋ง์ฝ ๊ฒ์๊ธ์ด 1000๊ฐ๊ฐ ๋์ด๊ฐ๊ฒ ๋๋ฉด ๊ฐ๊ฐ์ ๊ธ ๋ฒํธ์ ๋ฐ๋ผ ํ์ด์ง๋ฅผ 1000๊ฐ์ฉ ๋ง๋ค์ด ์ ์ ๋ผ์ฐํ ์ ํด์ฃผ๊ธฐ๋ ์ด๋ ต๊ธฐ ๋๋ฌธ์,
ํจ๊ณผ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ธฐ ์ํด์ ๋์ ๋ผ์ฐํ ์ ์ฌ์ฉํ๋ค.
/board/1 โ 1๋ฒ ๊ฒ์๊ธ ์์ธ๋ณด๊ธฐ ํ์ด์ง
/board/2 โ 2๋ฒ ๊ฒ์๊ธ ์์ธ๋ณด๊ธฐ ํ์ด์ง
/board/3 โ 3๋ฒ ๊ฒ์๊ธ ์์ธ๋ณด๊ธฐ ํ์ด์ง
/board/4 โ 4๋ฒ ๊ฒ์๊ธ ์์ธ๋ณด๊ธฐ ํ์ด์ง
next.js์์๋ ๋์ ๋ผ์ฐํ ์ ์ ๊ณตํด์ฃผ๊ณ ์๋ค.
์ ํด๋ ๊ตฌ์กฐ์ ๊ฐ์ด ๋ณด์ฌ์ฃผ๊ณ ์ ํ๋ ํด๋ ์ด๋ฆ์ ํ์ ํด๋๋ก
[boardId]ํด๋๋ฅผ ๋ง๋ค์ด ์ค ํ
์ด ์์ index.js ํ์ผ์ ๋ง๋ค์ด์ฃผ๋ฉด ๋์ ๋ผ์ฐํ ์ ์ฌ์ฉํ ์ ์๊ฒ ๋๋ค.
๋๊ดํธ๋ก ๊ฐ์ธ์ค ํด๋๋ฅผ ๋ง๋ค์ด์ฃผ๋ฉด ์ด๋ํด์ฃผ๊ณ ์ ํ๋ ํ์ด์ง ๋ฒํธ,
ํน์ ๊ฒ์๊ธ ๋ฒํธ๊ฐ ๋๊ดํธ ์์ ์ฐ์ฌ์ง ๋ณ์๋ช ์ ๋ด๊ฒจ์ ธ ๊ทธ ๋ณ์ ์์ ์๋ ๋ฐ์ดํฐ๋ฅผ ๊บผ๋ด ์กฐํํ ์ ์๋ค.
(์ด ๋, ๋๊ดํธ ์์ ์ฐ์ฌ์ง๋ ํด๋ ์ด๋ฆ์ ๋จ์ํ ๋ณ์๋ช ์ด๊ธฐ ๋๋ฌธ์ ์ด๋ป๊ฒ ์์ฑํด๋ ์๊ด์๋ค.)
์ด๋ฌํ ๊ณผ์ ์ router ๊ฐ์ฒด๊ฐ ๋์์ฃผ๋ ๊ฒ!
์ค์ ๋ก router.query = { boardId: 1 } ์ด๋ฐ ํ์์ผ๋ก ๋ค์ด๊ฐ๊ฒ ๋๋ฉด์ ์๋์ผ๋ก ๊ฒ์๊ธ ๋ฒํธ๋ฅผ ๊บผ๋ด์ฌ ์ ์๋ค.
โผ ์ค์ต[qqq] import { useQuery, gql } from "@apollo/client"; import { useRouter } from "next/router"; //playground์์ ์ฐ์ตํ๊ณ ๋ณต๋ถ const FETCH_BOARD = gql` query fetchBoard($number: Int) { fetchBoard(number: $number) { number writer title contents } } `; export default function StaticRoutingPage() { const router = useRouter(); console.log(router); const { data } = useQuery(FETCH_BOARD, { variables: { number: Number(router.query.qqq), //"๋ฌธ์์ด" -> ์ซ์ }, }); console.log(data); //์ ๋ฌ๋์ง ์กฐํํ๊ธฐ return ( <div> <div>2๋ฒ ์ด๋์๋ฃ</div> <div>์์ฑ์ : {data && data?.fetchBoard?.writer}</div> <div>์ ๋ชฉ : {data?.fetchBoard?.title}</div> <div>๋ด์ฉ : {data ? data?.fetchBoard?.contents : "๋ก๋ฉ์ค์ ๋๋ค"}</div> </div> ); }
โ ์์ฃผ ์ฌ์ฉํ๋ ๋ผ์ฐํฐ(router) ๊ฐ์ฒด ๊ธฐ๋ฅ
import Router from 'next/router' export default function Routing() { const handleClickPathname = () => { const pathname = Router.pathname alert(pathname) } const handleClickAsPath = () => { const asPath = Router.asPath alert(asPath) } const handleClickBack = () => { Router.back() } const handleClickPush = () => { Router.push('/') } const handleClickReload = () => { Router.reload() } const handleClickReplace = () => { Router.replace('/') } return ( <> <button onClick={handleClickPathname}>ํ์ฌ ์์น ์ฃผ์: Router.pathname</button><br/> <button onClick={handleClickAsPath}>ํ์ฌ ์์น ์ฃผ์: Router.asPath</button><br/> <button onClick={handleClickBack}>๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ: Router.back()</button><br/> <button onClick={handleClickPush}>ํ์ฌ ํ์ด์ง์์, ๋ค๋ฅธ ํ์ด์ง๋ก ์ด๋: Router.push()</button><br/> <button onClick={handleClickReload}>์๋ก๊ณ ์นจ: Router.reload()</button><br/> <button onClick={handleClickReplace}>ํ์ฌ ํ์ด์ง ์ญ์ ํ, ๋ค๋ฅธ ํ์ด์ง๋ก ์ด๋: Router.replace()</button><br/> </> ) }
-> ๋๊ดํธ๊ฐ ์๋ ํด๋๋ฅผ ๋ค์ด๋๋ฏน ๋ผ์ฐํ ํด๋, ๋๊ดํธ๊ฐ ์๋ ํด๋๋ ์ ์ ๋ผ์ฐํ
๋ก๊ทธ์ธ ์์1. ๋ผ์ฐํฐ ๊ฐ์ฒด์ push์ replace ์ ์ฐจ์ด์ ์ฌ์ฉ ์์
๋ผ์ฐํ ๋ณ๊ฒฝ ์ ์์ฃผ ์ฌ์ฉ๋๋ ๋ฉ์๋ ์ค push์ replace๊ฐ ์๋ค.
์๋ฅผ ๋ค์ด HOME > Page1 > Page2 > Page3 ์์ผ๋ก ํ์ด์ง ์ด๋์ ํ๋ค๊ณ ๊ฐ์ ํด๋ณด์.
Page2์์ history.push()๋ฅผ ์ฌ์ฉํ๋ฉดimport React from 'react'; function Page2({ history }) { history.push('/Page3'); return <div>Page2</div>; } export default Page2;
HOME > Page1 > Page2 > Page3 ์์๋๋ก history์ ์์ฌ, ๋ง์ง๋ง Page3 ์์ ๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ์ ๋๋ฅด๊ฒ ๋๋ฉด Page2๋ก ๋์๊ฐ๊ฒ ๋๋ค.
๋๊ฐ์ ์ํฉ์์ history.replace()๋ฅผ ์ฌ์ฉํ๋ฉดimport React from 'react'; function Page2({ history }) { history.replace('/Page3'); return <div>Page2</div>; } export default Page2;
Home > Page1 > Page3 ์์ผ๋ก history์ ์์ฌ, ๋ง์ง๋ง Page3์์ ๋ค๋ก ๊ฐ๊ธฐ ๋ฒํผ์ ๋๋ฅด๋ฉด Page1๋ก ๋๋์๊ฐ๋ค.
์ฆ, history๋ฅผ ์คํ์ด๋ผ๊ณ ์๊ฐํ์ ๋ push๋ ์์ฌ ์๋ browser history ์์ ์๋ ๊ฒ์ด๊ณ , replace๋ ์์ฌ ์๋ history ์ ์ผ ์์ ์์์ ํ์ฌ ๋ฃ๋ ์์๋ฅผ ๋ง ๊ทธ๋๋ก replace(๋์ฒด)ํ๋ ๊ฒ์ด๋ค.
window.history๋ ์์๋ก ์ญ์ ํ ์ ์๋ค.
๋์ ์ history๋ฅผ ๋์ฒดํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์๋๋ฐ, ๋ฐ๋ก Router.replace ๊ฐ์ฒด ํจ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
import React from 'react';
function Login({ history }) {
history.push('/item');
return <div>Login</div>;
}
export default Login;
-> push๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ ํ์คํ ๋ฆฌ๊ฐ ์์๋๋ก ์์ฌ์, ๋ก๊ทธ์ธ์ ๋ค์ ํ์ด์ง์์ ๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ์ ๋๋ฅด๋ฉด ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋์๊ฐ๊ฒ ๋๋ค.
import React from 'react';
function Login({ history }) {
history.replace('/item');
return <div>Login</div>;
}
export default Login;
-> replace๋ฅผ ์ฌ์ฉํ๋ฉด history ์ ์ผ ์์ ์๋ ์์๋ฅผ ์ง๊ธ ๋ฃ์ ์์๋ก ๋ฐ๊ฟ์ค์, ๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ์ ๋๋ฅด๋ฉด ๋ก๊ทธ์ธ์ ์ ํ์ด์ง๋ก ๋์๊ฐ๊ฒ ๋๋ค.
replace๋ ํ์ด์ง๋ฅผ ์ด๋ํ๋ค๊ธฐ ๋ณด๋ค๋ ํ์ฌ ํ์ด์ง๋ฅผ ๋ฐ๊ฟ์ฃผ๋ ๊ฐ๋
์ด๋ค.
2. ๋ผ์ฐํฐ ๊ฐ์ฒด์ pathname๊ณผ asPath์ ์ฐจ์ด์ : ํด๋๊ตฌ์กฐ์ ํ์ด์ง์ฃผ์(์ค์ ์ฃผ์)
https://velog.io/@e_juhee/router-pathname
https://im-designloper.tistory.com/1023. React์ Next์ Router์ ์ฐจ์ด์
https://velog.io/@moony_moon/React-Next-Router-vs-React-Router
javascript๋ ์์ฑ๋ ์ฝ๋๊ฐ ์๋จ์์๋ถํฐ ์์๋๋ก ์คํ๋๊ธฐ ๋๋ฌธ์ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ๊ณ ์๋ต์ ๋ฐ์์ค๋ ๋์ ํ๋ฉด์ ๊ทธ๋ ค์ง ๋ฐ์ดํฐ์ ๋ด์ฉ์ด undefined ์ด๋ฏ๋ก ์ฒซ ํ๋ฉด์ด ๊ทธ๋ ค์ง๋ ์๊ธฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ฉด์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
์ด ๋ถ๋ถ์ด ํจ์จ์ ์ผ๋ก ์คํ๋๊ธฐ ์ํด์ ํ๋ฉด์ ๋ฏธ๋ฆฌ ๊ทธ๋ ค๋๊ณ ๋ฐ์ดํฐ๋ฅผ ๊ทธ๋ ค์ฃผ๊ธฐ ์ํด์ ์กฐ๊ฑด๋ถ๋ ๋๋ง์ ์ฌ์ฉํ๋ค.
์กฐ๊ฑด๋ถ ๋ ๋๋ง์ ์ข ๋ฅ๋ก๋ &&์ฐ์ฐ์, ์ผํญ์ฐ์ฐ์, ์ต์ ๋์ฒด์ด๋์ด ์๋ค.โ ์ผํญ ์ฐ์ฐ์
์ ์ผ ์ฒ์์๋ ์ผํญ ์ฐ์ฐ์๋ฅผ ์ผ๋ค.
data๋ ๋๊ธฐ์ ์ผ๋ก ๋ฐ์์์ผํ๋ ๋ฐ์ดํฐ์ง๋ง,
๋ฐ์ดํฐ๊ฐ ์ค๊ธฐ ์ ์ ์ด๋ฏธ return ๋ถ๋ถ์์ rendering์ ํด์ฃผ๊ณ ์๊ธฐ ๋๋ฌธ์
์ผํญ ์ฐ์ฐ์๋ฅผ ์จ์ ๋ฐ์ดํฐ๊ฐ ์์ ๋, ์์ ๋๋ฅผ ๋ชจ๋ ์ ์ด์ค์ผํ๋ค.
์ผํญ์ฐ์ฐ์๋ 3๊ฐ์ ํผ์ฐ์ฐ์๋ฅผ ์ทจํ๋ค.
์กฐ๊ฑด ? ์ฐธ(๊ฐ ๋๋ ์) : ๊ฑฐ์ง(๊ฐ ๋๋ ์)data ? data.fetchProfile : undefined
โ && ์ฐ์ฐ์
๊ทธ ์ดํ && ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ๊ธฐ ์์ํ๋ค.
data && data.fetchProfile //์์ ๋ ๋ณด์ฌ์ค๋ผ
&&์ฐ์ฐ์๋ ๋ฐ์ดํฐ๊ฐ ์์ ๊ฒฝ์ฐ ์๋์ผ๋ก undefined๋ฅผ ๋ฐํํด์ค๋ค.
๋ฐ์ดํฐ๊ฐ ์์ ๋ ๋ฐ๋ก div๋ฅผ ์ธ ํ์๊ฐ ์์ผ๋ฉด else ๋ถ๋ถ์ ์ธ ํ์๊ฐ ์์ง๋ง ์ด ์ฝ๋์กฐ์ฐจ ๊ธธ๋ค๊ณ ๋๊ปด์ง๋ค.
&&์ฐ์ฐ์๋ ์์ ๊ฐ์ด ์ฐธ์ผ ๊ฒฝ์ฐ์๋ง ๋ค์ ๊ฐ์ ๋ณด์ฌ์ฃผ์๋๋ฐ, ๋ฐ๋๋ก ์์ ๊ฐ์ด ๊ฑฐ์ง์ผ๋ ๋ค์ ๊ฐ์ ๋ณด์ฌ์ฃผ๋ ๊ฒฝ์ฐ๋ ์๋ค. Nullish coalescing ์ฐ์ฐ์๋ผ ๋ถ๋ฆฐ๋ค.
nullish-coalescing : null๊ณผ ๋น์ทํ๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค(๋์ค๋ฝ๋ค)
??์ฐ์ฐ์๋ ์์ ๊ฐ์ด ๋น ๊ฐ์ด๋ฉด ๋ค์ ๊ฐ์ ๋ณด์ฌ์ฃผ๋ฉฐ ||์ฐ์ฐ์๋ ์์ ๊ฐ์ด ๊ฑฐ์ง(false)์ผ ๊ฒฝ์ฐ ๋ค์ ๊ฐ์ ๋ณด์ฌ์ค๋ค.data ?? data.fetchProfile data || data.fetchProfile //๊ธฐ๋ณธ๊ฐ์ฒ๋ฆฌ ์
โ ์ต์ ๋ ์ฒด์ด๋ (Optional-Chaining)
optional-chaing์ด๋ ๊ธฐ์กด์ && ์ฐ์ฐ์๋ฅผ ์ฐ๋ฉด์ ๊ธธ์ด์ก๋ ์ฝ๋๋ฅผ ๋์ฑ ๊ฐ๊ฒฐํ๊ฒ ์ฌ์ฉํ๋ ์ฐ์ฐ์ ์ด๋ค.
optional-chaing์ ์ต์ ๋ฌธ๋ฒ์ผ๋ก ๋ฌด๋ ค ES2020์์ ๋์จ ๊ฒ.
๊ทธ๋์ ์์ง ๋ชจ๋ฅด๋ ์ฌ๋์ด ๋ง์ ์๋ ์๋ค.data?.fetchProfile
optional-Chaining์ ? ์ฐ์ฐ์ ์ ๊ฐ์ฒด์ ์ฐธ์กฐ๊ฐ undefined || null ์ด๋ผ๋ฉด undefined๋ฅผ ๋ฆฌํดํด์ค๋ค.
์ผํญ์ฐ์ฐ์, && ์ฐ์ฐ์์ ๋๊ฐ์ ๊ธฐ๋ฅ์ ํ๋ ๊ฑฐ์ง๋ง ํจ์ฌ ๊ฐ๋จํด์ก๋ค.
๊ทธ๋ผ optional chaining ์ด ๋ฐ์ดํฐ๊ฐ ๋ค์ด์ฌ ๋ ๊น์ง ๊ธฐ๋ค๋ ค์ฃผ๋ ๊ฑธ๊นโ
๊ธฐ๋ค๋ ค ์ฃผ๋๊ฒ ์๋!
๋ฐ์ดํฐ๊ฐ ์์๋๋ undefined๋ฅผ ๋ฆฌํดํ๋ค๊ฐ ๋ฐ์ดํฐ๊ฐ ๋ค์ด์ค๋ฉด ๋ค์ด์จ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ํ๋ฉด์ ๋ค์ ๋ ๋ํด์ฃผ๋ ๊ฒ.
์ด๊ฒ์ ๋ฆฌ๋ ๋๋ง์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค.
optional chaining ์ธ์ ์ฌ์ฉํ ๊นโ
๋ฐ์ดํฐ๋ฅผ ํ๋ฉด์ ๋ ๋๋ง ํ ๋ ์ฌ์ฉํ๋ค.
๋ฐฑ์๋์ ํต์ ํด์ ๋ฐ์์ค๋ ๋ฐ์ดํฐ๋ค์ ์๊ฐ์ด ๊ฑธ๋ฆฌ๊ธฐ ๋๋ฌธ์ ์ต์ ๋ ์ฒด์ด๋์ ์ฌ์ฉํด์ผ ํ๋ฉด์ ์ค๋ฅ๊ฐ ์๋๊ณ ์ ๋ ๋๋๋ค.
mutation์ด ํญ์ ์ฑ๊ณตํ๋ ๊ฒ์ ์๋๋ค
Backend ์ปดํจํฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์๋ ์๊ณ , ๋ด๊ฐ ์์ ํ๋ ค๋ ๊ฒ์๋ฌผ์ด ๊ฐ์๊ธฐ ์ญ์ ๊ฐ ๋๋ ๋ฐ๋์ ์์ ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ ๋ฑ ์ฌ๋ฌ๊ฐ์ง ์คํจ ๊ฐ๋ฅ์ฑ์ด ์๋ค.
๋ฐ๋ผ์, ์ฐ๋ฆฌ๋ ์ฑ๊ณต์ ๋ํ ์ฒ๋ฆฌ, ์คํจ์ ๋ํ ์ฒ๋ฆฌ๋ฅผ ๋๋์ด ์์ ํด์ผ ํ๋ค.try { await createBoard({ variables: { aaa: "ํ์ด", bbb: "1234", ccc: "์๋ ํ์ธ์ ํ์ด์์", ddd: "๋ฐ๊ฐ์ต๋๋ค" } }) } catch(error) { alert(error.message) // ๊ฒฝ๊ณ ์ฐฝ(์คํจํ์ต๋๋ค.) ==> ๋ฐฑ์๋ ๊ฐ๋ฐ์๊ฐ ๋ณด๋ด์ฃผ๋ ์คํจ ๋ฉ์์ง } finally { // ์ฑ๊ณต, ์คํจ ์ฌ๋ถ์ ์๊ด์์ด ๋ฌด์กฐ๊ฑด ๋ง์ง๋ง์ ์คํ๋๋ ๋ถ๋ถ // ํ์์๋ค๋ฉด ์๋ต ๊ฐ๋ฅ }
โผ ์ค์ต
//05-05-dynamic-routing-board-mutation import { useMutation, gql } from "@apollo/client"; import { useRouter } from "next/router"; const ๋์๊ทธ๋ํํ์์ธํ = gql` mutation createBoard($writer: String, $title: String, $contents: String) { createBoard(writer: $writer, title: $title, contents: $contents) { _id number message } } `; export default function GraphqlMutationPage() { const router = useRouter(); const [๋์ํจ์] = useMutation(๋์๊ทธ๋ํํ์์ธํ ); const onClickSubmit = async () => { try { const result = await ๋์ํจ์({ variables: { //variables๊ฐ $์ญํ ์ ํ๋ค. writer: "์ฒ ์", title: "hi", contents: "you", }, }); console.log(result); console.log(result.data.createBoard.number); // router.push("/section05/05-05-dynamic-routing-board-mutation-moved/" + result.data.createBoard.number) router.push( `/section05/05-05-dynamic-routing-board-mutation-moved/${result.data.createBoard.number}` ); } catch (error) { alert(error.message); } }; //ํ ์ค์ผ ๋๋ (๊ดํธ) ํ์์์ return <button onClick={onClickSubmit}>Graphql-API ์์ฒญํ๊ธฐ</button>; }
import { useQuery, gql } from "@apollo/client"; import { useRouter } from "next/router"; //playground์์ ์ฐ์ตํ๊ณ ๋ณต๋ถ const FETCH_BOARD = gql` query fetchBoard($number: Int) { fetchBoard(number: $number) { number writer title contents } } `; export default function StaticRoutingPage() { const router = useRouter(); console.log(router); const { data } = useQuery(FETCH_BOARD, { variables: { number: Number(router.query.number), //"๋ฌธ์์ด" -> ์ซ์ }, }); console.log(data); //์ ๋ฌ๋์ง ์กฐํํ๊ธฐ return ( <div> <div>2๋ฒ ์ด๋์๋ฃ</div> <div>์์ฑ์ : {data && data?.fetchBoard?.writer}</div> <div>์ ๋ชฉ : {data?.fetchBoard?.title}</div> <div>๋ด์ฉ : {data ? data?.fetchBoard?.contents : "๋ก๋ฉ์ค์ ๋๋ค"}</div> </div> ); }
โ useQuery
-> const { data : qqq } = useQuery(FETCH_BOARD);
์ด๋ฆ๋ฐ๊ฟ๋ : ํด์ ๋ฐ๊ฟ.
์คํ ์ดํธ์ฒ๋ผ ๋ง์๋๋ก ์ด๋ฆ์ ๋ฐ๊ฟ ์ ์๋ค.
shorthand property๋ ๊ฐ์ฒด๋ฅผ ์ ์ํ ๋ ๊ฐ์ฒด์ key๊ฐ๊ณผ value ๊ฐ์ด ๊ฐ์ผ๋ฉด, ๊ฐ๊ฐ ํ๊ธฐํ์ง ์๊ณ ํ ๋ฒ๋ง ํ๊ธฐํ๋ ๊ฒ์ ์๋ฏธํ๋ค.
freeboard
//----------------------------------------> import ํด์ค๊ธฐ import { Wrapper, Title, Wrapper_input, Wrapper_info, Name, NameInput, PwdInput, Password, Wrapper_title, Subtitle, Sub_Input, Wrapper_content, Content, Content_input, Wrapper_address, Address_title, AddressZip, Address_Input, Search_btn, Address, Wrapper_youtube, Youtube_title, Youtube_Input, Wrapper_image, Img_title, UploadBox, UploadBtn, Box, Plus, Text, Wrapper_mainSet, Main_title, Radio, RadioButton, RadioLabel, Wrapper_register, SubmitBtn, Color, } from "../../styles/css"; import { useState } from "react"; import { useMutation, gql } from "@apollo/client"; import { useRouter } from "next/router"; //----------------------------------------> gql const CREATE_BOARD = gql` mutation createBoard($writer: String, $title: String, $contents: String) { createBoard(writer: $writer, title: $title, contents: $contents) { _id number message } } `; //----------------------------------------> freeBoardFrom() export default function freeBoardFrom() { const router = useRouter(); //------------------------> router์ ์ธ const [createBoard] = useMutation(CREATE_BOARD); //-----------> createBoard์ ์ธ const [TheName, setName] = useState(""); const [ThePassword, setPassword] = useState(""); const [TheSubtitle, setSubtitle] = useState(""); const [TheContent, setContent] = useState(""); const [NameError, setNameError] = useState(""); const [PasswordError, setPasswordError] = useState(""); const [SubtitleError, setSubtitleError] = useState(""); const [ContentError, setContentError] = useState(""); //---------------------------------------->onChangeํธ๋ค๋ฌ ํจ์ function onChangeName(event) { setName(event.target.value); if (event.target.value !== "") { setNameError(""); } } function onChangePassword(event) { setPassword(event.target.value); if (event.target.value !== "") { setPasswordError(""); } if (String(event.target.value).length < 8) { setPasswordError("๋น๋ฐ๋ฒํธ๋ 8์ ์ด์์ ๋๋ค."); } } function onChangeSubtitle(event) { setSubtitle(event.target.value); if (event.target.value !== "") { setSubtitleError(""); } } function onChangeContent(event) { setContent(event.target.value); if (event.target.value !== "") { setContentError(""); } } //---------------------------------------->Signup ๊ฒ์ฆํ๊ธฐ const Signup = async () => { //๊ฐ์ ํ๊ธฐ if (!TheName) { //๋น๊ฐ์ด๋ผ๋ฉด setNameError("์ด๋ฆ์ ์ ๋ ฅํด์ฃผ์ธ์"); } if (!ThePassword) { setPasswordError("๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์"); } else if (String(ThePassword) < 8) { setPasswordError("๋น๋ฐ๋ฒํธ๋ 8์ ์ด์์ ๋๋ค."); } if (!TheSubtitle) { setSubtitleError("์ ๋ชฉ์ ์ ๋ ฅํด์ฃผ์ธ์"); } if (!TheContent) { setContentError("๋ด์ฉ์ ์ ๋ ฅํด์ฃผ์ธ์"); } if ( TheName && ThePassword && String(ThePassword).length > 8 && TheSubtitle && TheContent ) { //๋น๊ฐ์ด ์๋๋ผ๋ฉด alert("๊ฒ์๊ธ์ด ๋ฑ๋ก๋์์ต๋๋ค."); try { const result = await createBoard({ variables: { //variables๊ฐ $์ญํ ์ ํ๋ค. writer: TheName, title: TheSubtitle, contents: TheContent, }, }); console.log(result); console.log(result.data.createBoard.number); router.push(`/freeboard-moved/${result.data.createBoard.number}`); } catch (error) { alert(error.message); } } }; // ----------------------------------------> JSX return ( <Wrapper> <Title>๊ฒ์๋ฌผ๋ฑ๋ก</Title> <Wrapper_input> <Wrapper_info> <Name>์์ฑ์</Name> <NameInput type="text" placeholder="์ด๋ฆ์ ์ ์ด์ฃผ์ธ์" onChange={onChangeName} /> <Color>{NameError}</Color> </Wrapper_info> <Wrapper_info> <Password>๋น๋ฐ๋ฒํธ</Password> <PwdInput type="password" placeholder="๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์" onChange={onChangePassword} /> <Color>{PasswordError}</Color> </Wrapper_info> </Wrapper_input> <Wrapper_title> <Subtitle>์ ๋ชฉ</Subtitle> <Sub_Input type="text" placeholder="์ ๋ชฉ์ ์์ฑํด์ฃผ์ธ์" onChange={onChangeSubtitle} /> <Color>{SubtitleError}</Color> </Wrapper_title> <Wrapper_content> <Content>๋ด์ฉ</Content> <Content_input type="text" placeholder="์ ๋ชฉ์ ์์ฑํด์ฃผ์ธ์" onChange={onChangeContent} /> <Color>{ContentError}</Color> </Wrapper_content> <Wrapper_address> <Address_title>์ฃผ์</Address_title> <AddressZip> <Address_Input type="text" placeholder="07250"></Address_Input> <Search_btn>์ฐํธ๋ฒํธ ๊ฒ์</Search_btn> </AddressZip> <Address /> <Address /> </Wrapper_address> <Wrapper_youtube> <Youtube_title>์ ํ๋ธ</Youtube_title> <Youtube_Input type="text" placeholder="๋งํฌ๋ฅผ ๋ณต์ฌํด์ฃผ์ธ์." </Youtube_Input> </Wrapper_youtube> <Wrapper_image> <Img_title>์ฌ์ง์ฒจ๋ถ</Img_title> <UploadBox> <UploadBtn> <Box> <Plus>+</Plus> <Text>Upload</Text> </Box> </UploadBtn> <UploadBtn> <Box> <Plus>+</Plus> <Text>Upload</Text> </Box> </UploadBtn> <UploadBtn> <Box> <Plus>+</Plus> <Text>Upload</Text> </Box> </UploadBtn> </UploadBox> </Wrapper_image> <Wrapper_mainSet> <Main_title>๋ฉ์ธ์ค์ </Main_title> <Radio> <RadioButton type="radio" id="youtube" name="radio-button" /> <RadioLabel>์ ํ๋ธ</RadioLabel> <RadioButton type="radio" id="image" name="radio-button" /> <RadioLabel>์ฌ์ง</RadioLabel> </Radio> </Wrapper_mainSet> <Wrapper_register> <SubmitBtn onClick={Signup}>๋ฑ๋กํ๊ธฐ</SubmitBtn> </Wrapper_register> </Wrapper> ); }
freeboard-moved/[page]
import { Wrapper_Board, Wrapper_one, Wrapper_Header, Wrapper_Writer, Image, Writer, WriterBox, Day, IconBox, LocationBox, ClipBox, LocationImg, ClipImg, Wrapper_Body, Title, ContentImg, Content, Wrapper_Video, VideoImg, Wrapper_Footer, ChoiceBox, LikeBox, LikeImg, LikeCount, DisLikeBox, DisLikeImg, DisLikeCount, Wrapper_two, Wrapper_Btn, BtnBox, GotoListBtn, ReviseBtn, Wrapper_Comment, CommentTop, Comment_Title, CommentImg, Comment, StarBox, Star, CommentInputBox, CommentInput, LimitedBox, LimitedText, ResisterBtn, CommentBottom, Wrapper_Commented, Wrapper_Commented1, CommentedImg, Wrapper_commentedBox, CommentedBox1, CommentedBox, CommentedName, CommentedStar, IconsBox, PencilBox, Pencil, CloseBox, Close, CommentedContents, CommentedDate, } from "../../../styles/[csspage]/css"; // import * as All from "../../../styles/[csspage]/css"; import { useQuery, gql } from "@apollo/client"; import { useRouter } from "next/router"; const FETCH_BOARD = gql` query fetchBoard($number: Int) { fetchBoard(number: $number) { number writer title contents like createdAt } } `; export default function freeBoardFrom() { const router = useRouter(); console.log(router); const { data } = useQuery(FETCH_BOARD, { variables: { number: Number(router.query.page), }, }); console.log(data); return ( <Wrapper_Board> <Wrapper_one> <Wrapper_Header> <Wrapper_Writer> <Image src="/img.png" /> <WriterBox> <Writer>{data && data?.fetchBoard?.writer}</Writer> <Day>{data && data?.fetchBoard?.createdAt}</Day> </WriterBox> </Wrapper_Writer> <IconBox> <ClipBox> <ClipImg src="/Clip.png" /> </ClipBox> <LocationBox> <LocationImg src="/location.png" /> </LocationBox> </IconBox> </Wrapper_Header> <Wrapper_Body> <Title>{data && data?.fetchBoard?.title}</Title> <ContentImg src="/content.png" /> <Content>{data && data?.fetchBoard?.contents}</Content> </Wrapper_Body> <Wrapper_Video> <VideoImg src="/video.png" /> </Wrapper_Video> <Wrapper_Footer> <ChoiceBox> <LikeBox> <LikeImg src="/like.png" /> <LikeCount>{data && data?.fetchBoard?.like}</LikeCount> </LikeBox> <DisLikeBox> <DisLikeImg src="/dislike.png" /> <DisLikeCount>{data && data?.fetchBoard?.like}</DisLikeCount> </DisLikeBox> </ChoiceBox> </Wrapper_Footer> </Wrapper_one> <Wrapper_two> <Wrapper_Btn> <BtnBox> <GotoListBtn>๋ชฉ๋ก์ผ๋ก</GotoListBtn> <ReviseBtn>์์ ํ๊ธฐ</ReviseBtn> </BtnBox> </Wrapper_Btn> <Wrapper_Comment> <CommentTop> <Comment_Title> <CommentImg src="/comment.png" /> <Comment>๋๊ธ</Comment> </Comment_Title> <StarBox> <Star src="/graystar.png" /> <Star src="/graystar.png" /> <Star src="/graystar.png" /> <Star src="/graystar.png" /> <Star src="/graystar.png" /> </StarBox> <CommentInputBox> <CommentInput type="text" placeholder="๊ฐ์ธ์ ๋ณด๋ฅผ ๊ณต์ ๋ฐ ์์ฒญํ๊ฑฐ๋, ๋ช ์ ํผ์, ๋ฌด๋จ ๊ด๊ณ , ๋ถ๋ฒ ์ ๋ณด ์ ํฌ์ ๋ชจ๋ํฐ๋ง ํ ์ญ์ ๋ ์ ์์ผ๋ฉฐ, ์ด์ ๋ํ ๋ฏผํ์ฌ์ ์ฑ ์์ ๊ฒ์์์๊ฒ ์์ต๋๋ค." /> <LimitedBox> <LimitedText>0/100</LimitedText> <ResisterBtn>๋ฑ๋กํ๊ธฐ</ResisterBtn> </LimitedBox> </CommentInputBox> </CommentTop> <CommentBottom> <Wrapper_Commented1> <CommentedImg src="/img.png" /> <Wrapper_commentedBox> <CommentedBox1> <CommentedBox> <CommentedName>๋ ธ์๋</CommentedName> <CommentedStar> <Star src="/star.png" /> <Star src="/star.png" /> <Star src="/star.png" /> <Star src="/graystar.png" /> <Star src="/graystar.png" /> </CommentedStar> </CommentedBox> <IconsBox> <PencilBox> <Pencil src="/pencil.png" /> </PencilBox> <CloseBox> <Close src="/close.png" /> </CloseBox> </IconsBox> </CommentedBox1> <CommentedContents> ์ง์ง ์ ์ตํ๊ณ ์ ๋ง ํ์ํ ์ ๋ณด์ธ ๊ฒ ๊ฐ์์~! ์์ผ๋ก๋ ์ข์ ์ ๋ณด ๋ถํ๋๋ฆฝ๋๋ค~! </CommentedContents> <CommentedDate>2021.02.22</CommentedDate> </Wrapper_commentedBox> </Wrapper_Commented1> <Wrapper_Commented> <CommentedImg src="/img.png" /> <Wrapper_commentedBox> <CommentedBox> <CommentedName>๋ ์ง</CommentedName> <CommentedStar> <Star src="/star.png" /> <Star src="/star.png" /> <Star src="/star.png" /> <Star src="/graystar.png" /> <Star src="/graystar.png" /> </CommentedStar> </CommentedBox> <CommentedContents>์ง์ง ์ข๋ค์~ ๊ฐ์ฌํฉ๋๋ค~!</CommentedContents> <CommentedDate>2021.02.22</CommentedDate> </Wrapper_commentedBox> </Wrapper_Commented> <Wrapper_Commented> <CommentedImg src="/img.png" /> <Wrapper_commentedBox> <CommentedBox> <CommentedName>์์ฐ์ฝ</CommentedName> <CommentedStar> <Star src="/star.png" /> <Star src="/star.png" /> <Star src="/star.png" /> <Star src="/graystar.png" /> <Star src="/graystar.png" /> </CommentedStar> </CommentedBox> <CommentedContents> ์์ผ๋ก๋ ์ข์ ์ ๋ณด ๋ถํ๋๋ฆฝ๋๋ค~! </CommentedContents> <CommentedDate>2021.02.22</CommentedDate> </Wrapper_commentedBox> </Wrapper_Commented> </CommentBottom> </Wrapper_Comment> </Wrapper_two> </Wrapper_Board> ); }
Image
- [์ด ์์ ์๋ ๊ฑธ ๋ณ์๋ก ๋ณด๊ฒ ๋ค๋ ์๋ฏธ์.] - ๋์ ๋ผ์ฐํ
- import { useRouter } from "next/router" ํด์ค๊ธฐ!
- ์คํจ๋ฅผ ์ผ๋ํด๋๊ณ ์์ ํด์ผํ๋ค.
try ์ฐ๊ธฐ
๊ฐ์ฅ ์ค์ํ๊ฒ ํธ๋ผ์ดํด๋ณด๊ณ ์๋๋ฉด ์บ์น๋ก ์๋ฌ๋์ด์ค!
ํธ๋ผ์ดํด์ ์คํจํ๋ฉด ์ฝ์ ์คํ๋์ง ์๊ณ ์บ์น๊ฐ ์คํ๋๋ค.- !๋ถ์ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ ์กฐ๊ฑด๋ฌธ์ ์ด์ฉํ ๊ฒ ํน์
event๋งค๊ฐ๋ณ์๋ก ๋ฐ์์ ์์์ ์์ ํ๊ธฐ- ์ฟผ๋ฆฌ์กฐํ์ return๊ฐ๋ ์ ๋ ฅํด์ผํจ.
- gql๋ฐ์ธ๋ฉ ๋น ๋ฅด๋ฆฌ์ง ๋ง๊ฒ
- ๊ฒฝ๋ก์ค์ ์ ๋๋ก ํ ๊ฒ!
๐ฅ Problem & Solve
- ๋ผ์ฐํฐ ํธ์ฌํ ๋ router.push(
/05/product-moved/${result.data.createProduct._id}
);
ํด์ค์ผ ๋๋๋ฐ page๋ก ํ์.
2.query fetchProduct($productId: ID) ํ๋ ์ด๋ณด๋ ๊ทธ๋๋ก ์์ด๋ ๋ฃ์ด์ค์ผ ํ๊ณ productId๋ P๊ฐ ์๋.- variables: {
productId: String(router.query.page),},
});
query๋ก ํด์ค์ผ ํ๋ค. ์กฐํํ๋๊ฑฐ๋๊น!!!- ์ค์ต ์ค์ ์ฝ๋๋ฅผ ๋ถ๋ช ๋ง๊ฒ ์์ฑํ๋๋ฐ api์์ฒญ์ ์๊พธ ์๋ฌ๊ฐ ๋๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค,,๋คํธ์ํฌ๋ก ๋ค์ด๊ฐ์ ๋ด๋ ๋ชจ๋ฅด๊ฒ ๊ณต,,์ด์ ์ฒ๋ผ ํ์ผ์ ์ฎ๊ธฐ๋ ๊ณผ์ ์์ ์๋ฌ๊ฐ ๋ฐ์ํ๊ฑด๊ฐ ์ถ์ด์ vscode๋ฅผ ์ข ๋ฃํ๊ณ ๋ค์ ์ผฐ๋๋ฐ๋ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํด์ ๊ฒฐ๊ตญ ๋ฉํ ๋๊ป ๋์์ ์ฒญํ๋๋ฐ, ์๊ณ ๋ณด๋ ํ์ผ๋ช ์ ๊ณต๋ฐฑ์ด ์์ด์ ์๊ธด ๋ฌธ์ ์๋ค,,,ใ
์ด์ฉ์งใ
ํ์ผ๋ช ์ธ ๋ ๊ณต๋ฐฑ ์กฐ์ฌํ ๊ฒ! ์ ์ํ์@.@
์ด๋ฒ ์ฃผ๊ฐ์ ์นํ์ด์ง์ ํต์ฌ์ ๋ฐฐ์ ๋ค.
๊ต์ฅํ ์ค์ํ ํฌ์ธํธ๋ผ๋ ๊ฒ!!
๋จธ๋ฆฟ ์์ ํ๋ํ๋ ์ ๋ค์ด์์ผ ํ๋๋ฐ
์๋ ๋ง์ ๋ด์ฉ์ด๋ผ ๋ด๊ฐ ์๋ฒฝํ ์ดํดํ ๊ฑธ๊น? ์ถ์ ์์ฌ์ด ๋ค๊ธฐ๋ ํ๋ค,,
๋ฉํ ๋๊ป์ ์๊ธฐ ์์ ๊ณผ ํํํ์ง ๋ง๋ฌ์ผ๋ ์ด๋ด ์๋ก ๋ ๊ณต๋ถํด์ผ๊ฒ ์ง
์๋ ์๋ฆฌ์์ ์ ์ ์ผ์ด๋๋ค ๋ณด๋๊น ๋ชฉ๊ณผ ์ด๊นจ๊ฐ ๋ง์ด ๋ป๊ทผํ๋ค.
๊ฑด๊ฐ๊ด๋ฆฌ๋ ์ ํด์ผ๊ฒ ๋ค๋ ์๊ฐ์ด ๋ ๋ค.
์ค๋ ๊ณผ์ ์ค ๊ฒ์ํ UI๋ฅผ ๊ตฌํํ๋ ๊ฒ์ด ์์ด,
ํด์ฆ ๊ณผ์ ๋ฅผ ๋ถ์ง๋ฐํ ํ๊ณ ๋์ด๊ฐ๋ค.
๋ค๋ฅธ ํ์ด๋ถ ๋์ JSX๋ฅผ ๋ ๋นจ๋ฆฌ ๋๋ด๋ ๋ฐฉ๋ฒ์ ์๊ฒ๋ฌ๋ค.
ํ๋๋ฅผ ํด๊ฒฐํ๊ณ ๋ค์์ผ๋ก ๋์ด๊ฐ๋ ค๋ ์ฑํฅ ๋๋ฌธ์ธ์ง
CSS๋ ๊ทธ๋ฐ ์์ผ๋ก ์์ ํด์ ๋ ์ค๋๊ฑธ๋ ธ๋ ๊ฒ์ด๋คใ ใ
ํ๋ฒ์ ์์ ํ๊ณ ๋์ด๊ฐ๋ ์๊ฐ์ด ๋จ์ถ๋ฌ๋ค!
์ค๋ฅ๋ฐ์์ ํด๊ฒฐํ๋ ๋ฌธ์ ์ ๋ํด์๋ ์๊ฒ๋ฌ๋๋ฐ,
ํนํ ๋คํธ์ํฌ ์๋ฌ๋ฅผ ํ์ธํ๊ณ ์์ ํ ์ ์๋ค๋ ๋ถ๋ถ์ด ๊ณผ์ ๋ฅผ ํ๋ฉด์๋ ํฐ ๋์์ด ๋ฌ๋ค.
์ปดํฌ๋ํธ๋ค์ importํด์ค๋ ๊ณผ์ ์ด ๋ฒ๊ฑฐ๋ก์์
import * All from ์ด๋ฐ ์์ผ๋ก ํ๋ ๋ฐฉ๋ฒ์ด ์์ด ์ ์ฉ์ ํ๋๋ฐ
์ ๋์ง ์๋ ๊ฑธ ๋ณด๋,,ํ๋ฆฐ ๋ฐฉ๋ฒ์ธ๊ฐ๋ณด๋ค^^
์ด ๋ํ ๊ณง ๋ฐฐ์ฐ๊ฒ ๋๊ฒ ์ง,,
๋ค์์ฃผ๋ถํฐ๋ ๋งค์ฃผ ์์์ผ๋ง๋ค ์๊ณ ๋ฆฌ์ฆ ํ ์คํธ๋ ์งํํ๋ค.
์๊ณ ๋ฆฌ์ฆ,,์ฝํ๋ฐ,,๊ฒ๋ ๋ง์ด ํ์ด๋ด์ผ ๋๊ฒ ์งใ ใ !!!
์๊ฐ๊ด๋ฆฌ๋ฅผ ์ด๋ค ์์ผ๋ก ์ง์ผ ํ ์ง ์ถฉ๋ถํ ๊ณ ๋ฏผํด์ผ๊ฒ ๋ค