-
Keycloakify를 활용한 로그인 화면 만들기Front-End 2025. 9. 28. 16:54
Keycloakify를 활용한 로그인 화면 만들기 로그인 기능을 만들려면 과거에는 애플리케이션 내부(Spring Security 등)에서 인증 로직을 직접 구현하는 경우가 많았지만, 요즘은 Keycloak 같은 자가 호스팅 IdP나 Auth0·Cognito 같은 관리형 IdP를 활용해 인증을 외부화하고 애플리케이션(프런트·백)은 OIDC 표준으로 연동하는 추세입니다.
이처럼 패러다임이 바뀌다보니 Keycloak의 인기도 계속해서 상승 중인데요. 많은 인기를 얻고 있는 만큼 keycloak에서는 인증 로직에 필요한 다양한 기능들이 제공되고 있습니다. 그중에서도 인증에 필요한 여러 가지 UI 템플릿(ex, 로그인, 비밀번호 찾기, 이메일, 회원가입 등)들을 기본으로 제공하고 있기 때문에 별도의 프론트 서버를 구축하지 않아도 IdP서버 내에서 전부 해결이 가능합니다.
다만, Keycloak에서 제공하는 로그인/계정 UI는 Freemarker(FTL) 기반 서버 템플릿이고, 기본 스타일도 PatternFly 체계를 따르기 때문에, 요즘 프론트 메인스트림 기술들과 디자인·빌드 파이프라인이 달라 프론트엔드 개발자 입장에서는 해당 기능을 요청을 받았다면 무척 난감할 것입니다.
이런 경우에는 Keycloakify라는 프레임워크를 사용하면 걱정 없이 개발하실 수 있게 되는데요. 이번 포스팅에서는 Keycloakify를 활용해서 로그인 화면을 디자인하고 설계하는 방법에 대해 포스팅 해보도록 하겠습니다.
🤔 Keycloakify란?
Quick Start | Keycloakify
docs.keycloakify.dev
Keycloakify란 Keycloak의 로그인/회원가입/계정 관리 등 화면을 React로 개발해 Keycloak 테마(JAR)로 포장해주는 도구입니다. 즉, 프리마커(FTL)나 비주류 CSS를 만지지 않고도, 팀에서 사용하는 프론트 스택(React + 원하는 스타일링) 그대로 Keycloak 화면을 만들 수 있게 해 줍니다.
위와 같은 장점들이 프론트엔드 개발자들의 부담을 크게 줄여줄 수 있는데요. 특히, 백엔드 개발자로부터 디버깅이 불가능한 FTL파일 달랑 하나 받아가지고 커스텀해달라는 요청을 받거나 백엔드 프로젝트를 클론 받아 직접 온보딩해야 하는 등의 부담감이 줄어듭니다.
이뿐만 아니라 Keycloakify를 사용하면 핫 리로드가 지원된다는 점이 장점입니다. Keycloakify는 두 가지 실행 방법을 지원하고 있는데요. 하나는 일반적인 vite 프론트 서버 실행방법이고 또 하나는 도커로 jar파일을 직접 배포하여 좀 더 프로덕션에 가까운 방법으로 실행하는 방법입니다.
물론, 후자는 핫 리로드는 아니고 빌드 자동화에 페이지 새로고침이 필요해서 조금 느리긴 하지만 몇 초 정도의 차이이기 때문에 크게 불편감 없이 개발할 수 있어서 저는 두 번째 실행방법으로 개발을 진행하였습니다. 이 처럼 빌드와 배포 또한 Keycloakify 기본 커맨드로 제공하고 있으니 편하게 사용하실 수 있습니다. 특히, Dockerfile로 만들어놓으시면 추후에 CI&CD를 구축하여 배포 자동화까지 만드실 수 있습니다.
Keycloakify 개발 및 빌드&배포 파이프라인
🛫 keycloakify-starter 프로젝트 세팅하기
GitHub - keycloakify/keycloakify-starter: 🔑 Keycloakify Projects Starter Template
🔑 Keycloakify Projects Starter Template. Contribute to keycloakify/keycloakify-starter development by creating an account on GitHub.
github.com
Keycloakify는 다음과 같이 keycloakify-starter라는 보일러플레이트를 공식적으로 제공하고 있습니다. 따라서 위 프로젝트를 클론 받아 README에서 제공하고 있는 튜토리얼 대로 진행하시면 간편하게 온보딩하실 수 있습니다. 아래는 포스팅 날짜 기준 최신 버전인 keycloakify v11 온보딩 방법입니다.
프로젝트 클론
git clone <https://github.com/keycloakify/keycloakify-starter.git>
프로젝트 로컬 세팅
yarn install
프로젝트 실행(jar 빌드 후 도커로 실행하는 방법)
npx keycloakify start-keycloak
- 사전에 메이븐, 도커데스크톱 설치되어있어야 함.
프로젝트 빌드(테마 jar로 빌드)
yarn run build-keycloak-theme
만약, keycloakify-starter를 사용하지 않고 직접 세팅하고 싶으시다면 아래 공식문서 가이드를 참고하시면 됩니다.
Keycloakify in my App | Keycloakify
Setting up Keycloakify in your Web App
docs.keycloakify.dev
🛒 Keycloakify와 같이 사용할 프레임워크 선정하기
가장 권장 조합(추천)
React + TypeScript + Vite + KeycloakifyKeycloakify는 위와 같은 조합으로 구성하시는 것을 추천드립니다. 우선 빌드 도구는 공식가이드 문서와 keycloakify-starter로 제공되고 있는 boilerplate 도 Vite로 구성되어있는 만큼 Vite를 1급 지원하고 있기 때문에 가장 안정성이 뛰어나다고 볼 수 있습니다.
문제는 CSS 프레임워크일 것 같은데요. CSS 프레임워크는 정말 다양하게 많이 사용되고 있기 때문에 어떤 CSS가 가장 좋을지 고민이 많이 되실 것 같습니다. 국내에서는 가장 인기가 많은 프레임워크인 tailwindcss와 MUI는 vite에 연동 가능한 플러그인이 지원되고 있기 때문에 둘 다 손쉽게 세팅하고 사용하실 수 있습니다.
만약 로그인, 회원가입 등 사용하는 페이지가 적다면 조금 더 가벼운 프레임워크인 tailwindcss를 선택하면 좋고 Dialog, Modal 등의 팝업이나 토스트 알림, 폼 에러, 포커스처리 등이 필요하다면 MUI를 채택하시면 좀 더 빠르게 구현할 수 있을 것 같습니다. (물론, shadcn을 사용하시면 tailwind에서도 유틸 컴포넌트들을 쉽게 구성하실 수 있습니다.)
만약 가장 가볍게 구축하고 싶으시다면 PatternFly나 기본 CSS를 사용하셔도 됩니다. PatternFly는 Red Hat에서 만든 CSS 프레임워크로 Keycloakify 자체 기본 테마에 사용되고 있는 프레임워크입니다. 따라서 프리마커 템플릿 파일(. ftl)을 보면 pf-, kc- 등의 prefix로 구성된 스타일 형태를 보실 수 있습니다.
PatternFly를 기존에 알고 있었고 사용해 본 적이 있으시다면 사용하셔도 무방하지만 만약 익숙지 않은 프레임워크라면 테일윈드를 사용할 때와 마찬가지로 클래스명을 외워야 하는 러닝커브가 발생할 수 있으므로 해당 부분을 충분히 고려하셔서 사용하셔야 합니다. 권장되는 사용방법으로는 PatternFly CSS를 전체 제거한 후 본인에게 익숙한 프레임워크로 세팅하는 것이 일반적인 사용방법입니다.
저의 경우에는 별도로 사용하던 디자인시스템이 있었기 때문에 공통 컴포넌트들은 디자인시스템에서 불러와서 사용하였고 레이아웃 등 필요한 CSS만 기본 CSS를 사용해서 스타일링을 하였습니다. CSS 레벨 vs 컴포넌트 레벨 두 가지 방식 모두 공식사이트에서 사용방법을 가이드하고 있으므로 팀원분들과 편하게 상의한 후 선택하시면 좋을 것 같습니다.
Customization Strategies | Keycloakify
docs.keycloakify.dev
🏗️ Keycloakify 프로젝트 구조 잡기
keycloakify-starter 프로젝트 구조는 다음과 같이 구성되어 있습니다.
my-app/ /* keycloakify-starter 루트 */ src/ login/ /* 로그인(인증) 화면 테마 소스 */ KcContext.ts // Keycloak이 각 페이지에 주입하는 kcContext의 타입/헬퍼. // 특정 페이지(예: 로그인, 회원가입 등)에 추가 필드가 필요하면 // 여기서 타입 확장하면 TS로 안전하게 접근 가능. KcPage.tsx // "페이지 스위처" 역할. kcContext.pageId에 따라 // 알맞은 페이지 컴포넌트를 렌더링하고, 공통 레이아웃(템플릿)을 감싼다. // 커스텀 페이지/스타일 입히는 핵심 진입점. KcPageStory.tsx // Storybook/로컬 프리뷰용 glue 컴포넌트. // 다양한 pageId·언어·상태를 mock kcContext로 바꿔가며 // Keycloak 서버 없이 UI를 빠르게 검증할 수 있다. i18n.ts // 번역 키(문구) 오버라이드/추가 정의. // messages_xx.properties를 직접 편집하지 않고 JS/TS로 관리. // ?ui_locales=ko 같은 쿼리로 프리뷰 언어도 바꿀 수 있다. email/ /* 이메일 템플릿 테마 `npx keycloakify initialize-email-theme`로 스캐폴딩. account/ /* Account 템플릿 테마 `npx keycloakify initialize-account-theme`로 스캐폴딩. kc.gen.tsx /* keycloakify가 사용하는 엔트리/생성 코드. KcPage와 테마 메타데이터를 export하는 연결 고리. 일반적으로 수정할 일은 거의 없고, 메인 엔트리와 결합된다. main.tsx /* 듀얼 엔트리: 실행 컨텍스트에 따라 App vs Keycloak 테마를 스위칭. window.kcContext가 있으면 <KcPage/> (Keycloak 테마), 없으면 일반 <App/>을 렌더. 이 파일에는 무거운 Provider/로직을 넣지 않는 게 성능상 유리. */ vite-env.d.ts /* Vite 타입 보조 선언 파일. */ package.json /* 스크립트/의존성 정의. - 로컬 미리보기, Storybook, 테마 빌드 등 명령 포함 - 테마 jar 빌드를 위해 Maven/Java 필요(환경별 설치 필요). */
주의해야 할 점은 위 폴더 구조가 마음에 안 든다고 변경하거나 하면 안 된다는 점입니다. 아래 가이드를 보면 몇 가지 추천 구조를 제공하고 있는데 이 이외의 구조로 변경한다면 빌드 및 실행 시 오류가 발생할 수 있으므로 주의하셔야 합니다.
Keycloakify in my App | Keycloakify
Setting up Keycloakify in your Web App
docs.keycloakify.dev
저도 처음에는 src 아래 login이 위치한게 마음에 안들어서 pages/login으로 옮겨놓았다가 오류가 발생해서 다시 원상복구 하는 등의 시간을 허비했었는데 저와 같은 불상사가 일어나지 않기를 바랍니다. 여기서의 login은 페이지로써의 login이 아니라 키클락 테마로써의 login이라고 보시면 됩니다.
프로젝트 구조를 원하는대로 커스텀하지 못하고 제약이 있다는 점은 어떻게 보면 keycloakify의 단점이라고도 볼 수 있을것 같습니다. 만약 로그인 테마에 컴포넌트랑 스타일, 에셋 등을 정의해야 한다면 다음과 같이 구성할 수는 있습니다.
my-app/ src/ login/ assets/ /* 로고, 이미지 등 정의 */ components/ /* 컴포넌트 정의 */ pages/ /* 페이지 정의 */ styles/ /* 스타일 정의 */ KcContext.ts KcPage.tsx /* 페이지 라우터 */ KcPageStory.tsx i18n.ts
만약 login 외에 accout, email 등의 테마를 추가적으로 사용하신다면 위 커스텀 폴더들을 src/ 하위에 두셔도 됩니다. 추가로, 각 테마별로 지원하고 있는 프리마커 템플릿(ex. login.ftl, login-reset-password.ftl)들이 어떤것 들이 있는지 궁금하시다면 공식사이트에서 지원하고 있는 아래 스토리북 링크를 통해 확인하실 수 있습니다.
Keycloakify
Creation of Keycloak Theme Made Easy
www.keycloakify.dev
다만, 스토리북에서는 UI만 확인 가능하고 해당 템플릿들에서 사용중인 form과 input 요소들의 id, name 값이 궁금하다면 keycloak 레포지토리에서 직접 확인하셔야 합니다.
keycloak/themes/src/main/resources/theme/base at 24.0.4 · keycloak/keycloak
Open Source Identity and Access Management For Modern Applications and Services - keycloak/keycloak
github.com
🎠 레이아웃(템플릿) 만들기
저희가 keycloakify를 사용하는 근본적인 이유는 로그인 페이지를 브랜드에 맞게 커스텀하기 위함입니다. 따라서, 각 페이지별로 로고와 헤더, 레이아웃은 기본적으로 공통된 양식을 갖고 있다면 keycloakify에서 제공하고 있는 Template 컴포넌트를 커스텀해서 사용하시면 됩니다.
커스텀 템플릿(MyTemplate) 생성
/* src/login/KcPage.tsx */ const MyTemplate = ({ children }: any) => { return ( <div className={pageContainer}> <header className={header}> <img src={logoImage} alt="My Logo" className={logo} /> </header> <div className={contentContainer}> {children} </div> ); }; export default function KcPage(props: { kcContext: KcContext }) { const { kcContext } = props; const { i18n } = useI18n({ kcContext }); return ( <Suspense> {(() => { switch (kcContext.pageId) { case "login.ftl": return ( <Login {...{ kcContext, i18n }} Template={MyTemplate} doUseDefaultCss={false} /> ); ...
/* src/login/pages/Login.tsx */ <Template kcContext={kcContext} i18n={i18n} doUseDefaultCss={doUseDefaultCss} displayMessage={!messagesPerField.existsError("username", "password")} headerNode={null} >
위와 같이 KcPage에서 템플릿과 컨텍스트들을 props로 전달하여 각 페이지에서 노드 가장 최상단에 Template을 위와 같이 래핑 해주시면 됩니다. 위의 정보들은 반드시 넣어주셔야 하는 필수 정보들이며 headerNode를 쓰지 않더라도 null을 꼭 넣어주셔야 타입에러가 발생하지 않습니다.
커스텀 템플릿 대신 headerNode 사용하기
만약, 커스텀 템플릿은 과한 것 같고 헤더만 필요하다고 하시면 다음과 같이 구성하실 수도 있습니다.
/* src/login/components/header.tsx */ import logoImage from "../assets/img/logo.png"; const Header = ()=> { return ( <header className={header}> <img src={logoImage} alt="My Logo" className={logo}/> </header> ); }; export default Header;
/* src/login/pages/Login.tsx */ import Header from "../components/header"; return ( <Template kcContext={kcContext} i18n={i18n} doUseDefaultCss={doUseDefaultCss} displayMessage={!messagesPerField.existsError("username", "password")} headerNode={<Header />} > ... )
👫 다국어 설정하기 (i18n)
keycloakify는 기본적으로 다국어 세팅이 가능하도록 i18n도 지원을 하고 있습니다. 다만, 한 가지 아쉬운 점은 keycloakify에서 한국어 팩을 기본적으로 지원을 안 하고 있다는 점인데요. 따라서, 한국어 세팅을 하려면 별도의 커스텀 작업이 필요합니다. keycloakify는 아래 링크를 통해서 한국어 i18n을 추가로 등록하실 수 있습니다.
Adding Support for Extra Languages | Keycloakify
docs.keycloakify.dev
간단하게 설명드리자면 키클락 어드민에 접속하여 realms localizations 설정에서 한국어 언어팩을 추가한 뒤에 keycloakify 프로젝트에 다음과 같이 파일을 만들어주시면 됩니다.
/* src/login/i18n.ko.ts */ import type { MessageKey_defaultSet } from "keycloakify/login or keycloakify/accont"; const messages: Record<MessageKey_defaultSet, string> = { // cspell: disable doLogIn: "로그인", doRegister: "회원가입", doRegisterSecurityKey: "보안 키 등록", // ... translation for all the other default messages // cspell: enable }; export default messages;
오버라이드할 키 파일은 다음 경로에서 참고하시면 됩니다.
node_modules/keycloakify/src/login/i18n/messages_defaultSet/en.ts
마지막으로 i18n.ts 파일에서 한국어팩을 import 해서 사용해 주시면 됩니다.
/* src/login/i18n.ts */ import { i18nBuilder } from "keycloakify/login or keycloakify/account"; import type { ThemeName } from "../kc.gen"; /** @see: <https://docs.keycloakify.dev/i18n> */ const { useI18n, ofTypeI18n } = i18nBuilder .withThemeName() .withExtraLanguages({ ko: { // cspell: disable-next-line label: "한국어", getMessages: () => import("./i18n.ko") } }) .withCustomTranslations({ /* 커스텀 메시지가 필요한 경우 */ en: { forgotUsername: "Forgot your username?", resetPassword: "Reset password" }, ko: { forgotUsername: "아이디를 잊으셨나요?", resetPassword: "비밀번호 재설정" } }) .build(); type I18n = typeof ofTypeI18n; export { useI18n, type I18n };
이제 설정한 i18n을 페이지 또는 컴포넌트에서 사용할 때는 다음과 같이 사용해 주시면 됩니다.
export default function Register(props: RegisterProps) { const { i18n } = props; const { msg, msgStr, advancedMsg, advancedMsgStr } = i18n; return ( //... <a href={url.loginUrl}> {msg("backToLogin")} </a> // ... ); }
메시지를 사용할때는 msg()와 msgStr() 중에서 사용하시면 되는데 기본적으로 msg()는 리액트노드(ReactNode) 타입을 반환하고 msgStr()은 string 타입을 반환합니다. msg()는 내부적으로 sanitizer를 거쳐서 일부 허용된 마크업(ex. <strong>)을 제외하고는 렌더링을 허용하지 않기 때문에 조금 더 안전하게 사용할 수 있지만, 디자인시스템이나 커스텀된 컴포넌트를 가져다 쓸 때 타입 오류가 발생할 수 있으므로 상황에 맞게 msgStr()을 사용하실 수도 있습니다.
🌠 정리하기
지금까지 keycloakify 프로젝트를 세팅하는 방법에 대해 정리해 보았는데요. keycloakify를 사용해 보면서 느낀 점은 프론트엔드 개발자 입장에서 퍼블리싱은 분명히 편리하지만 기획되어 있는 인증 로직이 keycloak 인증 로직과 많이 벗어난다면 결국 백엔드에 의존할 수 밖에 없게 되기 때문에 애매한 프레임워크라는 생각도 듭니다.
예를 들어 아이디 찾기, 비밀번호 찾기 템플릿에 휴대폰번호 입력이 추가되어야 하는 상황이라면 이 하나의 기능을 위해서 새로운 프리마커 템플릿을 정의해달라고 백엔드쪽에 요청(Keycloak SPI(RequiredActionProvider)를 활용해 프리마커 템플릿을 정의할 수 있다.)하거나 기존 템플릿을 UI부터 form 제출 로직까지 전부 뜯어고쳐야 합니다. 물론, 뜯어고쳐서 직접 백엔드 API 호출 로직까지 하드코딩하면 구현은 가능하지만 이는 추후에 유지보수의 복잡성을 높이는 결과를 초래할 수 있습니다.
따라서, 저는 팀원들과 논의한 후 로그인을 제외한 나머지 페이지들은 별도의 클라이언트 서버를 사용하는 방향으로 백엔드쪽의 부담을 줄여주는 방향으로 keycloakify를 사용하였습니다. 만약 팀 내 keycloakify 도입을 고려하고 계시다면 해당 부분들을 충분히 고려하셔서 프로젝트의 방향에 맞게 기술 선정하시면 좋을 것 같습니다.
'Front-End' 카테고리의 다른 글
해커톤에서 사용할 수 있는 다양한 프론트엔드(react, next) 빌드 및 배포 방법 (0) 2024.06.29 Nextjs13을 활용한 간단한 심리테스트 만들기 (0) 2023.03.23 Storybook 도입할 수 있을까? (0) 2023.01.31 웹 접근성 지침 적용방법 (IR 기법) (0) 2022.02.17 jsrender 사용시 화면이 나오다 만 경우 (1) 2021.03.02