Skip to content
/ vest Public

vest๐Ÿ€ (user management service)

Notifications You must be signed in to change notification settings

soo5sin/vest

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

vest๐Ÿ€

์œ ์ € ๊ด€๋ฆฌ ์„œ๋น„์Šค

: ์›ํ‹ฐ๋“œ ํ”„๋ฆฌ์˜จ๋ณด๋”ฉ ์ฝ”์Šค์—์„œ ํŒ€์œผ๋กœ ์ง„ํ–‰ํ–ˆ๋˜ ๊ธฐ์—…๊ณผ์ œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ


๋ชฉ์ฐจ


๋ฐฐํฌ ๋งํฌ

๋กœ๊ทธ์ธ

์•„์ด๋””(email): [email protected]

๋น„๋ฐ€๋ฒˆํ˜ธ(password): test


ํ”„๋กœ์ ํŠธ ์‹คํ–‰


ํ”„๋กœ์ ํŠธ ํด๋ก 

$ git clone https://github.com/seriparkdev/vest.git

์„œ๋ฒ„ ์‹คํ–‰

$ npm install
$ npm run gen
$ npm start

ํด๋ผ์ด์–ธํŠธ ์‹คํ–‰

$ npm install
$ npm start

๊ตฌํ˜„ ์š”๊ตฌ ์‚ฌํ•ญ

์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐœ๋ฐœํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฐธ๊ณ ์šฉ์ž…๋‹ˆ๋‹ค.

โœ”๏ธ ์‚ฌ์šฉ์ž ๋ชฉ๋ก

  • ํ‘œ๊ธฐ๋˜์–ด์•ผ ํ•˜๋Š” ์ •๋ณด
    • ๊ณ ๊ฐ๋ช…(name) : ๊ฐ€์šด๋ฐ ๊ธ€์ž ๋งˆ์Šคํ‚น ํ•„์š”, ๋‘๊ธ€์ž์ผ ๊ฒฝ์šฐ ์„ฑ์„ ์ œ์™ธํ•œ ์ด๋ฆ„ ๋งˆ์Šคํ‚น ์ฒ˜๋ฆฌ, 4๊ธ€์ž ์ด์ƒ์ผ ๊ฒฝ์šฐ ๋งˆ์Šคํ‚น ์ฒ˜๋ฆฌ ํ›„ ์•ž๋’ค ํ•œ๊ธ€์ž๋งŒ ํ‘œ๊ธฐ
      • ๊ณ ๊ฐ๋ช…์„ ๋ˆ„๋ฅผ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž ์ƒ์„ธํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
    • ๋ณด์œ ์ค‘์ธ ๊ณ„์ขŒ์ˆ˜(account_count) : (ํ•ด๋‹น API ํ˜ธ์ถœ ํ›„ ๋ฐ์ดํ„ฐ๋ฅผ ์ •์ œํ•˜์—ฌ ํ‘œ๊ธฐ)
    • ์ด๋ฉ”์ผ ์ฃผ์†Œ (email)
    • ์ฃผ๋ฏผ๋“ฑ๋ก์ƒ ์„ฑ๋ณ„์ฝ”๋“œ (gender_origin)
    • ์ƒ๋…„์›”์ผ (yyyy-mm-dd) (birth_date)
    • ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ (๊ฐ€์šด๋ฐ 4์ž๋ฆฌ *** ๋กœ ๋งˆ์Šคํ‚น ํ•„์š”) (phone_number)
    • ์ตœ๊ทผ๋กœ๊ทธ์ธ (last_login)
    • ํ˜œํƒ ์ˆ˜์‹  ๋™์˜ ์—ฌ๋ถ€ (ํ•ด๋‹น API ํ˜ธ์ถœ ํ›„ ๋ฐ์ดํ„ฐ๋ฅผ ์ •์ œํ•˜์—ฌ ํ‘œ๊ธฐ) (allow_marketing_push)
    • ํ™œ์„ฑํ™” ์—ฌ๋ถ€ (ํ•ด๋‹น API ํ˜ธ์ถœ ํ›„ ๋ฐ์ดํ„ฐ๋ฅผ ์ •์ œํ•˜์—ฌ ํ‘œ๊ธฐ) (is_active)
    • ๊ฐ€์ž…์ผ (created_at)
  • ๊ตฌํ˜„๋˜์–ด์•ผ ํ•˜๋Š” ๊ธฐ๋Šฅ
    • ํŽ˜์ด์ง€๋„ค์ด์…˜์ด ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ์ž„์˜๋กœ ์‹ ๊ทœ ์‚ฌ์šฉ์ž๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ์ž˜๋ชป ์ƒ์„ฑํ•œ ์‚ฌ์šฉ์ž๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ๊ฐœ๋ช…์„ ํ•œ ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉ์ž๋ช…์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

โœ”๏ธ ๊ณ„์ขŒ ๋ชฉ๋ก

  • ํ‘œ๊ธฐ๋˜์–ด์•ผ ํ•˜๋Š” ์ •๋ณด
    • ๊ณ ๊ฐ๋ช…(user_name) : ๊ณ ๊ฐID ๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ์‹ค์ œ ์ด๋ฆ„์œผ๋กœ ๋ณด์—ฌ์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
      • ๊ณ ๊ฐ๋ช…์„ ๋ˆ„๋ฅผ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž ์ƒ์„ธํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
    • ๋ธŒ๋กœ์ปค๋ช…(broker_name) : ์˜ˆ์‹œ) OO์ฆ๊ถŒ, brokers.json ๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ์‹ค์ œ ์ด๋ฆ„์œผ๋กœ ๋ณด์—ฌ์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ๊ณ„์ขŒ๋ฒˆํ˜ธ(number) : ์•ž ๋’ค ๊ฐ๊ฐ ๋‘๊ธ€์ž๋ฅผ ์ œ์™ธํ•˜๊ณ  ๋‚˜๋จธ์ง€๋Š” ๊ธ€์ž์ˆ˜์— ๋งž๊ฒŒ * ๊ธ€์ž๋กœ ๋งˆ์Šคํ‚น ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
    • ๊ณ„์ขŒ์ƒํƒœ(status) : ์˜ˆ์‹œ) ์šด์šฉ์ค‘, accountStatus.json ๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ์‹ค์ œ ์ด๋ฆ„์œผ๋กœ ๋ณด์—ฌ์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ๊ณ„์ขŒ๋ช…(name) : ๊ณ„์ขŒ๋ช…์ž…๋‹ˆ๋‹ค.
    • ํ‰๊ฐ€๊ธˆ์•ก(assets) : ์˜ˆ์‹œ) 123,123,123
    • ์ž…๊ธˆ๊ธˆ์•ก(payments) : ์˜ˆ์‹œ) 123,123,123
    • ๊ณ„์ขŒํ™œ์„ฑํ™”์—ฌ๋ถ€(is_active) : ๊ณ„์ขŒ ํ™œ์„ฑํ™” ์—ฌ๋ถ€
    • ๊ณ„์ขŒ๊ฐœ์„ค์ผ(created_at) :
  • ๊ตฌํ˜„๋˜์–ด์•ผ ํ•˜๋Š” ๊ธฐ๋Šฅ
    • ๋ฆฌ์ŠคํŠธ ํŽ˜์ด์ง€์—์„œ๋Š” ๊ฒ€์ƒ‰์ด ๊ฐ€๋Šฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ํŽ˜์ด์ง€๋„ค์ด์…˜์ด ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

โœ”๏ธ ์กฐ๊ฑด

  • Sider ๋ฉ”๋‰ด์—์„œ๋Š” ํ˜„์žฌ ๋ณด๊ณ  ์žˆ๋Š” ํ™”๋ฉด์— ํ•ด๋‹นํ•˜๋Š” ๋ฉ”๋‰ด๊ฐ€ ํ•˜์ด๋ผ์ดํŠธ ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•ด๋„ ๋กœ๊ทธ์ธ ์ƒํƒœ๊ฐ€ ์œ ์ง€๋˜์–ด์•ผ ํ•˜๋ฉฐ, ์ƒํƒœ์— ๋”ฐ๋ผ ๊ธฐ์กด์— ๋จธ๋ฌด๋ฅด๋˜ ํ™”๋ฉด์ด ๊ทธ๋Œ€๋กœ ๋ณด์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ณ„์ขŒ ๋ฆฌ์ŠคํŠธ์—์„œ ๊ณ„์ขŒ๋ฒˆํ˜ธ๋ฅผ ๋ˆ„๋ฅด๋ฉด ๊ณ„์ขŒ์ƒ์„ธ ํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ณ„์ขŒ ๋ฆฌ์ŠคํŠธ์—์„œ ์‚ฌ์šฉ์ž ์ด๋ฆ„์„ ๋ˆ„๋ฅด๋ฉด ์‚ฌ์šฉ์ž ์ƒ์„ธ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž ์ƒ์„ธ์—์„œ ์‚ฌ์šฉ์ž์˜ ๊ณ„์ขŒ๋ชฉ๋ก์ด ๋ณด์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์ˆ˜์ต๋ฅ ์ด ํ”Œ๋Ÿฌ์Šค์ธ ๊ณ„์ขŒ์˜ ์ด์ž์‚ฐ ๊ธˆ์•ก์€ ๋นจ๊ฐ„์ƒ‰, ์›๊ธˆ๊ณผ ๋™์ผํ•œ ๊ฒฝ์šฐ ๊ฒ€์ •์ƒ‰, ๋งˆ์ด๋„ˆ์Šค์ผ ๊ฒฝ์šฐ ํŒŒ๋ž€์ƒ‰์œผ๋กœ ๋ณด์—ฌ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ณ„์ขŒ ๋ชฉ๋ก์—์„œ broker_id ์— ํ•ด๋‹นํ•˜๋Š” ์‹ค์ œ ๋ธŒ๋กœ์ปค๋ช… (OOํˆฌ์ž์ฆ๊ถŒ) ์ด ๋ณด์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ์ˆ  ์Šคํƒ ๋ฐ ์‚ฌ์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ


TypeScript


๐Ÿ’ก Redux Toolkit ์‚ฌ์šฉ ์ด์œ 

Redux๋ฅผ ์ด ํ”„๋กœ์ ํŠธ๋ฅผ ํ†ตํ•ด์„œ ๋” ๊ณต๋ถ€ํ•˜๊ณ  ์‹ถ์—ˆ๊ณ  ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•„์š”ํ•ด์„œ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. Context API๊ฐ€ ๋˜ ํ•˜๋‚˜์˜ ๋Œ€์•ˆ์ด ๋  ์ˆ˜ ์žˆ์œผ๋‚˜, Redux๋Š” ๊ฐ’์˜ ๋ณ€ํ™”์— ๋Œ€ํ•œ ์ตœ์ ํ™”๊ฐ€ ๋˜์–ด์žˆ์–ด ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์„ ์ผ์œผํ‚ค์ง€ ์•Š๋Š” ์ ์„ ๊ณ ๋ คํ•˜์—ฌ Redux๋ฅผ ์ฑ„ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Redux๋Š” ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋ฏธ๋“ค์›จ์–ด๋‚˜, useSelector, useDispatch ๊ฐ™์ด ํŽธ๋ฆฌํ•˜๊ณ  ์ตœ์ ํ™”๊ฐ€ ์ž˜ ๋˜์–ด์žˆ๋Š” hook ํ•จ์ˆ˜ ๋“ฑ์„ ํฌํ•จํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ด ๊ธฐ๋Šฅ๋“ค์„ ์ด์šฉํ•ด์„œ ์ข€ ๋” ์ˆ˜์›”ํ•˜๊ฒŒ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์ตœ์ ํ™” ์‹œํ‚ค๊ธฐ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

Redux-toolkit์€ ๊ธฐ์กด์˜ thunk, saga์˜ ๊ธฐ๋Šฅ๋“ค์ด ํƒ‘์žฌ๋˜์–ด ์žˆ๊ณ , ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ๊ฐ€ ํ›จ์”ฌ ์งง์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด์˜ Redux๋Š” ์ด ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉํ•˜๊ธฐ์— ๋ฌด๊ฑฐ์šด ์ ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.


๐Ÿ’ก styled-components ์‚ฌ์šฉ ์ด์œ 

tailwind๋Š” ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์„ ๋งŽ์ด ํ•ด์น˜๊ธฐ๋„ ํ•˜๊ณ  ๊ด€์‹ฌ์‚ฌ์˜ ๋ถ„๋ฆฌ๊ฐ€ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. npm ํŒจํ‚ค์ง€ ๋‹ค์šด๋กœ๋“œ ์ˆ˜๋ฅผ ๋น„๊ตํ•ด๋ดค์„ ๋•Œ emotion๋ณด๋‹ค๋Š” styled-components๊ฐ€ ํ›จ์”ฌ ๋” ๋งŽ์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์–ด ์ด๋ฅผ ์ฑ„ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.


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

ํด๋” ์šฉ๋„
api axios ์ธ์Šคํ„ด์Šค, api ๊ด€๋ฆฌ ํด๋”
assets image, data ๋“ฑ ํ”„๋กœ์ ํŠธ์— ํ•„์š”ํ•œ ํŒŒ์ผ ๊ด€๋ฆฌ ํด๋”
components ๋ ˆ์ด์•„์›ƒ, ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ ๊ด€๋ฆฌ
constants ์ƒ์ˆ˜ ๊ด€๋ฆฌ ํด๋”
pages ๋„๋ฉ”์ธ ๋ณ„๋กœ ๋‚˜๋ˆˆ page ํด๋”
routes router์™€ ๊ด€๋ จ๋œ ํด๋”
store client ์ƒํƒœ ๊ด€๋ฆฌ์™€ ๊ด€๋ จ๋œ ํด๋”
styles ํ”„๋กœ์ ํŠธ์— ํ•„์š”ํ•œ global, theme CSS
types ๋ฐ˜๋ณต๋˜์–ด ์‚ฌ์šฉ๋˜๋Š” ํƒ€์ž…๋“ค์„ ๊ด€๋ฆฌ
utils ํ”„๋กœ์ ํŠธ์— ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋Š” utility ๊ด€๋ฆฌ

ํŒŒ์ผ ๋ถ€๊ฐ€ ์„ค๋ช…

utils

ํŒŒ์ผ ์šฉ๋„
account account์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๊ณตํ•ด์ฃผ๋Š” util
auth ์ธ์ฆ/์ธ๊ฐ€์™€ ๊ด€๋ จ๋œ util
extraReducer Redux extraReducers์˜ builder callback์„ ์œ„ํ•œ boilerplate๋ฅผ ์ž๋™ํ™”ํ•œ util
user user์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๊ณตํ•ด์ฃผ๋Š” util

ํŒŒ์ผ ๊ตฌ์กฐ tree
โ”œโ”€api
โ”œโ”€assets
โ”‚  โ”œโ”€data
โ”‚  โ””โ”€image
โ”œโ”€components
โ”‚  โ”œโ”€layout
โ”‚  โ””โ”€shared
โ”‚      โ”œโ”€error
โ”‚      โ””โ”€table
โ”œโ”€constants
โ”œโ”€hooks
โ”œโ”€pages
โ”‚  โ”œโ”€Account
โ”‚  โ”‚  โ”œโ”€components
โ”‚  โ”‚  โ””โ”€hooks
โ”‚  โ”œโ”€AccountDetail
โ”‚  โ”‚  โ”œโ”€components
โ”‚  โ”‚  โ””โ”€hooks
โ”‚  โ”œโ”€Login
โ”‚  โ”‚  โ”œโ”€components
โ”‚  โ”‚  โ””โ”€hooks
โ”‚  โ”œโ”€Main
โ”‚  โ”œโ”€Search
โ”‚  โ”‚  โ”œโ”€component
โ”‚  โ”‚  โ””โ”€hooks
โ”‚  โ”œโ”€User
โ”‚  โ”‚  โ”œโ”€components
โ”‚  โ”‚  โ””โ”€hooks
โ”‚  โ””โ”€UserDetail
โ”‚      โ”œโ”€components
โ”‚      โ””โ”€hooks
โ”œโ”€routes
โ”œโ”€store
โ”‚  โ””โ”€reducers
โ”œโ”€styles
โ”œโ”€types
โ””โ”€utils

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

๋ฌธ์ œ

axios interceptor์—์„œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ์ค‘ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜ค๋ฅ˜ ๋ฐœ์ƒ

issue1

ํ•ด๊ฒฐ

๋ฌธ์ œ๊ฐ€ ๋˜์—ˆ๋˜ ์ฝ”๋“œ

if (errorStatus === 401) {
  UserToken.remove();
  navigate(ROUTE.LOGIN); // useNavigate()
}

ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ axios interceptor์˜ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋ถ€๋ถ„์— ์ž‘์„ฑํ–ˆ๋Š”๋ฐ ์ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ hook ํ˜ธ์ถœ ๊ทœ์น™์„ ์–ด๊ธด ๊ฒƒ์ด๋‹ค.

Call them at the top level in the body of a function component.

Call them at the top level in the body of a custom Hook.

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— useNavigate๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋ฅผ ํ•˜์ง€ ์•Š๊ณ , ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์จ์•ผ ํ–ˆ๋‹ค. replace๋Š” ์•ž์„œ ์–ธ๊ธ‰ํ–ˆ๋˜ ์ œ์•ฝ์„ ๋ฐ›์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ์‚ฌ์šฉํ•ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ๋‹ค.

if (errorStatus === 401) {
  UserToken.remove();
  window.location.replace(ROUTE.LOGIN);
}

๋ฌธ์ œ

ํ† ํฐ์„ ๋ฐ›๋„๋ก axios๋ฅผ ์„ค์ •ํ–ˆ์Œ์—๋„ ๊ณ„์†ํ•ด์„œ client์—์„œ๋Š” ํ† ํฐ์„ ๋ฐ›์ง€ ๋ชปํ•˜๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

ํ•ด๊ฒฐ

์•„๋ž˜๋Š” ์˜ค๋ฅ˜์™€ ๊ด€๋ จ๋œ ์ฝ”๋“œ๋‹ค.

const token = UserToken.get();

const instance = axios.create({
  baseURL: process.env.REACT_APP_BASE_URL,
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

๋กœ์ปฌ์Šคํ† ๋ฆฌ์ง€์— ํ† ํฐ์ด ์ €์žฅ๋˜์–ด์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, axios์—์„œ token์„ ์ œ๋Œ€๋กœ ๋ฐ›์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ถ”์ •๋๋‹ค. ์ด ์˜ค๋ฅ˜์˜ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์œผ๋กœ interceptor๋ฅผ ๊ณต๋ถ€ํ•˜๊ณ  ์ ์šฉํ–ˆ๋‹ค. interceptor๋Š” then์ด๋‚˜, catch๋กœ ์ฒ˜๋ฆฌ๋˜๊ธฐ ์ „์— ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑŒ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ™•์‹คํ•˜๊ฒŒ ํ—ค๋”์— ํ† ํฐ์„ ๋‹ด์•„ ์š”์ฒญ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์ด interceptor๋ฅผ ์ ์šฉํ•œ ์ฝ”๋“œ๋กœ ์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ–ˆ๋‹ค.

instance.interceptors.request.use((config) => {
  const token = UserToken.get();
  config.headers = {
    Authorization: `Bearer ${token}`,
  };
});

๋ฌธ์ œ

์œ ์ € ์‚ญ์ œ๊ฐ€ ์ œ๋Œ€๋กœ ๋˜์ง€ ์•Š๋Š” ํ˜„์ƒ. ์‚ญ์ œ๊ฐ€ ๋˜์—ˆ์Œ์—๋„ view์— ๋ฐ˜์˜์ด ๋˜์ง€ ์•Š์Œ.

ํ•ด๊ฒฐ

redux-logger๋ฅผ ์‚ดํŽด๋ณด๋‹ˆ ์ด์™€ ๊ฐ™์€ ์ƒํ™ฉ์ผ ๋•Œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

logger

deleteUser๋ณด๋‹ค getUsers๊ฐ€ ๋จผ์ € ์ฒ˜๋ฆฌ๊ฐ€ ๋˜์–ด ์‚ญ์ œ๋˜๊ธฐ ์ „์— ๊ณ ๊ฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ›์•„์˜จ ๊ฒƒ์ด๋‹ค.

๊ทธ๋ž˜์„œ ์•„๋ž˜์™€ ๊ฐ™์ด deleteUser dispatch ์•ž์— await๋ฅผ ๋ถ™์—ฌ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ–ˆ๊ณ , ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋๋‹ค.

const deleteUserHandler = async () => {
  if (!confirm("์ •๋ง๋กœ ํ•ด๋‹น ๊ณ ๊ฐ์„ ์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?")) return;
  await dispatch(deleteUserThunk(id));
  dispatch(getUsersThunk());
};

๋ฌธ์ œ

๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์„ ๋•Œ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ”ผ๋“œ๋ฐฑ์„ ์ฃผ๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Œ์—๋„, ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์„ ๋•Œ์˜ ๋กœ์ง์„ ์‹คํ–‰ํ–ˆ๋‹ค.

data === Array

data ? ๋ฐ์ดํ„ฐ ์žˆ์„ ๋•Œ ๋กœ์ง : ์—†์„ ๋•Œ ๋กœ์ง

ํ•ด๊ฒฐ

if([])๋กœ ๋นˆ ๋ฐฐ์—ด์ผ ๋•Œ ์–ด๋–ค ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€ ๊ฒ€์‚ฌํ•ด๋ณด์•˜๋”๋‹ˆ, true๋ฅผ ๋ฐ˜ํ™˜ํ–ˆ๋‹ค. ๋ฌธ์ œ์˜ ์ฝ”๋“œ๋„ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™์„ ํ•˜์ง€ ์•Š์•˜๋˜ ๊ฒƒ์ด๋‹ค. ๋นˆ ๋ฐฐ์—ด์€ length๋กœ ๊ฒ€์‚ฌํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ํ†ตํ•ด์„œ ํ•ด๊ฒฐํ–ˆ๋‹ค.


๋ฌธ์ œ

๋‹ค์Œ ์ฝ”๋“œ์—์„œ axios request config๋กœ ๋“ค์–ด๊ฐ€๋Š” params์˜ ํƒ€์ž… ์ง€์ • ๋ฌธ์ œ

export const getAccountThunk = createAsyncThunk(
  ACCOUNT.GET,
  async (params?: โ“ ) => {
    try {
      const response = await api.get(`/accounts`, { params });

paramas์—๋Š” ๊ฐ์ฒด๊ฐ€ ๋“ค์–ด๊ฐ€๊ธฐ ๋•Œ๋ฌธ์—, object๋กœ ํƒ€์ž…์„ ์ง€์ •ํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  interceptor์˜ config์˜ params์—๋„ object ํƒ€์ž…์„ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค.

config.paramsSerializer = {
  serialize: (params: object) => {
    return new URLSearchParams(params).toString();
  },
};

๊ทธ๋Ÿฌ๋‚˜ ์ด์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

'object' ํ˜•์‹์˜ ์ธ์ˆ˜๋Š” 'string | string[][] | Record<string, string> | URLSearchParams | undefined' ํ˜•์‹์˜ ๋งค๊ฐœ ๋ณ€์ˆ˜์— ํ• ๋‹น๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.ts(2345)

ํ•ด๊ฒฐ

์—๋Ÿฌ ๋ฌธ๊ตฌ๋ฅผ ๋ณด๊ณ  Record ํƒ€์ž…์— ๋Œ€ํ•ด ๊ณต๋ถ€ํ–ˆ๊ณ , Record<string, string> ์ด๋ผ๋Š” ํƒ€์ž…์„ ๋„ฃ์–ด ํ•ด๊ฒฐํ–ˆ๋‹ค. ์ด๋Š” ํ”„๋กœํผํ‹ฐ ํ‚ค๊ฐ€ string, ํ”„๋กœํผํ‹ฐ ๊ฐ’์ด string์ธ ๊ฐ์ฒด์˜ ํƒ€์ž…์„ ๋œปํ•œ๋‹ค.


๊ณผ์ œ ์ง„ํ–‰ ์‹œ ์ฃผ์•ˆ์ 

๊ฐ€๋…์„ฑ ๋†’์ด๊ธฐ / ์ค‘๋ณต์„ฑ ์ค„์ด๊ธฐ

์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด๋ณด์ด๊ณ , ์ฝํžˆ์ง€ ์•Š์œผ๋ฉด ์ ์  ๋” ์œ ์ง€ ๋ณด์ˆ˜ํ•˜๊ธฐ ์–ด๋ ค์›Œ์ง€๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋…์„ฑ์ด ์ข‹์•„์งˆ ์ˆ˜ ์žˆ๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ์ž‘์„ฑํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ๋‹ค. ์„ธ๋ถ€๊ตฌํ˜„์€ ์ตœ๋Œ€ํ•œ ์ˆจ๊ธฐ๊ธฐ ์œ„ํ•ด ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋“ค์€ hook์œผ๋กœ ๋บ๊ณ , ๋ณ€์ˆ˜๋ช…์ด๋‚˜ ํ•จ์ˆ˜๋ช…์„ ์ง€์„ ๋•Œ ์ข€ ๋” ์ง๊ด€์ ์œผ๋กœ ๋ฐ”๋กœ ์•Œ์•„๋ณผ ์ˆ˜ ์žˆ๋Š” ์ด๋ฆ„์œผ๋กœ ์ง€์œผ๋ ค๊ณ  ํ–ˆ๋‹ค.

์ค‘๋ณต์ด ๋˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๋Š๋‚Œ์ด ๋“ค ๋•Œ๋Š” ์ตœ๋Œ€ํ•œ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“ค์–ด ๊ด€๋ฆฌํ–ˆ๊ณ , ๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ํ•จ์ˆ˜๋“ค์€ utils์˜ hooks ํด๋”์— ๋‹ด์•˜๋‹ค.


ํ•œ๊ณ„์  ๋ฐ ๊ฐœ์„  ์‚ฌํ•ญ

์œ ์ง€ ๋ณด์ˆ˜ / ์„ ์–ธํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ

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

๋ฆฌํŒฉํ† ๋ง

๊ณต๋ถ€ํ•˜๋ฉด์„œ ์ ์šฉํ•ด๋ณด๊ณ  ์‹ถ์€ ๊ฒƒ๋“ค์ด ์ƒ๊ธฐ๋ฉด ๋ฆฌํŒฉํ† ๋ง ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ์กด ํ”„๋กœ์ ํŠธ์˜ ๋ฌธ์ œ์ 

  • ํ•จ์ˆ˜ ๋‹จ์œ„์˜ ์ค‘๋ณต์„ฑ์€ ์ œ๊ฑฐํ–ˆ์œผ๋‚˜, ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„์—์„œ ์ค‘๋ณต๋˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋งŽ์Œ
  • ๋ทฐ์™€ ๋กœ์ง์ด ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ ์–ฝํ˜€์žˆ์Œ. ๊ด€์‹ฌ์‚ฌ์˜ ๋ถ„๋ฆฌ๊ฐ€ ์ œ๋Œ€๋กœ ์ด๋ค„์ง€์ง€ ์•Š์€ ์ 
  • ๊ทธ ์™ธ ๊ธฐ๋Šฅ ๊ด€๋ จ ๋ฒ„๊ทธ๋“ค

๋ฆฌํŒฉํ† ๋ง์„ ํ•˜๋ฉฐ ๊ฐœ์„ ํ•œ ์ 

  • modal, input, button, card ๋“ฑ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ, hook์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค
    • ์œ ์ง€/๋ณด์ˆ˜์— ๊ต‰์žฅํ•œ ์ด์ ์„ ๋Š๊ผˆ์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ์˜ ์ค‘๋ณต์„ฑ์ด ์ œ๊ฑฐ ๋˜์—ˆ๊ณ , ํ”„๋กœ์ ํŠธ๋ฅผ ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์Šต๋‹ˆ๋‹ค.
  • ํŽ˜์ด์ง€๋„ค์ด์…˜ -> ๋ฌดํ•œ ์Šคํฌ๋กค๋ง
    • ๊ตฌํ˜„ํ•ด๋ณด๊ณ  ์‹ถ์–ด์„œ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค. interactiveํ•ด์ง„ ๊ฒƒ ๊ฐ™์œผ๋‚˜, ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ด์•ผ ํ•  ๋• ํŽ˜์ด์ง€๋„ค์ด์…˜์ด ์ ํ•ฉํ•˜๋‹ค ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.
  • UI ๊ฐœ์„ 
    • interactiveํ•œ ํŽ˜์ด์ง€๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๊ณ  ์‹ถ์–ด์„œ ๋ฆฌํŒฉํ† ๋งํ–ˆ์Šต๋‹ˆ๋‹ค. (Table -> Card)
    • ๊ทธ ์™ธ ์œ ์ € ์•ก์…˜์— ๋”ฐ๋ฅธ ๋””์ž์ธ์„ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  • Redux-persist ๋„์ž…
    • ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์ „์—ญ์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ์™ธ ํ”„๋กœ์ ํŠธ์˜ ์œ ์ง€/๋ณด์ˆ˜์™€ ๊ด€๋ จํ•˜์—ฌ ์กฐ๊ธˆ์”ฉ ๋ฆฌํŒฉํ† ๋ง ํ–ˆ์Šต๋‹ˆ๋‹ค.

About

vest๐Ÿ€ (user management service)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published