공공기관 REST API 호출하기

goodjam92·2023년 2월 23일
0

WhichBeachSearch

목록 보기
2/3
post-thumbnail

예전에 진행 했던 사이드 프로젝트를 하면서 처음으로REST API를 사용했었다. 여러 일들이 있었는데 약간의 회고도 하면서 내가 어떤식으로 REST API호출 하였고 어떻게 사용했는지 그리고 어떤 문제가 있었는지 한 번 정리해보려고 한다.

사용 된 API

1. HOME

2. MAP

보면 홈 화면에만 3개의 API를 호출해서 각 데이터를 가공하여 화면에 표시를 해야했다.
그래서.. 시작하자마자 문제에 부딪혔는데... 바로 API를 호출하는 부분이었다.
API...? REST API?? 생소한 단어였다.. 그래서 이 때 당시에는 1~2번 검색해보고 이런게 있구나 라면서 한 번 훑고 넘어갔지만 명칭에 대해 이번에는 조금만 더 알아보고 넘어가자!

REST API

  • REST ?
    Representational State Transfer(REST)는 API 작동 방식에 대한 조건을 부과하는 소프트웨어 아키텍처이다. 복잡한 네트워크에서 통신을 관리하기 위한 지침으로 만들어졌다. REST 기반 아키텍처를 사용하여 대규모의 고성능 통신을 안정적으로 지원할 수 있다.
  • API ?
    API 란 Application Programming Interface 의 약자이며, 두 소프트웨어 구성 요소가 서로 통신할 수 있게 하는 메커니즘이다.

그래서 두 개를 합친 REST API 란 것은.. REST 아케틱처 스타일로 설계 된 웹 API를 말한다고 한다.

일단.. 어떤 API를 말하는지 알아보았으니! 이제 한 번 사용 해볼까?
일단 공공기관 해수욕장 날씨 조회서비스 API 사이트에서 활용 가이드를 다운받아서 내용을 확인해보았다.

공공기관 오픈 API 참고 문서

이 때 당시에는 이 가이드를 보는 것 조차 토나왔는데 그래도 지금은 거부반응 같은건 없어서 다행인 것 같다.. 먼저 가이드의 서비스 개요를 살펴 보도록 하자!
  • 인터페이스 표준
    • REST (GET)

분명 위에서 봤던 REST 이긴 한데.. 여기에 GET 이라는걸 사용하나?

REST Method

  • POST (POST를 통해 해당 URI를 요청하면 리소스를 생성)
    • HTTP Request Message의 Body 부분에 데이터를 담아 전송한다.
      GET과 비교하여 데이터 크기, 보안의 측면에서 좋을 수 있다.
  • GET (GET을 통해 해당 리소스를 조회, 해당 도큐먼트의 자세한 정보를 가져옴)
    • 요청하는 데이터가 HTTP Request Message의 Header부분의 url에 담겨 전송된다. url의 ? 뒤에 데이터를 붙여서 요청을 보내는 것 (request)

REST API에서 요청을 하는 2개의 Method가 있는데 이러한 차이점이 있고, 해당 서비스는 GET 요청을 표준으로 하는 것 같았다.

  • 교환 데이터 표준
    • XML, JSON

교환 데이터 표준으로 XML, JSON 두 가지를 사용한다고 되어있었다.

XML (EXtensible Markup Language)

  • 웹, 애플리케이션, 데이터베이스, 컴퓨터 시스템 간의 정보교환을 지원
  • HTML과 매우 유사한 문자 기반의 마크업 언어
  • 데이터를 보여주는 목적이 아닌, 데이터를 저장하고 전달하는 목적

JSON (Javascript Object Notation)

  • Javascript 객체 표기법
  • 사람이 읽을 수 있는 텍스트 기반의 데이터 교환 표준
  • XML의 대안으로 좀 더 쉬운 데이터 교환 및 저장의 목적

꽤 중요한.. 부분이다. 내가 요청을 해서 받는 데이터가 XML일 경우 JSON으로 변경해서 객체형식으로 사용하는 것이 좀 더 편리할 것이다. 이미지를 보면 XML은 html처럼 보이고 JSON 은 javascript 처럼 보이니.. 우리가 응답 받은 데이터를 사용해야 한다면 javascript 객체 형식으로 표기된 데이터를 사용하는 것이 좀 더 편리하지 않을까?

대충 훑어 보았으니 이제 내가 사용할 데이터를 어떻게 호출하는지 알아보자.

문서 상단을 보면 Call Back URL이 적혀있다. 만약 해수욕장 단기예보 조회 데이터를 얻으려면 저 "Call Back URL"/serviceKey="??"/pageNo="??" 등의 예시 요청 메세지처럼 URL뒤에 정보들을 적어서 요청을 보내면 응답으로 해당 데이터를 아래와 같이 나에게 보내줄 것이다.

API 호출

이제 이 API를 자바스크립트 코드로 입력하여 호출해야 하는 방법을 찾아야 했다.
라이브러리 사용 방법들이 많이 보였지만.. 이 때는 왠지 라이브러리에 대한 무지로 두려움이 있어 순수 javascript를 이용한 방법을 찾게되었다.
(이런 마음은 당장 고쳐먹었다.. 라이브러리 제작하시는 천재 개발자분들 감사합니다 땡큐)
그러다가 데이터를 XML로 받은 다음 JSON 변환을 javascript 코드만을 이용한 글을 보게 되었고, 코드를 가져와서 사용하였는데 그 코드는 아래와 같다.
아마 나랑 같은 고민을 하는 초보자들도 검색하다보면 한번 쯤은 보았을만한 코드라고 생각한다.

// XML -> JSON
export function xmlToJson(xml) {
  let obj = {};
  if (xml.nodeType == 1) {
    if (xml.attributes.length > 0) {
      obj['@attributes'] = {};
      for (let j = 0; j < xml.attributes.length; j++) {
        let attribute = xml.attributes.item(j);
        obj['@attributes'][attribute.nodeName] = attribute.nodeValue;
      }
    }
  } else if (xml.nodeType == 3) {
    obj = xml.nodeValue;
  }

  let textNodes = [].slice.call(xml.childNodes).filter(function (node) {
    return node.nodeType === 3;
  });
  if (xml.hasChildNodes() && xml.childNodes.length === textNodes.length) {
    obj = [].slice.call(xml.childNodes).reduce(function (text, node) {
      return text + node.nodeValue;
    }, '');
  } else if (xml.hasChildNodes()) {
    for (let i = 0; i < xml.childNodes.length; i++) {
      let item = xml.childNodes.item(i);
      let nodeName = item.nodeName;
      if (typeof obj[nodeName] == 'undefined') {
        obj[nodeName] = xmlToJson(item);
      } else {
        if (typeof obj[nodeName].push == 'undefined') {
          let old = obj[nodeName];
          obj[nodeName] = [];
          obj[nodeName].push(old);
        }
        obj[nodeName].push(xmlToJson(item));
      }
    }
  }
  return obj;
}
// XML 데이터 요청
export async function getLCRiseSetInfo(lonString, latString, dateString)   
const url =
      'http://apis.data.go.kr/B090041/openapi/service/RiseSetInfoService/getLCRiseSetInfo';
  const reqURL =
    url + '?serviceKey=' + RISE_SET_INFO_SERVICE_KEY +`&longitude=${lonString}&latitude=${latString}&locdate=${dateString}&dnYn=Y`;
  const response = await fetch(reqURL);
  const xmlString = await response.text();
  const XmlNode = new DOMParser().parseFromString(xmlString, 'text/xml');
  const resultJSON = xmlToJson(XmlNode);
  return resultJSON;
}

먼저 fetch로 XML 데이터를 가져와서 저 길고 긴 XML to JSON 함수를 이용하여 JSON으로 변환하여 사용하고, 데이터를 가공하여 화면에 표시해주는 방법으로 프로젝트를 마무리했었다.

같이 프로젝트를 진행했던 친구는 axios를 사용하여 데이터를 바로 JSON으로 받아서 사용했었고, 이번에 다시 프로젝트를 복기하기 위해 코드를 다시 보는 와중에 이 부분이 내 눈에 띄어서 나도 한 번 axios를 사용하여 리팩토링하기로 한다.

서버와 데이터를 주고 받기 위한 HTTP 통신의 대표적인 3가지 fetch, Ajax, Axios

  • Ajax
    • 제이쿼리와 함께 이용하여 개발을 손쉽게 할 수 있도록 미리 여러 가지 기능을 포함해 놓은 프레임워크
  • fetch
    • 내장 라이브러리이며 Promise기반이다. 응답 결과는 Response 객체이므로, JSON으로 변환하는 작업이 필요하다.
    • 구형 브라우저에서 지원이 안 되고, 네트워크 에러 발생 시 계속 기다려야 한다.
  • Axios
    • node.js와 브라우저를 위한 Promise기반 HTTP 클라이언트이며 설치가 필요하다.
    • 사용하기 어렵지 않고, Promise 기반이다.
    • 요청, 응답에 대한 데이터를 JSON 타입으로 자동 변환해준다.

간단하게 이러한 차이점이 있었는데 fetch보다는 axiosJSON 타입을 지원하는 API에서는 초보자가 사용하기에 좀 더 나아보였다.
(쓰다보니 궁금해서 좀 더 자세히 공부해서 글을 써봐야겠다)
그래서 위의 api 요청 및 json 변환 코드를 axios 사용법으로 바꿔보았다.

Axios - JSON Data 요청

export async function getXMLMidTiaFcstAxios(cityCode, baseDateTime) {
  const result = await axios({
    method: 'get',
    url: 'http://apis.data.go.kr/1360000/MidFcstInfoService/getMidTa',
    headers: { Accept: '*/*' },
    params: {
      serviceKey: SERVICE_KEY,
      numOfRows: 10,
      pageNo: 1,
      dataType: 'JSON',
      regId: cityCode,
      tmFc: baseDateTime,
    },
  });
  return result.data.response.body.items

fetch로 API를 호출하는 방법보다 좀 더 내가 요청을 보내는 부분이 명확해 보인다는 생각이 들었다.
그리고 사용법도 생각보다 어렵지 않았는데 살펴보면 요청 주소를 보내기 위한 url 뒤쪽으로 params 키와 값들이 나열되어 요청을 보내진다.
만약 내가 요청하는 API의 데이터 타입이 JSON을 지원한다면 위의 코드로 바로 JSON 데이터를 받아서 바로 사용할 수 있을 것이다.

Axios - XML Data 요청 후 변환

만약! XML만 지원하는 API라고 한다면 XmlToJson 라이브러리가 많으니 선택해서 사용하거나 위에 적혀있던 xmlToJson 함수를 사용하여 변환하면 된다.
나의 경우에도 일출, 일몰 데이터 APIXML 타입만 지원한다길래.. axios로 호출하고 위의 코드를 이용하여 변환하였다.

export async function getLCRiseSetInfo(lonString, latString, dateString) {
  const result = await axios({
    method: 'get',
    url: 'http://apis.data.go.kr/B090041/openapi/service/RiseSetInfoService/getLCRiseSetInfo',
    headers: { Accept: '*/*' },
    params: {
      serviceKey: SERVICE_KEY,
      longitude: lonString,
      latitude: latString,
      locdate: dateString,
      dnYn: 'Y',
    },
  });

  const xmlNode = new DOMParser().parseFromString(result.data, 'text/xml');
  const resultJSON = xmlToJson(xmlNode);
  return resultJSON.response.body.items.item;
}

위의 fetch 코드보다 좀 길어진 것 같지만, 데이터 요청 부분에 대한 코드 가독성이 더 괜찮아진 것 같다는 느낌이다. (어떤 method를 사용하여 어떤 데이터 요청을 params로 보내고 있지?..)

그리고 리팩토링 하던 도중에 문득 headers 부분의 기호가 무엇을 뜻하는지.. 갑자기 궁금해졌는데..

headers : { Accept: '*/*' } 

header : { Accept } 는 MIME 타입으로 표현되는 클라이언트가 이해 가능한 컨텐츠 타입이 무엇인지 알려주는 것이라고 한다. 위와 같은 기호는 모든 타입을 뜻한다.

MIME ?
클라이언트에게 전송된 문서의 다양성을 알려주기 위한 메커니즘이다.
웹에선 파일의 확장자가 의미가 없다보니 각 문서와 함께 올바른 타입을 전송하도록 서버가 정확하게 설정하는 것이 중요하다.

서버에 요청또는 응답 시 미리 타입을 알고 있으면 브라우저가 타입에 맞는 준비를 한다는 뜻이었다. 그래서 쌩뚱맞은 타입인 image/text이런식으로 기입해보니 500 (Internal Server Error) 이런 에러가 발생했다.
기본 타입으론 type/subtype을 사용한다고 하며, 대부분 이 타입으로 작성하면 문제가 없는 듯 하다.

MIME 타입은 굉~~장히 많았는데 호옥시 궁금하다면 문서를 참고해주시길!
MIME Type - MDN

끝으로

지금까지 API를 처음 사용했을 때 상황부터 지금 다시 약간의 리팩토링을 거친 부분에 대해 주절주절 떠들어보았다.
그래도 이러한 경험이 있었으니 다음 프로젝트나 실무에 API를 사용한다면 겁 먹지 않고 차분히 해낼 수 있을 것 같다는 생각이 들었다.

.
.
.
.
.

참고 사이트
REST API란? - AWS
REST(GET, POST) 비교
Accept - MDN
MIME - MDN

profile
습관을 들이도록 노력하자!

0개의 댓글