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 프로젝트 에서 찾을 수 있습니다 .