Loading...

공공 데이터 / / 2022. 1. 25. 17:46

공공 데이터 4강. 파싱(2)

반응형

파싱을 쉽게 도와주는 사이트도 있지만,

처음에는 직접 전송받아올 오브젝트를 만들어보자.

생성자와 getter, setter도 필요하지만

우선 필드 선언부터 해줄것이다.

 

gson이 인식할 때 무조건 타입이 일치해야 하는데

스트링은 어떤 타입도 다 받을 수 있다. 무적임!

어차피 문자열로 날아오니까!!

 

 

 

 

개발계정의 상세보기에 들어가서 스크롤을 내리면 요청 변수라는 것이 있다.

 

요청 변수에 쿼리 스트링의 키 값들과 정보들이 나와있다.

여기 항목에 옵션과 필수 항목이 나누어져 있는 것을 볼 수 있다.

 

* serviceKey는 디폴트 값이라 안 나와있어도 꼭 적어줘야 함! *

 

모든 항공사의 정보를 다 받고 싶으면

airlineId 옵션을 지워주면 된다.

 

빼도 되는지 안되는지 감으로 하는 것이 아니라

문서를 정독해보고 하자!

 

우선 제공하는 데이터의 구조를 보기 위해

샘플데이터를 넣어 미리보기로 확인해보자.

그럼 xml파일로 데이터를 보여줄 것이다.

지난 시간에 배운 것처럼 주소 제일 끝에 &_type=json을 붙여준다.

 

그럼 예쁘게 정리되어 json 표기법으로 보여준다.

 

json 구조를 확인하고 오브젝트를 만들어줄 것이다.

package data00;

import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;

public class ResponseDto {
    private Response response; // 변수명을 key값과 반드시 동일하게

    class Response {
        private Header header; // 변수명을 key값과 반드시 동일하게
        private Body body; // 변수명을 key값과 반드시 동일하게

        class Header {
            private String resultCode;
            private String resultMsg;
        }

        class Body {
            private Items items;
            private int numOfRows;
            private int pageNo;
            private int totalCount;

            class Items {
                private List<Item> item;

                class Item {
                    private String airlineNm;
                    private String arrAirportNm;
                    private String arrPlandTime;
                    private String depAirportNm;
                    private String depPlandTime;
                    private int economyCharge;
                    private int prestigeCharge;
                    private String vihicleId;
                }
            }
        }
    }
}

 

클래스 안에 만들어진 클래스를 내부 클래스라고 한다.

내부 클래스를 만드니 가독성도 좋아지고 코드가 깔끔해졌다.

 

이제 생성자와 getter, setter를 만들어야 하는데

지난 시간에 리플렉션 기법을 배웠다.

 

getter가 있는 라이브러리를 lombok이라고 하는데

이 라이브러리를 Referenced Libraries에 추가해주어야 한다.

우선 다운부터 받아오자!

 

 

 

 

 

 

라이브러리를 추가해주고 

클래스명 위에 @Getter를 적어주자.

그리고 main에서 바로 getter를 호출하면

빨간 줄이 그어지며 오류가 날 것이다.

 

 

Ctrl + Space 매직키로 확인을 해보니

변수 s에는 getName( ) 메서드가 없다.

왜일까?

 

실행을 하지 않았기 때문이다.

 

리플렉션은 main을 실행시키고 런타임 때 어노테이션이 붙은 클래스를 분석하기 때문에

컴파일 시점에 툴에서 막아버리기 때문에 오류가 나는 것이다.

 

실행 전에도 메서드가 뜰 수 있게

플러그인을 설치해줄 것이다.

 

Extensions에서 lombok을 검색하여

Lombok Annotations Support for VS Code를 Install 해준다.

 

 

설치하고 vscode를 껐다 켜면

오류가 잡힐 것이다.

 

 

풀 생성자

@AllArgsConstructor

디폴트 생성자

@NoArgsConstructor

getter

@Getter

setter

@Setter

 

getter, setter를 따로 써주기 귀찮으면

@Data를 써준다.

얘는 getter, setter, toString까지 가지고 있다!

 

package data00;

import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;

@Data
@AllArgsConstructor
public class ResponseDto {
    private Response response; // 변수명을 key값과 반드시 동일하게

    @Data
    @AllArgsConstructor
    class Response {
        private Header header; // 변수명을 key값과 반드시 동일하게
        private Body body; // 변수명을 key값과 반드시 동일하게

        @Data
        @AllArgsConstructor
        class Header {
            private String resultCode;
            private String resultMsg;
        }

        @Data
        @AllArgsConstructor
        class Body {
            private Items items;
            private int numOfRows;
            private int pageNo;
            private int totalCount;

            @Data
            @AllArgsConstructor
            class Items {
                private List<Item> item;

                @Data
                @AllArgsConstructor
                class Item {
                    private String airlineNm;
                    private String arrAirportNm;
                    private String arrPlandTime;
                    private String depAirportNm;
                    private String depPlandTime;
                    private int economyCharge;
                    private int prestigeCharge;
                    private String vihicleId;
                }
            }
        }
    }
}

 

 


 

 

내 컴퓨터와 공공 데이터 포털을 연결해주기 위해서는

주소 값을 받아와야 한다.

 

URL url = new URL(주소값);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();

 

URL url = new URL(" ");

따옴표 안에 json 주소를 적어준다.

그러면 Byte Stream을 연결할 대상을 지정해주기 위해서이다.

 

적으면 빨간 줄 오류가 날 텐데 URL 위에서 Alt + 엔터키를 눌러

try catch로 예외처리를 해준다.

 

MalformedURLException은 주소가 잘못 입력되었을 때 뜨는 예외처리인데

우리는 MalformedURLException의 부모인 Exception을 써주자.

 

우리가 받아온 주소가 http면 HttpURLConnection 써주면 되고,

https면 HttpsURLConnection를 써주면 된다.

 

http와 https의 차이는

http는 보안이 적용되지 않은 것,

https는 보안이 적용된 것이다.

 

공공 데이터는 오픈 데이터이기 때문에

보안이 적용되어 있지 않은 것이다.

 

HttpURLConnection의

url.openConnection( ) 메서드가 논리적 전기선을 만들어준다.

conn이 선(Byte Stream)인 셈이다.

 

url.openConnection( ) 메서드에 컨트롤 클릭해보면

리턴 타입이 URLConnection인데,

우리가 만든 conn은 HttpURLConnection 타입이다.

 

HttpURLConnection을 컨트롤 클릭해보면 부모가 URLConnection이다.

 

이때 다운 캐스팅을 해줘야 한다.

(HttpURLConnection)을 붙여줘야 하는 것이다.

 

부모를 가리키는 포인터를 자식으로 내리는 것이 다운 캐스팅이다.

 

기본 자료형에서 다운 캐스팅은 메모리의 크기를 깎는 것이지만,

레퍼런스 자료형의 다운 캐스팅은 부모를 바라보는 화살표를 자식으로 내리는 것이다.

 

그런데 왜 url.openConnection( )의 리턴 타입은 부모 타입인 URLConnection 타입일까?

만드는 사람이 HttpURLConnection 타입으로 만들지,

HttpsURLConnection 타입으로 만들지 모르니까

부모로 잡아두고 알아서 다운 캐스팅해서 사용하라는 것이다.

 

사용하는 사람이 어떤 것을 쓸 줄 모르기 때문에

부모 타입으로 리턴하는 것이다.

 

BufferedReader br = new BufferedReader(
                    new InputStreamReader(conn.getInputStream(), "utf-8"));

버퍼를 사용해서 받아온 json을

gson을 사용하여 만들어둔 오브젝트로 옮겨주면 된다.

 

실행해보면 한글이 깨질 것이다.

한글 깨지는 것은 InputStreamReader를 할 때

무조건 1Byte로 받기 때문이다.

한글은 2Byte로 받아야 모든 글자를 받을 수 있는데

1Byte로 던졌기 때문에 한글이 깨진 것이다.

 

한글은 2Byte로 받을 수 있지만

또 중국어는 3Byte로 받아야 한다.

 

전 세계 공통된 문자 인코딩이 필요해서 만들어진 게 UTF-8이다.

무조건 3Byte씩 던져준다.

 

한글을 3바이트로 끊어 읽겠다는 말을 InputStreamReader 안에 넣어준다.

InputStreamReader을 컨트롤 클릭해보면

InputStreamReader 안에는 charsetName이라는 매개변수 자리가 있는데

여기 그냥 넣어주면 된다.

 

앞으로 무조건 여기에 utf-8을 넣어줄 것이다.

 

이제 json을 받아왔으니까 gson을 사용해 파싱을 해줄 것이다.

String responseJson = br.readLine(); // 버퍼 비우기
System.out.println("받은 문자열 : " + responseJson);
Gson gson = new Gson();
ResponseDto dto = gson.fromJson(responseJson, ResponseDto.class);

우선 변수를 만들어서 json을 readLine( ) 메서드로 내려받는다.

우리가 받아온 데이터는 한 번만 내려받아도 충분하기 때문에

while을 사용하지 않았지만

더 많은 데이터를 내려받을 때는 while을 사용해 반복문을 돌려서 받아준다.

 

그리고 gson 객체를 만들어 받아온 json을 오브젝트로 옮겨주면 끝!

 

// System.out.println(dto);
// System.out.println(dto.getResponse().getBody().getItems().getItem().get(0));

List<Item> result = dto.getResponse().getBody().getItems().getItem();
System.out.println(result);

얻어온 데이터를 출력해볼 때

원하는 값을 일일이 .get().get().get하기 너무 귀찮으니까

데이터가 있는 메서드까지 찾아가서 item만 List에 따로 저장해주고

List를 출력해보면

 

데이터만 출력이 잘된다!

 

 

package data00;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.ResultSet;
import java.util.List;

import com.google.gson.Gson;

import data00.ResponseDto.Response.Body.Items.Item;

public class Test01 {
    public static void main(String[] args) {
        try {
            URL url = new URL(
                    "http://openapi.tago.go.kr/openapi/service/DmstcFlightNvgInfoService/getFlightOpratInfoList?serviceKey=wJmmW29e3AEUjwLioQR22CpmqS645ep4S8TSlqtSbEsxvnkZFoNe7YG1weEWQHYZ229eNLidnI2Yt5EZ3Stv7g%3D%3D&numOfRows=10&pageNo=1&depAirportId=NAARKPK&arrAirportId=NAARKPC&depPlandTime=20220125&airlineId=ABL&_type=json");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            // 원래 default 1Byte 한글을 읽었기 때문에 한글이 깨졌다.
            // utf-8 한글을 3Byte로 끊어 읽겠다.
            // charsetName = utf-8 무조건 고정 !!
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(conn.getInputStream(), "utf-8"));

            String responseJson = br.readLine(); // 버퍼 비우기
            System.out.println("받은 문자열 : " + responseJson);
            Gson gson = new Gson();
            ResponseDto dto = gson.fromJson(responseJson, ResponseDto.class);

            // System.out.println(dto);
            // System.out.println(dto.getResponse().getBody().getItems().getItem().get(0));

            List<Item> result = dto.getResponse().getBody().getItems().getItem();
            System.out.println(result);

        } catch (Exception e) {
            System.out.println("주소 입력이 잘못되었습니다.");
        }
    }
}

 

 

 

 

 

 

 

 

 

 

[출처]

 

https://cafe.naver.com/metacoding

 

메타코딩 : 네이버 카페

코린이들의 궁금증

cafe.naver.com

 

메타 코딩 유튜브

https://www.youtube.com/c/%EB%A9%94%ED%83%80%EC%BD%94%EB%94%A9

 

메타코딩

문의사항 : getinthere@naver.com 인스타그램 : https://www.instagram.com/meta4pm 깃헙 : https://github.com/codingspecialist 유료강좌 : https://www.easyupclass.com

www.youtube.com

 

반응형