Skip to content

refactor: yml 파일 수정 및 COMPANY_ID/HUB_ID 헤더 추가 - #7

Merged
HellWorlding merged 3 commits into
mainfrom
refactor/#6-add-header
Apr 8, 2026
Merged

refactor: yml 파일 수정 및 COMPANY_ID/HUB_ID 헤더 추가#7
HellWorlding merged 3 commits into
mainfrom
refactor/#6-add-header

Conversation

@oni128

@oni128 oni128 commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

✨ 기능 PR 템플릿


📌 작업 유형

  • 신규 기능 추가
  • 기존 기능 개선
  • 의존성 관리 (라이브러리 추가/업데이트)

✅ 작업 내용

  • yml 파일 수정 및 COMPANY_ID/HUB_ID 헤더 추가

🧪 테스트 / 확인 방법

  • 로컬 실행 확인
  • 주요 시나리오 동작 확인
  • 예외 케이스 확인 (해당 시)

⚠️ 영향 범위 (해당 시 체크)

  • API 스펙 변경
  • DB 스키마 변경
  • 환경설정 변경

👀 리뷰 포인트 (선택)

리뷰어가 특히 봐줬으면 하는 부분이 있으면 작성합니다.

Summary by CodeRabbit

릴리스 노트

  • 새 기능

    • 상품 관련 API 요청에 대한 새로운 라우팅 규칙이 추가되었습니다.
  • 개선 사항

    • 사용자 요청 컨텍스트에서 회사 및 허브 정보 전달이 추가되었습니다.
    • 사용자 역할 추출 로직이 최적화되었습니다.

@oni128 oni128 self-assigned this Apr 7, 2026
@coderabbitai

coderabbitai Bot commented Apr 7, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@HellWorlding has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 11 minutes and 28 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 11 minutes and 28 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c9b28563-9a18-4ffe-8813-9bb022dbc808

📥 Commits

Reviewing files that changed from the base of the PR and between 83ef9a9 and cfe272f.

📒 Files selected for processing (1)
  • src/main/resources/application.yaml
📝 Walkthrough

Walkthrough

이번 변경은 사용자 컨텍스트 필터와 게이트웨이 라우팅 규칙을 업데이트합니다. UserContextFilter에서 JWT 클레임으로부터 companyId와 hubId를 추출하여 X-User-Company-Id와 X-User-Hub-Id 헤더를 추가로 전달하도록 변경되었습니다. 역할 추출 로직은 realm\_access의 roles 스캔 방식에서 JWT의 최상위 role 클레임을 직접 읽는 방식으로 변경되었으며, 관련 헬퍼 메서드가 제거되었습니다. application.yaml에는 item-service로의 라우팅 규칙이 새로 추가되어 /api/companies/\*/items 경로의 요청을 처리합니다.

Poem

🐰 헤더를 추가하고 역할을 다시 정의하니,
JWT 클레임에서 진실을 찾아내고,
아이템 서비스의 길을 밝혀내는 라우트,
게이트웨이는 더욱 똑똑해졌네요! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 PR의 주요 변경사항(yml 파일 수정 및 COMPANY_ID/HUB_ID 헤더 추가)을 명확하게 요약하고 있습니다.
Linked Issues check ✅ Passed PR은 연결된 이슈 #6의 요구사항(yml 파일 수정, COMPANY_ID/HUB_ID 헤더 추가, role 추출 로직 수정)을 모두 충족합니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 이슈 #6의 범위 내에 있으며, 추가적인 범위 외 변경은 없습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/#6-add-header

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@oni128 oni128 added the enhancement New feature or request label Apr 7, 2026
  user-service의 KeycloakIdentityProvider가 회원가입 시 사용자 attributes로
  role/hubId/companyId/is_enabled를 직접 박아서 발행되는 JWT에는 top-level
  "role" 클레임에 ROLE_MASTER/HUB/DELIVERY/COMPANY 형식으로 들어온다.

  기존 extractRoleFromRealmAccess()는 realm_access.roles 배열에서 ROLE_*
  키워드를 찾는 옛날 로직이라 새 JWT에서 빈 문자열만 반환했다. 그 결과
  X-User-Role 헤더가 모두 빈 값으로 전달되어 도메인 서비스의 권한 체크가
  모두 실패하던 것을 fix.

  - claims.get("role")로 단순화
  - 더 이상 사용하지 않는 extractRoleFromRealmAccess() 메서드 제거

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/main/java/com/loopang/gateway/config/UserContextFilter.java (1)

67-69: role 단일 claim만 사용하면 전환 기간 토큰과 비호환될 수 있습니다.

Line 69에서 top-level role만 읽도록 바뀌어, 기존 realm_access.roles 형태 토큰이 아직 유통 중이면 X-User-Role이 빈 값이 될 수 있습니다. 마이그레이션 기간에는 fallback 경로를 잠시 유지하는 게 안전합니다.

호환성 fallback 예시
+ import java.util.List;
+ import java.util.Objects;
...
-          String role = (String) claims.get("role");
+          String role = Objects.toString(claims.get("role"), "");
+          if (role.isBlank() && claims.get("realm_access") instanceof Map<?, ?> realmAccess) {
+            Object rolesObj = realmAccess.get("roles");
+            if (rolesObj instanceof List<?> roles && !roles.isEmpty()) {
+              role = Objects.toString(roles.get(0), "");
+            }
+          }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/loopang/gateway/config/UserContextFilter.java` around lines
67 - 69, The code currently only reads the top-level role claim via
claims.get("role") into the local variable role, which breaks compatibility with
tokens that still use realm_access.roles; update the logic around String role
(in UserContextFilter) to: first attempt to read the top-level "role" claim, and
if that is null/empty, fall back to reading the "realm_access" claim and
extracting its "roles" list (use the first role or join roles as appropriate)
before setting the X-User-Role header; keep the existing variable names (claims,
role) and header name (X-User-Role) so the change is minimal and clearly
located.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/com/loopang/gateway/config/UserContextFilter.java`:
- Around line 64-65: The UserContextFilter currently casts JWT claim values
directly (e.g., companyId, hubId, id, email, name, slackId, enabled, role) from
the claims Map<String,Object>, which can throw ClassCastException; update the
code in UserContextFilter to replace direct (String) casts with safe conversion
using Objects.toString(claims.get("..."), null) (or a default) for each claim
read and propagate the resulting String into your existing setters so nulls are
handled gracefully instead of throwing runtime exceptions.

In `@src/main/resources/application.yaml`:
- Around line 17-20: The route id "item-service" is duplicated causing routing
conflicts; locate both route definitions that use id: item-service (e.g., the
block with predicates Path=/api/companies/*/items, /api/companies/*/items/**)
and rename one of the ids to a unique value (for example item-service-company or
item-service-v2), then update any other references to that route id (if used
elsewhere in config or monitoring) so all route ids are unique and the gateway
resolves routes deterministically.

---

Nitpick comments:
In `@src/main/java/com/loopang/gateway/config/UserContextFilter.java`:
- Around line 67-69: The code currently only reads the top-level role claim via
claims.get("role") into the local variable role, which breaks compatibility with
tokens that still use realm_access.roles; update the logic around String role
(in UserContextFilter) to: first attempt to read the top-level "role" claim, and
if that is null/empty, fall back to reading the "realm_access" claim and
extracting its "roles" list (use the first role or join roles as appropriate)
before setting the X-User-Role header; keep the existing variable names (claims,
role) and header name (X-User-Role) so the change is minimal and clearly
located.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: efa453e6-986a-4fbf-92ff-0a90ae810662

📥 Commits

Reviewing files that changed from the base of the PR and between d3ecfa6 and 83ef9a9.

📒 Files selected for processing (2)
  • src/main/java/com/loopang/gateway/config/UserContextFilter.java
  • src/main/resources/application.yaml

Comment on lines +64 to +65
String companyId = (String) claims.get("companyId");
String hubId = (String) claims.get("hubId");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd -type f -name "UserContextFilter.java" | head -20

Repository: MSA-Service-12th/gateway

Length of output: 238


🏁 Script executed:

head -100 src/main/java/com/loopang/gateway/config/UserContextFilter.java

Repository: MSA-Service-12th/gateway

Length of output: 4322


🏁 Script executed:

cat -n src/main/java/com/loopang/gateway/config/UserContextFilter.java | sed -n '50,100p'

Repository: MSA-Service-12th/gateway

Length of output: 2587


🏁 Script executed:

cat -n src/main/java/com/loopang/gateway/config/UserContextFilter.java | sed -n '1,20p'

Repository: MSA-Service-12th/gateway

Length of output: 1013


🏁 Script executed:

find . -type f -name "*KeycloakIdentityProvider*" -o -name "*KeycloakConfig*" | head -20

Repository: MSA-Service-12th/gateway

Length of output: 50


🏁 Script executed:

rg -i "keycloak|claim" --type java -l | grep -E "(test|config|provider)" | head -20

Repository: MSA-Service-12th/gateway

Length of output: 131


🏁 Script executed:

rg "companyId|hubId" --type java -A 2 -B 2 | head -50

Repository: MSA-Service-12th/gateway

Length of output: 1470


Keycloak JWT 클레임의 무조건적인 String 캐스팅은 런타임 예외를 발생시킬 수 있습니다.

Map<String, Object>인 claims에 대한 직접 (String) 캐스팅은 클레임 값이 숫자, 불리언, 또는 다른 타입으로 들어올 경우 ClassCastException을 발생시킵니다. 필터에서 처리되지 않은 예외는 요청이 5xx 에러로 실패하게 합니다.

이 문제는 64-65번 라인의 companyId, hubId뿐만 아니라 59-63번 라인의 id, email, name, slackId, enabled 및 69번 라인의 role에도 동일하게 적용됩니다. 모든 클레임을 안전하게 문자열로 변환하려면 Objects.toString()을 사용하세요.

수정 예시
+ import java.util.Objects;
...
-          String id = (String) claims.get("sub");
-          String email = (String) claims.get("email");
-          String name = (String) claims.get("name");
-          String slackId = (String) claims.get("slack_id");
-          String enabled = (String) claims.get("is_enabled");
-          String companyId = (String) claims.get("companyId");
-          String hubId = (String) claims.get("hubId");
-          String role = (String) claims.get("role");
+          String id = Objects.toString(claims.get("sub"), "");
+          String email = Objects.toString(claims.get("email"), "");
+          String name = Objects.toString(claims.get("name"), "");
+          String slackId = Objects.toString(claims.get("slack_id"), "");
+          String enabled = Objects.toString(claims.get("is_enabled"), "");
+          String companyId = Objects.toString(claims.get("companyId"), "");
+          String hubId = Objects.toString(claims.get("hubId"), "");
+          String role = Objects.toString(claims.get("role"), "");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/loopang/gateway/config/UserContextFilter.java` around lines
64 - 65, The UserContextFilter currently casts JWT claim values directly (e.g.,
companyId, hubId, id, email, name, slackId, enabled, role) from the claims
Map<String,Object>, which can throw ClassCastException; update the code in
UserContextFilter to replace direct (String) casts with safe conversion using
Objects.toString(claims.get("..."), null) (or a default) for each claim read and
propagate the resulting String into your existing setters so nulls are handled
gracefully instead of throwing runtime exceptions.

Comment thread src/main/resources/application.yaml Outdated
Spring Cloud Gateway는 route id가 unique해야 한다.
PR에서 추가한 새 route(/api/companies/*/items)와 기존 /api/items/** route가
모두 'item-service' id를 사용해 라우트 해석이 불안정해질 위험이 있었음.

새로 추가한 company sub-resource route의 id를 'item-company-service'로 변경.

CodeRabbit 리뷰 코멘트 반영.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@HellWorlding
HellWorlding merged commit bb2165c into main Apr 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Task] yml 파일 수정 및 COMPANY_ID/HUB_ID 헤더 추가

2 participants