이전 글인 Spring Boot 외부 API 통신을 참조해주세요.
이전 글의 경우 Spring Boot의 외부 API로 Controller 상에 test 메소드를 만들어서 테스트를 진행했었다.
이번 글에서는 Flask로 만든 API에 HTTP 방식의 통신을 실시했다.
Server
springboot 버전 2.6.7
JAVA 11
port 8080
외부 API
Flask
python 3.9
port 5000
문서 요약 기능을 python으로 개발해야하는 관계로 외부 API로 선택된 Flask를 생성해보자.
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def home():
return "home"
if __name__ == '__name__':
app.run(debug=True)
Flask로 만들 수 있는 가장 기본적인 서버를 만들었다. 이후 abstractive의 이름으로 문서 요약을 할 수 있는 API를 만들 것 이다.
Flask를 생성하면 기본적으로 localhost:5000으로 접속할 수 있다.
Flask 문서 요약 API인 'abstractive()'는 아래와 같은 방식으로 동작한다.
import json
import os
@app.route('/abstractive', methods=['POST'])
def abstractive():
# 스프링에서 넘어온 json 데이터를 변수에 저장합니다.
dto_json = request.get_json()
# data폴더 내에 모든 파일을 갖는 리스트를 만듭니다.
file_list = os.listdir("./data")
# 그 중 가장 높은 번호를 갖는 파일의 숫자를 가져온 후 해당 숫자에 +1한 값으로 json 파일 이름을 지정해줍니다.
max_file_num = max(file_list)[:-5]
next_file_name = str(int(max_file_num) + 1) + ".json"
path = "data/" + next_file_name
# 'w+' : 파일이 없을 경우 생성
# json파일을 생성하고 json 데이터 값을 json 문자열로 변환한 값을 저장합니다.
with open(path, 'w+') as f:
json.dump(dto_json, f, indent=4)
# --------------------------
# 해당 위치에 3번의 과정이 옵니다.
# --------------------------
# 요약이 완료된 json파일을 다시 json 문자열로 만들어 스프링으로 반환해줍니다.
with open("response/response.json", "r", encoding="utf-8") as f:
json_data = json.load(f)
response = json.dumps(json_data, ensure_ascii=False)
return response
아직 문서 요약 로직을 받지 못했기 때문에 임의로 고정된 json 파일을 반환해주는 프로그램을 작성했다.
파이썬에서는 json 데이터를 처리하기 위해서 json 모듈을 사용하고 있다.
이런 json 모듈에서 사용되는 메소드와 json을 변환하는 방법에 대해 알아보자.
import json
json_string = '''{
"id": 1,
"article": "test"
}'''
json_object = json.loads(json_string)
import json
json_object = {
"id": 1,
"article": "test"
}
json_string = json.dumps(json_object)
print(json_string) # {"id": 1, "article": "test"}
data.json
{
"id": 1,
"article": "test"
}
import json
with open('data.json') as f:
json_object = json.load(f)
import json
json_object = {
"id": 1,
"article": "test"
}
with open('data.json', 'w') as f:
json_string = json.dump(json_object, f, indent=2)
data.json
{
"id": 1,
"article": "test"
}
기존에 작성했던 URI의 경우 본인 서버에 존재하는 TestController에 요청을 했기 때문에 Flask로 외부 서버를 변경해준 만큼 URI를 변경해줄 필요가 있다.
// RestTemplate의 exchange 메소드를 통해 URL에 HttpEntity와 함께 요청
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.exchange("http://localhost:8080/test", HttpMethod.POST,
entity, String.class);
// RestTemplate의 exchange 메소드를 통해 URL에 HttpEntity와 함께 요청
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.exchange("http://localhost:5000/abstractive", HttpMethod.POST,
entity, String.class);
Intellij에서 발생한 에러로,
Intellij를 사용하기 위해서 접속하는 중에 필요 없는 폴더를 삭제했는데 이 과정중에 해당 에러가 발생했다.
찾아본 결과 import할 대상을 찾지 못해서 발생하는 에러로 여러가지 해결방법이 있다.
1. 프로젝트 빌드를 다시 한다. - Gradle Build
2. Intellij 캐시를 비우고 재실행한다. - File > Invalidate Cache...
이 중 두번 째 방법으로 해당 문제를 해결했다.
json 파일 내에 한글이 존재할 경우 python에서 아래와 같은 에러가 발생했다.
'cp949' codec can't decode byte 0xec in position 40: illegal multibyte sequence
UTF-8로 json파일이 저장되어 있기 때문에 발생한 문제로 python에서는 ANSI로 작성된 파일만 정상적으로 읽어오기 때문에 코드 상에 encoding-"utf-8"을 명시해줌으로서 해결할 수 있다.
with open("response/response.json", "r", encoding="utf-8") as f:
json_data = json.load(f)
response = json.dumps(json_data, ensure_ascii=False)
추가적으로 python 객체를 json 문자열로 변환할 때, 유니코드 16진수로 변환되는 문제가 발생한다면, 아래 옵션을 넣어줌으로서 해결할 수 있다.
ensure_ascii=False
Spring에서는 카멜식을 사용하고 python에서는 언더바를 사용한 변수명 방식을 사용한다. 이와같은 이유로 본 프로젝트에서는 Flask에서 처리하기로한 json 데이터의 키 값을 언더바를 넣은 형태로 설정했는데 스프링의 DTO상에는 카멜방식으로 되어 있어 데이터 응답 과정에서 해당 변수에 값이 제대로 들어가지 않아 nullException이 발생했다.
해당 문제는 @JsonProperty를 이용해 변수명을 변경해서 받을 수 있었다.
@Setter
@Getter
public static class response {
private Long id;
private String abstractive;
private List<Integer> extractive;
@JsonProperty(value = "article_original")
private List<String> articleOriginal;
}
매칭시키고자 하는 변수 명을 @JsonProperty 어노테이션에 넣어주면 넘어오는 데이터 형식에 맞춰 Spring 변수명을 그대로 사용할 수 있다.
[파이썬]json 모듈로 JSON 데이터 다루기 _ DaleSeo
[IntelliJ] Cannot resolve symbol 에러 해결 방법 총정리
[spring] 데이터 변수명 변경하여 전달하기 (@JsonProperty 사용) _ side impact
[python]파이썬: UnicodeDecodeError: 'cp949' codec can't decode byte 0xed in position 75: illegal multibyte sequence
[인코딩] 유니코드 인코딩 처리 (특히 json 입출력 시)