/villains

๐Ÿšƒ SUBONE - ์„œ์šธ ์ง€ํ•˜์ฒ  1ํ˜ธ์„  ์ปค๋ฎค๋‹ˆํ‹ฐ ์„œ๋น„์Šค

Primary LanguageJavaScript

๐Ÿšƒ SUBONE

image

0. ๋ชฉ์ฐจ

1. ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ ๋ฐ ๊ฐœ์š”

ID: villain@test.com
PW: 123123

[ํ”„๋กœ์ ํŠธ ์„ค๋ช…]

  • SUBONE์€ ์„œ์šธ ์ง€ํ•˜์ฒ  1ํ˜ธ์„  ์Šน๊ฐ๋“ค์„ ์œ„ํ•œ ๋งž์ถคํ˜• ์ปค๋ฎค๋‹ˆํ‹ฐ ์„œ๋น„์Šค ์ž…๋‹ˆ๋‹ค. ์ด ์•ฑ์„ ํ†ตํ•ด ์ด์šฉ์ž๋“ค์€ ์†์‰ฝ๊ฒŒ ๊ฒŒ์‹œ๊ธ€์„ ์˜ฌ๋ฆฌ๊ณ , ์ปค๋ฎค๋‹ˆํ‹ฐ ํ™œ๋™์„ ์ฆ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ง€ํ•˜์ฒ ์„ ์ด์šฉํ•œ ํƒ๋ฐฐ ๋ฐฐ์†ก ์„œ๋น„์Šค๋„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๊ธฐ์กด์˜ ์•ฑ๋“ค์ด ์‚ฌ์šฉ์ž์—๊ฒŒ ๋งŽ์€ ์ž…๋ ฅ์„ ์š”๊ตฌํ•˜๋ฉฐ, ์œ„์น˜์™€ ์‹œ๊ฐ„ ์ •๋ณด๋ฅผ ํ•œ๋ˆˆ์— ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ค์šด ๋ถˆํŽธํ•จ์„ ๊ฐ€์ง€๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. SUBONE์€ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด, ์ง€๋„ ์œ„์— ๊ฐ ์—ญ์˜ ์œ„์น˜์™€ ์‚ฌ์šฉ์ž์˜ ํ˜„์žฌ ์œ„์น˜๋ฅผ ์ง๊ด€์ ์œผ๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ์ง€ํ•˜์ฒ  ๋„์ฐฉ ์‹œ๊ฐ„๊ณผ ์Šค์ผ€์ค„์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ปค๋ฎค๋‹ˆํ‹ฐ์˜ ๊ฒŒ์‹œ๊ธ€๋„ ์ง€๋„์— ๋งˆํ‚นํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ ํŽธ์˜์„ฑ๊ณผ ์•ฑ์˜ ์žฌ๋ฏธ๋ฅผ ๋™์‹œ์— ๊ณ ๋ คํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ์„œ์šธ๊ถŒ์˜ ์ง€ํ•˜์ฒ  ๋…ธ์„ ๋งŒ ์ด 23๊ฐœ์ด๋ฏ€๋กœ ๋ชจ๋“  ๋…ธ์„ ์„ ๊ตฌํ˜„ํ•˜๊ธฐ์—” ํ•œ๊ณ„๊ฐ€ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ์˜ ๋ฒ”์œ„๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ์„ค์ •ํ•˜๊ณ , ์‹คํ˜„ ๊ฐ€๋Šฅํ•œ ๋ชฉํ‘œ๋ฅผ ์„ธ์šฐ๊ธฐ ์œ„ํ•ด ์ƒ์ง•์„ฑ๊ณผ ๋Œ€์ค‘์„ฑ์„ ๊ฐ–์ถ˜ 1ํ˜ธ์„ ์„ ์ค‘์‹ฌ์œผ๋กœ ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

[ํ”„๋กœ์ ํŠธ ์ผ์ •]

  • ์ „์ฒด ์ผ์ • ์ƒ์„ธ์ผ์ •

  • ๋ช…์„ธ ๊ธฐ๋Šฅ๋ณ„ ์ผ์ • ๋ช…์„ธ์ผ์ •

https://github.com/orgs/FRONTENDSCHOOL7/projects/1

2. ํŒ€ ์†Œ๊ฐœ

๐Ÿ˜ˆ Villains ํŒ€์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค!

์•ˆ๋…•ํ•˜์„ธ์š”, ์ €ํฌ๋Š” 4๋ช…์˜ Front-end ๊ฐœ๋ฐœ์ž๋กœ ๊ตฌ์„ฑ๋œ VillainsํŒ€์ž…๋‹ˆ๋‹ค.

(๋ฉ‹์Ÿ์ด์‚ฌ์ž์ฒ˜๋Ÿผ ํ”„๋ก ํŠธ์—”๋“œ ์Šค์ฟจ 7๊ธฐ ํ”„๋กœ์ ํŠธ 16ํŒ€์ž…๋‹ˆ๋‹ค.)

์ด์ง€ํ˜„ ๊น€๊ทœ๋ฆฌ ๊น€์œค์ƒ ์‹ ๋ช…์ง„
ํ”„๋กœํ•„_์ด์ง€ํ˜„ ํ”„๋กœํ•„_๊น€๊ทœ๋ฆฌ ํ”„๋กœํ•„_๊น€์œค์ƒ ํ”„๋กœํ•„_์‹ ๋ช…์ง„
JiHyeon Lee gyulls2 YOONSANG KIM mj.shin
ํŒ€์žฅ ํŒ€์› ํŒ€์› ํŒ€์›

[๋ฐฉํ–ฅ์„ฑ]

  • ํŒ€ ๋ชฉํ‘œ

    • ํ•ต์‹ฌ ๊ธฐ๋Šฅ(์ง€๋„ API, ๊ฒŒ์‹œ๊ธ€ ๋“ฑ๋ก ๋“ฑ)์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
    • ๋กœ๊ทธ์ธ, ๋ฉ”์ธ, ์œ ์ €, ํƒ๋ฐฐ, ๊ฒŒ์‹œ๊ธ€ ํŽ˜์ด์ง€๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
    • VITE, Recoil ๋“ฑ ์ƒˆ๋กœ์šด ๊ธฐ์ˆ ์„ ๊ณต๋ถ€ํ•˜์—ฌ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  • ์ƒํ™ฉ

    • ์œ„๋‹ˆ๋ธŒ์—์„œ API ๊ฐ€ ์ œ๊ณต๋˜์—ˆ์ง€๋งŒ ์›ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ ํ•œ๊ณ„๊ฐ€ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ•˜์˜€์Šต๋‹ˆ๋‹ค.
    • ํ”„๋กœ์ ํŠธ ๊ฒฝํ—˜์ด ์ฒ˜์Œ์ธ ํŒ€์›๊ณผ ๋‘ ๋ฒˆ์งธ, ์„ธ ๋ฒˆ์งธ์ธ ํŒ€์› ๋“ฑ ์‹ค๋ ฅ์ด ์ฒœ์ฐจ๋งŒ๋ณ„์ด์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฌธ์ œ

    • ์ „์ฒด ์ผ์ • ์ค‘ ๊ฐœ๋ฐœ ์ผ์ •์ด ์งง๋‹ค๋Š” ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.
    • ๋ฌธ์„œํ™” ์ž‘์—… ๋น„์ค‘์ด ํฌ๋‹ค๋Š” ๋น„์ค‘์ด ํฌ๋‹ค๋Š” ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.
    • ์Šค์Šค๋กœ ์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋‹ค ๋ณด๋‹ˆ ๊ฐœ๋ฐœ์ด ๋”๋ŽŒ์ง„๋‹ค๋Š” ๋ฌธ์ œ์ ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
    • ์ƒˆ๋กœ์šด ๊ฒƒ์ด ๋งŽ์•„ ์ฝ”๋“œ์— ์ ์šฉํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ์–ด๋ ค์›€์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฒฐ๊ณผ

    • ๊ธฐ์กด ๊ฐœ๋ฐœ ์ผ์ •์ด 10์ผ์ด์—ˆ๋Š”๋ฐ ๋ช…์„ธ ์ž‘์—… ์‹œ์ž‘๊ณผ ๋™์‹œ์— ์ฃผ์š” ๊ฐœ๋ฐœ ์ž‘์—…์„ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ผ์ •์„ ๋ณ€๊ฒฝํ•˜์—ฌ ์ด 15์ผ๊ฐ„ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ธฐ๊ฐ„์„ ๋Š˜๋ ธ์Šต๋‹ˆ๋‹ค.
    • ํšŒ์˜ ์‹œ์ž‘ ํ›„ 10๋ถ„๊ฐ„ ๊ฐ์ž์˜ ๋ชฉํ‘œ์™€ ๋‹ฌ์„ฑ ์—ฌ๋ถ€๋ฅผ ๊ฐœ์ธ์ด ์ง์ ‘ ์ž‘์„ฑํ•˜๋„๋ก ํ•˜์—ฌ ํšŒ์˜๋ก ์ž‘์„ฑ ์‹œ๊ฐ„์„ ์ค„์ด๊ณ  ๊ฐœ๋ฐœ์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ฐœ์„ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
    • ์ด์Šˆ๊ฐ€ ์ƒ๊ธฐ๋ฉด ์Šค์Šค๋กœ 30๋ถ„ ๊ฐ„ ๊ณ ๋ฏผ ํ›„ ๋ฌธ์ œ์ ๊ณผ ๋ณธ์ธ์ด ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ํ–ˆ๋˜ ๋ฐฉ๋ฒ•์„ ๊ณต์œ ํ•˜๋„๋ก ๊ทœ์น™์„ ์ •ํ•˜์—ฌ ์ง„ํ–‰ ์†๋„๋ฅผ ๋†’ํ˜”์Šต๋‹ˆ๋‹ค.
    • ์ˆ˜์š”์ผ๊ณผ ํ† ์š”์ผ ์ €๋… 7์‹œ์— ๋ชจ์—ฌ ์Šคํ„ฐ๋””๋ฅผ ์ง„ํ–‰ํ•จ์œผ๋กœ์จ ์ƒˆ๋กœ์šด ๊ธฐ์ˆ ์„ ๊ณต๋ถ€ํ•˜๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์กŒ์Šต๋‹ˆ๋‹ค.

๐Ÿ˜ˆ ์—ญํ• ๋ถ„๋‹ด

image

3. ๊ธฐ์ˆ  ์Šคํƒ ๋ฐ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ

image

[!NOTE] ํ”„๋กœ์ ํŠธ ์„ธํŒ…์„ ๋‹ด๋‹นํ•˜๋ฉด์„œ ์ง์ ‘ ๋นŒ๋“œ ๋„๊ตฌ๋ฅผ ์„ ํƒํ•˜๊ณ  ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋“ค๋งŒ ์ฑ„์›Œ์„œ ๊ฐ€๋ณ๊ฒŒ ๋งŒ๋“ค๋ ค๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ฆฌ์•กํŠธ ํ”„๋กœ์ ํŠธ๋ฅผ ์‰ฝ๊ฒŒ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” CRA๋ฅผ ์„ ํƒํ•˜์ง€ ์•Š์•˜๋Š”๋ฐ, ์›นํŒฉ์€ ์ดˆ๊ธฐ ์„ค์ •์ด ๋งŽ๊ณ  ์–ด๋ ต๊ธฐ๋„ ํ•ด์„œ, ์š”์ฆ˜ ๋Œ€์„ธ์ธ VITE๋ฅผ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋นŒ๋“œ๊ฐ€ ๋น ๋ฅด๊ธฐ๋„ ํ•˜๊ณ , ์„ค์ •๋„ ๊ฐ„๋‹จํ•˜์˜€๊ณ , ๋ฐ”๋ฒจ์„ ๋Œ€์ฒดํ•˜๋Š” SWC๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋”์šฑ ๋น ๋ฅด๊ฒŒ ๋นŒ๋“œํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค! [!NOTE] ์„œ๋ฒ„์˜ ๋ฐ์ดํ„ฐ์—์„œ ํ•„ํ„ฐ๋งํ•ด์•ผ ํ•  ์กฐ๊ฑด๋“ค์ด ์žˆ์—ˆ๊ณ , ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋˜๊ธฐ ์ „ ๋น ๋ฅด๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ•˜์—ฌ ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ๋ฅผ ๋„์ž…ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ๋ฅผ ์ฒ˜์Œ ์‚ฌ์šฉํ•˜๋‹ค ๋ณด๋‹ˆ, ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ์ƒํƒœ ๊ด€๋ฆฌ๋กœ๋Š” ๋Ÿฌ๋‹ ์ปค๋ธŒ๊ฐ€ ๋‚ฎ์€ ๋ฆฌ์ฝ”์ผ์„ ์„ ํƒํ•˜์˜€์Šต๋‹ˆ๋‹ค.

4. ํด๋” ๊ตฌ์กฐ

ํ† ๊ธ€ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ

๐Ÿ“ฆ villains
โ”œโ”€ .eslintrc.cjs
โ”œโ”€ .githu
โ”‚  โ”œโ”€ ISSUE_TEMPLATE
โ”‚  โ”‚  โ””โ”€ -๐Ÿ˜ˆfeat-.md
โ”‚  โ”œโ”€ pull_request_template.md
โ”‚  โ””โ”€ workflows
โ”‚     โ””โ”€ deploy.yml
โ”œโ”€ .gitignore
โ”œโ”€ .gitmessage.txt
โ”œโ”€ .nvmrc
โ”œโ”€ .prettierrc
โ”œโ”€ README.md
โ”œโ”€ index.html
โ”œโ”€ package-lock.json
โ”œโ”€ package.json
โ”œโ”€ public
โ”œโ”€ src
โ”‚  โ”œโ”€ App.jsx
โ”‚  โ”œโ”€ api
โ”‚  โ”‚  โ”œโ”€ delete
โ”‚  โ”‚  โ”‚  โ”œโ”€ deleteComments.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ deleteFollow.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ deletePost.api.js
โ”‚  โ”‚  โ”‚  โ””โ”€ deleteProduct.api.js
โ”‚  โ”‚  โ”œโ”€ get
โ”‚  โ”‚  โ”‚  โ”œโ”€ getCheckToken.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ getComments.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ getFollowingList.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ getMyInfo.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ getPostDetail.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ getPosts.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ getProductDetail.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ getProducts.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ getSearchUser.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ getSubOneInfo.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ getSubTime.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ getUserDetail.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ getUserFollower.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ getUserInfo.api.js
โ”‚  โ”‚  โ”‚  โ””โ”€ getUserPost.api.js
โ”‚  โ”‚  โ”œโ”€ loader
โ”‚  โ”‚  โ”‚  โ””โ”€ searchPlace.loader.js
โ”‚  โ”‚  โ”œโ”€ post
โ”‚  โ”‚  โ”‚  โ”œโ”€ postComments.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ postCommentsReport.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ postFollow.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ postHeart.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ postImage.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ postImages.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ postReport.api.js
โ”‚  โ”‚  โ”‚  โ”œโ”€ postUnFollow.api.js
โ”‚  โ”‚  โ”‚  โ””โ”€ postUploadPost.api.js
โ”‚  โ”‚  โ””โ”€ update
โ”‚  โ”‚     โ”œโ”€ updatePostEdit.api.js
โ”‚  โ”‚     โ”œโ”€ updateProduct.api.js
โ”‚  โ”‚     โ””โ”€ updateUser.api.js
โ”‚  โ”œโ”€ assets
โ”‚  โ”‚  โ”œโ”€ fonts
โ”‚  โ”‚  โ””โ”€ img
โ”‚  โ”œโ”€ atoms
โ”‚  โ”‚  โ”œโ”€ bottomSheetStateAtom.js
โ”‚  โ”‚  โ”œโ”€ followPageStateAtom.js
โ”‚  โ”‚  โ”œโ”€ goodsFocusEndAtom.js
โ”‚  โ”‚  โ”œโ”€ goodsFocusStartAtom.js
โ”‚  โ”‚  โ”œโ”€ goodsQueryEndAtom.js
โ”‚  โ”‚  โ”œโ”€ goodsQueryStartAtom.js
โ”‚  โ”‚  โ”œโ”€ headerBtnStateAtom.js
โ”‚  โ”‚  โ”œโ”€ profileAtom.js
โ”‚  โ”‚  โ”œโ”€ queryAtom.js
โ”‚  โ”‚  โ”œโ”€ queryFocusAtom.js
โ”‚  โ”‚  โ”œโ”€ realProductAuthorAtom.js
โ”‚  โ”‚  โ”œโ”€ stationPlaceAtom.js
โ”‚  โ”‚  โ”œโ”€ subOneAtom.js
โ”‚  โ”‚  โ”œโ”€ userAtom.js
โ”‚  โ”‚  โ””โ”€ userPostAtom.js
โ”‚  โ”œโ”€ components
โ”‚  โ”‚  โ”œโ”€ BottomSheet.jsx
โ”‚  โ”‚  โ”œโ”€ Buttons.jsx
โ”‚  โ”‚  โ”œโ”€ CheckBox.jsx
โ”‚  โ”‚  โ”œโ”€ DropDown.jsx
โ”‚  โ”‚  โ”œโ”€ FloatingButton.style.jsx
โ”‚  โ”‚  โ”œโ”€ Follower.jsx
โ”‚  โ”‚  โ”œโ”€ GlobalButton.jsx
โ”‚  โ”‚  โ”œโ”€ Goods.jsx
โ”‚  โ”‚  โ”œโ”€ Input.style.jsx
โ”‚  โ”‚  โ”œโ”€ Modal.jsx
โ”‚  โ”‚  โ”œโ”€ PageTemplate.jsx
โ”‚  โ”‚  โ”œโ”€ PageTemplate.style.js
โ”‚  โ”‚  โ”œโ”€ SearchSub.jsx
โ”‚  โ”‚  โ”œโ”€ Tanghulu.jsx
โ”‚  โ”‚  โ”œโ”€ chat
โ”‚  โ”‚  โ”‚  โ”œโ”€ ChatInputField.jsx
โ”‚  โ”‚  โ”‚  โ”œโ”€ ChatListItem.jsx
โ”‚  โ”‚  โ”‚  โ””โ”€ Message.jsx
โ”‚  โ”‚  โ”œโ”€ feed
โ”‚  โ”‚  โ”‚  โ”œโ”€ Comment.jsx
โ”‚  โ”‚  โ”‚  โ”œโ”€ CommentForm.jsx
โ”‚  โ”‚  โ”‚  โ”œโ”€ ImagePreview.jsx
โ”‚  โ”‚  โ”‚  โ”œโ”€ PostCard.jsx
โ”‚  โ”‚  โ”‚  โ””โ”€ ResizingTextarea.jsx
โ”‚  โ”‚  โ”œโ”€ layout
โ”‚  โ”‚  โ”‚  โ”œโ”€ BackHeader.jsx
โ”‚  โ”‚  โ”‚  โ”œโ”€ DefautlLayout.jsx
โ”‚  โ”‚  โ”‚  โ”œโ”€ NavMenu.jsx
โ”‚  โ”‚  โ”‚  โ”œโ”€ PrivateLayout.jsx
โ”‚  โ”‚  โ”‚  โ”œโ”€ SearchHeader.jsx
โ”‚  โ”‚  โ”‚  โ””โ”€ SearchLayout.jsx
โ”‚  โ”‚  โ”œโ”€ map
โ”‚  โ”‚  โ”‚  โ””โ”€ TrainMap.jsx
โ”‚  โ”‚  โ”œโ”€ profile
โ”‚  โ”‚  โ”‚  โ””โ”€ profile.jsx
โ”‚  โ”‚  โ”œโ”€ searchbar
โ”‚  โ”‚  โ”‚  โ”œโ”€ ListBox.jsx
โ”‚  โ”‚  โ”‚  โ”œโ”€ SearchBar.jsx
โ”‚  โ”‚  โ”‚  โ”œโ”€ UserCard.jsx
โ”‚  โ”‚  โ”‚  โ””โ”€ UserListBox.jsx
โ”‚  โ”‚  โ””โ”€ splash
โ”‚  โ”‚     โ””โ”€ Splash.style.js
โ”‚  โ”œโ”€ config
โ”‚  โ”‚  โ”œโ”€ api.config.js
โ”‚  โ”‚  โ”œโ”€ contact.jsx
โ”‚  โ”‚  โ”œโ”€ pageUrlConfig.js
โ”‚  โ”‚  โ””โ”€ route.config.jsx
โ”‚  โ”œโ”€ database
โ”‚  โ”‚  โ””โ”€ 2023-2024-holiday.json
โ”‚  โ”œโ”€ hooks
โ”‚  โ”‚  โ”œโ”€ useBlockToBack.js
โ”‚  โ”‚  โ”œโ”€ useBottomSheetOptions.js
โ”‚  โ”‚  โ”œโ”€ useFormatDate.js
โ”‚  โ”‚  โ”œโ”€ useGeoLocation.js
โ”‚  โ”‚  โ”œโ”€ useInfiniteScroll.js
โ”‚  โ”‚  โ”œโ”€ useModal.js
โ”‚  โ”‚  โ””โ”€ useSearchData.js
โ”‚  โ”œโ”€ index.jsx
โ”‚  โ”œโ”€ style
โ”‚  โ”‚  โ”œโ”€ GlobalStyles.jsx
โ”‚  โ”‚  โ”œโ”€ swiperStyle.js
โ”‚  โ”‚  โ””โ”€ theme.js
โ”‚  โ””โ”€ views
โ”‚     โ”œโ”€ Error.view.jsx
โ”‚     โ”œโ”€ FollowersList.view.jsx
โ”‚     โ”œโ”€ FollowingsList.view.jsx
โ”‚     โ”œโ”€ auth
โ”‚     โ”‚  โ”œโ”€ SignIn.view.jsx
โ”‚     โ”‚  โ”œโ”€ SignUp.view.jsx
โ”‚     โ”‚  โ”œโ”€ Splash.view.jsx
โ”‚     โ”‚  โ””โ”€ index.view.jsx
โ”‚     โ”œโ”€ chat
โ”‚     โ”‚  โ”œโ”€ Chat.view.jsx
โ”‚     โ”‚  โ”œโ”€ ChatDetail.view.jsx
โ”‚     โ”‚  โ””โ”€ index.view.jsx
โ”‚     โ”œโ”€ feed
โ”‚     โ”‚  โ”œโ”€ Feed.view.jsx
โ”‚     โ”‚  โ”œโ”€ FeedDetail.view.jsx
โ”‚     โ”‚  โ”œโ”€ FeedWrite.view.jsx
โ”‚     โ”‚  โ””โ”€ index.view.jsx
โ”‚     โ”œโ”€ goods
โ”‚     โ”‚  โ”œโ”€ Goods.view.jsx
โ”‚     โ”‚  โ”œโ”€ GoodsDetail.view.jsx
โ”‚     โ”‚  โ”œโ”€ GoodsWrite.view.jsx
โ”‚     โ”‚  โ””โ”€ index.view.jsx
โ”‚     โ”œโ”€ home
โ”‚     โ”‚  โ”œโ”€ Home.view.jsx
โ”‚     โ”‚  โ”œโ”€ Result.view.jsx
โ”‚     โ”‚  โ”œโ”€ Train.view.jsx
โ”‚     โ”‚  โ””โ”€ index.view.jsx
โ”‚     โ””โ”€ user
โ”‚        โ”œโ”€ Profile.view.jsx
โ”‚        โ”œโ”€ ProfileEdit.view.jsx
โ”‚        โ”œโ”€ ProfileFollow.view.jsx
โ”‚        โ””โ”€ index.view.jsx
โ””โ”€ vite.config.js

5. ๊ตฌํ˜„ ๊ธฐ๋Šฅ

  • ๋กœ๊ทธ์ธ/ ํšŒ์›๊ฐ€์ž…
  • ์ข‹์•„์š”
  • ๊ฒŒ์‹œ๊ธ€ ๋“ฑ๋ก
  • ์ง€๋„์— ์œ„์น˜ ํ‘œ์‹œ
  • ๊ฒ€์ƒ‰
  • ๋ฌดํ•œ์Šคํฌ๋กค
  • ํƒ๋ฐฐ ๋ฆฌ์ŠคํŠธ

6. UI

[์ „์ฒด ํŽ˜์ด์ง€]

image

[๋””์ž์ธ ์‹œ์Šคํ…œ]

basic design system

[ํŽ˜์ด์ง€ ๊ธฐ๋Šฅ]

1) ๋กœ๊ทธ์ธ / ํšŒ์›๊ฐ€์ž…

์Šคํ”Œ๋ž˜์‹œ ํŽ˜์ด์ง€ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€
์Šคํ”Œ๋ž˜์‹œ ๋กœ๊ทธ์ธ ํšŒ์›๊ฐ€์ž…

2) ํ™ˆ

ํ™ˆ ํŽ˜์ด์ง€
mainPage

3) ํ”ผ๋“œ

ํ”ผ๋“œ ํŽ˜์ด์ง€ ์œ ์ € ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ ๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ ํŽ˜์ด์ง€
ํ”ผ๋“œ ์œ ์ €๊ฒ€์ƒ‰ ๊ฒŒ์‹œ๊ธ€์ž‘์„ฑ
๊ฒŒ์‹œ๊ธ€ ์ƒ์„ธ ํŽ˜์ด์ง€ ๊ฒŒ์‹œ๊ธ€ ์ˆ˜์ • ๊ธฐ๋Šฅ ๊ฒŒ์‹œ๊ธ€ ์‚ญ์ œ ๊ธฐ๋Šฅ
๊ฒŒ์‹œ๊ธ€์ƒ์„ธ ๊ฒŒ์‹œ๊ธ€์ˆ˜์ • ๊ฒŒ์‹œ๊ธ€์‚ญ์ œ
๋Œ“๊ธ€ ์ž‘์„ฑ ๋Œ“๊ธ€ ์‚ญ์ œ ๊ธฐ๋Šฅ ๋Œ“๊ธ€ ์‹ ๊ณ  ๊ธฐ๋Šฅ
๋Œ“๊ธ€์ž‘์„ฑ ๋Œ“๊ธ€์‚ญ์ œ ๋Œ“๊ธ€์‹ ๊ณ 

4) ์ง€ํ•˜์ฒ  ํƒ๋ฐฐ

ํƒ๋ฐฐ ๋“ฑ๋ก ํƒ๋ฐฐ ๋ฐฐ๋‹ฌ์ˆ˜๋ฝ / ์ฑ„ํŒ… ์ฑ„ํŒ… ํ™•์ธ / ํƒ๋ฐฐ ์ƒํƒœ ๋ณ€๊ฒฝ
์œ ์ € 1 ํƒ๋ฐฐ ๋“ฑ๋ก ์œ ์ €2 ๋ฐฐ๋‹ฌ์ˆ˜๋ฝ ์œ ์ € 1 ์ฑ„ํŒ… ํ™•์ธ ํƒ๋ฐฐ ์ƒํƒœ ๋ณ€๊ฒฝ
๋ฐฐ๋‹ฌ ์™„๋ฃŒ ์ฑ„ํŒ… ๋ฐฐ๋‹ฌ ์™„๋ฃŒ ํ™•์ธ / ํƒ๋ฐฐ ์ƒํƒœ ๋ณ€๊ฒฝ ํƒ๋ฐฐ ์ˆ˜์ •
์œ ์ € 2 ๋ฐฐ๋‹ฌ ์™„๋ฃŒ ์ฑ„ํŒ… ์œ ์ € 1 ๋ฐฐ๋‹ฌ ์™„๋ฃŒ ํ™•์ธ ํƒ๋ฐฐ ์ƒํƒœ ๋ณ€๊ฒฝ ํƒ๋ฐฐ ์ˆ˜์ •
ํƒ๋ฐฐ ์‚ญ์ œ
ํƒ๋ฐฐ์‚ญ์ œ

5) ํ”„๋กœํ•„

ํ”„๋กœํ•„ ํŽ˜์ด์ง€ ํ”„๋กœํ•„ ์ˆ˜์ • ํŽ˜์ด์ง€ ํŒ”๋กœ์›Œ / ํŒ”๋กœ์ž‰ ํŽ˜์ด์ง€
ํ”„๋กœํ•„ํŽ˜์ด์ง€ ํ”„๋กœํ•„์ˆ˜์ • ํŒ”๋กœ์›Œ,ํŒ”๋กœ์ž‰ํŽ˜์ด์ง€

7. ํ•ต์‹ฌ ๊ธฐ๋Šฅ ๋ฐ ์ฝ”๋“œ

Open API

๐Ÿ“‚src/views/home/index.view.jsx

useMemo๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ •์  ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ณ , ๊ทธ๊ฒƒ์„ ๋ฐ”ํƒ•์œผ๋กœ ๋ฆฌ์ฝ”์ผ์— ์ €์žฅํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ถˆํ•„์š”ํ•œ api ์š”์ฒญ์„ ์ตœ๋Œ€ํ•œ ์ค„์ด๋ ค ํ–ˆ๊ณ , ์ด๋ฅผ ์ด์šฉํ•ด ListBox๋ฅผ ๋ Œ๋”๋งํ•˜๋Š”๋ฐ, ์ด ๋˜ํ•œ ๊ธฐ์กด์˜ ๊ฒ€์ƒ‰ ๋ฆฌ์ŠคํŠธ๋ฐ•์Šค๊ฐ€ position ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋˜ ๊ฒƒ๊ณผ ๋‹ฌ๋ฆฌ ๋ณ„๋„์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ํ•˜์—ฌ ๋ฆฌํ”Œ๋กœ์šฐ๋ฅผ ์ค„์ด๋ ค๊ณ  ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

const HomeIndexPage = () => {
  const navigate = useNavigate();
  const [subOneInfo, setSubOneInfo] = useRecoilState(subOneAtom);
  const [query, setQuery] = useRecoilState(queryAtom);

  useMemo(() => {
    //๋„์‹œ์ฒ ๋„ 1ํ˜ธ์„  ์ง€ํ•˜์ฒ ์—ญ ์ •๋ณด ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
    getSubOneInfo().then((data) => {
      const dataList = data.data.SearchInfoBySubwayNameService.row;
      const rowInfo = [...dataList].filter((elem) => elem.LINE_NUM === '01ํ˜ธ์„ ');
      const newInfo = rowInfo.map((info, index) => {
        return (info = { Query: info.STATION_NM, Id: info.STATION_CD });
      });
      setSubOneInfo(newInfo);
    });
  }, []);

  const list = useSearchData(query, subOneInfo);

  const handleClickBack = () => {
    setQuery('');
  };
  const handleClickStation = (event) => {
    navigate(`${pageUrlConfig.homePage}/${event.target.id}`, { state: event.target.textContent });
    setQuery('');
  };
  return (
    <>
      <SearchHeader onClick={handleClickBack} placeholder={`์ง€ํ•˜์ฒ ์—ญ์„ ๊ฒ€์ƒ‰ํ•ด์ฃผ์„ธ์š”`} />
      {query ? <ListBox list={list} onClick={handleClickStation} /> : <Outlet />}
      <NavMenu />
    </>
  );
};

export default HomeIndexPage;

๋ผ์šฐํ„ฐ

๐Ÿ“‚src/config/pageUrlConfig.js

๋ณ„๋„์˜ ํด๋ž˜์Šค๋กœ ์ƒ์„ฑํ•˜์—ฌ ๋ผ์šฐํ„ฐ๋ฅผ ์ตœ๋Œ€ํ•œ ํ•œ ๊ณณ์—์„œ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

const baseUrl = import.meta.env.BASE_URL;
class pageUlrConfig {
  splashPage = `${baseUrl}`;
  //์ƒ๋žต
  goodsDetailPage = `${baseUrl}goods/:id`;
  goodsEditPage = `${baseUrl}goods/edit/:id`;
}

export default new pageUlrConfig();

๐Ÿ“‚src/config/route.config.jsx

PrivateLayout์„ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ทธ์ธ ํ›„ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•˜์˜€๊ณ , loader๋ฅผ ์‚ฌ์šฉํ•ด ๋ฏธ๋ฆฌ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ํ•˜์—ฌ ๋งค๋ฒˆ localStorage์— ์ ‘๊ทผํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ค„์˜€์Šต๋‹ˆ๋‹ค.

const routeConfig = [
  {
    path: baseUrl,
    element: <DefaultLayout />,
    children: [
      {
        path: pageUrlConfig.splashPage,
        element: <AuthIndexPage/>,
        children: [
          { index: true, element: <SplashPage /> },
          { path: pageUrlConfig.signInPage, element: <SignInPage /> },
          { path: pageUrlConfig.signUpPage, element: <SignUpPage />},
        ],
      },
      {
        path: baseUrl,
        loader: async()=>sendUserInfo(),
        id: 'user',
        element: <PrivateLayout />,
        children: [
          {
            path: pageUrlConfig.homePage,
            element: <HomeIndexPage/>,
            children: [

์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ด์šฉํ•œ ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ, ๊ธ€๋กœ๋ฒŒ ์Šคํƒ€์ผ

๐Ÿ“‚src/components/default/GlobalButton.jsx

๊ณตํ†ต์ ์œผ๋กœ ์“ฐ์ด๋Š” ์Šคํƒ€์ผ ๋“ฑ์€ css๋กœ ์ •์˜ํ•˜๊ณ  ๋ฒ„ํŠผ์— ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ํ•˜์˜€๊ณ , ์™ธ์˜ ๋‹ค๋ฅธ ์Šคํƒ€์ผ์—๋„ ์‰ฝ๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

import styled, { css } from 'styled-components';
import theme from '../../style/theme';

/**@param variant: "primary" | "secondary" | "basic" */
const DefaultBtn = ({ children, variant, disabled, id }) => {
  return (
    <StyledButton variant={variant ?? 'primary'} disabled={disabled ?? false} id={id ?? ''}>
      {children}
    </StyledButton>
  );
};

const PrimaryStyle = css`
  background-color: ${theme.color.primary};
  color: ${theme.color.white};
  border-color: ${theme.color.primary};
`;

const SecondaryStyle = css`
  background-color: ${theme.color.secondary};
  color: ${theme.color.black};
  border-color: ${theme.color.secondary};
`;

const BasicStyle = css`
  background-color: ${theme.color.white};
  color: ${theme.color.primary};
  border-color: ${theme.color.primary};
`;

const StyledButton = styled.button`
  width: 100%;
  height: 100%;
  border: 1px solid;
  border-radius: 9999px;
  cursor: pointer;
  ${(props) => {
    switch (props.variant) {
      case 'primary':
        return PrimaryStyle;
      case 'secondary':
        return SecondaryStyle;
      case 'basic':
        return BasicStyle;
    }
  }}
  &:disabled {
    border-color: ${theme.color.grey};
    background-color: ${theme.color.grey};
    color: ${theme.color.white};
    cursor: default;
  }
`;
export { PrimaryStyle, SecondaryStyle, BasicStyle };
export default DefaultBtn;

๐Ÿ“‚src/style/theme.js

const theme = {
  color: {
    primary: '#3c58c1',
    secondary: '#B1BCE6',
    white: '#FFF',
    black: '#000',
    brown: '#663C06',
    grey: '#767676',
    light: '#DBDBDB',
  },
  //์ƒ๋žต
  fontSize: {
    h1: `48px`,
    h2: `36px`,
    h3: `24px`,
    body1: `18px`,
    body2: `16px`,
    body3: `14px`,
    caption: `12px`,
  },
};
export default theme;

ํ”„๋กœํ•„

๋งŽ์€ ๋ฒ„ํŠผ๋“ค์ด ํ•„์š”ํ•œ ๊ณณ์ด์–ด์„œ ์ด๋ฒคํŠธ ๋ฒ„๋ธ”๋ง์„ ํ†ตํ•ด ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ๋ฅผ ๊ด€๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ๊ฒŒ์‹œ๊ธ€๊ณผ ํƒ๋ฐฐ ๋ชฉ๋ก์„ ๋„˜๋‚˜๋“œ๋Š” ํƒญ ๋ฉ”๋‰ด๋Š” ๊ฐ„๋‹จํ•˜๊ฒŒ ์Šคํƒ€์ผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์กฐ์ ˆํ•˜์˜€์Šต๋‹ˆ๋‹ค.

const handleClickBtns = (event) => {
    switch(event.target.id){
      case 'chat':
        navigate(pageUrlConfig.chatPage);
        break;
      case 'share':
        const BASE_URL = import.meta.env.BASE_URL;
        navigator.clipboard.writeText(`${BASE_URL}${pathname}`);
        alert('๋งํฌ ๋ณต์‚ฌ ์™„๋ฃŒ!');
        break;
      case 'edit':
        navigate(`${pageUrlConfig.profilePage}/${user.accountname}/edit`)
        break;
      case 'unfollow':
        unFollowing();
        break;
      case 'follow':
        Following();
        break;
    }
//์ƒ๋žต
<ButtonWrap onClick={handleClickBtns}>
  <button id='chat'></ button>
  {isMy ?
       <DefaultBtn id={`edit`}>ํ”„๋กœํ•„ ์ˆ˜์ •</DefaultBtn>
				: (profileInfo.isfollow ?
		       <DefaultBtn variant={'basic'} id={`unfollow`}>์–ธํŒ”๋กœ์šฐ</DefaultBtn>
	        :  <DefaultBtn variant={'primary'} id={`follow`}>ํŒ”๋กœ์šฐ</DefaultBtn>
   )}
 <button id='share'></ button>
</ButtonWrap>
//์ƒ๋žต
//ํƒญ ๋ฉ”๋‰ด ํด๋ฆญํ•˜๋ฉด ์Šคํƒ€์ผ ๋ฐ˜๋Œ€๋กœ ์ฃผ๊ธฐ
const TabGroup = styled.div`
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  width: 100%;
  height: 64px;
  background-color: ${theme.color.white};

  & :nth-child(1){
    ${(props)=> props.color? SecondaryStyle : BasicStyle}
  }
  & :nth-child(2){
    ${(props)=> !props.color? SecondaryStyle : BasicStyle}
  }
`;

react-hook-form

ํ”„๋กœ์ ํŠธ ์ง„ํ–‰ ์ค‘ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€, ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€, ๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ ํŽ˜์ด์ง€, ์ˆ˜์ • ํŽ˜์ด์ง€ ๋“ฑ๋“ฑ form data๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ํŽ˜์ด์ง€๊ฐ€ ์ ์  ๋Š˜์–ด๋‚˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ์กด ํšŒ์›๊ฐ€์ž… form data๋ฅผ ๊ด€๋ฆฌ ๋ฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ง„ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” form ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ state , form ์ž…๋ ฅ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  handler, form ๋ฐ์ดํ„ฐ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๊ธฐ ์œ„ํ•œ validation ํ•จ์ˆ˜, ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๊ฐ’์„ ์ƒํƒœ๊ด€๋ฆฌํ•  state ๋ฅผ ์ „๋ถ€ ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•ด์•ผํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ €ํฌ๋Š” ์—ฌ๋Ÿฌ๊ฐœ์˜ form data๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ์—ˆ๊ณ , ๊ทธ์—๋”ฐ๋ฅธ state์™€ ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€๋กœ ์„ ์–ธํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฅผ ๊ฐ„์†Œํ™” ์‹œํ‚ค๊ธฐ ์œ„ํ•ด react-hook-form ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ–ˆ์œผ๋ฉฐ, react-hook-form ์‚ฌ์šฉ์œผ๋กœ ์œ„์— ์„ ์–ธํ•ด์•ผํ•  state ๋ฐ ํ•จ์ˆ˜๋“ค์„ ์ „๋ถ€ react-hook-form์œผ๋กœ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ–ˆ์Šต๋‹ˆ๋‹ค.

// react-hook-form
  const {
    register,
    handleSubmit,
    watch,
    formState: { isSubmitting, isSubmitted, errors },
  } = useForm({ mode: 'onChange' });
  const userAccountId = watch('accountId');
  const userEmail = watch('email');
  const userPwd = watch('password');

...

<Input
  id="accountId"
  type="accountId"
  name="accountId"
  placeholder="์˜๋ฌธ, ์ˆซ์ž, ํŠน์ˆ˜๋ฌธ์ž(.),(_)๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ"
  aria-invalid={isSubmitted ? (errors.accountId ? 'true' : 'false') : undefined}
   {...register('accountId', {
     required: '*๊ณ„์ •ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.',
     pattern: {
     value: /^[A-Za-z0-9._]+$/,
     message: '*๊ณ„์ •ID ํ˜•์‹์— ๋งž์ง€ ์•Š์Šต๋‹ˆ๋‹ค.',
    },
  })}
 />

...

register๋ฅผ ์‚ฌ์šฉํ•ด ์ž…๋ ฅ์ „ required ๋ฉ”์‹œ์ง€, ์œ ํšจ์„ฑ ๊ฒ€์ฆ ์ •๊ทœ์‹, ์œ ํšจ์„ฑ ๊ฒ€์ฆ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๊ด€๋ฆฌ ํ•  ์ˆ˜ ์žˆ๊ณ , watch๋ฅผ ํ†ตํ•ด form ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ state, ์ž…๋ ฅ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  handler๋ฅผ ๋Œ€์‹ ํ•˜์—ฌ react-hook-form์„ ์‚ฌ์šฉํ•˜๊ธฐ ์ „๋ณด๋‹ค ํ›จ์”ฌ ๊ฐ„๊ฒฐํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฐ”ํ…€์‹œํŠธ ์ปดํฌ๋„ŒํŠธ ์žฌ์‚ฌ์šฉ

๋ฐ”ํ…€์‹œํŠธ UI๋ฅผ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ƒํƒœ๋ฅผ Recoil์„ ํ†ตํ•ด ์ „์—ญ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ , ์˜ต์…˜์„ ์ƒ์„ฑํ•˜๋Š” ์ปค์Šคํ…€ ํ›…์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๐Ÿ“‚src/atoms/bottomSheetStateAtom.js

import { atom } from 'recoil';

export const bottomSheetStateAtom = atom({
  key: 'bottomSheetState',
  default: false,
});

export const bottomSheetOptions = atom({
  key: 'bottomSheetOptions',
  default: [],
});

๐Ÿ“‚src/hooks/uswBottomSheetOption.js

const useBottomSheetOptions = ({
  currentAccountname,
  authorAccountname,
  postEdit,
  postDelete,
  postReport,
  commentDelete,
  commentReport,
  type,
}) => {
  let options = [];

  switch (type) {
    case 'post':
      if (currentAccountname === authorAccountname) {
        options = [
          { label: '๊ฒŒ์‹œ๊ธ€ ์ˆ˜์ •', callback: postEdit },
          { label: '๊ฒŒ์‹œ๊ธ€ ์‚ญ์ œ', callback: postDelete },
        ];
      } else {
        options = [{ label: '๊ฒŒ์‹œ๊ธ€ ์‹ ๊ณ ', callback: postReport }];
      }
      break;

    case 'comment':
      if (currentAccountname === authorAccountname) {
        options = [{ label: '๋Œ“๊ธ€ ์‚ญ์ œ', callback: commentDelete }];
      } else {
        options = [{ label: '๋Œ“๊ธ€ ์‹ ๊ณ ', callback: commentReport }];
      }
      break;

    default:
      break;
  }

  return options;
};

export default useBottomSheetOptions;

8. ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

9. Branch ์ „๋žต

๋ธŒ๋žœ์น˜ ๋ชจ๋ธ์€ ๊นƒ ํ”Œ๋กœ์šฐ(Git Flow) ์ฐธ๊ณ ํ•˜์—ฌ main- development - feature ๋‹จ๊ณ„๋กœ ๋‚˜๋ˆ„์–ด ์ฝ”๋“œ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ „๋žต์„ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๋ธŒ๋žœ์น˜๋Š” ์„ธ ๊ฐ€์ง€๊ฐ€ ์žˆ๋Š”๋ฐ ํ•ญ์ƒ ์œ ์ง€๋˜๋Š” (main, development(dev)) ๋ธŒ๋žœ์น˜๊ฐ€ ์žˆ๊ณ , ์ผ์‹œ์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ๋‹ค๊ฐ€ ์‚ฌ๋ผ์ง€๋Š” (feature(feat)) ๋ธŒ๋žœ์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋Šฅ, ํŽ˜์ด์ง€ ์ž‘์—… ์‹œ feature ๋ธŒ๋žœ์น˜๋ฅผ ์ƒ์„ฑ ํ›„ ์ž‘์—…ํ•˜๊ณ  dev ์— push ํ•˜์—ฌ rebase ํ•˜๊ณ , ์™„๋ฃŒ๋˜๋ฉด ๋ธŒ๋žœ์น˜๋ฅผ ์‚ญ์ œ ํ›„ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค. main ์œผ๋กœ push ํ•˜๋Š” ๊ฒฝ์šฐ 2๋ช… ์ด์ƒ์˜ ์Šน์ธ ํ›„ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•˜์—ฌ ์‹ค์ˆ˜๋กœ ํŒŒ์ผ์ด ๋‚ ์•„๊ฐ€๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๊นƒ ํ”Œ๋กœ์šฐ

git flow: main - dev(test) - feat/๊ธฐ๋Šฅ๋ช…

main: ์ตœ์ข… ๋ฐฐํฌ์šฉ ๋ธŒ๋žœ์น˜

development: ํ…Œ์ŠคํŠธ์šฉ ๋ธŒ๋žœ์น˜

feat/: ๊ธฐ๋Šฅ๋ณ„ ์ž‘์—…์šฉ ๋ธŒ๋žœ์น˜

10. ํ˜‘์—…

  • ํŒ€ ๊ทœ์น™ image

๐Ÿ‘‰ํšŒ์˜๋ก image

๐Ÿ‘‰ํšŒ๊ณ ๋ก

๐Ÿ‘‰์ปจ๋ฒค์…˜

๐Ÿ‘‰Github discussions image

11. ํŒ€ ๋ฌธํ™”

  • ์•„์ด์Šค๋ธŒ๋ ˆ์ดํ‚น
    • ์ฒซ ๋ชจ์ž„์— ๋‚ฏ๊ฐ€๋ฆผ์ด ์žˆ์—ˆ์ง€๋งŒ ์‚ฌ๋Š” ์ง€์—ญ, MBTI, ์ „๊ณต, ๊ฐœ์ธ ์ผ์ • ๋“ฑ์„ ์ฃผ์ œ๋กœ ์ด์•ผ๊ธฐ ๋‚˜๋ˆ„์—ˆ์Šต๋‹ˆ๋‹ค. ์•„์ด์Šค๋ธŒ๋ ˆ์ดํ‚น์œผ๋กœ ๋ชจ๋“  ํŒ€์›์ด ์„œ์šธ ์ง€ํ•˜์ฒ  1ํ˜ธ์„  ๊ทผ์ฒ˜์— ๊ฑฐ์ฃผ ํ•œ๋‹ค๋Š” ๊ณตํ†ต์ ์„ ๋ฐœ๊ฒฌํ•˜์˜€๊ณ  ์ด ํŠน์ง•์„ ๋ฐ”ํƒ•์œผ๋กœ ๊ฐœ์„ฑ ์žˆ๋Š” ํŒ€ ํ”„๋กœ์ ํŠธ ์•„์ด๋””์–ด๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
    • ๋งค์ผ ์˜ค์ „ 9์‹œ, ์˜คํ›„ 5์‹œ์— ํšŒ๊ณ ๋ก์„ ์ž‘์„ฑํ•˜๋ฉด์„œ ์•„์ด์Šค๋ธŒ๋ ˆ์ดํ‚น ํƒ€์ž„์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋„๋ก ๋…ธ๋ ฅํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ํ”„๋กœ์ ํŠธ ์ค‘ ํž˜๋“ค์–ด์„œ ์ง€์น˜๋ฉด ํœด์‹ ์‹œ๊ฐ„์„ ๊ฐ€์ง€๋ฉฐ ๋ถ„์œ„๊ธฐ๋ฅผ ํ’€์–ด๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

1ํ˜ธ์„ 02 MBTI02 ๋งŒ์„ธ02jpg

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

์˜์‚ฌ๊ฒฐ์ •_3 ๋ฆฌ์ฝ”์ผ๋กœ02

  • ๋งค์ผ ์˜ค์ „, ์˜คํ›„ ์Šคํ”„๋ฆฐํŠธ 2ํšŒ ์ง„ํ–‰

    • ๊ฐ์ž ์ง„ํ–‰ ์ƒํ™ฉ์„ ํŒŒ์•…ํ•˜๊ณ  ์˜ค์ „, ์˜คํ›„ ๋ชฉํ‘œ๋ฅผ ์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๋จธ์ง€ ๊ณผ์ •์„ ๊ฑฐ์น˜๋ฉด์„œ ์ฝ”๋“œ๊ฐ€ ๋„ˆ๋ฌด ๋‹ฌ๋ผ์ ธ์„œ ํฐ ์ถฉ๋Œ์ด ์ƒ๊ธฐ๋Š” ์ผ์„ ๋ฐฉ์ง€ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • ์ฃผ 1ํšŒ ์˜คํ”„๋ผ์ธ ๋ชจ์ž„

    • ๋ชจ๋‘ ์„œ์šธ์— ๊ฑฐ์ฃผํ•˜์—ฌ ์ ‘๊ทผ์„ฑ์ด ์ข‹์•˜์œผ๋ฏ€๋กœ ์ฃผ 1ํšŒ ์ด์ƒ ์˜คํ”„๋ผ์ธ์œผ๋กœ ๋งŒ๋‚˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ ํ•˜๋„๋ก ์ผ์ •์„ ๊ณ„ํšํ–ˆ์Šต๋‹ˆ๋‹ค.

์šฉ์‚ฐ ๋ชจ์ž„ _1 ์šฉ์‚ฐ๋ชจ์ž„_6

  • ๐Ÿ‘‰์Šคํ„ฐ๋””

    • ์˜ค์ „ 9์‹œ๋ถ€ํ„ฐ ์˜คํ›„ 6์‹œ๊นŒ์ง€๋Š” ๊ฐœ๋ฐœ์— ์ง‘์ค‘ํ•˜๊ณ  ๊ณต๋ถ€๊ฐ€ ํ•„์š”ํ•œ ๋ถ€๋ถ„์€ ์ €๋… 7์‹œ ์ดํ›„์— ๋”ฐ๋กœ ์Šคํ„ฐ๋””๋ฅผ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
    • ๋ฆฌ์ฝ”์ผ์„ ์ด์šฉํ•ด ๊ฒ€์ƒ‰์–ด๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ๋ฅผ ์ด์šฉํ•ด ์ž์ฃผ์‚ฌ์šฉํ•˜๋Š” ๊ฒ€์ƒ‰์–ด ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๊ด€ํ•ด ์Šคํ„ฐ๋””๋ฅผ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • ๋ฌธ์„œํ™”

    • ๋…ธ์…˜, ๊นƒ ์œ„ํ‚ค, ์˜ต์‹œ๋””์–ธ ๋“ฑ์„ ํ™œ์šฉํ•˜์—ฌ ์ฃผ๊ธฐ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋˜ ํšŒ์˜์™€ ํšŒ๊ณ , ์Šคํ„ฐ๋””์— ๊ด€ํ•œ ์ž๋ฃŒ๋ฅผ ์ •๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.
    • ๋…ธ์…˜, ๋งˆ์ผ์Šคํ†ค์œผ๋กœ ํ”„๋กœ์ ํŠธ ๊ด€๋ จ ๊ธฐ๋Šฅ ๋ช…์„ธ ์ž‘์„ฑ๊ณผ ์ผ์ •๊ด€๋ฆฌ๋ฅผ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

12. ํ”„๋กœ์ ํŠธ ๊ด€๋ จ ๋ฌธ์„œ

๐Ÿ”— ํŒ€ ๋…ธ์…˜

๐Ÿ”— ๊ธฐ๋Šฅ ๋ช…์„ธ์„œ

๐Ÿ”— ์™€์ด์–ดํ”„๋ ˆ์ž„

๐Ÿ”— User Flow Chart

๐Ÿ”— SITE MAP

๐Ÿ”— ํ”ผ๊ทธ๋งˆ ๋””์ž์ธ

๐Ÿ”— ๊นƒํ—ˆ๋ธŒ ์œ„ํ‚ค

12. ๋ฆฌํŒฉํ† ๋ง

  • svg๋กœ ์‚ฌ์šฉํ•˜๋Š” ์ด๋ฏธ์ง€๋Š” ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฐ”๊พธ์–ด์„œ ์‚ฌ์šฉํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
  • BrowerRouter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ github page ๋“ฑ์˜ ์ •์  ํŽ˜์ด์ง€์—์„œ๋Š” ์ƒˆ๋กœ๊ณ ์นจ ์‹œ 404 ํŽ˜์ด์ง€๊ฐ€ ๋œจ๋Š” ์˜ค๋ฅ˜๋ฅผ ์ˆ˜์ •ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
  • http์™€ https ๊ฐ„์˜ ํ†ต์‹ ์ด ๋ง‰ํ˜€ ์ผ๋ถ€ ์˜คํ”ˆ api ์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€ํ•œ ์ ์„ ์ˆ˜์ •ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
  • ๋ฌดํ•œ์Šคํฌ๋กค์— ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ๋ฅผ ์ ์šฉํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ๊ฒŒ์‹œ๊ธ€ ํ•„ํ„ฐ๋ง๊ณผ ๊ฐ™์ด ์ ์šฉ์„ ํ•˜๋ ค๋‹ค ๋ณด๋‹ˆ ์ž˜ ์ž‘๋™์ด ์•ˆ๋˜๋Š” ์ ์ด ์žˆ์–ด์„œ ์Šคํฌ๋กค ์ด๋ฒคํŠธ๋กœ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

13. ๋Š๋‚€ ์ 

์ง€ํ˜„

๋นŒ๋“œ์™€ ๋ผ์šฐํ„ฐ์™€ ๋ฏธ์šด ์ •์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ์ •์  ํŽ˜์ด์ง€์˜ ๊ฒฝ์šฐ HashRouter๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜์ง€๋งŒ, ๊ทธ๋ ‡๋‹ค๊ณ  ๋ฐ”๊พธ๊ธฐ์—” ๋กœ์ปฌ์—์„œ ์•ˆ๋˜๊ณ โ€ฆ ๊ฒฐ๊ตญ BrowerRouter๋กœ ๊ฐœ๋ฐœ์˜ ํŽธ์˜์„ฑ์„ ์ฑ™๊ธด ๋’ค ๋ฆฌํŒฉํ† ๋ง ๊ธฐ๊ฐ„์— ์—ด์‹ฌํžˆ ์ˆ˜์ •ํ•˜๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ, ๋ฆฌ์ฝ”์ผ, vite ๋ชจ๋‘ ์ด๋ฒˆ์— ์ฒ˜์Œ ์จ๋ณธ ๊ฒƒ๋“ค์ด๋ผ์„œ ๋ฒ„๋ฒ…๊ฑฐ๋ ธ์ง€๋งŒ ๋‚˜์ค‘์—๋Š” ์ง์ ‘ ์•Œ๋ ค์ค„ ์ˆ˜ ์žˆ์„ ์ •๋„๋กœ ๋น ๋ฅด๊ฒŒ ์ต์ˆ™ํ•ด์กŒ์Šต๋‹ˆ๋‹ค. ์ œ ์žฅ์ ์„ ์•Œ ์ˆ˜ ์žˆ๋Š” ์‹œ๊ฐ„์ด์–ด์„œ ์ข‹์•˜๊ณ , ๋ฌด์—‡๋ณด๋‹ค ์ œ๊ฐ€ ์ค‘๊ฐ„์— ๋”ด ๊ธธ๋กœ ์ƒˆ๊ฑฐ๋‚˜ ํ•˜๋‚˜๋งŒ ํŒŒ๊ณ ๋“ค ๋•Œ ๋™๋ฃŒ๋“ค์ด ์˜†์—์„œ ์ž˜ ์žก์•„์ค˜์„œ ์ข‹์•˜์Šต๋‹ˆ๋‹ค.โœจ api ๋ณ„๊ฑฐ ์•„๋‹ˆ๋„ค์š”~~(์ง€๋„ ํ•˜๋‚˜๋กœ ์ผ์ฃผ์ผ ์žก์€ ์‚ฌ๋žŒ)~~

๊ทœ๋ฆฌ

์งง์€ ๊ธฐ๊ฐ„ ๋‚ด์— ํ”„๋กœ์ ํŠธ๋ฅผ ์™„์„ฑํ•  ์ˆ˜ ์žˆ์„์ง€ ๊ฑฑ์ •์ด ์ปธ์ง€๋งŒ ๋ฉ‹์ง„ ํŒ€์›๋ถ„๋“ค์„ ๋งŒ๋‚˜ ์ฆ๊ฒ๊ฒŒ, ๋๊นŒ์ง€ ๋งˆ๋ฌด๋ฆฌ ํ•  ์ˆ˜ ์žˆ์—ˆ๋˜ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋ฉด์„œ ์šฐ๋ฆฌ๊ฐ€ ํ•˜๋Š” ๊ฒƒ์— ๋Š์ž„์—†์ด ์ด์œ ๋ฅผ ์ฐพ๊ณ , ์˜๊ฒฌ์„ ๋‚˜๋ˆ„๊ณ , ๋ฌธ์„œํ™”๋ฅผ ํ•˜๋ฉฐ ์ฒด๊ณ„์ ์œผ๋กœ ํ˜‘์—…ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์šด ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋๋‚œ ๋’ค ๋Œ์•„๋ณด๋‹ˆ ๋ถ€์กฑํ•œ ๋ถ€๋ถ„๋„ ๋งŽ์ด ๋ณด์ด์ง€๋งŒ, ์•ž์œผ๋กœ์˜ ๋ฆฌํŒฉํ† ๋ง์„ ํ†ตํ•ด ์ฑ„์›Œ๋‚˜๊ฐ€๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์ตœ๊ณ ์˜ ๋นŒ๋Ÿฐ๋“ค ๐Ÿ˜ˆ ์•ž์œผ๋กœ๋„ ํŒŒ์ดํŒ…์ž…๋‹ˆ๋‹ค!

์œค์ƒ

ํ•ญ์ƒ ๊ธฐ๋Šฅ ์™„์„ฑ๋งŒ ๋ชฉ์ ์„ ๋‘” ์ฝ”๋”ฉ์„ ํ•ด์™”์—ˆ๋Š”๋ฐ ์ฒ˜์Œ์œผ๋กœ ๊ธฐํš๋ถ€ํ„ฐ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ, ์ปจ๋ฒค์…˜ ๋“ฑ ์ฒซ ๊ตฌ์กฐ๋ฅผ ํƒ„ํƒ„ํžˆ ์žก๊ณ  ์‹œ์ž‘ํ•œ ํ”„๋กœ์ ํŠธ๋Š” ์ฒ˜์Œ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋งŒํผ ์š•์‹ฌ๋„ ๋งŽ์•˜๊ณ , ์š•์‹ฌ์ด ๋งŽ์•˜๋˜ ๋งŒํผ ์•„์‰ฌ์›€๋„ ๋งŽ์•˜๋˜ ํ”„๋กœ์ ํŠธ ๊ฐ™์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํŒ€ ๋ฆฌ๋”๋กœ ์–ธ์ œ๋‚˜ ์ €ํฌ ํŒ€์›๋“ค์„ ์ด๋Œ์–ด์ฃผ๊ณ  ๋งŽ์ด ์•Œ๋ ค์ฃผ์‹  ์ง€ํ˜„๋‹˜๊ณผ ๋๊นŒ์ง€ ์—ด์‹ฌํžˆ ์ฐธ์—ฌํ•ด์ฃผ์‹  ๊ทœ๋ฆฌ๋‹˜ ๋ช…์ง„๋‹˜ ๋•๋ถ„์— ๊ทธ ์•„์‰ฌ์›€์ด ์—†์–ด์งˆ ๋งŒํผ ๋œป ๊นŠ์€ ์‹œ๊ฐ„์ด์—ˆ๋˜ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์•ž์œผ๋กœ๋„ ํŒ€์›๋ถ„๋“ค๊ณผ ๊พธ์ค€ํžˆ ๋ฆฌํŽ™ํ† ๋ง์„ ๊ฑฐ์ณ ๋งˆ๋ฌด๋ฆฌ๋„ ์™„๋ฒฝํ•˜๊ฒŒ ํ•˜๊ณ ์‹ถ์Šต๋‹ˆ๋‹ค~~!

๋ช…์ง„

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