API 게이트웨이(API Gateway)의 역할은 무엇인가요?

김상욱·2024년 12월 22일
0

API 게이트웨이(API Gateway)의 역할은 무엇인가요?

What?

API 게이트웨이는 클라이언트와 백엔드 마이크로서비스들 사이의 중간 계층으로, 모든 API 요청이 처음으로 도달하는 진입점입니다. 쉽게 말해, 클라이언트가 여러 개의 마이크로서비스에 직접 접근하는 대신, API 게이트웨이를 통해 요청을 전달하게 됩니다.

ROLE

  1. 요청 라우팅(Request Routing)
  • 클라이언트의 요청을 적절한 마이크로서비스로 전달합니다. 예를 들어, 사용자 관련 요청은 User Service로 주문 관련 요청은 Order Service로 라우팅됩니다. Spring Cloud Gateway 같은 도구를 사용하여 라우팅 규칙을 설정할 수 있습니다.
  1. 인증 및 권한 부여(Authentication & Authorization)
  • API 게이트웨이는 모든 요청에 대해 인증을 수행할 수 있습니다. 권한 부여도 여기서 처리하며, 사용자가 특정 리소스에 접근할 수 있는지 확인합니다. 이를 통해 개별 마이크로서비스에서는 인증 로직을 중복구현할 필요가 없어집니다.
  1. API 집계(API Aggregation)
  • 복수의 마이크로서비스에서 데이터를 가져와 하나의 응답으로 합칠 수 있습니다. 예를 들어, 사용자 정보와 주문 정보를 한 번의 API 호출로 제공할 수 있습니다. 이는 클라이언트의 요청 횟수를 줄이고 성능을 향상시킵니다.
  1. 로드 밸런싱(Load Balancing)
  • 여러 인스턴스에 걸쳐 요청을 분산시켜 서버의 부하를 균등하게 유지합니다. Spring Cloud Gateway는 내장된 로드 밸런싱 기능을 제공합니다.
  1. 요청 및 응답 변환(Request & Response Transformation)
  • 요청 헤더나 본문을 변환하거나, 응답을 클라이언트가 이해할 수 있는 형식으로 변환할 수 있습니다. 예를 들어, 특정 헤더를 추가하거나, 데이터 포맷을 변경하는 작업을 수행할 수 있습니다.
  1. 캐싱(Caching)
  • 자주 요청되는 데이터를 캐싱하여 응답 속도를 높이고, 백엔드 서비스의 부하를 줄입니다. API 게이트웨이는 캐시 정책을 설정하고 관리할 수 있습니다.
  1. 모니터링 및 로깅 (Monitoring & Logging)
  • 모든 요청과 응답을 로깅하고, 모니터링하여 시스템의 상태를 파악할 수 있습니다. 이를 통해 장애 발생 시 빠르게 대응하고 성능을 최적화할 수 있습니다.
  1. 보안(Security)
  • DDos 공격 방어, 트래픽 암호화(SSL/TLS), iP 화이트리스트/블랙리스트 관리 등 보안 관련 기능을 제공합니다. API 게이트웨이를 통해 중앙에서 보안 정책을 관리할 수 있습니다.

Why?

  1. 단일 진입점(Single entry Point) 제공
  • 클라이언트는 하나의 진입점을 통해 모든 마이크로서비스에 접근할 수 있어, 관리가 용이합니다.
  1. 마이크로서비스의 복잡성 감소
  • 클라이언트가 각 마이크로서비스의 엔드포인트를 알 필요가 없으며, API 게이트웨이가 이를 추상화합니다.
  1. 중앙 집중식 관리
  • 인증, 로깅, 모니터링 등 공통 기능을 중앙에서 관리할 수 있어 코드의 중복을 줄이고 유지보수를 용이하게 합니다.
  1. 유연한 확장성
  • 새론운 마이크로서비스를 추가하거나 기존 서비스를 변경할 때, API 게이트웨이의 라우팅 규칙만 수정하면 되어, 클라이언트의 변경 없이 확장이 가능합니다.

Solution

Java와 Spring에서는 Spring Cloud Gateway가 대표적인 API 게이트웨이 솔루션입니다. Spring Cloud Gateway는 다음과 같은 특징을 가지고 있음

  • Spring 통합 : spring boot와 완벽하게 통합되어 설정과 확장이 용이.
  • 필터 기반 아키텍처 : 요청과 응답에 대한 전처리 및 후처리를 필터를 통해 간편하게 구현할 수 있습니다.
  • 라우팅 : 간단한 설정으로 다양한 라우팅 규칙을 정의할 수 있습니다.
  • 부하 분산 : Spring Cloud LoadBalancer와 연동하여 로드 밸런싱을 손쉽게 구현할 수 있습니다

Example

// build.gradle에 의존성 추가
dependencies {
    implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
}

// application.yml 설정
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://USER-SERVICE
          predicates:
            - Path=/users/** 
          filters:
            - StripPrefix=1
        - id: order-service
          uri: lb://ORDER-SERVICE
          predicates:
            - Path=/orders/**
          filters:
            - StripPrefix=1

위 설정은 /users/ 경로의 요청을 USER-SERVICE로, /orders/ 경로의 요청을 ORDER-SERVICE로 라우팅합니다. lb://는 로드 밸런싱을 의미하며, StripPrefix 필터는 경로의 첫 번째 부분을 제거하여 마이크로서비스에 전달합니다.


취업 준비를 위해 실습을 통해 API 게이트웨이를 이해하고 경험을 쌓는 것은 매우 효과적입니다. Java와 Spring을 사용하여 API 게이트웨이를 구현하는 실습을 단계별로 안내해 드리겠습니다. 이 실습을 통해 API 게이트웨이의 주요 기능을 직접 구현하고, 마이크로서비스 아키텍처에서의 역할을 체험할 수 있습니다.

실습 목표

  • 마이크로서비스 아키텍처 이해 및 구현
  • Spring Cloud Gateway를 사용한 API 게이트웨이 설정
  • 요청 라우팅, 인증 및 권한 부여, 로드 밸런싱 등 API 게이트웨이의 주요 기능 구현
  • Docker를 사용한 서비스 컨테이너화 및 Docker Compose로 서비스 오케스트레이션

필요한 도구 및 기술

  • Java 17 이상
  • Spring Boot
  • Spring Cloud Gateway
  • Spring Security (JWT 인증)
  • Maven 또는 Gradle
  • Docker (선택 사항, 추천)
  • Postman 또는 cURL (API 테스트 도구)

1. 프로젝트 구조 설계

실습을 위해 간단한 마이크로서비스 아키텍처를 설계합니다. 예를 들어, 두 개의 마이크로서비스와 하나의 API 게이트웨이를 구축합니다.

  • API Gateway: 모든 클라이언트 요청을 수신하고 라우팅, 인증 등을 담당
  • User Service: 사용자 관련 기능 제공
  • Order Service: 주문 관련 기능 제공
project-root/
├── api-gateway/
├── user-service/
└── order-service/

2. 마이크로서비스 구현

각 마이크로서비스를 Spring Boot로 구현합니다.

2.1 User Service

  1. 프로젝트 생성

    • Spring Initializr에서 Spring Boot 프로젝트를 생성합니다.
    • 의존성: Spring Web, Spring Data JPA, H2 Database, Spring Security
  2. 엔티티 및 리포지토리 설정

    // User.java
    package com.example.userservice.model;
    
    import javax.persistence.*;
    
    @Entity
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String username;
        private String password;
        private String role;
    
        // Getters and Setters
    }
    // UserRepository.java
    package com.example.userservice.repository;
    
    import com.example.userservice.model.User;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface UserRepository extends JpaRepository<User, Long> {
        User findByUsername(String username);
    }
  3. 컨트롤러 구현

    // UserController.java
    package com.example.userservice.controller;
    
    import com.example.userservice.model.User;
    import com.example.userservice.repository.UserRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.List;
    
    @RestController
    @RequestMapping("/users")
    public class UserController {
    
        @Autowired
        private UserRepository userRepository;
    
        @GetMapping
        public List<User> getAllUsers() {
            return userRepository.findAll();
        }
    
        @PostMapping
        public User createUser(@RequestBody User user) {
            return userRepository.save(user);
        }
    }
  4. 애플리케이션 설정 (application.yml)

    spring:
      datasource:
        url: jdbc:h2:mem:testdb
        driverClassName: org.h2.Driver
        username: sa
        password:
      h2:
        console:
          enabled: true
      jpa:
        hibernate:
          ddl-auto: update
    server:
      port: 8081

2.2 Order Service

User Service와 유사하게 Order Service를 구현합니다.

  1. 프로젝트 생성

    • Spring Initializr에서 Spring Boot 프로젝트 생성
    • 의존성: Spring Web, Spring Data JPA, H2 Database, Spring Security
  2. 엔티티 및 리포지토리 설정

    // Order.java
    package com.example.orderservice.model;
    
    import javax.persistence.*;
    
    @Entity
    public class Order {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String product;
        private Integer quantity;
    
        // Getters and Setters
    }
    // OrderRepository.java
    package com.example.orderservice.repository;
    
    import com.example.orderservice.model.Order;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface OrderRepository extends JpaRepository<Order, Long> {
    }
  3. 컨트롤러 구현

    // OrderController.java
    package com.example.orderservice.controller;
    
    import com.example.orderservice.model.Order;
    import com.example.orderservice.repository.OrderRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.List;
    
    @RestController
    @RequestMapping("/orders")
    public class OrderController {
    
        @Autowired
        private OrderRepository orderRepository;
    
        @GetMapping
        public List<Order> getAllOrders() {
            return orderRepository.findAll();
        }
    
        @PostMapping
        public Order createOrder(@RequestBody Order order) {
            return orderRepository.save(order);
        }
    }
  4. 애플리케이션 설정 (application.yml)

    spring:
      datasource:
        url: jdbc:h2:mem:testdb
        driverClassName: org.h2.Driver
        username: sa
        password:
      h2:
        console:
          enabled: true
      jpa:
        hibernate:
          ddl-auto: update
    server:
      port: 8082

3. API Gateway 구현

Spring Cloud Gateway를 사용하여 API 게이트웨이를 구현합니다.

  1. 프로젝트 생성

    • Spring Initializr에서 Spring Boot 프로젝트 생성
    • 의존성: Spring Cloud Gateway, Spring Boot Actuator, Spring Security
  2. 의존성 추가 (build.gradle)

    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-webflux'
        implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
        implementation 'org.springframework.boot:spring-boot-starter-security'
        implementation 'io.jsonwebtoken:jjwt:0.9.1'
        // 기타 필요한 의존성
    }
    
    dependencyManagement {
        imports {
            mavenBom "org.springframework.cloud:spring-cloud-dependencies:2023.0.1"
        }
    }
  3. 애플리케이션 설정 (application.yml)

    spring:
      application:
        name: api-gateway
      cloud:
        gateway:
          routes:
            - id: user-service
              uri: http://localhost:8081
              predicates:
                - Path=/users/**
              filters:
                - StripPrefix=1
            - id: order-service
              uri: http://localhost:8082
              predicates:
                - Path=/orders/**
              filters:
                - StripPrefix=1
    server:
      port: 8080
  4. 보안 설정 (SecurityConfig.java)
    API 게이트웨이에서 JWT 인증을 처리하기 위해 Spring Security를 설정합니다.

    package com.example.apigateway.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.web.server.ServerHttpSecurity;
    import org.springframework.security.web.server.SecurityWebFilterChain;
    
    @Configuration
    public class SecurityConfig {
    
        @Bean
        public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
            return http
                    .csrf().disable()
                    .authorizeExchange()
                    .pathMatchers("/auth/**").permitAll()
                    .anyExchange().authenticated()
                    .and()
                    .oauth2ResourceServer().jwt()
                    .and().and().build();
        }
    }
  5. JWT 인증 필터 구현 (JwtAuthenticationFilter.java)
    JWT 토큰을 검증하고 사용자 정보를 설정하는 필터를 구현합니다.

    package com.example.apigateway.filter;
    
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jwts;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import org.springframework.web.server.WebFilter;
    import org.springframework.web.server.WebFilterChain;
    import reactor.core.publisher.Mono;
    
    @Component
    public class JwtAuthenticationFilter implements WebFilter {
    
        @Value("${jwt.secret}")
        private String secretKey;
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
            ServerHttpRequest request = exchange.getRequest();
            ServerHttpResponse response = exchange.getResponse();
    
            if (request.getURI().getPath().startsWith("/auth")) {
                return chain.filter(exchange);
            }
    
            String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
            if (authHeader == null || !authHeader.startsWith("Bearer ")) {
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return response.setComplete();
            }
    
            String token = authHeader.substring(7);
            try {
                Claims claims = Jwts.parser()
                        .setSigningKey(secretKey.getBytes())
                        .parseClaimsJws(token)
                        .getBody();
                // 사용자 정보 설정 등 추가 처리 가능
            } catch (Exception e) {
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return response.setComplete();
            }
    
            return chain.filter(exchange);
        }
    }
  6. JWT 생성 컨트롤러 (AuthController.java)
    테스트를 위해 간단히 JWT 토큰을 생성하는 엔드포인트를 추가합니다.

    package com.example.apigateway.controller;
    
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.Date;
    
    @RestController
    @RequestMapping("/auth")
    public class AuthController {
    
        @Value("${jwt.secret}")
        private String secretKey;
    
        @PostMapping("/token")
        public String generateToken(@RequestParam String username) {
            return Jwts.builder()
                    .setSubject(username)
                    .setIssuedAt(new Date())
                    .setExpiration(new Date(System.currentTimeMillis() + 86400000)) // 1일 유효
                    .signWith(SignatureAlgorithm.HS256, secretKey.getBytes())
                    .compact();
        }
    }
  7. JWT 비밀 키 설정 (application.yml)

    jwt:
      secret: mysecretkey1234567890

4. 서비스 실행 및 테스트

  1. 각 서비스 실행

    • User Service: 8081 포트
    • Order Service: 8082 포트
    • API Gateway: 8080 포트
  2. JWT 토큰 생성

    • POST http://localhost:8080/auth/token?username=testuser
    • 응답으로 JWT 토큰을 받습니다.
  3. API 요청 테스트

    • 유저 조회
      curl -H "Authorization: Bearer <JWT_TOKEN>" http://localhost:8080/users
    • 주문 생성
      curl -X POST -H "Authorization: Bearer <JWT_TOKEN>" -H "Content-Type: application/json" -d '{"product":"Laptop","quantity":2}' http://localhost:8080/orders

5. 추가 기능 구현

5.1 요청 라우팅 및 필터링 강화

  • Path Parameter 기반 라우팅
  • 헤더 추가/변경 필터 구현
  • 리퀘스트/리스폰스 변환 필터 구현

5.2 API 집계

  • 클라이언트 요청 시 여러 마이크로서비스의 데이터를 하나의 응답으로 합치는 기능 구현
  • Spring Cloud GatewayAggregate 필터 사용 또는 커스텀 구현

5.3 로드 밸런싱

  • 여러 인스턴스의 User Service 및 Order Service를 실행하고, API Gateway에서 라운드 로빈 방식으로 로드 밸런싱 구현
  • Spring Cloud LoadBalancer 설정

5.4 캐싱

  • 자주 요청되는 데이터를 API Gateway에서 캐싱하여 응답 속도 향상
  • Spring Cloud Gateway의 캐싱 필터 설정

5.5 모니터링 및 로깅

  • Spring Boot Actuator를 사용하여 서비스 상태 모니터링
  • ELK Stack (Elasticsearch, Logstash, Kibana) 또는 Prometheus & Grafana를 사용한 로그 및 메트릭 시각화

6. Docker를 사용한 서비스 컨테이너화 (선택 사항)

각 서비스를 Docker 이미지로 빌드하고, Docker Compose를 사용하여 서비스를 오케스트레이션합니다.

  1. 각 서비스에 Dockerfile 추가

    # 예: api-gateway/Dockerfile
    FROM openjdk:17-jdk-alpine
    VOLUME /tmp
    COPY target/api-gateway.jar app.jar
    ENTRYPOINT ["java","-jar","/app.jar"]
  2. Docker Compose 파일 작성 (docker-compose.yml)

    version: '3.8'
    services:
      api-gateway:
        build: ./api-gateway
        ports:
          - "8080:8080"
        depends_on:
          - user-service
          - order-service
    
      user-service:
        build: ./user-service
        ports:
          - "8081:8081"
    
      order-service:
        build: ./order-service
        ports:
          - "8082:8082"
  3. Docker Compose 실행

    docker-compose up --build

7. 실습을 통해 배우는 점

  • API 게이트웨이의 역할: 모든 클라이언트 요청을 중앙에서 관리하고, 라우팅, 인증, 로드 밸런싱 등을 처리
  • Spring Cloud Gateway 사용법: 라우팅 규칙 설정, 필터링, 보안 설정 등
  • 마이크로서비스 간 통신: 각 서비스의 독립적인 배포와 운영
  • 보안 구현: JWT 인증을 통해 API 접근 제어
  • 컨테이너화 및 오케스트레이션: Docker와 Docker Compose를 사용한 서비스 관리

추가 팁

  • 문서화: 각 서비스와 API 게이트웨이의 기능을 문서화하여 이해도를 높이세요.
  • 테스트 작성: 유닛 테스트와 통합 테스트를 작성하여 코드의 안정성을 확보하세요.
  • 버전 관리: Git을 사용하여 프로젝트의 변경 사항을 체계적으로 관리하세요.
  • 협업 도구 사용: GitHub, GitLab 등 협업 플랫폼을 활용하여 포트폴리오를 구축하세요.

참고 자료

마무리

이 실습을 통해 API 게이트웨이의 개념과 구현 방법을 체험할 수 있습니다. 실제 프로젝트에서 API 게이트웨이를 적용하는 경험은 백엔드 개발자로서의 역량을 크게 향상시킬 것입니다. 실습을 진행하면서 발생하는 문제나 추가적인 질문이 있다면 언제든지 문의해 주세요. 성공적인 취업 준비를 응원합니다!

0개의 댓글