[Main Project 2์ฃผ์ฐจ] 9.1 ~ 9.10
๊ณตํต ์ปดํฌ๋ํธ ์์
& UI ๊ตฌํ
๐ฌ ํ๋ก์ ํธ๊ฐ ๋๋ ํ์ ๋ฐ์ผ๋ฆฌ๋ก ์ ๋ฆฌํ๋ ๊ธฐ๋ก์ ๋ณด๋ฉฐ ํ๋ก์ ํธ ์ ์ฒด ๊ณผ์ ์ ์ ๋ฆฌํ์ฌ 4์ฃผ์ฐจ๋ก ๋๋์ด ์์ฑํ์ต๋๋ค.
2์ฃผ์ฐจ: ํ์ฌ๊ธ
๐ ๋ฐ๋ชจ๋ฐ์ด & ํ๋ก์ ํธ ์ ์ฒด ํ๊ณ
๐ฉ๐ป๐ป Github Repository https://github.com/sw2377/smoothie
๐ SUMMARY
9.1 FRY
1. ํ๋ก์ ํธ ์ด๊ธฐ ์ธํ
- ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ธํ
- CSS, Router ์ธํ
- ์ด๊ธฐ Layout ์์
2. ๊ฐ๋ฐ ์์ ์์
9.2 ~ 3 SAT, SUN
โก๏ธ ์ฃผ๋ง ๋์์ ์์ ์ ์กฐ๊ธ ํ ํ ๊ณต๋ถ (Redux Toolkit, TypeScript)
9.4 MON
1. ํ์ด์ง UI ์์ ์งํ
- ์ผ๋ถ ์ปดํฌ๋ํธ ์ ์(Card, Checkbox, Dropdown ๋ฑ)
- Header, Footer ํฌํจ ๋ ์ด์์ ์์
9.5 TUE
1. ํ๋ก์ ํธ๋ช ํ์ “์ค๋ฌด๋”
2. ๋ผ์ฐํ ์ฒ๋ฆฌ
3. (ํ๋ก ํธ ํ ์์ ์ํฉ ์์ธ ๊ณต์ )
9.6 WED
1. ๋ก๊ณ ์ ์ ์๋ฃ
2. ์๋ฒํต์ ํ์ธ
3. ๊ธฐ๋ฅ์ ์ ์ธํ UI ๊ตฌํ ์๋ฃ
4. front-end 1์ฐจ merge
9.7 THU
1. API call test
2. ๐ง๐ป 2์ฐจ ๋ฉํ ๋ง ์ธ์
9.8 FRY
- ์ด์ ๋ถํฐ ์งํ๋ ์นด๋ ์ปดํฌ๋ํธ ๋ถ๋ฆฌ
9.9 ~ 10 SAT, SUN
โก๏ธ ๊ณ์๋๋ ์ฃผ๋ง ์์
๐ 2์ฃผ์ฐจ ํ๊ณ
๐ ๋ชฉ์ฐจ
ํ๋ก์ ํธ ์ด๊ธฐ ์ธํ (Reset CSS, Router, Layout ๋ฑ)
โพ๏ธ CSS ์ด๊ธฐ ์ธํ (CSS Reset, ๊ณตํต CSS, CSS ๋ณ์)
โพ๏ธ ๋ผ์ฐํ ์ฒ๋ฆฌ (App.tsx)
ํ๋ก์ ํธ ๋ช ํ์ “์ค๋ฌด๋” & ๋ก๊ณ ์ ์ ์๋ฃ
์ปดํฌ๋ํธ ์ ์ ๋ฐ UI ๊ตฌํ
โพ๏ธ ๊ณตํต ์ปดํฌ๋ํธ ์ ์
โพ๏ธ ๋๋ฏธ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ UI ๊ตฌํ
๐ง๐ป 2์ฐจ ๋ฉํ ๋ง ์ธ์
1. ์ด๋ฏธ์ง ์ ์ฅ ์์น ๊ด๋ จ ์ง๋ฌธ
2. ์ปดํฌ๋ํธ ์์ ๊ด๋ จ ์ง๋ฌธ
โญ๏ธ ์นด๋ ์ปดํฌ๋ํธ ๋ถ๋ฆฌํ๊ธฐ
๐ 2์ฃผ์ฐจ, ์์ ํ๋ฉฐ ๋๋ ๋ฌด์์ ๋ฐฐ์ ์๊น? (What I Learned)
1. Vite ํ๋ก์ ํธ ์ธํ
2. Typescript์์ ReactComponent๋ก SVG ๋ถ๋ฌ์ค๊ธฐ
3. ์กฐ๊ฑด์ ๋ฐ๋ผ onClick ์ด๋ฒคํธ ํ์ฑํํ๊ธฐ
4. ์ํ๋ ํ์์ผ๋ก ๋ ์ง ๋ฐ์ดํฐ ๋ณด์ฌ์ฃผ๊ธฐ
ํ๋ก์ ํธ ์ด๊ธฐ ์ธํ (Reset CSS, Router, Layout ๋ฑ)
๊ฐ๋ฐํ๊ฒฝ ์ธํ (Vite) ํ์ ํ์๋ค๊ณผ ํ๋ก์ ํธ ์ด๊ธฐ ์ธํ ์ ์ํด ํ์ํ ๋ถ๋ถ๋ค์ ์ทจํฉํ์ฌ ์ด๊ธฐ ์ธํ ์ ํ๊ณ , ๊ฐ์ clone์ ๋ฐ์์ ๊ฐ์ธ ์์ ์ ์์ํ๊ธฐ๋ก ํ๋ค. ๊ณตํต์ผ๋ก ์ฌ์ฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ CSS ์ธํ ๋ฑ์ ๋ฏธ๋ฆฌ ํด๋๋๊ฒ ๊ฐ์ธ ์์ ์ ํธ๋ฆฌํ๊ณ , ์ถํ ์ฝ๋ ์ถฉ๋์ ๋ง์ ์ ์์๊ธฐ ๋๋ฌธ์ด์๋ค.
์ด ๋จ๊ณ์์ ์ฐ๋ฆฌ๋ ์ฌ์ฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น์ CSS ์ด๊ธฐ ์ธํ , Router ์ธํ ๋ฑ์ ํ๋ค. ๋, ์ด๋ฏธ์ง๋ ์์ด์ฝ์ assets ํด๋์ ์ ์ฅํ๊ธฐ๋ก ํ๊ณ , ํ์ ์ ์ฝ๋์ ํต์ผ๊ฐ์ ์ํด eslint์ prettier ์ธํ ๋ ์ถ๊ฐํ์๋ค.
โพ๏ธ CSS ์ด๊ธฐ ์ธํ (CSS Reset, ๊ณตํต CSS, CSS ๋ณ์)
CSS Reset ์์ ๊ณผ ๊ณตํต CSS ์ถ๊ฐ, CSS ๋ณ์ ๋ฑ์ ๋์์ธ ์์คํ ์ ์ฐธ๊ณ ํ์ฌ ์ด๊ธฐ ์ธํ ์ ํ๊ณ , React Router๋ฅผ ์ฌ์ฉํด ๋ผ์ฐํ ์ฒ๋ฆฌ๋ฅผ ๋ฏธ๋ฆฌ ํจ์ผ๋ก์จ ๊ฐ์ ์์ ํ๋ ํ์ด์ง์ ์ฝ๊ฒ ์ ๊ทผํ์ฌ ๊ฐ๋ฐ์ ํธ์์ฑ์ ๋์ด๋ ๊ฒ์ด ๋ชฉ์ ์ด์๋ค. ํ์ ์์ ํ๋ก์ ํธ๋ฅผ ์ด๋ค ์์ผ๋ก ์งํํ๋์ง ์๊ธธ์ด ์์ด ์ด๋ฌํ ์์๊ฐ ๋์ฒด์ ์ผ๋ก ํต์ฉ๋๋ ๊ฒ์ธ์ง๋ ์ ๋ชจ๋ฅด๊ฒ ์ง๋ง, ํ๋ฆฌํ๋ก์ ํธ ๋๋ ์ด์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์งํํด๋ณธ ๊ฒฐ๊ณผ ํ์คํ ์๋ฌด๋ฐ ์ธํ ์์ด ๊ฐ๋ฐํ๋ ๊ฒ๋ณด๋ค๋ ํธ๋ฆฌํ๋ค๋ ๊ฒ์ด ๋์ ์๊ฐ์ด์๋ค.
โพ๏ธ ๋ผ์ฐํ ์ฒ๋ฆฌ (App.tsx)
ํ์๋ค์ด ์ฌ์ฉํ ๋ผ์ฐํ ์ ๋ณด๋ฅผ ๋ฐ์ ๋ผ์ฐํ ์ธํ ๋ ์ด๊ธฐ์ ํ๋ค. ์ฒ์์๋ ์ต์ํ BrowserRouter๋ฅผ ์ฌ์ฉํ์ฌ ๋น ๋ฅด๊ฒ ์์ ์ธํ ์ ํ์๋๋ฐ, ์ผ์ฃผ์ผ ์ฏค ํ์ createBrowserRouter๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์์ผ๋ก ๋ฐ๊พธ์ด ํ์ํ path์ RootLayout, ErrorPage๋ฑ๋ ์ธํ ์ ํ์๋ค. ์ด๋ฏธ ์์ ํด์ผ ํ ํ์ด์ง๋ ์ ํด์ ธ ์์๊ธฐ ๋๋ฌธ์ ๋ผ์ฐํ ์ฒ๋ฆฌ๋ฅผ ๋จผ์ ํ๋๊ฒ์ด ๋ค๋ฅธ ํ์ด์ง๋ก ์ด๋์ ํ๋ ๋ฑ์ ์์ ์ ํจ์ฌ ํธํ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๋ค.
import { BrowserRouter, Route, Routes } from "react-router-dom";
import "./App.css";
/* ์์ ๋ผ์ฐํ
์ฒ๋ฆฌ */
import Main from "../pages/main/Main";
import UserList from "../pages/userlist/UserList";
// import ComponentName from "../pages/";
// import ComponentName from "../pages/";
function App() {
return (
<>
<BrowserRouter>
<div className="container">
<Routes>
<Route path="/" element={<Main />}></Route>
<Route path="/userlist" element={<UserList />}></Route>
{/* <Route path="/" element={<ComponentName />}></Route> */}
{/* <Route path="/" element={<ComponentName />}></Route> */}
</Routes>
</div>
</BrowserRouter>
</>
);
}
export default App;
โฌ๏ธ ์ด๊ธฐ์ ์์ ๋ผ์ฐํ ์ฒ๋ฆฌ
import { RouterProvider, createBrowserRouter } from "react-router-dom";
// import RootLayout from "./pages/Root"; // ์ ์ฒด์ ์ธ ๋ ์ด์์์ด ๋ค์ด๊ฐ๋ page (Header, Footer ํฌํจ)
// import ErrorPage from "./pages/Error"; // ์๋ฌํ์ด์ง
import Main from "../pages/main/Main";
// import Mypage from "../pages/mypage/Mypage";
import UserList from "../pages/userList/UserList";
import NewCard from "../pages/userList/NewCard";
import EditCard from "../pages/userList/EditCard";
import ProjectList from "../pages/projectList/ProjectList";
import Detail from "../pages/projectList/Detail";
import NewPost from "../pages/projectList/NewPost";
import EditPost from "../pages/projectList/EditPost";
const router = createBrowserRouter([
{
path: "/",
// element: <RootLayout />,
// errorElement: <ErrorPage />,
children: [
{ index: true, element: <Main /> },
/*** ๐ ๋ก๊ทธ์ธ/ํ์๊ฐ์
***/
{
path: "/",
children: [
{
path: "login",
// element: <Login />,
// children: [{ path: "findpassword", element: <PAGE /> }],
},
{
path: "signup",
// element: <Signup />,
},
],
},
/*** ๐ ๋ง์ดํ์ด์ง ***/
{
path: "mypage",
// element: <Mypage />,
/*
children: [
{
path: "profile",
// element: <PAGE />,
children: [
{
path: ":id",
// element: <PAGE />,
},
{
path: "edit",
// element: <PAGE />,
},
],
},
{
path: "summary",
// element: <PAGE />,
},
{
path: "review",
// element: <PAGE />,
},
],
*/
},
/*** ๐ ํ์ฐพ๊ธฐ ***/
{
path: "userlist",
children: [
{ index: true, element: <UserList /> },
{ path: "new", element: <NewCard /> },
{ path: "edit/:id", element: <EditCard /> },
],
},
/*** ๐ ํ์์ฐพ๊ธฐ ***/
{
path: "projectlist",
children: [
{ index: true, element: <ProjectList /> },
{ path: ":id", element: <Detail /> },
{ path: "new", element: <NewPost /> },
{ path: "edit/:id", element: <EditPost /> },
],
},
],
},
]);
function App() {
return <RouterProvider router={router} />;
}
export default App;
→ ์ด ๋ ๋ง์ดํ์ด์ง๋ฅผ ๋ด๋นํ๋ ํ์๋ถ์ด ๊ตฌ์กฐ๋ฅผ ์ง ์ํ๊ฐ ์๋์๊ธฐ ๋๋ฌธ์, ๋ง์ดํ์ด์ง๋ ์ฃผ์์ฒ๋ฆฌ๋ฅผ ํด๋์์๋ค. ๊ทธ๋ฆฌ๊ณ ๋น์์๋ ๋ ์ด์์๊ณผ ์๋ฌํ์ด์ง ์์ ๋ ์๋์ด์์๊ธฐ ๋๋ฌธ์ ์ฃผ์์ฒ๋ฆฌ๋ฅผ ํด๋์์๋ค. ์ดํ ์์ ํ๋ฉด์ ํ๋ผ๋ฏธํฐ ๋ฑ์ ๋ณ๊ฒฝ์ ์กฐ๊ธ ์์์ง๋ง, ํฐ ํ์ ์ ๊ตฌ์กฐ๋ฅผ ํฌ๊ฒ ๋ฒ์ด๋์ง ์์๋ค.
Root.tsx
์ ๋ ์ด์์ ์์
์ ๊ณง ์ถ๊ฐํ์๋๋ฐ, Header์ Footer๋ ๋ชจ๋ ํ์ด์ง์์ ๋
ธ์ถ์ด ๋๋๋ก ํ๊ณ , container๋ผ๋ ๊ณตํต className์ ๊ฐ์ง๋ div ์์ ์ฐ๋ฆฌ๊ฐ ๋ง๋ค ํ์ด์ง๋ค์ Outlet์ผ๋ก ์ฝ์
ํ์๋ค.
import { Outlet } from "react-router-dom";
import Header from "../components/common/Header/Header";
import Footer from "../components/common/Footer";
const RootLayout = () => {
return (
<>
<Header />
<div className="container">
<Outlet />
</div>
<Footer />
</>
);
};
export default RootLayout;
ํ๋ก์ ํธ ๋ช ํ์ “์ค๋ฌด๋” & ๋ก๊ณ ์ ์ ์๋ฃ
1์ฃผ์ฐจ ๋ ํ๋ก์ ํธ ๋ช ์ ์ ํ์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ ์ด๋ฒ์๋ ํ์๋ค๊ณผ ํจ๊ป ํ๋ก์ ํธ ๋ช ์ ์ ํ๊ธฐ๋ก ํ๋ค. ์ฌ์ค ๊ธฐํ ํ์ ์ด๋ฐ, ํ ๋ช ์ด “๋ธ๋ฃจ๋ฒ ๋ฆฌ์ค๋ฌด๋”๋ก ์ ํด์ง๋ฉด์ ์๊ฐํด๋ณธ ์๋น์ค ๋ช ์ด ์์๋๋ฐ, ์ข ๋ ๊ณ ๋ฏผํด๋ณด๊ณ ์ถ์ด์ ์๊ธฐํ์ง ์์์๋ค. ์ด๋ฒ์ ์๋น์ค ๋ช ํฌํ๋ฅผ ํ๋ฉด์ ์๊ฒฌ์ ๋๋๋ฐ, ๊ทธ๊ฒ ๋ฐ๋ก “์ค๋ฌด๋”์๋ค.
“์ค๋ฌด๋๊ฐ ์ฌ๋ฃ๋ฅผ Mixํ์ฌ ํ๋์ ์์ฑ๋ ์๋ฃ๋ฅผ ์ ๊ณตํ๋ ๊ฒ์ฒ๋ผ, ์ฐ๋ฆฌ๊ฐ ์ ๊ณตํ๋ ค๋ ์๋น์ค๋ ์์ด๋์ด๋ฅผ Mixํ๊ณ , ๊ฐ์์ ์ญ๋๊ณผ ์ฝ๋๋ฅผ Mixํ์ฌ ํ๋์ ํ๋ก๋ํธ๋ฅผ ๋ง๋๋ ค๋ ๊ฒ์ด๋ฏ๋ก ๊ทธ๋ฐ ์๋ฏธ๋ก ‘์ค๋ฌด๋’๋ผ๋ ์ด๋ฆ์ ์ด๋จ๊น? ์ฐ๋ฆฌ ํ ์ด๋ฆ์ด ๋ธ๋ฃจ๋ฒ ๋ฆฌ์ค๋ฌด๋ ์ด๊ธฐ๋ํ๊ณ .”
์ด๋ฌํ ์๋ฏธ์์ ์๊ฐํด๋ณธ ์ด๋ฆ์ธ๋ฐ ๋ญ๊ฐ ์กฐ๊ธ ์ค๊ธ๊ฑฐ๋ ค์ ๋จ์ถํด์ ํ์๋ค์๊ฒ ์ค๋ช ์ ํ๊ณ ์๊ฒฌ์ ๋๋ค. ๋ง์ฅ์ผ์น๋ก ๋ด๊ฐ ๋ธ ์๊ฒฌ์ด ์ฐ๋ฆฌ์ ํ๋ก์ ํธ ๋ช ์ด ๋์๋ค ๐
ํ๋ก์ ํธ ๋ช ์ด ์ ํด์ง๊ณ , Header์ ๋ก๊ณ ๋ฅผ ๋ฃ๊ณ ์ถ์ด์ ๋ก๊ณ ๋ ์ ์ํ๋ค. ๋ฐฑ์๋์ ํ์ฅ๋๊ณผ ํ์๋ถ๊ป์ ์๊ฐํ๋ ๋ก๊ณ ๊ฐ ๋ด๊ฐ ์๊ฐํ ๋ถ๋ถ์ด๋ ๋น์ทํ ๋ถ๋ถ์ด ์์ด์(์ค๋ง์ผํ๋ ์บ๋ฆญํฐ) ์ฐ๋ฆฌ์ ์๊ฒฌ์ ํตํฉํด์ ๋ง๋ค์๋ค. ์ฌ์ค ๋ฑํ ๋์์ธ์ ์๋กญ๊ฒ ํ ๋ฅ๋ ฅ์ ์์ด์ SMOOTHIE๋ผ๋ ๊ธ์๋ก ๋ง๋ ๋ก๊ณ ์๋๋ฐ, ๊ท์ฌ์ด ๋ก๊ณ ๋ก ํ์ํด์ ํ์๋ค์ด ๋ง์กฑํ๋ค๋ ์ด์ผ๊ธฐ!
์ปดํฌ๋ํธ ์ ์ ๋ฐ UI ๊ตฌํ
๋ณธ๊ฒฉ์ ์ผ๋ก ๊ฐ๋ฐ ์์ ์ ์์ํ๊ธฐ ์ ์ ์นธ๋ฐ ๋ณด๋์ ํ ์ผ์ ๋ฑ๋กํ๋ ๊ฒ ๋ถํฐ ์์ํ์๋ค. ์ด๋ฒ ๋ฉ์ธ ํ๋ก์ ํธ์์๋ ํ๋ฆฌ ํ๋ก์ ํธ์์๋ ์ ๋๋ก ํ์ง ๋ชปํ ์คํ๋ฆฐํธ ๋จ์์ Agileํ ๊ฐ๋ฐ์ ํ๊ณ ์ถ์์ง๋ง, ์ด๋ฒ ํ ํ๋ก์ ํธ ์ญ์ ‘Agileํ๊ฒ ๊ฐ๋ฐํ๊ธฐ’๋ ํ์ง ๋ชปํ๋ค. ์๋ง ์ด๊ธฐ ํ์๋ฅผ ํ ๋ ๊ทธ๋ฌํ ๋ถ๋ถ์ ์์ํ์ง ์์๊ธฐ ๋๋ฌธ์ ์คํ๋์ง ์์๋ ๊ฒ ๊ฐ๋ค. ์ฃผ๋ง๋ค ์คํ๋ฆฐํธ ๋จ์๋ก ๊ฐ๋ฐ์ ํ๊ณ , ํ ์คํธ์ ๋ค์ ์ผ์ ์ ํจ๊ป ๋ ผ์ํ๊ณ ๋ฑ๋กํ์๊ณ ์ ์ํ๊ณ ์ถ์์ง๋ง, ๊ทธ ๋ง์ ํ์ง ๋ชปํ๋ค.
์ฌํผ ๊ทธ๋์ ๋ณธ์ธ์ด ํ ์ผ์ ์์์ ๋ฑ๋กํ๊ณ , ๊ฐ๋ฐํ๋ ๋ฐฉ์์ผ๋ก ์งํ๋์๋ค.
๊ทธ๋ฐ๋ฐ ์นธ๋ฐ๋ณด๋์ ์ด์์นด๋๋ฅผ ์์ฑํ ๋์๋ ๊ฐ๊ฐ์ ์์ ์ ๋ฐ๋ก ๋ง๋ค์๋๋ฐ, ๋ง์ ์์ ์ ํ๋ค๋ณด๋ ์ด์์นด๋๋ฅผ ์๋ค๊ฐ๋ค ํ๋ฉฐ ์์ ํ๊ฒ ๋๋๋ผ๋…๐ซ ๋ถํธ์บ ํ์์ ํ์ต์ ํ ๋์๋ ํ๋ก์ ํธ ์นธ๋ฐ์ ‘In Progress’์๋ ๋์๊ฒ ํ ๋น๋ ํ๋์ ์์ (์ด์์นด๋)์ ๊ฐ์ ธ๋ค ๋๊ณ , ๋ด๊ฐ ํ์ฌ ๋ฌด์จ ์์ ์ ํ๋์ง ๋ ๋ฟ๋ง ์๋๋ผ ๋ค๋ฅธ ํ์๋ค์๊ฒ๋ ๋ณด์ฌ์ฃผ๋ ๊ฒ์ด๋ผ ๋ฐฐ์ ๋๋ฐ, ๋ ๊ฐ๋ฅผ ๋๊ฒ ๋๋… ์ด๋ป๊ฒ ํด์ผ ์๋์ ๋ง๊ฒ ๊ณํ์ ์ด๊ณ ํจ์จ์ ์ธ ์์ ์ ํ๋์ง๋ ์๋ฌด๋๋ ๊ฒฝํ์น๊ฐ ์์ฌ์ผ ํ ๊ฒ ๊ฐ๋ค.
๐ง ์ ์คํ๋ฆฐํธ ๋จ์๋ก ๊ฐ๋ฐ์ ํ์๊ณ ์ ์์ ํ์ง ๋ชปํ๋, ์ง๊ธ ์๊ฐํด๋ณด๋ ์ฐ์ ๊ธฐํ ๋จ๊ณ๊ฐ ๊ธธ์ด์ง์ ๋ฐ๋ผ ์ด๋ฐ์ ์๋์ง๋ฅผ ๋ง์ด ์๋นํด์ ์ด๋ฐ ์ ์์ ํ๋ฉด ๋๋ฌด ๋ฑ๋ฑํ ๋๋์ด ๋ค๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ค์๊ณ , ๋ด๊ฐ ์ด๋ฏธ ์๊ฒฌ์ ๋ธ ๋ค๋ฅธ ์ฌํญ๋ค์ด ๋ง์๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐ ๋จ๊ณ์์๊น์ง ์ผ์ ์ ๋ํ ๊ฐ์ ์ ํ๊ธฐ๊ฐ ์ฝ์ง ์์๋ ๊ฒ ๊ฐ๋ค. ๊ตณ์ด ๊ทธ๋ ๊ฒ ๋งํ์ง ์์๋ ํ์๋ค ๋ชจ๋ ์ผ์ ๊น์ง ์ ๋ง๋ฌด๋ฆฌํ ๊ฑฐ๋ผ๋ ๋ฏฟ์๋ ์์๋ค.
ํ์ง๋ง ๊ธฐํ ์ ๊ฐ๋ฐ์ค์ฝํ๊ฐ ๋๋ฌด ํฐ๊ฒ ๊ฐ๋ค๊ณ ํ์ฌ ์ผ์ ์ ๋ง๊ฒ ๋ฒ์๋ฅผ ์ค์๋ค๊ณ ํด๋ ๋ง๋ค๊ธฐ๋ก ํ ๊ธฐ๋ฅ์ด ๋ง์๊ณ , ์ค์ ๋ก ํ๋ก์ ํธ๊ฐ ๋๋ ๋ ์ฆ์์ ์๊ฐ์ ์ซ๊ธฐ๊ฒ ๋์๋ค. ์์์น ๋ชปํ ์ค๋ฅ๋ ์์๊ณ , ์๊ฐ์ด ๋ถ์กฑ์ ๋ณธ๋ ๊ตฌํํ๊ธฐ๋ก ํ ๊ธฐ๋ฅ์ด ๋ฏธํกํด์ง๊ธฐ๋ ํ๋ค. ํ ์คํธ๋ฅผ ํ ์๊ฐ์ด ๊ฑฐ์ ์์๋ค๋ ๊ฒ๋ ๋ฌธ์ ์๋ค. ์ข ๋ ๋ฒ์๋ฅผ ์ค์ด๊ณ , ์ผ์ ์ ๋ณด์์ ์ผ๋ก ์ก๊ณ , ์ฃผ๊ธฐ์ ์ผ๋ก ํ ์คํธ๋ฅผ ํ๋๋ผ๋ฉด ๋ ์ข์ ๊ฒฐ๊ณผ๋ฌผ์ด ์์ง ์์์๊น, ๋ผ๋ ์๊ฐ์ ํ๋ก์ ํธ๊ฐ ๋๋๊ณ ๋์์ผ ๋ค์๋ค.
โพ๏ธ ๊ณตํต ์ปดํฌ๋ํธ ์ ์
๋๋ ๊ณตํต ์ปดํฌ๋ํธ๋ฅผ ๋ง๋๋ ๊ฒ๋ถํฐ ์์์ ํ๋ค. ์นด๋๋ ์ก์ ๋ฒํผ, ์ ๋ ํธ๋ฐ์ค ๋ฑ์ ์ฌ๋ฌ ํ์ด์ง์์ ์ฌ์ฉ๋์๊ธฐ ๋๋ฌธ์ ์ปดํฌ๋ํธ๋ฅผ ๋จผ์ ๋ง๋ค์ด๋๋ฉด ์ถํ ์์ ์ด ์ฉ์ดํ ๊ฒ์ด๋ผ๊ณ ํ๋จํ๋ค. ๊ฒ๋ค๊ฐ ๋ฆฌ์กํธ๋ ์ปดํฌ๋ํธ ๋จ์๋ก ๊ฐ๋ฐ์ ํ๊ธฐ์ ์ ๋ง ์ข์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๋๊ฐ!
๊ทธ๋ฐ๋ฐ ์ฌ์ค ๋ด๊ฐ ๊ธฐํํ ํ๋ก์ ํธ๋ฅผ ๋ฆฌ์กํธ๋ก ๊ฐ๋ฐํ๋ ๊ฒ์ ์ฒ์์ด๋ผ, ์ปดํฌ๋ํธ๋ฅผ ๋ง๋๋ ๊ฒ์กฐ์ฐจ ์ฝ์ง๊ฐ ์์๋ค. ๋ง๋ค์ด์ผํ ์ปดํฌ๋ํธ๋ค์ ์ฌ๋ฌ ํ์ด์ง์์ ์ฌ์ฉ๋ ๊ฒ์ ์๊ฐํด์ผ ํ๋ค. ์๋ง์ props๋ฅผ ๋ฐ๊ณ , style์ ์ง์ ํด์ผ ํ์ผ๋ฉฐ, ์ฒ์ ์ฌ์ฉํด๋ณธ ํ์ ์คํฌ๋ฆฝํธ๊น์ง ์ ์ฉํด์ผ ํ์ผ๋ ๊ฐ๋ฐ ์ด๊ธฐ์๋ ์ด์ฒด์ ๋๊ตญ์ด์๋ค. ๊ทธ๋์ ์ฒซ ์ปดํฌ๋ํธ ๋ช ๊ฐ๋ ์ด๋ฐ์๋ ์ต์ํ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์์ฑ์ ํ๊ณ , ํ์ ์คํฌ๋ฆฝํธ๋ก ํฌํ ํ๋ ํํ๋ก ์์ ์ ํ๋ค. ์ปดํฌ๋ํธ ํ๋๋น ์ฝ๋๊ฐ ๊ธธ์ง ์๊ณ , ๊ฐ๋จํ๊ธฐ ๋๋ฌธ์ ๊ณต๋ถํ๋ ๋ด์ฉ์ ์ฐพ์๊ฐ๋ฉฐ JS → TS๋ก ๋ฐ๊พธ๋ ์์ ์ ํ๋๋ฐ ํฌ๊ฒ ์ด๋ ต์ง ์์๋ค. ์ด๋ ๊ฒ ์์ ์ ํ๋ฉฐ ํ์ ์คํฌ๋ฆฝํธ์ ์กฐ๊ธ ์ต์ํด์ก์ ๋ ๋ถํฐ๋ ํ์ ์คํฌ๋ฆฝํธ๋ก ๋ฐ๋ก ์์ฑ์ ํ๊ธฐ ์์ํ๋ค. React์์ ํ์ ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ด๋ ต๊ฒ๋ง ๋๊ปด์ก์๋๋ฐ, ์ด์ ๋ ์กฐ๊ธ ์ต์ํด์ก๊ณ , ์ฌ์ฉํ๋ ์ด์ ๋ฅผ ํ์คํ ์ ๊ฒ ๊ฐ์๋ค. ํ์ ์ด ๋ช ํํด์ง๋, props๋ฅผ ๋ฐ๊ธฐ๋ ํธ๋ฆฌํ๊ณ , ์ฝ๋ ์๋์์ฑ๊น์ง ํด์ฃผ๋ ๊ฐ๋ฐ์ ํจ์จ์ฑ๋ ๋์์ก๋ค.
์ฌํผ ๊ทธ๋ ๊ฒ ์ฌ๋ฌ๋ฒ์ ์ํ์ฐฉ์ค ๋์ Card, ActionButton, Checkbox, SearchInput, Selectbox ๋ฑ์ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์๋ค. ์ด ์ปดํฌ๋ํธ๋ค ๋ํ ํ์ด์ง ์์ ์ค์ ์์ ๋๋ ๊ฒฝ์ฐ๊ฐ ๋๋ฌ ์์์ง๋ง, ํ๋์ ๋ชฉ์ ์ ์ํด ์์ฑ๋ ๊ฐ๊ฒฐํ๊ณ ๊น๋ํ ์ฝ๋๋ค์ด ์ ์ง ๋ง์์ ๋ค์ด์ ๋ฟ๋ฏํ๋ค. ๋ง์์ ์๋๋ ์ฝ๋๊ฐ ์ ๋ง ๋ง์์ง๋ง, ์์ ํ๋์ ์ปดํฌ๋ํธ๊ฐ ์์ฑ๋ ๊ฒ์ ๋ณด๋ ๊ธฐ๋ถ์ด ์ข์๋ค. ์ฌ๊ธฐ์ ๊ธฐ ๋ค ๊ฐ์ ธ๋ค ์ฐ๊ณ ์ถ์๋ค. ํด๋ก ์ฝ๋ฉ์ด ์๋, ์ฒ์ ๋ง๋ค์ด๋ณธ ๋์ ์ปดํฌ๋ํธ ์๋ค.
โพ๏ธ ๋๋ฏธ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ UI ๊ตฌํ
ํผ๋ธ๋ฆฌ์ฑ์ ํ๋ ๊ฒฝํ ๋๋ฌธ์ธ์ง ๊ธฐ๋ฅ๋ณด๋ค UI๋ฅผ ๋จผ์ ๊ฐ๋ฐํ๋ ๊ฒ์ด ํธํ๋ ๋๋ UI๋ฅผ ๋จผ์ ๊ตฌํํ๋ค. API Call ํ ์คํธ ์ , UI ๊ตฌํ์ ์ํด ํ๋ฉด์ ํ์ํ ๋ฐ์ดํฐ๋ ๋ก์ปฌ์์ ๋๋ฏธ๋ฐ์ดํฐ๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๊ธฐ๋ก ํ๋ค.
ํ๋ก์ ํธ๊ฐ ์งํ๋๋ ๋์ ์ด๋ฐ์ ๋ฐ ์์ ์ด ๋ฐ๋ณต ๋์๊ธฐ ๋๋ฌธ์ ๋ฐฑ์๋์์๋ API ๋ช ์ธ๋ฅผ ์ง์์ ์ผ๋ก ์ ๋ฐ์ดํธ ํด์ผํ๊ณ , ์๋ฒ๊ฐ 24์๊ฐ ๋ด๋ด ์ผ์ ธ์๋ ์ํ๊ฐ ์๋์๊ธฐ ๋๋ฌธ์ ์ํํ ํ๋ฉด ๊ตฌํ์ ์ํด ๋๋ฏธ๋ฐ์ดํฐ๊ฐ ํ์ํ๋ค๊ณ ์๊ฐ๋์๋ค. (ํ๋ก์ ํธ ์ด๋ฐ์๋ ๊ณผ๊ธ ์ด์๋ก ์๋ฒ๋ฅผ ์ผ์ฃผ์ผ์ 3๋ฒ, 9์์์ 6์๊น์ง๋ง ์ผ๊ธฐ๋ก ํ์๋ค.)
๋ฌธ์ ๋ ๋๋ฏธ๋ฐ์ดํฐ๋ฅผ ์ด๋ค ์์ผ๋ก ๊ฐ์ ธ์ค๋๊ฒ ์ข์์ง ๊ฒฐ์ ํ๋ ๊ฒ์ด์๋ค. ๊ด๋ จํ์ฌ ์๋์ ๊ฐ์ ์๋๋ฅผ ํด๋ณด์๋ค.
1. ์ฒ์์๋ ๋ก์ปฌ์ ๋๋ฏธ๋ฐ์ดํฐ๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ์๋๋ฐ, (GET ์์ฒญ๊ณผ ๋น์ทํ) ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ์์ ๋ฐ์ ๋์ง ์๋ ๋จ์ ์ด ์์๋ค. POST๋ PATCH, DELETE์ ๊ฐ์ ์์ ์ ํ ์๊ฐ ์์๋ค.
2. mock server๋ฅผ ๋ง๋ค ์ ์๋ json-server์ firebase๋ฅผ ์ฌ์ฉํด๋ณด์๋๋ฐ, ๋ด๊ฐ ์ํ๋๋๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ๊ฐ ๋ฒ๊ฑฐ๋กญ๊ฑฐ๋ ์ ๋์ง ์์๋ค.
json-server๋ฅผ ์๋ก๋ค๋ฉด ์๋์ ๊ฐ์ ๋ฌธ์ ๊ฐ ์์๋ค.
// ์๋์ ๊ฐ์ด ๋์ด์ผ ์๋ํฌ์ธํธ /users๋ก ์ด๋ ํ ๋ฉ์๋์ ์ํ์ด ๋๋ค๊ณ ํ๋๋ฐ
users: []
// ์ฐ๋ฆฌ ํ ํ๋ก์ ํธ์ ๋ฐ์ดํฐ๋ ์ด๋ฐ ํ์์ผ๋ก ๊ตฌ์ฑ๋์ด ์์ด ์์ฒญ์ ์ด๋ป๊ฒ ๋ณด๋ด์ผ ํ ์ง ๋ชฐ๋๋ค.
// /users/data๋ก ๋ณด๋ด๋ฉด ๋์์๊น? ์๋ง ์๋์ด์ ๋ด๊ฐ ์ด๋ฐ ๊ณ ๋ฏผ์ ํ๋ ๊ฒ ๊ฐ์๋ฐ...
users: {
data: []
}
๋ฐฉ๋ฒ์ ๋ ์ฐพ์๋ดค๋๋ผ๋ฉด ์๋ง ๋ฌด์จ ํด๊ฒฐ์ฑ ์ด ์์์ ๊ฒ ๊ฐ์ง๋ง, ๋น์์๋ ์๊ฐ์ด ์ค์ํ๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์ฐพ์๋ณด๊ธฐ๋ก ํ๋ค.
3. ๋ก์ปฌ์์ ์ง์ Java Server๋ฅผ ์ผ๋ ๋ฐฉ๋ฒ๋ ์๋ํด ๋ณด์๋ค. ๊ทธ๋ฐ๋ฐ ์ด๊ฑด Java๋ฅผ ์ ํ ๋ชจ๋ฅด๋ ๋์๊ฒ ๋๋ฌด๋ ๊ณ ๋์ด๋์๋ค.
ํ๋ก์ ํธ๊ฐ ๋๋๊ณ ๋์์ผ Java Server๋ฅผ ๋ก์ปฌ์์ ์ผ๋ ๊ฑธ ์ฑ๊ณตํ์๋๋ฐ, ํ๋ก์ ํธ๋ฅผ ํ๋ ๋์ค์๋ Java๋ฅผ ์ ํ ๋ชจ๋ฅด๋๋ฐ ํ๊ฒฝ๋ณ์๋ ์ค์ ํ๊ณ , ์๋ฒ๋ ์คํํ๋ ค๋ ์๊ฐ๋ ๋ง์ด ๋ค๊ณ , ๊ฒฐ๊ตญ ์ฑ๊ณตํ์ง๋ ๋ชปํด์ ํฌ๊ธฐํ์๋ค.
๊ฒฐ๊ตญ ์ฌํํ๊ณ ๊ณ ์ ์ ์ธ ๋ฐฉ์์ด์์ง๋ง, ์ฒซ๋ฒ์งธ ๋ฐฉ๋ฒ์ผ๋ก ๋์๊ฐ๋ค. UI ๊ตฌํ์ด ๋ชฉ์ ์ด์๊ธฐ ๋๋ฌธ์ ๊ฐ๋จํ ๋ก์ปฌ์ ๋๋ฏธ๋ฐ์ดํฐ๋ฅผ ์์ฑํด ๊ฐ์ ธ์ค๋ ๋ฐฉ์์ ์ ํํ๋ค. ๋์ค์ ๊ธฐ๋ฅ ๊ตฌํ์ ์ค์ HTTP ์์ฒญ์ผ๋ก CRUD ํ ์คํธ๋ฅผ ํ๋ฉด ๋์๊ธฐ ๋๋ฌธ์ ํฌ๊ฒ ๋ถํธํ ๋ฐฉ๋ฒ์ ์๋์๋ค.
๊ฒ๋ค๊ฐ ์ด ๋ฐฉ๋ฒ์ด ์์ธ๋ก ๊ด์ฐฎ๋ค๊ณ ์๊ฐ๋์๋ ์ด์ ๊ฐ ์์๋๋ฐ, ๋ก์ปฌ์ ๋๋ฏธ๋ฐ์ดํฐ๊ฐ ์์ผ๋ ๋คํธ์ํฌ ์์ฒญ์ด ์คํจํ์ ๋ ๋ก์ปฌ ๋๋ฏธ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ์ฝ๋๋ฅผ ์์ฑํด๋๋ฉด UI ์์ ์ ํ๋๋ฐ ์๋ฌด๋ฐ ๋ฌธ์ ๊ฐ ์์๊ณ , API ๋ช ์ธ๊ฐ ์ ๋ฐ์ดํธ ๋์ด Response๊ฐ ์์ ๋์์ ๋ ์๋ํฐ์์ ๊ฐ๋จํ ๋๋ฏธ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝ๋ง ํ๋ฉด ๋คํธ์ํฌ ํต์ ์ด ์ด๋ฃจ์ด์ง์ง ์์๋ ํ๋ฉด์ ๋ณด์ฌ์ง๋ ๋ถ๋ถ์ ๋น ๋ฅด๊ฒ ํ์ธํ ์ ์์ด์ ์๊ฐ๋ณด๋ค ํธ๋ฆฌํ๋ค. ๊ฐ๋ฐ ์ด๋ฐ์๋ ์๋ฒ๊ฐ ํญ์ ์คํ๋ ์ํ๊ฐ ์๋์๊ธฐ ๋๋ฌธ์ ์ด์ฉ ์ ์์ด ๋๋ฏธ๋ฐ์ดํฐ๊ฐ ํ์ํ๋ค.
ํ์ง๋ง ์ญ์ ๋ด ์ ๋ง๋๋ก ์๋ฒ๋ฅผ ๊ฐ๋จํ๊ฒ ๊ตฌ์ถํ ์ ์์๋ค๋ฉด ์์ ์ด ํจ์ฌ ํธ๋ฆฌํ์ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ค์๊ณ , ์ธ์ ๊ฐ๋ ๊ทธ๋ ๊ฒ ํ ์ ์๋๋ก ํด๋ด์ผ๊ฒ ๋ค๋ ์๊ฐ์ด ๋ฉ์ธ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉฐ ์ฒ์์ผ๋ก ํ์ ํ์๋ค.
๐ง ์ฌ๋ด์ด์ง๋ง, html๊ณผ css๋ฅผ ๋ฐฐ์ฐ๋ฉฐ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ํ๊ณ ์ถ๋ค๊ณ ์๊ฐํ์๋ค. ์ ์ ์ธ ํ๋ฉด์ ๋ณด๋ฉฐ, ๋์ ์ธ ํ๋ฉด ๋ํ ๋ด๊ฐ ์ง์ ๋ง๋ค๊ณ ์ถ๋ค๋ ์๊ฐ์ด ๋ค์๋ค. ํ๋ฉด ์๋จ์์๋ ์ด๋ค ์ผ์ด ์ผ์ด๋๋์ง, ์ด๋ค ๊ฑธ ๋ฐฐ์ฐ๋ฉด ๋๋ ์ ํ๋ก ํธ์์ ์ผ์ด๋๋ ์ผ๋ค์ ๋ชจ๋ ์ ์ดํ ์ ์์์ง ๊ถ๊ธํ๋ค. ๊ทธ๋ฐ๋ฐ ํ๋ก ํธ ๊ฐ๋ฐ์ ๋ฐฐ์ฐ๋ค๋ณด๋ ํ๋ฉด ๋ท๋จ์์๋ ๋ ์ด๋ค ์ผ๋ค์ด ์ผ์ด๋๋์ง๊ฐ ๊ถ๊ธํด์ก๋ค. ๊ฐ๋จํ๊ฒ๋ผ๋ ์๋ฒ๋ ๊ตฌ์ถํด๋ณด๊ณ ์ถ๋ค๋ ์๊ฐ์ด ๋ค์๋ ๊ฒ์ด๋ค.
โญ ๋์ค์ ์๋ฒ๊ฐ ๊ตฌ์ถ์ด ๋๊ณ ๋ ํ์๋ ์๋ฒ ํต์ ์ด ์ ๋๋์ง ํ์ธํ๋ ๊ณผ์ ์ด ํ์ํ๋ค. ์ฐ๋ฆฌ๋ Axios๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ํด์ Axios๋ฅผ ์ฌ์ฉํด ์๋ฒ ํต์ ์ด ๋๋ ๊ฒ๋ ํ์ธ์ ํ๋ค. ์ค๊ฐ์ ์ญ์ ํผํด๊ฐ์ง ๋ชปํ๋ CORS ์๋ฌ๊ฐ ์์์ง๋ง, ๋ฐฑ์๋ ํ์๋ถ์ด ํด๊ฒฐํด์ฃผ์ จ๋ค. ๐
UI ๊ตฌํ์ ํ๋ฉฐ ์๊ฐ์ ํด๋ดค๋๋ฐ, ๋ด๊ฐ ๋งก์ ๊ฒ์ํ์์๋ ๋๋์ฒด ๋ฌด์์ ์ ์ญ ์ํ๋ก ๋์ด์ผ ํ๋๊ฑธ๊น? ์ด๋๊น์ง๋ง ํด๋ Redux Toolkit์ ์ด๋์ ์ฌ์ฉํด์ผ ํ ์ง ์ ํ ๊ฐ์ด ์กํ์ง ์์๊ณ , React์ TypeScript๋ฅผ ์ฌ์ฉํ๋ ๊ฒ ๋ง์ผ๋ก ๋จธ๋ฆฌ๊ฐ ์ํ ์๊ธฐ์๋ค. ๊ฒฐ๊ตญ ์๋ฒ ํต์ ์ด ๋๋๊ฑด ํ์ธํ์ง๋ง, ์ฌ์ค ๊ทธ ๋ค์๋ถํฐ๋ ์ง๋๊ฐ ์ ๋ง ์๋๊ฐ์๋ค… ๐ฅฒ
๐ง๐ป 2์ฐจ ๋ฉํ ๋ง ์ธ์
๋๋ฒ์งธ ๋ฉํ ๋ง ์๊ฐ์ด ๋ค๊ฐ์๋ค. 1์ฃผ ์ฐจ์ ๋ฉํ ๋ง ์ธ์ ์ ๊ธฐํ ๋จ๊ณ์์ ์ด๋ฃจ์ด์ก๊ธฐ ๋๋ฌธ์, ์ฝ๋์ ๊ด๋ จ๋ ์ง๋ฌธ์ด ๋ฑํ ์์๋๋ฐ, ์์ ์ ์์ํ๊ณ ๋ณด๋ ์ญ์ ๊ถ๊ธํ ๋ถ๋ถ์ด ์๊ฒจ์ ๋ฉํ ๋ง ํ์ด์ง์ ์ง๋ฌธ์ ๋ฏธ๋ฆฌ ์์ฑํ๋ค.
1. ์ด๋ฏธ์ง ์ ์ฅ ์์น ๊ด๋ จ ์ง๋ฌธ
์ฐ๋ฆฌ๊ฐ ๊ตฌํํ ํ์ด์ง ๋ด์ ์์ ๊ฐ์ด ๊ธฐ์ ์คํ์ ์ด๋ฏธ์ง๋ฅผ ๋ณด์ฌ์ฃผ๋ ๋ถ๋ถ์ด ๋ช์ฐจ๋ก ๋ฑ์ฅํ๋๋ฐ, ์ด๋ฌํ ์ด๋ฏธ์ง์ ์ ์ฅ์ ๋ณดํต ์ด๋์์ ํ๋์ง ๊ถ๊ธํ๋ค.
์ด๋ฏธ์ง ๋ฟ๋ง ์๋๋ผ ์ด๋ฆ๊ณผ ์ค๋ช ๋ ํ์ํ๊ธฐ ๋๋ฌธ์, ์๋ฒ์ธก์์ ๋ฐ๋ก ์ ์ฅ์ ํด๋๋๊ฒ ์ข์ ๊ฒ ๊ฐ๋ค๊ณ ์๊ฐ์ ํ์๋ค. ๊ทธ๋ฐ๋ฐ ๋ฌด์จ ์ด์ ์์์๋์ง ํ์ ์ค ์๋ฒ์ธก์์๋ ์ด๋ฆ๋ง string์ผ๋ก ์ ์ฅํ๊ธฐ๋ก ํ ์ํ์๊ธฐ ๋๋ฌธ์ ํ๋ก ํธ์ชฝ์์ ํ์ํ ์ด๋ฏธ์ง๋ ์ค๋ช ๋ฑ์ ์ด๋ป๊ฒ ์ ์ฅํ๋ฉด ์ข์์ง์ ๋ํ ์ง๋ฌธ์ด์๋ค.
// ๋ง์ฝ ํ๋ก ํธ๋จ์์ ์ฒ๋ฆฌํ๋ค๋ฉด, public์ assets์ ์ด๋ฏธ์ง๋ฅผ ์ ๋ถ ์ ์ฅํด๋๊ณ ,
// src ํด๋ ๋ด์ language.ts ํ์ผ์ ๋ง๋ค์ด์ ์ ์ฅํ๋ ๋ฐฉ๋ฒ์ ์ด๋ค๊ฐ์?
const languageList = [
{
language_id: 1,
language_name: "javascript",
language_img: process.env.PUBLIC_URL + `/assets/javascript.png`,
language_descript: "javascript๋ ~~ํ ์ธ์ด๋ก, ~~ํ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค."
},
]
๋ฉํ ๋์ ๋ต๋ณ์ ์๋์ ๊ฐ์๋ค.
/*
์๋ฒ์ ๋ฐ๋ก ์ ์ฅํ์ง ์๋๋ค๋ฉด ์์ ๊ฐ์ด ๊ตฌํํ๋ ๊ฒ์ด ์ข์๋ณด์
๋๋ค. :)
๋ค๋ง ํด๋น ๋ฆฌ์คํธ ์์ object๊ฐ ์ด๋ฏธ language์์ ์๊ณ ์์ผ๋ฏ๋ก
key ๊ฐ์ `language_*`์์ ์๋ถ๋ถ์ ๋นผ๋ ์ข์ ๊ฒ ๊ฐ์ต๋๋ค.
์กฐ๊ธ ๋ ํจ์จ์ ์ผ๋ก ์์ฑํ๊ธฐ ์ํด name์ ํค ๊ฐ์ผ๋ก ํ๋ object ํํ๋ก ๋ง๋ค์ด๋ ์ข์๋ณด์
๋๋ค.
*/
{
"Javascript": {
....,
},
}
์ด ๋ถ๋ถ์ ์ค์ ๋ก ๋ฉํ ๋ง ์๊ฐ์ ์์ ์ฝ๋๋ฅผ ๋ณด์ฌ์ฃผ๋ฉฐ ์ค๋ช ์ ํด์ฃผ์ จ๋ค. ์ฐ์ ์๋ฒ์ ๋ฐ๋ก ์ ์ฅ์ ํ์ง ์๋๋ค๋ฉด, ์์ ๊ฐ์ด name์ ํค ๊ฐ์ผ๋ก ํ๋ object ํํ๋ฅผ ๋ง๋๋ ๊ฒ์ ์ถ์ฒํ์ จ๋ค. ์, ์ ๋ ๊ฒ๋ ํ ์ ์๊ฒ ๊ตฌ๋ ๋ผ๋ ์๊ฐ์ด ๋ค์๋ค.
(๊ฒฐ๋ก ์ ์ผ๋ก๋, ์ด ๋ถ๋ถ์ ํ๋ก ํธ ํ์๋ถ๊ป์ devicon์ด๋ผ๋ ์คํ์์ค๋ฅผ ํ์ฉํด getLogo๋ผ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด์ ์ฌ์ฉํ๋ค. ์ด๋ฏธ์ง๋ฅผ ํ๋์ฉ ์ ์ฅํ๋ ๋ฐฉ๋ฒ๋ณด๋ค ์ธ๋ถ์์ ๋ถ๋ฌ์ค๋ ๋ฐฉ๋ฒ์ ์ ํํ๋ ๊ฒ์ด๋ค.)
2. ์ปดํฌ๋ํธ ์์ ๊ด๋ จ ์ง๋ฌธ
๋๋ฒ์งธ ์ง๋ฌธ์ ์นด๋ ์ปดํฌ๋ํธ ๊ด๋ จํ ์ง๋ฌธ์ด์๋ค. ๋ด๊ฐ ๋ด๋นํ๊ฒ ๋ ํ์ฐพ๊ธฐ/ํ์์ฐพ๊ธฐ ๊ฒ์ํ์ ์นด๋ ํํ์ ๊ณตํต ์ปดํฌ๋ํธ๋ ์๋์ ๊ฐ์ด ์ด๋ฃจ์ด์ ธ ์์๋ค.
- ํ์ฐพ๊ธฐ : ์นด๋ ์๋ฉด, ์นด๋ ๋ท๋ฉด
- ํ์์ฐพ๊ธฐ : ์นด๋ ์๋ฉด
- ํ์ฐพ๊ธฐ ๊ฒ์๊ธ ์์ฑ ๋ฐ ์์ : ์นด๋ ์๋ฉด, ์นด๋ ๋ท๋ฉด
์ธ ๊ฐ์ ํ์ด์ง ๋ชจ๋ ์นด๋ UI๋ฅผ ๊ฐ์ก์ง๋ง, ๋ค์ด๊ฐ๋ ๋ฐ์ดํฐ๋ ์กฐ๊ธ์ฉ ๋ค๋ฅธ ํํ์๋ค.
ํ์ฐฝ ์์ ์ค์๋ ํ ๊ฐ์ Card ์ปดํฌ๋ํธ๋ก ์์ ์ ํ๊ณ ์์๋๋ฐ, ํํํด์ผ ํ๋ ๋ฐฉ์์ด ๋ค์ํ๋ค๋ณด๋ ์ด๋ ์๊ฐ ์ปดํฌ๋ํธ๊ฐ ์ ์ ๋ณต์กํด์ง๋๊ฒ ๋๊ปด์ก๋ค. ํนํ ๊ฒ์๊ธ ์์ฑ/์์ ์์๋ ํ ์คํธ ๋ ๋๋ง ๋์ input ํ๊ทธ๊ฐ ๋ค์ด๊ฐ์ผ ํ๋ค๋ ๊ฒ์ ๊นจ๋ณ์ ์๊ฐ ์, ์ด๊ฑฐ ๋ถ๋ฆฌ์์ผฐ์ด์ผ ํ๋? ๋ผ๋ ์๊ฐ์ด ๋ค๊ธฐ ์์ํ๋ค. ๊ทธ๋์ ๊ฒฐ๊ตญ ์นด๋ ์ปดํฌ๋ํธ๋ฅผ ‘๋ด์ฉ์ ๋ณด์ฌ์ฃผ๋ ์นด๋’์ ‘์์ฑ/์์ ์ ํ๋ ์นด๋’๋ฅผ ๋ถ๋ฆฌํ๋๊ฒ ์ข์์ง ์กฐ์ธ์ ๋ฃ๊ณ ์ถ์ด์ ์ง๋ฌธ์ ํ๋๋ฐ, ๋ฐ๋ก ๋ถ๋ฆฌํ๋๊ฒ ์ข์ ๊ฒ ๊ฐ๋ค๊ณ ๋ง์ํด์ฃผ์ จ๋ค ๐ซ
์๋ฅผ ๋ค๋ฉด ์๋์ ๊ฐ์ ํ์์ด์๋ค.
<Card>
<์ฝ๊ธฐ์ ์ฉ ์นด๋>
<์์ ๊ฐ๋ฅ ์นด๋>
</Card>
// cardView : usercard / project card
type CardType = "USER_CARD" | "PROJECT_CARD";
interface CardProps { ... }
const CardView = ({type, cardData}) => {
const isProject = type === "PROJECT_CARD";
return (
<div>
<p>{ cardData.title }</p>
{ isProject ? <p>{cardData.username}</p> }
</div>
)
}
{
isEdit ? (
<CardEdit cardData={cardData} />
) : (
<CardView cardData={cardData} type={type} />
);
}
→ isEdit=”true”
์ธ ์นด๋๋ ์์ ๊ฐ๋ฅ ์นด๋๊ฐ ๋๋ค.
→ ์ฝ๊ธฐ์นด๋๋ ์ ์ ์นด๋์ ํ๋ก์ ํธ ์นด๋๋ก ๋๋๋ค. (์์ ์นด๋๋ ์ ์ ์นด๋ ๋ฐ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ ํ์ X)
→ ์ฝ๊ธฐ์นด๋๊ฐ ํ๋ก์ ํธ ์นด๋์ธ ๊ฒฝ์ฐ ํ์ํ ํ ์คํธ๋ฅผ ๋ ๋ณด์ฌ์ค๋ค.
๊ทธ ์ธ์ ๋ฉํ ๋ง ์๊ฐ์ ์๋์ ๊ฐ์ ์ด์ผ๊ธฐ๋ ๋๋์๋ค.
- tailwind, mui, typescript, react-query๋ ์ง๊ธ ์ ํด๋ณด๋ ๊ฑธ ์ถ์ฒ
- ์๋ฆผ๊ธฐ๋ฅ์ ๊ตฌํํ๊ธฐ๋ก ํ๋๋ฐ, ์น์์ผ์ ๊ตฌํํ๊ธฐ์๋ ์๊ฐ์ด ๋ถ์กฑํ ๊ฒ ๊ฐ์ polling ๋ฐฉ์(api ์์ฒญ์ 1์ด์ ํ๋ฒ์ฉ ๋ณด๋ด์ ์๋ต์ ๋ฐ๋ ๋ฐฉ์)์ ์ด์ฉํ๋๊ฑด ์ด๋ค์ง์ ๋ํ ์ด์ผ๊ธฐ
- ๋ฐฐํฌ๋ฅผ ์ต๋ํ ๋นจ๋ฆฌ ํด๋ณด๊ธฐ!
โญ๏ธ ์นด๋ ์ปดํฌ๋ํธ ๋ถ๋ฆฌํ๊ธฐ
์์ํ๊ธฐ ์ ๋ถํฐ ๋จธ๋ฆฌ๊ฐ ์ํ ์ง๋ง, ์ด๋ป๊ฒ๋ ํด์ผํ๋ ์ด๋ฏธ ์์ฑํ๋ ์ฝ๋ ์์ ํ๊ธฐ…
๋ค์ ์์ฝํ์๋ฉด, ๋ด๊ฐ ์์ ํ 2๊ฐ์ ๊ฒ์ํ(ํ์ฐพ๊ธฐ/ํ์์ฐพ๊ธฐ)์๋ ์นด๋ ํํ์ ์ปดํฌ๋ํธ๊ฐ ์๋นํ ๋ง์ด ๋ฑ์ฅํ๋ค. ๋ฐ๋ณต์ ์ผ๋ก ์ฌ์ฉ๋๋ ์ปดํฌ๋ํธ์ธ ๊ฒ์ด๋ค. ์์ ์ธ๊ธํ ๊ณตํต ์ปดํฌ๋ํธ ์์ ์ ํ๋ฉฐ ์นด๋ ์ปดํฌ๋ํธ ๋ํ ์์ ์ ํ์๋ค.
๋ฉํ ๋ง ์ธ์ ๋์ ์ง๋ฌธ์ ๋ค์ ๋์ง์ด๋ณด์๋ฉด, ์ด ์นด๋ ์ปดํฌ๋ํธ๋ 2๊ฐ์ ๊ฒ์ํ์์ ๊ฐ๊ฐ ๋ค๋ฅด๊ฒ ํ์ฉ์ด ๋๋ค. ํ์ฐพ๊ธฐ(Find Team) ๊ฒ์ํ์ “์ ์ ์นด๋”๋ ์๋ฉด๊ณผ ๋ท๋ฉด์ด ์์ด ๋ค์งํ๋ ํจ๊ณผ๋ฅผ ๊ตฌํํด์ผ ํ๊ณ , ์นด๋ ์์ฑ๊ณผ ์์ ํ์ด์ง์์๋ ์ฌ์ฉ์ด ๋์๋ค. ๋ฐ๋ฉด ํ์์ฐพ๊ธฐ(Find Mate) ๊ฒ์ํ์ ์นด๋๋ ์๋ฉด๋ง ์๋ ํํ ํ๋๋ง ๊ตฌํํ๋ฉด ๋์๋ค. ๋ค์งํ๋ ํจ๊ณผ๋ ์์ฑ/์์ ํ์ด์ง์์๋ ์ฌ์ฉ๋์ง ์๋๋ค.
์ฒ์์๋ ์ด ์นด๋ ์ปดํฌ๋ํธ๋ฅผ ํ๋์ Card๋ผ๋ ํ๋์ ์ปดํฌ๋ํธ๋ก ๋ง๋ค์ด ์์ ์ ํ์๋๋ฐ, ์์ ์ ํ๋ค๋ณด๋ Card ๋ผ๋ ์ปดํฌ๋ํธ ์์ ๋๋ฌด ๋ง์ ๊ฒ๋ค์ด ๋ค์ด๊ฐ์๋ ๊ฒ ๊ฐ์ ์ฝ๋๊ฐ ์๋ง์ง์ฐฝ์ด์๋ ๊ฑธ ๋ ์ญ์๋ ๋๋ผ๊ณ ์์๋ค. ๊ฒฐ๊ตญ ๋ฉํ ๋ง ์๊ฐ์ ์นด๋ ์ปดํฌ๋ํธ๋ฅผ ViewCard์ EditCard๋ก ๋ถ๋ฆฌํ๋๊ฒ ์ข์ ๊ฒ ๊ฐ๋ค๋ ๋ฉํ ๋์ ์๊ฒฌ์ ์นด๋ ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ฆฌํ๊ธฐ๋ก ํ๋ค. ์์ํ๋ ๋๋ต์ด์์ง๋ง, ํ์ ์ด ๋ค์๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ์นด๋ ์ปดํฌ๋ํธ ๋ถ๋ฆฌ ์์ ์ ์์ํ๋ค.
// ๊ธฐ์กด์ Card.tsx
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import classes from "./Card.module.css";
import { ReactComponent as EditSvg } from "../../assets/icons/edit.svg";
import { CardType } from "../../pages/userList/types";
type Props = {
type: "USER_CARD" | "PROJECT_CARD";
// CardType
cardData: CardType;
isEdit?: boolean;
};
// ์นด๋ ์์ ํ ๊ฒฝ์ฐ cardData ๋ณด๋ด์ฃผ๊ธฐ
const Card = ({ type, cardData, isEdit }: Props) => {
const { title, position, keywords, createdAt } = cardData;
const date: string = new Date(createdAt).toLocaleDateString();
const [editTitle, setEditTitle] = useState("");
const handleChangeTitle = (e: React.ChangeEvent<HTMLInputElement>) => {
setEditTitle(e.target.value);
};
console.log("๐ EDITDATA: ", editTitle);
const navigate = useNavigate();
return (
<li className={classes.cardWrapper}>
<div
className={`${classes.card} ${
type === "PROJECT_CARD" ? classes.project : classes.user
}`}
onClick={
type === "PROJECT_CARD" ? () => navigate(":detailPageId") : undefined
}
>
{/* FRONT */}
<div className={classes.front}>
<div className={classes.topArea}>
<div className={classes.meta}>
<span className={classes.date}>
{!isEdit ? date : new Date().toLocaleDateString()}
</span>
{type === "PROJECT_CARD" && (
<span className={classes.view}>์กฐํ์ 123</span>
)}
</div>
{type === "PROJECT_CARD" && (
<div className={classes.recruitTag}>๋ชจ์ง์ค</div>
)}
</div>
<div className={classes.centerArea}>
{type === "PROJECT_CARD" && (
<span className={classes.username}>์ ์ ABC</span>
)}
<div className={classes.title}>
{isEdit ? (
<input
type="text"
placeholder="์ ๋ชฉ์ ์
๋ ฅํด์ฃผ์ธ์."
value={editTitle}
onChange={handleChangeTitle}
/>
) : (
title
)}
</div>
</div>
<div className={classes.bottomArea}>
<div className={classes.position}>{position}</div>
<ul className={classes.stack}>
<li>JS</li>
<li>TS</li>
<li>React</li>
<li>Node</li>
</ul>
</div>
</div>
{/* BACK :: USER_CARD๋ง ์กด์ฌ */}
{type === "USER_CARD" && (
<div className={classes.back}>
<div className={classes.topArea}>
{!isEdit && (
<span
className={classes.edit}
onClick={() => {
navigate("/userlist/edit/:id");
}}
>
<EditSvg width="24" height="24" />
</span>
)}
</div>
<div className={classes.centerArea}>
<div
className={classes.userImage}
onClick={() => navigate("/")}
></div>
<div className={classes.keywordTag}>
{keywords.map(item => (
<span key={item}> #{item}</span>
))}
</div>
</div>
<div className={classes.bottomArea}>
{!isEdit && (
<div className={classes.infoText}>
์ ์ AAA๋์ด ๋ ๊ถ๊ธํ์ ๊ฐ์?
<br />
ํ๋กํ ์ฌ์ง์ ํด๋ฆญํด ๋ณด์ธ์!
</div>
)}
</div>
</div>
)}
</div>
</li>
);
};
export default Card;
โฌ๏ธ
๊ธฐ์กด์ ํ๋์๋ Card.tsx๋ฅผ ์ฌ๋ฌ๊ฐ์ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌํ๊ณ , ์นด๋์ ํ์ ์ ์ง์ ํด ์ํ๋ ํํ์ ์นด๋๋ฅผ ๋ ๋๋ง ํ ์ ์๋๋ก ์์ ํด์ฃผ์๋ค.
์นด๋์ ํ์ ์ด ํ๋ก์ ํธ ์นด๋๋ผ๋ฉด ์นด๋ ๋ท๋ฉด์ด ๋ ธ์ถ๋์ง ์๋๋กํ๊ณ , ์ ์ ์นด๋๋ผ๋ฉด isEdit์ ์ฐธ๊ฑฐ์ง ์ฌ๋ถ์ ๋ฐ๋ผ EditCard์ ViewCard๋ก ๋๋์ด ์ฃผ์๋ค. ์์ ์นด๋๋ผ๋ฉด ํ ์คํธ ์ธํ์ฐฝ์ด ๋ค์ด๊ฐ ์นด๋๋ฅผ ์์ ๋๋ ์์ฑํ ์ ์๋๋ก ๋ง๋ค ์ ์์๋ค.
๐ง ์ด๋ ๊ฒ ๋ณต์กํ๋ ์ปดํฌ๋ํธ๋ฅผ ๋๋์ด ๊ตฌํํจ์ผ๋ก์จ ์ด๋์ ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ฆฌ์ํฌ ์ ์์๋ค. ํ์ง๋ง ์ง๊ธ๋ ์นด๋ ์ปดํฌ๋ํธ์ ์ฌ๋ฌ๊ฐ์ง ๋ด์ฉ์ด ๋ค์ด๊ฐ๋ค ๋ณด๋ ์ฌ์ ํ ๋ณต์กํ๊ณ , ๊ฐ๋ ์ฑ์ด ์ข์๋ณด์ด์ง ์์์ ํ๋ก์ ํธ๊ฐ ๋๋ ํ์ ๋ฆฌํฉํ ๋ง์ ํ๋ ค๊ณ ํ์๋๋ฐ, ์ด๋์๋ถํฐ ์๋์ผํ ์ง ์ ๋ง ๋ชจ๋ฅด๊ฒ ์ด์ ๊ฒฐ๊ตญ ๋์ ๊ฑฐ๋ฆฌ๋ค๊ฐ ๋ง์๋ค. ํด… ๋ด๊ฐ ์ด๊ฑธ ๋ค์ ๊ฑด๋๋ฆด ์ ์์๊น?
๐ 2์ฃผ์ฐจ, ์์ ํ๋ฉฐ ๋๋ ๋ฌด์์ ๋ฐฐ์ ์๊น? (What I Learned)
1. Vite ํ๋ก์ ํธ ์ธํ
๐ https://ko.vitejs.dev/guide/
React๋ฅผ ํ์ตํ๋ฉฐ ํญ์ Create React App์ ์ฌ์ฉํ์๋๋ฐ, ์ต๊ทผ์๋ ๋น๋ ์๋๊ฐ ์๋์ ์ผ๋ก ๋น ๋ฅด๋ค๊ณ ํ๋ Vite๊ฐ ๋์ธ๋ผ๊ณ ํ๋ค. ์์ ์๋ ๊ฐ์์์ CRA๋ฅผ ์ฌ์ฉํ React ์ธํ ์ด ๋๋ถ๋ถ์ด์๋๋ฐ, ์์ฆ ๊ฐ์๋ฅผ ๋ณด๋ฉด Vite๋ก React๋ฅผ ์์ํ๋ ๊ฒฝ์ฐ๋ ๋ง์๋ค. ํ๋ก ํธ ํ์๋ถ์ด Vite๋ก ์ธํ ์ ํ์๊ณ ํด์ ์ฐ๋ฆฌ ํ๋ก์ ํธ๋ Vite๋ก ์์ํ๊ฒ ๋์๊ณ , ์ค์ ๋ก ํ๋ก์ ํธ Build์ CRA์ ๋น๊ตํ์ฌ ์๋๊ฐ ๊ต์ฅํ ๋น ๋ฅธ ๊ฒ์ ๋๋ ์ ์์๋ค. Vite๋ ๊ณต์๋ฌธ์๋ ๊ต์ฅํ ์ ๋์ด์์ด์, ์์ํ๋ ๊ฒ์ ์ ํ ์ด๋ ต์ง ์์๋ค ๐
2. Typescript์์ ReactComponent๋ก SVG ๋ถ๋ฌ์ค๊ธฐ
typescript์์ svg ํ์ผ์ ReactComponent๋ก importํ๋ ๊ณผ์ ์์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค. ๊ตฌ๊ธ๋ง์ ํด๋ณด๋ ํ์ ์ ์ ํ์ผ์ ์์ฑํด ๋ชจ๋ svg ํ์ผ์ ๋ํ ์์ฑ์ ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํด์ฃผ๋ผ๊ณ ํ์๋ค. (์ฐธ๊ณ https://choi95.tistory.com/206)
declare module "*.svg" {
import React = require("react");
export const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;
const src: string;
export default src;
}
๊ทธ๋ฐ๋ฐ ์ด๋ ๊ฒ ํด๋ ๋์ค์ง๊ฐ ์์์ ๋ ๊ฒ์์ ํด๋ณด๋ vite-plugin-svgr
์ ์ค์นํด์ฃผ๋ผ๊ณ ํด์ ์ค์นํ๊ณ , ์ธํ
ํ๋๋ ๊ทธ์ ์์ผ ์ ์์ ์ผ๋ก ๋์๋ค. (์ฐธ๊ณ https://www.freecodecamp.org/news/how-to-import-svgs-in-react-and-vite/ ๊ธ์ 6. How to Import SVGs Using the Vite Plugin for SVGR)
์ด ๋ถ๋ถ์ ์ข ๋ ์ฐพ์๋ด์ผ ์ ํํ ์์ธ์ ์ ๊ฒ ๊ฐ์๋ฐ, ์ฐ์ ์ ์์ ์ด ๊ธํด ์ธํ ํ ๊ทธ๋ฅ ๋๊ฒผ๋ค.
โ๏ธ React์์ SVG๋ฅผ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฌ์ ์๋์ ๊ฐ์ด ์ฌ์ฉํ ์ ์๋ค.
// customํ๊ณ ์ถ์ ๋ถ๋ถ์ current๋ฅผ ์ง์ ํ๋ฉด ๋๋ค.
<svg width="current" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17 3.0003C17.2626 2.73766 17.5744 2.52932 17.9176 2.38718C18.2608 2.24503 18.6286 2.17188 19 2.17188C19.3714 2.17187 19.7392 2.24503 20.0824 2.38718C20.4256 2.52932 20.7374 2.73766 21 3.0003C21.2626 3.26295 21.471 3.57475 21.6131 3.91791C21.7553 4.26107 21.8284 4.62887 21.8284 5.0003C21.8284 5.37174 21.7553 5.73953 21.6131 6.08269C21.471 6.42585 21.2626 6.73766 21 7.0003L7.5 20.5003L2 22.0003L3.5 16.5003L17 3.0003Z" stroke="current" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
import { ReactComponent as EditSVG } from "./assets/edit.svg";
<EditSVG width="100px" stroke="#888" />
// current๋ก ์ง์ ํ ๊ณณ์ ์์น๋ฅผ ๋ฃ์ด์ฃผ๋ฉด ์ ์ฉ๋๋ค.
3. ์กฐ๊ฑด์ ๋ฐ๋ผ onClick ์ด๋ฒคํธ ํ์ฑํํ๊ธฐ
// CardView.tsx
const CardView = ({ type, cardData }: CardViewProps) => {
const isUserCard = type === "USER_CARD";
const { memberBoardId } = cardData as ProjectListDataType;
const navigate = useNavigate();
return (
<div
className={`${classes.card} ${
isUserCard ? classes.user : classes.project
}`}
onClick={
**isUserCard ? undefined : () => navigate(`/projectlist/${memberBoardId}`)**
}
>
<CardViewFront type={type} cardData={cardData} />
{isUserCard && <CardViewBack cardData={cardData as UserListDataType} />}
</div>
);
};
์นด๋์ ํ์ ์ project card์ user card๊ฐ ์๋๋ฐ, project card์ธ ๊ฒฝ์ฐ์๋ง ๊ฒ์๋ฌผ ์์ธํ์ด์ง๋ก ์ด๋ํ๋ click ์ด๋ฒคํธ๋ฅผ ํ์ฑํ์์ผ์ผ ํ๋ค.
type์ด project card์ธ ๊ฒฝ์ฐ์๋ง onClick ์ด๋ฒคํธ๋ฅผ ํ์ฑํํ๊ธฐ ์ํด์๋ (์ฆ, isUserCard๊ฐ false์ผ ๊ฒฝ์ฐ์๋ง) ์กฐ๊ฑด๋ถ๋ก onClick์ ์ค์ ํ ์ ์๋ค. onClick์ด undefined๋ก ์ค์ ๋๋ฉด ํด๋ฆญ ์ด๋ฒคํธ๊ฐ ๋นํ์ฑํ๋๋ค.
4. ์ํ๋ ํ์์ผ๋ก ๋ ์ง ๋ฐ์ดํฐ ๋ณด์ฌ์ฃผ๊ธฐ
๋ ์ง ๋ฐ์ดํฐ๋ฅผ ์ํ๋ ํ์์ผ๋ก ๋ณด์ฌ์ฃผ๊ณ ์ถ์๋ค. JavaScript์์ ์ ๊ณตํ๋ ํํ๋ค์ ๋ชจ์๋ดค๋๋ฐ, ์ํ๋ ํ์์ด ์์ด์ ํจ์๋ฅผ ์ง์ ๋ง๋ค์ด ์ฌ์ฉํ์๋ค.
// JS Date ๋ฉ์๋
.toLocaleString() // 2023. 9. 6. ์คํ 10:42:19
.toLocaleDateString() // 2023. 9. 6.
.toLocaleTimeString() // ์คํ 10:42:19
.toDateString() // Wed Sep 06 2023
.toISOString() // 2023-09-06T13:42:19.000Z
// FORMAT :: YYYY.MM.DD
export const getStringDate = (date: string | Date) => {
return new Date(date).toLocaleDateString();
};
// FORMAT :: YYYY-MM-DD
export const sliceISOString = (date: string) => {
return new Date(date).toISOString().slice(0, 10);
};
// FORMAT :: YYYY-MM-DDTHH:MM:SS
export const requestFormatDate = (date: string) => {
return new Date(date).toISOString().split(".")[0];
};