Skip to content

JSUYA/todo_vite-react-ts

 
 

Repository files navigation

๐Ÿ“Œ ํ•  ์ผ(Todo) ๊ด€๋ฆฌ ์•ฑ

์ฃผ์–ด์ง„ API๋ฅผ ํ™œ์šฉํ•ด '์™„์„ฑ ์˜ˆ์‹œ' ์ฒ˜๋Ÿผ ํ•  ์ผ(Todo) ๊ด€๋ฆฌ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

์ด ์•ฑ์—์„œ ์‚ฌ์šฉํ•œ ์ฃผ์š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • react-router-dom: React ์•ฑ์˜ ๋ผ์šฐํŒ… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, URL๊ณผ ์ปดํฌ๋„ŒํŠธ์˜ ๋งคํ•‘์„ ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • framer-motion: ๋ณต์žกํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ์ œ์Šค์ฒ˜๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.
  • axios: HTTP ํด๋ผ์ด์–ธํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, ๋ธŒ๋ผ์šฐ์ €์™€ Node.js์—์„œ ๋ชจ๋‘ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • zustand: ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ๊ฐ„ํŽธํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์ง€๋งŒ ๊ฐ•๋ ฅํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.
  • @tanstack/react-query: ๋ฐ์ดํ„ฐ ํŒจ์นญ๊ณผ ์บ์‹ฑ ๋“ฑ์„ ๊ฐ„ํŽธํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.
  • sortablejs: ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ ๊ธฐ๋Šฅ์œผ๋กœ ๋ชฉ๋ก์„ ์ •๋ ฌํ•˜๊ฑฐ๋‚˜ ์žฌ๋ฐฐ์น˜ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • sass: CSS ์ „์ฒ˜๋ฆฌ๊ธฐ๋กœ CSS๋ณด๋‹ค ๋” ๋‚˜์€ ๋ฌธ๋ฒ•๊ณผ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • clsx: ์กฐ๊ฑด๋ถ€ CSS ํด๋ž˜์Šค๋ฅผ ๋™์ ์œผ๋กœ ์กฐํ•ฉํ•˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ์ž…๋‹ˆ๋‹ค.
  • vercel: Vercel์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฐฐํฌ ๋„๊ตฌ๋กœ, ์ž๋™ ๋ฐฐํฌ, ์„œ๋ฒ„๋ฆฌ์Šค ํ•จ์ˆ˜ ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • concurrently: ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์Šคํฌ๋ฆฝํŠธ ๋ช…๋ น์„ ๋™์‹œ์—(๋ณ‘๋ ฌ) ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ์ž…๋‹ˆ๋‹ค.
  • dotenv: ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, .env ํŒŒ์ผ์— ์ •์˜๋œ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

ํ•  ์ผ ๊ด€๋ฆฌ ์•ฑ, ์™„์„ฑ ์˜ˆ์‹œ
ํ•  ์ผ ๊ด€๋ฆฌ ์•ฑ, ์™„์„ฑ ์˜ˆ์‹œ

Vercel

Vercel SPA Fallback

Browser Router ๋ชจ๋“œ์—์„œ ๋‹จ์ผ ํŽ˜์ด์ง€ ์•ฑ(SPA)์„ ์ œ๊ณตํ•  ๋•Œ๋Š” ์ ์ ˆํ•œ ์„œ๋ฒ„ ๊ตฌ์„ฑ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
์šฐ๋ฆฌ๋Š” Vercel ํ˜ธ์ŠคํŒ…์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ๋ชจ๋“  ๊ฒฝ๋กœ์— ๋Œ€ํ•ด index.html ํŒŒ์ผ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์˜ต์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

https://vercel.com/docs/concepts/projects/project-configuration#legacy-spa-fallback

/vercel.json

{
  "routes": [
    { "handle": "filesystem" },
    { "src": "/(.*)", "dest": "/index.html" }
  ]
}

API ์ •๋ณด

๋ชจ๋“  ์š”์ฒญ์€ ๋‹ค์Œ Headers ์ •๋ณด๊ฐ€ ํ•„์ˆ˜๋กœ ํฌํ•จ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
<TODO_APIKEY>์™€ <TODO_USERNAME> ์ •๋ณด๋Š” ๋ณ„๋„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

curl <API_ENDPOINT>
  \ -X <REQUEST_METHOD>
  \ -H 'content-type: application/json'
  \ -H 'apikey: <TODO_APIKEY>'
  \ -H 'username: <TODO_USERNAME>'

ํ•  ์ผ ๋ชฉ๋ก ์กฐํšŒ

์ „์ฒด ํ•  ์ผ ๋ชฉ๋ก์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.

curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/todos
  \ -X 'GET'

์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ:

  • N/A

์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ:

type ResponseValue = Todo[] // ํ•  ์ผ ๋ชฉ๋ก

interface Todo {
  id: string // ํ•  ์ผ ID
  order: number // ํ•  ์ผ ์ˆœ์„œ
  title: string // ํ•  ์ผ ์ œ๋ชฉ
  done: boolean // ํ•  ์ผ ์™„๋ฃŒ ์—ฌ๋ถ€
  createdAt: string // ํ•  ์ผ ์ƒ์„ฑ์ผ
  updatedAt: string // ํ•  ์ผ ์ˆ˜์ •์ผ
}
[
  {
    "id": "mnIwaAPIAE1ayQmqekiR",
    "order": 0,
    "title": "JS ๊ณต๋ถ€ํ•˜๊ธฐ",
    "done": false,
    "createdAt": "2021-10-28T05:18:51.868Z",
    "updatedAt": "2021-10-28T05:18:51.868Z"
  },
  {
    "id": "tMzPImGoWtRdJ6yyVv2y",
    "order": 1,
    "title": "๊ณผ์ œ PullRequest(PR) ์ƒ์„ฑ",
    "done": true,
    "createdAt": "2021-10-28T04:16:53.980Z",
    "updatedAt": "2021-10-28T09:40:17.955Z"
  },
  {
    "id": "Rq8BebKihCgteHHhMIRS",
    "order": 2,
    "title": "API ์Šคํ„ฐ๋””",
    "done": false,
    "createdAt": "2021-10-28T04:17:02.510Z",
    "updatedAt": "2021-10-28T04:17:02.510Z"
  }
]

ํ•  ์ผ ํ•ญ๋ชฉ ์ถ”๊ฐ€

ํ•  ์ผ ํ•ญ๋ชฉ์„ ์ƒˆ๋กญ๊ฒŒ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/todos
  \ -X 'POST'

์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ:

interface RequestBody {
  title: string // ํ•  ์ผ ์ œ๋ชฉ
  order?: number // ํ•  ์ผ ์ˆœ์„œ
}
{
  "title": "KDT ๊ณผ์ • ์„ค๊ณ„ ๋ฏธํŒ…"
}

์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ:

interface ResponseValue {
  id: string
  order: number
  title: string
  done: boolean
  createdAt: string
  updatedAt: string
}
{
  "id": "7P8dOM4voAv8a8cfoeKZ",
  "order": 0,
  "title": "KDT ๊ณผ์ • ์„ค๊ณ„ ๋ฏธํŒ…",
  "done": false,
  "createdAt": "2021-10-29T07:20:02.749Z",
  "updatedAt": "2021-10-29T07:20:02.749Z"
}

ํ•  ์ผ ํ•ญ๋ชฉ ์ˆ˜์ •

ํŠน์ • ํ•  ์ผ ํ•ญ๋ชฉ์„ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/todos/:todoId
  \ -X 'PUT'

์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ:

interface RequestBody {
  title: string // ํ•  ์ผ ์ œ๋ชฉ
  done: boolean // ํ•  ์ผ ์™„๋ฃŒ ์—ฌ๋ถ€
  order?: number // ํ•  ์ผ ์ˆœ์„œ
}
{
  "title": "Bootstrap ์Šคํƒ€์ผ ์ถ”๊ฐ€",
  "done": false
}

์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ:

interface ResponseValue {
  id: string
  order: number
  title: string
  done: boolean
  createdAt: string
  updatedAt: string
}
{
  "id": "7P8dOM4voAv8a8cfoeKZ",
  "title": "Bootstrap ์Šคํƒ€์ผ ์ถ”๊ฐ€",
  "done": false,
  "order": 2,
  "createdAt": "2021-10-29T07:20:02.749Z",
  "updatedAt": "2021-10-29T07:20:02.749Z"
}

ํ•  ์ผ ํ•ญ๋ชฉ ์‚ญ์ œ

ํŠน์ • ํ•  ์ผ ํ•ญ๋ชฉ์„ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.

curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/todos/:todoId
  \ -X 'DELETE'

์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ:

  • N/A

์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ:

type ResponseValue = true // ์ •์ƒ ์‘๋‹ต

ํ•  ์ผ ํ•ญ๋ชฉ ์ผ๊ด„ ์‚ญ์ œ

curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/todos/deletions
  \ -X 'DELETE'

์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ:

interface RequestBody {
  todoIds: string[] // ์‚ญ์ œํ•  ํ•  ์ผ ID ๋ชฉ๋ก
}
{
  "todoIds": [
    "mnIwaAPIAE1ayQmqekiR",
    "tMzPImGoWtRdJ6yyVv2y",
    "GHrvr3LaPx1g7y2sNuaC",
    "Rq8BebKihCgteHHhMIRS"
  ]
}

์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ:

type ResponseValue = true // ์ •์ƒ ์‘๋‹ต

ํ•  ์ผ ๋ชฉ๋ก ์ˆœ์„œ ๋ณ€๊ฒฝ

ํ•  ์ผ ๋ชฉ๋ก์˜ ์ˆœ์„œ๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/todos/reorder
  \ -X 'PUT'

์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ:

interface RequestBody {
  todoIds: string[] // ์ƒˆ๋กญ๊ฒŒ ์ •๋ ฌํ•  ํ•  ์ผ ID ๋ชฉ๋ก
}
{
  "todoIds": [
    "mnIwaAPIAE1ayQmqekiR",
    "tMzPImGoWtRdJ6yyVv2y",
    "GHrvr3LaPx1g7y2sNuaC",
    "Rq8BebKihCgteHHhMIRS"
  ]
}

์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ:

type ResponseValue = true // ์ •์ƒ ์‘๋‹ต

Releases

No releases published

Packages

No packages published

Languages

  • TypeScript 78.8%
  • SCSS 16.8%
  • HTML 2.5%
  • JavaScript 1.9%