๋ณธ๋‚ด์šฉ์€ ํ•„์ž์˜ " 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๊ฐ’ ๋ฐ ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š”๊ฒƒ์„ ์•Œ์•„๋ณด์ž.

์•ž์„  ์„ค๋ช…ํ–ˆ๋“ฏ์ด, ์˜คํ”„์†Œ์Šค 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