-
[Spring Cloud Gateway][운영] routes.count는 같은데 target-route snapshot이 다를 때 handler-mapping.order 다음에 무엇을 다시 보나백엔드/Spring 2026. 8. 27. 20:15
IT 리서치 노트
[Spring Cloud Gateway][운영] routes.count는 같은데 target-route snapshot이 다를 때 handler-mapping.order 다음에 무엇을 다시 보나
Spring Cloud Gateway 운영에서 routes.count가 같으면 많은 팀이 route 구성이도 같다고 생각한다. 하지만 2026년 8월 27일 기준 공식 문서를 다시 보면 `spring.cloud.gateway.routes.count`는 RouteDefinitions 총개수이고, `/gateway` actuator detail은 개별 route 구성과 order를 보여 주며, handler-mapping.order는 그보다 바깥의 진입 순서다. 그래서 handler-mapping.order를 이미 분리했다면 그 다음에는 target route snapshot 자체를 다시 비교해야 한다.
1. 개요
결론부터 말하면 routes.count가 같아도 target-route snapshot이 다를 수 있으므로, handler-mapping.order 다음에는
route id,uri,predicates,filters,mappings timestamp를 한 묶음으로 다시 남겨야 한다. count parity는 총량 확인일 뿐, 개별 route 구성이 같다는 보장은 아니다.같은 Spring Cloud Gateway branch를 이어 읽는다면 handler-mapping.order와 route order snapshot을 분리한 글과 refresh 200인데 route가 안 바뀔 때 보는 글이 앞단 문맥이 된다.
2. 어디서 실제로 막히는가
가장 흔한 실패는 routes.count parity를 성공 신호처럼 해석하는 것이다. 공식 metrics 문서는 이 값이 RouteDefinitions 개수라고 설명한다. 즉 열 개가 열 개인지만 알려 줄 뿐, 어느 route가 어떤 uri와 filter chain을 가졌는지는 말해 주지 않는다.
두 번째 실패는 handler-mapping.order를 이미 점검한 뒤에도 같은 메모를 계속 재사용하는 것이다. 그러면 target route detail, mappings snapshot, filter chain 비교가 모두 한 단락에 섞인다. 나중에 보면 count 문제였는지, route detail 문제였는지, 진입 순서 문제였는지 구분이 사라진다.
세 번째 실패는
/actuator/gateway/routes목록만 보고 끝내는 것이다. target route가 목록에 있다고 해도 개별 route detail의 order, predicates, filters, uri가 달라졌을 수 있다. 특히 RewritePath나 StripPrefix 같은 filter가 얽히면 count parity는 거의 도움이 되지 않는다.마지막으로 mappings 시각을 안 남기는 것도 문제다. routes와 mappings와 metrics를 서로 다른 시점에 모으면 같은 배포 기준인지 확신하기 어렵다. target drift를 다루는 단계에서는 snapshot timestamp가 곧 비교 정확도다.
- 증상: routes.count는 같은데 실제 프록시 대상이나 경로 매칭이 기대와 다르다.
- 실패: 총개수 지표를 개별 route 동일성으로 읽는다.
- 막힘: route 목록과 route detail과 mappings를 한 메모에 섞어 적는다.
- 누락: 수집 시각과 filter chain 해시를 안 남긴다.
같아 보여도 다른 값 실제 의미 다시 남길 이유 routes.count RouteDefinitions 총개수 동일 route 구성 여부는 알려 주지 않기 때문 handler-mapping.order 진입 handler 순서 개별 route detail과 층위가 다르기 때문 target-route snapshot uri, predicates, filters, route order 실제 프록시 동작 차이를 설명하기 위해 3. 실무에서 적용하는 순서
가장 짧은 점검 순서는 네 단계다. 첫째, actuator
/gateway접근 모드와 수집 시각을 기록한다. 둘째,spring.cloud.gateway.routes.count로 총량 parity를 확인한다. 셋째,/actuator/gateway/routes와/actuator/gateway/routes/{id}에서 target route detail을 저장한다. 넷째,/actuator/mappings를 같은 시각으로 붙여 진입 층위가 아니라 route detail 층위 문제인지 확인한다.이후 메모는 count 영역과 detail 영역을 나눠 두는 편이 좋다. count 영역에는 gauge 값과 수집 시각만 적고, detail 영역에는 route id, uri, predicates, filters, order를 적는다. 이렇게 분리해야 다음 변경이 어느 층위를 건드렸는지 분명해진다.
특히 target drift 단계에서는 해시를 써도 좋다. predicate 문자열과 filter 목록을 그대로 붙이는 대신 hash와 대표 필드만 남기면 diff를 읽기가 더 쉬워진다.
실제로는 콘솔 한 곳만 보지 말고 같은 배포 직후 명령을 차례로 실행해 응답을 조회하고 파일로 저장해야 한다. 먼저 gateway 접근 설정을 확인하고, 다음으로 metrics 응답을 조회하고, 이어서 route detail 응답과 mappings 응답을 조회하고, 마지막으로 저장한 파일과 로그를 비교한다. 이 절차를 매번 같은 순서로 실행해야 오류가 route count 문제인지 target route 문제인지 짧게 분리된다.
관련 글로는 handler-mapping.order와 route order 분리 글, include-expression과 route set drift 분리 글, Java DSL route source 점검 글을 같이 두면 branch가 자연스럽다.
4. 공식 문서와 예시 화면으로 확인하기
첫 자료는 Spring Cloud Gateway actuator API 문서다. 이 화면에서는 `/gateway` endpoint 접근이 기본 비활성이고 read-only 또는 unrestricted로 열어야 한다는 점을 먼저 확인해야 한다.
즉 routes.count와 route dump를 비교하기 전에 actuator 접근이 현재 어떤 모드인지 남겨야 한다. 접근이 안 열린 상태의 빈 비교는 아무 의미가 없다.
두 번째 자료는 configuration 문서의 RouteDefinition metrics 구간이다. 여기서는 `spring.cloud.gateway.routes.count`가 RouteDefinitions 개수를 보여 주는 gauge라는 점을 읽어야 한다.
이 값이 같다고 해서 target route detail까지 같다는 뜻은 아니다. routes.count는 총량 지표이고, target-route snapshot은 개별 route 구성 지표다. 둘을 분리해서 봐야 하는 이유가 여기서 나온다.
세 번째 자료는 appendix의 handler-mapping.order 항목이다. 이전 글에서 다뤘듯 이 값은 route order와 비슷해 보여도 전혀 다른 층위다.
이번 글은 그 다음 단계다. handler-mapping.order가 그대로인데도 route behavior가 다르면, 이제는 target route detail과 filter chain snapshot을 다시 봐야 한다.
실무에서 도움이 되는 것은 count와 detail과 mappings를 같은 시각으로 비교하는 표다. 아래 표는 routes.count가 같을 때 추가로 붙여야 할 snapshot 열을 정리한 것이다.
같은 Spring branch를 이어 읽는다면 handler-mapping.order와 route order snapshot 분리 글이 앞단이다. 이번 표는 그 이후에 target route detail을 어떻게 고정할지에 집중한다.
마지막 자료는 실제 점검 순서를 코드처럼 적어 둔 예시다. count와 route detail과 mappings를 같은 메모에 붙이면 어느 시점의 스냅샷인지 모호해지기 쉽다.
이 정도 순서만 고정해도 count parity 단계와 target drift 단계가 섞이지 않는다. 이미 include-expression과 route set drift를 분리한 글을 읽었다면 이번 글이 그 다음 확인 순서가 된다.
5. 주의사항과 리스크
첫 번째 리스크는 routes.count가 같으니 route도 같다고 단정하는 것이다. 두 번째 리스크는 route detail과 mappings를 다른 시점에 모아 같은 배포 비교처럼 쓰는 것이다. 세 번째 리스크는 predicates와 filters를 남기지 않아 uri만 보고 문제를 추정하는 것이다.
운영 전에 최소한
target_route_id,target_uri,predicate_hash,filter_chain_hash,snapshot_at은 남기는 편이 좋다. 이 다섯 값이 없으면 target drift를 다시 좁히는 데 시간이 더 든다.- routes.count parity는 총량 확인이다.
- target-route snapshot은 개별 동작 확인이다.
- 같은 시각 기준 snapshot이 아니면 비교 정확도가 떨어진다.
6. 결론
Spring Cloud Gateway에서 routes.count가 같은데 동작이 다르면, 다음 단계는 handler-mapping.order를 또 만지는 일이 아니라 target-route snapshot을 다시 고정하는 일이다. route id, uri, predicates, filters, mappings 시각을 남기면 총량 parity 이후의 차이를 더 짧게 설명할 수 있다.
- count와 detail 메모를 분리한다.
- target route의 uri와 filter chain을 같이 남긴다.
- snapshot 수집 시각을 붙여 같은 배포 비교인지 보장한다.
routes.count 다음에 실제 체인을 어디서 깨끗하게 비교해야 하는지까지 이어가려면 RewritePath는 맞는데 filter order와 actuator route dump가 어긋날 때 어떤 로그부터 비교하나를 같이 보면 handler mapping 이후 점검 순서가 더 선명해진다.
7. 참고 링크
- https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway-server-webflux/actuator-api.html
- https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway-server-webflux/configuration.html
- https://docs.spring.io/spring-cloud-gateway/reference/appendix.html
- https://docs.spring.io/spring-boot/api/rest/actuator/mappings.html
'백엔드 > Spring' 카테고리의 다른 글