ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring Cloud Gateway][업그레이드] discovery locator는 보이는데 route-id-prefix와 RewritePath 기본값이 엇갈릴 때 어떤 actuator snapshot부터 다시 남기나
    백엔드/Java 2026. 8. 12. 20:15

    IT 리서치 노트

    [Spring Cloud Gateway][업그레이드] discovery locator는 보이는데 route-id-prefix와 RewritePath 기본값이 엇갈릴 때 어떤 actuator snapshot부터 다시 남기나

    Spring Cloud Gateway 업그레이드에서 discovery locator route가 actuator에는 보이는데 실제 요청 path나 route id naming이 계속 어긋나는 경우가 있다. 2026년 8월 12일 기준 Spring 공식 문서를 다시 보면 discovery locator는 기본 path predicate와 RewritePath filter를 한 세트로 만들고, appendix에서는 route-id-prefix와 lower-case-service-id가 이 결과를 바꾸는 속성으로 설명된다. 이 글은 discovery locator는 보이는데 route-id-prefix와 RewritePath 기본값이 엇갈릴 때 어떤 actuator snapshot부터 다시 남겨야 하는지 정리한 것이다.

    1. 개요

    결론부터 말하면 discovery locator drift는 route id, predicate path, RewritePath filter를 같은 순간의 actuator snapshot으로 먼저 묶어야 한다. 그다음 route-id-prefix와 lower-case-service-id를 따로 비교하면 route naming 문제와 backend path 문제를 분리하기 쉽다.

    이미 locator smoke test 글이 route source와 locator surface를 분리했다면, 이번 글은 locator 안에서 다시 route-id-prefix와 RewritePath를 분리하는 후속편이다. 또 predicate template와 lowerCaseServiceId 글을 읽었다면 casing 문제를 naming 문제와 섞지 않는 기준도 바로 이어진다.

    2. 어디서 실제로 막히는가

    현장에서 자주 꼬이는 증상은 세 가지다. 첫째, actuator에는 locator route가 보이는데 backend로 넘어가는 path prefix가 남아 있어 404가 난다. 둘째, route id naming이 기대한 값과 달라서 snapshot diff가 계속 틀어진다. 셋째, Eureka나 registry가 대문자 serviceId를 주는데 lower-case-service-id와 route-id-prefix를 같이 바꾸면서 predicate, filters, route id drift가 한꺼번에 발생한다.

    Spring Cloud Gateway reference는 discovery locator의 기본 predicate가 /serviceId/** 패턴의 path predicate라고 설명하고, 기본 filter는 /serviceId/?(?<remaining>.*) 를 /{remaining} 로 바꾸는 RewritePath 라고 적고 있다. 즉 locator route를 본다는 것은 predicate와 filter가 이미 붙어 있다는 뜻이고, 어느 한쪽만 보는 것은 반쪽 점검에 가깝다.

    appendix는 route-id-prefix와 lower-case-service-id를 별도 속성으로 설명한다. route-id-prefix는 actuator route id naming을, lower-case-service-id는 predicates와 filters 안의 serviceId casing까지 바꾼다. 이 둘을 같은 커밋에서 바꾸면 snapshot을 한 장으로만 남겼을 때 drift 원인이 섞인다.

    • 증상: route는 보이는데 backend path가 계속 /serviceId/... 형태로 남는다.
    • 실패: predicate만 보고 RewritePath filter snapshot을 안 남긴다.
    • 막힘: route-id-prefix와 lower-case-service-id를 한 번에 바꾸고 diff를 한 장만 본다.
    • 누락: actuator route id와 실제 registry serviceId casing을 같이 기록하지 않는다.
    증상 먼저 볼 곳 판단 기준
    backend로 path prefix가 남아 간다 RewritePath filter snapshot regex와 replacement가 기본값과 같은지 본다
    actuator route id가 예상과 다르다 route-id-prefix prefix가 누락되거나 중복됐는지 본다
    대소문자만 바꿨는데 전체 route가 달라진다 lower-case-service-id predicates와 filters 안의 serviceId까지 바뀌는지 본다

    3. 실무에서 적용하는 순서

    실무 점검 순서는 다섯 단계가 실용적이다. 먼저 actuator route snapshot에서 route id, predicates, filters를 같은 시점에 캡처한다. 두 번째로 registry가 주는 원본 serviceId casing을 조회한다. 세 번째로 route-id-prefix와 lower-case-service-id 현재 값을 기록한다. 네 번째로 기본 RewritePath regex와 replacement가 유지됐는지 비교한다. 마지막으로 그 뒤에야 custom predicates나 custom filters가 기본 동작을 덮었는지 확인한다.

    1. actuator route id, predicates, filters를 한 번에 캡처한다.
    2. registry가 주는 원본 serviceId casing을 적는다.
    3. route-id-prefix와 lower-case-service-id 값을 기록한다.
    4. RewritePath 기본 regex와 replacement를 비교한다.
    5. 그다음 custom predicates와 filters override를 본다.

    실제 triage에서는 actuator JSON을 조회하고, route id를 기록하고, predicate path를 비교하고, RewritePath regex를 확인하고, replacement 값을 비교하고, registry serviceId 응답을 다시 조회해야 한다. 이 여섯 단계를 같은 체크리스트에 적어 두면 누가 실행해도 같은 순서로 재현할 수 있다. 여기에 application.yml 설정을 확인하고, actuator 응답 본문을 저장하고, 수정 전후 로그를 비교하고, 재요청 응답을 다시 확인하는 절차까지 붙이면 drift 재검증이 훨씬 빨라진다.

    이 순서를 쓰면 drift가 훨씬 빨리 갈린다. route id만 다르면 route-id-prefix를 먼저 보면 되고, backend path만 이상하면 RewritePath filter부터 보면 된다. casing만 바뀐 줄 알았는데 predicates와 filters 전체가 달라졌다면 lower-case-service-id 설정을 먼저 다시 확인하는 편이 맞다.

    actuator 기록 예시
    route_id=discoveryClient_orders
    predicate_path=/orders/**
    rewrite_regex=/orders/?(?<remaining>.*)
    rewrite_replacement=/{remaining}
    route_id_prefix=discoveryClient_
    lower_case_service_id=true
    registry_service_id=ORDERS

    같은 포맷으로 snapshot을 남겨 두면 upgrade commit 전후 diff도 보기 쉬워진다. property file, actuator output, registry serviceId 세 장을 같은 시점에 묶어 두는 것이 핵심이다.

    4. 공식 문서와 예시 화면으로 확인하기

    첫 실제 자료는 discovery locator 기본 predicate 설명이다. 기본 locator는 /serviceId/** 패턴의 path predicate를 자동으로 만든다.

    Spring Cloud Gateway 문서는 discovery locator의 기본 predicate가 /serviceId/** 패턴이라고 설명한다.
    Spring Cloud Gateway 문서는 discovery locator의 기본 predicate가 /serviceId/** 패턴이라고 설명한다.

    즉 actuator에 route가 보인다고 끝이 아니라, 실제 path predicate가 기대한 serviceId 대소문자와 경로를 쓰는지 같이 봐야 한다. locator enabled만 확인하고 지나가면 이 분기를 놓친다.

    두 번째 자료는 기본 RewritePath 설명이다. locator는 path predicate만 자동으로 만드는 것이 아니라 rewrite path filter도 같이 붙인다.

    Spring Cloud Gateway 문서는 discovery locator의 기본 filter가 RewritePath 라고 적고 있다.
    Spring Cloud Gateway 문서는 discovery locator의 기본 filter가 RewritePath 라고 적고 있다.

    이 지점을 놓치면 route-id는 맞아 보이는데 실제 backend로 넘어가는 path가 계속 달라지는 증상이 생긴다. predicate와 filter를 따로 snapshot으로 남겨야 하는 이유가 여기 있다.

    세 번째 실제 자료는 appendix의 route-id-prefix 속성이다. locator route id는 discoveryClient class simple name과 serviceId 조합이 기본이지만 prefix를 따로 줄 수도 있다.

    Spring Cloud Gateway appendix는 route-id-prefix 속성이 locator route id 생성 방식에 관여한다고 설명한다.
    Spring Cloud Gateway appendix는 route-id-prefix 속성이 locator route id 생성 방식에 관여한다고 설명한다.

    업그레이드 중 actuator route id와 기대한 route naming이 어긋난다면, route-id-prefix를 먼저 의심해야 한다. rewrite filter만 고치고 route id naming을 놔두면 snapshot 비교가 계속 어긋난다.

    네 번째 실제 자료는 lower-case-service-id 속성이다. 이 값은 predicate와 filters 안의 serviceId 대소문자까지 같이 바꾼다.

    Spring Cloud Gateway appendix는 lower-case-service-id가 predicates와 filters의 serviceId 대소문자에 영향을 준다고 설명한다.
    Spring Cloud Gateway appendix는 lower-case-service-id가 predicates와 filters의 serviceId 대소문자에 영향을 준다고 설명한다.

    따라서 route-id-prefix와 lower-case-service-id를 따로 기록하지 않으면, actuator route id drift와 RewritePath drift가 한 문제처럼 보이기 쉽다.

    업그레이드 triage는 snapshot 표가 있어야 빠르다. route id, predicate path, RewritePath regex, serviceId casing을 한 장에 넣어야 변경 지점을 바로 찾을 수 있다.

    discovery locator drift를 route-id-prefix와 RewritePath 기준으로 나눠 보는 snapshot 표다.
    discovery locator drift를 route-id-prefix와 RewritePath 기준으로 나눠 보는 snapshot 표다.

    이미 predicate template와 lowerCaseServiceId 글을 읽었다면, 이번 표는 그 다음 단계인 route id naming까지 포함한 actuator snapshot 판이다.

    마지막 자료는 actuator snapshot 예시다. 업그레이드 중에는 path와 filter와 route id를 같은 순간에 캡처해야 비교가 된다.

    discovery locator drift를 재현할 때 남기면 좋은 actuator snapshot 예시다.
    discovery locator drift를 재현할 때 남기면 좋은 actuator snapshot 예시다.

    이 정도 메모만 있어도 route source 문제인지, locator naming 문제인지, RewritePath 문제인지 훨씬 빨리 갈린다. locator smoke test 글과 이어 보면 smoke surface를 어디까지 넓혀야 하는지도 정리된다.

    5. 주의사항과 리스크

    첫 번째 리스크는 locator route가 보인다는 이유로 RewritePath filter를 생략해 점검하는 것이다. 두 번째 리스크는 route-id-prefix와 lower-case-service-id를 동시에 바꾸고도 단일 snapshot만 남기는 것이다. 세 번째 리스크는 registry가 주는 serviceId casing과 actuator route id casing을 같은 값이라고 가정하는 것이다.

    또 custom predicates나 filters를 추가할 때 기본 predicate와 RewritePath를 빠뜨리면 문서의 기본 동작이 사라질 수 있다. Spring 문서도 custom locator predicates와 filters를 쓸 때는 기본 predicate와 filter를 유지하려면 직접 포함해야 한다고 설명한다. 따라서 '커스텀을 넣었다'는 사실 자체를 snapshot에 남겨야 한다.

    • route id drift와 path rewrite drift를 같은 현상으로 합치지 않는다.
    • custom locator 설정을 넣었다면 기본 predicate와 filter 포함 여부를 다시 본다.
    • registry serviceId casing과 actuator snapshot을 같이 남긴다.

    6. 결론

    discovery locator가 보이는데도 실제 라우팅이 어긋난다면, route id, predicate path, RewritePath filter를 같은 actuator snapshot으로 먼저 묶는 편이 가장 빠르다. 그다음 route-id-prefix와 lower-case-service-id를 따로 비교하면 naming 문제와 path rewrite 문제를 분리할 수 있다.

    • route id와 RewritePath를 같은 순간에 캡처한다.
    • route-id-prefix와 lower-case-service-id를 따로 기록한다.
    • 기본 predicate와 filter를 잃지 않았는지 마지막에 확인한다.

    7. 참고 링크

    1. https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/
    2. https://docs.spring.io/spring-cloud-gateway/reference/appendix.html
Designed by Tistory.