본문 바로가기
Spring | SpringBoot

[SpringBoot] JSON 데이터 DB 파싱 오류 및 해결

by hunbal 2023. 8. 9.
SMALL

오류 발생

KMDB에서 제공하는 오픈 API로 영화 정보를 요청하고 응답 받은 데이터를 DB에 저장하기 위해 DTO 클래스를 만들고 JSON 데이터를 파싱하려고 했는데 테이블에 NULL값이 들어오는 오류가 발생했다.

 

API 응답 데이터의 구조는 아래와 같았고

{
    "Query": "",
    "KMAQuery": "",
    "TotalCount": 1,
    "Data": [
        {
            "CollName": "kmdb_new2",
            "TotalCount": 1,
            "Count": 1,
            "Result": [
                {
                    "DOCID": "F07555",
                    "movieId": "F",
                    "movieSeq": "07555",
                    "title": " 에이.아이.",
                    "titleEng": "A.I.",
                    "titleOrg": "Artificial Intelligence: AI",
                    "titleEtc": "에이.아이.^Artificial Intelligence: AI^에이아이, AI^A.I.^A.I.",
                    "prodYear": "2001",
                    "directors": {
                        "director": [
                            {
                                "directorNm": "스티븐 스필버그",
                                "directorEnNm": "Steven Spielberg",
                                "directorId": "00048320"
                            }
                        ]
                    },
                    "actors": {
                        "actor": [
                            {
                                "actorNm": "할리 조엘 오스멘트",
                                "actorEnNm": "Haley Joel Osment",
                                "actorId": "00049115"
                            },
                            {
                                "actorNm": "주드 로",
                                "actorEnNm": "Jude Law",
                                "actorId": "00049631"
                            },
                            {
                                "actorNm": "프란시스 오코너",
                                "actorEnNm": "Frances O'Connor",
                                "actorId": "00071530"
                            },
                            {
                                "actorNm": "브렌단 글리슨",
                                "actorEnNm": "Brendan Gleeson",
                                "actorId": "00059320"
                            },
                            {
                                "actorNm": "샘 로바즈",
                                "actorEnNm": "Sam Robards",
                                "actorId": "00114491"
                            },
                            {
                                "actorNm": "윌리엄 허트",
                                "actorEnNm": "William Hurt",
                                "actorId": "00048563"
                            },
                            {
                                "actorNm": "제이크 토마스",
                                "actorEnNm": "Jake Thomas",
                                "actorId": "00085794"
                            },
                            {
                                "actorNm": "켄 렁",
                                "actorEnNm": "Ken Leung",
                                "actorId": "00093910"
                            },
                            {
                                "actorNm": "마이클 맨텔",
                                "actorEnNm": "Michael Mantell",
                                "actorId": "00102664"
                            },
                            {
                                "actorNm": "마이클 베레시",
                                "actorEnNm": "Michael Berresse",
                                "actorId": "00102147"
                            },
                            {
                                "actorNm": "캐서린 모리스",
                                "actorEnNm": "Kathryn Morris",
                                "actorId": "00093314"
                            }
                        ]
                    },
                    "nation": "미국",
                    "company": "Ambin Entertainment,Dream Works SKG,Stanley Kubric",
                    "plots": {
                        "plot": [
                            {
                                "plotLang": "한국어",
                                "plotText": "지구상의 천연자원이 고갈되고 과학문명은 천문학적 속도로 발전되어 가는 미래의 어느 날, 모든 생활이 감시되는 그 세계에서 인간들은 인공지능을 가진 인조인간들의 봉사를 받으며 살아간다. 정원가꾸기, 집안 일 등 로봇이 인간을 위해 해줄수 있는 일은 무한하다. 단 한가지 사랑만 빼고... 로봇에게 감정을 주입시키는 것은 로봇공학 발전의 마지막 관문이자, 논란의 쟁점이기도 했다. 인간들은 로봇을 정교한 가재도구로 여길 뿐, 그 이상의 것을 용납하지 않았다. 그러나 많은 부부가 자식을 갖을 수 없게 되면서 인간들은 로봇에게서 가재 도구 이상의 가치를 찾게 되고 소비자들의 그러한 욕구를 충족시키기 위해 CYBERTRONICS MAUFACTURING 이라는 로봇회사에선 감정을 가진 최초의 인조인간, 데이빗을 만들어낸다. 데이빗은 CYBERTRONICS사의 한 직원인 헨리 스윈튼의 집에 실험적 케이스로 입양된다. 그 집의 친 아들은 불치의 병에 걸려 치료약이 개발될 때 까지 냉동되어 있는 상태. 인간을 사랑하게 끔 프로그래밍된 최초의 로봇 소년 데이빗은 점차 그들 부부의 아들 노릇에 익숙해져 가지만 여러 가지 예기치 않은 상황이 발생하면서 더 이상 그 생활을 계속 할수 없게 된다. 결국, 인간과 로봇 양쪽 사회에서 배척당하게 된 데이빗은 자신의 유일한 장난감이자 친구이자 보호자인 곰인형 테디만을 데리고 집을 나선다. 자신이 진정 몸담아야 할 곳이 어딘지를 찾아 머나먼 여정길에 오른 그는 새로운 한 세계를 발견하게 된다. 로봇과 기계의 경계선이 너무나 뚜렷하면서도 또한 모호한 그 어떤 신세계를..."
                            }
                        ]
                    },
                    "runtime": "145",
                    "rating": "12세관람가",
                    "genre": "드라마,SF,어드벤처",
                    "kmdbUrl": "https://www.kmdb.or.kr/db/kor/detail/movie/F/07555"
                }
            ]
        }
    ]
}

 

 

DTO 클래스에 데이터를 파싱하는 역할을 하는 MovieServiceImpl 클래스의 메서드는 아래와 같았다.

@Service
@Slf4j
@RequiredArgsConstructor
public class MovieServiceImpl implements MovieService {

	private final MovieDao movieDao;
	
	@Override
	public void saveMoviesFromApi() {
     log.info("영화 api 호출");
     List<String> movieSeqs = Arrays.asList("26312" , "32372" , "56635" , "06697" , "34240" , "10779" , "20520" , "57556" , "03559" , "22274" 
    		 , "09710" , "05440" , "31128" , "02219" , "07555" , "04948" , "05107" , "07998" , "38932" , "13479");

     ObjectMapper objectMapper = new ObjectMapper();
     
     for(String movieSeq : movieSeqs) {
		String apiUrl = "http://api.koreafilm.or.kr/openapi-data2/wisenut/search_api/search_json2.jsp?collection=kmdb_new2&detail=Y&ServiceKey=95B85M4L8264VL3I1IYE&listCount=1&genre=SF&movieSeq=" + movieSeq;
	
		 RestTemplate restTemplate = new RestTemplate();
         ResponseEntity<String> response = restTemplate.getForEntity(apiUrl, String.class);
         String responseBody = response.getBody();
     
    try {
        MovieDto movieDto = objectMapper.readValue(responseBody, MovieDto.class);

        movieDto.setTitle(movieDto.getTitle());
        movieDto.setTitleOrg(movieDto.getTitleOrg());
        movieDto.setProdYear(movieDto.getProdYear());
        movieDto.setDirectors(movieDto.getDirectors());
        movieDto.setActors(movieDto.getActors());
        movieDto.setNation(movieDto.getNation());
        movieDto.setPlots(movieDto.getPlots());
        movieDto.setRuntime(movieDto.getRuntime());
        movieDto.setRating(movieDto.getRating());
        movieDto.setRepRlsDate(movieDto.getRepRlsDate());
        movieDto.setPosters(movieDto.getPosters());

        movieDao.save(movieDto);

    } catch (IOException e) {
        log.error("Error parsing JSON response for movieSeq: {}", movieSeq, e);
    }
	}
}
}

배열에 영화 정보를 응답하는 고유코드들을 담아서 반복문을 통해 DTO 클래스의 필드에 데이터를 넣게끔 구조를 짰는데

DB 테이블에 자꾸 NULL값이 들어오는 문제가 생겼고, 오류를 해결하기 위해 차근차근 코드를 검토했다.

 

 

 

원인 및 문제 해결

응답 데이터와 내 코드를 다시 살펴보니 내게 필요한 데이터는 Data - Result 내부에 들어있는데, 나는 JSON 데이터와 DTO 클래스 내부 필드를 바로 연결지으려 해서  NULL 값만 받아오는 것을 알게 되었다.

ResponseEntity로 받아온 API를 JsonNode로 변환한 후, Data - Result 내부에 들어있는 필요한 필드들만 추출하여 MovieDto 객체로 변환하여 응답 데이터 구조와 연결짓는 방법으로 문제를 해결할 수 있었다.

 

 

 

아래는 문제를 발견한 후 새롭게 수정한 코드.

 private final MovieDao movieDao;
    private final ObjectMapper objectMapper;

    @Override
    public void saveMoviesFromApi() {
        log.info("영화 API 호출");
        List<String> movieSeqs = Arrays.asList("26312", "32372", "56635", "06697", "34240", "10779", "20520", "57556", "03559", "22274",
                "09710", "05440", "31128", "02219", "07555", "04948", "05107", "07998", "38932", "13479");

        RestTemplate restTemplate = new RestTemplate();

        for (String movieSeq : movieSeqs) {
            String apiUrl = "http://api.koreafilm.or.kr/openapi-data2/wisenut/search_api/search_json2.jsp?collection=kmdb_new2&detail=Y&ServiceKey=95B85M4L8264VL3I1IYE&listCount=1&genre=SF&movieSeq=" + movieSeq;

            ResponseEntity<String> response = restTemplate.getForEntity(apiUrl, String.class);
            String responseBody = response.getBody();

            try {
                // JSON 데이터를 파싱
                JsonNode rootNode = objectMapper.readTree(responseBody);
                JsonNode dataNode = rootNode.get("Data").get(0);
                JsonNode resultNode = dataNode.get("Result").get(0);

                MovieDto movieDto = objectMapper.treeToValue(resultNode, MovieDto.class);

                //DB에 값 저장
                movieDao.save(movieDto);

            } catch (IOException e) {
                log.error("movieSeq: {}에 대한 JSON 응답 파싱 중 오류 발생", movieSeq, e);
            }
        }
    }
}

 

 

코드를 수정한 후 API를 호출했더니 정상적으로 테이블에 데이터가 들어오는 것을 확인할 수 있었다! 👍

 

 

 

 

 

'Spring | SpringBoot' 카테고리의 다른 글

[SpringBoot] 어노테이션 (Annotation)  (0) 2023.03.14

댓글