💡OpenAPI
OpenAPI는 외부에서 웹 프로토콜(HTTP)로 호출하여 사용할 수 있도록 개방한 API이다. 그래서 OpenAPI는 공개된 프로그래밍 기능이라고 부르며, 모두에게 공개되어 있긴 하지만 무료라는 뜻으로 말하는 Open은 아니다.
OpenAPI는 웹 서비스, 라이브러리, 또는 다른 소프트웨어와의 통합에 용이하게 사용할 수 있다. 일반적으로 공공 데이터 포털 등에서 이러한 데이터를 쉽게 받아볼 수 있다.
공공 데이터 포털
1. 공공 데이터 포탈 가입
https://www.data.go.kr/index.do
2. 활용 신청하기
서울특별시_노선정보조회 서비스
https://www.data.go.kr/data/15000193/openapi.do
국토교통부_(TAGO)_지하철정보
https://www.data.go.kr/data/15098554/openapi.do
오른쪽 버튼에 [바로가기]가 되어 있으면 다른 사이트로 가서 가입하는 별도의 과정이 요구될 수 있으므로, [활용신청]으로 되어 있는 데이터를 사용하는 게 편하다.
서울특별시 노선정보 조회 서비스는 별도의 과정이 요구되므로, 국토교통부 지하철 정보를 사용하도록 하자.
💡OpenAPI 활용신청
사용하려고 하는 OpenAPI의 페이지에 들어가 [활용신청] 버튼을 클릭한다.
그리고 활용목적을 기입한 뒤, 활용신청 버튼을 클릭하면 승인 신청이 완료된다.
OpenAPI 승인이 되기까지 시간이 걸리는 경우도 있지만, 바로 승인이 되기도 한다.
국토교통부 지하철 정보 OpenAPI는 신청 직후에 승인 처리가 되었다.
💡요청 URL 생성
요청주소 입력
'요청주소'는 우리가 뭔가를 요청하면 데이터를 돌려주는 주소이다.
그러나 이 주소를 브라우저창에 보내서 호출한다고 무조건 값을 돌려주는 게 아니다. 내가 신청 승인이 된 사람이라는 것과 어떤 데이터를 보고 싶은지를 알려줘야 데이터를 돌려받을 수 있다.
인터넷 주소를 통해서 OpenAPI에 요청을 하면 이와 같이 인터넷 주소를 통해서 값을 받아올 수 있다.
String url = "http://apis.data.go.kr/1613000/SubwayInfoService/getKwrdFndSubwaySttnList?"; // '?'를 붙인다
String url에 요청주소를 입력한 뒤, 제일 끝에 '?'를 붙여 주었다.
요청변수 입력
다음에 입력하는 것들은 모두 요청변수(Request Parameter)에서 요구되는 것들이다.
OpenAPI의 종류마다, OpenAPI 내에서의 기능마다 요청주소, 요청변수가 다를 수 있다.
QueryString
http://data.go.kr/SttnList?키=데이터&키=데이터&키=데이터&키=데이터
인터넷 주소 맨 끝에 붙이는 '?'는 예약어이다. '?'는 이 주소를 호출할 때, 데이터를 같이 보내겠다는 의미이다.
'?' 뒤에 서버에 넘겨주고 싶은 데이터를 입력한다. 이때 변수의 이름을 붙이듯이 데이터에 이름을 붙여줘야 하는데, 이를 key라고 한다.
이렇게 '?'로 연결된 값들을 바로 QueryString이라고 한다.
ServiceKey 입력
url += "ServiceKey=할당받은 서비스키"
할당받은 개인 키를 입력해야 한다. 활용신청을 하면서 받은 서비스키를 "ServiceKey=" 뒤에 입력한다.
이로써 OpenAPI 서비스를 이용할 수 있는 자격이 증명된 것이다.
_type 입력
url += "&_type=json"; // XML or JSON
데이터를 요청하면 데이터를 반환받는데, XML으로 받을지 JSON으로 받을지를 선택할 수 있다.
지금은 JSON으로 돌려받기로 한다.
numOfRows, pageNo 입력
url += "&numOfRows=10";
url += "&pageNo=1";
numOfRows는 받을 열의 수이고, pageNo는 받고자 하는 열을 받기 시작할 페이지의 번호이다.
위 소스코드는 첫 번째 페이지부터 열을 10개씩 돌려받겠다는 의미이다.
subwayStationName 입력
url += "&subwayStationName=" + "역삼";
이제 요청변수를 기입한 뒤, 알고 싶은 정보를 입력한다.
역 이름이 "역삼"일 때와 "잠실"때의 결과를 살펴보도록 하자.
System.out.println(url);
완성된 url을 출력하여 Chrome 브라우저 주소창에 붙여 넣어보자.
💡데이터 반환
역삼 데이터 반환
우리가 서버가 가지고 있는 정보를 사용해 데이터를 요청하면 서버에서 데이터를 돌려주는 메서드를 만들어 두었다.
일반적으로 메소드를 호출할 때처럼.(메서드명)으로 호출하지 못하며, 대신 이렇게 인터넷 주소 형태(url 형태)로 데이터를 호출할 수 있다.
그런데 이렇게 데이터를 받으니 어떤 내용인지 해석하기 어렵다.
JSON viewer
JSON viewer는 JSON이라는 데이터를 표현하는 문법을 보기 좋게 변환시켜 주는 사이트이다.
JSON으로 조회한 결과를 붙여 넣어 보자.
parse를 보면 response라는 이름으로 객체를 돌려받은 것을 확인할 수 있다. response 안에는 header와 body라는 데이터로 구분된다.
보통 body 안의 items 파트 안에 사용자가 알고 싶은 데이터가 들어 있으며, 그것에 관련된 추가 상황들을 아래에서 볼 수 있다.
역삼을 입력한 결과로 subwayRouteName이 2호선이라는 정보와 subwayStationId가 MTRS12221이라는 정보를 반환받았다. 아래에는 역삼과 함께 요청할 때의 정보인 numOfRows와 PageNo 데이터를 확인할 수 있다.
잠실 데이터 반환
💡URL 객체 생성
Scanner scan = new Scanner(System.in);
System.out.println("[지하철 정보 검색]");
System.out.print("지하철 역 검색: ");
String stationName = scan.nextLine();
// 요청 URL 생성
String url = "http://apis.data.go.kr/1613000/SubwayInfoService/getKwrdFndSubwaySttnList?";
// ServiceKey 입력
url += "ServiceKey=할당받은 서비스키";
// type 입력
url += "&_type=json"; // XML or JSON
// numOfRows, pageNo 입력
url += "&numOfRows=10";
url += "&pageNo=1";
// 조회하고 싶은 역 조회
url += "&subwayStationName=" + URLEncoder.encode(stationName);
System.out.println(url);
try {
// URL 객체 생성
URL obj_url = new URL(url);
// URL과 연결하는 객체 생성
HttpURLConnection conn = (HttpURLConnection)obj_url.openConnection();
// HttpURLConnection는 java.net으로 가져와야 한다.
// 요청할 때 들어가는 옵션
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-type", "application/json");
BufferedReader reader = null;
if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
} else {
reader = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
}
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
/*
// 에러 발생
<OpenAPI_ServiceResponse>
<cmmMsgHeader>
<errMsg>SERVICE ERROR</errMsg>
<returnAuthMsg>SERVICE_KEY_IS_NOT_REGISTERED_ERROR</returnAuthMsg>
<returnReasonCode>30</returnReasonCode>
</cmmMsgHeader>
</OpenAPI_ServiceResponse>
*/
reader.close();
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
HttpURLConnection로 URL과 연결하는 객체를 생성하면 브라우저 대신에 접속을 해준다.
url을 입력했을 때, 데이터를 반환받는 작업을 성공할 수도 있고, 실패할 수도 있다. 이때 if문이 작업을 성공했는지, 실패했는지를 알려준다.
HttpURLConnection를 가져올 때에는 java.net으로 가져와야 한다는 점을 주의하자. Ctrl + Space로 자동완성을 했을 때, 4가지가 나오는 것을 확인했다. 다른 것을 import 하지 않도록 하자.
이렇게 만들어진 주소를 URL이라는 클래스의 인스턴스로 만든다. 인터넷으로 나가는 입출력이므로 혹시 모를 오류에 대해 예외처리를 해주면 완성이다.
💡JSON parsing
JSON 표현 방식
{
"name": "홍길동",
"kor": 100,
"eng": 90,
"math", 80
"nick": ["데이터1", "데이터2"]
}
JSON 표현 방식은 자바에서는 이해할 수 없다. 따라서 이를 자바에 그대로 쓰면 오류가 난다.
parsing은 이러한 표현을 자바에서 인식시키는 과정으로, 구문을 분석하고 해석하는 역할을 하는 것들을 주로 parser라고 부른다.
mvnrepository
https://mvnrepository.com/artifact/com.googlecode.json-simple/json-simple/1.1.1
mvnrepository에서 json simple을 검색하여 jar 파일을 다운로드한다.
버전은 가장 최신 것으로 다운로드하도록 한다.
다운로드한 jar 파일을 Java Build Path > Classpath > Add JARs에서 인식시켜 주면 된다.
JSONparser 활용
try {
// URL 객체 생성
URL obj_url = new URL(url);
// URL과 연결하는 객체 생성
HttpURLConnection conn = (HttpURLConnection)obj_url.openConnection();
// 요청할 때 들어가는 옵션
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-type", "application/json");
BufferedReader reader = null;
if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
} else {
reader = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
}
String line = null;
// JSONParser
JSONParser parser = new JSONParser();
JSONObject root = (JSONObject)parser.parse(reader);
JSONObject response = (JSONObject)root.get("response");
JSONObject body = (JSONObject)response.get("body");
JSONObject items = (JSONObject)body.get("items");
if (items.get("item") instanceof JSONObject) {
JSONObject item = (JSONObject)items.get("item");
System.out.println("호선: " + item.get("subwayRouteName"));
System.out.println("역명: " + item.get("subwayStationName"));
} else {
JSONArray arr = (JSONArray)items.get("item");
for (Object obj : arr) {
JSONObject item = (JSONObject)obj;
System.out.println("호선: " + item.get("subwayRouteName"));
System.out.println("역명: " + item.get("subwayStationName"));
}
}
reader.close();
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
파싱(해석)을 하면 JSON 오브젝트라는 클래스 타입으로 리턴한다.
데이터를 받아올 때마다 JSONObject로 다운캐스팅을 해줘야 하며, 역이 1개일 때와 여러 개일 때 JSON 표현이 다르므로 ifelse문으로 item을 받아오는 방식에 차이를 두었다.
받아오는 값이 1개일 때에는 JSONObject로, 배열일 때에는 JSONArray로 값을 받아온다.
위 소스코드에서 JSONParser 하는 부분을 살펴보면, 순차적으로 아래로 내려가는 구조인 것을 확인할 수 있다. JSON으로 받은 정보를 다운캐스팅하며 추출해 가는 과정이다.
JSON parsing 결과
최종 코드
지하철역 출구번호별로 주변시설의 목록을 조회하는 기능을 추가하여 최종 코드를 작성해 보자.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Scanner;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
public class TAGO {
public static void main(String[] args) {
while (true) {
methodTAGO();
}
}
private static void methodTAGO() {
Scanner scan = new Scanner(System.in);
System.out.println("[지하철 정보 검색]");
System.out.print("지하철 역 검색: ");
String stationName = scan.nextLine();
String url = "http://apis.data.go.kr/1613000/SubwayInfoService/getKwrdFndSubwaySttnList?"; // ?를 붙인다
url += "ServiceKey=할당받은 개인키";
url += "&_type=json";
url += "&numOfRows=10";
url += "&pageNo=1";
url += "&subwayStationName=" + URLEncoder.encode(stationName);
try {
URL obj_url = new URL(url);
HttpURLConnection conn = (HttpURLConnection)obj_url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-type", "application/json");
BufferedReader reader = null;
if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
} else {
reader = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
}
String line = null;
JSONParser parser = new JSONParser();
JSONObject root = (JSONObject)parser.parse(reader);
JSONObject response = (JSONObject)root.get("response");
JSONObject body = (JSONObject)response.get("body");
JSONObject items = (JSONObject)body.get("items");
ArrayList<String> temp = new ArrayList<String>();
if (items.get("item") instanceof JSONObject) {
JSONObject item = (JSONObject)items.get("item");
System.out.println("1. ");
System.out.println("호선: " + item.get("subwayRouteName"));
System.out.println("역명: " + item.get("subwayStationName"));
temp.add(item.get("subwayStationId").toString());
} else {
JSONArray arr = (JSONArray)items.get("item");
int n = 1;
for (Object obj : arr) {
JSONObject item = (JSONObject)obj;
System.out.println(n + ".");
System.out.println("호선: " + item.get("subwayRouteName"));
System.out.println("역명: " + item.get("subwayStationName"));
System.out.println("--------");
temp.add(item.get("subwayStationId").toString());
n++;
}
}
reader.close();
conn.disconnect();
System.out.print("역 선택: ");
int sel = scan.nextInt();
System.out.println(temp.get(sel - 1)); // 역 ID
// 지하철역 출구번호별로 주변시설의 목록을 조회하는 기능
url = "http://apis.data.go.kr/1613000/SubwayInfoService/getSubwaySttnExitAcctoCfrFcltyList?";
url += "ServiceKey=할당받은 개인키";
url += "&_type=json";
url += "&numOfRows=10";
url += "&pageNo=1";
url += "&subwayStationId=" + temp.get(sel - 1);
obj_url = new URL(url);
conn = (HttpURLConnection)obj_url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-type", "application/json");
reader = null;
if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
} else {
reader = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
}
root = (JSONObject)parser.parse(reader);
response = (JSONObject)root.get("response");
body = (JSONObject)response.get("body");
items = (JSONObject)body.get("items");
if (items.get("item") instanceof JSONObject) {
JSONObject item = (JSONObject)items.get("item");
System.out.println("출구: " + item.get("exitNo") + "번");
System.out.println("주변시설: " + item.get("dirDesc"));
} else {
JSONArray arr = (JSONArray)items.get("item");
for (Object obj : arr) {
JSONObject item = (JSONObject)obj;
System.out.println("출구: " + item.get("exitNo") + "번");
System.out.println("주변시설: " + item.get("dirDesc"));
}
}
reader.close();
conn.disconnect();
System.out.println("계속하려면 Enter 키를 누르세요.");
scan.nextLine();
scan.nextLine();
} catch (Exception e) {
e.printStackTrace();
}
}
}
최종 코드 결과
입력한 역의 지하철 역 정보와 이름, 선택한 역의 주변 시설이 출력된다.