๋ฏธ๋‹ˆ๋น„์น˜

๋ฒ ํŠธ๋‚จ ๋‚˜ํŠธ๋ž‘ ํ˜ผ์ž ๋– ๋‚˜๋Š” ์—ฌํ–‰

 

 ํ˜ธํ•‘ํˆฌ์–ด

 

์•„๋ฌด๋ฆฌ ํ˜ผ์ž ์™”์ง€๋งŒ, ๊ทธ๋ž˜๋„ ๋‚˜ํŠธ๋ž‘ ์™”์œผ๋‹ˆ ๋ฐ˜๋‚˜์ ˆ ํ˜ธํ•‘ํˆฌ์–ด๋ฅผ ํ•ด๋ณด๊ณ ์ž ์ „๋‚  ํ˜ธํ…”์—์„œ ๊ฒ€์ƒ‰ํ•˜๋‹ค๊ฐ€ ๋งˆ์ด๋ฆฌ์–ผํŠธ๋ฆฝ์—์„œ ํˆฌ์–ด ์‹ ์ฒญ์„ ํ–ˆ๋‹ค.

์ƒ์ „ ์ฒ˜์Œ ์Šค๋…ธ์ฟจ๋ง์„ ํ•ด์•ผํ•ด์„œ ํ•œ๊ตญ์ธ ๊ฐ€์ด๋“œ ํˆฌ์–ด๋ฅผ ์‹ ์ฒญ.

 

ํˆฌ์–ด๋Š” ๋Œ€๋ถ€๋ถ„ ๋‚˜ํŠธ๋ž‘ ์‹œ๋‚ด๋Š” ํ˜ธํ…” ํ”ฝ์—…์„ ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์‹ ์ฒญ์‹œ ํ˜ธํ…” ์œ„์น˜๋ฅผ ์•Œ๋ ค์ฃผ๋ฉด ๋œ๋‹ค.

ํˆฌ์–ด ์‹ ์ฒญํ›„ ์•„๋ž˜์™€ ๊ฐ™์ด ์ง„ํ–‰ํ•˜๋ฉด ๋œ๋‹ค. 

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

 

โ— ๋‚˜ํŠธ๋ž‘ ํ˜ธํ•‘ํˆฌ์–ด ์Šค์ผ€์ค„

+ 08:00 - 08:30 ๋‚˜ํŠธ๋ž‘ ์‹œ๋‚ด ํ˜ธํ…” ํ”ฝ์—…

+ 09:00 - 10:30 ์„ ์ฐฉ์žฅ ์ง‘๊ฒฐ ์ถœ๋ฐœ

+ 10:30 ~ ํ˜ผ๋ฌธ์„ฌ ํฌ์ธํŠธ ์ด๋™ ์Šค๋…ธํด๋ง ์ฆ๊ธฐ๊ธฐ

+ 11:00 - 13:40 ๋ฏธ๋‹ˆ๋น„์น˜ ์ด๋™ ๋ฐ ์ ์‹ฌ์‹์‚ฌ ํ›„ ์ž์œ  ์‹œ๊ฐ„

 

โ— ํฌํ•จ์‚ฌํ•ญ

ํ•œ๊ตญ์–ด๊ฐ€์ด๋“œ, ์ ์‹ฌ์‹์‚ฌ, ์Šค๋…ธํด๋ง์žฅ๋น„,๋ฏธ๋‹ˆ๋น„์น˜ ์„ ๋ฒ ๋“œ

 

โ— ์ค€๋น„๋ฌผ

์ˆ˜์˜๋ณต ๊ฐˆ์•„์ž…์„์ˆ˜ ์žˆ๋Š” ๊ณต๊ฐ„์ด ์—†์œผ๋ฏ€๋กœ, ๋ฏธ๋ฆฌ ์ˆ˜์˜๋ณต์„ ์ž…๊ณ  ์˜ค๋ฉด ๋œ๋‹ค.

์ƒค์›Œ๋Š” ํ•˜์‹ค๋ถ„๋งŒ...( ๋‚˜๋Š” ํŒจ~์Šค )

์„ ํฌ๋ฆผ์€ ํ•„์ˆ˜!!!

๋งค๋„ˆํŒ 5๋งŒ๋™ ํ˜„๊ธˆ ์ค€๋น„.

 

 

์„ ์ฐฉ์žฅ์— ๋„์ฐฉํ•ด์„œ ์Šคํ”ผ๋“œ๋ณดํŠธ๋ฅผ ํƒ€๊ณ  ํ˜ผ๋ฌธ์„ฌ์— ๋„์ฐฉ ๊ฐ„๋‹จํžˆ ์Šค๋…ธ์ฟจ๋ง์„ ํ–ˆ๋‹ค.

์ฒ˜์Œ์ด๋ผ์„œ ์ง  ์†Œ๊ธˆ๋ฌผ์„ ๋งŽ์ด ๋จน์—ˆ๊ณ , ๋ฐœ ๋‹ฟ์ง€ ์•Š๋Š”๊ณณ์— ๊ฐ€๋ฉด ์•ฝ๊ฐ„ ๊ณตํฌ์Šค๋Ÿฝ์ง€๋งŒ, ๊ทธ๋ž˜๋„ ์ƒˆ๋กœ์šด ๊ฒฝํ—˜์ด์˜€๊ณ , ์•ž์œผ๋กœ ํ•œ๊ตญ์—์„œ ์Šค๋…ธ์ฟจ๋ง์„ ๋ฐฐ์›Œ๋ณผ๊นŒ ์ƒ๊ฐํ–ˆ๋‹ค.

๋ฏธ๋‹ˆ๋น„์น˜

๋ฏธ๋‹ˆ๋น„์น˜์— ๋„์ฐฉํ•ด์„œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ ์ถ”๊ฐ€๋กœ ์ฆ๊ธธ ์ˆ˜ ์žˆ์—ˆ์œผ๋‚˜, ์•„์žฌ์ธ ๋‚˜๋Š” ๋งฅ์ฃผ ๋จผ์ €..

๊ฐœ์ธ์ ์œผ๋กœ ํŒŒ๋ผ์„ธ์ผ๋ง์„ ๋ชปํ•œ๊ฒŒ ์ง€๊ธˆ ํ›„ํšŒํ•œ๋‹ค. ๋“œ๋ก ์œผ๋กœ ์ดฌ์˜๊นŒ์ง€ ํ•ด์ฃผ๋‹ˆ ์ถ”์–ต์ด ๋ ์ˆ˜ ์žˆ์—ˆ์œผ๋‚˜,

์‚ฌ๋žŒ๋“ค์ด ์ค„์„ ๋งŽ์ด ์„œ์„œ ๊ฒฐ๊ตญ ๋งฅ์ฃผ๋งŒ ๋งˆ์‹œ๋ฉด์„œ ์—ฌ์œ ๋งŒ ์ฆ๊ธฐ๋‹ค ์™”๋‹ค.

ํŒ : ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ๋งฅ์ฃผ๋„ ๋ช‡๊ฐœ ๊ฐ€์ง€๊ณ  ์™€๋„ ์ข‹์„๋“ฏํ•˜๋‹ค. ๋ฏธ๋‹ˆ๋น„์น˜์—์„œ ๋งฅ์ฃผ ๊ฐ€๊ฒฉ์€ ์‚ฌ์•…ํ•˜๋‹ค.

 

์ ์‹ฌ์€ ๋‹จ์ฒด๋กœ ํ˜„์ง€์‹์„ ๋จน์œผ๋ฉฐ, ๋งฅ์ฃผ ํ•œ์บ”์”ฉ ์ค๋‹ˆ๋‹ค. ( ์‚ฌ์ง„์„ ๋ชป์ฐ์Œ ใ… ใ…  )

 


 

 ์ €๋…

 

ํ˜ธํ•‘ํˆฌ์–ด๋ฅผ ํ•˜๊ณ  ๋Œ์•„์™€ ํ˜ธํ…”์—์„œ ์žฌ ์ •๋น„ํ•˜๊ณ , ๋ฐ–์—์„œ ์„ธํƒ์„ ๋งก๊ธฐ๊ณ  ์ €๋…์„ ๋จน์œผ๋ ค ๊ตฌ๊ธ€ ๊ฒ€์ƒ‰.

์˜ค๋Š˜์€ Flexํ•ด๋ณด์ž๊ณ  ๋ž์Šคํ„ฐ๋ฅผ ๋จน๊ธฐ๋กœ ํ•˜๊ณ  ๊ตฌ๊ธ€ ํ‰์ ์ด ๊ทธ๋ž˜๋„ ์ข‹์€ ๊ณณ์œผ๋กœ ์ฐพ์•„๊ฐ.

https://maps.app.goo.gl/VNKrQZGwTiFUe5Jc6 

 

Quán แปc Ngon · 34/2/11B Nguyแป…n Thiแป‡n Thuแบญt, Tân Lแบญp, Nha Trang, Khánh Hòa 650000 ๋ฒ ํŠธ๋‚จ

โ˜…โ˜…โ˜…โ˜…โ˜† · ํ•ด์‚ฐ๋ฌผ ์š”๋ฆฌ ์ „๋ฌธ์‹๋‹น

www.google.com

 

 

ํ˜ผ์ž๋ผ ํฌ๊ธฐ๊ฐ€ ์ค‘๊ฐ„์œผ๋กœ ํ•ด์„œ ๋จน์„๋ ค๊ณ  ํ–ˆ์œผ๋‚˜, ํฐ๊ฒƒ ๋ฐ–์— ์—†๋‹ค๊ณ ํ•ด์„œ ์–ผ๋งˆ ์ฐจ์ด๊ฐ€ ์•ˆ๋‚˜ ์‹œํ‚ด..

๊ฐœ์ธ์ ์œผ๋กœ ๋ง›์กฐ๊ฐœ๊ฐ€ ์ œ์ผ ๋ง›์žˆ์—ˆ๊ณ , ๋ž์Šคํƒ€๋Š” ์น˜์ฆˆ๋ณด๋‹ค ๊ฐˆ๋ฆญ์ด ์ข‹์•˜๋‹ค. ( ์น˜์ฆˆ๋ฐ˜, ๊ฐˆ๋ฆญ๋ฐ˜์œผ๋กœ ์‹œํ‚ด )

 

์ง„์งœ ํ•ด๋ณด๊ณ  ์‹ถ์—ˆ๋˜....๋ž์Šคํ„ฐ์„ ํ†ต์งธ๋กœ ์™€๊ตฌ์™€๊ตฌ ๋จน๊ณ  ์‹ถ์—ˆ๋‹ค.

ํ˜ผ์ž ๋งฅ์ฃผ 5์บ”๊ณผ ๋ž์Šคํƒ€, ๋ง›์กฐ๊ฐœ๊นŒ์ง€ ๋จน๊ณ  ์›ํ™”๋กœ ์•ฝ 5๋งŒ์›(10๋งŒ๋™) ์ •๋„ ๋‚˜์˜จ๊ฒƒ ๊ฐ™๋‹ค.

 

์˜ค๋Š˜์€ Flex ํ•œ ๋‚ 

- ๋‹ค์ŒํŽธ์— ๊ณ„์† -


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

 

๋ฌธ์ œ๋ฐœ์ƒ

Figma(ํ”ผ๊ทธ๋งˆ) ์ ‘์†์‹œ ๊ฐ‘์ž๊ธฐ WebGL ์˜ค๋ฅ˜๋ฐœ์ƒ.

์•„๋ž˜์™€ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒ์œผ๋กœ ํ”ผ๊ทธ๋งˆ์—์„œ ๋””์ž์ธ ํŒŒ์ผ์„ ์ ‘์†ํ•  ์ˆ˜ ์—†์Œ.

 

Figma ๋””์ž์ธ ํŒŒ์ผ ์ ‘์†์‹œ ์˜ค๋ฅ˜

 

ํ•ด๊ฒฐ

 

  • Chorome flags ์„ค์ •์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€๊ฒฝ ( chrome://flags/ ์ ‘์† )
  • WebGL Draft Extensions : ์‚ฌ์šฉ๊ฐ€๋Šฅ
  • Choose ANGLE graphics backend : OpenGL

ํฌ๋กฌ ํ”Œ๋ž˜๊ทธ

 

  • Chrome ์„ค์ • ( ์„ค์ • -> ์‹œ์Šคํ…œ ) 
  • ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ ๊ทธ๋ž˜ํ”ฝ ๊ฐ€์† ์‚ฌ์šฉ : checked

ํฌ๋กฌ ์„ค์ • ์‹œ์Šคํ…œ

 

Chrome ์žฌ ์‹œ์ž‘ํ•˜๋ฉด ์ •์ƒ ์ ‘์†์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

๋‚˜ํŠธ๋ž‘ ํ•ด๋ณ€

 

๋ฒ ํŠธ๋‚จ ๋‚˜ํŠธ๋ž‘ ํ˜ผ์ž ๋– ๋‚˜๋Š” ์—ฌํ–‰

๋‚˜ํŠธ๋ž‘์— ์ €๋… ๋Šฆ๊ฒŒ ๋„์ฐฉํ•˜์—ฌ ๋ฒ ํŠธ๋‚จ ์ˆ ๊ณผ ํ•จ๊ป˜ ์ˆ™์†Œ์—์„œ ์ž๊ณ  ์ผ์–ด๋‚˜, ๊น€์ฒญ์—์„œ ํ™˜์ „ํ•˜๊ณ  ์•„์นจ ์ผ์ฐ ์ฃผ๋ณ€ ๊ตฌ๊ฒฝ ์‹œ์ž‘.

 

ํƒ‘์งฌํ์—‰         โ‡ฅ          ๊ธธ๊ฑฐ๋ฆฌ ์ปคํ”ผ(์ฝฉ์นดํŽ˜ ๋Œ€์‹ )       โ‡ฅ       ํฌ๋‚˜๊ฐ€๋ฅด์‚ฌ์›

๊ทธ๋žฉ ๋ฐ”์ดํฌ / ํƒ‘์งฌํ์—‰
๊ธธ๊ฑฐ๋ฆฌ ์ปคํ”ผ์™€ ํฌ๋‚˜๊ฐ€๋ฅด์‚ฌ์›

 

๋‚˜ํŠธ๋ž‘์—์„œ๋Š” ๊ทธ๋žฉ ๋ฐ”์ดํฌ๋กœ๋งŒ ์ด๋™์„ ํ–ˆ๋‹ค. ๊ทธ๋žฉ์นด๋Š” ์ž˜ ์•ˆ์žกํžˆ๋Š”๋ฐ, ๊ทธ๋žฉ ๋ฐ”์ดํฌ๋Š” ๋ฐ”๋กœ๋ฐ”๋กœ ์žกํžˆ๊ณ  ๊ฐ€๊ฒฉ๋„ ์‹ธ์„œ ๋‚˜ํŠธ๋ž‘ ์žˆ๋Š”๋™์•ˆ ๊ทธ๋žฉ ๋ฐ”์ดํฌ๋งŒ ํƒ”๋‹ค.

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

(๊ฐ€๊ฒฉ์ด ๋‚ด ๊ธฐ์–ต์œผ๋กœ 250์›์œผ๋กœ ๋„ˆ๋ฌด ์ž‘์€ ๊ธˆ์•ก์ด๋ผ ์ •ํ™•ํ•˜์ง€ ์•Š์Œ.)

 

์ปคํ”ผ๋งˆ์‹œ๋ฉด์„œ ๊ธธ๊ฑฐ๋ฆฌ์— ์•‰์•„์„œ, ์–ด๋”œ๊ฐˆ๊นŒ ๊ตฌ๊ธ€๋งต์„ ๋ณด๋‹ค๊ฐ€ ๊ทธ๋žฉ ๋ฐ”์ดํฌ๋ฅผ ๋ถˆ๋Ÿฌ ๊ด€๊ด‘์ง€์ธ ํฌ๋‚˜๊ฐ€๋ฅด์‚ฌ์›์œผ๋กœ ๊ฐ”๋‹ค.

ํฌ๋‚˜๊ฐ€๋ฅด์‚ฌ์›

๋‹จ์ฒด ํ–‰์‚ฌ๊ฐ€ ์žˆ์–ด์„œ ์‚ด์ง ๋ผ์–ด์„œ ํ•จ๊ป˜ ๊ตฌ๊ฒฝ๋„ ํ–ˆ๋‹ค.

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

ํฌ๋‚˜๊ฐ€๋ฅด์‚ฌ์› ๋ฐ–์— ๋‚˜์™€์„œ ๊ธธ๊ฑฐ๋ฆฌ ์‚ฌํƒ•์ˆ˜์ˆ˜๋ฅผ ์ฒ˜์Œ ๋จน์—ˆ๋Š”๋ฐ, ์ตœ๊ณ ์˜ ๋ง›!!,  ๋ฒ ํŠธ๋‚จ์— ์žˆ์„๋•Œ ๋ง๊ณ ์ฃผ์Šค๋ณด๋‹ค ๋” ๋งŽ์ด ๋จน์€๋“ฏ.

 

 ์ ์‹ฌ

๋Šฆ์€ ์ ์‹ฌ์œผ๋กœ ์†๋‹˜์ด ์—†๋Š” ๊ฐ€๊ฒŒ๋ฅผ ์ฐพ์•„ ์กฐ์šฉํžˆ ํ˜ผ์ˆ !!

 

ํ˜ธํ…” ์กฐ์‹์— ์Œ€๊ตญ์ˆ˜๋ฅผ ๋จน์—ˆ์œผ๋‚˜, ์—ญ์‹œ ๋ฒ ํŠธ๋‚จ์—์„œ๋Š” ์Œ€๊ตญ์ˆ˜๊ฐ€ ์ตœ๊ณ !!

์˜ค๊ธฐ์ „์— ๊ณ ์ˆ˜๋ฅผ ๊ฒฝํ—˜ํ•ด์„œ ๊ทธ๋Ÿฐ์ง€ ๊ณ ์ˆ˜๊ฐ€ ์—†์œผ๋ฉด ์‹ฌ์‹ฌํ•œ ๋ง›์ด ๋‚œ๋‹ค.

์Œ€๊ตญ์ˆ˜์— ๋‹ญ๋‹ค๋ฆฌ ํŠ€๊น€ ์ถ”๊ฐ€ํ•ด์„œ ์‹ธ์ด๊ณต๋งฅ์ฃผ๋กœ ํ˜ผ์ˆ . ์—์–ด์ปจ์€ ์—†์ง€๋งŒ, ์„ ํ’๊ธฐ๋กœ๋งŒ์œผ๋กœ ๊ดœ์ฐฎ์•˜๋‹ค.

 

์—ฌ๊ธฐ ์‹๋‹น์—์„œ ๋ฐฐ์šด๊ฒƒ.

๋งฅ์ฃผ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ ค๋‹ˆ "์ €๊ธฐ์š”" ๋“ฑ ์ข…์—…์›์„ ๋ถ€๋ฅด๋Š”๊ฒŒ ๋ญ”์ง€ ๊ถ๊ธˆํ•ด์„œ ์ฐพ์•„๋ณด์•˜๋‹ค.

 

 ์•Œ๋ฉด ์ข‹์€ ์ƒ์‹

๊ฐ„๋‹จํžˆ ์ •๋ฆฌํ•˜๋ฉด

๋‚˜๋ณด๋‹ค ์–ด๋ฆฌ๋ฉด ๋‚จ๋…€๊ตฌ๋ถ„ ์—†์ด "์— ์–ด์ด(์—๋จธ์ด)"

๋‚˜๋ณด๋‹ค ๋‚˜์ด ๋งŽ์€ ๋‚จ์ž๋Š” "์•ˆ์–ด์ด(์•™์–ด์ด)"

๋‚˜๋ณด๋‹ค ๋‚˜์ด ๋งŽ์€ ์—ฌ์ž๋Š” "์น˜์–ด์ด"

๋ผ๊ณ  ๋ถ€๋ฅด๋ฉด ๋œ๋‹ค๊ณ  ํ•œ๋‹ค.  ๊ทธ๋Ÿฌ๋‚˜ ๋ฌด์กฐ๊ฑด '์—๋จธ์ด' ๋ถ€๋ฅด๋ฉด ๋.  ์—ฌํ–‰ ๋๋‚ ๋•Œ๊นŒ์ง€ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ๋‹จ์–ด๋‹ค.

 

'์—๋จธ์ด' : ๋‚˜์ด ๋งŽ์€ ์•„์ฃผ๋จธ๋‹ˆ์—๊ฒŒ ํ•ด์ฃผ๋ฉด ๋งŽ์ด ์›ƒ์œผ์‹œ๋ฉฐ, ์ข‹์•„ํ•˜์‹ ๋‹ค. 

 

 

 ์ €๋…

์˜ค์ง ์ƒ๋งฅ์ฃผํ†ต ๋•Œ๋ฌธ์— ๊ฐ„ ๊ณณ.
 

Quán ฤƒn X.O · 17 Hoàng Diแป‡u, Vฤฉnh Nguyên, Nha Trang, Khánh Hòa, ๋ฒ ํŠธ๋‚จ

โ˜…โ˜…โ˜…โ˜…โ˜† · ์Œ์‹์ 

www.google.com

๊ตฌ๊ธ€๋งต์—์„œ ์ € ์ƒ๋งฅ์ฃผํ†ต ์‚ฌ์ง„๋ณด๊ณ  ์ฐพ์•„๊ฐ„๊ณณ.(ํ•œ๊ตญ๋ถ„๋“ค๋„ ์ข€ ์žˆ๋‹ค)

 

 

๊ตฌ๊ธ€๋งต์„ ๋ณด๋‹ค๊ฐ€ ์ € ์ƒ๋งฅ์ฃผํ†ต ๋•Œ๋ฌธ์— ์ฐพ์•„๊ฐ„ ๊ณณ์ด๋‹ค.

์šฐ๋ฆฌ๋‚˜๋ผ์— ์ˆ˜์ž…์ด ํ•„์š”ํ•œ๋“ฏ ํ•˜๋‹ค. ์ˆ˜์ž…ํ•˜๊ณ  ์‹ถ๋‹ค.

๋น„๋ก ํ˜ผ์ˆ ์ด์ง€๋งŒ, ๊ฐ‘์ž๊ธฐ ์Šค์ฝœ์„ฑ ๋น„๊นŒ์ง€ ์™€์„œ ๋‚ญ๋งŒ ์ตœ๊ณ ์˜€๋‹ค.

 

ํ•˜๋ฃจ๊ฐ€ ๊ธธ์—ˆ๋˜ ๋‚ ์ด๋‹ค.

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

๋น„ ๋งž์œผ๋ฉฐ ๋ฐ”์ดํฌ ํƒ€๊ณ  ํ˜ธํ…”๋กœ~~

 

 

1์ผ์ฐจ๋Š” ์—ฌ๊ธฐ์„œ ๋งˆ๋ฌด๋ฆฌ

- ๋‹ค์ŒํŽธ์— ๊ณ„์† - 

๋ฏธ๋‹ˆ๋น„์น˜

 

๋ฒ ํŠธ๋‚จ ๋‚˜ํŠธ๋ž‘ ํ˜ผ์ž ๋– ๋‚˜๋Š” ์—ฌํ–‰


5๋…„๋งŒ์— ๋– ๋‚˜๋Š” ํ•ด์™ธ์—ฌํ–‰์ด๋‹ค. 4๊ฐœ์›” ํ”„๋กœ์ ํŠธ๊ฐ€ ๋๋‚˜๊ณ  ์—ฌ๋ฆ„์ด ๊ฐ€๊ธฐ์ „ ๋– ๋‚˜์•ผ๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์— ์—ฌ๊ธฐ์ €๊ธฐ ์—ฌํ–‰์ง€๋ฅผ ๋ณด๋‹ค๊ฐ€ ํ˜ผ์ž ๋– ๋‚˜๊ธฐ ์ข‹์€ ํœด์–‘์ง€์ธ ๋ฒ ํŠธ๋‚จ ๋‚˜ํŠธ๋ž‘์œผ๋กœ ๋– ๋‚˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค. ์ธ์•„์›ƒ ํ•ญ๊ณต๊ถŒ์„ ๋ฌด์ž‘์ • ์˜ˆ์•ฝํ•˜๊ณ  ์—ฌํ–‰์ค€๋น„๋ฅผ ์‹œ์ž‘ํ–ˆ๋‹ค.

 

์ฒ˜์Œ์—๋Š” ๋ฒ ํŠธ๋‚จ ์ผ์ฃผ๋ฅผ ๊ณ„ํšํ•˜๊ณ , ๋ฌด์ž‘์ • ๋– ๋‚˜๋ ค๊ณ  ํ–ˆ์œผ๋‚˜, ์ถ”์„์œผ๋กœ ์ธํ•ด ๊ณ ํ–ฅ์—๋„ ๊ฐ€์•ผํ•ด์„œ ์–ด์ฉ”์ˆ˜ ์—†์ด 10์ผ๊ฐ„๋งŒ ๋– ๋‚˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค.

 

[์ด๋ฒˆ ์—ฌํ–‰์˜ ์ปจ์…‰]

1. ํœด์–‘์ง€์—์„œ ์—ฌํ–‰ ์ŠคํŠธ๋ ˆ์Šค๋ณด๋‹ค ํœด์‹์ด ๋จผ์ €๋‹ค.

2. ๋น„ํ–‰๊ธฐ์™€ ์ˆ™๋ฐ•๋งŒ ์˜ˆ์•ฝํ•œ ํ›„ ๋– ๋‚œ๋‹ค.

   - ์—ฌํ–‰์ผ์ •์— ๋”ฐ๋ผ ์ˆ™๋ฐ•์„ ์˜ฎ๊ฒจ ๋‹ค๋‹ˆ๋Š” ์ŠคํŠธ๋ ˆ์Šค ๋ณด๋‹ค๋Š” ํ•œ๊ณณ์—์„œ ํœด์‹.

3. ๊ทธ๋™์•ˆ ๋ชปํ–ˆ๋˜๊ฒƒ ๊ฒฝํ—˜ํ•˜๊ธฐ.

4. ๋‚˜์ด์™€ ์ƒ๊ด€์—†์ด ๋†€๊ธฐ.

5. ํ• ๊ฑด ๋‹ค ํ•ด๋ณด์ž.

 


 ์ถœ๋ฐœ

 

์ธ์ฒœ๊ณตํ•ญ ์Šค์นด์ด๋ผ์šด์ง€

 

 

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

์•ˆํ•ด๋ณธ๊ฒƒ์„ ํ•ด๋ณด์ž๊ณ  ํ–ˆ์œผ๋‹ˆ, ํ‰์ƒ ์ฒ˜์Œ ๋ผ์šด์ง€๋ฅผ ์ด์šฉํ•ด ๋ณธ ๊ฒฐ๊ณผ ์•„์ฃผ ๋ง˜์— ๋“ค์—ˆ๊ณ , ํŠนํžˆ ์ˆ ์„ ์ข‹์•„ํ•˜๋Š” ๋‚˜์—๊ฒŒ ๋น„ํ–‰๊ธฐ ํƒ€๊ธฐ์ „์— ์•Œ์ฝœ์„ ์„ญ์ทจํ• ์ˆ˜ ์žˆ๋‹ค๋Š”๊ฒŒ ๋ง˜์— ๋“ค์—ˆ๋‹ค. ๊ณตํ•ญ์—์„œ ๊ธฐ๋‹ค๋ฆฌ๋‹ค ๋ฐฅ๋จน๊ณ  ๋งฅ์ฃผํ•œ์ž” ํ•  ์ƒ๊ฐ์ด๋ผ๋ฉด ์ €๋Š” ์ฐจ๋ผ๋ฆฌ ๋ผ์šด์ง€๋ฅผ ์ด์šฉํ•˜๋Š”๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค.(๊ฐœ์ธ์ ์ธ ์ƒ๊ฐ) ๊ฐ€๊ฒฉ์„ ๊ณ„์‚ฐํ•ด๋ณด๋ฉด ๋ณ„๋กœ ์ฐจ์ด๊ฐ€ ์—†์„๋“ฏ.

 

๋ผ์šด์ง€ ์ด์šฉ๊ถŒ์€ ๊ผญ ๋ฌด๋ฃŒ์ด์šฉ ์นด๋“œ๊ฐ€ ์•„๋‹ˆ์—ฌ๋„ ๋งˆ์ด๋ฆฌ์–ผํŠธ๋ฆฝ, ํด๋ฃฉ๋“ฑ์—์„œ ํ• ์ธ๋œ ๊ฐ€๊ฒฉ์œผ๋กœ ํŒ๋งคํ•˜๋ฉฐ, ์™ ๋งŒํ•œ ์นด๋“œ์‚ฌ์—์„œ 30% ํ• ์ธ์„ ํ•ด์ฃผ๋‹ˆ ์ž˜ ํ™œ์šฉํ•ด์„œ ์ด์šฉํ•˜๋Š”๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค.

 

 

 

๊ณตํ•ญ์ถœ๋ฐœ๊ณผ ํ˜ธํ…”ํ”ฝ์—…

 

์ด๋ฒˆ์— ์ด์šฉํ•œ ํ•ญ๊ณต์€ ํ‹ฐ์›จ์ด ํ•ญ๊ณต.

์ €๋… 11:45๋ถ„ ๋‚˜ํŠธ๋ž‘ ๊นœ๋ผ์ธ ๊ณตํ•ญ ๋„์ฐฉ.

๋ณดํ†ต ์ฐพ์•„๋ณด๋‹ˆ ์–ผ๋ฆฌ๋ชจ๋‹ํˆฌ์–ด๋ฅผ ์‹ ์ฒญํ•˜๋˜์ง€, 0.5๋ฐ• ํ˜ธํ…” ํˆฌ์ˆ™์„ ์•Œ์•„๋ณด์ง€๋งŒ, ๋‚œ Virgoํ˜ธํ…” ๊ฐ€์„ฑ๋น„๊ฐ€ ์ข‹์•„์„œ ์ด๋‚ ์„ ํฌํ•จํ•ด์„œ 4๋ฐ•์„ ์‹ ์ฒญํ—€๊ณ , ํ˜ธํ…”์— ๋ฉ”์ผ์„ ๋ณด๋‚ด ํ”ฝ์—…์„œ๋น„์Šค ์š”์ฒญ์„ ํ•ด๋‘์—ˆ๋‹ค.

์ €๋… ๋Šฆ๊ฒŒ ๋„์ฐฉํ•œ ๊นœ๋ผ์ธ ๊ณตํ•ญ์—์„œ ํƒ์‹œ๋Š” ๋ถ€๋ฅด๋Š”๊ฒŒ ๊ฐ€๊ฒฉ์ด๋ผ, ์ฒ˜์Œ๋ถ€ํ„ฐ ์ŠคํŠธ๋ ˆ์Šค ๋ฐ›๊ธฐ ์‹ซ์–ด์„œ ํ˜ธํ…” ํ”ฝ์—…์„œ๋น„์Šค๋กœ ์‹œ๋‚ด ์ด๋™ ํ•ด๊ฒฐ.

 

 ํ˜ธํ…”

 

4๋ฐ•์„ ์˜ˆ์•ฝํ•œ  Virgo ํ˜ธํ…”.

https://maps.app.goo.gl/NMqqoB2M47AS7TSu5

 

๋ฒ„๊ณ ํ˜ธํ…”๋‚˜ํŠธ๋ž‘ · 39-41 Nguyแป…n Thแป‹ Minh Khai, Tân Lแบญp, Nha Trang, Khánh Hòa 650000 ๋ฒ ํŠธ๋‚จ

โ˜…โ˜…โ˜…โ˜…โ˜† · ํ˜ธํ…”

www.google.com

์ฒดํฌ์ธ : 14:00
์ฒดํฌ์•„์›ƒ : 12:00
์กฐ์‹ : 06:00 ~ 10:00 (3์ธต)
์‹œ๋‚ด์ค‘์‹ฌ์œผ๋กœ ํ˜ธํ…”์ฃผ๋ณ€์œผ๋กœ ์ธํ”„๋ผ ์ž˜ ๋˜์–ด ์žˆ์œผ๋ฉฐ,
์™ ๋งŒํ•œ ๊ณณ์€ ๊ฑธ์–ด์„œ ๊ฐˆ์ˆ˜ ์žˆ๋Š” ์œ„์น˜์™€ ๊ฐ€์„ฑ๋น„๊ฐ€ ์ข‹์€ ํ˜ธํ…”.
๊ฐœ์ธ์ ์ธ ์ƒ๊ฐ์œผ๋กœ ๋ ˆ๊ฐˆ๋ฆฌ์•„๊ณจ๋“œํ˜ธํ…”์„ ๋” ์ถ”์ฒœํ•œ๋‹ค. ( ๋งˆ์ง€๋ง‰ 1๋ฐ•์„ ํ–ˆ๋Š”๋ฐ, ์—ฌ๊ธฐ ์„œ๋น„์Šค๊ฐ€ ๋” ์ข‹์€๋“ฏ.)

ํ˜ธํ…”์กฐ์‹ ๋ฐ ์˜ฅ์ƒ ์ˆ˜์˜์žฅ ํ’๊ฒฝ

 

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

์กฐ์‹๋„ ๋ง˜์— ๋“ค์—ˆ์œผ๋ฉฐ(ํŠนํžˆ ์Œ€๊ตญ์ˆ˜ ์œก์ˆ˜๋Š” ์•„์ฃผ ์ง„ํ•ด์„œ ์ข‹์•˜์Œ) , ์ค‘๊ตญ ๋‹จ์ฒด์—ฌํ–‰์ด ์™”๋Š”์ง€ ์ค‘๊ตญ์‚ฌ๋žŒ๋“ค์ด ๋งŽ์•˜๋‹ค.

1๋ฐ•์— 3๋งŒ์›๋Œ€์— 5์„ฑ๊ธ‰ ์„œ๋น„์Šค, ์œ„์น˜๊นŒ์ง€ ์ข‹์•„์„œ 4๋ฐ•๋™์•ˆ ํŽธํžˆ ์ง€๋‚ผ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

์˜ค์ž๋งˆ์ž, ๋ฐ”๋กœ ์•ž ๋งˆํŠธ์— ๊ฐ€์„œ ๋ฒ ํŠธ๋‚จ ์ˆ  ์ข…๋ฅ˜๋Š” ๋ชจ๋‘ ๊ตฌ๋งค

ํ˜ผ์ž์—ฌํ–‰์ด๋ผ ์‚ฌ์ง„๋งŒ ์ฐ๋Š” ๋Ÿญ์…”๋ฆฌ ๋ฆฌ์กฐํŠธ/ํ˜ธํ…”์€ ํ•„์š” ์—†๋‹ค.

 

 

์ž~~์ด์   ๋†€์•„๋ณผ๊นŒ!!

-๋‹ค์ŒํŽธ์— ๊ณ„์†-

๋ณธ๋‚ด์šฉ์€ ํ•„์ž์˜ " React&Python์œผ๋กœ ์›น๊ฐœ๋ฐœ(๋งํฌ์„œ๋น„์Šค)" ์นดํ…Œ๊ณ ๋ฆฌ ๊ฐœ๋ฐœ์†Œ์Šค์— ๊ณ„์† ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

( ์†Œ์Šค ํด๋”๋Š” ์œ„ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์ž˜ ์ฝ์–ด๋ณด๊ณ ,  git ์†Œ์Šค๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. )

 

react-keycloak ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ useKeycloak Hook๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ˜ธ์ถœ

 

react-keycloak ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ useKeycloak Hook๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ˜ธ์ถœ

๋ณธ๋‚ด์šฉ์€ ํ•„์ž์˜ " React&Python์œผ๋กœ ์›น๊ฐœ๋ฐœ(๋งํฌ์„œ๋น„์Šค)" ์นดํ…Œ๊ณ ๋ฆฌ ๊ฐœ๋ฐœ์†Œ์Šค์— ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ( ์†Œ์Šค ํด๋”๋Š” ์œ„ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์ž˜ ์ฝ์–ด๋ณด๊ณ , git ์†Œ์Šค๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. ) ์ง€๋‚œํŽธ

firstvalue.tistory.com

 

์ด์ „๊ธ€์—์„œ Keycloak ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋กœ๊ทธ์ธ์„ ํ•˜๋„๋ก ์ฒ˜๋ฆฌํ–ˆ์—ˆ๋‹ค.

 

์ด๋ฒˆ์—๋Š” Keycloak ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋ฅผ ํ†ตํ•ด ๋กœ๊ทธ์ธํ›„ token ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€ Keycloak์˜ ์ €์žฅ๋œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ํ™•์ธ ํ•ด๋ณด์ž. 

 

์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด์„œ ์‹ ๊ทœ ์†Œ์Šค๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

 

์‹ ๊ทœ 1๊ฐœ์™€ ๊ธฐ์กด ์†Œ์Šค๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  react์— ์‹ ๊ทœ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•  ์˜ˆ์ •์ด๋‹ค.

1. userinfo.jsx (์‹ ๊ทœ)

2. menu.jsx (์ˆ˜์ •)

3. ์‹ ๊ทœ ํŒจํ‚ค์ง€ ์„ค์น˜ :  yarn add base-64

 

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

JWT๋Š” ๊ฐ๊ฐ์˜ ๊ตฌ์„ฑ์š”์†Œ๊ฐ€ ์ (.) ์œผ๋กœ ๊ตฌ๋ถ„์ด ๋˜์–ด ์žˆ์œผ๋ฉฐ , ๊ตฌ์„ฑ์š”์†Œ๋Š” HEADER(ํ—ค๋”).PAYLOAD(๋‚ด์šฉ).SIGNATURE(์„œ๋ช…) ํ˜•ํƒœ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค.  

 

JWT ๋””์ฝ”๋”ฉ ํ™•์ธ ์‚ฌ์ดํŠธ : jwt.io

 

์•„๋ž˜ ์†Œ์Šค์—์„œ๋Š” ์ถ”๊ฐ€์ ์œผ๋กœ PAYLOAD ๋””์ฝ”๋”ฉ๊นŒ์ง€ ํฌํ•จํ•œ๋‹ค.

โŒ˜ userinfo.jsx ( ์œ„์น˜: linkservice/client/src/component/userinfo.jsx )

---------------------------------------------------------------------------------------------------

์•„๋ž˜ ์†Œ์Šค๋ฅผ ๋ฐ˜์˜ํ•œ๋‹ค.

import { useKeycloak } from "@react-keycloak/web";
import base64 from "base-64";
import React from "react";

const UserInfo = () => {

    const { keycloak } = useKeycloak();
    let payload, decodingInfo;
    
    const accessToken = keycloak.idToken;

    if (accessToken) {
      payload = accessToken.split('.')[1];
      decodingInfo = base64.decode(payload);
    }

  return (
    <div className="container">
      <div >

        <p>idToken ์ •๋ณด</p>
        <textarea cols="100" rows="8" value={keycloak.idToken} readOnly>
        </textarea>

        <p>token ์ •๋ณด</p>
        <textarea cols="100" rows="8" value={keycloak.token} readOnly>
        </textarea>

        <p>payload ์ •๋ณด</p>
        <textarea cols="100" rows="8" value={decodingInfo} readOnly>
        </textarea>

      </div>    
    </div>    
   )
}
export default UserInfo

 

๊ฐ„๋‹จํžˆ ์„ค๋ช…ํ•˜๋‹ค๋ฉด,

useKeycloak Hook๋ฅผ ํ†ตํ•ด keycloak ๊ฐœ๋ณ„ ๋ณ€์ˆ˜์— ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ํ• ๋‹นํ•˜๊ณ , accessToken๊ฐ’์„ ๋ฐ›์•„์˜จ๋‹ค.

import { useKeycloak } from "@react-keycloak/web";
//....
//....
//....
      const { keycloak } = useKeycloak();
      
// keycloak๋ฅผ ํ†ตํ•ด ๋กœ๊ทธ์ธ์ด ๋˜์—ˆ์„ ๊ฒฝ์šฐ ํ† ํฐ๊ฐ’์„ ๋ฐ›๊ฒŒ ๋œ๋‹ค.
//.....
      const accessToken = keycloak.idToken;

 

accessToken๊ฐ’์ด ์žˆ์„ ๊ฒฝ์šฐ, ์ฆ‰ ์ •์ƒ ๋กœ๊ทธ์ธ์ด ๋˜์—ˆ์„๋•Œ, JWT๊ตฌ์กฐ์ƒ splitํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด PAYLOAD๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ

base64๋กœ ๋‹ค์‹œ decode๋ฅผ ํ•œ๋‹ค. 

    if (accessToken) {
      payload = accessToken.split('.')[1];
      decodingInfo = base64.decode(payload);
    }

 

 

โŒ˜ menu.jsx ( ์œ„์น˜: linkservice/client/src/component/menu.js )

---------------------------------------------------------------------------------------------------

์ด์ „ menu.jsx ์†Œ์Šค๋ฅผ ์ž„์‹œ๋กœ ์ˆ˜์ •ํ•˜์—ฌ ์ •๋ณด๋ฅผ ํ™•์ธํ•ด๋ณด์ž.

import HomeIcon from '@mui/icons-material/Home';
import SettingsIcon from '@mui/icons-material/Settings';
import { useNavigate } from 'react-router-dom';
// useKeycloak import
import { useKeycloak } from "@react-keycloak/web";
import UserInfo from './userinfo';

export default function MenuIcon(props) {
    const navigator = useNavigate()
    
    // Using Object destructuring
    const { keycloak } = useKeycloak();

    const handleIcon = (e) => {
        switch (e) {
            case 'A' : 
                // keycloak์— ๋กœ๊ทธ์ธ์ด ๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค๋ฉด, keycloak ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ˜ธ์ถœ
                if (!keycloak.authenticated) {
                    alert("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•œ ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค.")
                    // keycloak ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ˜ธ์ถœ์‹œ redirectUri๋ฅผ ๊ฐœ์ธํ™”๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
                    keycloak.redirectUri= window.location.href + '/manager'
                    // keycloak ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ˜ธ์ถœ
                    keycloak.login();
                }

            return navigator('/manager')
            
            case 'M' : return navigator('/')
            default : return navigator('/')
        }
    }
    return (
<>
      <div className='menu-wrap'>
        <div className='menu-box'>
          <HomeIcon fontSize='large' color="secondary"
             onClick={() => handleIcon('M')} 
          />
          <SettingsIcon fontSize='large' color="secondary"
             onClick={() => handleIcon('A')} 
          /> 
        </div>
      </div>      

//์ž„์‹œ๋กœ ๋ฐ˜์˜.. 
//๋กœ๊ทธ์ธ๋˜๋ฉด token๊ฐ’๊ณผ payload๊ฐ’์„ ๋ณด์—ฌ์ค€๋‹ค.

      { keycloak.idToken ?
          <UserInfo /> 
        : null
      }
</>
    )
}

 

menu.jsx ๋งˆ์ง€๋ง‰์— UserInfo ์ปจํฌ๋„ŒํŠธ๋ฅผ ์ž„์‹œ๋กœ ๋ฐ˜์˜ํ•œ๋‹ค.

 

      { keycloak.idToken ?
          <UserInfo /> 
        : null
      }

 

 

๊ทธ๋Ÿผ ๋กœ๊ทธ์ธ์„ ํ†ตํ•ด idToken๊ณผ Token , ๊ทธ๋ฆฌ๊ณ  payload๊ฐ’์„ ์ถœ๋ ฅํ•ด๋ณธ๋‹ค.

 

 

๋งํฌ์„œ๋น„์Šค ์‚ฌ์ดํŠธ์— ๋กœ๊ทธ์ธ์ด ๋˜์–ด token๊ฐ’๊ณผ playload์ •๋ณด๋ฅผ ๋ณผ์ˆ˜ ์žˆ๋‹ค.

๋˜ํ•œ, ๊ฐœ๋ฐœ์ž๋ชจ๋“œ console ์—์„œ๋Š” auth token๊ณผ refresh token์„ ๊ณ„์†์ ์œผ๋กœ ์ƒ์„ฑ๋จ์„ ํ™•์ธํ• ์ˆ˜ ์žˆ๋‹ค.

 

๋™์ผํ•˜๊ฒŒ  jwt.io ์‚ฌ์ดํŠธ์—์„œ๋„ ํ™•์ธํ•ด๋ณธ๋‹ค. 

๋‘๊ฐœ๋ฅผ ๋น„๊ตํ•ด๋ณด๋ฉด ๋™์ผํ•œ ๊ฒฐ๊ณผ๊ฐ’์ž„์„ ์•Œ์ˆ˜ ์žˆ๋‹ค.

 

 

๊ทธ๋Ÿผ token์ •๋ณด์—์„œ ๋กœ๊ทธ์ธ ์•„์ด๋””, ์ด๋ฆ„ ๋“ฑ ์ถœ๋ ฅํ• ๋•Œ๋Š” idTokenParsed ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•˜๋ฉด ํ•œ๋‹ค.

 

์•„์ด๋”” ์ •๋ณด๋ฅผ ์•Œ๊ณ  ์‹ถ์„๋•Œ, 

keycloak.idTokenParsed.preferred_username

 

์œ„์™€ ๊ฐ™์€ ํ˜•ํƒœ๋กœ payload๊ฐ’์„ ์ค‘ ํ•˜๋‚˜๋ฅผ ๊ฐ€์ ธ์˜ค๋ฉด ๋œ๋‹ค.

 

 

๊ทธ๋Ÿผ ๋‹ค์ŒํŽธ์— ๊ณ„์†..................................

 

 

 

๋ณธ๋‚ด์šฉ์€ ํ•„์ž์˜ " React&Python์œผ๋กœ ์›น๊ฐœ๋ฐœ(๋งํฌ์„œ๋น„์Šค)" ์นดํ…Œ๊ณ ๋ฆฌ ๊ฐœ๋ฐœ์†Œ์Šค์— ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

( ์†Œ์Šค ํด๋”๋Š” ์œ„ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์ž˜ ์ฝ์–ด๋ณด๊ณ ,  git ์†Œ์Šค๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. )

 

์ง€๋‚œํŽธ์—์„œ๋Š” http://localhost:3000 ์ ‘๊ทผ์‹œ ๋ฐ”๋กœ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๊ฐ€ ๋‚˜์˜ค๋„๋ก ์ฒ˜๋ฆฌํ–ˆ๊ณ ,

์ด๋ฒˆํŽธ์—๋Š” admin ๋ฒ„ํŠผ ํด๋ฆญ ํ˜น์€ ๋กœ๊ทธ์ธ ์•„์ด์ฝ˜์„ ํด๋ฆญ ํ• ๋•Œ Keycloak ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๊ฐ€ ๋‚˜์˜ค๋„๋ก ์ˆ˜์ •ํ•ด๋ณด์ž.

 

์ด์ „์— material-icons๋ฅผ ํ†ตํ•œ menu.jsx๋ฅผ ์ œ์ž‘ํ–ˆ๋‹ค. ์•„๋ž˜ ์ด์ „๊ธ€ ์ฐธ๊ณ ๋ฐ”๋ž๋‹ˆ๋‹ค.

 

#14. material-icons ๋ฅผ ํ†ตํ•œ ๋ฉ”๋‰ดํ™”๋ฉด

 

#14. material-icons ๋ฅผ ํ†ตํ•œ ๋ฉ”๋‰ดํ™”๋ฉด

๋“ฑ๋กํ™”๋ฉด์€ ์™„๋ฃŒ๋˜์—ˆ์œผ๋‹ˆ, ์ด์   ํŽธ์ง‘ํ™”๋ฉด์„ ๊ฐœ๋ฐœํ•ด ๋ณด์ž. #13. ๋“ฑ๋กํ™”๋ฉด์—์„œ ๋‚ด์šฉ ์ €์žฅ ๋ฐ ์ด๋ฏธ์ง€ ์ €์žฅ ๊ธฐ๋Šฅ ๊ตฌํ˜„ #13. ๋“ฑ๋กํ™”๋ฉด์—์„œ ๋‚ด์šฉ ์ €์žฅ ๋ฐ ์ด๋ฏธ์ง€ ์ €์žฅ ๊ธฐ๋Šฅ ๊ตฌํ˜„ #12. React์—์„œ Drag & Drop์„

firstvalue.tistory.com

 

๊ธฐ์กด menu.jsx ์— SettingsIcon ํด๋ฆญ์‹œ Keycloak์˜ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๋„๋ก ์ˆ˜์ •ํ•œ๋‹ค.

 

์ด 3๊ฐœ์˜ ์†Œ์Šค๋ฅผ ์ˆ˜์ •ํ•  ์˜ˆ์ •์ด๋‹ค.

1.  menu.jsx

2. App.js

3. index.js

โŒ˜ menu.jsx ( ์œ„์น˜: linkservice/client/src/component/menu.jsx )

---------------------------------------------------------------------------------------------------

์šฐ์„  react-keycloak/web ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ตฌ์„ฑ์š”์†Œ๊ฐ€ Keycloak์— Accessํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ 

useKeycloak Hook๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.

import { useKeycloak } from "@react-keycloak/web";

 

useKeycloak๊ตฌ์กฐํ™”๋œ ๊ฐ์ฒด๋ฅผ Destructuring ํ•˜์—ฌ ๊ฐœ๋ณ„์ ์ธ ๋ณ€์ˆ˜์— ํ• ๋‹นํ•œ๋‹ค.

   const { keycloak } = useKeycloak();

์ด๋ ‡๊ฒŒ ํ•จ์œผ๋กœ์จ, ๋ชจ๋“  keycloak ๋ฉ”์„œ๋“œ ๋ฐ ๋ณ€์ˆ˜์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

handleIcon ์ด๋ฒคํŠธ์— keycloak.authenticated (์ธ์ฆ์ด๋˜์—ˆ๋Š”์ง€) ์— ๋”ฐ๋ผ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋ฅผ ํ˜ธ์ถœํ•ด๋ณด์ž.

                // keycloak์— ๋กœ๊ทธ์ธ์ด ๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค๋ฉด, keycloak ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ˜ธ์ถœ
                if (!keycloak.authenticated) {
                    alert("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•œ ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค.")
                    // keycloak ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ˜ธ์ถœ
                    keycloak.login();
                }

์œ„์™€ ๊ฐ™์ด handle ์ด๋ฒคํŠธ์— ๋”ฐ๋ผ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋ฅผ ํ˜ธ์ถœํ• ์ˆ˜์žˆ์œผ๋ฉฐ,

์ด๋ฏธ ๋กœ๊ทธ์ธ์ด ๋˜์—ˆ๋‹ค๋ฉด ๊ด€๋ฆฌ์ž ํ™”๋ฉด์œผ๋กœ ๋ฐ”๋กœ ์ด๋™์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

 

์œ„ ๋‚ด์šฉ์—์„œ ์ถ”๊ฐ€์ ์œผ๋กœ keycloak.login() ํ›„ ๊ธฐ๋ณธ redirect-uri ์ด http://localhost:3000 ์ด๋‹ค. 

์ฆ‰, ๊ด€๋ฆฌ์ž ํ™”๋ฉด์œผ๋กœ ์ด๋™์‹œ ๋กœ๊ทธ์ธ ํ›„ ๋ฐ”๋กœ ์ด๋™์ด ์•ˆ๋˜๊ณ , ์ดˆํ™”๋ฉด์ด ๋‚˜์˜จ๋‹ค๋Š” ๋œป์ด๋‹ค.

๊ทธ๋ž˜์„œ redirect-uri๋ฅผ ๊ด€๋ฆฌ์ž ํ™”๋ฉด์œผ๋กœ ์ง€์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

 

                    // keycloak ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ˜ธ์ถœ์‹œ redirectUri๋ฅผ ๊ฐœ์ธํ™”๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
                    keycloak.redirectUri= window.location.href + '/manager'

 

์ˆ˜์ •๋œ menu.jsx


import HomeIcon from '@mui/icons-material/Home';
import SettingsIcon from '@mui/icons-material/Settings';
import { useNavigate } from 'react-router-dom';
// useKeycloak import
import { useKeycloak } from "@react-keycloak/web";

export default function MenuIcon(props) {
    const navigator = useNavigate()
    
    // Using Object destructuring
    const { keycloak } = useKeycloak();

    const handleIcon = (e) => {
        switch (e) {
            case 'A' : 
                // keycloak์— ๋กœ๊ทธ์ธ์ด ๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค๋ฉด, keycloak ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ˜ธ์ถœ
                if (!keycloak.authenticated) {
                    alert("๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•œ ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค.")
                    // keycloak ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ˜ธ์ถœ์‹œ redirectUri๋ฅผ ๊ฐœ์ธํ™”๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
                    keycloak.redirectUri= window.location.href + '/manager'
                    // keycloak ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ˜ธ์ถœ
                    keycloak.login();
                }

            return navigator('/manager')
            
            case 'M' : return navigator('/')
            default : return navigator('/')
        }
    }
    return (
<>
      <div className='menu-wrap'>
        <div className='menu-box'>
          <HomeIcon fontSize='large' color="secondary"
             onClick={() => handleIcon('M')} 
          />
          <SettingsIcon fontSize='large' color="secondary"
             onClick={() => handleIcon('A')} 
          /> 
        </div>
      </div>      
</>
    )
}

 

โŒ˜ App.js ( ์œ„์น˜: linkservice/client/src/App.js )

---------------------------------------------------------------------------------------------------

์ด์ „ App.js ์†Œ์Šค๋‚ด์—์„œ๋Š” ReactKeycloakProvider์˜ initOptions ์„ ์ œ์™ธํ•œ๋‹ค.

 onEvent={onKeycloakEvent} ๋„ ์ œ์™ธํ•ด๋„ ๋˜๋‚˜, ๊ฐœ๋ฐœ์ž ๋ชจ๋“œ์—์„œ refressToken๊ฐ’ ํ™•์ธ ์ฐจ์›์—์„œ

๋ฐ˜์˜ํ•ด ๋‘”๋‹ค.

 

์ˆ˜์ •๋œ App.js 

import React from "react"
import { BrowserRouter, Route, Routes } from "react-router-dom"
import LinkMain from "./component/links"
import LinkCreateMain from "./component/links.create"
import ManagerHome from "./component/manager"
import MenuIcon from "./component/menu"
import LinksList from "./component/links.list"
import LinkEdit from "./component/links.edit"
import { ReactKeycloakProvider } from "@react-keycloak/web";
import keycloak, { onKeycloakEvent } from "./keycloak";
import PrivateRoute from "./PrivateRoute";
// import { onKeycloakEvent } from "./keycloak"

function App() {
  return (

    // ๊ธฐ์กด BrowserRouter ๋ฐ–์— ReactKeycloakProvider๋ฅผ ์„ ์–ธํ•œ๋‹ค. 
    // initOptions์— ์˜ํ•ด์„œ http://localhost:3000 ์œผ๋กœ ์ ‘์†์‹œ keycloak ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™๋œ๋‹ค.
    <ReactKeycloakProvider authClient={keycloak} onEvent={onKeycloakEvent} >
    {/* initOptions={initOptions} > ์ œ์™ธ์‹œํ‚จ๋‹ค. */}

      <BrowserRouter>    
        <Routes>
          <Route exact path="/" element={<LinkMain />} />  

          {/* PrivateRoute๋ฅผ ํ†ตํ•ด ๋กœ๊ทธ์ธ์ด ๋œ ์ƒํƒœ์—์„œ๋งŒ ํŽ˜์ด์ง€ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ์ฒ˜๋ฆฌ  */}
          <Route exact path="/create" element={<PrivateRoute> <LinkCreateMain /> </PrivateRoute>} />  
          <Route exact path="/manager" element={<PrivateRoute> <ManagerHome /> </PrivateRoute>} />  
          <Route exact path="/links/list" element={<PrivateRoute> <LinksList /> </PrivateRoute>} />  
          <Route exact path="/link/edit/:id" element={<PrivateRoute> <LinkEdit /> </PrivateRoute>} />  
        </Routes> 
        <MenuIcon />
      </BrowserRouter>

     </ReactKeycloakProvider>
  );
  
}
export default App;

 

โŒ˜ index.js ( ์œ„์น˜: linkservice/client/src/index.js )

---------------------------------------------------------------------------------------------------

๊ธฐ์กด index.js์—์„œ <React.StrictMode> ๋งŒ ์ œ์™ธํ•œ๋‹ค.

StrictMode๊ฐ€ ์žˆ์œผ๋ฉด http://localhost:3000 ์ ‘์†์‹œ ๋ฌดํ•œ๋กœ๋”ฉ์ด ๋˜๋ฏ€๋กœ, ํ•ด๋‹น StictMode๋ฅผ ๋นผ์ค€๋‹ค.

 

์ˆ˜์ •๋œ index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
// import './index.css';
import App from './App';
// import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  // <React.StrictMode>  ์ œ์™ธ์‹œํ‚จ๋‹ค.
    <App />
  // </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
// reportWebVitals();

๊ทธ๋Ÿผ ์„œ๋ฒ„๋‹จ๊ณผ ํด๋ผ์ด์–ธํŠธ๋‹จ์„ ์‹คํ–‰ํ•ด์„œ ์ •์ƒ์ ์œผ๋กœ ๋ฐ˜์˜์ด ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž.

 

linkservice ํ™”๋ฉด

์ •์ƒ์ ์ธ ํŽ˜์ด์ง€๊ฐ€ ๋‚˜์˜จ๋‹ค๋ฉด ํ•˜๋‹จ SettingIcon์„ ํด๋ฆญํ•œ๋‹ค.

linkservice ํ™”๋ฉด

ํ™•์ธ์„ ๋ˆ„๋ฅด๋ฉด Keycloak์˜ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๊ฐ€ ๋‚˜์˜ค๋Š”๊ฒƒ์„ ํ™•์ธํ• ์ˆ˜ ์žˆ๋‹ค.

keycloak ๊ธฐ๋ณธ ๋กœ๊ทธ์ธ ํ™”๋ฉด

 

๋‹ค์Œ์—๋Š” ๋กœ๊ทธ์ธํ›„ token๊ฐ’ ๋ฐ ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š”๊ฒƒ์„ ์•Œ์•„๋ณด์ž.

 

React ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ๋Š” ํ•„์ž์˜ " React&Python์œผ๋กœ ์›น๊ฐœ๋ฐœ(๋งํฌ์„œ๋น„์Šค)" ์นดํ…Œ๊ณ ๋ฆฌ ๊ฐœ๋ฐœ์†Œ์Šค์—
์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
( ์†Œ์Šค ํด๋”๋Š” ์œ„ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์ž˜ ์ฝ์–ด๋ณด๊ณ ,  git ์†Œ์Šค๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. )


1. ์‚ฌ์šฉ๋˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค

       โ–ถ๏ธŽ keycloak-js

       โ–ถ๏ธŽ @react-keycloak/web

 

์šฐ์„  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•œ๋‹ค.

์•„๋ž˜ ํฌ์ŠคํŒ…์—์„œ ํด๋”๊ตฌ์กฐ๋ฅผ ํ™•์ธํ•˜๊ณ ,  ํ„ฐ๋ฏธ๋‚ ์„ ์—ด์–ด ~>linserver/client ์—์„œ react ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•œ๋‹ค.

 

#4. ํ”„๋กœ์ ํŠธ ํด๋” ๊ตฌ์กฐ ๋ฐ ํ™˜๊ฒฝ์„ค์ •

 

#4. ํ”„๋กœ์ ํŠธ ํด๋” ๊ตฌ์กฐ ๋ฐ ํ™˜๊ฒฝ์„ค์ •

1.1. ํ”„๋กœ์ ํŠธ ํด๋” ๊ตฌ์กฐ ๋ฐ ํ™˜๊ฒฝ์„ค์ • ํ”„๋กœ์ ํŠธ ํด๋” ๊ตฌ์กฐ๋Š” ํ”„๋ก ํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ๋ฅผ ๋‚˜๋ˆ„๊ณ ์ž client / server๋กœ ๊ตฌ๋ถ„ ํ•œ๋‹ค. โŽฎํ”„๋กœ์ ํŠธ ํด๋” : linkserver/ |-------- client ( React ๊ตฌ์กฐ) |-------- server ( Python๊ตฌ

firstvalue.tistory.com

 

$ yarn add keycloak-js

$ yarn add @react-keycloak/web

2. React์— Keycloak ์„ค์ •

   โŒ˜ keyclaok.js ์œ„์น˜: linkservice/client/src/keycloak.js)

   ----------------------------------------------------------------------------------------------

keyclaok์„ค์ •์„ ์œ„ํ•ด์„œ keycloak.js ํŒŒ์ผ์„ ์ƒ์„ฑํ•œ๋‹ค.

import Keycloak from "keycloak-js";

//์ด์ „ ํฌ์ŠคํŒ…์˜ Keycloak ์‚ฌ์šฉ์ž ์•„์ด๋””์™€ ํด๋ผ์ด์–ธํŠธ ์ƒ์„ฑ์„ ์ฐธ๊ณ ํ•˜์—ฌ ์•„๋ž˜ ์„ค์ •
const keycloak = new Keycloak({
    realm: 'aip',
    url: 'http://localhost:8080/',
    clientId: 'testClient',
});

// initOption ์ง€์ •
export const initOptions = {
    onLoad: 'login-required',
    checkLoginIframe: false,
  };

export const onKeycloakEvent = (event, error) => {
  // console.log('keycloak event ', event, error);
  switch (event) {
    case 'onAuthLogout':
      keycloak.logout();
      break;
    case 'onAuthRefreshError':
      keycloak.logout();
      break;
    case 'onAuthRefreshSuccess':
    //๊ฐœ๋ฐœ๊ณ„์—์„œ๋งŒ ์‚ฌ์šฉ, ์‹ค์ œ ์šด์˜๊ณ„์—์„œ๋Š” ์ œ์™ธ
      console.log('auth token:  ' + keycloak.token);
      console.log('refresh token:  ' + keycloak.refreshToken);
      break;
    default:
      break;
  }
};

export default keycloak;


const keycloak = new Keycloak({
    realm: 'aip',
    url: 'http://localhost:8080/',
    clientId: 'testClient',
});

ํ•ด๋‹น ์„ค์ •์€ ์•„๋ž˜ ์ด๋ฏธ์ง€์ฒ˜๋Ÿผ keycloak ์˜ Client์—์„œ ์˜ค๋ฅธ์ชฝ ์ƒ๋‹จ Action ํ•˜์œ„ ํ™”์‚ดํ‘œ๋ฅผ ํด๋ฆญํ•˜๋ฉด "Download adapter config ํ™”๋ฉด์—์„œ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ( reaml, url, clientid ํ‚ค ๋ช…์นญ์ด ๋‹ค๋ฅด์ง€๋งŒ ๋ฌธ์ œ์—†๋‹ค. )

 

initOptions๊ณผ onKeycloakEvent๋Š” App.js์—์„œ ReactKeycloakProvider ์— ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์„ ์–ธํ•œ๋‹ค.

 

initOptions

keycloak ๋กœ๊ทธ์ธ๋ฐฉ์‹์— ๋Œ€ํ•ด์„œ ์„ค์ •์œผ๋กœ onLoad

  • login-required : ์‚ฌ์šฉ์ž๊ฐ€ Keycloak์— ๋กœ๊ทธ์ธํ•œ ๊ฒฝ์šฐ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ธ์ฆํ•˜๊ณ  ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋ฅผ ํ‘œ์‹œ. 
  • check-sso : ์ด๋ฏธ ๋กœ๊ทธ์ธํ•œ ๊ฒฝ์šฐ์—๋งŒ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ธ์ฆํ•˜๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋ธŒ๋ผ์šฐ์ €๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ๋‹ค์‹œ ๋ฆฌ๋””๋ ‰์…˜๋˜๊ณ  ์ธ์ฆ๋˜์ง€ ์•Š์€ ์ƒํƒœ๋กœ ์œ ์ง€.

์—ฌ๊ธฐ๋Š” App.js์— ReactKeycloakProvider ํ˜•ํƒœ๋กœ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด์„œ login-required ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.

  ( ์ฆ‰, ํŠน์ •ํŽ˜์ด์ง€ ์ด๋™์‹œ ๋กœ๊ทธ์ธ์ด ์•ˆ๋˜์–ด ์žˆ์œผ๋ฉด ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋ฅผ ํ‘œ์‹œํ•˜๋„๋ก ์ฒ˜๋ฆฌ )

 

onKeycloakEvent

keycloak event๋ฅผ ์„ค์ •ํ•˜๋ฉฐ, Provider์— ์ง€์ •ํ•˜์—ฌ event์‹œ ๋งˆ๋‹ค ์ฒ˜๋ฆฌ ๋‚ด์šฉ์„ ์„ ์–ธํ•œ๋‹ค. ์œ„๋Š” default๊ฐ’์œผ๋กœ ๊ทธ๋Œ€๋กœ ๋ฐ˜์˜ํ•ด๋„ ๋ฌด๋ฐฉํ•˜๋‹ค.

 

3. PrivateRoute.js ์ƒ์„ฑ

   โŒ˜ PrivateRoute.js ์œ„์น˜: linkservice/client/src/PrivateRoute.js)

   ----------------------------------------------------------------------------------------------

Keycloak์— ๋กœ๊ทธ์ธ์ด ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ ์ž Route๋ฅผ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ƒ์„ฑํ•œ๋‹ค.

import { useKeycloak } from "@react-keycloak/web";

const PrivateRoute = ({ children }) => {
  const { keycloak } = useKeycloak();

  //keycloak์— ๋กœ๊ทธ์ธ์ด ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
  const isLoggedIn = keycloak.authenticated;

  return isLoggedIn ? children : null;
};

export default PrivateRoute;

 

3. App.js ์— ReactKeyclaokProvider ์™€ PrivateRoute ๋ฐ˜์˜ 

   โŒ˜ App.js ์œ„์น˜: linkservice/client/src/App.js)

   ----------------------------------------------------------------------------------------------

๊ธฐ์กด App.js ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•œ๋‹ค.

import React from "react"
import { BrowserRouter, Route, Routes } from "react-router-dom"
import LinkMain from "./component/links"
import LinkCreateMain from "./component/links.create"
import ManagerHome from "./component/manager"
import MenuIcon from "./component/menu"
import LinksList from "./component/links.list"
import LinkEdit from "./component/links.edit"

//์•„๋ž˜ ๋ถ€๋ถ„์ด ์ถ”๊ฐ€๋˜์—ˆ๋‹ค.
import { ReactKeycloakProvider } from "@react-keycloak/web";
import keycloak , { initOptions, onKeycloakEvent } from "./keycloak";
import PrivateRoute from "./PrivateRoute";

function App() {
  return (

    // ๊ธฐ์กด BrowserRouter ๋ฐ–์— ReactKeycloakProvider๋ฅผ ์„ ์–ธํ•œ๋‹ค. 
    // initOptions์— ์˜ํ•ด์„œ http://localhost:3000 ์œผ๋กœ ์ ‘์†์‹œ keycloak ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™๋œ๋‹ค.
    <ReactKeycloakProvider authClient={keycloak} 
    initOptions={initOptions} onEvent={onKeycloakEvent}>

      <BrowserRouter>    
        <Routes>
          <Route exact path="/" element={<LinkMain />} />  

          {/* PrivateRoute๋ฅผ ํ†ตํ•ด ๋กœ๊ทธ์ธ์ด ๋œ ์ƒํƒœ์—์„œ๋งŒ ํŽ˜์ด์ง€ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ์ฒ˜๋ฆฌ  */}
          <Route exact path="/create" element={<PrivateRoute> <LinkCreateMain /> </PrivateRoute>} />  
          <Route exact path="/manager" element={<PrivateRoute> <ManagerHome /> </PrivateRoute>} />  
          <Route exact path="/links/list" element={<PrivateRoute> <LinksList /> </PrivateRoute>} />  
          <Route exact path="/link/edit/:id" element={<PrivateRoute> <LinkEdit /> </PrivateRoute>} />  
        </Routes> 
        <MenuIcon />
      </BrowserRouter>

     </ReactKeycloakProvider>
  );
  
}
export default App;

๊ทธ๋Ÿผ http://localhost:3000 ์œผ๋กœ ์ ‘์†ํ•˜๋ฉด Keycloak์˜ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธ ํ• ์ˆ˜ ์žˆ๋‹ค.

์—ฌ๊ธฐ์—์„œ ๊ธฐ์กด์—(์ด์ „ ํฌ์ŠคํŠธ์—์„œ) ์ƒ์„ฑํ–ˆ๋˜ ์‚ฌ์šฉ์ž๋กœ ๋กœ๊ทธ์ธ์„ ํ•˜๋ฉด ๋œ๋‹ค.

 

keycloak ๊ธฐ๋ณธ ๋กœ๊ทธ์ธํ™”๋ฉด

 

๋‹ค์ŒํŽธ์—๋Š” http://localhost:3000 ์ ‘๊ทผ์‹œ ๋ฐ”๋กœ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๊ฐ€ ๋‚˜์˜ค์ง€ ์•Š๊ณ ,

admin ๋ฒ„ํŠผ ํด๋ฆญ ํ˜น์€ ๋กœ๊ทธ์ธ ์•„์ด์ฝ˜์„ ํด๋ฆญ ํ• ๋•Œ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๊ฐ€ ๋‚˜์˜ค๋„๋ก ์ˆ˜์ •ํ•ด๋ณด์ž.

 

KeyCloak ์šฉ์–ด ์ •๋ฆฌ

  • OIDC : OAuth ๊ฐ€ ๊ถŒํ•œ ๋ถ€์—ฌ๋งŒ ๋‹ค๋ฃจ๋Š” ๊ฒƒ์ด๋ผ๋ฉด OIDC ๋Š” OAuth ๋ฅผ ํฌํ•จํ•˜์—ฌ ์ธ์ฆ๊ณผ ๊ถŒํ•œ๋ถ€์—ฌ๋ฅผ ๋ชจ๋‘ ํฌํ•จํ•œ ๊ฒƒ์ด๋‹ค. SSO ์˜ ๊ตฌํ˜„์„ ์œ„ํ•œ ์ˆ˜๋‹จ์œผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.
  • Realm : ์ธ์ฆ, ๊ถŒํ•œ ๋ถ€์—ฌ๊ฐ€ ์ ์šฉ๋˜๋Š” ๋ฒ”์œ„๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋‹จ์œ„์ด๋‹ค. SSO ๋ฅผ ์ ์šฉํ•œ๋‹ค๊ณ  ํ–ˆ์„๋•Œ ํ•ด๋‹น SSO ๊ฐ€ ์ ์šฉ๋˜๋Š” ๋ฒ”์œ„๋Š” Realm ๋‹จ์œ„์ด๋‹ค.
  • Client : ์ธ์ฆ, ๊ถŒํ•œ ๋ถ€์—ฌ ํ–‰์œ„๋ฅผ ๋Œ€ํ–‰ํ•˜๋„๋ก ๋งก๊ธธ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋‹จ์œ„์ด๋‹ค. ๊ทธ ๋‹จ์œ„๋Š” ์›น์‚ฌ์ดํŠธ ํ˜น์€ REST API ๋ฅผ ์ œ๊ณตํ•˜๋Š” ์„œ๋น„์Šค๋„ ๋  ์ˆ˜ ์žˆ๋‹ค. ํ•˜๋‚˜์˜ Realm ์— n๊ฐœ์˜ Client ๋ฅผ ์ƒ์„ฑ, ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • User : Client ์— ์ธ์ฆ์„ ์š”์ฒญํ•  ์‚ฌ์šฉ์ž๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค. ํ•˜๋‚˜์˜ Realm ์—๋Š” Realm ์— ์ข…์†๋œ n๊ฐœ์˜ User ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ User ๋Š” Username, Email, FirstName, LastName ์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์ง€๋งŒ Custom User Attribute ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์›ํ•˜๋Š” ์†์„ฑ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Role : User ์—๊ฒŒ ๋ถ€์—ฌํ•  ๊ถŒํ•œ ๋‚ด์šฉ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค. ์—ฌ๊ธฐ์—๋Š” Keycloak ์˜ REST API ๋ฅผ ์‚ฌ์šฉํ•  ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ์ •์˜ํ•œ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

 

Keycloak ์‚ฌ์šฉ์„ ์œ„ํ•œ ์„ค์ •

Keycloak ์‚ฌ์šฉ์„ ์œ„ํ•ด์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์„ค์ •์„ ํ•„์š”ํ•˜๋‹ค.

  1. Realm ์ƒ์„ฑ
  2. User ์ƒ์„ฑ
  3. Client ์ƒ์„ฑ

Realm ์€ ์ดˆ๊ธฐ์—๋Š” Master Realm ๋งŒ ์กด์žฌํ•œ๋‹ค. Master ๊ฐ€ ์•„๋‹Œ ์‹ค์ œ ์‚ฌ์šฉ์„ ์œ„ํ•œ ๋ ๋ฆ„์„ ์ƒ์„ฑํ•ด์ค€๋‹ค.

Realm ๊ธฐ์ค€ ํŒ๋‹จ์€ ๋‹จ์ผ ํšŒ์‚ฌ ๋‹จ์œ„๋ผ๊ณ  ์ƒ๊ฐ ํ•˜๋ฉด ๋ ๋“ฏ ํ•˜๋‹ค. 

   ์˜ˆ๋ฅผ ๋“ค์–ด ํฌ์Šค์ฝ” ๊ทธ๋ฃน์ธ ํฌ์Šค์ฝ”, ํฌ์Šค์ฝ”DX, ํฌ์Šค์ฝ”์ธํ„ฐ๋‚ด์…”๋„, ํฌ์Šค์ฝ”E&C ๊ฐ€ Keycloak๋ฅผ ๋„์ž…ํ•˜๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด
   Realm๋Š” ์„œ๋กœ๋‹ค๋ฅธ 4๊ฐœ๊ฐ€ ํ•„์š”ํ• ๊ฒƒ์ด๋‹ค.
   ๋‹จ, ๋งŒ์•ฝ์— ์œ„ 4๊ฐœ ๊ทธ๋ฃน์‚ฌ๊ฐ€ ๋ชจ๋‘ ์•„์ด๋”” ๋™์ผํ•œ ์•„์ด๋””๋กœ ๊ฐ ํšŒ์‚ฌ์˜ Legacy์‹œ์Šคํ…œ์„ ์‚ฌ์šฉํ•˜๊ฒŒ ํ•œ๋‹ค๋ฉด Realm๋Š”
   ํ•˜๋‚˜๋กœ ํ†ต์ผํ•ด์•ผํ•œ๋‹ค.

1. Realm ์ƒ์„ฑ

โ— ์™ผ์ชฝ์ƒ๋‹จ master ์•„๋ž˜ ํ™”์‚ดํ‘œ๋ฅผ ๋ˆŒ๋ ค Create Realm๋ฅผ ํ•ด๋ณด์ž.

โ— Realm name์„ ์ •ํ•˜๊ณ  Create๋ฅผ ๋ˆŒ๋Ÿฌ ๋‹ค์Œ์œผ๋กœ ๋„˜์–ด๊ฐ‘๋‹ˆ๋‹ค.

 

2. User ์ƒ์„ฑ

โ— Keycloak์— ๋Œ€ํ•œ ์ƒˆ ์‚ฌ์šฉ์ž๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

     ์™ผ์ชฝ Manage -> Users๋ฅผ ํด๋ฆญํ•˜๊ณ  Create new user๋ฅผ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.

โ— Username์„ ์ž…๋ ฅํ•˜๊ณ  Create๋ฅผ ํด๋ฆญํ•˜์—ฌ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ„๋‹ค.

  - ๊ฐ ํ•ญ๋ชฉ์€ ํ•„์ˆ˜๊ฐ’์ด ์•„๋‹ˆ์ง€๋งŒ, ํ•„์š”ํ•œ ํ•ญ๋ชฉ์€ ๊ธฐ์ž…ํ•ด๋„ ๋œ๋‹ค.

  - Required user actions๋Š” ๋กœ๊ทธ์ธ ํ• ๋•Œ ํ•„์š”ํ•œ ์กฐ์น˜๋ฅผ ์„ ํƒํ• ์ˆ˜ ์žˆ์œผ๋ฉฐ, ( 'Verify email' ) email ์ฃผ์†Œ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด

    ์‚ฌ์šฉ์ž์—๊ฒŒ  email๋ฅผ ๋ณด๋‚ด๊ฑฐ๋‚˜, ( 'Update profile' ) ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐœ์ธ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•˜๋„๋ก ์š”๊ตฌํ•˜๊ฑฐ๋‚˜,

    ('Update password') ์‚ฌ์šฉ์ž๊ฐ€ ์ƒˆ๋กœ์šด ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜๋„๋ก ์š”๊ตฌํ• ์ˆ˜ ์žˆ์œผ๋ฉฐ, ( 'Configure OTP') OTP๊ตฌ์„ฑ๋„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

โ— Credentials ํƒญ์—์„œ Set Password๋ฅผ ํด๋ฆญํ•ด์„œ ํŒจ์Šค์›Œ๋“œ๋ฅผ ์ž…๋ ฅํ•œ๋‹ค.

 Temporary๋ฅผ On์œผ๋กœ ํ•˜๋ฉด test์œ ์ €๊ฐ€ ์ฒ˜์Œ ๋กœ๊ทธ์ธ์‹œ ํŒจ์Šค์›Œ๋“œ๋ฅผ ๋ณ€๊ฒฝํ• ์ˆ˜ ์žˆ๋‹ค.


3. Client ์ƒ์„ฑ

Client๋Š” Application ์ธ์ฆ์„ ์œ„ํ•œ ๋‹จ์œ„๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.

 

โ— ์™ผ์ชฝ ๋ฉ”๋‰ด์—์„œ Clients์˜ Create client ๋ฅผ ํด๋ฆญํ•œ๋‹ค. 

 

โ— Client ID๋ฅผ ์ž…๋ ฅํ•œ๋‹ค.

โ— Capability config๋Š” default๋กœ ํ•˜๊ณ  Next .

 

โ— Login settings์—์„œ redirect URIs๋ฅผ ์„ค์ •ํ•œ ํ›„ Save.

 

 

Valid redirect URls ๋Š” ์‹ค์ œ ์ธ์ฆ(๋กœ๊ทธ์ธ) ์„ฑ๊ณตํ›„ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ํ• ์ˆ˜ ์žˆ๋Š” URLํŒจํ„ด
    - React๋กœ ์ƒ˜ํ”Œ ์‚ฌ์ดํŠธ๋ฅผ ๊ตฌ์ถ•ํ•  ์˜ˆ์ •์ด๋ฏ€๋กœ react ๊ธฐ๋ณธ URL์ธ http://localhost:3000 ์œผ๋กœ
       * ์™€์ผ๋“œ์นด๋“œ๋Š” react์—์„œ ๋ผ์šฐํŒ…๋œ ๋ชจ๋“  URL๋ฅผ ํ—ˆ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉ
Valid post logout redirect URls ๋Š” ๋กœ๊ทธ์•„์›ƒํ›„ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ํ•˜๋Š” URL์ด๋‹ค.
Web origins ๋Š” CORS๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ€๋Šฅํ•˜๋ฉด ์‚ฌ์šฉ 
    - ํŠน์ • URL๋งŒ ํ—ˆ์šฉํ•  ๊ฒฝ์šฐ๋Š” ํ•ด๋‹น URL๋ฅผ ๊ธฐ์ž…ํ•˜๋ฉด ๋œ๋‹ค.

๋‹ค์Œ์—๋Š” ๊ฐ„๋‹จํ•œ react๋กœ ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž.

 

์•ž์„  ์„ค๋ช…ํ–ˆ๋“ฏ์ด, ์˜คํ”„์†Œ์Šค SSO์ค‘ Keycloak ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ์„œ๋น„์Šค์— ์ดˆ์ ์„ ๋‘” ID ๋ฐ ์ ‘๊ทผ ๊ด€๋ฆฌ(Access Management)์— ํ†ตํ•ฉ ์ธ์ฆ(SSO)์„ ํ—ˆ์šฉํ•˜๋Š” ์˜คํ”ˆ ์†Œ์Šค ์†Œํ”„ํŠธ์›จ์–ด๋กœ Kubernetes ๋˜๋Š” MSA ํ™˜๊ฒฝ์— ์ตœ์ ํ™” ๋œ ์†”๋ฃจ์…˜ ์ž…๋‹ˆ๋‹ค. ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด ์ธ์ฆ(Authentification)๊ณผ ์ธ๊ฐ€(Authorization)๋ฅผ ์‰ฝ๊ฒŒ ํ•ด์ฃผ๊ณ  SSO(Single-Sign-On)๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.

 

์˜ค๋Š˜์€ ์šฐ๋ถ„ํˆฌ(Ubuntu) ํ™˜๊ฒฝ์—์„œ Keycloak๋ฅผ ์„ค์น˜ ํ•ด ๋ณด์ž.

 

โ€ป ์šฐ๋ถ„ํˆฌ ๋ฒ„์ „์€ Ubuntu 22.04.1 LTS ์ž…๋‹ˆ๋‹ค.

 

์‚ฌ ์ „ ์ค€ ๋น„

1. JAVA ์„ค์น˜

sudo apt-get install openjdk-11-jdk
 ์ตœ์‹  Keycloak๋Š” OpenJDK 11 ์ด์ƒ ์„ค์น˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

 

2. KeyCloak ๋‹ค์šด๋กœ๋“œ

๋‹ค์šด๋กœ๋“œ keycloak-21.1.1.tar.gz

์ตœ์‹ ๋ฒ„์ „ keycloak ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€์—์„œ ํ™•์ธ ๋ฐ”๋ž๋‹ˆ๋‹ค.  
 

downloads - Keycloak

Downloads 21.1.1 For a list of community maintained extensions check out the Extensions page. Server Quickstarts Client Adapters WildFly [DEPRECATED] <= 23 ZIP (sha1) TAR.GZ (sha1) JBoss EAP [DEPRECATED] 7 ZIP (sha1) TAR.GZ (sha1) JavaScript Node.js [DEPRE

www.keycloak.org

3. ๋‹ค์šด๋กœ๋“œ ๋œ ํŒŒ์ผ ์••์ถ• ํ’€๊ธฐ

$ tar -zxvf keycloak-21.1.1.tar.gz

 

4. ๊ธฐ๋ณธ ์‹คํ–‰

  • keycloak ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
$cd keycloak-21.1.1/
  • Keycloak๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ์ดˆ๊ธฐ ๊ด€๋ฆฌ์ž ๊ณ„์ •์„ ์ƒ์„ฑ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • Keycloak๋Š” default admin ์œ ์ €๊ฐ€ ์—†์œผ๋ฏ€๋กœ ํ™˜๊ฒฝ์„ ์…‹ํŒ…ํ•˜์—ฌ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
$ export KEYCLOAK_ADMIN=admin
$ export KEYCLOAK_ADMIN_PASSWORD=admin
  • keycloak์„ ์‹คํ–‰ ํ•ฉ๋‹ˆ๋‹ค.
$ cd bin
$ ./kc.sh start-dev

 

  • http://localhost:8080 ์ ‘์†

keycloak ํ™”๋ฉด

 

5. Administration Console ์ ‘์†

์œ„ ํŽ˜์ด์ง€๊ฐ€ ๋‚˜์˜ค๋ฉด Adminnistration Console ํƒญ์„ ํด๋ฆญํ•ด ์ค๋‹ˆ๋‹ค.

Keycloak ๋กœ๊ทธ์ธ ํ™”๋ฉด

์ดˆ๊ธฐ์— exportํ•œ ๊ด€๋ฆฌ์ž ๊ณ„์ •๊ณผ ํŒจ์Šค์›Œ๋“œ๋ฅผ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค. ( admin / admin )

 

๋กœ๊ทธ์ธ์„ ํ•˜๊ฒŒ ๋˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ KeyCloak ๊ธฐ๋ณธ ๋Œ€์‹œ๋ณด๋“œ๊ฐ€ ํ‘œ์‹œ๋œ๋‹ค.

 

๋‹ค์ŒํŽธ์—๋Š” realm, client, user ์ƒ์„ฑ๋“ฑ์„ ์•Œ์•„๋ณด์ž.

๋Œ€๊ทœ๋ชจ ์กฐ์ง(๋Œ€๊ธฐ์—… ํ˜น์€ ์ค‘์†Œ๊ธฐ์—…)์˜ ์‚ฌ์šฉ์ž๋Š” SSO(Single Sign-On) ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›ํ™œํ•˜๊ฒŒ ๋ชจ๋“  ์ž์›(์‹œ์Šคํ…œ) ์— ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค.SSO ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•œ ID ๋ฐ ์•ก์„ธ์Šค ๊ด€๋ฆฌ๋Š” ์ผ์ƒ์ ์ธ ๋น„์ฆˆ๋‹ˆ์Šค ์šด์˜์˜ ํ•„์ˆ˜์ ์ธ ๋ถ€๋ถ„์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ธ๊ธฐ ์žˆ๋Š” ์˜คํ”ˆ ์†Œ์Šค ์‹ฑ๊ธ€ ์‚ฌ์ธ์˜จ(SSO) ์†Œํ”„ํŠธ์›จ์–ด ์ƒ์œ„ 3๊ฐœ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  • IdentityServer
  • KeyCloak
  • WSO2

IdentityServer

IdentityServer๋Š” ์˜คํ”ˆ ์†Œ์Šค ๋ฌด๋ฃŒ ์‹ฑ๊ธ€ ์‚ฌ์ธ์˜จ ์†Œํ”„ํŠธ์›จ์–ด์ž…๋‹ˆ๋‹ค. OpenID Connect ๋ฐ OAuth 2๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋Š” ๊ต์ฐจ ํ”Œ๋žซํผ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด ์˜คํ”ˆ ์†Œ์Šค ์†Œํ”„ํŠธ์›จ์–ด๋Š” ์—ฌ๋Ÿฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•œ ์ค‘์•™ ์ธ์ฆ ๋ฐ ๊ถŒํ•œ ๋ถ€์—ฌ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. federated identities, ๋‹ค์ค‘ ํ๋ฆ„ ๋ฐ API ์ธ์ฆ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด ์ž์ฒด ํ˜ธ์ŠคํŒ… ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์—ฌ๋Ÿฌ ์•ฑ์—์„œ ๋‹จ์ผ ์‚ฌ์šฉ์ž ์ด๋ฆ„/๋น„๋ฐ€๋ฒˆํ˜ธ ์„ธํŠธ๋กœ ๋กœ๊ทธ์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

IdentityServer๋Š” C#์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์œผ๋ฉฐ ๋ฐฐํฌ ๋ฐ ๊ฐœ๋ฐœ์— ๊ด€ํ•œ ๋ฌธ์„œ์™€ ํ•จ๊ป˜ Github์—์„œ ๋ชจ๋“  ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋” ์•Œ์•„๋ณด๊ธฐ

 

KeyCloak

KeyCloak์€ OpenID Connect, OAuth2.0 ๋ฐ SAML2.0์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฌด๋ฃŒ ์†Œํ”„ํŠธ์›จ์–ด์ž…๋‹ˆ๋‹ค. 

์›น ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ๋ฐ ์›น ์„œ๋น„์Šค์—์„œ SSO ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ฌด์—‡๋ณด๋‹ค๋„ ์ด ์˜คํ”ˆ ์†Œ์Šค ์†Œํ”„ํŠธ์›จ๋Š” LDAP ๋ฐ Active Directory์™€์˜ ํ†ตํ•ฉ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ์—ญํ• , ๊ถŒํ•œ ๋ฐ ์„ธ์…˜์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋…ผ๋ฆฌ์  ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ Java, JavaScript ๋ฐ C#๊ณผ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ์–ธ์–ด์— ๋Œ€ํ•œ ํด๋ผ์ด์–ธํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

KeyCloak์€ ์ฃผ๋กœ Java๋กœ ์ž‘์„ฑ๋˜๋ฉฐ JavaScript๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค..

์†Œ์Šค ์ฝ”๋“œ๋Š” Github์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋” ์•Œ์•„๋ณด๊ธฐ

 

WSO2

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

 

๋” ์•Œ์•„๋ณด๊ธฐ

 

์–ธ๊ธ‰๋œ ์˜คํ”ˆ ์†Œ์Šค SSO ์†Œํ”„ํŠธ์›จ์–ด๋Š” ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์ˆ˜์ค€์—์„œ ๋„๋ฆฌ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. 

๋น„์ฆˆ๋‹ˆ์Šค๋ฅผ ์œ„ํ•œ ์ตœ๊ณ ์˜ ์‹ฑ๊ธ€ ์‚ฌ์ธ์˜จ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์„ ํƒํ•˜๋Š” ์ค‘์ด๋ผ๋ฉด ์œ„ ๋‚ด์šฉ์„ ์ฐธ๊ณ ํ•˜์—ฌ ํ™•์ธํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

 

์ €๋Š” ๊ฐ€์žฅ ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์šด KeyCloak์„ ํ†ตํ•œ ์ธ์ฆ ๋ฐฉ์‹์— ๋Œ€ํ•ด์„œ ์•ž์œผ๋กœ ๊ฒŒ์‹œ๊ธ€์„ ์ž‘์„ฑํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

 

+ Recent posts