외부 API 통신 _ 문서요약프로젝트

이전 글인 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

Flask 서버

문서 요약 기능을 python으로 개발해야하는 관계로 외부 API로 선택된 Flask를 생성해보자.

Flask 서버 생성

  • 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으로 접속할 수 있다.

문서 요약 API

Flask 문서 요약 API인 'abstractive()'는 아래와 같은 방식으로 동작한다.

  1. 스프링으로부터 json 데이터를 받는다.
  2. 받은 json 데이터를 json 파일로 저장합니다.
  3. 일련의 요약 과정을 거친 후 요약 내용이 장석된 json 파일을 저장합니다.
  4. 요약이 완료된 json파일을 json 문자열로 변환 후 스프링으로 반환해줍니다.
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 모듈에서 사용되는 메소드와 json을 변환하는 방법에 대해 알아보자.

  • loads() : json 문자열을 Python 객체로 변환
    json 문자열을 python의 객체로 변환해준다.
import json

json_string = '''{
	"id": 1,
    "article": "test"
}'''

json_object = json.loads(json_string)
  • dumps() : python 객체를 json 문자열로 변환
    python 객체를 json 문자열로 변환해준다.
import json

json_object = {
	"id": 1,
    "article": "test"
}

json_string = json.dumps(json_object)
print(json_string) # {"id": 1, "article": "test"}
  • load() : json 파일을 python 객체로 불러온다.
    json 파일에 저장된 데이터를 읽어서 python 객체로 불러오고 싶은 경우 loads()가 아니라 load()를 사용한다.
data.json
{
  "id": 1,
  "article": "test"
}
import json

with open('data.json') as f:
	json_object = json.load(f)
  • dump() : python 객체를 json 파일에 저장
    python 객체를 json 문자로 변환한 결과를 파일에 바로 쓰고싶은 경우에 dumps() 대신에 dump()를 쓴다.
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 수정

기존에 작성했던 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);

트러블 슈팅

cannot resolve symbol 에러

Intellij에서 발생한 에러로,
Intellij를 사용하기 위해서 접속하는 중에 필요 없는 폴더를 삭제했는데 이 과정중에 해당 에러가 발생했다.
찾아본 결과 import할 대상을 찾지 못해서 발생하는 에러로 여러가지 해결방법이 있다.

1. 프로젝트 빌드를 다시 한다. - Gradle Build
2. Intellij 캐시를 비우고 재실행한다. - File > Invalidate Cache...

이 중 두번 째 방법으로 해당 문제를 해결했다.

python json 파일 한글 처리

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 입출력 시)

profile
잘 부탁드려요

0개의 댓글