Skip to content

Pyotato/vinylify

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

412 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๋ฐ”์ด๋‹ ๋ ˆ์ฝ”๋“œ์ฒ˜๋Ÿผ, ์Œ์•…์„ ์ƒˆ๊ธฐ๋‹ค

image

Spotify API๋ฅผ ํ™œ์šฉํ•ด ๋‚˜์˜ Top ํŠธ๋ž™๊ณผ ์ถ”์ฒœ ์Œ์•…์„ ํƒ์ƒ‰ํ•˜๊ณ ,
๊ฐ€์‚ฌ์™€ ์•„ํ‹ฐ์ŠคํŠธ ์ •๋ณด๊นŒ์ง€ ํ•œ ํ™”๋ฉด์—์„œ ์ฆ๊ธธ ์ˆ˜ ์žˆ๋Š” Spotify ์›น ํด๋ผ์ด์–ธํŠธ ํ”„๋กœ์ ํŠธ.

๊ธฐ์ˆ ์Šคํƒ :

ํด๋ผ์ด์–ธํŠธ

  • React : ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜์˜ UI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, ํ’๋ถ€ํ•œ ์ƒํƒœ๊ณ„์™€ ๋†’์€ ์ž์œจ์„ฑ์„ ๋ฐ”ํƒ•์œผ๋กœ ๊ตฌ์กฐ์ ์ธ UI๋ฅผ ์„ค๊ณ„ํ•˜๊ธฐ์— ์ ํ•ฉํ•˜๋‹ค๊ณ  ํŒ๋‹จํ•˜์—ฌ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

  • TypeScript : ์ •์  ํƒ€์ž… ๊ฒ€์‚ฌ๋ฅผ ํ†ตํ•œ ์•ˆ์ •์„ฑ ํ™•๋ณด์™€, ํƒ€์ž… ์ธํ…”๋ฆฌ์„ผ์Šค๋ฅผ ํ†ตํ•œ ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ ํ–ฅ์ƒ์„ ์œ„ํ•ด ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

  • SASS -> Tailwind CSS : ๊ธฐ์กด์— ์‚ฌ์šฉํ•˜๋˜ Sass ๋Œ€์‹  Tailwind CSS v4๋ฅผ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค. @theme ๋ธ”๋ก์—์„œ ๋””์ž์ธ ํ† ํฐ์„ CSS ๋ณ€์ˆ˜๋กœ ์ •์˜ํ•˜๋ฉด ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๊ฐ€ ์ž๋™ ์ƒ์„ฑ๋˜์–ด ๋ณ„๋„์˜ ๋ณ€์ˆ˜ ํŒŒ์ผ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š” ์—†์–ด์กŒ๊ณ , ์Šคํƒ€์ผ์„ JSX์™€ ๊ฐ™์€ ํŒŒ์ผ์—์„œ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งŽ์€ ๊ตฌ์กฐ์—์„œ ํŒŒ์ผ ์ „ํ™˜ ๋น„์šฉ์„ ์ค„์˜€์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์‚ฌ์šฉํ•œ ํด๋ž˜์Šค๋งŒ ๋ฒˆ๋“ค์— ํฌํ•จ๋˜๋Š” ํŠธ๋ฆฌ์‰์ดํ‚น์œผ๋กœ Sass ๋Œ€๋น„ CSS ๋ฒˆ๋“ค ํฌ๊ธฐ๋ฅผ ์ตœ์†Œํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • React Router DOM : SPA์—์„œ ํŽ˜์ด์ง€ ์ „ํ™˜์„ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. React.lazy์™€ ๊ฒฐํ•ฉํ•ด ํŽ˜์ด์ง€ ๋‹จ์œ„์˜ ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…์„ ์„ ์–ธ์ ์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋Šฅ ๊ตฌํ˜„์— ์‚ฌ์šฉํ•œ ๋„๊ตฌ

  • ky : fetch API ๊ธฐ๋ฐ˜์˜ ๊ฒฝ๋Ÿ‰ HTTP ํด๋ผ์ด์–ธํŠธ๋กœ, axios์— ๋น„ํ•ด ๋ฒˆ๋“ค ํฌ๊ธฐ๊ฐ€ ์ž‘๊ณ  ์ธํ„ฐ์…‰ํ„ฐ ๊ตฌ์กฐ๊ฐ€ ๊ฐ„๊ฒฐํ•˜์—ฌ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. afterResponse ํ›…์„ ํ™œ์šฉํ•ด API ์˜ค๋ฅ˜๋ฅผ ์ค‘์•™์—์„œ ์ผ๊ด€๋˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • Zustand : ์ „์—ญ ํ† ์ŠคํŠธ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. React Context ๋Œ€๋น„ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ๊ฐ€ ์ ๊ณ , ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์—์„œ๋„ ์Šคํ† ์–ด์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด ํ† ์ŠคํŠธ ํŒฉํ† ๋ฆฌ ํŒจํ„ด๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํŒฉํ† ๋ฆฌ ID๋ณ„๋กœ ํ™œ์„ฑ ํ† ์ŠคํŠธ๋ฅผ ์ถ”์ ํ•˜๋ฉฐ, dismissAllExcept๋ฅผ ํ†ตํ•ด ํŠน์ • ํ™”๋ฉด ์ „ํ™˜ ์‹œ ์ปจํ…์ŠคํŠธ ํ† ์ŠคํŠธ๋งŒ ์ œ๊ฑฐํ•˜๊ณ  ์ „์—ญ ํ† ์ŠคํŠธ๋Š” ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • TanStack Virtual : ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ์ฒ˜๋Ÿผ ์ˆ˜๋ฐฑ ๊ฐœ์˜ ์•„์ดํ…œ์ด ํ•œ ๋ฒˆ์— ๋ Œ๋”๋ง๋  ์ˆ˜ ์žˆ๋Š” ๋ฆฌ์ŠคํŠธ์—์„œ, DOM์— ์‹ค์ œ๋กœ ๋ณด์ด๋Š” ์š”์†Œ๋งŒ ๋ Œ๋”๋งํ•˜๋Š” ๊ฐ€์ƒํ™”๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ํ–‰ยท์—ด์„ ๋ชจ๋‘ ๊ฐ€์ƒํ™”ํ•˜๋Š” 2D ๊ทธ๋ฆฌ๋“œ ๊ตฌํ˜„์— ํ™œ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  • TanStack Query : ์„œ๋ฒ„ ์ƒํƒœ์™€ ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ๋ฅผ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์บ์‹ฑ, ์ž๋™ ๋ฆฌํŒจ์นญ, ๋กœ๋”ฉยท์—๋Ÿฌ ์ƒํƒœ ๊ด€๋ฆฌ ๋“ฑ์„ ์„ ์–ธ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด, ์ง์ ‘ useEffect๋กœ ๋น„๋™๊ธฐ ๋กœ์ง์„ ๋‹ค๋ฃจ๋Š” ๊ฒƒ๋ณด๋‹ค ํ›จ์”ฌ ๊ฐ„๊ฒฐํ•˜๊ณ  ์•ˆ์ •์ ์ธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์—๋Ÿฌ ํŠธ๋ž˜ํ‚น์— ์‚ฌ์šฉํ•œ ๋„๊ตฌ :

  • Sentry : ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํŠธ๋ž˜ํ‚นํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” ๋น„ํ™œ์„ฑํ™”ํ•˜์—ฌ ๋…ธ์ด์ฆˆ๋ฅผ ์ค„์ด๊ณ , sourcemap์„ ํ™œ์šฉํ•ด minified๋œ ์ฝ”๋“œ์—์„œ๋„ ์—๋Ÿฌ ๋ฐœ์ƒ ์œ„์น˜๋ฅผ ์ •ํ™•ํžˆ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋นŒ๋“œ ๋„๊ตฌ :

  • Vite : ๋น ๋ฅธ HMR๊ณผ ES Module ๊ธฐ๋ฐ˜์˜ ๋ฒˆ๋“ค๋ง์„ ์œ„ํ•ด ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. rollup-plugin-visualizer๋ฅผ ํ†ตํ•ด ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ์˜ ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ๋ถ„์„ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฐฐํฌ :

  • Vercel : ๋ณ„๋„์˜ ์„œ๋ฒ„ ์„ค์ • ์—†์ด GitHub ์—ฐ๋™๋งŒ์œผ๋กœ CI/CD๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๊ณ , ํ”„๋ก ํŠธ์—”๋“œ ํ”„๋กœ์ ํŠธ์— ์ตœ์ ํ™”๋œ ๋ฐฐํฌ ํ™˜๊ฒฝ์„ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋Šฅ์†Œ๊ฐœ :

๐Ÿ“ผ ๋ฐ๋ชจ ์˜์ƒ

๊ธฐ๋Šฅ ์Šคํฌ๋ฆฐ์ƒท
๋กœ๊ทธ์ธ image
๋ฉ”์ธ
ํ˜„์žฌ ์žฌ์ƒ ๊ณก
์•จ๋ฒ”, ์•„ํ‹ฐ์ŠคํŠธ, ํŠธ๋ž™, ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ ๊ฒ€์ƒ‰

โœจ ์ž๋ž‘ํ•˜๊ณ  ์‹ถ์€ ๋‚ด์šฉ

TanStack Query๋ฅผ ํ™œ์šฉํ•œ ๋„๋ฉ”์ธ๋ณ„ ์ฟผ๋ฆฌ ํ›… ์„ค๊ณ„ :

API ํ˜ธ์ถœ ๋กœ์ง์„ ์ปดํฌ๋„ŒํŠธ์—์„œ ์™„์ „ํžˆ ๋ถ„๋ฆฌํ•˜๊ณ , ๋„๋ฉ”์ธ(track / artist / search)๋ณ„๋กœ ์ฟผ๋ฆฌ ํ›…์„ ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์ฟผ๋ฆฌ ํ›…์€ ๊ณตํ†ต CONFIG๋ฅผ ์ฐธ์กฐํ•ด ์ผ๊ด€๋œ retry / throwOnError ์ •์ฑ…์„ ๋”ฐ๋ฅด๋„๋ก ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค.

retry ์ •์ฑ…์˜ ๊ฒฝ์šฐ 401, 403, 429 ์ƒํƒœ๋Š” ์žฌ์‹œ๋„ํ•˜์ง€ ์•Š๊ณ , 408(ํƒ€์ž„์•„์›ƒ) ๋“ฑ์˜ ์ผ์‹œ์  ์˜ค๋ฅ˜์— ๋Œ€ํ•ด์„œ๋งŒ ์žฌ์‹œ๋„๋ฅผ ํ—ˆ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. throwOnError ์ •์ฑ…์€ ์ธ์ฆ ๊ด€๋ จ ์˜ค๋ฅ˜(401, 403)์— ํ•œํ•ด์„œ๋งŒ ์—๋Ÿฌ๋ฅผ ErrorBoundary๋กœ ์ „ํŒŒํ•˜๋„๋ก ์„ค์ •ํ•˜์—ฌ, ์ธ์ฆ ์˜ค๋ฅ˜์ผ ๋•Œ๋Š” ์‚ฌ์šฉ์ž๋ฅผ ๋ช…ํ™•ํ•œ ์—๋Ÿฌ ํ™”๋ฉด์œผ๋กœ ์•ˆ๋‚ดํ•˜๊ณ , ๊ทธ ์™ธ์˜ ์˜ค๋ฅ˜๋Š” ์กฐ์šฉํžˆ ์ฒ˜๋ฆฌ๋˜๋„๋ก ๊ตฌ๋ถ„ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ค‘์•™ํ™”๋œ ์—๋Ÿฌ ํ•ธ๋“ค๋ง :

ky์˜ afterResponse ํ›…์„ ํ™œ์šฉํ•ด ๋ชจ๋“  API ์‘๋‹ต์˜ ์˜ค๋ฅ˜๋ฅผ handleError ํ•จ์ˆ˜์—์„œ ์ผ๊ด„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ HTTP ์ƒํƒœ ์ฝ”๋“œ์— ๋Œ€์‘ํ•˜๋Š” ERROR_NAMES์™€ ERROR_MESSAGES๋ฅผ ์ƒ์ˆ˜๋กœ ์ •์˜ํ•˜๊ณ , ์˜ค๋ฅ˜ ๊ฐ์ฒด์˜ name๊ณผ message๋ฅผ ํ†ต์ผ๋œ ํ˜•์‹์œผ๋กœ ๊ต์ฒดํ•จ์œผ๋กœ์จ ์ปดํฌ๋„ŒํŠธ ๊ณ„์ธต์—์„œ๋Š” ์—๋Ÿฌ์˜ ์ถœ์ฒ˜(Spotify API, OVH, ReccoBeats)์— ๊ด€๊ณ„์—†์ด ๋™์ผํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์—๋Ÿฌ๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.

401, 403 ์˜ค๋ฅ˜์˜ ๊ฒฝ์šฐ ๋กœ์ปฌ์Šคํ† ๋ฆฌ์ง€์˜ ์ธ์ฆ ํ† ํฐ์„ ์ฆ‰์‹œ ์ œ๊ฑฐํ•˜์—ฌ ๋งŒ๋ฃŒ๋œ ์„ธ์…˜์ด ๋‚จ์•„์žˆ์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. ErrorBoundary์˜ FallbackRender์—์„œ๋„ ๋™์ผํ•œ ERROR_MESSAGES ์ƒ์ˆ˜๋ฅผ ์ฐธ์กฐํ•˜์—ฌ, ์ธ์ฆ ์˜ค๋ฅ˜์ผ ๋•Œ๋Š” ๊ณ„์ • ์˜ค๋ฅ˜ ํ™”๋ฉด์„, ๊ทธ ์™ธ์—๋Š” ์ผ๋ฐ˜ ์—๋Ÿฌ ํ™”๋ฉด์„ ๋ Œ๋”๋งํ•˜๋„๋ก ๋ถ„๊ธฐํ–ˆ์Šต๋‹ˆ๋‹ค.

2D ๊ทธ๋ฆฌ๋“œ ๊ฐ€์ƒํ™” (useGridVirtualizer) :

๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ํŽ˜์ด์ง€์—์„œ ์ˆ˜๋ฐฑ ๊ฐœ์˜ ์นด๋“œ๊ฐ€ ๊ทธ๋ฆฌ๋“œ ํ˜•ํƒœ๋กœ ๋ Œ๋”๋ง๋  ๋•Œ, DOM์— ๊ณผ๋ถ€ํ•˜๊ฐ€ ๊ฑธ๋ฆฌ์ง€ ์•Š๋„๋ก TanStack Virtual์˜ ํ–‰ยท์—ด ๋™์‹œ ๊ฐ€์ƒํ™”๋ฅผ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. useGridVirtualizer ํ›… ์•ˆ์— ํ–‰(row) virtualizer์™€ ์—ด(column) virtualizer๋ฅผ ํ•จ๊ป˜ ๊ตฌ์„ฑํ•˜๊ณ , ํ˜„์žฌ ๋ทฐํฌํŠธ ๋„ˆ๋น„์— ๋”ฐ๋ผ ์ปฌ๋Ÿผ ์ˆ˜๋ฅผ ๋™์ ์œผ๋กœ ๊ณ„์‚ฐํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๊ฐ€์ƒํ™”๋œ ํ–‰์„ ์ˆœํšŒํ•˜๋ฉฐ ์ด๋ฏธ์ง€ URL์„ ๋ฏธ๋ฆฌ ํ”„๋ฆฌ๋กœ๋“œํ•จ์œผ๋กœ์จ, ์Šคํฌ๋กค ์‹œ ์ด๋ฏธ์ง€๊ฐ€ ๋Šฆ๊ฒŒ ๋‚˜ํƒ€๋‚˜๋Š” ํ˜„์ƒ์„ ์ค„์˜€์Šต๋‹ˆ๋‹ค. ๋ฌดํ•œ ์Šคํฌ๋กค์€ ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ณด์ด๋Š” ์•„์ดํ…œ์˜ ์ธ๋ฑ์Šค์™€ ์ „์ฒด ์•„์ดํ…œ ์ˆ˜๋ฅผ ๋น„๊ตํ•ด threshold ์ด๋‚ด๋กœ ์ขํ˜€์กŒ์„ ๋•Œ fetchNextPage๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿง ์‹ ๊ฒฝ ์“ด ๋ถ€๋ถ„

์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…์„ ํ†ตํ•œ ์ดˆ๊ธฐ ๋กœ๋”ฉ ์ตœ์ ํ™” :

React.lazy์™€ Suspense๋ฅผ ํ™œ์šฉํ•ด ํŽ˜์ด์ง€ ๋‹จ์œ„์™€ ์ฃผ์š” ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„๋กœ ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…์„ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ดˆ๊ธฐ ๋ฒˆ๋“ค์— ํฌํ•จ๋  ํ•„์š”๊ฐ€ ์—†๋Š” ์ปดํฌ๋„ŒํŠธ๋Š” ์‚ฌ์šฉ ์‹œ์ ์— ๋™์ ์œผ๋กœ ๋กœ๋“œ๋˜๋„๋ก ํ•˜์—ฌ, ์ดˆ๊ธฐ ๋กœ๋”ฉ ์‹œ๊ฐ„์„ ์ค„์ด๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค. Skeleton ์ปดํฌ๋„ŒํŠธ๋ฅผ fallback์œผ๋กœ ์ œ๊ณตํ•ด ๋กœ๋”ฉ ์ค‘์—๋„ ๋ ˆ์ด์•„์›ƒ์ด ์•ˆ์ •์ ์œผ๋กœ ์œ ์ง€๋˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋„๋ฉ”์ธ ์ค‘์‹ฌ์˜ ํด๋” ๊ตฌ์กฐ :

atomic ํŒจํ„ด ๋Œ€์‹  ๋„๋ฉ”์ธ ์ค‘์‹ฌ์˜ ํด๋” ๊ตฌ์กฐ๋ฅผ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋Šฅ๊ณผ ๋„๋ฉ”์ธ ๋‹จ์œ„๋กœ ํŒŒ์ผ์„ ๋ฌถ์œผ๋ฉด ํŠน์ • ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์„ ๋•Œ ๊ณ„์ธต์  ๋ถ„๋ฅ˜๋ฅผ ๋จผ์ € ๋– ์˜ฌ๋ ค์•ผ ํ•˜๋Š” ์ธ์ง€ ๋ถ€ํ•˜๊ฐ€ ์ค„์–ด๋“ค๊ณ , ๊ด€๋ จ ํŒŒ์ผ๋ผ๋ฆฌ์˜ ์‘์ง‘๋„๊ฐ€ ๋†’์•„์ง„๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋ฒ”์šฉ UI ์š”์†Œ๋Š” ui ํด๋”์—, ๋„๋ฉ”์ธ๋ณ„ ์ปดํฌ๋„ŒํŠธ๋Š” components ํ•˜์œ„์— ๋„๋ฉ”์ธ ์ด๋ฆ„์œผ๋กœ ๊ตฌ๋ถ„ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ ˆ๋Œ€ ๊ฒฝ๋กœ ์ž„ํฌํŠธ :

@/* ๋กœ src ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ฐธ์กฐํ•˜๋Š” ์ ˆ๋Œ€ ๊ฒฝ๋กœ ์ž„ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ํŒŒ์ผ์ด ์ด๋™ํ•˜๊ฑฐ๋‚˜ ํด๋” ๊ตฌ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์ƒ๋Œ€ ๊ฒฝ๋กœ(../../../)๋ฅผ ์ผ์ผ์ด ์ˆ˜์ •ํ•˜์ง€ ์•Š์•„๋„ ๋˜์–ด ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ์„ ๋‚ฎ์ถœ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ปค๋ฐ‹ ์ปจ๋ฒค์…˜ ์ž๋™ํ™” :

Husky์™€ commitlint๋ฅผ ์—ฐ๋™ํ•ด ์ปค๋ฐ‹ ๋ฉ”์‹œ์ง€๊ฐ€ Conventional Commits ๊ทœ๊ฒฉ์„ ๋”ฐ๋ฅด์ง€ ์•Š์œผ๋ฉด ์ปค๋ฐ‹์ด ๊ฑฐ๋ถ€๋˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ผ๊ด€๋œ ์ปค๋ฐ‹ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์œ ์ง€ํ•จ์œผ๋กœ์จ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์˜ ๋งฅ๋ฝ์„ ์‰ฝ๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜• ๋””์ž์ธ :

clamp() ยท max() ๊ธฐ๋ฐ˜์˜ ์œ ๋™์  CSS ์ปค์Šคํ…€ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ •์˜ํ•ด ๋ทฐํฌํŠธ์— ๋”ฐ๋ผ ํ…์ŠคํŠธ ํฌ๊ธฐยท์ด๋ฏธ์ง€ ํฌ๊ธฐยท์—ฌ๋ฐฑยท๋ณด๋” ๋ฐ˜๊ฒฝ์ด ์ž๋™์œผ๋กœ ์กฐ์ •๋˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ณ ์ • ๋ธŒ๋ ˆ์ดํฌํฌ์ธํŠธ ๋Œ€์‹  fluid ๊ฐ’์„ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ์ค‘๊ฐ„ ๋ทฐํฌํŠธ์—์„œ๋„ ๋ ˆ์ด์•„์›ƒ์ด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ๋ฅด๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

/* fluid typography */
--text-fluid-xl: clamp(var(--text-base), 5vw, var(--text-4xl));
--text-fluid-xs: clamp(0.3rem, 3vw, var(--text-xs));

/* fluid vinyl album cover */
--vinyl-album-fluid: clamp(9rem, 26rem, 28vw);

๊ฐ€์ƒํ™” ๊ทธ๋ฆฌ๋“œ(useGridVirtualizer)๋Š” useThrottledWindowSize๋กœ ํ˜„์žฌ ๋ทฐํฌํŠธ ๋„ˆ๋น„๋ฅผ ๊ฐ์ง€ํ•ด ์ปฌ๋Ÿผ ์ˆ˜๋ฅผ ๋™์ ์œผ๋กœ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. Tailwind์˜ sm: ยท md: ยท lg: ๋ฐ˜์‘ํ˜• prefix๋ฅผ ๊ทธ๋ฆฌ๋“œ์™€ ๋ ˆ์ด์•„์›ƒ ์ปดํฌ๋„ŒํŠธ ์ „๋ฐ˜์— ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

์›น ์ ‘๊ทผ์„ฑ :

  • ํ‚ค๋ณด๋“œ ํƒ์ƒ‰ : ๋ชจ๋“  ๋ฒ„ํŠผ๊ณผ ํƒญ์— focus-visible ํฌ์ปค์Šค ์ธ๋””์ผ€์ดํ„ฐ๋ฅผ ์ ์šฉํ•ด ๋งˆ์šฐ์Šค ์—†์ด๋„ ํƒ์ƒ‰ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ์ง€์› : ์•„์ด์ฝ˜ ์ „์šฉ ๋ฒ„ํŠผ์—๋Š” aria-label๋กœ ๋ชฉ์ ์„ ์„ค๋ช…ํ•˜๊ณ , ์žฅ์‹์šฉ SVG์—๋Š” aria-hidden + focusable="false"๋ฅผ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋ฒ„ํŠผ์—๋Š” ํ˜„์žฌ ํŽ˜์ด์ง€ ์—ฌ๋ถ€๋ฅผ aria-current="page"๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
  • ๋™์  ์ฝ˜ํ…์ธ  ์•Œ๋ฆผ : ๊ฒ€์ƒ‰ ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ aria-live="polite"๋กœ ์„ ์–ธํ•ด ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ์Šคํฌ๋ฆฐ ๋ฆฌ๋”์— ์•Œ๋ฆฝ๋‹ˆ๋‹ค.
  • ํƒญ ํŒจํ„ด : ๊ฒ€์ƒ‰ ์นดํ…Œ๊ณ ๋ฆฌ ํƒญ์— role="tablist" ยท role="tab" ยท aria-selected๋ฅผ ์ ์šฉํ•ด WAI-ARIA ํƒญ ํŒจํ„ด์„ ์ค€์ˆ˜ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ชจ์…˜ ๊ฐ์†Œ ์„ค์ • ์กด์ค‘ : @media (prefers-reduced-motion: reduce) ๋กœ ๋ฐ”์ด๋‹ ํšŒ์ „ยท์บ๋Ÿฌ์…€ยท๋„ค์˜จ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋น„ํ™œ์„ฑํ™”ํ•ด ์ „์ • ์žฅ์• ๊ฐ€ ์žˆ๋Š” ์‚ฌ์šฉ์ž๋ฅผ ๋ฐฐ๋ คํ–ˆ์Šต๋‹ˆ๋‹ค.

About

๐ŸŽถ search, listen to music via spotify api

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages