์๋ ํ์ธ์.
์ด๋ฒ์ ์ํํธ์จ์ดํ๊ณผ 4-1ํ๊ธฐ ์บก์คํค ํ๋ก์ ํธ๋ฅผ ๋ง์น๋ฉฐ ํ๊ณ ๋ก์ ๋จ๊ธฐ๊ณ ์ ํฉ๋๋ค.
์ด ๊ธ์ ํตํ์ฌ ๋ฟ๋ฏํ๊ธฐ๋ ํ์ง๋ง ์์ฌ์ ๋ <๋จํต> ํ๋ก์ ํธ๋ฅผ ํ๊ณ ํด๋ณด๊ฒ ์ต๋๋ค.
์๋น์ค ์๊ฐ


๋จํต์ ํ์ฐ๋ค์ ํ์ํ ํ์ฌ ์ฐธ์ฌ๋ฅผ ์ฆ์งํ๊ณ , ํ์ํ์ ํผ ๊ฐ๊ณต ์ ๋ฌด๋ฅผ ๊ฐํธํํด์ฃผ๋ ์๋น์ค์ ๋๋ค.
ํ์ํ๋ ๋จํต์ ํตํ์ฌ ํ์ํ ํ์ฌ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ํ๋ณดํ๊ณ , ํ์ฐ๋ ํ์ฌ ์ฐธ์ฌ ์ ๋ณด, ์งํ ์ํฉ, ํ์ฌ ์ผ์ ๋ฑ์ ํธ๋ฆฌํ๊ฒ ํ์ธํ ์ ์์ต๋๋ค.
๋จํต์ ํ์ฐ๋ค์ด ํ์ฌ ํผ์ ์ ์ถํ ๋ ๋ฒ๊ฑฐ๋ก์ ๋ ์ฌํ ์ธ์ฆ ์ ๋ณด (๋จ๊ตญ๋ํ๊ต ์น์ ๋ณด ํ๋ฉด ์บก์ณ, ์ ๊ณต๊ณผ ํ๋ฒ ์ ๋ ฅ ๋ฑ) ๊ธฐ์ ์ ์ฐจ๋ฅผ ํ์๊ฐ์ ์ ๋จ ํ ๋ฒ์ ํ๊ต ์ด๋ฉ์ผ ์ธ์ฆ์ผ๋ก ๋์ฒดํ๊ณ , ํผ ์๋ต ๋ฆฌ์คํธ๋ฅผ ์์ ๋ก ์ถ์ถํ๋ ๊ธฐ๋ฅ์ ํตํ์ฌ ํ์ํ์ ์ ๋ฌด ์ ์ฐจ๋ฅผ ๊ฐ์ํํฉ๋๋ค!
์ด ํ๋ก์ ํธ๋ ํ์ํ์ ๋์์ ๊ตฌํ์ฌ ์ค์ ํ์ฐ๋ถ๋ค๊ป ์๋น์ค๋ฅผ ์ ๊ณตํ๋ ๊ฒ์ ๋ชฉํ๋ก ์งํํ๊ฒ ๋์์ต๋๋ค.
ํ๋ฉด ๊ตฌ์
ํผ๊ทธ๋ง๋ก ํ๋ฉด์ ์ง์ ๋ง๋ค๊ณ ๋ง์ ๋ ํผ๋ฐ์ค๋ค์ ๋ณด๋ฉฐ, ๋์์ธ์ ์ผ๊ด์ฑ, ์ฌ์ฉ์ ์ ๊ทผ์ฑ ๋ฑ UI/UX์ ๋ํ ๊น์ด ์๋ ๊ณ ๋ฏผ์ ํ ์ ์์๋๋ฐ์!
ํนํ ๊ฐํธํ ํ์๊ฐ์ ์ ์ฐจ๋ฅผ ์ ๊ณตํ๊ณ ์ ํผ๋์ ์ผ์ฌ์ฐจ๊ฒ ๋์ ํ์์ต๋๋ค. ใ ใ

๊ธฐ์ ์คํ ์ ์
๋จํต์ ์ฃผ๋ก ์ค๋งํธํฐ์ผ๋ก ํ์ฌ ๋ด์ฉ์ ํ์ธํ๋ ํ์ฐ๋ค์ ์ํ์ฌ ๋ชจ๋ฐ์ผ ๊ธฐ์ค์ UI๋ฅผ ์ ๊ณตํ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ์๋ฆผ ๊ธฐ๋ฅ์ ๊ตฌํํ๊ธฐ ์ํ์ฌ ๋ค์ดํฐ๋ธ ์ฑ๊ณผ PWA ์ค ์ ํ๋ฆฌ์ผ์ด์ ์ค์น๋ฅผ ์๊ตฌํ์ง ์๊ณ ํ๋ซํผ์ ์ ์ฝ์ด ์๋ PWA๋ฅผ ์ ํํ๊ฒ ๋์์ต๋๋ค.
๊ทธ๋์ ์น์ผ๋ก ๋ค์ดํฐ๋ธ ์ฑ๋งํผ์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ์ฌํ๊ธฐ ์ํด์, SPA๋ฅผ ์ง์ํ๋ React.js๋ฅผ ํ๋ก ํธ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ์ฑํํ์๊ณ , ํ์ ์ ์์ ์ธํฐํ์ด์ค๋ฅผ ํตํ์ฌ ์ ์ง๋ณด์, ํ์ฅ์ฑ, ์ผ๊ด์ฑ๋ฅผ ์ ๊ณตํ๋ Typescript๋ก ์์ฑํ์์ต๋๋ค.
๋จํต์ ์ด๊ธฐ ์น ๋ ๋๋ง ์๋ ๊ฐ์ถ์ ์ํ์ฌ ๋ ๋๋ง ์ฑ๋ฅ ์ด์๊ฐ ์๋ CSS-in-JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ(Styled Components, Emotion)๊ฐ ์๋, Utility First ๋ฐฉ์์ ์ฑํํ Tailwind CSS๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ๊ฒฐ์ ํ์์ต๋๋ค. Tailwind CSS๋ ๋ฐ์ด๋ ๊ฐ๋ฐ ์์ฐ์ฑ์ ์ ๊ณตํ๋ค๋ ์ ์์๋ ํฐ ์ด์ ์ ์ง๋๋๋ค.
๋ง์ง๋ง์ผ๋ก ๋ฐ์ดํฐ ํจ์นญ, ์บ์ฑ, ๋๊ธฐํ ๋ฑ ์๋ฒ ์ํ ๊ด๋ฆฌ ์์ ์ ๊ฐ๋จํ๊ณ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌ ๊ฐ๋ฅํ๊ฒ ํ๋ React Query๋ฅผ ๋์ ํ์ฌ, ํด๋ผ์ด์ธํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ๊ณผ ๊ฐ๋ฐ ์์ฐ์ฑ์ ํฅ์ํ๊ณ ์ ํ์์ต๋๋ค.
๊ตฌํ
(1) ํผ๋

ํผ๋์ ๋ ์ด์์์ children props๊ฐ ReactNode๋ค์ ๋ฐฐ์ด ์์๋ก ๊ฐ์ง๋ค๋ ์์ด๋์ด๋ฅผ ๋ฐํ์ผ๋ก, SignUpLayout ์์ ํ๋์ children ์์๋ฅผ ์ธ๋ฑ์ค๋ก ์ง์ ํ์ฌ(children[index]) ํ๋์ ์ปดํฌ๋ํธ๋ง ๋ํ๋๋ ๋ก์ง์ ๊ตฌ์ํ์์ต๋๋ค.
SignUpLayout children์ ์ธ๋ฑ์ค์ธ ํผ๋์ ๋จ๊ณ(step)๋ ์ซ์๋ก ํํํ์๊ณ , pathname์ด ๋ฐ๋ ๋๋ง๋ค pathname์์ steps์ ์ํ pageName์ ๊ฐ์ ธ์ steps.findIndex๋ฅผ ํตํ์ฌ pageName์ index๋ฅผ ์ฐพ์ step์ผ๋ก ์
๋ฐ์ดํธํ ๋ค์, SignUpLayout์ step props๋ก ์ ๋ฌํ์์ต๋๋ค.

ํผ๋์ ํ๋ฆ์ ์ฝ๊ฒ ํ์ ํ ์ ์๊ฒ ํ๊ธฐ ์ํด ํ์ฌ step, ์คํ ๋ณ ์ปดํฌ๋ํธ๋ค์ ํ๋์ ํ์ผ์์(SignupPage) ๊ด๋ฆฌํ ์ ์๋๋ก ์ค๊ณํ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ์คํ ์ปดํฌ๋ํธ์ onNext props์ ํตํ์ฌ ์คํ ๋ณ ๋ค์ ์ด๋ฒคํธ๋ ํ ๋ฒ์ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
SignUpPage.tsx
export const SignUpPage = () => {
const navigate = useNavigate();
const { mutate: postSignUpInfo } = usePostSignUpInfo();
const { pathname } = useLocation();
const [step, setStep] = useState(0);
const steps = ['email', 'info', 'password', 'success'];
useEffect(() => {
const pageName = pathname.split('?')[0].split('/sign-up/')[1];
setStep(steps.findIndex((item) => item === pageName));
}, [pathname]);
const submitSignUpInfo = () => {
postSignUpInfo();
};
return (
<SignUpLayout step={step}>
<MailEntryPage />
<InfoEntryPage onNext={() => navigate('/sign-up/password')} />
<PasswordEntryPage onNext={() => submitSignUpInfo()} />
<SignUpSuccessPage onNext={() => navigate('/login')} />
</SignUpLayout>
);
};
SignUpLayout.tsx
export const SignUpLayout = ({
children,
step,
}: {
children: ReactNode[];
step: number;
}) => {
const { pathname } = useLocation();
const isInSuccessPage = pathname === '/sign-up/success';
return (
<div
className={`pwa-layout grid h-full w-full gap-8 p-8 ${isInSuccessPage && 'gradientBackground'}`}
>
{children[step]}
</div>
);
};
(2) input ์๋ ํฌ์ปค์ค

ํ์๊ฐ์ ์ ํต์ฌ, ์ผ๋ง๋ ๊ฐํธํ Input ์ ๋ ฅ์ ์ ๊ณตํ ์ง์ ๋ํด์๋ ๊ณ ๋ฏผํด๋ดค๋๋ฐ์.
์ฌ๋ฌ ํ์ด์ง์ ๊ฑฐ์ณ ์ ๋ ฅ๋๋ ํ์ ์ ๋ณด๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํ์ฌ ํ์ ์ ๋ณด๋ zustand๋ฅผ ํตํด ์ ์ญ ์ํ๋ก ๊ด๋ฆฌํ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ input ์ ๋ ฅ ํ ์ํฐ๋ฅผ ๋๋ฅผ ๋๋ง๋ค ์๋ก์ด input์ด ์์์ ํ์๋๋ ๊ตฌ์กฐ์ด๋ฏ๋ก ๋ค์๊ณผ ๊ฐ์ ๋ก์ง์ผ๋ก ๊ตฌํํ์์ต๋๋ค.
1. input ์ปดํฌ๋ํธ๋ค์ ์ ๋ณด๋ฅผ ๋ด์ ๋ฐฐ์ด์ธ inputAttrList๋ฅผ ์ ์
2. ์ํฐ ์ ๋ ฅ์ ์๋กญ๊ฒ ๋ณด์ฌ์ง๋ input์ index์ธ activatedIndex์ 0์ผ๋ก ์ด๊ธฐํํ๋ค.
3. inputAttrList๋ฅผ slice(0, activatedIndex + 1).reverse().map() ํ์ฌ inputAttrList๋ฅผ activatedIndex๊น์ง๋ง ์๋ฅด๊ณ , ์ด๋ฅผ ๊ฑฐ๊พธ๋ก ๋์ดํ ํ ํ๋ฉด์ ๋งตํํ๋ค.
4. ์ํฐ ์ ๋ ฅ์ activatedIndex๋ฅผ 1์ ๋ํ๋ค.
์ด๋ info ํ์ด์ง์ password ํ์ด์ง์์ ๋์ผํ๊ฒ ๋์ํ๋ฏ๋ก ๊ณตํต ์ปดํฌ๋ํธ InputContainer๋ฅผ ๋ง๋ค์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ํ์ด์ง๋ณ state๋ก activatedIndex๋ฅผ ๊ด๋ฆฌํ๋ฉด ํ์ด์ง๊ฐ ์ ํ๋ ๋๋ง๋ค activatedIndex๊ฐ 0์ผ๋ก ์ด๊ธฐํ๋๋ฏ๋ก, ์ด์ ํ์ด์ง๋ก ์ด๋์ ์ ๋ ฅ ๋ด์ฉ์ ๋ณผ ์ ์๊ฒ ๋ฉ๋๋ค.
๋ฐ๋ผ์ activatedIndex๋ฅผ info์ password ํ๋กํผํฐ๋ฅผ ๊ฐ์ง ์ ์ญ ์ํ์ ๊ฐ์ฒด๋ก ์ ์ํ์ฌ, ๊ฐ ํ์ด์ง์ ํ์ฑํ๋ input ์ธ๋ฑ์ค๋ฅผ ๋ฐ๋ก ์ ์ฅํ๋๋ก ํ์์ต๋๋ค.
InputContainer์๋ ๋ค์์ props๋ฅผ ๋ถ์ฌํ์ฌ info, password ํ์ด์ง์์ ๋ชจ๋ ์ฌ์ฉ ๊ฐ๋ฅํ๋๋ก ํ์์ต๋๋ค.
inputAttrList: userInfoInputAttr[];
containerID: 'info' | 'password';
onNext: () => void;
input์ด ํ์๋๋ ๊ฒ์ ๊ตฌํํ์์ผ๋ฏ๋ก, ์ด์ input์ ์๋ focus ๋๋๋ก ํด์ผ ํ๋๋ฐ์.
InputContainer์ inputContainerRef๋ฅผ ์ ๋ฌํ ํ, useEffect๋ฅผ ํตํ์ฌ activatedInputIndex ๊ฐ์ด ๋ฐ๋ ๋๋ง๋ค DOM์์ inputContainerRef.current์ ChildNodes ๋ฐฐ์ด(Input ์ปดํฌ๋ํธ ์์๋ก ๊ฐ์ง) ์ค 0๋ฒ์งธ ์ธ๋ฑ์ค(์ต์์์ ์์นํ Input ์ปดํฌ๋ํธ)๋ฅผ focusํ๋๋ก ํ์์ต๋๋ค.
useEffect(() => {
const currentInput =
inputContainerRef.current?.children[0]?.querySelector('input');
currentInput?.focus();
}, [activatedInputIndex]);

(3) ํผ ์์ฑ
๋ณต์กํ ๊ฐ์ฒด state์ ํ์ ๊ฐ๋๋ฅผ ์ ์ฉํ๊ณ , input ์ด๋ฒคํธ๋ก ์ํ๋ฅผ ์ ๋ฐ์ดํธํด์ผ ํด์ ๊ฑฑ์ ๋ ํํธ์๋๋ฐ์.
์ด ๋ถ๋ถ์ ๋ํด์๋ trouble shooting์ ๊ธฐ๋กํ๊ธฐ๋ ํ์์ต๋๋ค.
ํผ ์์ฑ ํ์ด์ง๋ ์ง๋ฌธ์ ๋ด์ฉ, ์์ธ ์ค๋ช , ์ ํ, ์ต์ ๋ฆฌ์คํธ ๋ฑ์ ํ๋กํผํฐ๋ฅผ ์ง๋ ์ง๋ฌธ์ ๋ฆฌ์คํธ๋ฅผ ํ๋์ state๋ก ๊ด๋ฆฌํ์๋๋ฐ์.
๋ฆฌ์กํธ์ useState๋ ๊ฐ์ฒด state๋ฅผ ์ ๋ฐ์ดํธ์ ๊ธฐ์กด state ๊ฐ์ฒด์ ๋ถ๋ณ์ฑ์ ์ ์งํด์ผ ํฉ๋๋ค.
๊ทธ๋ฌ๋ฏ๋ก state์ ํน์ ์์๋ง ์ ๋ฐ์ดํธ์ ์คํ๋ ๋ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ์กด ๊ฐ์ฒด๋ฅผ ์๋ก์ด ๊ฐ์ฒด๋ก ๋ง๋ ํ ์ ๋ฐ์ดํธํ ๋ด์ฉ์ ์์ฑํด์ผ ํฉ๋๋ค.
์ด์ ๊ฐ์ ์์ ์ ๊ฑฐ์น๋ setState ํจ์ ์์ ์ฝ๋๊ฐ ๋งค์ฐ ๋ณต์กํด์ก์ต๋๋ค.
๋คํํ ํผ ์์ฑ ํ์ด์ง๋ ๊ณํํ๋๋ก ๊ตฌํ์ ์ฑ๊ณตํ์๊ณ ๋ฆฌ์กํธ์ state ์ ๋ฐ์ดํธ ์๋ฆฌ๋ฅผ ๊นจ์ฐ์ณ ์ข์์ง๋ง, ํ๋์ state์ ์ฌ๋ฌ ๋ฒ ์ค์ฒฉ๋ ๋ณต์กํ ๊ฐ์ฒด๋ฅผ ๊ด๋ฆฌํ๊ณ ์ด ๋ณํ์ ๋ฐ๋ผ ํ๋ฉด์ ๋ฆฌ๋ ๋๋งํ๋ ๊ฒ์ ์ฑ๋ฅ๊ณผ ์ ์ง๋ณด์ ์ธก๋ฉด์์ ์ข์ง ์์ ๋ถ๋ฆฌ ์์ ์ด ํ์ํ ๊ฒ ๊ฐ์ต๋๋ค.๐ฅฒ

(4) React Query ๋์
๋จํต์ ์ฝ๋ ์ค๋ณต์ ๋ณ๋ ๊ธฐ์กด ๋น๋๊ธฐ ํต์ ๋ก์ง์ React Query๋ก ๋์ฒดํ์ฌ ๊ฐํธํ๊ฒ ๊ตฌํํ ์ ์์๋๋ฐ์.
ํนํ useInfiniteQuery์ ํตํ์ฌ ๋ฌดํ ์คํฌ๋กค์ ์ฝ๊ฒ ๊ตฌํํ๋ ์ ์ด ๊ธฐ์ต์ ๋จ์ต๋๋ค.
๋ฌดํ ์คํฌ๋กค์ IntersectionObserver API๋ฅผ ์ฌ์ฉํ์ฌ ์์์ ๋ทฐํฌํธ์ ๊ต์ฐจ์ ์ ๊ฐ์งํ๋ intersection, useInfiniteQuery๋ฅผ ํตํ์ฌ ๊ฐ์ ธ์จ data์ isFetching์ ๋ฐํํ๋ ์ปค์คํ
ํ
์ธ useInfiniteScroll์ ๋ง๋ค์ด ๋ ์ฝ๊ฒ ์ฌ์ฌ์ฉํ ์ ์๊ฒ ํ์๋ต๋๋ค.


useInfiniteQuery({
queryKey: ['infinitePostList'],
queryFn: ({ pageParam: pageNum }) =>
Post.getInfinitePostList({ page: pageNum, size, category }),
initialPageParam: 0,
getNextPageParam: (lastPage, allPages) =>
lastPage.length ? allPages.length : undefined,
gcTime: 0,
});
ํ๋ค์๋ ์ ์ useQuery ์ฌ์ฉ์ ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋์๋ง์ ๋ฐ์ดํฐ๊ฐ fetch๋์ด, zustand๋ก ๊ด๋ฆฌํ๋ ์ ์ญ ์ํ์ ๋ฐ๋ผ ๋ฐ์ดํฐ fetch๋์ด์ผ ํ๋ ๊ฒฝ์ฐ, ์ ์ญ ์ํ์ ์ ๋ฐ์ดํธ๋ณด๋ค ๋จผ์ fetch๊ฐ ์ด๋ฃจ์ด์ก๋ ์ ์ ๋๋ค. useQuery์ enabled ์ต์ ์ ํตํ์ฌ ๊ธํ ๋ถ์ ๊ป์ง๋ง, ์ ์ญ ์ํ ๊ด๋ฆฌ ๋ก์ง์ด ๋ถ์์ ํ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ค์ด zustand์ useQuery์ ๋์ ๋ฐฐ๊ฒฝ์ ๋ ๊ณต๋ถํ ํ ์ฝ๋๋ฅผ ๊ฐ์ ํด๋ณผ ์์ ์ ๋๋ค.
(5) ๊ทธ์ธ ๊ธฐ๋ฅ๋ค



Swiper.js, React Spring Bottom Sheet, ReactBigCalendar, ToastUIEditor ๋ฑ ๋ค์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํ์ฌ ๋น ๋ฅด๊ฒ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์์์ต๋๋ค.
ํนํ ReactBigCalendar์ ์ปค์คํฐ๋ง์ด์ฆํ๋ฉฐ ํด๋์คํ ์ปดํฌ๋ํธ๋ก ์์ฑ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ฝ๋๋ฅผ ๋ฏ์ด๋ดค๋ ๊ฒ์ด ํ๋ค์๋ ๋งํผ ๋ง์ด ๊ธฐ์ต์ ๋จ์ต๋๋ค.
ํ๋ก์ ํธ ๋๋ฒจ๋กญ

๋ชฉํํ๋ ๊ธฐ๋ฅ๋ค์ ์์ฑํ๋ฉฐ ์บก์คํค์ ์ฑ๊ณต์ ์ผ๋ก ๋ง๋ฌด๋ฆฌํ์์ต๋๋ค!
๊ทธ๋ฌ๋ ์ฌํด ์บก์คํค๊น์ง 4๊ฐ์ ํ๋ก์ ํธ๋ฅผ ๋ง์น ํ, ๋ฉฐ์น ๋์ ์ ๊ฐ ์ ์ฌ๋ฌ ํ๋ก์ ํธ๋ฅผ ๋ง์ณค์์๋ ๋ถ๊ตฌํ๊ณ ๊ธฐ๋ํ ๋งํผ์ ์ฑ์ฅ์ ์ด๋ฃจ์ง ๋ชป ํ๋์ ๋ํ ๊ณ ๋ฏผ์ ํ๊ฒ ๋์์ต๋๋ค. ๊ฐ๋ฐ์๋ ๊ตฌํ๋ง ํ๋ ์ฌ๋์ด ์๋๋ผ๋ ๊ฒ์ ์ ์๋ฉด์, ๊ทธ์ ๊ฒฐ๊ณผ๋ฌผ ๋ด๋๊ธฐ์ ๊ธ๊ธํ๋ ๊ฒ ๋๋ฌธ์ด์ง ์์๊น ์ถ๋ค์.
๊ทธ๋์ ์ง๋ ๋ ์ ๋ฐ์ฑํ๋ฉฐ ๋จํต ํ๋ก์ ํธ ๋๋ฒจ๋กญ์ ๊น์ด ์๋ ํ์ต์ ์ด์ ์ ๋๋ ค ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ฝ๋ ํ์ง๊ณผ ๋ฆฌ์กํธ ๋ ๋๋ง ์ต์ ํ๋ฅผ ์ค์ฌ์ผ๋ก ํ๋ก์ ํธ๋ฅผ ์งํํ ์์ ์ ๋๋ค!
๊ทธ๋ผ ์ด๋ฒ ํ๊ณ ๊ธ์์ ์ ์๋ ๊ฐ์ ์ฌํญ์ ๊ณ ์ณ ๋ค์ ํ๊ณ ๊ธ๋ก ๊ธ๋ฐฉ ๋์์ค๊ฒ ์ต๋๋ค.
๊ธด ๊ธ ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค!
