✔ keycloakify 설치하기
✔ 로그인 화면 커스텀 하기
✔ 회원 가입 화면 커스텀 하기
이 시리즈에서 개발한 키클락 앱 코드는 깃헙에 올려두었다.
👉 https://github.com/cindy-choi/keycloak-sample-ts
완성된 코드 말고 처음부터 따라하고 싶은 사람은 아래에 나오는 보일러플레이트를 사용해도 된다.
다음은 keycloakify 의 메인 페이지에서 확인 할 수 있는 gif 이다.
gif를 보면 Onyxia 라는 이름의 검은색 테마의 페이지들이 있고,
쌩뚱맞은 파란색 테마의 로그인 페이지가 보이는데
검은 페이지들이 일반 서비스 UI 이고, 테마가 맞지 않은 파란 페이지들이 Keycloak UI이다.
잘 보면 keycloak UI 에 진입했을 때 URL이 기존과는 다르게 몹시 긴 것을 볼 수 있다. 완전히 다른 두 개의 UI를 개발해야 하는 것이다.
이 시리즈에서 진행하는 프로젝트는 하나의 repository에 서비스 UI 와 Keycloak UI 코드를 한 번에 관리&빌드 할 수 있도록 구성했다.
시작하기 전에, 개발 하기 전에 세팅해놓는 것을 좋아해서 미리 만들어둔 boilerplate 를 사용했다.
샘플 코드를 만드는 사람이라면 이 보일러플레이트를 클론 받아서 같이 시작하면 되겠다.
👉 https://github.com/cindy-choi/cindy-boilerplate-ts
git clone https://github.com/cindy-choi/cindy-boilerplate-ts.git
다음의 명령어로 keycloakify 와 친구들을 설치한다.
npm install --save-dev keycloakify @emotion/react tss-react powerhooks
keycloak 관련 전역 변수 등을 관리하기 위해 매니저 파일을 만든다.
파일 명과 경로는 마음대로..
@/utils/keycloakManager.ts
import { getKcContext } from 'keycloakify';
export const { kcContext } = getKcContext<{
pageId: 'login.ftl',
}>({
mockData: [
{
pageId: 'login.ftl',
},
],
});
const keycloakManager = {
kcContext,
};
export type KcContextType = NonNullable<typeof kcContext>;
export default keycloakManager;
keycloakify 로 UI를 빌드할 때 kcContext 값을 사용해서
등등을 결정한다.
이렇게 생긴 로그인 페이지를... 만들어보자.
프론트엔드 개발자 타이틀 달고 퀄리티가 말이 아니다.
아무튼...
keycloak 관련 페이지는 keycloak 경로에 다 묶어두었다.
서비스 UI 랑 같이 하나의 repo 에서 관리하려면 이 방법이 편하다.
@/pages/keycloak/Login.tsx
import React, { useState, useRef, memo, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import styled from 'styled-components';
import { Button, TextField } from '@mui/material';
import type { KcProps } from 'keycloakify/lib/components/KcProps';
import type { KcContextType } from '@/utils/keycloakManager';
type KcContext_Login = Extract<KcContextType, { pageId: 'login.ftl' }>;
const StyledLogin = styled.div`
min-width: 100vw;
min-height: 100vh;
background-image: url(${bg});
background-size: cover;
background-repeat: no-repeat;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`;
const LoginForm = styled.form`
width: 25rem;
height: 15rem;
background-color: white;
border-radius: 5px;
box-shadow: 2px 2px 8px 0px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`;
const LoginInput = styled(TextField)`
width: 20rem;
margin-bottom: 8px !important;
`;
const LoginButton = styled(Button)`
width: 20rem;
`;
export const Login = memo(({ kcContext, ...props }: { kcContext: KcContext_Login } & KcProps) => {
const { t } = useTranslation();
const form = useRef<HTMLFormElement>(null);
const { social, url, message, realm, } = kcContext;
const isSessionOut = message?.summary.includes('attempt timed out') || message?.summary.includes('Timeout');
console.log(kcContext);
const handleSubmit = () => {
form?.current?.submit();
};
return (
<StyledLogin>
<LoginForm ref={form} method="post" action={url.loginAction}>
<LoginInput
id="username"
name="username"
size="small"
label={t('id')}
/>
<LoginInput
label={t('password')}
id="password"
name="password"
type="password"
size="small"
/>
<LoginButton variant="contained" onClick={() => handleSubmit()}>{ t('login') }</LoginButton>
</LoginForm>
</StyledLogin>
);
},
);
export default Login;
벨로그에서는 line number 기능이 없군요
하여간 코드를 보면 알 수 있듯, 기존 React로 개발하던 방식 그대로 로그인 페이지를 만들 수 있다. no more ftl!
keycloak 서버가 UI를 렌더링 할 때 전달하는 변수들은 모두 kcContext 객체 안에 들어있다.
예를 들어 위 코드에서 다국어 지원을 i18next 로 하고 있지만, 서버에서 atrribute 로 전달하는 것을 사용해도 무방할 것이다. 난 안해봄. 잘되면 알려주세요.
로그인 페이지를 만들었으면, 이제 keycloakify 가 이걸 빌드하도록 설정 해주어야한다.
프로젝트의 root 디렉토리에 KeycloakApp.tsx 파일을 만들자.
@/KeycloakApp.tsx
import { memo } from 'react';
import { defaultKcProps } from 'keycloakify';
import { useTranslation } from 'react-i18next';
import type { KcContextType } from '@/utils/keycloakManager';
import Login from '@/pages/keycloak/Login';
// import Error404 from '@/pages/common/Error404';
import './KeycloakApp.scss';
export const KeycloakApp = memo(({ kcContext }: { kcContext: KcContextType; }) => {
const { t } = useTranslation();
console.log(kcContext);
switch (kcContext.pageId) {
case 'login.ftl':
return <Login {...{ kcContext, ...defaultKcProps }} />;
default:
return undefined;
}
});
export default KeycloakApp;
switch를 보면 서버에서 넘겨주는 kcContext의 pageId 에 따라 어떤 페이지를 렌더링 할지를 결정하는 것을 알 수 있다.
추후 추가로 회원 가입 페이지나, 404 에러 페이지를 개발하게 될 경우 swtich case 에 추가해주면 된다.
그리고 이 KeycloakApp.tsx 를 사용하도록 설정해주자.
@/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import reportWebVitals from './reportWebVitals';
// service ui
import App from './App';
// keycloak ui
import KeycloakApp from '@/KeycloakApp';
import { kcContext } from '@/utils/keycloakManager';
/**
* keycloak 서버에서 제공할 때에만 kcContext 값이 활성화되며, 기본적으로는 App을 렌더링합니다.
*/
ReactDOM.render(
<React.StrictMode>
{
// kcContext가 존재하면 키클락 App 을 렌더링하고, 그렇지 않으면 App을 렌더링합니다.
kcContext !== undefined ? (
<KeycloakApp kcContext={kcContext} />
) : (
<App />
)
}
</React.StrictMode>,
document.getElementById('root'),
);
reportWebVitals();
App.tsx 와 KeycloakApp 을 상황에 따라 다르게 사용하는 것을 볼 수 있다.
구체적으로 말하자면 이 다음에 나오는 npm run keycloak
명령어를 때리면 keycloakify 로 우리가 개발한 웹 페이지를 빌드하는데, 이 때 kcContext가 전달된다고 보면 된다.
그냥 만들면 짠하고 keycloak UI 가 spa 로 동작하면 얼마나 좋을까.
이제 실제로 keycloakify 를 사용해서 키클락 theme 파일을 만들어보자.
우선 빌드 명령어를 추가해주어야 한다.
package.json
"scripts": {
"keycloak": "craco build && build-keycloak-theme",
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
},
맨 위에 있는 keycloak 을 추가해주면 된다.
나는 craco 를 쓰는데 npm을 사용중이라면 다음과 같이 변경하면 된다.
"keycloak": "npm build && build-keycloak-theme"
그리고 명령어를 때려보자.
우선 npm build
때린 것 처럼 빌드를 먼저 하고 진행하기 때문에 제법 오래 걸린다.
이렇게 긴~ 과정을 끝내면
프로젝트의 root 경로에 build_keycloak 이라는 경로가 생성되고
그 안에 keycloak 적용에 필요한 것들이 모두 포함되어 나온다.
그 중 우리에게 필요한 건 theme 폴더!
build_keycloak/src/main/resources/theme/
경로 안에 있는 keycloakify-sampe
경로를 확인하자.
참고로 이 폴더의 이름은 package.json 에 명시된 프로젝트의 name 을 따라간다.
이 keycloakify-sample
폴더를 키클락이 설치된 곳에 복사해두면 끝!
다른 jar 파일들이 많이 생기는데, 내 경우 혹시 몰라서 jar 파일만 서버에 적용 해주었고 나머지는 손대지 않았지만 잘 동작했다.
여기까지 keycloakify 로 UI 빌드까지 끝냈다.
이제 로컬에 키클락을 띄우고 로그인이 필요한 페이지에서 리디렉션을 걸어서 실제 UI랑 연동을.. 내일 해보자.
끝
안녕하세요 이 글 키클락 페이스북 한국 사용자 모임에 올려도 될까요?