Java_Spring WebFlux

Minki CHO·2023년 1월 30일
0

CodeStates

목록 보기
36/43

Spring WebFlux

WebFlux
:Reactor의 타입인 Flux가 Webd에서 사용됨
:리액티브한 웹 애플리케이션

Spring WebFlux 애플리케이션 vs Spring MVC 애플리케이션

-기술 스택 비교

1) Netty, Servlet 3.1+ Containers
:Spring WebFlux :Non-Blocking 통신 지원
:Spring MVC :Blocking 통신 방신 사용

2) Reactive Streams Adapters
:Spring WebFlux :Reactive Adapter를 사용해서 Reactor 뿐만 아니라 RxJava 등의 다른 리액티브 라이브러리를 사용할 수 있는 유연함을 제공
:Spring MVC :Servlet API의 스펙에 의존적임

3) Spring Security Reactive
:Spring WebFlux와 Spring MVC 모두 Spring Security 사용
:Spring WebFlux :서블릿 필터 방식이 아닌 WebFilter를 사용해 리액티브 특성에 맞게 인증과 권한 등의 보안을 적용

4) Spring WebFlux
:웹 계층(프리젠테이션 계층, API 계층에서
:Reactive Stack : Spring WebFlux 사용
:Servlet Stack : Spring MVC 사용

5) R2DBC
:Spring WebFlux :완전한 Non-Blocking 통신을 통해 리액티브 스택을 데이터 액세스 계층까지 확장
:R2DBC :관계형 데이터베이스에서 Non-Blocking 통신을 적용하기 위한 표준 사양
(Spring MVC에서 사용하는 JDBC API에서는 Non-Blocking 통신 미지원)

-샘플 코드

:샘플 애플리케이션 요청 흐름

Spring MVC의 Blocking 처리 방식
1-1 Spring MVC 기반 메인 애플리케이션의 Controller(SpringMvcMainCoffeeController) 샘플 코드

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;

@Slf4j
@RestController
@RequestMapping("/v11/coffees")
public class SpringMvcMainCoffeeController {
    private final RestTemplate restTemplate;

    String uri = "http://localhost:7070/v11/coffees/1";

    public SpringMvcMainCoffeeController(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.build();
    }

    @GetMapping("/{coffee-id}")
    public ResponseEntity getCoffee(@PathVariable("coffee-id") long coffeeId) {
        log.info("# call Spring MVC Main Controller: {}", LocalDateTime.now());
        
        // 1)  
        ResponseEntity<CoffeeResponseDto> response = restTemplate.getForEntity(uri, CoffeeResponseDto.class);
        return ResponseEntity.ok(response.getBody());
    }
}

:클라이언트의 커피 정보 요청 전달받는 메인 애플리케이션의 Controller 클래스

1)ResponseEntity<CoffeeResponseDto> response = restTemplate.getForEntity(uri, CoffeeResponseDto.class);
:클라이언트 요청을 메인 애플리케이션에서 직접 처리하는 것이 아닌
Spring의 Rest Client인 RestTemplate을 이용해
외부에 있는 다른 애플리케이션에게 한번 더 요청을 전송

:클라이언트쪽에서 getCoffee() 핸들러 메서드로 요청 전송시
:"# call Spring MVC Main Controller: {요청 수신 시간}" 로그 출력됨

1-2 Spring MVC 기반 외부의 애플리케이션의 Controller(SpringMvcOutboundCoffeeController) 샘플 코드

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/v11/coffees")
public class SpringMvcOutboundCoffeeController {
    @GetMapping("/{coffee-id}")
    public ResponseEntity getCoffee(@PathVariable("coffee-id") long coffeeId) throws InterruptedException {
        CoffeeResponseDto responseDto = new CoffeeResponseDto(coffeeId, "카페라떼", "CafeLattee", 4000);

        // 1)
        Thread.sleep(5000);
        return ResponseEntity.ok(responseDto);
    }
}

:1-1의 메인 애플리케이션 Controller(SpringMvcMainCoffeeController)의 getCoffee() 핸들러 메서드에서 RestTemplate으로 전송한 요청을
전달받는 외부 애플리케이션의 Controller(SpringMvcOutboundCoffeeController) 코드임
:Spring MVC 애플리케이션의 요청 처리 방식을 확인하는 것이 주목적이므로
별도의 데이터베이스 연동없이 단순히 Stub 데이터(responseDto)를 응답으로 넘겨줌

1-3 Spring MVC 기반 메인 애플리케이션을 호출하는 클라이언트 샘플 코드

import com.codestates.coffee.CoffeeResponseDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import java.time.LocalTime;

@Slf4j
@SpringBootApplication
public class SpringMvcMainSampleApplication {

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

	@Bean
	public CommandLineRunner run() {
		return (String... args) -> {
			log.info("# 요청 시작 시간: {}", LocalTime.now());

      // 1)
			for (int i = 1; i <= 5; i++) {
				CoffeeResponseDto response = this.getCoffee();
				log.info("{}: coffee name: {}", LocalTime.now(), response.getKorName());
			}
		};
	}

	private CoffeeResponseDto getCoffee() {
		RestTemplate restTemplate = new RestTemplate();
		String uri = "http://localhost:8080/v11/coffees/1";
		ResponseEntity<CoffeeResponseDto> response = restTemplate.getForEntity(uri, CoffeeResponseDto.class);

		return response.getBody();
	}
}

:메인 애플리케이션 Controller(SpringMvcMainSampleApplication)의 getCoffe() 핸들러 메서드를 호출하는 클라이언트를 구현하는 코드
:프런트엔드 역할

1) for문
:for 문을 이용해 SpringMvcMainCoffeeController의 getCoffee() 핸들러 메서드를 5번 호출함

"애플리케이션 실행 시, 메인 애플리케이션이 호출하는 대상인 외부 애플리케이션을 먼저 실행시킨 후에 메인 애플리케이션을 실행해야 함"

결과

2022-07-27 14:17:16.707  INFO 17484 --- [           main] c.c.SpringMvcMainSampleApplication       : # 요청 시작 시간: 14:17:16.707955700

2022-07-27 14:17:16.833  INFO 17484 --- [nio-8080-exec-1] c.c.c.SpringMvcMainCoffeeController      : # call Spring MVC Main Controller: 2022-07-27T14:17:16.833451500  // 1-1) 첫 번째 요청
2022-07-27 14:17:21.984  INFO 17484 --- [           main] c.c.SpringMvcMainSampleApplication       : 14:17:21.984873800: coffee name: 카페라떼 // 1-2) 첫 번째 요청 처리 결과

2022-07-27 14:17:21.990  INFO 17484 --- [nio-8080-exec-2] c.c.c.SpringMvcMainCoffeeController      : # call Spring MVC Main Controller: 2022-07-27T14:17:21.990018700 // 2-1) 두 번째 요청
2022-07-27 14:17:26.999  INFO 17484 --- [           main] c.c.SpringMvcMainSampleApplication       : 14:17:26.999949600: coffee name: 카페라떼 // 2-2) 두 번째 요청 처리 결과

2022-07-27 14:17:27.003  INFO 17484 --- [nio-8080-exec-3] c.c.c.SpringMvcMainCoffeeController      : # call Spring MVC Main Controller: 2022-07-27T14:17:27.003373200
2022-07-27 14:17:32.010  INFO 17484 --- [           main] c.c.SpringMvcMainSampleApplication       : 14:17:32.010208600: coffee name: 카페라떼

2022-07-27 14:17:32.014  INFO 17484 --- [nio-8080-exec-4] c.c.c.SpringMvcMainCoffeeController      : # call Spring MVC Main Controller: 2022-07-27T14:17:32.014291900
2022-07-27 14:17:37.021  INFO 17484 --- [           main] c.c.SpringMvcMainSampleApplication       : 14:17:37.021770: coffee name: 카페라떼

2022-07-27 14:17:37.026  INFO 17484 --- [nio-8080-exec-5] c.c.c.SpringMvcMainCoffeeController      : # call Spring MVC Main Controller: 2022-07-27T14:17:37.026077900
2022-07-27 14:17:42.033  INFO 17484 --- [           main] c.c.SpringMvcMainSampleApplication       : 14:17:42.033187200: coffee name: 카페라떼
profile
Developer

0개의 댓글