From fca75ad227d9eaac802901e292be9f915bdb43be Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Tue, 12 Aug 2025 09:51:25 +0900 Subject: [PATCH 01/49] =?UTF-8?q?MNMS-328=20feat:gateway=20=EB=94=94?= =?UTF-8?q?=EB=B2=84=EA=B9=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/filter/JwtToHeaderFilter.java | 6 +++++- .../config/security/jwt/JwtTokenProvider.java | 20 ++++++++++++++----- src/main/resources/application-local.yml | 2 +- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java b/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java index fa6a2bb..e4fa39d 100644 --- a/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java +++ b/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java @@ -32,11 +32,15 @@ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { String jwt = auth.getToken().getTokenValue(); Claims claims = jwtTokenProvider.getAllClaims(jwt); ServerHttpRequest mutatedRequest = exchange.getRequest().mutate() + .header("X-Login-Id", jwtTokenProvider.getSubject(claims)) .header("X-User-Id", jwtTokenProvider.getClaimAsString(claims, "userId")) .header("X-User-Name", jwtTokenProvider.getClaimAsString(claims,"name")) .header("X-User-Role", jwtTokenProvider.getClaimAsString(claims,"role")) .build(); - + System.out.println("X-Login-Id" + jwtTokenProvider.getSubject(claims)); + System.out.println("X-User-Id" + jwtTokenProvider.getClaimAsString(claims, "userId")); + System.out.println("X-User-Name" + jwtTokenProvider.getClaimAsString(claims,"name")); + System.out.println("X-User-Role" + jwtTokenProvider.getClaimAsString(claims,"role")); return chain.filter(exchange.mutate().request(mutatedRequest).build()); }) .switchIfEmpty(Mono.defer(() -> { diff --git a/src/main/java/com/tekcit/gateway/config/security/jwt/JwtTokenProvider.java b/src/main/java/com/tekcit/gateway/config/security/jwt/JwtTokenProvider.java index 60137a9..cf031fc 100644 --- a/src/main/java/com/tekcit/gateway/config/security/jwt/JwtTokenProvider.java +++ b/src/main/java/com/tekcit/gateway/config/security/jwt/JwtTokenProvider.java @@ -82,16 +82,19 @@ public Jwt convertToSpringJwt(String token) { Map claimsMap = new HashMap<>(claims); - Integer expInt = (Integer) claims.get("exp"); - Integer iatInt = (Integer) claims.get("iat"); + Object expObj = claims.get("exp"); + Object iatObj = claims.get("iat"); - Instant expiresAt = Instant.ofEpochSecond(expInt.longValue()); - Instant issuedAt = Instant.ofEpochSecond(iatInt.longValue()); + Long exp = expObj instanceof Number ? ((Number) expObj).longValue() : null; + Long iat = iatObj instanceof Number ? ((Number) iatObj).longValue() : null; + + Instant expiresAt = Instant.ofEpochSecond(exp); + Instant issuedAt = Instant.ofEpochSecond(iat); return new Jwt(token, issuedAt, expiresAt, headers, claimsMap); } - // Claim 추출 + // 토큰 추출 public Claims getAllClaims(String token) { return Jwts.parserBuilder() .setSigningKey(publicKey) @@ -100,6 +103,13 @@ public Claims getAllClaims(String token) { .parseClaimsJws(token) .getBody(); } + public String getSubject(String token){ + return getAllClaims(token).getSubject(); + } + public String getSubject(Claims claims){ + return claims.getSubject(); + } + public String getClaimAsString(Claims claims, String claimName){ return claims.get(claimName).toString(); } diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 23a31be..aeb69bc 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -18,7 +18,7 @@ spring: - id: local-service uri: http://localhost:10003 predicates: - - Path=/api/** + - Path=/ jwt: private-pem-path: classpath:keys/private.pem public-pem-path: classpath:keys/public.pem From 76ba9852d6a89817c9ff2f9be784f3957388bcda Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Tue, 12 Aug 2025 10:23:37 +0900 Subject: [PATCH 02/49] =?UTF-8?q?MNMS-328=20fix:=20=C3=ACyaml=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-local.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index aeb69bc..9061a0d 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -18,7 +18,7 @@ spring: - id: local-service uri: http://localhost:10003 predicates: - - Path=/ + - Path=/** jwt: private-pem-path: classpath:keys/private.pem public-pem-path: classpath:keys/public.pem From b1e69dcb82f79b694193504dc49ce021cb461345 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Tue, 12 Aug 2025 11:02:34 +0900 Subject: [PATCH 03/49] =?UTF-8?q?MNMS-328=20corfeat:s=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tekcit/gateway/config/security/WebfluxSecurityConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/tekcit/gateway/config/security/WebfluxSecurityConfig.java b/src/main/java/com/tekcit/gateway/config/security/WebfluxSecurityConfig.java index b4d976d..f72869f 100644 --- a/src/main/java/com/tekcit/gateway/config/security/WebfluxSecurityConfig.java +++ b/src/main/java/com/tekcit/gateway/config/security/WebfluxSecurityConfig.java @@ -33,6 +33,7 @@ public PasswordEncoder passwordEncoder() { @Bean public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { return http + .cors(ServerHttpSecurity.CorsSpec::disable) .csrf(ServerHttpSecurity.CsrfSpec::disable) .authorizeExchange(auth -> auth .anyExchange().permitAll() From be90f7177bcadc6438b31129a7e1d33b87dec121 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Tue, 12 Aug 2025 13:11:35 +0900 Subject: [PATCH 04/49] =?UTF-8?q?=C3=A3MNMS-328=20feat:=20=C3=A3=C2=85gate?= =?UTF-8?q?way=20cors=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/filter/GatewayCorsConfig.java | 25 +++++++++++++++++++ src/main/resources/application-dev.yml | 12 +++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java diff --git a/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java new file mode 100644 index 0000000..183f843 --- /dev/null +++ b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java @@ -0,0 +1,25 @@ +package com.tekcit.gateway.config.gateway.filter; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.reactive.CorsWebFilter; +import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; + +@Configuration +public class GatewayCorsConfig { + + @Bean + public CorsWebFilter corsWebFilter() { + CorsConfiguration config = new CorsConfiguration(); + config.addAllowedOrigin("http://localhost:5173"); // React dev 서버 주소 + config.addAllowedMethod("*"); // 모든 HTTP 메서드 허용 + config.addAllowedHeader("*"); // 모든 헤더 허용 + config.setAllowCredentials(true); // 쿠키 등 자격증명 허용 + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + + return new CorsWebFilter(source); + } +} \ No newline at end of file diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 06e5df1..25d5aa8 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -16,6 +16,18 @@ spring: service-labels: expose-via-spring-gateway: "true" gateway: + globalcors: + cors-configurations: + '[/**]': + allowedOrigins: "http://localhost:5173" + allowedMethods: + - GET + - POST + - PUT + - DELETE + - OPTIONS + allowedHeaders: "*" + allowCredentials: true default-filters: - StripPrefix=1 discovery: From 25b34f0cc0f806e093809994a8b8133fa4979bff Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Thu, 14 Aug 2025 15:11:37 +0900 Subject: [PATCH 05/49] =?UTF-8?q?MNMS-345=20feat:claim=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/config/gateway/filter/JwtToHeaderFilter.java | 7 +------ .../security/repository/JwtSecurityContextRepository.java | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java b/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java index e4fa39d..8945b0a 100644 --- a/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java +++ b/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java @@ -32,15 +32,10 @@ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { String jwt = auth.getToken().getTokenValue(); Claims claims = jwtTokenProvider.getAllClaims(jwt); ServerHttpRequest mutatedRequest = exchange.getRequest().mutate() - .header("X-Login-Id", jwtTokenProvider.getSubject(claims)) - .header("X-User-Id", jwtTokenProvider.getClaimAsString(claims, "userId")) + .header("X-User-Id", jwtTokenProvider.getSubject(claims)) .header("X-User-Name", jwtTokenProvider.getClaimAsString(claims,"name")) .header("X-User-Role", jwtTokenProvider.getClaimAsString(claims,"role")) .build(); - System.out.println("X-Login-Id" + jwtTokenProvider.getSubject(claims)); - System.out.println("X-User-Id" + jwtTokenProvider.getClaimAsString(claims, "userId")); - System.out.println("X-User-Name" + jwtTokenProvider.getClaimAsString(claims,"name")); - System.out.println("X-User-Role" + jwtTokenProvider.getClaimAsString(claims,"role")); return chain.filter(exchange.mutate().request(mutatedRequest).build()); }) .switchIfEmpty(Mono.defer(() -> { diff --git a/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java b/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java index a81b223..cc58461 100644 --- a/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java +++ b/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java @@ -39,7 +39,7 @@ public Mono load(ServerWebExchange exchange) { try { Claims claims = jwtTokenProvider.getAllClaims(token); String username = jwtTokenProvider.getName(claims); - String role = jwtTokenProvider.getRole(claims); + String role = jwtTokenProvider.getRole(claims);w List authorities = List.of(new SimpleGrantedAuthority("ROLE_" + role)); From 34686c07ff75d7b6cf2a33d1484ed7d05f5198a3 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Thu, 14 Aug 2025 15:12:34 +0900 Subject: [PATCH 06/49] =?UTF-8?q?MNMS-345=20feat:claim=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/repository/JwtSecurityContextRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java b/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java index cc58461..a81b223 100644 --- a/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java +++ b/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java @@ -39,7 +39,7 @@ public Mono load(ServerWebExchange exchange) { try { Claims claims = jwtTokenProvider.getAllClaims(token); String username = jwtTokenProvider.getName(claims); - String role = jwtTokenProvider.getRole(claims);w + String role = jwtTokenProvider.getRole(claims); List authorities = List.of(new SimpleGrantedAuthority("ROLE_" + role)); From 670e331bedb01c05a59838800003ac0e617e303d Mon Sep 17 00:00:00 2001 From: V4N1LL4 Date: Tue, 19 Aug 2025 16:10:15 +0900 Subject: [PATCH 07/49] build(api-gateway): add production Dockerfile for Spring Boot (Java 17) --- Dockerfile | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bad5fdb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM eclipse-temurin:17-jre-alpine +RUN addgroup -S spring && adduser -S spring -G spring +USER spring +WORKDIR /app +ARG JAR_FILE=build/libs/*.jar +COPY ${JAR_FILE} /app/app.jar +EXPOSE 8080 +ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75" +ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar"] \ No newline at end of file From f8fb72bcdbddcc859ab87bbe522a6a3dda0ad3d5 Mon Sep 17 00:00:00 2001 From: V4N1LL4 Date: Tue, 19 Aug 2025 16:10:28 +0900 Subject: [PATCH 08/49] ci(api-gateway): add workflow api-gateway-docker-dispatch.yml (build & GitOps dispatch) --- .../workflows/api-gateway-docker-dispatch.yml | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 .github/workflows/api-gateway-docker-dispatch.yml diff --git a/.github/workflows/api-gateway-docker-dispatch.yml b/.github/workflows/api-gateway-docker-dispatch.yml new file mode 100644 index 0000000..370984c --- /dev/null +++ b/.github/workflows/api-gateway-docker-dispatch.yml @@ -0,0 +1,77 @@ +name: "💰 api-gateway - Docker Build & GitOps Dispatch" + +on: + push: + branches: [ main, develop ] + workflow_dispatch: {} + +permissions: + contents: read + +concurrency: + group: api-gateway-${{ github.ref_name }} + cancel-in-progress: true + +env: + SERVICE: gateway + IMAGE: ${{ secrets.DOCKER_USERNAME }}/api-gateway + DEVOPS_REPO: 3-mnms/gitops-repo + +jobs: + build-push: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: '17' + + - run: chmod +x ./gradlew + - run: ./gradlew bootJar --no-daemon + + - name: ⏱️ Tag & Env + run: | + echo "TAG=v$(date -u +'%Y%m%d%H%M%S')" >> $GITHUB_ENV + if [ "${GITHUB_REF_NAME}" = "main" ]; then echo "ENV=prod" >> $GITHUB_ENV; else echo "ENV=develop" >> $GITHUB_ENV; fi + + - uses: docker/setup-buildx-action@v3 + + - uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + + - id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE }} + tags: | + type=raw,value=${{ env.TAG }} + type=sha + type=raw,value=latest,enable=${{ github.ref_name == 'main' }} + + - uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Dispatch to GitOps + uses: peter-evans/repository-dispatch@v3 + with: + token: ${{ secrets.GH_PAT }} + repository: ${{ env.DEVOPS_REPO }} + event-type: image-updated + client-payload: | + { + "service": "${{ env.SERVICE }}", + "image": "${{ env.IMAGE }}", + "tag": "${{ env.TAG }}", + "env": "${{ env.ENV }}" + } \ No newline at end of file From c738008d519257568ebd6995a7af3170a47525a8 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Wed, 20 Aug 2025 10:10:05 +0900 Subject: [PATCH 09/49] =?UTF-8?q?MNMS-346=20feat:=20yaml=20=EA=B0=92=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EB=8B=AC=EA=B8=B0=20=EB=B0=8F=20gateway?= =?UTF-8?q?=20webflux=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../gateway/filter/GatewayCorsConfig.java | 14 +++-- ...cation-dev.yml => application-develop.yml} | 52 +++++++++---------- src/main/resources/application-local.yml | 37 ++++++++----- src/main/resources/application-prod.yml | 40 +++++++++----- 5 files changed, 87 insertions(+), 58 deletions(-) rename src/main/resources/{application-dev.yml => application-develop.yml} (50%) diff --git a/build.gradle b/build.gradle index fb4262f..60011aa 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ ext { } dependencies { - implementation 'org.springframework.cloud:spring-cloud-starter-gateway' + implementation 'org.springframework.cloud:spring-cloud-starter-gateway-server-webflux' implementation 'org.springframework.cloud:spring-cloud-starter-kubernetes-client-all' implementation 'org.springframework.boot:spring-boot-starter-actuator' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java index 183f843..7bd2ece 100644 --- a/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java +++ b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java @@ -1,5 +1,6 @@ package com.tekcit.gateway.config.gateway.filter; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; @@ -9,13 +10,18 @@ @Configuration public class GatewayCorsConfig { + + @Value("${cors.url:http://localhost:5173}") + private String corsUrl; + + @Bean public CorsWebFilter corsWebFilter() { CorsConfiguration config = new CorsConfiguration(); - config.addAllowedOrigin("http://localhost:5173"); // React dev 서버 주소 - config.addAllowedMethod("*"); // 모든 HTTP 메서드 허용 - config.addAllowedHeader("*"); // 모든 헤더 허용 - config.setAllowCredentials(true); // 쿠키 등 자격증명 허용 + config.addAllowedOrigin(corsUrl); + config.addAllowedMethod("*"); + config.addAllowedHeader("*"); + config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-develop.yml similarity index 50% rename from src/main/resources/application-dev.yml rename to src/main/resources/application-develop.yml index 25d5aa8..a07b672 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-develop.yml @@ -5,6 +5,7 @@ spring: application: name: api-gateway cloud: + # Kubernetes 자동 Routing 설정 및 검색 범위 namespace 설정 kubernetes: discovery: enabled: true @@ -13,42 +14,41 @@ spring: - festival - payment - user + # 검색 조건 : service 가 이 라벨을 포함할 경우 service-labels: expose-via-spring-gateway: "true" gateway: - globalcors: - cors-configurations: - '[/**]': - allowedOrigins: "http://localhost:5173" - allowedMethods: - - GET - - POST - - PUT - - DELETE - - OPTIONS - allowedHeaders: "*" - allowCredentials: true - default-filters: - - StripPrefix=1 - discovery: - locator: - enabled: true - lower-case-service-id: true - predicates: - - name: Path - args: - pattern: "'/' + serviceId + '/**'" - filters: - - name: StripPrefix - args: - parts: 1 + server: + webflux: + # Kubernetes Routing 을 위한 규칙 설정 + discovery: + locator: + enabled: true + lower-case-service-id: true + # serviceID 를 기점으로 자동 라우팅 + predicates: + - name: Path + args: + pattern: "'/' + serviceId + '/**'" + # 첫 uri 없이 전달 + filters: + - name: StripPrefix + args: + parts: 1 + +# JWT key 세팅 jwt: private-pem-path: classpath:keys/private.pem public-pem-path: classpath:keys/public.pem +# cors url 설정 +cors: + url: http://localhost:5173 + +# actuator 노출 범위, 조건 management: endpoints: web: diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 9061a0d..e9dea36 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -5,25 +5,36 @@ spring: application: name: api-gateway cloud: - kubernetes: - discovery: - enabled: true - namespaces: - - booking - - festival - - payment - - user gateway: - routes: - - id: local-service - uri: http://localhost:10003 - predicates: - - Path=/** + server: + webflux: + # 각 MSA 서비스 라우팅을 위한 설정. uri 가 어떻게 이루어져있는지 파악되지 않아서 predicates ** 처리 + routes: + - id: local-service1 + uri: http://localhost:8080 + predicates: + - Path=/** + - id: local-service2 + uri: http://localhost:8081 + predicates: + - Path=/** + - id: local-service3 + uri: http://localhost:8082 + predicates: + - Path=/** + - id: local-service4 + uri: http://localhost:8083 + predicates: + - Path=/** jwt: private-pem-path: classpath:keys/private.pem public-pem-path: classpath:keys/public.pem +# cors url 설정 +cors: + url: http://localhost:5173 + management: endpoints: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index cf61e49..8561a29 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -2,6 +2,7 @@ spring: application: name: api-gateway cloud: + # Kubernetes 자동 Routing 설정을 위한 namespace 항목 설정 kubernetes: discovery: enabled: true @@ -10,28 +11,39 @@ spring: - festival - payment - user + # 검색 조건 : service 가 이 라벨을 포함할 경우 service-labels: expose-via-spring-gateway: "true" gateway: - default-filters: - - StripPrefix=1 - discovery: - locator: - enabled: true - lower-case-service-id: true - predicates: - - name: Path - args: - pattern: "'/' + serviceId + '/**'" - filters: - - name: StripPrefix - args: - parts: 1 + server: + webflux: + # Kubernetes Routing 을 위한 규칙 설정 + discovery: + locator: + enabled: true + lower-case-service-id: true + # serviceID 를 기점으로 자동 라우팅 + predicates: + - name: Path + args: + pattern: "'/' + serviceId + '/**'" + # 첫 uri 없이 전달 + filters: + - name: StripPrefix + args: + parts: 1 jwt: private-pem-path: classpath:keys/private.pem public-pem-path: classpath:keys/public.pem + + +# cors url 설정 +cors: + url: http://localhost:5173 + + server: port: 8080 From 6b0d83043f50fda7b7468c03b1fb764ca09b40d6 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Wed, 20 Aug 2025 21:51:46 +0900 Subject: [PATCH 10/49] =?UTF-8?q?=E3=85=A1MNMS-386=20feat:=20Gateway=20Swa?= =?UTF-8?q?gger=20Aggregator=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +- .../config/swagger/OpenApiGatewayConfig.java | 18 ++++++++ src/main/resources/application-local.yml | 41 +++++++++++++++---- 3 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/tekcit/gateway/config/swagger/OpenApiGatewayConfig.java diff --git a/build.gradle b/build.gradle index 60011aa..f20f9de 100644 --- a/build.gradle +++ b/build.gradle @@ -47,7 +47,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-json' testImplementation 'org.springframework.kafka:spring-kafka-test' - + // Swagger + implementation 'org.springdoc:springdoc-openapi-starter-webflux-ui:2.8.9' } dependencyManagement { imports { diff --git a/src/main/java/com/tekcit/gateway/config/swagger/OpenApiGatewayConfig.java b/src/main/java/com/tekcit/gateway/config/swagger/OpenApiGatewayConfig.java new file mode 100644 index 0000000..76992c6 --- /dev/null +++ b/src/main/java/com/tekcit/gateway/config/swagger/OpenApiGatewayConfig.java @@ -0,0 +1,18 @@ +package com.tekcit.gateway.config.swagger; + + +import org.springdoc.core.models.GroupedOpenApi; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class OpenApiGatewayConfig { + + @Bean + public GroupedOpenApi apiGroup(){ + return GroupedOpenApi.builder() + .group("all-services") + .pathsToMatch("/**") + .build(); + } +} diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index e9dea36..9b9c3c6 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -10,22 +10,30 @@ spring: webflux: # 각 MSA 서비스 라우팅을 위한 설정. uri 가 어떻게 이루어져있는지 파악되지 않아서 predicates ** 처리 routes: - - id: local-service1 + - id: user uri: http://localhost:8080 predicates: - - Path=/** - - id: local-service2 + - Path=/user/** + filters: + - StripPrefix=1 + - id: payment uri: http://localhost:8081 predicates: - - Path=/** - - id: local-service3 + - Path=/payment + filters: + - StripPrefix=1 + - id: booking uri: http://localhost:8082 predicates: - - Path=/** - - id: local-service4 + - Path=/booking/** + filters: + - StripPrefix=1 + - id: festival uri: http://localhost:8083 predicates: - - Path=/** + - Path=/festival/** + filters: + - StripPrefix=1 jwt: private-pem-path: classpath:keys/private.pem public-pem-path: classpath:keys/public.pem @@ -52,4 +60,19 @@ logging: org.springframework.cloud.gateway: DEBUG org.springframework.cloud.gateway.discovery: DEBUG org.springframework.cloud.kubernetes.discovery: DEBUG - reactor.netty.http.client.HttpClient: DEBUG \ No newline at end of file + reactor.netty.http.client.HttpClient: DEBUG + +springdoc: + swagger-ui: + urls: + - name: User + url: /user/v3/api-docs + + - name: Payment + url: http://localhost:8081/api/v3/api-docs + + - name: Booking + url: /booking/v3/api-docs + + - name: Festival + url: http://localhost:8083/api/v3/api-docs \ No newline at end of file From 1a4057562d49eed26032d342cbbe4b0a771481f1 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Wed, 20 Aug 2025 21:53:02 +0900 Subject: [PATCH 11/49] =?UTF-8?q?=E3=85=A1MNMS-386=20feat:=20Gateway=20Swa?= =?UTF-8?q?gger=20Aggregator=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-local.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 9b9c3c6..2c17041 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -69,10 +69,10 @@ springdoc: url: /user/v3/api-docs - name: Payment - url: http://localhost:8081/api/v3/api-docs + url: /payment/v3/api-docs - name: Booking url: /booking/v3/api-docs - name: Festival - url: http://localhost:8083/api/v3/api-docs \ No newline at end of file + url: /festival/v3/api-docs \ No newline at end of file From 1132fdd63fe4095050605dde7f44271e87d0fd07 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Wed, 20 Aug 2025 21:54:59 +0900 Subject: [PATCH 12/49] =?UTF-8?q?=E3=85=A1MNMS-386=20feat:=20Gateway=20Swa?= =?UTF-8?q?gger=20Aggregator=20=C3=AC=20develop/production=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=C2=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-develop.yml | 14 +++++++++++++- src/main/resources/application-prod.yml | 14 +++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application-develop.yml b/src/main/resources/application-develop.yml index a07b672..d26dbbe 100644 --- a/src/main/resources/application-develop.yml +++ b/src/main/resources/application-develop.yml @@ -65,4 +65,16 @@ logging: org.springframework.cloud.gateway: DEBUG org.springframework.cloud.gateway.discovery: DEBUG org.springframework.cloud.kubernetes.discovery: DEBUG - reactor.netty.http.client.HttpClient: DEBUG \ No newline at end of file + reactor.netty.http.client.HttpClient: DEBUG + +springdoc: + swagger-ui: + urls: + - name: User + url: /api-user-service/v3/api-docs + - name: Payment + url: /api-payment-service/v3/api-docs + - name: Booking + url: /api-booking-service/v3/api-docs + - name: Festival + url: /api-festival-service/v3/api-docs \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 8561a29..e078ab0 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -63,4 +63,16 @@ logging: org.springframework.cloud.gateway: DEBUG org.springframework.cloud.gateway.discovery: DEBUG org.springframework.cloud.kubernetes.discovery: DEBUG - reactor.netty.http.client.HttpClient: DEBUG \ No newline at end of file + reactor.netty.http.client.HttpClient: DEBUG + +springdoc: + swagger-ui: + urls: + - name: User + url: /api-user-service/v3/api-docs + - name: Payment + url: /api-payment-service/v3/api-docs + - name: Booking + url: /api-booking-service/v3/api-docs + - name: Festival + url: /api-festival-service/v3/api-docs \ No newline at end of file From 949c0ec950e1a93f80a04159b6bd32705dd2f52d Mon Sep 17 00:00:00 2001 From: V4N1LL4 Date: Thu, 21 Aug 2025 10:29:19 +0900 Subject: [PATCH 13/49] ci(api): single dispatch with multi-targets (develop-gke,develop-aws) --- .../workflows/api-gateway-docker-dispatch.yml | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/.github/workflows/api-gateway-docker-dispatch.yml b/.github/workflows/api-gateway-docker-dispatch.yml index 370984c..fad5de9 100644 --- a/.github/workflows/api-gateway-docker-dispatch.yml +++ b/.github/workflows/api-gateway-docker-dispatch.yml @@ -1,4 +1,4 @@ -name: "💰 api-gateway - Docker Build & GitOps Dispatch" +name: "💰 api-gateway – Docker Build & GitOps Dispatch" on: push: @@ -31,10 +31,8 @@ jobs: - run: chmod +x ./gradlew - run: ./gradlew bootJar --no-daemon - - name: ⏱️ Tag & Env - run: | - echo "TAG=v$(date -u +'%Y%m%d%H%M%S')" >> $GITHUB_ENV - if [ "${GITHUB_REF_NAME}" = "main" ]; then echo "ENV=prod" >> $GITHUB_ENV; else echo "ENV=develop" >> $GITHUB_ENV; fi + - name: ⏱️ Tag + run: echo "TAG=v$(date -u +'%Y%m%d%H%M%S')" >> $GITHUB_ENV - uses: docker/setup-buildx-action@v3 @@ -62,7 +60,8 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max - - name: Dispatch to GitOps + - name: Dispatch to GitOps (develop-gke + develop-aws) + if: ${{ github.ref_name == 'develop' }} uses: peter-evans/repository-dispatch@v3 with: token: ${{ secrets.GH_PAT }} @@ -73,5 +72,20 @@ jobs: "service": "${{ env.SERVICE }}", "image": "${{ env.IMAGE }}", "tag": "${{ env.TAG }}", - "env": "${{ env.ENV }}" + "targets": "develop-gke,develop-aws" + } + + - name: Dispatch to GitOps (prod) + if: ${{ github.ref_name == 'main' }} + uses: peter-evans/repository-dispatch@v3 + with: + token: ${{ secrets.GH_PAT }} + repository: ${{ env.DEVOPS_REPO }} + event-type: image-updated + client-payload: | + { + "service": "${{ env.SERVICE }}", + "image": "${{ env.IMAGE }}", + "tag": "${{ env.TAG }}", + "env": "prod" } \ No newline at end of file From 5069640bcd1bfb92d4e0e243a2a6a669289f20f1 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Thu, 21 Aug 2025 12:49:31 +0900 Subject: [PATCH 14/49] =?UTF-8?q?MNMS-328=20feat:gateway=20cors=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-develop.yml | 76 ++++++++++++++----- src/main/resources/application-prod.yml | 88 ++++++++++++++++------ 2 files changed, 125 insertions(+), 39 deletions(-) diff --git a/src/main/resources/application-develop.yml b/src/main/resources/application-develop.yml index d26dbbe..dc81143 100644 --- a/src/main/resources/application-develop.yml +++ b/src/main/resources/application-develop.yml @@ -20,21 +20,60 @@ spring: gateway: server: webflux: - # Kubernetes Routing 을 위한 규칙 설정 - discovery: - locator: - enabled: true - lower-case-service-id: true - # serviceID 를 기점으로 자동 라우팅 + routes: + - id: user + uri: http://api-user-service:8080 predicates: - - name: Path - args: - pattern: "'/' + serviceId + '/**'" - # 첫 uri 없이 전달 + - Path=/api/user/** + + - id: user-swagger + uri: http://api-user-service:8080 + predicates: + - Path=/api/user/v3/api-docs + filters: + - StripPrefix=2 + order: -1 + + + - id: payment + uri: http://api-payment-service:8080 + predicates: + - Path=/api/payment/** + + - id: payment-swagger + uri: http://api-payment-service:8080 + predicates: + - Path=/api/payment/v3/api-docs + filters: + - StripPrefix=2 + order: -1 + + + - id: booking + uri: http://api-booking-service:8080 + predicates: + - Path=/api/booking/** + + - id: booking-swagger + uri: http://api-booking-service:8080 + predicates: + - Path=/api/booking/v3/api-docs filters: - - name: StripPrefix - args: - parts: 1 + - StripPrefix=2 + order: -1 + + - id: festival + uri: http://api-festival-service:8080 + predicates: + - Path=/api/festival/** + + - id: festival-swagger + uri: http://api-festival-service:8080 + predicates: + - Path=/api/festival/v3/api-docs + filters: + - StripPrefix=2 + order: -1 # JWT key 세팅 @@ -71,10 +110,13 @@ springdoc: swagger-ui: urls: - name: User - url: /api-user-service/v3/api-docs + url: /api/user/v3/api-docs + - name: Payment - url: /api-payment-service/v3/api-docs + url: /api/payment/v3/api-docs + - name: Booking - url: /api-booking-service/v3/api-docs + url: /api/booking/v3/api-docs + - name: Festival - url: /api-festival-service/v3/api-docs \ No newline at end of file + url: /api/festival/v3/api-docs \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index e078ab0..dc81143 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,8 +1,11 @@ +server: + port: 8080 + spring: application: name: api-gateway cloud: - # Kubernetes 자동 Routing 설정을 위한 namespace 항목 설정 + # Kubernetes 자동 Routing 설정 및 검색 범위 namespace 설정 kubernetes: discovery: enabled: true @@ -17,36 +20,74 @@ spring: gateway: server: webflux: - # Kubernetes Routing 을 위한 규칙 설정 - discovery: - locator: - enabled: true - lower-case-service-id: true - # serviceID 를 기점으로 자동 라우팅 + routes: + - id: user + uri: http://api-user-service:8080 + predicates: + - Path=/api/user/** + + - id: user-swagger + uri: http://api-user-service:8080 + predicates: + - Path=/api/user/v3/api-docs + filters: + - StripPrefix=2 + order: -1 + + + - id: payment + uri: http://api-payment-service:8080 + predicates: + - Path=/api/payment/** + + - id: payment-swagger + uri: http://api-payment-service:8080 + predicates: + - Path=/api/payment/v3/api-docs + filters: + - StripPrefix=2 + order: -1 + + + - id: booking + uri: http://api-booking-service:8080 predicates: - - name: Path - args: - pattern: "'/' + serviceId + '/**'" - # 첫 uri 없이 전달 + - Path=/api/booking/** + + - id: booking-swagger + uri: http://api-booking-service:8080 + predicates: + - Path=/api/booking/v3/api-docs + filters: + - StripPrefix=2 + order: -1 + + - id: festival + uri: http://api-festival-service:8080 + predicates: + - Path=/api/festival/** + + - id: festival-swagger + uri: http://api-festival-service:8080 + predicates: + - Path=/api/festival/v3/api-docs filters: - - name: StripPrefix - args: - parts: 1 + - StripPrefix=2 + order: -1 + +# JWT key 세팅 jwt: private-pem-path: classpath:keys/private.pem public-pem-path: classpath:keys/public.pem - # cors url 설정 cors: url: http://localhost:5173 -server: - port: 8080 - +# actuator 노출 범위, 조건 management: endpoints: web: @@ -69,10 +110,13 @@ springdoc: swagger-ui: urls: - name: User - url: /api-user-service/v3/api-docs + url: /api/user/v3/api-docs + - name: Payment - url: /api-payment-service/v3/api-docs + url: /api/payment/v3/api-docs + - name: Booking - url: /api-booking-service/v3/api-docs + url: /api/booking/v3/api-docs + - name: Festival - url: /api-festival-service/v3/api-docs \ No newline at end of file + url: /api/festival/v3/api-docs \ No newline at end of file From 3b713247c606d2650f2baf40b8752e0738dd9cc0 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Thu, 21 Aug 2025 13:25:20 +0900 Subject: [PATCH 15/49] =?UTF-8?q?MNMS-370=20feat:=20FQDN=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-develop.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/resources/application-develop.yml b/src/main/resources/application-develop.yml index dc81143..4b584d3 100644 --- a/src/main/resources/application-develop.yml +++ b/src/main/resources/application-develop.yml @@ -22,12 +22,12 @@ spring: webflux: routes: - id: user - uri: http://api-user-service:8080 + uri: http://api-user-service.user.svc.cluster.local:8080 predicates: - Path=/api/user/** - id: user-swagger - uri: http://api-user-service:8080 + uri: http://api-user-service.user.svc.cluster.local:8080 predicates: - Path=/api/user/v3/api-docs filters: @@ -36,12 +36,12 @@ spring: - id: payment - uri: http://api-payment-service:8080 + uri: http://api-payment-service.payment.svc.cluster.local:8080 predicates: - Path=/api/payment/** - id: payment-swagger - uri: http://api-payment-service:8080 + uri: http://api-payment-service.payment.svc.cluster.local:8080 predicates: - Path=/api/payment/v3/api-docs filters: @@ -50,12 +50,12 @@ spring: - id: booking - uri: http://api-booking-service:8080 + uri: http://api-booking-service.booking.svc.cluster.local:8080 predicates: - Path=/api/booking/** - id: booking-swagger - uri: http://api-booking-service:8080 + uri: http://api-booking-service.booking.svc.cluster.local:8080 predicates: - Path=/api/booking/v3/api-docs filters: @@ -63,12 +63,12 @@ spring: order: -1 - id: festival - uri: http://api-festival-service:8080 + uri: http://api-festival-service.festival.svc.cluster.local:8080 predicates: - Path=/api/festival/** - id: festival-swagger - uri: http://api-festival-service:8080 + uri: http://api-festival-service.festival.svc.cluster.local:8080 predicates: - Path=/api/festival/v3/api-docs filters: From e098f26f6b741b835e18f6bbf6ec56a88553c077 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Thu, 21 Aug 2025 13:30:19 +0900 Subject: [PATCH 16/49] MNMS-370 feat: update prod file --- src/main/resources/application-prod.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index dc81143..4b584d3 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -22,12 +22,12 @@ spring: webflux: routes: - id: user - uri: http://api-user-service:8080 + uri: http://api-user-service.user.svc.cluster.local:8080 predicates: - Path=/api/user/** - id: user-swagger - uri: http://api-user-service:8080 + uri: http://api-user-service.user.svc.cluster.local:8080 predicates: - Path=/api/user/v3/api-docs filters: @@ -36,12 +36,12 @@ spring: - id: payment - uri: http://api-payment-service:8080 + uri: http://api-payment-service.payment.svc.cluster.local:8080 predicates: - Path=/api/payment/** - id: payment-swagger - uri: http://api-payment-service:8080 + uri: http://api-payment-service.payment.svc.cluster.local:8080 predicates: - Path=/api/payment/v3/api-docs filters: @@ -50,12 +50,12 @@ spring: - id: booking - uri: http://api-booking-service:8080 + uri: http://api-booking-service.booking.svc.cluster.local:8080 predicates: - Path=/api/booking/** - id: booking-swagger - uri: http://api-booking-service:8080 + uri: http://api-booking-service.booking.svc.cluster.local:8080 predicates: - Path=/api/booking/v3/api-docs filters: @@ -63,12 +63,12 @@ spring: order: -1 - id: festival - uri: http://api-festival-service:8080 + uri: http://api-festival-service.festival.svc.cluster.local:8080 predicates: - Path=/api/festival/** - id: festival-swagger - uri: http://api-festival-service:8080 + uri: http://api-festival-service.festival.svc.cluster.local:8080 predicates: - Path=/api/festival/v3/api-docs filters: From 29f695f8b4d76a0b4a5f797f6915a4e802c09fb9 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Thu, 21 Aug 2025 14:25:27 +0900 Subject: [PATCH 17/49] =?UTF-8?q?MNMS-370=20feat:=20local=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-local.yml | 47 ++++++++++++++++++------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 2c17041..338d24e 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -13,27 +13,50 @@ spring: - id: user uri: http://localhost:8080 predicates: - - Path=/user/** + - Path=/api/user/** + - id: user-swagger + uri: http://localhost:8080 + predicates: + - Path=/api/user/v3/api-docs filters: - - StripPrefix=1 + - StripPrefix=2 + order: -1 + - id: payment uri: http://localhost:8081 predicates: - - Path=/payment + - Path=/api/payment/** + - id: payment-swagger + uri: http://localhost:8081 + predicates: + - Path=/api/payment/v3/api-docs filters: - - StripPrefix=1 + - StripPrefix=2 + order: -1 + - id: booking uri: http://localhost:8082 predicates: - - Path=/booking/** + - Path=/api/booking/** + - id: booking-swagger + uri: http://localhost:8082 + predicates: + - Path=/api/booking/v3/api-docs filters: - - StripPrefix=1 + - StripPrefix=2 + order: -1 + - id: festival uri: http://localhost:8083 predicates: - - Path=/festival/** + - Path=/api/festival/** + - id: festival-swagger + uri: http://localhost:8083 + predicates: + - Path=/api/festival/v3/api-docs filters: - - StripPrefix=1 + - StripPrefix=2 + order: -1 jwt: private-pem-path: classpath:keys/private.pem public-pem-path: classpath:keys/public.pem @@ -66,13 +89,13 @@ springdoc: swagger-ui: urls: - name: User - url: /user/v3/api-docs + url: /api/user/v3/api-docs - name: Payment - url: /payment/v3/api-docs + url: /api/payment/v3/api-docs - name: Booking - url: /booking/v3/api-docs + url: /api/booking/v3/api-docs - name: Festival - url: /festival/v3/api-docs \ No newline at end of file + url: /api/festival/v3/api-docs \ No newline at end of file From b44b87b08702ee3b205a209787bd204321d82843 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Thu, 21 Aug 2025 16:36:00 +0900 Subject: [PATCH 18/49] MNMS-370 feat: predicates fix --- src/main/resources/application-local.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 338d24e..1d0fee3 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -13,7 +13,7 @@ spring: - id: user uri: http://localhost:8080 predicates: - - Path=/api/user/** + - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/** - id: user-swagger uri: http://localhost:8080 predicates: @@ -37,7 +37,8 @@ spring: - id: booking uri: http://localhost:8082 predicates: - - Path=/api/booking/** + - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/capcha/** + - id: booking-swagger uri: http://localhost:8082 predicates: From 007523e158f1c6d32fee14ea4a09de6e0cd50598 Mon Sep 17 00:00:00 2001 From: V4N1LL4 Date: Fri, 22 Aug 2025 18:30:01 +0900 Subject: [PATCH 19/49] =?UTF-8?q?ci(workflows):=20=EB=AA=A8=EB=93=A0=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A9=80=ED=8B=B0=EC=95=84?= =?UTF-8?q?=ED=82=A4=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=ED=91=B8=EC=8B=9C(am?= =?UTF-8?q?d64/arm64)=20+=20=ED=83=9C=EA=B7=B8=20=EB=B6=84=EB=A6=AC(-win/-?= =?UTF-8?q?mac)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 대상: api-booking, api-festival, api-gateway, api-payment, api-user, nginx-client - buildx 멀티아키 빌드 추가: platforms=linux/amd64,linux/arm64 - 태그 규칙 분리: - TAG_AMD=v{UTC시각}-win - TAG_ARM=v{UTC시각}-mac - 호환성 유지 위해 TAG=TAG_AMD 로 설정(Repo Dispatch에 그대로 사용) - docker/metadata-action: 두 태그(raw) 모두 발행, sha 및 main에서 latest 유지 - docker/build-push-action: 두 태그 모두 push - GitOps Dispatch payload 스키마 변경 없음(`tag`만 전송), develop의 nginx-client는 기존 targets 유지 - Helm 태그 자동 반영 워크플로우 변경 없음 검증 방법 - develop/main 브랜치 push 후 Docker Hub에 -win / -mac 두 태그 생성 확인 - gitops-repo로 repository_dispatch 이벤트 수신 및 values 파일의 .image.tag 가 -win 으로 패치되는지 확인 --- .github/workflows/api-gateway-docker-dispatch.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/api-gateway-docker-dispatch.yml b/.github/workflows/api-gateway-docker-dispatch.yml index fad5de9..75cd349 100644 --- a/.github/workflows/api-gateway-docker-dispatch.yml +++ b/.github/workflows/api-gateway-docker-dispatch.yml @@ -32,7 +32,11 @@ jobs: - run: ./gradlew bootJar --no-daemon - name: ⏱️ Tag - run: echo "TAG=v$(date -u +'%Y%m%d%H%M%S')" >> $GITHUB_ENV + run: | + TS=$(date -u +'%Y%m%d%H%M%S') + echo "TAG_AMD=v${TS}-win" >> $GITHUB_ENV + echo "TAG_ARM=v${TS}-mac" >> $GITHUB_ENV + echo "TAG=v${TS}-win" >> $GITHUB_ENV - uses: docker/setup-buildx-action@v3 @@ -46,7 +50,8 @@ jobs: with: images: ${{ env.IMAGE }} tags: | - type=raw,value=${{ env.TAG }} + type=raw,value=${{ env.TAG_AMD }} + type=raw,value=${{ env.TAG_ARM }} type=sha type=raw,value=latest,enable=${{ github.ref_name == 'main' }} @@ -55,6 +60,7 @@ jobs: context: . file: ./Dockerfile push: true + platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha From 088721129c733149caac9c1814da5f2884156c30 Mon Sep 17 00:00:00 2001 From: V4N1LL4 Date: Fri, 22 Aug 2025 19:04:39 +0900 Subject: [PATCH 20/49] =?UTF-8?q?fix(dockerfile):=20Alpine=20=E2=86=92=20J?= =?UTF-8?q?ammy=EB=A1=9C=20=EA=B5=90=EC=B2=B4=ED=95=B4=20=EB=A9=80?= =?UTF-8?q?=ED=8B=B0=EC=95=84=ED=82=A4(amd64/arm64)=20=EB=B9=8C=EB=93=9C?= =?UTF-8?q?=20=EC=8B=A4=ED=8C=A8=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index bad5fdb..838322d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,20 @@ -FROM eclipse-temurin:17-jre-alpine -RUN addgroup -S spring && adduser -S spring -G spring -USER spring +# Debian/Ubuntu 계열 JRE (amd64/arm64 모두 제공) +FROM eclipse-temurin:17-jre-jammy + +# spring 사용자/그룹 생성 (Debian 표준 명령) +RUN groupadd -r spring \ + && useradd -r -g spring -d /home/spring -s /usr/sbin/nologin spring \ + && mkdir -p /app /home/spring + WORKDIR /app + +# 빌드 산출물 복사 (권한도 함께 설정) ARG JAR_FILE=build/libs/*.jar -COPY ${JAR_FILE} /app/app.jar +COPY --chown=spring:spring ${JAR_FILE} /app/app.jar + +# 비루트 실행 +USER spring + EXPOSE 8080 ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75" ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar"] \ No newline at end of file From 70c4cdeac128c79d575e8837d9ad78b09e54a455 Mon Sep 17 00:00:00 2001 From: V4N1LL4 Date: Sat, 23 Aug 2025 09:24:52 +0900 Subject: [PATCH 21/49] =?UTF-8?q?ci:=20=EB=A9=80=ED=8B=B0=EC=95=84?= =?UTF-8?q?=ED=82=A4=20=EB=B9=8C=EB=93=9C=20+=20latest(dev/main),=20v0.1?= =?UTF-8?q?=20=ED=83=9C=EA=B7=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/api-gateway-docker-dispatch.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/api-gateway-docker-dispatch.yml b/.github/workflows/api-gateway-docker-dispatch.yml index 75cd349..4d728ab 100644 --- a/.github/workflows/api-gateway-docker-dispatch.yml +++ b/.github/workflows/api-gateway-docker-dispatch.yml @@ -32,11 +32,7 @@ jobs: - run: ./gradlew bootJar --no-daemon - name: ⏱️ Tag - run: | - TS=$(date -u +'%Y%m%d%H%M%S') - echo "TAG_AMD=v${TS}-win" >> $GITHUB_ENV - echo "TAG_ARM=v${TS}-mac" >> $GITHUB_ENV - echo "TAG=v${TS}-win" >> $GITHUB_ENV + run: echo "TAG=v$(date -u +'%Y%m%d%H%M%S')" >> $GITHUB_ENV - uses: docker/setup-buildx-action@v3 @@ -50,10 +46,10 @@ jobs: with: images: ${{ env.IMAGE }} tags: | - type=raw,value=${{ env.TAG_AMD }} - type=raw,value=${{ env.TAG_ARM }} + type=raw,value=${{ env.TAG }} type=sha - type=raw,value=latest,enable=${{ github.ref_name == 'main' }} + type=raw,value=v0.1 + type=raw,value=latest - uses: docker/build-push-action@v5 with: From 252d34dae7cb6e110792c9734a52e7507e19f082 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Tue, 26 Aug 2025 14:41:39 +0900 Subject: [PATCH 22/49] =?UTF-8?q?MNMS-504=20feat:=20=C3=A3lcors=20?= =?UTF-8?q?=EC=97=AC=EB=A0=A4=C3=AA=EA=B0=9C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/gateway/filter/GatewayCorsConfig.java | 9 +++++++-- src/main/resources/application-develop.yml | 6 +++++- src/main/resources/application-local.yml | 12 ++++++++---- src/main/resources/application-prod.yml | 6 +++++- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java index 7bd2ece..d6ad2f1 100644 --- a/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java +++ b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java @@ -7,18 +7,23 @@ import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; +import java.util.List; + @Configuration public class GatewayCorsConfig { @Value("${cors.url:http://localhost:5173}") - private String corsUrl; + private List corsUrl; @Bean public CorsWebFilter corsWebFilter() { CorsConfiguration config = new CorsConfiguration(); - config.addAllowedOrigin(corsUrl); + for(String url : corsUrl){ + + config.addAllowedOrigin(url); + } config.addAllowedMethod("*"); config.addAllowedHeader("*"); config.setAllowCredentials(true); diff --git a/src/main/resources/application-develop.yml b/src/main/resources/application-develop.yml index 4b584d3..e83584a 100644 --- a/src/main/resources/application-develop.yml +++ b/src/main/resources/application-develop.yml @@ -84,7 +84,11 @@ jwt: # cors url 설정 cors: - url: http://localhost:5173 + url: + - http://localhost:5173 + - http://127.0.0.1:5173 + - http://localhost:5174 + - http://127.0.0.1:5174 # actuator 노출 범위, 조건 diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 1d0fee3..9f754e5 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -13,7 +13,7 @@ spring: - id: user uri: http://localhost:8080 predicates: - - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/** + - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/**,/api/mypage/** - id: user-swagger uri: http://localhost:8080 predicates: @@ -25,11 +25,11 @@ spring: - id: payment uri: http://localhost:8081 predicates: - - Path=/api/payment/** + - Path=/api/payments/** - id: payment-swagger uri: http://localhost:8081 predicates: - - Path=/api/payment/v3/api-docs + - Path=/api/payments/v3/api-docs filters: - StripPrefix=2 order: -1 @@ -65,7 +65,11 @@ jwt: # cors url 설정 cors: - url: http://localhost:5173 + url: + - http://localhost:5173 + - http://127.0.0.1:5173 + - http://localhost:5174 + - http://127.0.031:5174 management: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 4b584d3..cc7826a 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -84,7 +84,11 @@ jwt: # cors url 설정 cors: - url: http://localhost:5173 + url: + - http://localhost:5173 + - http://127.0.0.1:5173 + - http://localhost:5174 + - http://127.0.031:5174 # actuator 노출 범위, 조건 From 747109a22ed4935dfe32b57a9410d0dbf38fb87f Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Tue, 26 Aug 2025 14:59:58 +0900 Subject: [PATCH 23/49] MNMS-504 multi origin --- .../config/gateway/filter/GatewayCorsConfig.java | 9 ++++++--- .../config/gateway/property/CorsProperties.java | 15 +++++++++++++++ src/main/resources/application-local.yml | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/tekcit/gateway/config/gateway/property/CorsProperties.java diff --git a/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java index d6ad2f1..b880ddf 100644 --- a/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java +++ b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java @@ -1,6 +1,9 @@ package com.tekcit.gateway.config.gateway.filter; +import com.tekcit.gateway.config.gateway.property.CorsProperties; +import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; @@ -10,17 +13,17 @@ import java.util.List; @Configuration +@RequiredArgsConstructor public class GatewayCorsConfig { - @Value("${cors.url:http://localhost:5173}") - private List corsUrl; + private final CorsProperties corsProperties; @Bean public CorsWebFilter corsWebFilter() { CorsConfiguration config = new CorsConfiguration(); - for(String url : corsUrl){ + for(String url : corsProperties.getUrl()){ config.addAllowedOrigin(url); } diff --git a/src/main/java/com/tekcit/gateway/config/gateway/property/CorsProperties.java b/src/main/java/com/tekcit/gateway/config/gateway/property/CorsProperties.java new file mode 100644 index 0000000..a08e40c --- /dev/null +++ b/src/main/java/com/tekcit/gateway/config/gateway/property/CorsProperties.java @@ -0,0 +1,15 @@ +package com.tekcit.gateway.config.gateway.property; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@Getter @Setter +@ConfigurationProperties(prefix = "cors") +public class CorsProperties { + private List url; +} diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 9f754e5..9f097bf 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -69,7 +69,7 @@ cors: - http://localhost:5173 - http://127.0.0.1:5173 - http://localhost:5174 - - http://127.0.031:5174 + - http://127.0.0.1:5174 management: From 04628700bd296edec0b7e9366a0b7d7721fbeaf8 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Wed, 27 Aug 2025 09:40:42 +0900 Subject: [PATCH 24/49] MNMS-512 feat: Gateway Upgrade --- src/main/resources/application-develop.yml | 6 +++--- src/main/resources/application-local.yml | 2 +- src/main/resources/application-prod.yml | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/resources/application-develop.yml b/src/main/resources/application-develop.yml index e83584a..a42dce6 100644 --- a/src/main/resources/application-develop.yml +++ b/src/main/resources/application-develop.yml @@ -24,7 +24,7 @@ spring: - id: user uri: http://api-user-service.user.svc.cluster.local:8080 predicates: - - Path=/api/user/** + - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/**,/api/mypage/** - id: user-swagger uri: http://api-user-service.user.svc.cluster.local:8080 @@ -38,7 +38,7 @@ spring: - id: payment uri: http://api-payment-service.payment.svc.cluster.local:8080 predicates: - - Path=/api/payment/** + - Path=/api/payments/**,/api/tekcitpay/** - id: payment-swagger uri: http://api-payment-service.payment.svc.cluster.local:8080 @@ -52,7 +52,7 @@ spring: - id: booking uri: http://api-booking-service.booking.svc.cluster.local:8080 predicates: - - Path=/api/booking/** + - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/capcha/** - id: booking-swagger uri: http://api-booking-service.booking.svc.cluster.local:8080 diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 9f097bf..091156b 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -25,7 +25,7 @@ spring: - id: payment uri: http://localhost:8081 predicates: - - Path=/api/payments/** + - Path=/api/payments/**,/api/tekcitpay/** - id: payment-swagger uri: http://localhost:8081 predicates: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index cc7826a..b9f1012 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -24,7 +24,7 @@ spring: - id: user uri: http://api-user-service.user.svc.cluster.local:8080 predicates: - - Path=/api/user/** + - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/**,/api/mypage/** - id: user-swagger uri: http://api-user-service.user.svc.cluster.local:8080 @@ -38,7 +38,7 @@ spring: - id: payment uri: http://api-payment-service.payment.svc.cluster.local:8080 predicates: - - Path=/api/payment/** + - Path=/api/payments/**,/api/tekcitpay/** - id: payment-swagger uri: http://api-payment-service.payment.svc.cluster.local:8080 @@ -52,7 +52,7 @@ spring: - id: booking uri: http://api-booking-service.booking.svc.cluster.local:8080 predicates: - - Path=/api/booking/** + - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/capcha/** - id: booking-swagger uri: http://api-booking-service.booking.svc.cluster.local:8080 From 6fafbe33e817aa36f93936b6a036170413910229 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Mon, 1 Sep 2025 16:25:41 +0900 Subject: [PATCH 25/49] =?UTF-8?q?YAML=20=EC=B6=94=C3=AA=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-docker.yml | 70 +++++++++++++++++++++++ src/main/resources/application-local.yml | 1 - 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/application-docker.yml diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml new file mode 100644 index 0000000..bfbae8d --- /dev/null +++ b/src/main/resources/application-docker.yml @@ -0,0 +1,70 @@ +server: + port: 10000 + +spring: + cloud: + gateway: + server: + webflux: + routes: + - id: user + uri: http://user:8080 + predicates: + - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/**,/api/mypage/** + - id: user-swagger + uri: http://user:8080 + predicates: + - Path=/api/user/v3/api-docs + filters: + - StripPrefix=2 + order: -1 + + - id: payment + uri: http://payment:8081 + predicates: + - Path=/api/payments/**,/api/tekcitpay/** + - id: payment-swagger + uri: http://payment:8081 + predicates: + - Path=/api/payment/v3/api-docs + filters: + - StripPrefix=2 + order: -1 + + - id: booking + uri: http://booking:8082 + predicates: + - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/captcha/** + - id: booking-swagger + uri: http://booking:8082 + predicates: + - Path=/api/booking/v3/api-docs + filters: + - StripPrefix=2 + order: -1 + + - id: festival + uri: http://festival:8083 + predicates: + - Path=/api/festival/** + - id: festival-swagger + uri: http://festival:8083 + predicates: + - Path=/api/festival/v3/api-docs + filters: + - StripPrefix=2 + order: -1 + +springdoc: + swagger-ui: + urls: + - { name: User, url: /api/user/v3/api-docs } + - { name: Payment, url: /api/payment/v3/api-docs } + - { name: Booking, url: /api/booking/v3/api-docs } + - { name: Festival, url: /api/festival/v3/api-docs } + + +jwt: + private-pem-path: ${JWT_PRIVATE_PEM_PATH:file:/etc/keys/private.pem} + public-pem-path: ${JWT_PUBLIC_PEM_PATH:file:/etc/keys/public.pem} + diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 091156b..4adb33e 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -8,7 +8,6 @@ spring: gateway: server: webflux: - # 각 MSA 서비스 라우팅을 위한 설정. uri 가 어떻게 이루어져있는지 파악되지 않아서 predicates ** 처리 routes: - id: user uri: http://localhost:8080 From 6441840e8c507ebb6e474ca113c787f5d034274e Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Mon, 1 Sep 2025 16:30:49 +0900 Subject: [PATCH 26/49] =?UTF-8?q?YAML=20=EC=B6=94=C3=AA=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-docker.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml index bfbae8d..36156e4 100644 --- a/src/main/resources/application-docker.yml +++ b/src/main/resources/application-docker.yml @@ -63,6 +63,13 @@ springdoc: - { name: Booking, url: /api/booking/v3/api-docs } - { name: Festival, url: /api/festival/v3/api-docs } +cors: + url: + - http://localhost:5173 + - http://127.0.0.1:5173 + - http://localhost:5174 + - http://127.0.0.1:5174 + jwt: private-pem-path: ${JWT_PRIVATE_PEM_PATH:file:/etc/keys/private.pem} From 1357578b25db92f0a2eedcfb94e0a26b492e95fc Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Mon, 1 Sep 2025 16:31:41 +0900 Subject: [PATCH 27/49] =?UTF-8?q?YAML=20=EC=B6=94=C3=AA=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-docker.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml index 36156e4..415ea85 100644 --- a/src/main/resources/application-docker.yml +++ b/src/main/resources/application-docker.yml @@ -75,3 +75,20 @@ jwt: private-pem-path: ${JWT_PRIVATE_PEM_PATH:file:/etc/keys/private.pem} public-pem-path: ${JWT_PUBLIC_PEM_PATH:file:/etc/keys/public.pem} +management: + endpoints: + web: + exposure: + include: '*' + health: + show-details: always + endpoint: + gateway: + access: unrestricted + +logging: + level: + org.springframework.cloud.gateway: DEBUG + org.springframework.cloud.gateway.discovery: DEBUG + org.springframework.cloud.kubernetes.discovery: DEBUG + reactor.netty.http.client.HttpClient: DEBUG \ No newline at end of file From c60747e15301cb4a3300569f71cff4d11b1645ca Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Mon, 1 Sep 2025 17:39:36 +0900 Subject: [PATCH 28/49] push --- src/main/resources/application-develop.yml | 2 +- src/main/resources/application-docker.yml | 2 +- src/main/resources/application-local.yml | 2 +- src/main/resources/application-prod.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/application-develop.yml b/src/main/resources/application-develop.yml index a42dce6..8e7aa76 100644 --- a/src/main/resources/application-develop.yml +++ b/src/main/resources/application-develop.yml @@ -52,7 +52,7 @@ spring: - id: booking uri: http://api-booking-service.booking.svc.cluster.local:8080 predicates: - - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/capcha/** + - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/capcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** - id: booking-swagger uri: http://api-booking-service.booking.svc.cluster.local:8080 diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml index 415ea85..d780bc0 100644 --- a/src/main/resources/application-docker.yml +++ b/src/main/resources/application-docker.yml @@ -34,7 +34,7 @@ spring: - id: booking uri: http://booking:8082 predicates: - - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/captcha/** + - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/captcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** - id: booking-swagger uri: http://booking:8082 predicates: diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 4adb33e..8d22769 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -36,7 +36,7 @@ spring: - id: booking uri: http://localhost:8082 predicates: - - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/capcha/** + - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/capcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** - id: booking-swagger uri: http://localhost:8082 diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index b9f1012..efc4dc9 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -52,7 +52,7 @@ spring: - id: booking uri: http://api-booking-service.booking.svc.cluster.local:8080 predicates: - - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/capcha/** + - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/capcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** - id: booking-swagger uri: http://api-booking-service.booking.svc.cluster.local:8080 From 6cb7739258d8b388d975f647630ed19633e5ace7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=9E=AC=EC=9A=B4?= Date: Tue, 2 Sep 2025 12:21:30 +0900 Subject: [PATCH 29/49] =?UTF-8?q?fix:=20=EC=9D=B4=EB=A6=84=20=EA=B9=A8?= =?UTF-8?q?=EC=A7=90=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/config/gateway/filter/JwtToHeaderFilter.java | 3 ++- .../tekcit/gateway/config/security/jwt/JwtTokenProvider.java | 5 ++++- src/main/resources/application-develop.yml | 2 +- src/main/resources/application-docker.yml | 2 +- src/main/resources/application-local.yml | 2 +- src/main/resources/application-prod.yml | 2 +- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java b/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java index 8945b0a..76eecc1 100644 --- a/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java +++ b/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java @@ -1,5 +1,6 @@ package com.tekcit.gateway.config.gateway.filter; +import com.fasterxml.jackson.databind.ObjectMapper; import com.tekcit.gateway.config.security.jwt.JwtTokenProvider; import io.jsonwebtoken.Claims; import lombok.RequiredArgsConstructor; @@ -33,7 +34,7 @@ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { Claims claims = jwtTokenProvider.getAllClaims(jwt); ServerHttpRequest mutatedRequest = exchange.getRequest().mutate() .header("X-User-Id", jwtTokenProvider.getSubject(claims)) - .header("X-User-Name", jwtTokenProvider.getClaimAsString(claims,"name")) + .header("X-User-Name", jwtTokenProvider.getName(claims)) .header("X-User-Role", jwtTokenProvider.getClaimAsString(claims,"role")) .build(); return chain.filter(exchange.mutate().request(mutatedRequest).build()); diff --git a/src/main/java/com/tekcit/gateway/config/security/jwt/JwtTokenProvider.java b/src/main/java/com/tekcit/gateway/config/security/jwt/JwtTokenProvider.java index cf031fc..8e1c19b 100644 --- a/src/main/java/com/tekcit/gateway/config/security/jwt/JwtTokenProvider.java +++ b/src/main/java/com/tekcit/gateway/config/security/jwt/JwtTokenProvider.java @@ -9,6 +9,7 @@ import org.springframework.stereotype.Component; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; @@ -130,7 +131,9 @@ public String getName(String token){ } public String getName(Claims claims){ - return claims.get("name", String.class); + String raw = claims.get("name", String.class); + String name = Base64.getUrlEncoder().encodeToString(raw.getBytes(StandardCharsets.UTF_8)); + return name; } // ===== PEM 로더들 diff --git a/src/main/resources/application-develop.yml b/src/main/resources/application-develop.yml index 8e7aa76..f76c693 100644 --- a/src/main/resources/application-develop.yml +++ b/src/main/resources/application-develop.yml @@ -24,7 +24,7 @@ spring: - id: user uri: http://api-user-service.user.svc.cluster.local:8080 predicates: - - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/**,/api/mypage/** + - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/**,/api/myPage/** - id: user-swagger uri: http://api-user-service.user.svc.cluster.local:8080 diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml index d780bc0..870423a 100644 --- a/src/main/resources/application-docker.yml +++ b/src/main/resources/application-docker.yml @@ -10,7 +10,7 @@ spring: - id: user uri: http://user:8080 predicates: - - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/**,/api/mypage/** + - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/**,/api/myPage/** - id: user-swagger uri: http://user:8080 predicates: diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 8d22769..d4f5f17 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -12,7 +12,7 @@ spring: - id: user uri: http://localhost:8080 predicates: - - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/**,/api/mypage/** + - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/**,/api/myPage/** - id: user-swagger uri: http://localhost:8080 predicates: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index efc4dc9..0929791 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -24,7 +24,7 @@ spring: - id: user uri: http://api-user-service.user.svc.cluster.local:8080 predicates: - - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/**,/api/mypage/** + - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/**,/api/myPage/** - id: user-swagger uri: http://api-user-service.user.svc.cluster.local:8080 From 06a206c3a7c2339ed198e3bedd9bd0fe52962e83 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Tue, 2 Sep 2025 13:07:36 +0900 Subject: [PATCH 30/49] =?UTF-8?q?=20yaml=20=EA=B0=92=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-develop.yml | 2 +- src/main/resources/application-docker.yml | 2 +- src/main/resources/application-local.yml | 2 +- src/main/resources/application-prod.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/application-develop.yml b/src/main/resources/application-develop.yml index f76c693..80cec79 100644 --- a/src/main/resources/application-develop.yml +++ b/src/main/resources/application-develop.yml @@ -65,7 +65,7 @@ spring: - id: festival uri: http://api-festival-service.festival.svc.cluster.local:8080 predicates: - - Path=/api/festival/** + - Path=/api/festival/**,/uploads/** - id: festival-swagger uri: http://api-festival-service.festival.svc.cluster.local:8080 diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml index 870423a..d0da894 100644 --- a/src/main/resources/application-docker.yml +++ b/src/main/resources/application-docker.yml @@ -46,7 +46,7 @@ spring: - id: festival uri: http://festival:8083 predicates: - - Path=/api/festival/** + - Path=/api/festival/**,/uploads/** - id: festival-swagger uri: http://festival:8083 predicates: diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index d4f5f17..a60cd50 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -49,7 +49,7 @@ spring: - id: festival uri: http://localhost:8083 predicates: - - Path=/api/festival/** + - Path=/api/festival/**,/uploads/** - id: festival-swagger uri: http://localhost:8083 predicates: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 0929791..983fa0a 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -65,7 +65,7 @@ spring: - id: festival uri: http://api-festival-service.festival.svc.cluster.local:8080 predicates: - - Path=/api/festival/** + - Path=/api/festival/**,/uploads/** - id: festival-swagger uri: http://api-festival-service.festival.svc.cluster.local:8080 From 01af780e63a748f5406bcc4e9b5cf7789d6642f0 Mon Sep 17 00:00:00 2001 From: V4N1LL4 Date: Tue, 2 Sep 2025 13:11:41 +0900 Subject: [PATCH 31/49] =?UTF-8?q?MNMS-570=20chore:=20prometheus=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f20f9de..a753595 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ ext { dependencies { implementation 'org.springframework.cloud:spring-cloud-starter-gateway-server-webflux' - implementation 'org.springframework.cloud:spring-cloud-starter-kubernetes-client-all' + implementation 'org.springframework.cloud:spring-cloud-starter-kubernetes-client-all' implementation 'org.springframework.boot:spring-boot-starter-actuator' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' @@ -49,6 +49,9 @@ dependencies { // Swagger implementation 'org.springdoc:springdoc-openapi-starter-webflux-ui:2.8.9' + + // Prometheus + implementation 'io.micrometer:micrometer-registry-prometheus' } dependencyManagement { imports { From 59c0992c73660696bfd4e8f5af6e09fb996565e1 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Thu, 4 Sep 2025 10:16:36 +0900 Subject: [PATCH 32/49] =?UTF-8?q?MNMS-468=20ssl=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 983fa0a..5f4500e 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -88,7 +88,9 @@ cors: - http://localhost:5173 - http://127.0.0.1:5173 - http://localhost:5174 - - http://127.0.031:5174 + - http://127.0.0.1:5174 + - https://www.rookies-tekcit.com + - http://www.rookies-tekcit.com # actuator 노출 범위, 조건 From 618e851a0bdbe722879c7b32012fa563f5d7acde Mon Sep 17 00:00:00 2001 From: V4N1LL4 Date: Thu, 4 Sep 2025 15:55:36 +0900 Subject: [PATCH 33/49] =?UTF-8?q?feat:=20application-local.yml=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(metric=20=ED=99=9C=EC=84=B1=ED=99=94)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-local.yml | 32 ++++++++++++++++++------ 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index a60cd50..5db75b6 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -6,8 +6,15 @@ spring: name: api-gateway cloud: gateway: + # ★ Gateway 메트릭 활성화 + metrics: + enabled: true server: webflux: + # ★ WebFlux 메트릭 활성화 + metrics: + enabled: true + # 기존 라우팅 유지(로컬 실행용 localhost 대상) routes: - id: user uri: http://localhost:8080 @@ -36,7 +43,8 @@ spring: - id: booking uri: http://localhost:8082 predicates: - - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/capcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** + # ★ capcha → captcha 로 오탈자 수정 + - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/captcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** - id: booking-swagger uri: http://localhost:8082 @@ -57,11 +65,11 @@ spring: filters: - StripPrefix=2 order: -1 + jwt: private-pem-path: classpath:keys/private.pem public-pem-path: classpath:keys/public.pem - # cors url 설정 cors: url: @@ -70,17 +78,26 @@ cors: - http://localhost:5174 - http://127.0.0.1:5174 - management: endpoints: web: - exposure: - include: '*' + # ★ Prometheus/Health 만 노출 (보안/안정) + include: # Spring Boot 3 표기 (기존 exposure.include 과 동일 효과) + - health + - prometheus + endpoint: health: show-details: always - endpoint: + probes: + enabled: true gateway: access: unrestricted + metrics: + # ★ 히스토그램 활성화(백분위 계산) + distribution: + percentiles-histogram: + http.server.requests: true + spring.cloud.gateway.requests: true logging: level: @@ -95,8 +112,9 @@ springdoc: - name: User url: /api/user/v3/api-docs + # ★ payments 로 경로 정합성 수정 - name: Payment - url: /api/payment/v3/api-docs + url: /api/payments/v3/api-docs - name: Booking url: /api/booking/v3/api-docs From 345c7f1d5ffec19b4482441aa1017e411b973a7a Mon Sep 17 00:00:00 2001 From: V4N1LL4 Date: Thu, 4 Sep 2025 16:29:22 +0900 Subject: [PATCH 34/49] =?UTF-8?q?feat:=20application-docker.yml=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(metric=20=ED=99=9C=EC=84=B1=ED=99=94)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-docker.yml | 35 +++++++++++++++++++---- src/main/resources/application-local.yml | 4 +-- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml index d0da894..35fe2ee 100644 --- a/src/main/resources/application-docker.yml +++ b/src/main/resources/application-docker.yml @@ -2,11 +2,20 @@ server: port: 10000 spring: + application: + name: api-gateway cloud: gateway: + # ★ Gateway 메트릭 활성화 + metrics: + enabled: true server: webflux: + # ★ WebFlux 메트릭 활성화 + metrics: + enabled: true routes: + # ========== User ========== - id: user uri: http://user:8080 predicates: @@ -19,6 +28,7 @@ spring: - StripPrefix=2 order: -1 + # ========== Payment ========== - id: payment uri: http://payment:8081 predicates: @@ -26,11 +36,13 @@ spring: - id: payment-swagger uri: http://payment:8081 predicates: - - Path=/api/payment/v3/api-docs + # ★ payments 로 정합성 수정 + - Path=/api/payments/v3/api-docs filters: - StripPrefix=2 order: -1 + # ========== Booking ========== - id: booking uri: http://booking:8082 predicates: @@ -43,6 +55,7 @@ spring: - StripPrefix=2 order: -1 + # ========== Festival ========== - id: festival uri: http://festival:8083 predicates: @@ -59,7 +72,7 @@ springdoc: swagger-ui: urls: - { name: User, url: /api/user/v3/api-docs } - - { name: Payment, url: /api/payment/v3/api-docs } + - { name: Payment, url: /api/payments/v3/api-docs } - { name: Booking, url: /api/booking/v3/api-docs } - { name: Festival, url: /api/festival/v3/api-docs } @@ -70,7 +83,6 @@ cors: - http://localhost:5174 - http://127.0.0.1:5174 - jwt: private-pem-path: ${JWT_PRIVATE_PEM_PATH:file:/etc/keys/private.pem} public-pem-path: ${JWT_PUBLIC_PEM_PATH:file:/etc/keys/public.pem} @@ -79,12 +91,25 @@ management: endpoints: web: exposure: - include: '*' + include: + - health + - prometheus + endpoint: health: show-details: always - endpoint: + probes: + enabled: true gateway: access: unrestricted + metrics: + # ★ 백분위 계산용 히스토그램 + distribution: + percentiles-histogram: + http.server.requests: true + spring.cloud.gateway.requests: true + # (선택) 모든 메트릭에 app 태그 부여 + tags: + application: ${spring.application.name:api-gateway} logging: level: diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 5db75b6..b4e486a 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -81,8 +81,8 @@ cors: management: endpoints: web: - # ★ Prometheus/Health 만 노출 (보안/안정) - include: # Spring Boot 3 표기 (기존 exposure.include 과 동일 효과) + exposure: + include: - health - prometheus endpoint: From 9192e0fd3f723a88d32dd57068dcb90c209e9d07 Mon Sep 17 00:00:00 2001 From: V4N1LL4 Date: Thu, 4 Sep 2025 17:14:30 +0900 Subject: [PATCH 35/49] =?UTF-8?q?feat:=20build.gradle=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 80 +++++++++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/build.gradle b/build.gradle index a753595..48af7f9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,63 +1,71 @@ plugins { - id 'java' - id 'org.springframework.boot' version '3.5.4' - id 'io.spring.dependency-management' version '1.1.7' + id 'java' + id 'org.springframework.boot' version '3.5.4' + id 'io.spring.dependency-management' version '1.1.7' } group = 'com.tekcit' version = '0.0.1-SNAPSHOT' java { - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } } repositories { - mavenCentral() + mavenCentral() } + ext { - set('springCloudVersion', "2025.0.0") + set('springCloudVersion', "2025.0.0") } dependencies { - implementation 'org.springframework.cloud:spring-cloud-starter-gateway-server-webflux' - implementation 'org.springframework.cloud:spring-cloud-starter-kubernetes-client-all' - implementation 'org.springframework.boot:spring-boot-starter-actuator' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - implementation 'org.springframework.boot:spring-boot-starter-security' - + // ===== Core ===== + implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'org.springframework.boot:spring-boot-starter-security' - // JWT - implementation 'io.jsonwebtoken:jjwt-api:0.11.5' - runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' - runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' - implementation 'org.springframework.security:spring-security-oauth2-jose' - implementation 'org.springframework.security:spring-security-oauth2-resource-server' + // ===== Spring Cloud Gateway (WebFlux 기반) ===== + implementation 'org.springframework.cloud:spring-cloud-starter-gateway-server-webflux' + implementation 'org.springframework.boot:spring-boot-starter-webflux' + // ===== Kubernetes Discovery ===== + implementation 'org.springframework.cloud:spring-cloud-starter-kubernetes-client-all' - // Lombok - compileOnly 'org.projectlombok:lombok' - annotationProcessor 'org.projectlombok:lombok' + // ===== JWT / Security ===== + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + implementation 'org.springframework.security:spring-security-oauth2-jose' + implementation 'org.springframework.security:spring-security-oauth2-resource-server' + // ===== Lombok ===== + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' - // JSON 직렬화용 - implementation 'com.fasterxml.jackson.core:jackson-databind' - implementation 'org.springframework.boot:spring-boot-starter-json' - testImplementation 'org.springframework.kafka:spring-kafka-test' + // ===== JSON ===== + implementation 'com.fasterxml.jackson.core:jackson-databind' + implementation 'org.springframework.boot:spring-boot-starter-json' - // Swagger + // ===== Swagger ===== implementation 'org.springdoc:springdoc-openapi-starter-webflux-ui:2.8.9' - // Prometheus - implementation 'io.micrometer:micrometer-registry-prometheus' + // ===== Metrics / Prometheus ===== + implementation 'io.micrometer:micrometer-registry-prometheus' + + // ===== Test ===== + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + testImplementation 'org.springframework.kafka:spring-kafka-test' } + dependencyManagement { - imports { - mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" - } + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" + } } + tasks.named('test') { - useJUnitPlatform() -} + useJUnitPlatform() +} \ No newline at end of file From 7c908a6a375d2e7a21698b94270f0cf3206cdff7 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Fri, 5 Sep 2025 09:54:56 +0900 Subject: [PATCH 36/49] =?UTF-8?q?MNMS-601=20feat=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-develop.yml | 2 +- src/main/resources/application-prod.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application-develop.yml b/src/main/resources/application-develop.yml index 80cec79..ce7deef 100644 --- a/src/main/resources/application-develop.yml +++ b/src/main/resources/application-develop.yml @@ -52,7 +52,7 @@ spring: - id: booking uri: http://api-booking-service.booking.svc.cluster.local:8080 predicates: - - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/capcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** + - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/captcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** - id: booking-swagger uri: http://api-booking-service.booking.svc.cluster.local:8080 diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 5f4500e..b205cea 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -52,7 +52,7 @@ spring: - id: booking uri: http://api-booking-service.booking.svc.cluster.local:8080 predicates: - - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/capcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** + - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/captcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** - id: booking-swagger uri: http://api-booking-service.booking.svc.cluster.local:8080 From cac9729b1fd9f7be6e2612f88dccd3eb83d11683 Mon Sep 17 00:00:00 2001 From: V4N1LL4 Date: Fri, 5 Sep 2025 10:32:06 +0900 Subject: [PATCH 37/49] =?UTF-8?q?chore:=20application-docker.yml=20observa?= =?UTF-8?q?tion=20enabled=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-docker.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml index 35fe2ee..5978cd5 100644 --- a/src/main/resources/application-docker.yml +++ b/src/main/resources/application-docker.yml @@ -9,6 +9,8 @@ spring: # ★ Gateway 메트릭 활성화 metrics: enabled: true + observation: + enabled: true server: webflux: # ★ WebFlux 메트릭 활성화 From 1b1c9ce73564e5514be5e86ad385ea05bcd5823d Mon Sep 17 00:00:00 2001 From: V4N1LL4 Date: Fri, 5 Sep 2025 10:39:26 +0900 Subject: [PATCH 38/49] =?UTF-8?q?chore:=20build.gradle=20micrometer=20impl?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 48af7f9..41aaefb 100644 --- a/build.gradle +++ b/build.gradle @@ -57,7 +57,10 @@ dependencies { // ===== Test ===== testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - testImplementation 'org.springframework.kafka:spring-kafka-test' + testImplementation 'org.springframework.kafka:spring-kafka-test'\ + + // ===== Micrometer Observation for Spring Cloud Gateway ===== + implementation 'org.springframework.cloud:spring-cloud-gateway-server-micrometer' } dependencyManagement { From da9e766e617d6c86e47629ab5a9f79513a21365c Mon Sep 17 00:00:00 2001 From: V4N1LL4 Date: Fri, 5 Sep 2025 10:52:13 +0900 Subject: [PATCH 39/49] =?UTF-8?q?feat(gateway):=20routes=20=EB=B8=94?= =?UTF-8?q?=EB=A1=9D=EC=9D=84=20spring.cloud.gateway=20=EB=A3=A8=ED=8A=B8?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99=ED=95=98=EC=97=AC=20=EB=9D=BC?= =?UTF-8?q?=EC=9A=B0=ED=8C=85/=EB=A9=94=ED=8A=B8=EB=A6=AD=20=EC=A0=95?= =?UTF-8?q?=EC=83=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 38 ++++---- src/main/resources/application-docker.yml | 102 +++++++++++----------- 2 files changed, 67 insertions(+), 73 deletions(-) diff --git a/build.gradle b/build.gradle index 41aaefb..8a7af4f 100644 --- a/build.gradle +++ b/build.gradle @@ -8,9 +8,7 @@ group = 'com.tekcit' version = '0.0.1-SNAPSHOT' java { - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } + toolchain { languageVersion = JavaLanguageVersion.of(17) } } repositories { @@ -22,45 +20,41 @@ ext { } dependencies { - // ===== Core ===== + // Core implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-security' - // ===== Spring Cloud Gateway (WebFlux 기반) ===== - implementation 'org.springframework.cloud:spring-cloud-starter-gateway-server-webflux' - implementation 'org.springframework.boot:spring-boot-starter-webflux' + // ★ Spring Cloud Gateway (starter 하나면 충분; WebFlux 포함됨) + implementation 'org.springframework.cloud:spring-cloud-starter-gateway' - // ===== Kubernetes Discovery ===== + // Kubernetes (필요시) implementation 'org.springframework.cloud:spring-cloud-starter-kubernetes-client-all' - // ===== JWT / Security ===== + // JWT / Security implementation 'io.jsonwebtoken:jjwt-api:0.11.5' - runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' - runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' implementation 'org.springframework.security:spring-security-oauth2-jose' implementation 'org.springframework.security:spring-security-oauth2-resource-server' - // ===== Lombok ===== + // Lombok compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' - // ===== JSON ===== + // JSON implementation 'com.fasterxml.jackson.core:jackson-databind' implementation 'org.springframework.boot:spring-boot-starter-json' - // ===== Swagger ===== + // Swagger implementation 'org.springdoc:springdoc-openapi-starter-webflux-ui:2.8.9' - // ===== Metrics / Prometheus ===== + // ★ Prometheus 레지스트리 implementation 'io.micrometer:micrometer-registry-prometheus' - // ===== Test ===== + // Test testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - testImplementation 'org.springframework.kafka:spring-kafka-test'\ - - // ===== Micrometer Observation for Spring Cloud Gateway ===== - implementation 'org.springframework.cloud:spring-cloud-gateway-server-micrometer' + testImplementation 'org.springframework.kafka:spring-kafka-test' } dependencyManagement { @@ -69,6 +63,4 @@ dependencyManagement { } } -tasks.named('test') { - useJUnitPlatform() -} \ No newline at end of file +tasks.named('test') { useJUnitPlatform() } \ No newline at end of file diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml index 5978cd5..21f6c1e 100644 --- a/src/main/resources/application-docker.yml +++ b/src/main/resources/application-docker.yml @@ -16,59 +16,61 @@ spring: # ★ WebFlux 메트릭 활성화 metrics: enabled: true - routes: - # ========== User ========== - - id: user - uri: http://user:8080 - predicates: - - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/**,/api/myPage/** - - id: user-swagger - uri: http://user:8080 - predicates: - - Path=/api/user/v3/api-docs - filters: - - StripPrefix=2 - order: -1 - # ========== Payment ========== - - id: payment - uri: http://payment:8081 - predicates: - - Path=/api/payments/**,/api/tekcitpay/** - - id: payment-swagger - uri: http://payment:8081 - predicates: - # ★ payments 로 정합성 수정 - - Path=/api/payments/v3/api-docs - filters: - - StripPrefix=2 - order: -1 + # ★★★ 여기만 이동 (gateway 직속) + routes: + # ========== User ========== + - id: user + uri: http://user:8080 + predicates: + - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/**,/api/myPage/** + - id: user-swagger + uri: http://user:8080 + predicates: + - Path=/api/user/v3/api-docs + filters: + - StripPrefix=2 + order: -1 - # ========== Booking ========== - - id: booking - uri: http://booking:8082 - predicates: - - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/captcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** - - id: booking-swagger - uri: http://booking:8082 - predicates: - - Path=/api/booking/v3/api-docs - filters: - - StripPrefix=2 - order: -1 + # ========== Payment ========== + - id: payment + uri: http://payment:8081 + predicates: + - Path=/api/payments/**,/api/tekcitpay/** + - id: payment-swagger + uri: http://payment:8081 + predicates: + # ★ payments 로 정합성 수정 + - Path=/api/payments/v3/api-docs + filters: + - StripPrefix=2 + order: -1 - # ========== Festival ========== - - id: festival - uri: http://festival:8083 - predicates: - - Path=/api/festival/**,/uploads/** - - id: festival-swagger - uri: http://festival:8083 - predicates: - - Path=/api/festival/v3/api-docs - filters: - - StripPrefix=2 - order: -1 + # ========== Booking ========== + - id: booking + uri: http://booking:8082 + predicates: + - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/captcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** + - id: booking-swagger + uri: http://booking:8082 + predicates: + - Path=/api/booking/v3/api-docs + filters: + - StripPrefix=2 + order: -1 + + # ========== Festival ========== + - id: festival + uri: http://festival:8083 + predicates: + - Path=/api/festival/**,/uploads/** + - id: festival-swagger + uri: http://festival:8083 + predicates: + - Path=/api/festival/v3/api-docs + filters: + - StripPrefix=2 + order: -1 springdoc: swagger-ui: From b3a9d4a6dfbf483ec64edbdca9d067049798cc12 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Wed, 10 Sep 2025 10:48:17 +0900 Subject: [PATCH 40/49] =?UTF-8?q?cors=20=EC=97=90=EB=9F=AC=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-develop.yml | 5 +++++ src/main/resources/application-docker.yml | 5 +++++ src/main/resources/application-local.yml | 4 ++++ src/main/resources/application-prod.yml | 5 ++++- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/resources/application-develop.yml b/src/main/resources/application-develop.yml index ce7deef..68bb737 100644 --- a/src/main/resources/application-develop.yml +++ b/src/main/resources/application-develop.yml @@ -54,6 +54,11 @@ spring: predicates: - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/captcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** + - id: booking_ws_route + uri: ws://api-booking-service.booking.svc.cluster.local:8080 + predicates: + - Path=/ws/** + - id: booking-swagger uri: http://api-booking-service.booking.svc.cluster.local:8080 predicates: diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml index 35fe2ee..627330d 100644 --- a/src/main/resources/application-docker.yml +++ b/src/main/resources/application-docker.yml @@ -47,6 +47,11 @@ spring: uri: http://booking:8082 predicates: - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/captcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** + - id: booking_ws_route + uri: ws://booking:8082 + predicates: + - Path=/ws/** + - - id: booking-swagger uri: http://booking:8082 predicates: diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index b4e486a..f1df18e 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -45,6 +45,10 @@ spring: predicates: # ★ capcha → captcha 로 오탈자 수정 - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/captcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** + - id: booking_ws_route + uri: ws://localhost:8082 + predicates: + - Path=/ws/** - id: booking-swagger uri: http://localhost:8082 diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index b205cea..e358e07 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -53,7 +53,10 @@ spring: uri: http://api-booking-service.booking.svc.cluster.local:8080 predicates: - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/captcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** - + - id: booking_ws_route + uri: ws://api-booking-service.booking.svc.cluster.local:8080 + predicates: + - Path=/ws/** - id: booking-swagger uri: http://api-booking-service.booking.svc.cluster.local:8080 predicates: From 3a4d64a8d5c7a0f5ca91b1db5ebeefd141b5853a Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Wed, 10 Sep 2025 10:59:52 +0900 Subject: [PATCH 41/49] =?UTF-8?q?=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-develop.yml | 3 --- src/main/resources/application-docker.yml | 1 - 2 files changed, 4 deletions(-) diff --git a/src/main/resources/application-develop.yml b/src/main/resources/application-develop.yml index 68bb737..74a6dea 100644 --- a/src/main/resources/application-develop.yml +++ b/src/main/resources/application-develop.yml @@ -25,7 +25,6 @@ spring: uri: http://api-user-service.user.svc.cluster.local:8080 predicates: - Path=/api/user/**,/api/addresses/**,/api/admin/**,/api/users/**,/api/mail/**,/api/auth/**,/api/myPage/** - - id: user-swagger uri: http://api-user-service.user.svc.cluster.local:8080 predicates: @@ -53,12 +52,10 @@ spring: uri: http://api-booking-service.booking.svc.cluster.local:8080 predicates: - Path=/api/booking/**,/api/qr/**,/api/host/**,/api/captcha/**,/api/transfer/**,/api/ticket/**,/api/statistics/**,/ws/** - - id: booking_ws_route uri: ws://api-booking-service.booking.svc.cluster.local:8080 predicates: - Path=/ws/** - - id: booking-swagger uri: http://api-booking-service.booking.svc.cluster.local:8080 predicates: diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml index 7d21a4b..9f72ca1 100644 --- a/src/main/resources/application-docker.yml +++ b/src/main/resources/application-docker.yml @@ -53,7 +53,6 @@ spring: uri: ws://booking:8082 predicates: - Path=/ws/** - - - id: booking-swagger uri: http://booking:8082 predicates: From cd1d434b57f4faad2a9899d51ba8b7c93e05d88c Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Wed, 10 Sep 2025 11:15:56 +0900 Subject: [PATCH 42/49] =?UTF-8?q?=20ws=20cors=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/filter/GatewayCorsConfig.java | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java index b880ddf..ef22846 100644 --- a/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java +++ b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java @@ -6,9 +6,14 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpHeaders; +import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; import java.util.List; @@ -24,7 +29,6 @@ public class GatewayCorsConfig { public CorsWebFilter corsWebFilter() { CorsConfiguration config = new CorsConfiguration(); for(String url : corsProperties.getUrl()){ - config.addAllowedOrigin(url); } config.addAllowedMethod("*"); @@ -36,4 +40,29 @@ public CorsWebFilter corsWebFilter() { return new CorsWebFilter(source); } + + @Bean + public WebFilter webSocketCorsFilter() { + return (ServerWebExchange exchange, WebFilterChain chain) -> { + ServerHttpRequest request = exchange.getRequest(); + String upgradeHeader = request.getHeaders().getFirst(HttpHeaders.UPGRADE); + + if ("websocket".equalsIgnoreCase(upgradeHeader)) { + String origin = request.getHeaders().getOrigin(); + CorsConfiguration corsConfig = new CorsConfiguration(); + + if (origin != null && corsProperties.getUrl().contains(origin)) { + corsConfig.addAllowedOrigin(origin); + corsConfig.setAllowCredentials(true); + corsConfig.addAllowedMethod("*"); + corsConfig.addAllowedHeader("*"); + } + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", corsConfig); + } + + return chain.filter(exchange); + }; + } } \ No newline at end of file From ae58aeeee0f04a13a58b711e13c95ca891e9c6e6 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Wed, 10 Sep 2025 13:01:46 +0900 Subject: [PATCH 43/49] =?UTF-8?q?=20payments=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-develop.yml | 4 ++-- src/main/resources/application-prod.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/application-develop.yml b/src/main/resources/application-develop.yml index 74a6dea..92ab792 100644 --- a/src/main/resources/application-develop.yml +++ b/src/main/resources/application-develop.yml @@ -42,7 +42,7 @@ spring: - id: payment-swagger uri: http://api-payment-service.payment.svc.cluster.local:8080 predicates: - - Path=/api/payment/v3/api-docs + - Path=/api/payments/v3/api-docs filters: - StripPrefix=2 order: -1 @@ -119,7 +119,7 @@ springdoc: url: /api/user/v3/api-docs - name: Payment - url: /api/payment/v3/api-docs + url: /api/payments/v3/api-docs - name: Booking url: /api/booking/v3/api-docs diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index e358e07..195db4a 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -43,7 +43,7 @@ spring: - id: payment-swagger uri: http://api-payment-service.payment.svc.cluster.local:8080 predicates: - - Path=/api/payment/v3/api-docs + - Path=/api/payments/v3/api-docs filters: - StripPrefix=2 order: -1 @@ -122,7 +122,7 @@ springdoc: url: /api/user/v3/api-docs - name: Payment - url: /api/payment/v3/api-docs + url: /api/payments/v3/api-docs - name: Booking url: /api/booking/v3/api-docs From 1f20ea8ce100ef67cfd94448883c902a321a1000 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Wed, 10 Sep 2025 13:52:00 +0900 Subject: [PATCH 44/49] =?UTF-8?q?=20header=20=EB=91=90=EA=B0=9C=20?= =?UTF-8?q?=EB=B6=99=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/gateway/filter/GatewayCorsConfig.java | 12 +++++++----- src/main/resources/application-docker.yml | 1 - 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java index ef22846..88d3dfa 100644 --- a/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java +++ b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java @@ -51,11 +51,13 @@ public WebFilter webSocketCorsFilter() { String origin = request.getHeaders().getOrigin(); CorsConfiguration corsConfig = new CorsConfiguration(); - if (origin != null && corsProperties.getUrl().contains(origin)) { - corsConfig.addAllowedOrigin(origin); - corsConfig.setAllowCredentials(true); - corsConfig.addAllowedMethod("*"); - corsConfig.addAllowedHeader("*"); + if (origin != null && corsProperties.getUrl().contains(origin) && + !exchange.getResponse().getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)) { + + exchange.getResponse().getHeaders().add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin); + exchange.getResponse().getHeaders().add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"); + exchange.getResponse().getHeaders().add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "*"); + exchange.getResponse().getHeaders().add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*"); } UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml index 9f72ca1..38621af 100644 --- a/src/main/resources/application-docker.yml +++ b/src/main/resources/application-docker.yml @@ -38,7 +38,6 @@ spring: - id: payment-swagger uri: http://payment:8081 predicates: - # ★ payments 로 정합성 수정 - Path=/api/payments/v3/api-docs filters: - StripPrefix=2 From bb09cc763db4c968d5f5981efcfab76c06bf72b0 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Wed, 10 Sep 2025 14:39:38 +0900 Subject: [PATCH 45/49] cors --- .../gateway/filter/GatewayCorsConfig.java | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java index 88d3dfa..e21f224 100644 --- a/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java +++ b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java @@ -41,30 +41,6 @@ public CorsWebFilter corsWebFilter() { return new CorsWebFilter(source); } - @Bean - public WebFilter webSocketCorsFilter() { - return (ServerWebExchange exchange, WebFilterChain chain) -> { - ServerHttpRequest request = exchange.getRequest(); - String upgradeHeader = request.getHeaders().getFirst(HttpHeaders.UPGRADE); - - if ("websocket".equalsIgnoreCase(upgradeHeader)) { - String origin = request.getHeaders().getOrigin(); - CorsConfiguration corsConfig = new CorsConfiguration(); - - if (origin != null && corsProperties.getUrl().contains(origin) && - !exchange.getResponse().getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)) { - exchange.getResponse().getHeaders().add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin); - exchange.getResponse().getHeaders().add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"); - exchange.getResponse().getHeaders().add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "*"); - exchange.getResponse().getHeaders().add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*"); - } - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", corsConfig); - } - - return chain.filter(exchange); - }; - } } \ No newline at end of file From 2805d7babc46556712a079a6f6c6a85a84689dad Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Wed, 10 Sep 2025 17:07:26 +0900 Subject: [PATCH 46/49] cors filter : --- .../gateway/filter/GatewayCorsConfig.java | 51 ++++++++++++++----- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java index e21f224..e083ff5 100644 --- a/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java +++ b/src/main/java/com/tekcit/gateway/config/gateway/filter/GatewayCorsConfig.java @@ -25,22 +25,47 @@ public class GatewayCorsConfig { private final CorsProperties corsProperties; - @Bean - public CorsWebFilter corsWebFilter() { - CorsConfiguration config = new CorsConfiguration(); - for(String url : corsProperties.getUrl()){ - config.addAllowedOrigin(url); - } - config.addAllowedMethod("*"); - config.addAllowedHeader("*"); - config.setAllowCredentials(true); +// @Bean +// public CorsWebFilter corsWebFilter() { +// CorsConfiguration config = new CorsConfiguration(); +// for(String url : corsProperties.getUrl()){ +// config.addAllowedOrigin(url); +// } +// config.addAllowedMethod("*"); +// config.addAllowedHeader("*"); +// config.setAllowCredentials(true); +// +// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); +// +// source.registerCorsConfiguration("/**", config); +// +// return new CorsWebFilter(source); +// } +// + + @Bean + public WebFilter corsFilter() { + return (exchange, chain) -> { + String path = exchange.getRequest().getURI().getPath(); + if (path.startsWith("/ws")) { + // WebSocket 경로는 CORS 필터 건너뛰기 (서버로 그대로 전달) + return chain.filter(exchange); + } - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", config); + CorsConfiguration config = new CorsConfiguration(); + for (String url : corsProperties.getUrl()) { + config.addAllowedOrigin(url); + } + config.addAllowedMethod("*"); + config.addAllowedHeader("*"); + config.setAllowCredentials(true); - return new CorsWebFilter(source); - } + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + return new CorsWebFilter(source).filter(exchange, chain); + }; + } } \ No newline at end of file From 7a559b82a3c6e1ffc840eb8aea0d2aeb4ab0b6b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=9E=AC=EC=9A=B4?= Date: Thu, 11 Sep 2025 11:08:40 +0900 Subject: [PATCH 47/49] =?UTF-8?q?feat:=20=EB=A7=8C=EB=A3=8C=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EC=97=90=EB=9F=AC=20=EC=B2=98=EB=A6=AC=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../JwtSecurityContextRepository.java | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java b/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java index a81b223..3cb2f83 100644 --- a/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java +++ b/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java @@ -2,8 +2,12 @@ import com.tekcit.gateway.config.security.jwt.JwtTokenProvider; import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.JwtException; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContext; @@ -14,6 +18,7 @@ import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; +import java.nio.charset.StandardCharsets; import java.util.List; @Component @@ -45,8 +50,32 @@ public Mono load(ServerWebExchange exchange) { JwtAuthenticationToken auth = new JwtAuthenticationToken(jwtTokenProvider.convertToSpringJwt(token), authorities); return Mono.just(new SecurityContextImpl(auth)); + } catch (ExpiredJwtException e) { + return writeJson(exchange, HttpStatus.UNAUTHORIZED, + false, "AUTHENTICATION_ERROR", "액세스 토큰이 만료되었습니다.") + .then(Mono.empty()); + + } catch (JwtException e) { + return writeJson(exchange, HttpStatus.UNAUTHORIZED, + false, "AUTHENTICATION_ERROR", "유효하지 않은 액세스 토큰입니다.") + .then(Mono.empty()); + } catch (Exception e) { - return Mono.empty(); + return writeJson(exchange, HttpStatus.UNAUTHORIZED, + false, "AUTHENTICATION_ERROR", "토큰 처리 중 오류가 발생했습니다.") + .then(Mono.empty()); } } + + + private Mono writeJson(ServerWebExchange exchange, HttpStatus status, boolean success, String code, String message) { + var res = exchange.getResponse(); + if (res.isCommitted()) + return Mono.empty(); + res.setStatusCode(status); + res.getHeaders().setContentType(MediaType.APPLICATION_JSON); + String body = String.format("{\"success\":%s,\"code\":\"%s\",\"message\":\"%s\"}", success, code, message); + var buf = res.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8)); + return res.writeWith(Mono.just(buf)); + } } \ No newline at end of file From 1cb93939f1f0aa9af7964788f8dbdb87c5995d0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=9E=AC=EC=9A=B4?= Date: Mon, 15 Sep 2025 14:12:16 +0900 Subject: [PATCH 48/49] =?UTF-8?q?fix:=20error=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EB=B0=A9=EB=B2=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/filter/JwtToHeaderFilter.java | 8 +-- .../JwtSecurityContextRepository.java | 27 ++-------- .../gateway/exception/ApiErrorUtils.java | 49 +++++++++++++++++++ 3 files changed, 58 insertions(+), 26 deletions(-) create mode 100644 src/main/java/com/tekcit/gateway/exception/ApiErrorUtils.java diff --git a/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java b/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java index 76eecc1..deec468 100644 --- a/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java +++ b/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java @@ -33,9 +33,11 @@ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { String jwt = auth.getToken().getTokenValue(); Claims claims = jwtTokenProvider.getAllClaims(jwt); ServerHttpRequest mutatedRequest = exchange.getRequest().mutate() - .header("X-User-Id", jwtTokenProvider.getSubject(claims)) - .header("X-User-Name", jwtTokenProvider.getName(claims)) - .header("X-User-Role", jwtTokenProvider.getClaimAsString(claims,"role")) + .headers(h ->{ + h.set("X-User-Id", jwtTokenProvider.getSubject(claims)); + h.set("X-User-Name", jwtTokenProvider.getName(claims)); + h.set("X-User-Role", jwtTokenProvider.getClaimAsString(claims, "role")); + }) .build(); return chain.filter(exchange.mutate().request(mutatedRequest).build()); }) diff --git a/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java b/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java index 3cb2f83..adbd959 100644 --- a/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java +++ b/src/main/java/com/tekcit/gateway/config/security/repository/JwtSecurityContextRepository.java @@ -1,13 +1,12 @@ package com.tekcit.gateway.config.security.repository; import com.tekcit.gateway.config.security.jwt.JwtTokenProvider; +import com.tekcit.gateway.exception.ApiErrorUtils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.JwtException; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContext; @@ -18,7 +17,6 @@ import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; -import java.nio.charset.StandardCharsets; import java.util.List; @Component @@ -51,31 +49,14 @@ public Mono load(ServerWebExchange exchange) { JwtAuthenticationToken auth = new JwtAuthenticationToken(jwtTokenProvider.convertToSpringJwt(token), authorities); return Mono.just(new SecurityContextImpl(auth)); } catch (ExpiredJwtException e) { - return writeJson(exchange, HttpStatus.UNAUTHORIZED, - false, "AUTHENTICATION_ERROR", "액세스 토큰이 만료되었습니다.") + return ApiErrorUtils.unauthorized(exchange, "AUTH_ACCESS_TOKEN_EXPIRED", "액세스 토큰이 만료되었습니다.") .then(Mono.empty()); - } catch (JwtException e) { - return writeJson(exchange, HttpStatus.UNAUTHORIZED, - false, "AUTHENTICATION_ERROR", "유효하지 않은 액세스 토큰입니다.") + return ApiErrorUtils.unauthorized(exchange, "AUTH_INVALID_TOKEN", "유효하지 않은 액세스 토큰입니다.") .then(Mono.empty()); - } catch (Exception e) { - return writeJson(exchange, HttpStatus.UNAUTHORIZED, - false, "AUTHENTICATION_ERROR", "토큰 처리 중 오류가 발생했습니다.") + return ApiErrorUtils.unauthorized(exchange, "AUTHENTICATION_ERROR", "토큰 처리 중 오류가 발생했습니다.") .then(Mono.empty()); } } - - - private Mono writeJson(ServerWebExchange exchange, HttpStatus status, boolean success, String code, String message) { - var res = exchange.getResponse(); - if (res.isCommitted()) - return Mono.empty(); - res.setStatusCode(status); - res.getHeaders().setContentType(MediaType.APPLICATION_JSON); - String body = String.format("{\"success\":%s,\"code\":\"%s\",\"message\":\"%s\"}", success, code, message); - var buf = res.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8)); - return res.writeWith(Mono.just(buf)); - } } \ No newline at end of file diff --git a/src/main/java/com/tekcit/gateway/exception/ApiErrorUtils.java b/src/main/java/com/tekcit/gateway/exception/ApiErrorUtils.java new file mode 100644 index 0000000..04ba8b7 --- /dev/null +++ b/src/main/java/com/tekcit/gateway/exception/ApiErrorUtils.java @@ -0,0 +1,49 @@ +package com.tekcit.gateway.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.nio.charset.StandardCharsets; + +public final class ApiErrorUtils { + + private ApiErrorUtils() {} + + public static Mono write(ServerWebExchange exchange, HttpStatus status, String code, String message) { + if (exchange.getResponse().isCommitted()) return Mono.empty(); + + exchange.getResponse().setStatusCode(status); + exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON); + + String body = String.format( + "{\"success\":false," + + "\"errorCode\":\"%s\"," + + "\"errorMessage\":\"%s\"}", + code, escape(message) + ); + + var buf = exchange.getResponse() + .bufferFactory() + .wrap(body.getBytes(StandardCharsets.UTF_8)); + + return exchange.getResponse().writeWith(Mono.just(buf)); + } + + public static Mono unauthorized(ServerWebExchange ex, String code, String msg) { + return write(ex, HttpStatus.UNAUTHORIZED, code, msg); + } + + public static Mono forbidden(ServerWebExchange ex, String code, String msg) { + return write(ex, HttpStatus.FORBIDDEN, code, msg); + } + + public static Mono badRequest(ServerWebExchange ex, String code, String msg) { + return write(ex, HttpStatus.BAD_REQUEST, code, msg); + } + + private static String escape(String s) { + return s == null ? "" : s.replace("\"", "\\\""); + } +} \ No newline at end of file From 46ad7255dd437dc4e31d9680e317c44ad78defa2 Mon Sep 17 00:00:00 2001 From: rookies-dogun Date: Tue, 16 Sep 2025 15:39:20 +0900 Subject: [PATCH 49/49] performance test --- build.gradle | 2 + .../gateway/filter/JwtToHeaderFilter.java | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/build.gradle b/build.gradle index 8a7af4f..15c364b 100644 --- a/build.gradle +++ b/build.gradle @@ -55,6 +55,8 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testImplementation 'org.springframework.kafka:spring-kafka-test' + + } dependencyManagement { diff --git a/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java b/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java index deec468..f3460c5 100644 --- a/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java +++ b/src/main/java/com/tekcit/gateway/config/gateway/filter/JwtToHeaderFilter.java @@ -23,6 +23,35 @@ public class JwtToHeaderFilter implements GlobalFilter, Ordered { @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + + String upgradeHeader = exchange.getRequest().getHeaders().getFirst("Upgrade"); + System.out.println(upgradeHeader); + + + if (upgradeHeader != null && upgradeHeader.equalsIgnoreCase("websocket")) { + // WebSocket handshake 요청이면 SecurityContext와 상관없이 JWT를 직접 읽어 헤더 추가 + String token = exchange.getRequest().getQueryParams().getFirst("token"); + exchange.getRequest().getHeaders().forEach((key, values) -> { + System.out.println(key + " : " + values); + }); + if (token != null && token.startsWith("Bearer ")) { + String jwt = token.substring(7); + Claims claims = jwtTokenProvider.getAllClaims(jwt); + System.out.println(" ============= WebSocket Authorization ================"); + System.out.println("X-User-Id" + jwtTokenProvider.getSubject(claims)); + System.out.println("X-User-Name" + jwtTokenProvider.getName(claims)); + System.out.println("X-User-Role" + jwtTokenProvider.getClaimAsString(claims, "role")); + ServerHttpRequest mutatedRequest = exchange.getRequest().mutate() + .headers(h -> { + h.set("X-User-Id", jwtTokenProvider.getSubject(claims)); + h.set("X-User-Name", jwtTokenProvider.getName(claims)); + h.set("X-User-Role", jwtTokenProvider.getClaimAsString(claims, "role")); + }) + .build(); + return chain.filter(exchange.mutate().request(mutatedRequest).build()); + } + } + return ReactiveSecurityContextHolder.getContext() .doOnNext(ctx -> System.out.println("SecurityContext 있음: " + ctx)) .map(securityContext -> securityContext.getAuthentication()) @@ -32,6 +61,17 @@ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { .flatMap(auth -> { String jwt = auth.getToken().getTokenValue(); Claims claims = jwtTokenProvider.getAllClaims(jwt); + + // 요청 주소 확인 + ServerHttpRequest request = exchange.getRequest(); + System.out.println("요청 URI: " + request.getURI()); + System.out.println("요청 Path: " + request.getPath()); + System.out.println("요청 Method: " + request.getMethod()); + + System.out.println("X-User-Id " + jwtTokenProvider.getSubject(claims)); + System.out.println("X-User-Name " + jwtTokenProvider.getName(claims)); + System.out.println("X-User-Role " + jwtTokenProvider.getClaimAsString(claims, "role")); + ServerHttpRequest mutatedRequest = exchange.getRequest().mutate() .headers(h ->{ h.set("X-User-Id", jwtTokenProvider.getSubject(claims));