Skip to content

Repository files navigation

Lesson Matching Platform API Gateway

Lesson Matching Platform의 외부 진입점입니다. Spring Cloud Gateway(WebFlux)를 기반으로 클라이언트 요청을 내부 서비스로 전달하고, JWT 인증·CORS·요청 추적·사용자 정보 전달을 공통 처리합니다.

Role in the Platform

flowchart LR
    Client["Client"] --> Gateway["API Gateway\nSpring Cloud Gateway"]
    Gateway --> Backend["Core Backend"]
    Gateway --> Ledger["Ledger"]
    Gateway --> Notice["Notice Server"]
Loading

게이트웨이는 비즈니스 로직을 직접 처리하기보다 모든 서비스 요청에 공통으로 필요한 경계 책임을 담당합니다.

Responsibilities

1. Service Routing

라우팅 대상 주소는 환경 변수로 주입합니다. 로컬에서는 기본값으로 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

2. JWT Authentication

  • Authorization: Bearer <token> 형식의 JWT를 추출합니다.
  • JwtProvider가 서명과 만료 여부를 검증합니다.
  • JWT claims에서 userId, role을 읽어 Reactive Security Context에 저장합니다.
  • 인증이 필요한 요청은 검증에 실패하면 내부 서비스로 전달하지 않습니다.
  • CSRF, Form Login, HTTP Basic은 stateless API 특성에 맞게 비활성화했습니다.

인증 없이 접근할 수 있는 주요 경로는 다음과 같습니다.

  • OPTIONS /**
  • GET /api/v1/categories
  • GET /api/v1/lessons/search
  • GET /api/v1/lessons/{lessonId}
  • GET /api/v1/lessons/{lessonId}/reviews
  • POST /api/v1/auth/login
  • POST /api/v1/members/signup
  • /auth/**, /public/**, /actuator/**
  • /resources/files/**, /test

그 외 요청은 인증된 JWT가 필요합니다.

3. User Context Propagation

인증된 요청은 UserInfoFilter를 통해 아래 헤더를 내부 서비스로 전달합니다.

X-User-Id    # JWT의 userId claim
X-User-Role  # JWT의 role claim

내부 서비스가 요청마다 JWT를 다시 파싱하지 않고도 인증된 사용자의 식별자와 역할을 사용할 수 있도록 공통 경계를 제공합니다.

4. Request ID Propagation

RequestIdFilter는 모든 요청에 X-Request-Id를 부여합니다.

  • 클라이언트가 유효한 X-Request-Id를 보내면 기존 값을 유지합니다.
  • 값이 없으면 UUID를 생성합니다.
  • 요청 헤더와 응답 헤더에 동일한 ID를 설정합니다.
  • MDC에 ID를 넣어 로그에서 요청 단위 추적이 가능하도록 합니다.

5. CORS and Observability

  • 로컬 프론트엔드(localhost:3000, 127.0.0.1:3000)를 CORS 허용 origin으로 설정합니다.
  • Authorization, Content-Type, Accept 등의 요청 헤더를 허용합니다.
  • /actuator/health, /actuator/info, /actuator/prometheus를 노출합니다.
  • Prometheus 메트릭에 애플리케이션 이름을 태그로 추가합니다.

Authentication Flow

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
Loading

Project Structure

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

Tech Stack

  • 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

Configuration

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을 분리해서 사용해야 합니다.

Local Development

./gradlew bootRun

기본 포트는 8080입니다. 내부 서비스 주소를 로컬에서 지정하려면 다음과 같이 실행할 수 있습니다.

BACKEND_HOST=localhost \
LEDGER_HOST=localhost \
NOTIFICATION_HOST=localhost \
./gradlew bootRun

/auth/token 엔드포인트는 userIdrole을 받아 JWT를 생성합니다. 이 기능은 로컬·개발 테스트 편의를 위한 용도로만 사용하고, 외부에 공개되는 운영 환경에서는 접근을 제한해야 합니다.

예시:

curl "http://localhost:8080/auth/token?userId=user-001&role=USER"

Build and Test

./gradlew test
./gradlew build

현재 테스트는 JWT 생성·파싱·검증과 Spring Boot 컨텍스트 구성을 중심으로 구성되어 있습니다.

CI/CD

.github/workflows/gradle.ymlmain 브랜치 push를 기준으로 다음 작업을 수행합니다.

  1. Java 21 환경 구성
  2. Gradle build
  3. GitHub Actions Workload Identity Federation으로 GCP 인증
  4. Docker 이미지 빌드
  5. Artifact Registry에 이미지 push
  6. api-gateway Compute Engine VM 재시작

GCP 인증 정보는 GitHub Actions Secret과 Variable로 관리하며 저장소에 직접 기록하지 않습니다.

Security Notes

  • JWT secret은 Secret Manager에서 관리합니다.
  • 내부 서비스는 외부에 직접 노출하지 않고 Gateway를 통해 접근하도록 구성하는 것을 전제로 합니다.
  • X-User-Id, X-User-Role은 내부 네트워크에서만 신뢰해야 하며, 내부 서비스 포트가 외부에 공개되지 않도록 방화벽을 제한해야 합니다.
  • /auth/token은 테스트용 토큰 발급 기능이므로 운영 환경에서 비활성화하거나 접근을 제한해야 합니다.
  • CORS 허용 origin은 실제 배포 도메인에 맞게 최소 범위로 설정해야 합니다.

About

MSA 구조에서 api를 중개하고 인증 및 인가를 처리하는 게이트웨이

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages