๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Frontend Dev/๐Ÿฅ ์ฝ”๋“œ์Šคํ…Œ์ด์ธ  FE ๋ถ€ํŠธ์บ ํ”„

[Main Project 2์ฃผ์ฐจ] 9.1 ~ 9.10 | ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ ์ž‘์—… & UI ๊ตฌํ˜„

๋ฐ˜์‘ํ˜•

[Main Project 2์ฃผ์ฐจ]  9.1 ~ 9.10
๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ ์ž‘์—… & UI ๊ตฌํ˜„

๐Ÿ’ฌ ํ”„๋กœ์ ํŠธ๊ฐ€ ๋๋‚œ ํ›„์— ๋ฐ์ผ๋ฆฌ๋กœ ์ •๋ฆฌํ–ˆ๋˜ ๊ธฐ๋ก์„ ๋ณด๋ฉฐ ํ”„๋กœ์ ํŠธ ์ „์ฒด ๊ณผ์ •์„ ์ •๋ฆฌํ•˜์—ฌ 4์ฃผ์ฐจ๋กœ ๋‚˜๋ˆ„์–ด ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ”— 1์ฃผ์ฐจ

2์ฃผ์ฐจ: ํ˜„์žฌ๊ธ€

๐Ÿ”— 3์ฃผ์ฐจ

๐Ÿ”— 4์ฃผ์ฐจ

๐Ÿ”— ๋ฐ๋ชจ๋ฐ์ด & ํ”„๋กœ์ ํŠธ ์ „์ฒด ํšŒ๊ณ 

๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป 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๋ฅผ ์‚ฌ์šฉํ•ด ๋ผ์šฐํŒ… ์ฒ˜๋ฆฌ๋ฅผ ๋ฏธ๋ฆฌ ํ•จ์œผ๋กœ์จ ๊ฐ์ž ์ž‘์—…ํ•˜๋Š” ํŽ˜์ด์ง€์— ์‰ฝ๊ฒŒ ์ ‘๊ทผํ•˜์—ฌ ๊ฐœ๋ฐœ์˜ ํŽธ์˜์„ฑ์„ ๋†’์ด๋Š” ๊ฒƒ์ด ๋ชฉ์ ์ด์—ˆ๋‹ค. ํ˜„์—…์—์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์–ด๋–ค ์‹์œผ๋กœ ์ง„ํ–‰ํ•˜๋Š”์ง€ ์•Œ๊ธธ์ด ์—†์–ด ์ด๋Ÿฌํ•œ ์ˆœ์„œ๊ฐ€ ๋Œ€์ฒด์ ์œผ๋กœ ํ†ต์šฉ๋˜๋Š” ๊ฒƒ์ธ์ง€๋Š” ์ž˜ ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ, ํ”„๋ฆฌํ”„๋กœ์ ํŠธ ๋•Œ๋„ ์ด์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•ด๋ณธ ๊ฒฐ๊ณผ ํ™•์‹คํžˆ ์•„๋ฌด๋Ÿฐ ์„ธํŒ… ์—†์ด ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค๋Š” ํŽธ๋ฆฌํ•˜๋‹ค๋Š” ๊ฒƒ์ด ๋‚˜์˜ ์ƒ๊ฐ์ด์—ˆ๋‹ค.

๋””์ž์ธ ๊ฐ€์ด๋“œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ์ดˆ๊ธฐ CSS ์ž‘์„ฑ

 

โ—พ๏ธ ๋ผ์šฐํŒ… ์ฒ˜๋ฆฌ (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)

 → ์ฝ๊ธฐ์นด๋“œ๊ฐ€ ํ”„๋กœ์ ํŠธ ์นด๋“œ์ธ ๊ฒฝ์šฐ ํ•„์š”ํ•œ ํ…์ŠคํŠธ๋ฅผ ๋” ๋ณด์—ฌ์ค€๋‹ค.

 

๊ทธ ์™ธ์— ๋ฉ˜ํ† ๋ง ์‹œ๊ฐ„์— ์•„๋ž˜์™€ ๊ฐ™์€ ์ด์•ผ๊ธฐ๋„ ๋‚˜๋ˆ„์—ˆ๋‹ค.

  1. tailwind, mui, typescript, react-query๋Š” ์ง€๊ธˆ ์ ‘ํ•ด๋ณด๋Š” ๊ฑธ ์ถ”์ฒœ
  2. ์•Œ๋ฆผ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ธฐ๋กœ ํ–ˆ๋Š”๋ฐ, ์›น์†Œ์ผ“์„ ๊ตฌํ˜„ํ•˜๊ธฐ์—๋Š” ์‹œ๊ฐ„์ด ๋ถ€์กฑํ•  ๊ฒƒ ๊ฐ™์•„ polling ๋ฐฉ์‹(api ์š”์ฒญ์„ 1์ดˆ์— ํ•œ๋ฒˆ์”ฉ ๋ณด๋‚ด์„œ ์‘๋‹ต์„ ๋ฐ›๋Š” ๋ฐฉ์‹)์„ ์ด์šฉํ•˜๋Š”๊ฑด ์–ด๋–ค์ง€์— ๋Œ€ํ•œ ์ด์•ผ๊ธฐ
  3. ๋ฐฐํฌ๋ฅผ ์ตœ๋Œ€ํ•œ ๋นจ๋ฆฌ ํ•ด๋ณด๊ธฐ!

โญ๏ธ ์นด๋“œ ์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌํ•˜๊ธฐ

 ์‹œ์ž‘ํ•˜๊ธฐ ์ „๋ถ€ํ„ฐ ๋จธ๋ฆฌ๊ฐ€ ์•„ํŒ ์ง€๋งŒ, ์–ด๋–ป๊ฒŒ๋“  ํ•ด์•ผํ–ˆ๋˜ ์ด๋ฏธ ์ž‘์„ฑํ–ˆ๋˜ ์ฝ”๋“œ ์ˆ˜์ •ํ•˜๊ธฐ…

 

 ๋‹ค์‹œ ์š”์•ฝํ•˜์ž๋ฉด, ๋‚ด๊ฐ€ ์ž‘์—…ํ•  2๊ฐœ์˜ ๊ฒŒ์‹œํŒ(ํŒ€์ฐพ๊ธฐ/ํŒ€์›์ฐพ๊ธฐ)์—๋Š” ์นด๋“œ ํ˜•ํƒœ์˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ƒ๋‹นํžˆ ๋งŽ์ด ๋“ฑ์žฅํ•œ๋‹ค. ๋ฐ˜๋ณต์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์ธ ๊ฒƒ์ด๋‹ค. ์•ž์„œ ์–ธ๊ธ‰ํ•œ ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ ์ž‘์—…์„ ํ•˜๋ฉฐ ์นด๋“œ ์ปดํฌ๋„ŒํŠธ ๋˜ํ•œ ์ž‘์—…์„ ํ–ˆ์—ˆ๋‹ค.

 ๋ฉ˜ํ† ๋ง ์„ธ์…˜ ๋•Œ์˜ ์งˆ๋ฌธ์„ ๋‹ค์‹œ ๋˜์งš์–ด๋ณด์ž๋ฉด, ์ด ์นด๋“œ ์ปดํฌ๋„ŒํŠธ๋Š” 2๊ฐœ์˜ ๊ฒŒ์‹œํŒ์—์„œ ๊ฐ๊ฐ ๋‹ค๋ฅด๊ฒŒ ํ™œ์šฉ์ด ๋œ๋‹ค. ํŒ€์ฐพ๊ธฐ(Find Team) ๊ฒŒ์‹œํŒ์˜ “์œ ์ € ์นด๋“œ”๋Š” ์•ž๋ฉด๊ณผ ๋’ท๋ฉด์ด ์žˆ์–ด ๋’ค์ง‘ํžˆ๋Š” ํšจ๊ณผ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ–ˆ๊ณ , ์นด๋“œ ์ž‘์„ฑ๊ณผ ์ˆ˜์ • ํŽ˜์ด์ง€์—์„œ๋„ ์‚ฌ์šฉ์ด ๋˜์—ˆ๋‹ค. ๋ฐ˜๋ฉด ํŒ€์›์ฐพ๊ธฐ(Find Mate) ๊ฒŒ์‹œํŒ์˜ ์นด๋“œ๋Š” ์•ž๋ฉด๋งŒ ์žˆ๋Š” ํ˜•ํƒœ ํ•˜๋‚˜๋งŒ ๊ตฌํ˜„ํ•˜๋ฉด ๋˜์—ˆ๋‹ค. ๋’ค์ง‘ํžˆ๋Š” ํšจ๊ณผ๋‚˜ ์ž‘์„ฑ/์ˆ˜์ • ํŽ˜์ด์ง€์—์„œ๋„ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.

 

 ์ฒ˜์Œ์—๋Š” ์ด ์นด๋“œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ•˜๋‚˜์˜ Card๋ผ๋Š” ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“ค์–ด ์ž‘์—…์„ ํ–ˆ์—ˆ๋Š”๋ฐ, ์ž‘์—…์„ ํ•˜๋‹ค๋ณด๋‹ˆ Card ๋ผ๋Š” ์ปดํฌ๋„ŒํŠธ ์•ˆ์— ๋„ˆ๋ฌด ๋งŽ์€ ๊ฒƒ๋“ค์ด ๋“ค์–ด๊ฐ€์žˆ๋Š” ๊ฒƒ ๊ฐ™์•„ ์ฝ”๋“œ๊ฐ€ ์—‰๋ง์ง„์ฐฝ์ด์—ˆ๋˜ ๊ฑธ ๋‚˜ ์—ญ์‹œ๋„ ๋Š๋ผ๊ณ  ์žˆ์—ˆ๋‹ค. ๊ฒฐ๊ตญ ๋ฉ˜ํ† ๋ง ์‹œ๊ฐ„์— ์นด๋“œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ViewCard์™€ EditCard๋กœ ๋ถ„๋ฆฌํ•˜๋Š”๊ฒŒ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ๋ฉ˜ํ† ๋‹˜์˜ ์˜๊ฒฌ์— ์นด๋“œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ถ„๋ฆฌํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค. ์˜ˆ์ƒํ–ˆ๋˜ ๋Œ€๋‹ต์ด์—ˆ์ง€๋งŒ, ํ™•์‹ ์ด ๋“ค์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ”๋กœ ์นด๋“œ ์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ ์ž‘์—…์„ ์‹œ์ž‘ํ–ˆ๋‹ค.

 

์›๋ž˜ ํ•˜๋‚˜์˜ ํŒŒ์ผ์—์„œ ์ž‘์—…ํ–ˆ๋˜ Card.tsx ๋ฅผ card ํด๋” ๋‚ด์—์„œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํŒŒ์ผ๋กœ ๋‚˜๋ˆ„์—ˆ๋‹ค.

 

// ๊ธฐ์กด์˜ 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}>&nbsp;#{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];
};

 

๋ฐ˜์‘ํ˜•