API Gateway 라우팅 설정 개선

- 서비스 디스커버리 제거하고 직접 URL 라우팅으로 변경
- application.yml 환경변수 기반 동적 라우팅 적용
- 로그인/리프레시 API 경로 수정 (/api/v1/auth/login, /api/v1/auth/refresh)
- 사용자 관련 API 경로 추가 (/api/v1/users/**)
- JWT 인증 필터 적용 및 Circuit Breaker 설정 유지

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
hiondal 2025-09-10 12:27:34 +09:00
parent 08aec4251c
commit 9bfdeda316
3 changed files with 25 additions and 18 deletions

View File

@ -32,8 +32,20 @@ public class GatewayConfig {
private final JwtAuthenticationGatewayFilterFactory jwtAuthFilter; private final JwtAuthenticationGatewayFilterFactory jwtAuthFilter;
@Value("${cors.allowed-origins") @Value("${cors.allowed-origins}")
private String allowedOrigins; private String allowedOrigins;
@Value("${services.user-service.url}")
private String userServiceUrl;
@Value("${services.bill-service.url}")
private String billServiceUrl;
@Value("${services.product-service.url}")
private String productServiceUrl;
@Value("${services.kos-mock.url}")
private String kosMockUrl;
public GatewayConfig(JwtAuthenticationGatewayFilterFactory jwtAuthFilter) { public GatewayConfig(JwtAuthenticationGatewayFilterFactory jwtAuthFilter) {
this.jwtAuthFilter = jwtAuthFilter; this.jwtAuthFilter = jwtAuthFilter;
@ -53,17 +65,15 @@ public class GatewayConfig {
return builder.routes() return builder.routes()
// Auth Service 라우팅 (인증 불필요) // Auth Service 라우팅 (인증 불필요)
.route("user-service-login", r -> r .route("user-service-login", r -> r
.path("/api/auth/login", "/api/auth/refresh") .path("/api/v1/auth/login", "/api/v1/auth/refresh")
.and() .and()
.method("POST") .method("POST")
.filters(f -> f.rewritePath("/api/auth/(?<segment>.*)", "/api/v1/auth/${segment}")) .uri(userServiceUrl))
.uri("lb://user-service"))
// Auth Service 라우팅 (인증 필요) // Auth Service 라우팅 (인증 필요)
.route("user-service-authenticated", r -> r .route("user-service-authenticated", r -> r
.path("/api/auth/**") .path("/api/v1/auth/**", "/api/v1/users/**")
.filters(f -> f .filters(f -> f
.rewritePath("/api/auth/(?<segment>.*)", "/api/v1/auth/${segment}")
.filter(jwtAuthFilter.apply(new JwtAuthenticationGatewayFilterFactory.Config())) .filter(jwtAuthFilter.apply(new JwtAuthenticationGatewayFilterFactory.Config()))
.circuitBreaker(cb -> cb .circuitBreaker(cb -> cb
.setName("user-service-cb") .setName("user-service-cb")
@ -71,13 +81,12 @@ public class GatewayConfig {
.retry(retry -> retry .retry(retry -> retry
.setRetries(3) .setRetries(3)
.setBackoff(java.time.Duration.ofSeconds(2), java.time.Duration.ofSeconds(10), 2, true))) .setBackoff(java.time.Duration.ofSeconds(2), java.time.Duration.ofSeconds(10), 2, true)))
.uri("lb://user-service")) .uri(userServiceUrl))
// Bill-Inquiry Service 라우팅 (인증 필요) // Bill-Inquiry Service 라우팅 (인증 필요)
.route("bill-service", r -> r .route("bill-service", r -> r
.path("/api/bills/**") .path("/api/v1/bills/**")
.filters(f -> f .filters(f -> f
.rewritePath("/api/bills/(?<segment>.*)", "/api/v1/bills/${segment}")
.filter(jwtAuthFilter.apply(new JwtAuthenticationGatewayFilterFactory.Config())) .filter(jwtAuthFilter.apply(new JwtAuthenticationGatewayFilterFactory.Config()))
.circuitBreaker(cb -> cb .circuitBreaker(cb -> cb
.setName("bill-service-cb") .setName("bill-service-cb")
@ -86,13 +95,12 @@ public class GatewayConfig {
.setRetries(3) .setRetries(3)
.setBackoff(java.time.Duration.ofSeconds(2), java.time.Duration.ofSeconds(10), 2, true)) .setBackoff(java.time.Duration.ofSeconds(2), java.time.Duration.ofSeconds(10), 2, true))
) )
.uri("lb://bill-service")) .uri(billServiceUrl))
// Product-Change Service 라우팅 (인증 필요) // Product-Change Service 라우팅 (인증 필요)
.route("product-service", r -> r .route("product-service", r -> r
.path("/api/products/**") .path("/api/v1/products/**")
.filters(f -> f .filters(f -> f
.rewritePath("/api/products/(?<segment>.*)", "/products/${segment}")
.filter(jwtAuthFilter.apply(new JwtAuthenticationGatewayFilterFactory.Config())) .filter(jwtAuthFilter.apply(new JwtAuthenticationGatewayFilterFactory.Config()))
.circuitBreaker(cb -> cb .circuitBreaker(cb -> cb
.setName("product-service-cb") .setName("product-service-cb")
@ -101,13 +109,12 @@ public class GatewayConfig {
.setRetries(3) .setRetries(3)
.setBackoff(java.time.Duration.ofSeconds(2), java.time.Duration.ofSeconds(10), 2, true)) .setBackoff(java.time.Duration.ofSeconds(2), java.time.Duration.ofSeconds(10), 2, true))
) )
.uri("lb://product-service")) .uri(productServiceUrl))
// KOS Mock Service 라우팅 (인증 불필요 - 목업용) // KOS Mock Service 라우팅 (인증 불필요 - 목업용)
.route("kos-mock-service", r -> r .route("kos-mock-service", r -> r
.path("/api/kos/**") .path("/api/v1/kos/**")
.filters(f -> f .filters(f -> f
.rewritePath("/api/kos/(?<segment>.*)", "/kos/${segment}")
.circuitBreaker(cb -> cb .circuitBreaker(cb -> cb
.setName("kos-mock-cb") .setName("kos-mock-cb")
.setFallbackUri("forward:/fallback/kos")) .setFallbackUri("forward:/fallback/kos"))
@ -115,7 +122,7 @@ public class GatewayConfig {
.setRetries(2) .setRetries(2)
.setBackoff(java.time.Duration.ofSeconds(1), java.time.Duration.ofSeconds(5), 2, true)) .setBackoff(java.time.Duration.ofSeconds(1), java.time.Duration.ofSeconds(5), 2, true))
) )
.uri("lb://kos-mock")) .uri(kosMockUrl))
// 주의: Gateway 자체 엔드포인트는 라우팅하지 않음 // 주의: Gateway 자체 엔드포인트는 라우팅하지 않음
// Health Check와 Swagger UI는 Spring Boot에서 직접 제공 // Health Check와 Swagger UI는 Spring Boot에서 직접 제공

View File

@ -50,7 +50,7 @@ public class SwaggerConfig {
return GroupedOpenApi.builder() return GroupedOpenApi.builder()
.group("product") .group("product")
.displayName("Product Service") .displayName("Product Service")
.pathsToMatch("/products/**", "/product/**") .pathsToMatch("/api/v1/products/**", "/product/**")
.build(); .build();
} }

View File

@ -35,7 +35,7 @@ import java.time.LocalDate;
* - 상품변경 이력 조회 * - 상품변경 이력 조회
*/ */
@RestController @RestController
@RequestMapping("/products") @RequestMapping("/api/v1/products")
@Validated @Validated
@Tag(name = "Product Change Service", description = "상품변경 서비스 API") @Tag(name = "Product Change Service", description = "상품변경 서비스 API")
@SecurityRequirement(name = "bearerAuth") @SecurityRequirement(name = "bearerAuth")