본문 바로가기
won2dev-log
HomeArchiveTagsCategoriesAboutProjects
HomeArchiveTagsCategoriesAboutProjects
won2dev-logwon2dev-logwon2dev-log

비전공 개발자의 로그 | won2dev-log

Navigation
  • Home
  • Archive
  • About
  • Projects
Categories
  • Docs
  • TIL
  • Project
  • Automation
  • Git · GitHub
더보기
Tags
  • TIL
  • Java
  • Spring
  • Backend
  • n8n
더보기
About

기록을 거름 삼아 공유는 성장을 만든다.

LicensePrivacy
© won2dev 2026. All rights reserved.
Home›TIL›Keycloak을 처음 쓰면서 배운 것들 — 그게 뭔지도 모르고 시작했다
TIL

Keycloak을 처음 쓰면서 배운 것들 — 그게 뭔지도 모르고 시작했다

won2dev·2026년 05월 28일
#TIL

인증 서버가 필요하다고 해서 Keycloak을 쓰게 됐다.

근데 솔직히 처음엔 뭔지 몰랐다. JWT는 알았고, 구글 로그인이나 카카오 로그인 같은 소셜 로그인도 어렴풋이 알았다. 근데 Keycloak은 그것들이랑 뭐가 다른 건지, 왜 쓰는 건지부터가 몰랐다.

그리고 지금도 솔직히 어렵다. 이 글은 "Keycloak 마스터" 글이 아니라, 모르고 시작했다가 하나씩 부딪혀가는 과정을 기록한 글이다.


Keycloak이 뭔가

한 줄로 정리하면 직접 운영할 수 있는 인증 서버다.

로그인, 회원가입, 토큰 발급, 권한 관리 — 이런 걸 직접 짜지 않아도 되게 해준다. 오픈소스고, 서버에 직접 띄워서 쓴다.


JWT 쓰면 되는 거 아닌가

JWT는 인증 상태를 담는 토큰 포맷이다. 로그인 자체를 처리해주는 시스템은 아니다.

서명된 데이터 규격에 가깝다. JWT를 직접 구현한다는 건 이런 걸 다 짜야 한다는 거다.

  • 로그인 API
  • 토큰 발급 / 검증 / 갱신 로직
  • 회원가입, 비밀번호 찾기
  • 권한 체계 (role, permission)
  • 토큰 만료 처리

간단한 프로젝트면 직접 짜는 게 낫다. 근데 서비스가 여러 개가 되면 얘기가 달라진다. 매번 각 서비스마다 인증 로직을 따로 구현하거나 공유해야 한다.

Keycloak은 인증 자체를 중앙 서버로 분리한다. 각 서비스는 발급된 토큰을 검증하고 권한만 판단하면 된다.


그럼 Okta나 소셜 로그인 쓰면 되는 거 아닌가

Okta, Auth0 같은 SaaS 인증 플랫폼이나, Google, 카카오 같은 소셜 로그인 제공자를 사용할 수도 있다. 이것들은 레이어가 다르긴 한데, 공통점은 외부 서비스에 인증을 맡긴다는 거다.

직접 운영할 필요가 없다는 건 장점이다. 근데 단점도 있다.

KeycloakOkta / Auth0
운영 주체직접외부 서비스
비용서버 비용만유저 수 기반 과금
데이터 위치내 서버외부
커스터마이징높음제한적

유저 데이터를 외부에 두기 어려운 환경, 유저가 많아질수록 비용이 부담되는 상황, 인증 흐름을 세밀하게 제어해야 할 때 — Keycloak이 선택지가 된다.


Keycloak의 장점 — 설정할 수 있는 게 많다

Keycloak은 진짜 설정할 수 있는 게 많다.

  • Realm: 독립된 인증 영역. 테넌트처럼 분리해서 쓸 수 있다
  • Client: 인증을 요청하는 애플리케이션 단위
  • Role / Group: 권한 체계
  • Identity Provider: 구글, 카카오 같은 소셜 로그인 연동
  • User Federation: LDAP, Active Directory 연동
  • Client Scope / Mapper: JWT 토큰에 어떤 정보를 담을지 제어
  • User Profile: 유저 attribute 스키마 관리

한 화면에서 로그인 페이지 디자인부터 토큰 만료 시간까지 다 건드릴 수 있다.


근데 그게 단점이기도 하다

설정할 수 있는 게 많다는 건, 설정해줘야 하는 게 많다는 뜻이기도 하다.

간단하게 "유저한테 company_id attribute 저장하고 싶어" 라고 생각하면 이렇게 된다.

  1. User Profile에 attribute 스키마 등록
  2. Client Scope 생성
  3. Mapper 추가
  4. Client에 Scope 연결

이게 다 안 맞으면 동작을 안 한다. 그리고 동작을 안 해도 에러가 안 난다.


실제로 겪었다...

승인 처리 시 해당 유저에게 company_id를 붙여줘야 했다. Keycloak Admin Client로 이렇게 짰다.

java
public void updateUserAttribute(String email, String key, String value) {
    try (Keycloak keycloak = buildAdminKeycloak()) {
        var usersResource = keycloak.realm("sparta-logistics").users();
        var users = usersResource.searchByUsername(email, true);
        if (users.isEmpty()) return;

        var userResource = usersResource.get(users.get(0).getId());
        var userRep = userResource.toRepresentation();

        Map<String, List<String>> attrs = userRep.getAttributes();
        if (attrs == null) attrs = new HashMap<>();
        attrs.put(key, List.of(value));
        userRep.setAttributes(attrs);
        userResource.update(userRep);

        log.info("[Keycloak] 유저 attribute 업데이트 완료 - email={}, {}={}", email, key, value);
    }
}
public void updateUserAttribute(String email, String key, String value) {
    try (Keycloak keycloak = buildAdminKeycloak()) {
        var usersResource = keycloak.realm("sparta-logistics").users();
        var users = usersResource.searchByUsername(email, true);
        if (users.isEmpty()) return;

        var userResource = usersResource.get(users.get(0).getId());
        var userRep = userResource.toRepresentation();

        Map<String, List<String>> attrs = userRep.getAttributes();
        if (attrs == null) attrs = new HashMap<>();
        attrs.put(key, List.of(value));
        userRep.setAttributes(attrs);
        userResource.update(userRep);

        log.info("[Keycloak] 유저 attribute 업데이트 완료 - email={}, {}={}", email, key, value);
    }
}

로그엔 "유저 attribute 업데이트 완료"가 찍혔다. 근데 Admin Console에서 확인하면 null이다. JWT에도 company_id가 없다.

원인은 Keycloak 24.0부터 새로 만든 realm에서 User Profile 기능이 기본 활성화됐기 때문이다. 이전 버전에서는 Admin Client로 attribute를 그냥 넣어도 저장되는 경우가 많았는데, 24.0 이후부터는 User Profile 설정과 attribute 정책 영향을 받게 됐다. 내 경우엔 User Profile 스키마에 등록되지 않은 attribute를 update() 했을 때 예외 없이 조용히 무시됐다.

추가로, attribute를 먼저 User Profile에 등록해야 한다는 것 자체를 모르고 있었다.

설정이 안 돼 있으면 에러 대신 침묵이 온다.

특히 더 헷갈렸던 건, 코드상으론 성공처럼 보인다는 점이었다. 로그도 정상이고 예외도 없는데 실제 데이터만 저장되지 않았다. 코드만 보면 절대 못 찾는다.


해결 — 스키마 먼저, 코드는 나중에

Realm Settings > User Profile > Create attribute 에서 먼저 등록해야 한다.

이미지
Attribute NameDisplay Name
company_idCompany ID
hub_idHub ID

등록 후 동일한 코드 재실행 → 정상 저장됐다.


JWT에 포함시키는 것도 별개다

attribute가 저장됐다고 JWT에 자동으로 들어오지 않는다.

Keycloak은 User Attribute와 JWT Claim을 자동으로 연결하지 않는다. Mapper를 통해 어떤 attribute를 어떤 claim으로 노출할지 직접 지정해야 한다. attribute 저장과 토큰 노출은 완전히 분리된 개념이다.

  1. Client Scope 생성 — user-attributes
    이미지
  2. Mapper 추가 — User Attribute 타입, Attribute Name: company_id, Token Claim Name: company_id
    이미지
  3. Client에 Scope를 Default로 추가
이미지

이 세 가지가 다 맞아야 토큰에 들어온다.


후기

Keycloak은 아직도 어렵다.

설정 하나하나가 연결돼 있는데, 그 구조를 모르면 계속 막힌다. 에러가 나면 그나마 낫다. 그냥 아무 일도 안 일어나는 게 더 무섭다.

Keycloak은 코드보다 설정을 먼저 의심해야 하는 툴이었다.

공유하기
이전 글JWT는 Stateless가 장점인데, 왜 Redis까지 붙였나다음 글 MSA에서 트랜잭션은 왜 어려운가 — 분산 트랜잭션과 Saga Pattern

목차

  • Keycloak이 뭔가
  • JWT 쓰면 되는 거 아닌가
  • 그럼 Okta나 소셜 로그인 쓰면 되는 거 아닌가
  • Keycloak의 장점 — 설정할 수 있는 게 많다
  • 근데 그게 단점이기도 하다
  • 실제로 겪었다...
  • 해결 — 스키마 먼저, 코드는 나중에
  • JWT에 포함시키는 것도 별개다
  • 후기

카테고리

TIL

태그

#TIL

최근 글

AI 에이전트에게 부하테스트를 맡겼더니, 테스트 환경을 직접 만들고 버그까지 찾아냈다JWT는 Stateless가 장점인데, 왜 Redis까지 붙였나MSA에서 트랜잭션은 왜 어려운가 — 분산 트랜잭션과 Saga PatternFK는 포인터다 — 주문 시스템에서 스냅샷이 필요한 이유Kafka 입문 — 메시징 큐가 왜 필요한가