개발자로 후회없는 삶 살기
(작성중) spring PART.로컬 호스트에서 spring 서버와 flask 서버 통신하기 본문
(작성중) spring PART.로컬 호스트에서 spring 서버와 flask 서버 통신하기
몽이장쥰 2023. 6. 16. 21:37서론
현재 진행하고 있는 백엔드 프로젝트에서 spring으로 작성된 어플리케이션 서버와 flask로 작성된 AI 서버를 통신하는 과정을 기록합니다.
본론
- spring to spring 문자열 주고 받기
로컬 호스트에서 서버 간 통신은 처음해보기도 하고 spring과 flask는 이미지를 주고 받을 것이라서 그 전에 spring와 spring을 먼저 연결해 봅니다.
1. 요청과 응답 DTO 객체
// 요청 객체
@Data
public class RequestDTO {
private String body;
public RequestDTO(String body) {
this.body = body;
}
public RequestDTO() {
}
}
// 응답 객체
@Data
public class ResponseDTO {
private String body;
public ResponseDTO(String body) {
this.body = body;
}
public ResponseDTO() {
}
}
spring과 spring은 통신의 기본 원리를 파악하기 위해 먼저 단순 string을 주고 받고 이미지를 주고 받아보겠습니다. 필드로는 문자열 한 개를 준비하였습니다.
2. 컨트롤러
@Controller
public class TestController {
@PostMapping("/test")
public ResponseEntity<ResponseDTO> testApi(@RequestBody RequestDTO requestDTO) {
ResponseDTO responseDTO = new ResponseDTO(requestDTO.getBody());
return new ResponseEntity<>(responseDTO, HttpStatus.OK);
}
}
서버간 통신을 할 때 호출할 컨트롤러를 작성합니다. 서버간 통신을 할 때도 클라이언트가 서버를 호출할 때처럼 Mapping 메서드의 URL을 통해 서로를 호출합니다. 예를들어 이 경우는 localhost:8080/test로 호출하게 됩니다.
3. 요청 비즈니스 로직 작성
@Service
public class TestService {
private final ObjectMapper objectMapper = new ObjectMapper();
public ResponseDTO getAbstractive(RequestDTO requestDTO) throws Exception {
// 1) 헤더 설정
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(new MediaType("application", "json", Charset.forName("UTF-8")));
// 2) Object Mapper를 통한 JSON 바인딩
requestDTO.setBody("test");
String params2 = objectMapper.writeValueAsString(requestDTO);
// 3) HttpEntity에 헤더 및 params 설정
HttpEntity entity = new HttpEntity(params2, httpHeaders);
// 4) RestTemplate의 exchange 메소드를 통해 URL에 HttpEntity와 함께 요청
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.exchange("http://localhost:8080/test", HttpMethod.POST,
entity, String.class);
// 요청 후 응답 확인
System.out.println(responseEntity.getStatusCode());
System.out.println(responseEntity.getBody());
// 5) String to Object
ResponseDTO responseDTO = objectMapper.readValue(
responseEntity.getBody(), ResponseDTO.class
);
return responseDTO;
}
}
RestTemplate를 통해 서버간 통신을 진행합니다.
1) HTTP 요청 헤더 설정
2) ObjectMapper를 통해 요청 DTO를 JSON 형식의 문자열로 변환
3) 변환한 문자열을 HTTP 요청 메세지에 삽입
4) RestTemplate의 exchange 메서드를 통해 요청 및 응답 교환
5) JSON 형식의 문자열 데이터를 DTO에 초기화
ResponseEntity<String> responseEntity = restTemplate.exchange("http://localhost:8080/test", HttpMethod.POST,
entity, String.class);
서버간 통신을 하는 방법도 클라이언트가 서버에게 요청하는 것과 차이가 없고 URL을 호출하여 이뤄집니다. 클라이언트는 페이지를 요청하거나 form을 수행할 때 버튼에 있는 href나 action을 통해 URL을 호출을 하고 서버는 호출할 URL과 함께 HTTPEntity를 전달하여 호출을 합니다. exchange 메서드를 통해 요청을 하면 응답을 반환받습니다.
4. 요청과 응답 수행
이제 서버간 통신을 진행합니다. 로컬 호스트로 서버간 통신을 하는 것은 간단합니다. 작성한 어플리케이션 코드를 인텔리제이를 2개 열고 요청을 받는 서버는 Application.java를 실행하고 요청을 하는 서버는 위에 작성한 요청 비즈니스 로직을 실행하면 됩니다.
@Test
void getAbstractive() throws Exception {
RequestDTO requestDTO = new RequestDTO("test");
ResponseDTO responseDTO = testService.getAbstractive(requestDTO);
log.info("{}", responseDTO);
}
테스트 코드를 작성하고 응답 서버를 실행하지 않은 채로 테스트하면 연결이 실패됐다고 나오는데
응답 서버를 실행하고 테스트 코드를 실행하면 요청 비즈니스 로직에 작성한대로 결과를 로그에 찍습니다.
- spring to spring 이미지 주고 받기(패스)
이제 단순 텍스트를 주고 받는 것은 확인됐으니 이미지를 주고 받아보겠습니다.
- spring to flask 문자열 주고 받기(하는 중)
이제 flask와 spring 간의 통신을 해보겠습니다.
@bp.route('/test', methods = ['POST'])
def test():
dto_json = request.get_json()
print(dto_json)
flask에 api를 만들고
// RestTemplate의 exchange 메소드를 통해 URL에 HttpEntity와 함께 요청
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.exchange("http://localhost:5000/image/test", HttpMethod.POST,
entity, String.class);
스프링에서 RestTemplate으로 요청하면
spring to spring 때처럼 서버간 통신이 됩니다.
- spring to flask 이미지 주고 받기
마지막으로 최종 어플리케이션에 필요한 이미지 주고 받기를 해보겠습니다.
결론
이번 기회로 수업에서 배운 클라이언트와 서버, 서버와 서버 사이에 네트워크 통신이 어떻게 일어나고 메세지를 어떻게 주고 받는지 직접 경험해 볼 수 있었습니다. 네트워크로 통신을 하기 위해서는 소캣에 ip와 포트번호가 클라이언트와 서버에 둘 다 있어서 서로를 확인해야 연결이 되는 것인데 exchange 메서드에서 ip와 port를 인자로 주어 소캣에 저장되고 연결할 수 있는 것입니다.
'[백엔드] > [spring+JPA | 이슈해결]' 카테고리의 다른 글
spring PART.Value Object와 Custom Validator를 이용한 검증 개선 (0) | 2023.07.21 |
---|---|
[Java] Java의 immutable (0) | 2023.07.07 |
spring PART.JPA 사용하지 않고 enum 타입 DB에 저장하기 (0) | 2023.06.14 |
[spring] equals()와 hashCode()를 재정의 해야하는 이유 (0) | 2023.05.03 |
[spring] @NotNull, @NotEmpty, @NotBlank의 차이점 (1) | 2023.04.30 |