개발일기
서울시 헌옷 수거함 개발일기 - 공공 데이터 API (1) 본문
글쓴이는 초보 서버 개발자로써... 코드상에 많은 문제점이 있을 수 있습니다. 언제든 코드 개선 및 훈수 환영합니다!
서울시 헌 옷 수거함의 위치를 알려 줄 수 있는 웹사이트를 만들고 싶다고 생각하게 되었고 해당 API를 제공하는지 여부를 찾았다. 그러다가 서울시 지역의 모든 헌옷수거함 위치를 알 수는 없지만 공공 데이터 API에서 어느 정도 제공한다는 것을 알게 되어 프로젝트를 시작하게 되었다.
https://www.data.go.kr/iim/api/selectAPIAcountView.do
공공데이터포털 통합 로그인
공공데이터포털 로그인 국민과 함께 하는 공공데이터포털에 오신 것을 환영합니다
auth.data.go.kr
해당 데이터 포털사이트에서 API를 찾아 주었다.
공공 데이터 연결을 위해
요렇게 보이는 인증키를 사용해주면 된다.
[PostMan을 통한 API 통신 확인]
위에 보이는 Param값에 page , perPage, seviceKey를 API에서 넣어주라것을 참고하여 입력해주면 Data가 아래에 보이는 것을 확인할 수 있다.
여기까지 1차 준비완료!
이제부터 자바 소스코드로 해당 코드를 구현해보려고 한다.
우선 일차적으로 dto폴더를 만들어주었다. 그 후, 아래에 보이는 자바 파일 dto를 만들어주었다.
OpenApiManagerDto에는 OpenApi를 만들어주는 클래스 DTO가 될 것이다. 우선 모든 코드를 보기전에 하나하나 따로 보도록 하겠다.
private String serviceKey;
private int page;
private int perPage;
private String uddi;
private int locationNumber;
우선 필드 값들이다. 기본적인 BaseUrl, 아까 APIKey를 넣어줄 ServiceKey 필드값이 보인다. 또한 page, perPage가 보이며 uddi와 locationNumber 값을 필드값으로 생성해주었다.
각각의 필드 값들을 OpenApiManagerDto를 new 연산자로 인스턴스를 생성할 때, 파라미터 값들로 받을 것이며 이를 위한 준비를 해주었다..!
이제 makeUrl Function을 살펴보자
private URI makeUrl() throws URISyntaxException {
logger.info("makeUrl>>"+BaseUrl+locationNumber+"/v1/uddi:"+uddi+"?page="+page+"&perPage="+perPage+"&serviceKey="+serviceKey);
return new URI(BaseUrl+locationNumber+"/v1/uddi:"+uddi+"?page="+page+"&perPage="+perPage+"&serviceKey="+serviceKey);
}
해당 함수는 openApi에 접근하기 위해서는 URL이 필요한데 이를 만들기 위해서 new URI함수를 통해 해당 URL를 만들어주었다.
참고로 URI와 URL은 거의 비슷한 것이라 보면 되는데 ,
URI는 통합 자원 식별자 => 소스를 식별하는 통일된 방식
URL은 네트워크상에서 통합 자원(리소스)의 “위치”를 나타내기 위한 규약 => 웹 사이트 주소 + 컴퓨터 네트워크 상의 자원
위에 내용과 같다. 더욱 자세한 것은 따로 찾아보면 될 것 같다.
각각의 서울시 지역별 공공 데이터 API 헌 옷 수거함의 위치가 다르기 때문에 해당 getAllCountFunction을 만들어 주었다.
public void getAllCount() throws URISyntaxException, ParseException {
RestTemplate template = new RestTemplate();
JSONParser jsonParser = new JSONParser();
HttpEntity<?> entity = new HttpEntity<>(new HttpHeaders());
ResponseEntity<String> resultMap = null;
try {
resultMap = template.exchange(makeUrl(), HttpMethod.GET, entity, String.class);
JSONObject jsonObject = (JSONObject) jsonParser.parse(resultMap.getBody());
perPage = Integer.parseInt(String.valueOf(jsonObject.get("totalCount")));
logger.info("perPage : " +perPage);
} catch (HttpClientErrorException e) {
logger.info(String.valueOf(e.getStatusCode()));
logger.info(e.getResponseBodyAsString());
}
}
Spring에서 지원해주는 RestTemplate를 사용하여 RestApi를 호출해주었다. 이때, jsonParser를 사용하여 JSON데이터에 있는 totalCount를 받아와 주었다. 이 함수는 각각의 다양한 구별 데이터를 전부다 가져와주기 위하여 totalCount를 가져와주는 것이다. 사실 이렇게 해주면 RestTemplate를 두번해주어야 하는 별로인 방법이지만... 더 좋은 방법이 생각나지 않아 해당 방법으로 구현 하였다.
getAllFetch Function
public List<String> getAllFetch() throws URISyntaxException, ParseException {
RestTemplate template = new RestTemplate();
HttpEntity<?> entity = new HttpEntity<>(new HttpHeaders());
getAllCount();
ResponseEntity<String> apiData = template.exchange(makeUrl(), HttpMethod.GET, entity, String.class);
ArrayList<String> targetPositionDatas = (ArrayList<String>) getPosition(apiData);
for(String target : targetPositionDatas){
logger.info(target);
}
return targetPositionDatas;
}
해당함수는 API에 있는 데이터를 가져오기 위하여 구현한 코드이다. getAllCount에서 코드를 보았듯 perPage에 모든 숫자값이 들어 있기 때문에 해당 방식으로 구현하면 해당 API에 있는 위치 정보를 전부 가져올 수 있는 것을 확인할 수 있다.
getPosition Function
public List<String> getPosition(ResponseEntity<String> apiData) throws ParseException {
List<String> getOpenApiPositions = new ArrayList<>();
JSONParser jsonParser = new JSONParser();
String apiResult = apiData.getBody().toString();
JSONObject targetJsonObject = (JSONObject) jsonParser.parse(apiResult);
JSONArray targetJsonArray = (JSONArray) targetJsonObject.get("data");
System.out.println("targetJsonArray >> "+targetJsonArray);
for(Object targetApiData : targetJsonArray){
String transferTargetData = targetApiData.toString();
JSONObject realTarget = (JSONObject) jsonParser.parse(transferTargetData);
// 각각의 DTO
if(realTarget.containsKey("위치"))
{
String targetPosition = (String) realTarget.get("위치");
getOpenApiPositions.add(targetPosition);
}else if(realTarget.containsKey("도로명주소")){
String targetPosition = (String) realTarget.get("도로명주소");
getOpenApiPositions.add(targetPosition);
}else if(realTarget.containsKey("주소")){
String targetPosition = (String) realTarget.get("주소");
getOpenApiPositions.add(targetPosition);
} else if(realTarget.containsKey("지번주소")){
String targetPosition = (String) realTarget.get("지번주소");
getOpenApiPositions.add(targetPosition);
}
}
return getOpenApiPositions;
}
해당 함수는 API에 있는 위치 정보를 가져오기 위하여 작성해주었다. if문을 통해서 위치 분기를 나누어 주었으며 우리나라 주소에는 도로명주소, 구주소 , 지번주소가 있기 때문에 해당 방법으로 위치정보를 받아 올 수 있도록 만들어주었다.
해당 방식을 통해서 위치 정보를 가져온 후 나중에 Kakao에서 제공해주는 위도,경도 , 위치 정보등을 받아 데이터베이스에 넣어주는 작업을 할 것이다.
OpenApiManager의 전체 코드이다.
package com.odd.oddProject.dto;
import lombok.AllArgsConstructor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
@AllArgsConstructor
public class OpenApiManagerDto {
private String BaseUrl;
private String serviceKey;
private int page;
private int perPage;
private String uddi;
private int locationNumber;
private final Logger logger = LogManager.getLogger(OpenApiManagerDto.class);
/*공공데이터 URL을 만들어준다. */
private URI makeUrl() throws URISyntaxException {
logger.info("makeUrl>>"+BaseUrl+locationNumber+"/v1/uddi:"+uddi+"?page="+page+"&perPage="+perPage+"&serviceKey="+serviceKey);
return new URI(BaseUrl+locationNumber+"/v1/uddi:"+uddi+"?page="+page+"&perPage="+perPage+"&serviceKey="+serviceKey);
}
/* 각 Api의 totalCount(총 개수를 불러온다.) */
public void getAllCount() throws URISyntaxException, ParseException {
RestTemplate template = new RestTemplate();
JSONParser jsonParser = new JSONParser();
HttpEntity<?> entity = new HttpEntity<>(new HttpHeaders());
ResponseEntity<String> resultMap = null;
try {
resultMap = template.exchange(makeUrl(), HttpMethod.GET, entity, String.class);
JSONObject jsonObject = (JSONObject) jsonParser.parse(resultMap.getBody());
perPage = Integer.parseInt(String.valueOf(jsonObject.get("totalCount")));
logger.info("perPage : " +perPage);
} catch (HttpClientErrorException e) {
logger.info(String.valueOf(e.getStatusCode()));
logger.info(e.getResponseBodyAsString());
}
}
/*모든 API의 개수를 가져와준다.*/
public List<String> getAllFetch() throws URISyntaxException, ParseException {
RestTemplate template = new RestTemplate();
HttpEntity<?> entity = new HttpEntity<>(new HttpHeaders());
getAllCount();
ResponseEntity<String> apiData = template.exchange(makeUrl(), HttpMethod.GET, entity, String.class);
ArrayList<String> targetPositionDatas = (ArrayList<String>) getPosition(apiData);
for(String target : targetPositionDatas){
logger.info(target);
}
return targetPositionDatas;
}
/* JsonData의 위치 데이터를 가져와준다. */
public List<String> getPosition(ResponseEntity<String> apiData) throws ParseException {
List<String> getOpenApiPositions = new ArrayList<>();
JSONParser jsonParser = new JSONParser();
String apiResult = apiData.getBody().toString();
JSONObject targetJsonObject = (JSONObject) jsonParser.parse(apiResult);
JSONArray targetJsonArray = (JSONArray) targetJsonObject.get("data");
System.out.println("targetJsonArray >> "+targetJsonArray);
for(Object targetApiData : targetJsonArray){
String transferTargetData = targetApiData.toString();
JSONObject realTarget = (JSONObject) jsonParser.parse(transferTargetData);
// 각각의 DTO
if(realTarget.containsKey("위치"))
{
String targetPosition = (String) realTarget.get("위치");
getOpenApiPositions.add(targetPosition);
}else if(realTarget.containsKey("도로명주소")){
String targetPosition = (String) realTarget.get("도로명주소");
getOpenApiPositions.add(targetPosition);
}else if(realTarget.containsKey("주소")){
String targetPosition = (String) realTarget.get("주소");
getOpenApiPositions.add(targetPosition);
} else if(realTarget.containsKey("지번주소")){
String targetPosition = (String) realTarget.get("지번주소");
getOpenApiPositions.add(targetPosition);
}
}
return getOpenApiPositions;
}
}
ConnectApiTest라는 이름으로 자바클래스 파일을 하나 만들어주었다. 해당 클래스 파일은 ApiTest를 위해 작성 생성해주었다.
@DisplayName("Api연결 테스트")
void apiFetchTest() throws URISyntaxException, ParseException {
String[] locations = {"guro","jongno","yangcheon","gwanak"};
for(String location : locations){
System.out.println("location"+location);
ArrayList<String> responseEntity = (ArrayList<String>) CmnUtil.selectApiLocation(location);
System.out.println("getApi = " + responseEntity);
}
}
해당 방법으로 로그가 잘 출력되는지 테스트하였다.
여기서 CmnUtil은 공통 함수를 만들어주는 곳인데 사실 공통함수는 아니지만 공통함수에 넣어서 빼두었다.
public static List<String> selectApiLocation(String location) throws URISyntaxException, ParseException {
switch (location)
{
case "guro":
openApiManagerDto = new OpenApiManagerDto("https://api.odcloud.kr/api/",serviceKey,1,constPerPage,"672059d4-1830-44af-97dd-cf0954b2ee86",15068871);
apiData = (ArrayList<String>) openApiManagerDto.getAllFetch();
break;
case "yangcheon" :
openApiManagerDto = new OpenApiManagerDto("https://api.odcloud.kr/api/",serviceKey, 1, constPerPage,"ce842bd1-877f-41b0-b612-81bb77bdbb1d",15105196);
apiData = (ArrayList<String>) openApiManagerDto.getAllFetch(); break;
case "jongno" :
openApiManagerDto = new OpenApiManagerDto("https://api.odcloud.kr/api/",serviceKey,1,constPerPage, "94ddbdca-b180-4156-b4f0-8048116792f9",15104622);
apiData = (ArrayList<String>) openApiManagerDto.getAllFetch(); break;
case "gwanak" :
openApiManagerDto = new OpenApiManagerDto("https://api.odcloud.kr/api/",serviceKey,1,constPerPage, "6dec2a8d-6404-4318-8767-85419b3c45a0",15076398);
apiData = (ArrayList<String>) openApiManagerDto.getAllFetch();
break;
case "dongjak" :
openApiManagerDto = new OpenApiManagerDto("https://api.odcloud.kr/api/",serviceKey,1,constPerPage, "05be3e28-de40-426b-9198-e4ca5b3ceee7", 15068021);
apiData = (ArrayList<String>) openApiManagerDto.getAllFetch();
break;
case "gwangjin" :
openApiManagerDto = new OpenApiManagerDto("https://api.odcloud.kr/api/",serviceKey,1,constPerPage,"d63e68bf-e03d-4d3c-a203-fd9add3d372c", 15109594);
apiData = (ArrayList<String>) openApiManagerDto.getAllFetch();
break;
}
return apiData;
}
해당 방식으로 각각 구 별로 API를 가져올 수 있도록 만들어주었다.
정상적으로 잘 출력되는 것을 확인할 수 있다.
'OttDaDam 서울시 헌옷수거함 project' 카테고리의 다른 글
백그라운드 React nohup으로 실행시키기 (2) | 2024.06.27 |
---|---|
AWS Springboot React 배포 (0) | 2023.11.11 |
(3) OttDaDam 프로젝트 DB 구축을 위한 Mac M1에서 MySql 설치 (0) | 2023.08.02 |
(2) OttDaDam 헌옷 수거함 공공 API (0) | 2023.08.02 |
옷다담 환경 설정 에러 JAVA_HOME is set to an invalid directory: (2) | 2023.07.22 |