[Trouble Shooting] JWT roles 배열 불일치로 인한 빈 응답 문제
Portal에서 roles를 배열로 발급하는데 하위 서비스에서 단수 role로 읽어 빈 데이터가 반환되는 문제 해결
문제 상황
멀티 서비스 포탈 구조에서, 특정 하위 서비스의 데이터 조회 API가 항상 빈 배열을 반환하는 현상이 발생함.
- 로그인은 정상적으로 됨
- 다른 서비스는 정상 동작
- 해당 서비스만 데이터가 안 나옴
추가로, 로그인 실패 시 “비밀번호가 틀렸습니다” 대신 “인증이 만료되었습니다” 라는 부정확한 에러 메시지가 표시되는 문제도 함께 발견됨.
환경
- Portal: Express + JWT 기반 인증
- 하위 서비스: Express, 독립적인 auth middleware 사용
- 구조: Portal에서 JWT 발급 → 하위 서비스에서 토큰 검증 후 권한 기반 데이터 반환
원인 분석
1. JWT 토큰 구조 불일치
Portal에서 발급하는 JWT 토큰:
1
2
3
4
5
6
// Portal JWT payload
{
sub: userId,
tenantId: "tenant-001",
roles: ["super_admin", "service_manager"] // 배열
}
하위 서비스의 auth middleware:
1
2
3
4
5
6
// 하위 서비스 auth middleware
req.user = {
role: decoded.role, // ❌ 단수 'role'로 접근 → undefined
tenantId: decoded.tenantId,
userId: decoded.sub
};
roles(복수)로 보내는데 role(단수)로 읽고 있었음. 당연히 undefined.
2. Controller의 role 기반 분기 문제
1
2
3
4
5
6
7
8
// controller
switch (role) {
case 'companies_admin': // Portal에 없는 role
case 'company_admin': // Portal에 없는 role
return getAllData();
default:
return { items: [] }; // 여기로 빠짐
}
Portal에서 사용하는 role 체계(super_admin, service_manager)와 하위 서비스에서 기대하는 role 체계(companies_admin, company_admin)가 완전히 달랐음.
결과적으로:
roles→role로 읽어서undefinedundefined는 어떤 case에도 매칭 안 됨default로 빠져서 빈 배열 반환
해결 방법
Before
1
2
3
4
5
6
7
// auth middleware
const decoded = jwt.verify(token, secret);
req.user = {
role: decoded.role, // undefined
tenantId: decoded.tenantId,
userId: decoded.sub
};
After
1
2
3
4
5
6
7
8
9
10
// auth middleware
const decoded = jwt.verify(token, secret);
const roles = decoded.roles || [];
req.user = {
roles: roles,
role: roles[0] || null,
tenantId: decoded.tenantId,
userId: decoded.sub
};
1
2
3
4
5
6
7
8
9
10
11
12
// controller
const { roles } = req.user;
const hasAdminAccess = roles.some(r =>
['super_admin', 'portal_admin', 'companies_admin', 'company_admin'].includes(r)
);
if (hasAdminAccess) {
return getAllData();
}
return getDataByRole(roles);
핵심 변경:
role(단수) →roles(배열) 정상 매핑switch분기 대신Array.some()으로 role 포함 여부 체크- Portal role 체계와 레거시 role 체계 모두 허용
결과
- 하위 서비스 데이터 정상 조회 확인
- Portal admin 계정으로 전체 데이터 접근 가능
- 기존 레거시 role도 하위 호환 유지
정리
- JWT 토큰의 claim 이름은 발급 측과 검증 측이 정확히 일치해야 함.
rolevsroles같은 단수/복수 차이도 치명적임. - 멀티 서비스 구조에서 각 서비스가 독립적으로 auth를 구현하면 이런 불일치가 생기기 쉬움. 공통 auth 미들웨어를 shared 패키지로 관리하는 게 맞음.
switch로 role을 하드코딩하는 패턴은 role 체계가 바뀔 때마다 깨짐. 허용 role 목록을 설정으로 분리하는 게 유지보수에 유리함.
This post is licensed under CC BY 4.0 by the author.