๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Study in Bootcamp/Main Project

[Main Project] ๋ฉ”์ธ ํ”„๋กœ์ ํŠธ ํšŒ๊ณ 

by Bhinney 2022. 12. 8.

โœ”๏ธDate : 2022.11.08 ~ 2022.12.07


What did you do?

๐Ÿ‘ฉ๐Ÿป‍๐ŸŒพ ์ž์ฒด ๋กœ๊ทธ์ธ๊ณผ ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ตฌํ˜„

  • JWT ํ† ํฐ์„ ์ด์šฉํ•œ ์ž์ฒด ๋กœ๊ทธ์ธ๊ณผ ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ตฌํ˜„
  • REST API๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ตฌํ˜„
  • ํ”„๋กœ์ ํŠธ ์ดํ›„ ๋””๋ฐธ๋กญ : ํ•„ํ„ฐ๋ฅผ ์ด์šฉํ•˜์—ฌ ์ž์ฒด ๋กœ๊ทธ์ธ๊ณผ ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ตฌํ˜„, ์†Œ์…œ ๋กœ๊ทธ์ธ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์‹œ header๋กœ ํ† ํฐ ์ „๋‹ฌ

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


๐Ÿ‘ฉ๐Ÿป‍๐ŸŒพ ํ† ํฐ์„ ์ด์šฉํ•˜์—ฌ ์œ ์ € ๊ถŒํ•œ ํŒ๋ณ„

  • ํ—ค๋”์— ๋“ค์–ด์˜ค๋Š” ํ† ํฐ์—์„œ ํ•ด๋‹นํ•˜๋Š” ๊ถŒํ•œ์„ ๊ฐ€์ง„ ์œ ์ €๊ฐ€ ๋งž๋Š”์ง€ ํ™•์ธ
  • Security FilterChain์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ตฌํ˜„
  • ํ”„๋กœ์ ํŠธ ์ดํ›„ ๋””๋ฐธ๋กญ : ๋ช…์‹œ์ ์œผ๋กœ ํ•˜๋‚˜์”ฉ ์ ์€ ๋ถ€๋ถ„์„ ํ•ฉ์ณ์„œ ์ ๊ธฐ

: FilterChain์˜ authorizeHttpRequests()๋ฅผ ์ด์šฉํ•˜์—ฌ uri์™€ method๋ฅผ ๋น„๊ตํ•˜์—ฌ ๊ถŒํ•œ์„ ์ œ๋Œ€๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€ ํŒ๋ณ„ํ•˜์˜€๋‹ค. ๋ชจ๋‘๊ฐ€ ์ ‘๊ทผํ•ด์•ผํ•˜๋Š” ๋ถ€๋ถ„๊ณผ ๊ถŒํ•œ๋ณ„๋กœ ์ ‘๊ทผํ•ด์•ผํ•˜๋Š” ๋ถ€๋ถ„์„ ๋‚˜๋ˆ„์—ˆ๋‹ค. ๋ช…์‹œ์ ์œผ๋กœ ํ•˜๋‚˜์”ฉ ์ ์–ด์ฃผ์—ˆ๋Š”๋ฐ, ํ•˜๋‚˜์”ฉ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ ์—ˆ๋‹ค. ์ดํ›„ ํ”„๋กœ์ ํŠธ๊ฐ€ ๋๋‚˜๊ณ ๋Š” ํ•ด๋‹น ๋ถ€๋ถ„์„ ๋ชจ์•„์„œ ์ฒ˜๋ฆฌํ•ด ๋ณผ ์ƒ๊ฐ์ด๋‹ค. ์™œ๋ƒํ•˜๋ฉด, ์œ„์— ์žˆ๋Š” ์ˆœ์„œ๋ณ„๋กœ ๊ฑธ๋Ÿฌ์ง€๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฌถ์„ ๋•Œ ๊ทธ๊ฒƒ์„ ๊ณ ๋ คํ•ด์„œ ๋ฌถ์–ด๋ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ํ•˜๋‚˜์”ฉ ์žˆ์„ ๋•Œ์—๋Š” ๊ทธ ๋ถ€๋ถ„์ด ์กฐ๊ธˆ์€ ์ˆ˜์›”ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 

์•„๋ž˜๋Š” ์›๋ž˜ ์ค€๋น„ํ–ˆ๋˜ ํ”ผํ”ผํ‹ฐ์˜ ์ผ๋ถ€์ด๋‹ค. ๋งŒ์•ฝ ์œ ์ € ๊ถŒํ•œ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์•„๋ž˜์ฒ˜๋Ÿผ ํ˜๋Ÿฌ๊ฐ„๋‹ค. 


๐Ÿ‘ฉ๐Ÿป‍๐ŸŒพ ํšŒ์› CRUD ๊ตฌํ˜„

  • ํšŒ์› ๊ด€๋ฆฌ ๋กœ์ง CRUD ๊ตฌํ˜„
  • ํšŒ์› ๊ฐ€์ž… ์‹œ ํ™˜์˜ ๋ฉ”์ผ ๋ฐœ์†ก
  • ํ”„๋กœ์ ํŠธ ์ดํ›„ ๋””๋ฐธ๋กญ : ์ด๋ฉ”์ผ ์ธ์ฆ ๊ตฌํ˜„ ํ˜น์€ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ฐพ๊ธฐ ๊ตฌํ˜„ 

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


๐Ÿ‘ฉ๐Ÿป‍๐ŸŒพ ์—ฐ๊ด€ ๊ด€๊ณ„

  • ํ…Œ์ด๋ธ” ๊ฐ„์˜ ์—ฐ๊ด€ ๊ด€๊ณ„ ์—ฐ๊ฒฐ
  • ์–‘๋ฐฉํ–ฅ ์—ฐ๊ฒฐ์„ ์œ„ํ•œ ์—ฐ๊ด€ ๊ด€๊ณ„ ํŽธ์˜ ๋ฉ”์„œ๋“œ ๊ตฌํ˜„
  • ํ”„๋กœ์ ํŠธ ์ดํ›„ ๋””๋ฒจ๋กญ : ๋” ํšจ์œจ์ ์ธ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ์ƒ๊ฐํ•ด ๋ณผ ๊ฒƒ

: ์œ„์— ์—ฐ๊ฒฐํ•ด์„œ ์—”ํ‹ฐํ‹ฐ ๊ฐ„์˜ ์—ฐ๊ฒฐ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•ด์ฃผ์—ˆ๋‹ค. ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ์ œ๋Œ€๋กœ ํ•˜์ง€ ์•Š์œผ๋ฉด ์‚ญ์ œํ•  ๋•Œ์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด๋‹ค. ์ด๋ฒˆ์— ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋ฉด์„œ ๋ฆฌ๋ทฐ๋‚˜ ๋ฌธ์˜๊ฐ€ ์ œ๋Œ€๋กœ ์‚ญ์ œ๊ฐ€ ์•ˆ๋˜๋ฉด ํƒˆํ‡ด๊ฐ€ ์•ˆ๋˜๋Š” ์—๋Ÿฌ๋ฅผ ๋งŒ๋‚ฌ์—ˆ๋‹ค. ์ด์œ ๋Š” ๋ฐ”๋กœ ํ•œ ์ชฝ์—๋งŒ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์ฃผ๊ฑฐ๋‚˜, ์ž˜๋ชป๋œ ๋ฐฉ์‹์œผ๋กœ ์—ฐ๊ด€ ๊ด€๊ณ„์— ์žˆ์—ˆ๋˜ ๋ถ€๋ถ„๋“ค์ด ๋‚˜์ค‘์— ๋ฌธ์ œ์ ์œผ๋กœ ๋ฐœ๊ฒฌ๋˜๋Š” ๊ฒƒ์ด์—ˆ๋‹ค. ์‚ฌ์‹ค ๊ทธ๋™์•ˆ ์—ฐ๊ด€๊ด€๊ณ„๋Š” ๊ทธ๋ƒฅ ์„œ๋กœ ์ฐธ์กฐํ•ด์„œ ๊ฐ€์ ธ์˜ค๋Š” ๊ฑฐ ์ •๋„๋กœ๋งŒ ํŒ๋‹จํ–ˆ์—ˆ๋Š”๋ฐ, ์ด๋ฒˆ์— ์ œ๋Œ€๋กœ ์•ˆํ•˜๋ฉด ์–ด๋–ค ๋ฌธ์ œ๊ฐ€ ๋‚˜ํƒ€๋‚˜๋Š”์ง€ ๋„ˆ๋ฌด ๋ผˆ์ ธ๋ฆฌ๊ฒŒ ๋Š๊ผˆ๋‹ค. ER ๋‹ค์ด์–ด๊ทธ๋žจ๋„ ์•ฝ 5~6๋ฒˆ ์ •๋„ ๊ณ ์นœ ๊ฒƒ ๊ฐ™๋‹ค. ์ฒ˜์Œ์— ์ •๋ง ์ฒ ์ €ํ•˜๊ฒŒ ํ•œ๋‹ค๊ณ  ํ–ˆ๋Š”๋ฐ, ํ•˜๋‹ค๋ณด๋‹ˆ ๋ฐ”๋€” ํ•„์š”๊ฐ€ ์žˆ๋Š” ๋ถ€๋ถ„๋“ค์ด ๊ณ„์† ๋‚˜ํƒ€๋‚ฌ๋‹ค. 

ํ”„๋กœ์ ํŠธ๊ฐ€ ๋๋‚ฌ์–ด๋„ ์ด๊ฑฐ๋ณด๋‹ค ๋” ํšจ์œจ์ ์ธ ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ์žˆ๋Š”์ง€ ์ƒ๊ฐํ•ด ๋ณด๊ณ  ์‹ถ๋‹ค. ์™œ๋ƒํ•˜๋ฉด, ๊ทธ ์ด์œ ๋Š” ์•„๋ž˜์— ๋ฆฌ๋ทฐ์— ์„ค๋ช…ํ•˜๋ ค๊ณ  ํ•œ๋‹ค. 


๐Ÿ‘ฉ๐Ÿป‍๐ŸŒพ ๊ตฌ๋งค๋ฅผ ํ•œ ํšŒ์›๋งŒ ๋ฆฌ๋ทฐ ์ž‘์„ฑ

  • ์ฟผ๋ฆฌ๋ฅผ ์ด์šฉํ•˜์—ฌ ์—ฐ๊ด€ ๊ด€๊ณ„์— ์žˆ๋Š” Client๋กœ 1์ฐจ ์ฃผ๋ฌธ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ฐ€์ ธ์˜ด
  • Stream์„ ์ด์šฉํ•˜์—ฌ filter๋กœ ์ฃผ๋ฌธ ๋ฆฌ์ŠคํŠธ์˜ ์ƒํ’ˆ ์•„์ด๋””์™€ ๋ฆฌ๋ทฐ๋ฅผ ์“ฐ๋ ค๋Š” ๊ณณ์˜ ์ƒํ’ˆ ์•„์ด๋””๊ฐ€ ์ผ์น˜ํ•˜๋Š” ์ง€ ํŒ๋ณ„
  • ๋‹ค์Œ์œผ๋กœ ๊ฑธ๋Ÿฌ์ง„ ์ฃผ๋ฌธ ๋ฆฌ์ŠคํŠธ์—์„œ ๊ฒฐ์ œ ์™„๋ฃŒ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ํŒ๋ณ„
  • ํ”„๋กœ์ ํŠธ ์ดํ›„ ๋””๋ฐธ๋กญ : ์ผ๋Œ€์ผ ์—ฐ๊ด€๊ด€๊ณ„๋กœ๋„ ํ•œ ๋ฒˆ ํ•ด๋ณด๊ธฐ

: ์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” ๋ฆฌ๋ทฐ๊ฐ€ ํ•ด๋‹น ๊ธ€ ๊ฒŒ์‹œํŒ ์•„๋ž˜์—์„œ ์ž‘์„ฑํ•œ๋‹ค. ์œ„์— ER ๋‹ค์ด์–ด๊ทธ๋žจ์ฒ˜๋Ÿผ ์—ฐ๊ด€ ๊ด€๊ณ„์—์„œ ๋ฆฌ์ŠคํŠธ๋กœ ์ด์šฉํ•  ์ˆ˜ ๋ฐ–์— ์—†์—ˆ๋‹ค. ๋งŒ์•ฝ ์ฃผ๋ฌธ ๋‚ด์—ญ์—์„œ ์ž‘์„ฑํ–ˆ๋‹ค๋ฉด ์ผ๋Œ€์ผ๋กœ ๊ฐ€๋Šฅํ–ˆ์„ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์ด ๋ถ€๋ถ„๋„ ํ•œ ๋ฒˆ ๊ตฌํ˜„ํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค. ํ™•์‹คํžˆ ์ผ๋Œ€์ผ๋กœ ํ•œ๋‹ค๋ฉด ํ•ด๋‹น ์ฃผ๋ฌธ์„ ์ œ๋Œ€๋กœ ๊ฐ€์ง€๊ณ  ์˜ฌ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค. ๋งŒ์•ฝ ์ฝ”๋“œ๊ฐ€ ๊ถ๊ธˆํ•˜๋‹ค๋ฉด ์•„๋ž˜์˜ ๊ธ€์„ ์‚ดํŽด๋ณด๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค.

 

[Main Project] Day 21 : ๋งˆ์ง€๋ง‰ ๋””๋ฐธ๋กญ ๋ฐ ์ˆ˜์ •

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

bhinney.tistory.com


๐Ÿ‘ฉ๐Ÿป‍๐ŸŒพ ๋ฆฌ๋ทฐ ์‚ญ์ œ ์‹œ ๊ฒŒ์‹œํŒ์˜ ํ‰์ ๊ณผ ๋ฆฌ๋ทฐ ๊ฐœ์ˆ˜ ๋ณ€ํ™” ๊ตฌํ˜„ (+ ํŠธ๋žœ์žญ์…˜ ๋ฌธ์ œ)

  • ๋ฆฌ๋ทฐ๋ฅผ ์‚ญ์ œํ•˜๋ฉด ํ‰์ ๊ณผ ๋ฆฌ๋ทฐ ๊ฐœ์ˆ˜๊ฐ€ ๋ณ€ํ•ด์•ผ ํ•œ๋‹ค.
  • ๋งŒ์•ฝ ๋ฆฌ๋ทฐ๊ฐ€ 1๊ฐœ์ด๋ฉด, ๋ ˆํฌ์ง€ํ† ๋ฆฌ ํด๋ž˜์Šค์˜ ์ฟผ๋ฆฌ๋ฌธ์œผ๋กœ ๊ฐ€์ ธ์˜ค์ง€ ๋ชปํ•˜๋‹ˆ ์ž„์˜๋กœ ์ง€์ •ํ•ด์ค€๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ๋ฆฌ๋ทฐ ๊ฐœ์ˆ˜์™€ ํ‰์ ์„ ๋‹ค์‹œ ์ •๋ฆฌํ•ด์ค€๋‹ค.
  • โญ๏ธ ํŠธ๋žœ์žญ์…”๋„ ์–ด๋…ธํ…Œ์ด์…˜์ด ์—†์–ด์•ผ ํ•œ๋‹ค. ์žˆ์œผ๋ฉด ๋กค๋ฐฑ์ด ๋˜์–ด ์ง€์›Œ์ง€์ง€ ์•Š๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. โญ๏ธ
  • ํ”„๋กœ์ ํŠธ ์ดํ›„ ๋””๋ฐธ๋กญ :  ํŠธ๋žœ์žญ์…˜ ๊ณต๋ถ€ ๋ฐ ์ •๋ฆฌ

: ํŠธ๋žœ์žญ์…˜์€ ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์˜ ๊ฝƒ์ด์—ˆ๋‹ค. ์–ด๋–ค ์„œ๋น„์Šค๋Š” ํŠธ๋žœ์žญ์…˜ ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์ด์ง€ ์•Š์•„์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ , ์–ด๋–ค ์„œ๋น„์Šค๋Š” ๋ถ™์—ฌ์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋ณ€ํ™”๋ฅผ ์ค˜์•ผํ•˜๋Š” ๋ฉ”์„œ๋“œ์— @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ์ž˜๋ชป ๋ถ™์ด๋ฉด ๋ณ€ํ™”๊ฐ€ ์ƒ๊ธฐ์ง€ ์•Š์•˜๊ณ , ๋ฉ”์„œ๋“œ ์•ˆ์— ๋กœ์ง๋“ค์ด ํ•˜๋‚˜๋กœ ๊ฐ€์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์— @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์ด์ง€ ์•Š์œผ๋ฉด ํ•˜๋‚˜๋Š” ์ผ์–ด๋‚˜๊ณ  ํ•˜๋‚˜๋Š” ์ผ์–ด๋‚˜์ง€ ์•Š๋Š” ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

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

๊ทธ๋ž˜์„œ ํŠธ๋žœ์žญ์…˜์— ๋Œ€ํ•ด์„œ ์กฐ๊ธˆ ๋” ๋งŽ์€ ๊ณต๋ถ€๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๋Š๊ผˆ๋‹ค.

ํ•ด๋‹น ์—๋Ÿฌ์˜ ๊ฒฝํ—˜์€ ์•„๋ž˜ ๋ธ”๋กœ๊ทธ์— ์งง๊ฒŒ ์จ์žˆ๋‹ค.

 

[Main Project] Day 20 : ์†Œ์…œ๋กœ๊ทธ์ธ ๊ถŒํ•œ ์ˆ˜์ •

๋ณธ ๊ธ€์€ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ธฐ๋กํ•˜๊ธฐ ์œ„ํ•ด ์ ์€ ๊ธ€ ์ž…๋‹ˆ๋‹ค. ์ˆ˜์ •์ด ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ •ํ™•ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๐Ÿšจ Board์™€ Product์˜ id๊ฐ€ ๊ฐ™์ด ๊ฐ€์ง€ ์•Š๋Š” ๋ฌธ์ œ ์›์ธ : Board์˜ ๋‚ด์šฉ์ด vachar์—ฌ์„œ ํŠน์ • ๊ธธ์ด๊ฐ€ ๋„˜

bhinney.tistory.com


๐Ÿ‘ฉ๐Ÿป‍๐ŸŒพ ํ† ํฐ ์žฌ๋ฐœ๊ธ‰ ๊ตฌํ˜„

  • ์—‘์„ธ์Šค ํ† ํฐ ํ˜น์€ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ๋งŒ๋ฃŒ ์‹œ ์žฌ๋ฐœ๊ธ‰ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„
  • AOP, REST API๋กœ ๊ตฌํ˜„
  • ํ”„๋กœ์ ํŠธ ์ดํ›„ ๋””๋ฐธ๋กญ : ๋‹ค๋ฅธ ํšจ์œจ์ ์ธ ๋ฐฉ๋ฒ•์„ ์ƒ๊ฐํ•ด ๋ณผ ๊ฒƒ

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

@Transactional
public TokenDto reissueRefresh(HttpServletRequest request, HttpServletResponse response) {

   /* Access Token ํ™•์ธ */
   String accessToken = request.getHeader("Authorization").replace("Bearer ","");

   if (!securityProvider.validate(accessToken)) {
      log.error("์œ ํšจํ•˜์ง€ ์•Š์€ access token");
      throw new BusinessLogicException(ExceptionCode.UNAUTHORIZED_TOKEN);
   }

   Map<String, Object> claims = securityProvider.parseClaims(accessToken);

   if (claims == null) {
      throw new BusinessLogicException(ExceptionCode.NOT_EXPIRATION_TOKEN);
   }

   /* Refresh Token ํ™•์ธ */
   String username = (String)claims.get("username");
   String refreshToken = CookieUtil.getCookie(request, "Refresh")
      .map(Cookie::getValue)
      .orElseThrow(() -> new BusinessLogicException(ExceptionCode.LOGOUT_MEMBER));

   RefreshToken userRefreshToken =
      refreshTokenRepository.findByKey(username)
         .orElseThrow(() -> new BusinessLogicException(ExceptionCode.LOGOUT_MEMBER));

   if (securityProvider.validate(refreshToken)) {
      log.error("์œ ํšจํ•˜์ง€ ์•Š์€ refresh token");
      throw new BusinessLogicException(ExceptionCode.UNAUTHORIZED_TOKEN);
   }

   long now = (new Date()).getTime();
   long validTime = securityProvider.getTokenClaims(refreshToken).getExpiration().getTime() - now;

   /* ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์ด ํ•˜๋ฃจ ์ด๋‚ด๋กœ ๋‚จ์•˜๋‹ค๋ฉด, ์žฌ ๋ฐœ๊ธ‰ (์—‘์„ธ์Šค ํ† ํฐ๋„ ๊ฐ™์ด ์žฌ๋ฐœ๊ธ‰) */
   if (validTime <= 1000 * 60 * 60 * 24) {

      TokenDto tokenDto = securityProvider.generatedTokenDto(username);

      accessToken = tokenDto.getAccessToken();
      refreshToken = tokenDto.getRefreshToken();

      /* Refresh DB update */
      userRefreshToken.updateValue(refreshToken);
      refreshTokenRepository.saveAndFlush(userRefreshToken);

      int cookieMaxAge = (int) 1000 * 60 * 24 * 7;
      CookieUtil.deleteCookie(request, response, "Refresh");
      CookieUtil.addCookie(response, "Refresh", refreshToken, cookieMaxAge);
   }

   /* ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์ด ์œ ํšจํ•œ๋ฐ ๋ณด๋‚ธ ์„ค์ •์ด๊ธฐ์— ์˜ˆ์™ธ ๋˜์ง€๊ธฐ */
   log.error("refresh token ์ด ๋งŒ๋ฃŒ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.");
   throw new BusinessLogicException(ExceptionCode.NOT_EXPIRATION_TOKEN);
}

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ReissueToken {
}

 

@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class SecurityAspect {

   private final SecurityProvider securityProvider;
   private final RefreshTokenRepository refreshTokenRepository;

   @Before("@annotation(reissueToken)")
   public void getAccessTokenAgain(ReissueToken reissueToken) throws Throwable {
      log.info("# ์–ด๋…ธํ…Œ์ด์…˜ ํ† ํฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์‹œ์ž‘");
      ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
      HttpServletRequest request = requestAttributes.getRequest();
      HttpServletResponse response = requestAttributes.getResponse();

      String accessToken = request.getHeader("Authorization").replace("Bearer ", "");
      Authentication authentication = securityProvider.getAuthentication(accessToken);
      RefreshToken refreshTokenTable = refreshTokenRepository.findByKey(authentication.getName())
         .orElseThrow(()-> new BusinessLogicException(ExceptionCode.NOT_FOUND_TOKEN));
      String refreshToken = refreshTokenTable.getValue();

      if (accessToken == null) {
         log.error("์—‘์„ธ์Šค ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
         throw new BusinessLogicException(ExceptionCode.NOT_FOUND_TOKEN);
      }

      /* ํ† ํฐ ์œ ํšจ ์‹œ๊ฐ„ */
      Date now = new Date();
      long accessValidTime = securityProvider.getTokenClaims(accessToken).getExpiration().getTime() - now.getTime();
      long refreshValidTime = securityProvider.getTokenClaims(refreshToken).getExpiration().getTime() - now.getTime();

      /* ์—‘์„ธ์Šค ํ† ํฐ์˜ ์‹œ๊ฐ„์ด 10๋ถ„ ์ด๋‚ด๋กœ ๋‚จ์•˜๋‹ค๋ฉด ์žฌ ๋ฐœ๊ธ‰ */
      if (accessValidTime <= 1000 * 60 * 10) {
         log.info("# ์—‘์„ธ์Šค ํ† ํฐ ์žฌ ๋ฐœ๊ธ‰");
         String role = authentication.getAuthorities().toString().replace("[ROLE_","").replace("]", "");

         Date newAccessExpiration = new Date(now.getTime() + securityProvider.getAccessTokenTime());
         accessToken =
            securityProvider.createAccessToken(authentication.getName(), role, newAccessExpiration);
      }

      /* ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์˜ ์‹œ๊ฐ„์ด ํ•˜๋ฃจ ์ด๋‚ด๋กœ ๋‚จ์•˜๋‹ค๋ฉด ์žฌ ๋ฐœ๊ธ‰ */
      if (refreshValidTime <= 1000 * 60 * 60 * 24) {
         log.info("# ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ์žฌ ๋ฐœ๊ธ‰");
         Date newRefreshExpiration = new Date(now.getTime() + securityProvider.getRefreshTokenTime());
         refreshToken = securityProvider.createRefreshToken(authentication.getName(), newRefreshExpiration);

         refreshTokenTable.updateValue(refreshToken);
         refreshTokenRepository.saveAndFlush(refreshTokenTable);
      }

      response.setHeader("Authorization", accessToken);

      log.info("# ์–ด๋…ธํ…Œ์ด์…˜ ํ† ํฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์ข…๋ฃŒ ");
   }
}

ํ”„๋กœ์ ํŠธ๊ฐ€ ๋๋‚˜๊ณ  ์ข€ ๋” ๊น”๋”ํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ •๋ฆฌํ•ด์„œ ์ ์„ ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ด์•ผ๊ฒ ๋‹ค.


๐Ÿ‘ฉ๐Ÿป‍๐ŸŒพ ํ˜„์žฌ ๋กœ๊ทธ์ธํ•œ ์œ ์ € ํŒ๋ณ„

  • ํ˜„์žฌ ๋กœ๊ทธ์ธํ•œ ์œ ์ €์™€ ์ ‘๊ทผํ•˜๋ ค๋Š” ํŽ˜์ด์ง€์˜ ์œ ์ €๊ฐ€ ๊ฐ™์€์ง€ ํŒ๋ณ„์ด ํ•„์š”
  • ๊ณตํ†ต์˜ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค๊ณ , ๋ฉค๋ฒ„ ์„œ๋น„์Šค ๋‹จ์— ํ•ด๋‹น ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•˜์—ฌ ํŒ๋ณ„ ๋ฉ”์„œ๋“œ ์ž‘์„ฑ

: ์ €๋ฒˆ ํ”„๋ฆฌ ํ”„๋กœ์ ํŠธ ๋•Œ์—๋Š” Principal ๊ฐ์ฒด๋กœ ์ผ์ผ์ด ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ž‘์—…์„ ํ•ด์ฃผ์—ˆ๋‹ค. ๊ฐœ์ธ์ ์œผ๋กœ ๊ทธ๊ฒŒ ๊ต‰์žฅํžˆ ํšจ์œจ์ ์ด์ง€ ๋ชปํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๋Š๊ผˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด, ๊ฒฐ๊ตญ ๋น„์Šทํ•œ ๋กœ์ง์˜ ๋ฐ˜๋ณต์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์ด๊ฒƒ์„ ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ํšจ์œจ์ ์œผ๋กœ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ํŒ๋ณ„ํ•  ์ˆ˜ ์žˆ์„๊นŒ ์ƒ๊ฐํ•ด๋ณด์•˜๋‹ค. ์ด๋ ‡๊ฒŒ ํŒ๋ณ„ํ•˜๋Š” ์ด์œ ๋Š” ํ•ด๋‹น ํŽ˜์ด์ง€ ์š”์ฒญ ๊ถŒํ•œ์€ ์ •๋ง ๊ถŒํ•œ๋งŒ ๋ณด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์ด ํŽ˜์ด์ง€์˜ ์ฃผ์ธ์ธ ์œ ์ €๊ฐ€ ์•„๋‹ˆ์–ด๋„ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐฑ์—์„œ๋„ ๋ง‰์•„์•ผ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. 

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

๊ฐœ์ธ์ ์œผ๋กœ ์ด๋ ‡๊ฒŒ ํ•˜๋‹ˆ ํ›จ์”ฌ ํšจ์œจ์ ์ด์—ˆ๋˜ ๊ฒƒ ๊ฐ™๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

public class SecurityUtil {
   private SecurityUtil() {}

   public static String getCurrentEmail() {

      /* ContextHolder ์—์„œ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ */
      final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

      if (authentication == null || authentication.getName() == null) {
         throw new RuntimeException("ํ•ด๋‹นํ•˜๋Š” ์ธ์ฆ ์ •๋ณด๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
      }

      return authentication.getName();
   }
}

 

/* ์†Œ๋น„์ž ์กฐํšŒ */
@Transactional(readOnly = true)
public Client findClient(long clientId) {
	correctClient(clientId);
	Client client = findVerifiedClient(clientId);

	return client;
}

/* ๋กœ๊ทธ์ธ ํ•œ ์‚ฌ์šฉ์ž์™€ ์ ‘๊ทผ ์‚ฌ์šฉ์ž ํŒ๋ณ„ */
public void correctClient(long accessId) {
   String email = SecurityUtil.getCurrentEmail();
   Member loginMember = memberService.findMemberByEmail(email);
   log.info("๋กœ๊ทธ์ธํ•œ ์œ ์ € : {}", loginMember.getName());

   if (loginMember.getClient().getClientId() != accessId) {
      throw new BusinessLogicException(ExceptionCode.WRONG_ACCESS);
   }
}

 

/* ๋กœ๊ทธ์ธ ํ•œ ์‚ฌ์šฉ์ž์™€ ์ ‘๊ทผ ์‚ฌ์šฉ์ž ํŒ๋ณ„ */
public void correctSeller(long accessId) {
    String email = SecurityUtil.getCurrentEmail();
    Member loginMember = memberService.findMemberByEmail(email);
    log.info("๋กœ๊ทธ์ธํ•œ ์œ ์ € : {}", loginMember.getName());

    if (loginMember.getSeller().getSellerId() != accessId) {
        throw new BusinessLogicException(ExceptionCode.WRONG_ACCESS);
    }
}

๐Ÿ‘ฉ๐Ÿป‍๐ŸŒพ ์นด์นด์˜ค ํŽ˜์ด ๊ตฌํ˜„

  • ์นด์นด์˜ค ํŽ˜์ด API๋ฅผ ์ด์šฉํ•œ ๊ฒฐ์ œ ๊ตฌํ˜„
  • ํ”„๋กœ์ ํŠธ ์ดํ›„ ๋””๋ฐธ๋กญ : ๋‹ค๋ฅธ PG์‚ฌ ํ™•์ธํ•ด๋ณด๊ธฐ

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

 

+ ํฌ์ŠคํŒ… ์™„๋ฃŒ

 

[Spring] Spring boot Java ์นด์นด์˜ค ํŽ˜์ด ๋‹จ๊ฑด ๊ฒฐ์ œ ๊ตฌํ˜„ํ•˜๊ธฐ

๋ฉ”์ธ ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋ฉด์„œ ์นด์นด์˜ค API๋ฅผ ์ด์šฉํ•˜์—ฌ ์นด์นด์˜ค ํŽ˜์ด๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์•˜๋‹ค. ํ•ด๋‹น ๋ถ€๋ถ„์„ ๊ธฐ์–ตํ•˜๊ธฐ ์œ„ํ•ด ์ด ๊ธ€์„ ์ž‘์„ฑํ•˜์˜€๋‹ค. ์ด ํฌ์ŠคํŒ…์€ ํ”„๋กœ์ ํŠธ ๊ณผ์ •์—์„œ ํ๋ฆ„์„ ๊ธฐ์–ตํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์„ฑํ•œ ๊ฒƒ

bhinney.tistory.com


๋Œ์•„๋ณด๊ธฐ

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

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

 

๋Œ“๊ธ€