오랜만에 포스팅을 한다. 새해도 거의 보름이 지나가는데 요즘은 포스팅이 뜸했다. 다시 블로그를 열심히 해야 겠다. 물론 될지는 모르겠지만.. 어쨋든 오늘은 Spring에서 지원해주는 web 비동기 기술을 몇가지 살펴보도록 하자.

오늘은 이런 것들이 있다는 것만 알고 넘어가자. 추후에 좀 더 상세하게 살펴볼 수 있으면 그때 살펴보도록 하자. 너무 처음부터 깊게 파고 들면 어려우니.. 이 기술은 요즘 나오는 reactive streams 과 많이 비슷하므로 이 기술 먼저 알고 가면 좋을 듯하다.

실제 이 기술(오늘 말할려고 하는)은 최신 기술이 아니다. 정확히 말하면 대략 5년전 그러니까 spring3.2가 발표되면서 이 기술을 선보였다. 뭐 그건 그렇고 한번 살펴보도록 하자.

Callable

이것은 java 1.5부터 가능한 Callable 인터페이스다. Callable 인터페이스는 추상 메서드가 하나 있는 FunctionalInterface 이다. 해당 타입을 리턴하면 Spring에서 적절하게 리턴값을 만들어 준다.

@GetMapping("/callable")
public Callable<List<Person>> persons(){
  return () -> {
    return generatorPersons.getPersons();
  };
}

이것의 단점은 아무 설정을 할 수 없다. 타임아웃이라던지 어떤 스레드풀을 사용할지 결정할 수 없다.

CompletableFuture

이것은 java 1.8부터 가능한 CompletableFuture 클래스이다. 해당 타입도 마찬가지로 mvc에서 리턴 타입으로 정의하면 Spring에서 적절하게 리턴해준다.

@GetMapping("/future")
public CompletableFuture<List<Person>> future() {
  return CompletableFuture.supplyAsync(() -> generatorPersons.getPersons());
}

supplyAsync 경우에는 Supplier를 파라미터로 받고 있다. 그래서 위와 같이 적절하게 람다를 사용해서 좀 더 나은 코드를 만들 수 있다. CompletableFuture.supplyAsync 경우에는 Executor를 추가로 파라미터로 받고 있다. 그래서 좀 더 나은 설정을 할 수 있다.


@GetMapping("/future") public CompletableFuture<List<Person>> future() { return CompletableFuture.supplyAsync(() -> generatorPersons.getPersons(), this.asyncExecutor); }

ListenableFuture

ListenableFuture는 spring 4.0에 추가된 인터페이스이다. 4.0 이전 버전에서는 사용할 수 없으니 참고하면 되겠다. 실제로 AsyncRestTemplate의 리턴타입은 ListenableFuture로 정의 되어있다. 비동기 적으로 특정한 API를 호출할 때 유용한 AsyncRestTemplate은 리턴 타입 그대로 spring mvc에게 넘겨줘도 된다.

@GetMapping("/async")
public ListenableFuture<ResponseEntity<List<Person>>> persons() {
  return asyncRestTemplate.exchange("http://localhost:8081/persons",
      HttpMethod.GET,
      null,
      new ParameterizedTypeReference<List<Person>>() {});
}

API 통신후에 적절히 사용한다면 매우 유용한 인터페이스이다. 다음 시간이나 혹은 추후에 좀 더 자세히 이야기 해보도록 하고 일단 이정도만 알고 있자.

WebAsyncTask

WebAsyncTask 은 Spring에서 제공해주는 클래스이다. spring 3.2 부터 제공되고 있으니 참고하면 되겠다. 이 클래스는 Callable 인터페어스보다 좀 더 나은 설정을 갖고 있다. 실제로 WebAsyncTask 클래스 안에는 Callable을 사용하고 있으며 타임아웃 설정, executor 등을 설정 할 수 있다.

@GetMapping("/webAsyncTask")
public WebAsyncTask<List<Person>> webAsyncTaskPerson() {
  return new WebAsyncTask<>(() -> generatorPersons.getPersons());
}

위는 기본적인 사용법이다.

@GetMapping("/webAsyncTask")
public WebAsyncTask<List<Person>> webAsyncTaskPerson1() {
  return new WebAsyncTask<>(1000L, this.executor, () -> generatorPersons.getPersons());
}

타임아웃 설정과 Executor를 설정 할 수 있다. Callable 보다 설정할게 좀 더 있으니 Callable 보다는 WebAsyncTask를 사용하길 권장한다.

DeferredResult

DeferredResult 클래스 역시 spring 3.2부터 사용가능 하다. 이건 조금 특이하다. 이건 전혀 다른 스레드에서도 사용가능하다.(물론 위의 것도 전혀 다른 스레드이긴하지만..) 아무튼 설명하기 좀 힘드니 소스부터 보자..

private final Queue<DeferredResult<List<Person>>> personsQueue = new ConcurrentLinkedQueue<>();

@GetMapping("/deferred")
public DeferredResult<List<Person>> persons() {
  DeferredResult<List<Person>> result = new DeferredResult<>();
  personsQueue.add(result);
  return result;
}

@Scheduled(fixedRate = 2000)
public void processQueues() {
  for (DeferredResult<List<Person>> result : this.personsQueue) {
    result.setResult(generatorPersons.getPersons());
    this.personsQueue.remove(result);
  }
}

음 먼저 DeferredResult를 생성해서 큐에 담고 바로 리턴하면 된다. 그리고 나서 전혀 다른스레드에서 setResult를 호출하면 그때 뷰에 전달된다. 아주 특이한 아이이다. 적절하게 잘 쓰면 좋은 클래스인듯 하다.

일단 오늘은 어떤 클래스 혹은 인터페이스들이 Spring mvc가 지원해주는지 알아봤다. 이외에도 몇가지 더 있긴한데 그것까지는 알아보지 않았다. 나중에는 이것저것 테스트도 해보고 어떤 의미를 갖고 있는지도 한번 살펴보도록 하자.

오늘은 이만!