Lesson Matching Platform의 외부 진입점입니다. Spring Cloud Gateway(WebFlux)를 기반으로 클라이언트 요청을 내부 서비스로 전달하고, JWT 인증·CORS·요청 추적·사용자 정보 전달을 공통 처리합니다.
flowchart LR
Client["Client"] --> Gateway["API Gateway\nSpring Cloud Gateway"]
Gateway --> Backend["Core Backend"]
Gateway --> Ledger["Ledger"]
Gateway --> Notice["Notice Server"]
게이트웨이는 비즈니스 로직을 직접 처리하기보다 모든 서비스 요청에 공통으로 필요한 경계 책임을 담당합니다.
라우팅 대상 주소는 환경 변수로 주입합니다. 로컬에서는 기본값으로 localhost를 사용하고, GCP에서는 Terraform이 내부 DNS 호스트명을 전달합니다.
| Incoming path | Destination | Gateway behavior |
|---|---|---|
/api/** |
Core Backend | X-Request-Source: Gateway 추가 |
/resources/files/** |
Core Backend | 파일 리소스 전달, 인증 예외 |
/api/v1/ledger-entry/** |
Ledger | 앞의 두 경로 세그먼트 제거 후 전달 |
/api/v1/notification/** |
Notice Server | X-Request-Source: Gateway 추가 |
/test |
Mock Server | 테스트용 경로 재작성 |
주요 환경 변수:
BACKEND_HOST # Core Backend host
LEDGER_HOST # Ledger host
NOTIFICATION_HOST # Notice Server host
Authorization: Bearer <token>형식의 JWT를 추출합니다.JwtProvider가 서명과 만료 여부를 검증합니다.- JWT claims에서
userId,role을 읽어 Reactive Security Context에 저장합니다. - 인증이 필요한 요청은 검증에 실패하면 내부 서비스로 전달하지 않습니다.
- CSRF, Form Login, HTTP Basic은 stateless API 특성에 맞게 비활성화했습니다.
인증 없이 접근할 수 있는 주요 경로는 다음과 같습니다.
OPTIONS /**GET /api/v1/categoriesGET /api/v1/lessons/searchGET /api/v1/lessons/{lessonId}GET /api/v1/lessons/{lessonId}/reviewsPOST /api/v1/auth/loginPOST /api/v1/members/signup/auth/**,/public/**,/actuator/**/resources/files/**,/test
그 외 요청은 인증된 JWT가 필요합니다.
인증된 요청은 UserInfoFilter를 통해 아래 헤더를 내부 서비스로 전달합니다.
X-User-Id # JWT의 userId claim
X-User-Role # JWT의 role claim
내부 서비스가 요청마다 JWT를 다시 파싱하지 않고도 인증된 사용자의 식별자와 역할을 사용할 수 있도록 공통 경계를 제공합니다.
RequestIdFilter는 모든 요청에 X-Request-Id를 부여합니다.
- 클라이언트가 유효한
X-Request-Id를 보내면 기존 값을 유지합니다. - 값이 없으면 UUID를 생성합니다.
- 요청 헤더와 응답 헤더에 동일한 ID를 설정합니다.
- MDC에 ID를 넣어 로그에서 요청 단위 추적이 가능하도록 합니다.
- 로컬 프론트엔드(
localhost:3000,127.0.0.1:3000)를 CORS 허용 origin으로 설정합니다. Authorization,Content-Type,Accept등의 요청 헤더를 허용합니다./actuator/health,/actuator/info,/actuator/prometheus를 노출합니다.- Prometheus 메트릭에 애플리케이션 이름을 태그로 추가합니다.
sequenceDiagram
participant Client
participant Gateway
participant Service as Internal Service
Client->>Gateway: Request + Bearer JWT
Gateway->>Gateway: Validate signature and claims
Gateway->>Gateway: Create or preserve X-Request-Id
Gateway->>Service: X-User-Id / X-User-Role / X-Request-Id
Service-->>Gateway: Response
Gateway-->>Client: Response + X-Request-Id
src/main/java/com/hwan/gateway/
├── config/
│ └── SecurityConfig.java # WebFlux Security, CORS, public paths
├── controller/
│ └── AuthController.java # Token endpoint for local/dev use
├── filter/
│ ├── RequestIdFilter.java # Request ID 생성·전파·MDC 기록
│ └── UserInfoFilter.java # 사용자 정보 헤더 전달
├── jwt/
│ ├── JwtAuthenticationConverter.java # Bearer token 추출
│ ├── JwtAuthenticationManager.java # JWT 검증과 Authentication 생성
│ └── JwtProvider.java # JWT 생성·파싱·검증
└── GatewayApplication.java
- Java 21
- Spring Boot 4.1.0
- Spring Cloud Gateway 2025.1.2
- Spring WebFlux
- Spring Security WebFlux
- JJWT 0.12.7
- Spring Cloud GCP Secret Manager
- Spring Boot Actuator + Micrometer Prometheus Registry
- Gradle
JWT secret은 코드나 설정 파일에 직접 저장하지 않고 Spring Cloud GCP Secret Manager import를 통해 주입합니다.
spring:
config:
import: sm://
jwt:
secret: ${sm://JWT_SECRET_KEY}로컬에서 Secret Manager를 사용하지 않는 경우에는 실행 환경에서 JWT_SECRET_KEY를 안전하게 주입하는 별도 설정이 필요합니다. 실제 운영 Secret과 테스트용 Secret을 분리해서 사용해야 합니다.
./gradlew bootRun기본 포트는 8080입니다. 내부 서비스 주소를 로컬에서 지정하려면 다음과 같이 실행할 수 있습니다.
BACKEND_HOST=localhost \
LEDGER_HOST=localhost \
NOTIFICATION_HOST=localhost \
./gradlew bootRun/auth/token 엔드포인트는 userId와 role을 받아 JWT를 생성합니다. 이 기능은 로컬·개발 테스트 편의를 위한 용도로만 사용하고, 외부에 공개되는 운영 환경에서는 접근을 제한해야 합니다.
예시:
curl "http://localhost:8080/auth/token?userId=user-001&role=USER"./gradlew test
./gradlew build현재 테스트는 JWT 생성·파싱·검증과 Spring Boot 컨텍스트 구성을 중심으로 구성되어 있습니다.
.github/workflows/gradle.yml은 main 브랜치 push를 기준으로 다음 작업을 수행합니다.
- Java 21 환경 구성
- Gradle build
- GitHub Actions Workload Identity Federation으로 GCP 인증
- Docker 이미지 빌드
- Artifact Registry에 이미지 push
api-gatewayCompute Engine VM 재시작
GCP 인증 정보는 GitHub Actions Secret과 Variable로 관리하며 저장소에 직접 기록하지 않습니다.
- JWT secret은 Secret Manager에서 관리합니다.
- 내부 서비스는 외부에 직접 노출하지 않고 Gateway를 통해 접근하도록 구성하는 것을 전제로 합니다.
X-User-Id,X-User-Role은 내부 네트워크에서만 신뢰해야 하며, 내부 서비스 포트가 외부에 공개되지 않도록 방화벽을 제한해야 합니다./auth/token은 테스트용 토큰 발급 기능이므로 운영 환경에서 비활성화하거나 접근을 제한해야 합니다.- CORS 허용 origin은 실제 배포 도메인에 맞게 최소 범위로 설정해야 합니다.