[Spring] 서울열린데이터광장 OpenAPI 활용하기

SeonJin·2023년 11월 6일
0

SpringFramework_[Kosta]

목록 보기
1/1
post-custom-banner

전체코드보기


[활용 데이터 ]

서울시 동물병원 인허가 정보
JSON 미리보기


[코드분석 ]

DTO

DTO의 경우 필요한 정보만 정의해도 좋고, 모두 생성해도 무방하다
나의 경우 필요한 정보만 정의하고 생성자와 setter/getter를 만들어 주었다

Service

  int startIdx = (pageInfo.getCurPage() - 1) * 10 + 1;
  • 현재 페이지에 따라 API를 호출하기 위한 시작 인덱스를 계산


  StringBuilder urlBuilder = new StringBuilder("http://openapi.seoul.go.kr:8088");
  urlBuilder.append("/"+URLEncoder.encode("4864596d7173656f3131306649487165", "UTF-8"));
  urlBuilder.append("/"+URLEncoder.encode("json", "UTF-8"));
  urlBuilder.append("/"+URLEncoder.encode("LOCALDATA_020301", "UTF-8"));
  urlBuilder.append("/"+URLEncoder.encode(startIdx+"", "UTF-8"));
  urlBuilder.append("/"+URLEncoder.encode(startIdx+10+"", "UTF-8"));

💡샘플 URL(http://openapi.seoul.go.kr:8088/인증키/json/LOCALDATA_020301/1/5/)에 따라 작성

  • **SpringBuilder**는 문자열을 동적으로 조작하고 연결할 때 사용되는 클래스
  • String을 사용하는 경우 문자열을 변경할 때마다 새로운 객체가 생성되기 때문에 성능 향상을 위해 가변 문자열을 다룰 수 있는 StringBuilder를 사용하는 것

// request
URL url = new URL(urlBuilder.toString());
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-type", "application/json");

// response
BufferedReader br;
int resultCode = conn.getResponseCode();
if(resultCode>=200 && resultCode<=300) { // 정상
	br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
	} else { // 에러
    br = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
    }

StringBuilder resBuilder = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
	resBuilder.append(line);
}

br.close();
conn.disconnect();

System.out.println(resBuilder.toString());
  • 위에서 구성한 URL 문자열로 생성한 URL 객체를 통해 HTTP 연결을 생성
  • API 응답 데이터를 읽기 위해 BufferedReader를 선언
  • HTTP 응답 코드를 확인하고 결과에 따라 API 응답 스트림을 읽을 BufferedReader를 초기화
  • API 응답 데이터를 저장하기 위해 StringBuilder를 생성하고, 한 줄씩 읽어와 데이터를 추가
  • BufferedReader를 닫고 HTTP 연결을 종료

JSONParser parser = new JSONParser();
JSONObject mobj = (JSONObject) parser.parse(resBuilder.toString());
JSONObject LOCALDATA_020301 = (JSONObject) mobj.get("LOCALDATA_020301");
Long list_total_count = (Long) LOCALDATA_020301.get("list_total_count");
JSONArray row = (JSONArray) LOCALDATA_020301.get("row");
  • API 응답 페이터를 파싱하여 저장할 리스트를 초기화

  • JSONParser 객체를 생성하여 API 응답 데이터와, 객체, 객체 내에서 필요한 정보를 파싱

  • JSON 데이터 구조를 살펴보자.

    • “LOCALDATA_020301" 최상위 객체가 "list_total_count" 필드와 "row" 배열을 가진다
    • list_total_count 필드는 전체 데이터 항목 수를 나타낸다 → 이후에 페이징 처리에서 활용
    • row 배열에는 동물병원 정보를 설명하는 각각의 객체를 가지고 있다

List<AnimalClinic> acList = new ArrayList<>();
for(int i=0; i<row.size(); i++) {
    JSONObject acJson = (JSONObject) row.get(i);
    String trdStatNm = (String) acJson.get("TRDSTATENM");
    String siteTel = (String) acJson.get("SITETEL");
    String rdnwhlAddr = (String) acJson.get("RDNWHLADDR");
    String bplcNm = (String) acJson.get("BPLCNM");
    String x = (String) acJson.get("X");
    String y = (String) acJson.get("Y");
    acList.add(new AnimalClinic(trdStatNm, siteTel, rdnwhlAddr, bplcNm, x, y));
}
  • 앞서 추출한 row 배열에서 각 동물병원의 정보를 추출하고, asList에 추가

// 페이지 정보
int allPage = (int)Math.ceil(list_total_count.doubleValue()/10);
int startPage = (pageInfo.getCurPage()-1)/10*10+1;
int endPage = Math.min(startPage+10-1, allPage);

pageInfo.setAllPage(allPage);
pageInfo.setStartPage(startPage);
pageInfo.setEndPage(endPage);
if(pageInfo.getCurPage()>allPage) pageInfo.setCurPage(allPage);
  • 전체 페이지 수, 시작 페이지, 끝 페이지 계산
  • 파라미터로 받은 pageInfo 객체에 계산한 페이지 정보를 설정

return acList;
  • 처리된 동물병원 정보를 리스트 형태로 반환

Controller

  @Controller
@RequestMapping("/")
public class SeoulApiController {
    @Autowired
    private SeoulApiService service;
    @GetMapping(value = {"clinic", "clinic/{page}"})
    public ModelAndView animalClinicList(@PathVariable(required=false) Integer page) {
        PageInfo pageInfo = new PageInfo();
        if(page!=null) {// url에 page 값이 없다면 기본값 1로 지정
            pageInfo.setCurPage(page);
        } else {
            pageInfo.setCurPage(1);
        }
        ModelAndView mav = new ModelAndView();
        try {
            List<AnimalClinic> acList = service.animalClinicList(pageInfo);
            mav.addObject("acList", acList);
            mav.addObject("pageInfo", pageInfo);
            mav.setViewName("animalclinic");
        } catch (Exception e) {
            e.printStackTrace();
            mav.addObject("err", "서울시 동물병원 허가 정보 조회 실패");
            mav.setViewName("error");
        }
        return mav;
    }
}
  • SeoulApiService 타입의 빈을 주입하여 컨트롤러에서 사용
  • 둘 이상의 URL 패턴을 정의하는 경우 value임을 명시하고 중괄호 안에 나열하여 사용
  • @PathVariable(required=false) URL에서 가져온 page 값을 변수에 할당할 때, 해당 매개변수가 필수가 아니라는 것을 나타냄
  • if문을 통해 page 값이 없는 경우, 기본값을 1로 설정
profile
study notebook
post-custom-banner

0개의 댓글