๐Ÿš€ LinkAV Beta ๋ฆด๋ฆฌ์ฆˆ ๐Ÿš€

์„œ๋ก 

๐Ÿ”— linkav.net

์ด์ „์— Springboot์™€ Next.js ๊ธ€์—์„œ ํ’€์Šคํƒ์„ ๊ฐœ๋ฐœํ•œ ๊ฒฝํ—˜์„ ์ ์—ˆ์—ˆ๋Š”๋ฐ
ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•œ ์†Œ๊ฐœ์ด๋‹ค.

์ตœ์†Œํ•œ์˜ ๊ธฐ๋Šฅ๋งŒ์„ ๊ฐ–์ถ˜ ์ƒํƒœ๋ผ ์ดˆ๋ผํ•˜๊ณ  ๋ณด์ž˜๊ฒƒ์—†์„ ์ˆ˜ ์žˆ์œผ๋‚˜
๊ทธ๋ž˜๋„ ์ด๋Ÿฐ ๋•Œ ๊ธ€์„ ๋‚จ๊ฒจ๋‘๋ฉด ๋‚˜์ค‘์— ๋‹ค์‹œ ๋ดค์„ ๋•Œ ์žฌ๋ฐŒ์–ด์„œ
์ž๊ธฐ๋งŒ์กฑ๋ฐ˜ + ์†Œ๊ฐœ์šฉ๋ฐ˜ ๊ฐ์„ฑ์œผ๋กœ๋‹ค๊ฐ€ ๊ธ€์„ ์ ์–ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

LinkAV๋Š” ๋งํฌ๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์„œ๋น„์Šค์ด๋‹ค.

์•ฝ์ž์—์„œ ๋“œ๋Ÿฌ๋‚˜๋“ฏ์ด (Vault๋Š” hasicorp์˜ vault์—์„œ ๋”ฐ์™”๋‹ค)
๋ธŒ๋ผ์šฐ์ €์—์„œ ํžˆ์Šคํ† ๋ฆฌ๋‚˜ ๋ถ๋งˆํฌ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์‹ถ์ง€์•Š์€ url๋“ค์„

์œ„์™€ ๊ฐ™์€ ๋ฐฉ์‹๋“ค๋กœ ํŽธ๋ฆฌํ•˜๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

๋” ์„ค๋ช…์ด ํ•„์š”์—†์„ ๊ฒƒ ๊ฐ™๋‹ค.
๋ง๋ณด๋‹ค๋Š” ๋ˆˆ์œผ๋กœ ๋ณด๋Š” ๊ฒŒ ์‰ฝ๊ณ  ๋น ๋ฅด๋‹ค.


์ง€๋ฉด ์†Œ๊ฐœ

Landing Page

landing-page

๋žœ๋”ฉํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค์–ด ๋ดค๋‹ค.
์ฐธ๊ณ ํ•œ ๋””์ž์ธ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

ํ—ค๋”์˜์—ญ์˜ ๊ฒฝ์šฐ ์ผ๋‹จ Dashboard ๋„๋ฉ”์ธ์—์„œ๋Š” ๊ณต์œ ๊ฐ€ ๋œ๋‹ค.
stickyํ•˜๊ฒŒ ๋งŒ๋“ค์ง€๋Š” ๊ณ ๋ ค์ค‘์ด๋‹ค.

ํ—ค๋”์˜์—ญ ์˜ค๋ฅธ์ชฝ์— ์œ„์น˜ํ•œ AccountBox ์˜์—ญ์€
Login๊ณผ Signup์„ ๋“œ๋Ÿฌ๋‚ด์„œ ๋ถ„๋ฆฌํ–ˆ๋‹ค.

๊ฒ€์ƒ‰์„ ํ•ด๋ณธ ๊ฒฐ๊ณผ DropdownMenu๋กœ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜
๊ทธ๋ƒฅ ๋ฒ„ํŠผ์„ ํ•œ๊ฐœ๋งŒ ๋‘๊ณ  ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ๋œ ๊ณณ์—์„œ Tabs๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹๋„ ์žˆ์—ˆ๋‹ค.
์ถ”ํ›„์— ๊ณ ๋ฏผํ•ด๋ณผ ์ง€์ ์ธ ๊ฒƒ ๊ฐ™๋‹ค.

๋ณด๋‹ค์‹œํ”ผ ๊ธฐ๋Šฅ์ด Dashboard ํ•˜๋‚˜๋ฟ์ด๋ผ ํ—ค๋”๊ฐ€ ํœ‘ํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
ํ•˜๋‚˜์”ฉ ์ฑ„์›Œ๋‚˜๊ฐˆ ์ƒ๊ฐ์ด๋‹ค.

Sign In & Sign Up

signin-card
signup-card

Landing Page์˜ AccountBox์—์„œ Sign In๊ณผ Sign Up์„ ๋ˆ„๋ฅด๋ฉด
์œ„์™€ ๊ฐ™์€ ์นด๋“œ๋ทฐ๊ฐ€ ๋‚˜ํƒ€๋‚œ๋‹ค. main์˜์—ญ๋งŒ ๊ต์ฒด๋˜๋Š” ๊ฒƒ์ด๋‹ค.

์—ฌ๋Ÿฌ ์—๋Ÿฌ ํ•ธ๋“ค๋ง์— ๋Œ€ํ•œ Toast์™€
zod validation error๋กœ ์ƒ์„ฑ๋˜๋Š” FormMessage ๋“ฑ
์„ธ๋ถ€์ ์ธ ์‚ฌํ•ญ๋“ค์€ ์ƒ๋žตํ–ˆ๋‹ค.

Sing In๊ณผ Sing Up์˜ ์ฐจ์ด๊ฐ€ ์ž˜ ๊ตฌ๋ณ„๋˜์ง€ ์•Š์•„์„œ
์ด ์ง€์ ๋„ ๊ฐœ์„ ํ•ด์•ผํ•  ์‚ฌํ•ญ์ธ๋“ฏ ํ•˜๋‹ค.

Dashboard

dashboard-page

์ด๋ ‡๊ฒŒ ์„ค๊ณ„ํ•œ ์ฃผ์š” ๋ชฉ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค

Video Card

videocard-dropdown

Table๊ณผ Grid Card ๋ทฐ ์‚ฌ์ด์—์„œ ๊ฐˆ๋“ฑ์ด ์ข€ ์žˆ์—ˆ์œผ๋‚˜
ํด๋ฆญํ•˜๊ธฐ ์‰ฝ๊ณ  ์ง๊ด€์ ์œผ๋กœ ๋ณด์—ฌ์•ผ ํ•œ๋‹ค๋Š” ์ƒ๊ฐ ๋•Œ๋ฌธ์—
ํ›„์ž๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค.

์œ„์˜ ์‚ฌ์ง„์€ ์นด๋“œ ์•ˆ ์˜ค๋ฅธ์ชฝ ์ƒ๋‹จ์˜
DotsVertical ์•„์ด์ฝ˜์„ ๋ˆŒ๋ €์„ ๋•Œ ๋‚˜ํƒ€๋‚˜๋Š” ๋ทฐ์ด๋‹ค.

์ง๊ด€์ ์œผ๋กœ ์ˆ˜์ •ํ•˜๊ณ  ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋„๋ก ์นด๋“œ ์•ˆ์ชฝ์— ๋ฐฐ์น˜ํ–ˆ๋‹ค.

Video Update & Delete Dialog

videocard-update-dialog
videocard-delete-dialog

์ง๊ด€์ ์œผ๋กœ Update์™€ Delete๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

Update์˜ ๊ฒฝ์šฐ ์นด๋“œ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” state๋“ค์„
input์˜ defaultValue๋กœ ๋„ฃ์–ด์ฃผ๋Š” ๋™์ž‘์ด ๊ตฌํ˜„๋˜์–ด ์žˆ๋‹ค.

add-link-dialog

๋Œ€์‹œ๋ณด๋“œ ์™ผ์ชฝ ์ƒ๋‹จ์˜ Add Link ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด
์œ„์™€ ๊ฐ™์€ Dialog๋ฅผ ๋งŒ๋“ค์–ด ์ค€๋‹ค. Update Dialog์™€ ๊ฑฐ์˜ ์œ ์‚ฌํ•˜๋‹ค.

Category

edit-category-dialog-create
edit-category-dialog-update
edit-category-dialog-delete

๋Œ€์‹œ๋ณด๋“œ ์™ผ์ชฝ ์ƒ๋‹จ์˜ Edit Category ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด
Category๋ฅผ ๋งŒ๋“ค๊ณ , ์ˆ˜์ •ํ•˜๊ณ , ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋Š” Dialog๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.
๋‚ด๋ถ€๋Š” Tabs๋กœ ๊ตฌํ˜„ํ•ด๋ดค๋‹ค.

Avatar

avatar-dropdown

Login ์ดํ›„ session์ด ์กด์žฌํ•˜๊ฒŒ ๋˜๋ฉด ์œ„์™€ ๊ฐ™์€ Avatar๊ฐ€ ๋‚˜ํƒ€๋‚œ๋‹ค.
ํด๋ฆญํ•˜๋ฉด DropdownMenu๊ฐ€ ๋‚˜ํƒ€๋‚˜๋ฉฐ ๋‹ค์–‘ํ•œ ์œ ํ‹ธ์„ ์ œ๊ณตํ•  ์ƒ๊ฐ์ด๋‹ค.
ํ˜„์žฌ๋Š” Log out๋งŒ ๊ตฌํ˜„์ด ๋˜์–ด์žˆ๋‹ค.

Filter & Pagination

์•„์ง ๊ตฌํ˜„์ด ๋˜์ง€ ์•Š์•˜๋‹ค. ์ด์œ ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ๋“ค์ด ์žˆ๋‹ค.

1. api ๋””์ž์ธ์˜ ๊ฒฐํ•จ


bad-rest-api-design
bad-rest-api-design

์ผ๋‹จ ๋ณด๋‹ค์‹œํ”ผ api ๋””์ž์ธ์ด ์ข‹์ง€ ์•Š๋‹ค.

์œ„์™€ ๊ฐ™์€ ๋ฌธ์ œ๋“ค์ด ์žˆ๋Š”๋ฐ ์ด๋“ค์„ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ์ „์—
๋จผ์ € ๋ฆฌํŒฉํ† ๋ง ํ•˜๋ฉด์„œ ์žก์•„๋‚ด๊ณ  ์‹ถ์—ˆ๋‹ค.

2. ์„ ํ–‰๋˜์–ด์•ผ ํ•˜๋Š” ๋ฆฌํŒฉํ† ๋ง๋“ค

๋น„๋‹จ Controller ๋ฌธ์ œ๋งŒ์€ ๋‹น์—ฐํžˆ ์•„๋‹ˆ๋‹ค.

์ด๋ฒˆ์— ๋ฆฌํŒฉํ† ๋ง์„ ํ•˜๋ฉฐ Mapstruct์™€ QueryDSL์„ ๋„์ž…ํ–ˆ๊ณ 
์ด์— ๋งž์ถฐ์„œ Service, Repository ๋˜ํ•œ ์ˆ˜์ •์„ ํ•ด์•ผํ•œ๋‹ค.

์ด๋Ÿฐ ๊ฒƒ๋“ค์ด ๋จผ์ € ์„ ํ–‰๋˜์–ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.


๋‚˜๋จธ์ง€ ํ›„๊ธฐ

FE์— ์ข€๋” ๊น”๋”ํ•œ ๊ตฌ์กฐ๋ฅผ ..

์ด๋ฒˆ ๋ฆด๋ฆฌ์ฆˆ๋ฅผ ํ•˜๋ฉฐ ์กฐ๊ธˆ ๋” cleanํ•œ ์•„ํ‚คํ…์ณ๋ฅผ
FE์—์„œ ์œ ์ง€ํ•˜๋„๋ก ๋ฆฌํŒฉํ† ๋ง ํ–ˆ๋‹ค.

BE์— Category๋ผ๋Š” ์ƒˆ๋กœ์šด Entity๊ฐ€ ์ถ”๊ฐ€๋˜๋ฉด์„œ
FE์ชฝ์—์„œ ๋‚ ๋ฆฌ๋Š” service, dto, zod์˜ schema ๋“ค๋„ ๊ฐ™์ด ์ˆ˜์ •ํ•ด์•ผ ํ–ˆ๊ณ 
๊ฒธ์‚ฌ๊ฒธ์‚ฌ ๋ฆฌํŒฉํ† ๋ง์„ ์ง„ํ–‰ํ–ˆ๋‹ค.

eza๋กœ Tree๋ฅผ ๊ธ์–ด์™€ ๋ดค๋‹ค.

โฏ eza -T -L 2 -I "node_modules|docs|scripts|README.md|*.js|*.json|*.yaml|*.mjs|*.ts|*.tsx|*.css"
.
โ”œโ”€โ”€ app
โ”‚  โ”œโ”€โ”€ api
โ”‚  โ”œโ”€โ”€ dashboard
โ”‚  โ”œโ”€โ”€ signin
โ”‚  โ””โ”€โ”€ signup
โ”œโ”€โ”€ components
โ”‚  โ”œโ”€โ”€ auth
โ”‚  โ”œโ”€โ”€ common
โ”‚  โ”œโ”€โ”€ dashboard
โ”‚  โ””โ”€โ”€ ui
โ”œโ”€โ”€ dto
โ”œโ”€โ”€ hook
โ”œโ”€โ”€ lib
โ”œโ”€โ”€ public
โ”‚  โ””โ”€โ”€ images
โ”œโ”€โ”€ service
โ”œโ”€โ”€ state
โ”œโ”€โ”€ styles
โ”œโ”€โ”€ test
โ”‚  โ”œโ”€โ”€ e2e
โ”‚  โ”œโ”€โ”€ integration
โ”‚  โ””โ”€โ”€ unit
โ””โ”€โ”€ types

ํ…Œ์ŠคํŠธ์— ๋Œ€ํ•˜์—ฌ

BE ์ž‘์—…์„ ํ•ด๋†“๊ณ  FE์—์„œ ์ž‘์—…์„ ํ•˜๋‹ค๋ณด๋‹ˆ
์–ด์ฉ” ์ˆ˜ ์—†์ด ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ api ์„ค๊ณ„๋“ค๊ณผ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋“ค์ด ์ถ”๊ฐ€๋๋‹ค.

๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ด๋Ÿฌ๋Š” ๊ณผ์ •์—์„œ ๋†“์ณค๋˜ ์‹ค์ˆ˜ ์ง€์ ๋“ค์ด ๋ฐœ๊ฒฌ๋˜๊ณ 
์ด์— ์ˆ˜์ •์„ ํ•˜๋‹ค๋ณด๋‹ˆ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋“ค์ด ๋งŽ์ด ๊นจ์ง€๊ฒŒ ๋˜์—ˆ๋‹ค.

๋ชจ๋‘ ํ†ต๊ณผํ•˜๋˜ 45๊ฐœ์˜ ํ…Œ์ŠคํŠธ์—์„œ 9๊ฐœ๊ฐ€ ์‹คํŒจํ•˜๋Š” ์ƒํƒœ
๋ชจ๋‘ ํ†ต๊ณผํ•˜๋˜ 45๊ฐœ์˜ ํ…Œ์ŠคํŠธ์—์„œ 9๊ฐœ๊ฐ€ ์‹คํŒจํ•˜๋Š” ์ƒํƒœ

์ด ๊ธ€ ์„œ๋ก ์—์„œ ์–ธ๊ธ‰ํ•œ
ํ…Œ์ŠคํŠธ ํ”Œ๋กœ์šฐ๋Š” ๋ถ€์ฑ„๋กœ ๋‚จ๊ฒŒ ๋˜์—ˆ๋‹ค.
์™œ๋ƒ๋ฉด ํ…Œ์ŠคํŠธ ์ž‘์„ฑ๋ง๊ณ ๋„ ํ•ด๋‚ด์•ผ ํ•˜๋Š”๊ฒŒ ์‚ฐ๋”๋ฏธ๋กœ ์Œ“์—ฌ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์•„๋งˆ ๋‚œ ๋‹น๋ถ„๊ฐ„ ci์—์„œ

./gradlew clean build -x test

๋ฅผ ํ•˜์ง€์•Š์„๊นŒ..

๋ฌผ๋ก  domain -> repository -> service -> controller ํ๋ฆ„์œผ๋กœ
unit / integration ํ…Œ์ŠคํŒ… ํ•˜๋Š” ๋ฐฉ์‹๊ณผ ํ๋ฆ„์„ ํ•™์Šตํ•œ ๊ฒƒ์€
์ข‹์€ ์ž์‚ฐ์œผ๋กœ ๋‚จ์„ ๊ฒƒ์ž„์€ ๋ถ„๋ช…ํ•˜๋‹ค.

mock์„ ํ•ด๋‚˜๊ฐ€๋ฉฐ stub ๊ฐ์ฒด๋ฅผ ์…‹์—…ํ•ด์„œ
ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•˜๋Š” ์ผ€์ด์Šค๋ฅผ ์ž‘์„ฑํ•ด
์„ฑ๊ณตํ•˜๊ฒŒ๋” ์ฝ”๋“œ๋ฅผ ์งœ๋‚ด๊ฐ€๋Š” ๊ณผ์ •์€ ํฅ๋ฏธ๋กœ์› ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‹ค์ œ๋กœ ๋„์›€์ด ๋˜์—ˆ๋‹ค.

๋™์‹œ์— ์ดˆ๋ฐ˜์— ์˜๋ฏธ์—†์ด ์งœ๋ฒ„๋ฆฐ ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋“ค ๋˜ํ•œ ๋งŽ์•˜๋‹ค.
๊ทธ๋ž˜์„œ ์‹œ๊ฐ„์„ ์–ด๋Š์ •๋„ ๋‚ ๋ ค๋จน์€ ๊ฒƒ ๋˜ํ•œ ์‚ฌ์‹ค์ด๋‹ค.

Storybook์„ ๋„์ž…ํ•˜์ž

์ถ”๊ฐ€๋กœ ๊ธ€์„ ์“ฐ๋ฉด์„œ ๋Š๋‚€๊ฑด๋ฐ storybook์„ ๋„์ž…ํ•ด์•ผ๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.

ํ˜„์žฌ ๋„๋ฉ”์ธ์ด 1๊ฐœ๊ณ  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งŽ์ง€ ์•Š์•„์„œ ๋‹คํ–‰์ด์ง€
์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ›จ์”ฌ ๋งŽ์•„์ง€๊ณ  ํ”Œ๋กœ์šฐ๊ฐ€ ๋‹ค์–‘ํ•ด์ง€๋ฉด
๋‹ต์ด ์—†์„ ๊ฒƒ ๊ฐ™๊ณ  ์•ž์œผ๋กœ ํฌ์ŠคํŒ…์ด ๋ถˆ๊ฐ€๋Šฅํ•  ๊ฒƒ ๊ฐ™๋‹ค.
(Figma๋กœ ๊ด€๋ฆฌ์ค‘์ด์ง€๋„ ์•Š๊ธฐ์— ๋”์šฑ ๊ทธ๋ ‡๋‹ค.)

๋งˆ๋ฌด๋ฆฌ

์‚ฌ์‹ค ์ด ์„œ๋น„์Šค์˜ ์ง„์งœ ํ•ต์‹ฌ์€ chrome-extension์— ์žˆ๋‹ค.
chrome-extension๋งŒ์ด ํ•ด์ค„ ์ˆ˜ ์žˆ๋Š” ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ๋“ค์ด ๋„ˆ๋ฌด ๋งŽ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์‹œ์ž‘ํ•  ๋•Œ ์›น์„ ๋จผ์ € ํ”„๋กœํ† ํƒ€์ดํ•‘ ํ•ด์„œ mvp๋ฅผ ๋‹ฌ์„ฑํ•˜๋Š” ๊ฑธ ๋ชฉํ‘œ๋กœ ์žก์•˜์—ˆ๊ณ ,
์ด์ œ ๊ทธ ๋ชฉํ‘œ๊นŒ์ง€ task๋Š” 3๊ฐœ๊ฐ€ ๋‚จ์•˜๋‹ค.

๋‹ฌ์„ฑ ํ›„์— ์ต์Šคํ…์…˜ ๊ฐœ๋ฐœ์„ ๋‹ค์‹œ ๊ฐ€์†ํ™” ํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.