1. 개요

이 문서에서는 별도로 배포되는 프런트 엔드 애플리케이션과 REST API 간의 통신을 살펴보겠습니다 .

목표는 브라우저의 CORS 및 Same Origin Policy 제한을 해결하고 UI가 동일한 출처를 공유하지 않더라도 API를 호출할 수 있도록 하는 것입니다.

우리는 기본적으로 UI 애플리케이션과 간단한 REST API라는 두 개의 별도 애플리케이션을 만들고 UI 애플리케이션 에서 Zuul 프록시 를 사용하여 REST API에 대한 프록시 호출을 할 것입니다.

Zuul은 Netflix의 JVM 기반 라우터 및 서버 측 로드 밸런서입니다. 그리고 Spring Cloud는 포함된 Zuul 프록시와 잘 통합되어 있습니다. 여기서 사용할 것입니다.

2. 메이븐 구성

먼저 Spring Cloud의 zuul 지원에 대한 의존성을 UI 애플리케이션의 pom.xml 에 추가해야 합니다 .

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    <version>2.2.0.RELEASE</version>
</dependency>

최신 버전은 여기 에서 찾을 수 있습니다 .

3. 주울 속성

다음 – Zuul을 구성해야 하며 Spring Boot를 사용하고 있으므로 application.yml에서 구성할 것 입니다 .

zuul:
  routes:
    foos:
      path: /foos/**
      url: http://localhost:8081/spring-zuul-foos-resource/foos

참고:

  • 리소스 서버 인 Foos에 프록시하고 있습니다.
  • " /foos/ " 로 시작하는 UI의 모든 요청은 http://loclahost:8081/spring-zuul-foos-resource/foos/ 의 Foos 리소스 서버 로 라우팅됩니다 .

4. API

API 애플리케이션은 간단한 Spring Boot 앱입니다.

이 문서에서는 포트 8081에서 실행되는 서버에 배포된 API를 고려할 것 입니다.

먼저 사용할 리소스에 대한 기본 DTO를 정의해 보겠습니다.

public class Foo {
    private long id;
    private String name;

    // standard getters and setters
}

간단한 컨트롤러:

@RestController
public class FooController {

    @GetMapping("/foos/{id}")
    public Foo findById(
      @PathVariable long id, HttpServletRequest req, HttpServletResponse res) {
        return new Foo(Long.parseLong(randomNumeric(2)), randomAlphabetic(4));
    }
}

5. UI 애플리케이션

우리의 UI 애플리케이션은 간단한 Spring Boot 애플리케이션이기도 합니다.

이 문서에서는 포트 8080에서 실행되는 서버에 배포된 API를 고려할 것 입니다.

약간의 AngularJS를 사용하여 기본 index.html 부터 시작하겠습니다.

<html>
<body ng-app="myApp" ng-controller="mainCtrl">
<script src="angular.min.js"></script>
<script src="angular-resource.min.js"></script>

<script>
var app = angular.module('myApp', ["ngResource"]);

app.controller('mainCtrl', function($scope,$resource,$http) {
    $scope.foo = {id:0 , name:"sample foo"};
    $scope.foos = $resource("/foos/:fooId",{fooId:'@id'});
    
    $scope.getFoo = function(){
        $scope.foo = $scope.foos.get({fooId:$scope.foo.id});
    }  
});
</script>

<div>
    <h1>Foo Details</h1>
    <span>{{foo.id}}</span>
    <span>{{foo.name}}</span>
    <a href="#" ng-click="getFoo()">New Foo</a>
</div>
</body>
</html>

여기서 가장 중요한 측면은 상대 URL을 사용하여 API에 액세스하는 방법입니다!

API 애플리케이션은 UI 애플리케이션과 동일한 서버에 배포되지 않으므로 상대 URL은 작동하지 않으며 프록시 없이는 작동하지 않습니다.

그러나 프록시 를 사용하면 Zuul 프록시를 통해 Foo 리소스에 액세스하고 있으며, 이는 물론 API가 실제로 배포되는 곳으로 이러한 요청을 라우팅하도록 구성됩니다.

마지막으로 실제로 부팅이 가능한 애플리케이션은 다음과 같습니다.

@EnableZuulProxy
@SpringBootApplication
public class UiApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(UiApplication.class, args);
    }
}

간단한 부팅 어노테이션 외에도 Zuul 프록시에 대한 어노테이션의 활성화 스타일도 사용하고 있다는 점에 유의하십시오. 이는 매우 멋지고 깨끗하며 간결합니다.

6. 라우팅 테스트

이제 다음과 같이 UI 애플리케이션을 테스트해 보겠습니다.

@Test
public void whenSendRequestToFooResource_thenOK() {
    Response response = RestAssured.get("http://localhost:8080/foos/1");
 
    assertEquals(200, response.getStatusCode());
}

7. Custom형 Zuul 필터

여러 Zuul 필터를 사용할 수 있으며 자체 사용자 지정 필터를 만들 수도 있습니다.

@Component
public class CustomZuulFilter extends ZuulFilter {

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        ctx.addZuulRequestHeader("Test", "TestSample");
        return null;
    }

    @Override
    public boolean shouldFilter() {
       return true;
    }
    // ...
}

이 간단한 필터는 " Test " 라는 헤더 를 요청에 추가하기만 합니다. 물론 여기에서 요청을 보강하는 데 필요한 만큼 복잡해질 수 있습니다.

8. 사용자 지정 Zuul 필터 테스트

마지막으로 사용자 지정 필터가 작동하는지 테스트해 보겠습니다. 먼저 Foos 리소스 서버에서 FooController 를 수정합니다.

@RestController
public class FooController {

    @GetMapping("/foos/{id}")
    public Foo findById(
      @PathVariable long id, HttpServletRequest req, HttpServletResponse res) {
        if (req.getHeader("Test") != null) {
            res.addHeader("Test", req.getHeader("Test"));
        }
        return new Foo(Long.parseLong(randomNumeric(2)), randomAlphabetic(4));
    }
}

이제 테스트해 보겠습니다.

@Test
public void whenSendRequest_thenHeaderAdded() {
    Response response = RestAssured.get("http://localhost:8080/foos/1");
 
    assertEquals(200, response.getStatusCode());
    assertEquals("TestSample", response.getHeader("Test"));
}

9. 결론

이 글에서는 Zuul을 사용하여 UI 애플리케이션에서 REST API로 요청을 라우팅하는 데 중점을 두었습니다. 우리는 CORS와 동일 출처 정책을 성공적으로 해결했으며 전송 중인 HTTP 요청을 사용자 정의하고 확장했습니다.

이 사용방법(예제)의 전체 구현은 GitHub 프로젝트 에서 찾을 수 있습니다 .

REST footer banner