요구 사항
클라이언트가 서버에 이미지 생성을 요청하면, 서버는 이미지 생성 모델을 배포하고 있는 서버에 요청을 전달한다.
15초 가량의 시간이 지난 후, 서버는 모델 배포 서버로부터 이미지를 받아 이런저런 처리를 하고 클라이언트에 생성 완료를 알려주는 서비스를 구현해야 한다.
위 프로세스가 진행되는 동시에 해당 클라이언트가 앱의 다른 기능을 계속 사용할 수 있도록, 서버에서 다른 요청들을 처리할 수 있도록 구현해야 한다.
우리는 클라이언트 알람으로 이미 FCM을 사용하고 있었기 때문에 클라이언트 측에선 비동기로 API를 호출하지 않고 일단 성공 응답을 받은 후 서버에서만 이미지 생성을 비동기적으로 처리하여 알람을 보내주는 방식으로 구현하였다.
Webflux
Spring boot에선 Webflux라는 모듈에 있는 Webclient로 다른 서버에 비동기 요청을 보낼 수 있다. Webflux 모듈은 Spring MVC를 사용하는 프로젝트에서도 reactive 스타일의 개발을 도와주는 모듈이라고 한다.
(필요한 부분만 참고해보면 non-blocking을 지원해준다는 뜻)
사용
1. 의존성 추가
// Webflux
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-webflux'
2. Webclient 객체 생성
@Configuration
public class WebClientConfig {
@Value("${img.server}")
private String imgServer;
@Bean
public ReactorResourceFactory resourceFactory() {
ReactorResourceFactory factory = new ReactorResourceFactory();
factory.setUseGlobalResources(true);
return factory;
}
@Bean
public WebClient webClient(){
return WebClient.builder()
.baseUrl(imgServer)
.build();
}
}
외부 서버로 요청을 보내기 위해서 사용 중인 서버가 클라이언트로써 HTTP 통신을 보내야 한다. 그리고 그 HTTP 통신은 비동기 처리를 위해 reactive한 성격을 지녀야 하는데, 그것을 가능케 하는 것이 위 코드에서 ReactorResourceFactory를 Bean으로 등록하는 부분이다.
- ReactorResourceFactory :
React netty 자원 사용
- React netty:
Reactor Netty는 네트워크 I/O에 기반한 리액티브 프로그래밍을 제공하는 라이브러리. ClientHttpConnector 인터페이스 를 구현하여 서버에서 클라이언트 역할을 하는 WebClient가 Reactor Netty를 기반으로 한 클라이언트로 동작하게 함
- ClientHttpConnector:
Spring WebFlux에서 ClientHttpConnector는 HTTP 클라이언트의 연결을 추상화하는 인터페이스
즉, 내 서버가 클라이언트로써 외부 서버에 HTTP 통신을 보내야 할 때 ClientHttpConnector를 사용해야 하고, ClientHttpConnector를 reactive하게 사용하기 위해 React netty로 ClientHttpConnector 인터페이스를 구현하고,
ReactorResourceFactory를 사용하여 좀 더 쉽게 Reactor Netty의 자원을 설정할 수 있는 것 같다.
Webclient의 구현체는 알아서 resourcefactory 구현체를 사용하여 webclient를 만들게 된다. 통신을 할 외부 서버 url을 필수로 설정해야 함
3. 구현
@Service
@RequiredArgsConstructor
public class RegisterGrimService {
private final WebClient webClient;
private final FcmService fcmService;
public void generateGrim(Member member, RegisterGrimInfo registerGrimInfo) {
String prompt = registerGrimInfo.getStyle() + registerGrimInfo.getNoun() + registerGrimInfo.getVerb();
Mono<String> resultMono = webClient.post()
.uri("/txt2img")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(Map.of(
"prompt", prompt,
"guidance_scale", 7.5,
"height", 512,
"width", 512,
"num_inference_steps", 50,
"safety_check", true,
"seed", 1
))
.retrieve()
.bodyToMono(String.class);
resultMono.subscribe(
result -> {
System.out.println("Success: " + result);
String imgUrl = result.replace("\"", "");
fcmService.sendGrimGenerationSuccess(register(member, imgUrl), member.getFcmToken());
},
error -> {
System.err.println("Error: " + error.getMessage());
fcmService.sendGrimGenerationFail(member.getFcmToken());
}
);
}
}
webClient로 post 요청을 보낸다. 이미지 생성 서버는 위와 같은 json을 요청 데이터로 받고 있고, 이미지 url을 리턴한다.
리턴받을 url이 Mono<String>으로 받아지는데, Mono 타입으로 받음으로써 외부 서버의 응답을 비동기적으로 처리할 수 있게 된다. 이미지 생성이 완료 되면 resultMono에 데이터가 담기고 이후 subscribe() 안에 있는 코드가 실행된다.
마지막으로 FCM을 통해 성공여부에 따라 각 메세지를 클라이언트에게 전달함으로써 클라이언트도 비동기적으로 해당 기능을 사용할 수 있게 된다!
'develop > Backend -Java' 카테고리의 다른 글
[spring] 에러 코드 관리 (0) | 2022.11.25 |
---|---|
스프링 - jwt (0) | 2022.11.20 |
jwt (json wep token) (0) | 2022.11.20 |
Java(eclips)에서 DB(postgresql) 서버로 - JDBC (0) | 2022.04.04 |