kjp0411 님의 블로그

내일배움캠프 단기심화 Java - 본 캠프 Day 60 본문

TIL

내일배움캠프 단기심화 Java - 본 캠프 Day 60

kjp0411 2026. 5. 8. 16:08

2026.05.08 TIL

오늘 한 일

오늘은 MSA 티켓팅 시스템의 로컬 통합 테스트 환경을 점검하면서 Gateway를 통해 User Service와 Payment Service를 호출할 수 있도록 인증/라우팅 문제를 해결했다.

특히 Gateway에서 발생하던 401 Unauthorized, 503 Service Unavailable, User Service 내부에서 발생하던 401 Unauthorized 문제를 순서대로 추적했다.

최종적으로 Keycloak access token을 사용해 Gateway를 경유한 GET /api/users 요청이 정상적으로 200 OK 응답을 반환하는 것까지 확인했다.


문제 상황

처음에는 Gateway를 통해 API를 호출하면 계속 401 Unauthorized가 발생했다.

단순히 토큰이 잘못된 문제인지, Gateway에서 막히는 문제인지, User Service까지 도달한 뒤 막히는 문제인지 구분이 되지 않았다.

그래서 Gateway와 User Service에 요청/응답 로그를 추가해서 요청 흐름을 추적했다.

확인해야 했던 흐름은 다음과 같았다.

Client / Postman
  → Gateway
  → Gateway JWT 검증
  → Gateway Route Matching
  → Eureka 기반 서비스 라우팅
  → User Service
  → User Service JWT 검증
  → Controller
 

1. Gateway 401 문제

원인

Gateway에서 JWT 검증에 실패하고 있었다.

로그에서는 다음과 같은 오류가 확인되었다.

The iss claim is not valid
Signed JWT rejected: Another algorithm expected, or no matching key(s) found
 

Keycloak에서 발급받은 access token의 iss 값은 다음과 같았다.

http://localhost:18080/realms/ticketing
 

그런데 Gateway의 JWT 설정이 이 값과 맞지 않아 인증에 실패했다.

또한 Docker 컨테이너 내부에서 localhost:18080은 Keycloak이 아니라 Gateway 컨테이너 자기 자신을 의미한다.

따라서 jwk-set-uri는 Docker Compose 네트워크에서 접근 가능한 keycloak:8080 주소를 사용해야 했다.

해결

Gateway의 JWT 설정을 다음 기준으로 정리했다.

 
issuer-uri: http://localhost:18080/realms/ticketing
jwk-set-uri: http://keycloak:8080/realms/ticketing/protocol/openid-connect/certs
 

정리하면 다음과 같다.

issuer-uri
→ JWT 토큰 안의 iss 값과 정확히 일치해야 함

jwk-set-uri
→ Gateway 컨테이너가 Keycloak 공개키를 가져올 수 있는 Docker 내부 주소여야 함
 

2. Gateway 503 문제

원인

Gateway의 JWT 검증은 통과했지만, 이번에는 503 Service Unavailable이 발생했다.

Gateway 로그에서 다음 오류가 확인되었다.

No servers available for service: USER-SERVICE
Unable to find instance for USER-SERVICE
 

Eureka에는 User Service가 등록되어 있었지만, Gateway route 설정은 다음처럼 대문자 서비스명을 사용하고 있었다.

 
uri: lb://USER-SERVICE
 

반면 Eureka에 등록된 vipAddress는 소문자였다.

user-service
 

즉 Gateway LoadBalancer가 USER-SERVICE라는 이름으로 인스턴스를 찾았지만, Eureka에는 user-service로 등록되어 있어 인스턴스를 찾지 못했다.

해결

Gateway route URI를 Eureka 등록명 기준으로 소문자로 통일했다.

 
uri: lb://user-service
 

다른 서비스들도 같은 방식으로 정리했다.

 
lb://user-service
lb://club-service
lb://match-service
lb://seat-service
lb://reservation-service
lb://queue-service
lb://payment-service
 

이후 Gateway의 503 문제는 해결되었다.


3. User Service 401 문제

원인

Gateway에서는 JWT 검증과 라우팅이 통과했지만, User Service 내부에서 다시 401이 발생했다.

User Service 로그에서는 다음 오류가 확인되었다.

The iss claim is not valid
Failed to authenticate since the JWT was invalid
 

즉 Gateway는 통과했지만, User Service의 JWT issuer 설정이 토큰의 iss 값과 맞지 않아 User Service 내부 JWT 검증에서 실패한 것이다.

해결

User Service에도 Gateway와 동일한 기준으로 JWT 검증 설정을 추가했다.

 
KEYCLOAK_ISSUER_URI: http://localhost:18080/realms/ticketing
KEYCLOAK_JWK_SET_URI: http://keycloak:8080/realms/ticketing/protocol/openid-connect/certs

SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: http://localhost:18080/realms/ticketing
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: http://keycloak:8080/realms/ticketing/protocol/openid-connect/certs
 

이후 Gateway를 경유한 GET /api/users 요청이 정상적으로 성공했다.


추가한 로그

Gateway에는 요청 진입/응답 로그와 401/403 예외 로그를 추가했다.

확인 가능한 정보는 다음과 같다.

[GW-IN]
- method
- path
- query
- Authorization 헤더 존재 여부
- Authorization 헤더 길이
- routeId

[GW-OUT]
- method
- path
- status
- routeId

[GW-401]
- Gateway 인증 실패 원인

[GW-403]
- Gateway 인가 실패 원인
 

User Service에도 요청 진입/응답 로그와 401/403 예외 로그를 추가했다.

[USER-IN]
- method
- uri
- query
- Authorization 헤더 존재 여부
- X-User-Id 헤더

[USER-OUT]
- method
- uri
- status
- 인증 여부
- principal

[USER-401]
- User Service 인증 실패 원인

[USER-403]
- User Service 인가 실패 원인

[USER-JWT]
- JWT subject
- issuer
- authorities
 

이 로그 덕분에 요청이 Gateway에서 막히는지, User Service까지 도달했는지, User Service 내부 JWT 검증에서 실패하는지 구분할 수 있었다.


Docker Compose 설정 정리

오늘 통합 테스트를 위해 docker-compose.yml에도 여러 설정을 추가했다.

주요 설정은 다음과 같다.

 
SPRING_CONFIG_IMPORT: optional:configserver:http://config-server:10002
SPRING_CLOUD_CONFIG_URI: http://config-server:10002

EUREKA_DEFAULT_ZONE: http://eureka-server:10001/eureka/
EUREKA_CLIENT_SERVICEURL_DEFAULTZONE: http://eureka-server:10001/eureka/
EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE: http://eureka-server:10001/eureka/

KEYCLOAK_ISSUER_URI: http://localhost:18080/realms/ticketing
KEYCLOAK_JWK_SET_URI: http://keycloak:8080/realms/ticketing/protocol/openid-connect/certs
 

특히 Config Server와 Eureka Server가 먼저 떠 있어야 서비스들이 정상 설정을 받아오고 Eureka에 등록될 수 있었다.


테스트 결과

최종적으로 다음을 확인했다.

GET http://localhost:10000/api/users
Authorization: Bearer {access_token}
 

결과:

200 OK
 

즉 다음 흐름이 정상 동작했다.

Postman
→ Gateway
→ Keycloak JWT 검증
→ Eureka 기반 user-service 라우팅
→ User Service JWT 검증
→ User Controller
→ DB 조회
→ 200 OK
 

Eureka에는 다음 서비스들이 등록된 것을 확인했다.

GATEWAY-SERVICE
USER-SERVICE
PAYMENT-SERVICE
CLUB-SERVICE
SEAT-SERVICE
 

아직 남은 문제

아직 모든 서비스가 완전히 정상 등록된 것은 아니다.

현재 추가 확인이 필요한 서비스는 다음과 같다.

MATCH-SERVICE
QUEUE-SERVICE
RESERVATION-SERVICE
 

확인된 문제는 다음과 같다.

Match Service

ERROR: relation "p_outbox" does not exist
 

Outbox 스케줄러가 p_outbox 테이블을 조회하지만, 해당 테이블이 없어서 오류가 발생하고 있다.

Queue Service

Zipkin 연결 관련 로그가 발생했다.

Zipkin ConnectException
UnresolvedAddressException
 

Zipkin 컨테이너 실행 여부 또는 tracing 설정 확인이 필요하다.

Reservation Service

다음 오류로 인해 계속 재시작되고 있다.

http://${feign.payment-service.url} is malformed
 

feign.payment-service.url 값이 치환되지 않아 Feign Client URL이 잘못 생성되는 문제다.


배운 점

이번 문제를 해결하면서 MSA 환경에서는 단순히 애플리케이션이 실행되는 것만으로는 충분하지 않다는 것을 다시 느꼈다.

특히 다음 요소들이 모두 맞아야 정상적인 통합 테스트가 가능하다.

1. Docker Compose 네트워크 주소
2. Config Server 설정 로딩
3. Eureka 등록 여부
4. Gateway route URI와 Eureka 서비스명 일치 여부
5. Keycloak JWT issuer-uri 일치 여부
6. 컨테이너 내부에서 접근 가능한 jwk-set-uri 설정
7. 각 서비스의 Resource Server JWT 설정
 

또한 401, 403, 503은 모두 비슷하게 API 호출 실패처럼 보이지만 실제 원인은 전혀 다를 수 있다.

401
→ JWT 인증 실패

403
→ 인증은 되었지만 권한 부족

503
→ Gateway가 대상 서비스 인스턴스를 찾지 못하거나 라우팅 실패
 

이번에는 로그를 직접 추가하면서 요청 흐름을 단계별로 분리해서 확인했고, 그 결과 Gateway 문제인지 User Service 문제인지 정확하게 구분할 수 있었다.


오늘의 정리

오늘은 Gateway, User Service, Payment Service 중심의 로컬 통합 테스트 환경을 정리했다.

가장 큰 성과는 Keycloak JWT 인증, Gateway 라우팅, Eureka 기반 서비스 디스커버리, User Service JWT 검증까지 이어지는 흐름을 실제로 성공시킨 것이다.

아직 Match, Queue, Reservation Service에는 남은 문제가 있지만, 오늘 해결한 Gateway/User 인증 및 라우팅 문제는 이후 다른 서비스 연동 문제를 해결하는 기준점이 될 것 같다.