[JS] 공공데이터포털 API 사용하기 : XMLhttp, Fetch, Axios 방법으로 리팩토링하기

2024. 8. 15. 13:20개발 회고록 : FrontEnd

2024.08 기준, 공공데이터포탈에서 제공하는 코드 예제

/* Javascript 샘플 코드 */
var xhr = new XMLHttpRequest();

var url = 'http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst'; /*URL*/
var queryParams = '?' + encodeURIComponent('serviceKey') + '='+'서비스키'; /*Service Key*/
queryParams += '&' + encodeURIComponent('pageNo') + '=' + encodeURIComponent('1'); /**/
queryParams += '&' + encodeURIComponent('numOfRows') + '=' + encodeURIComponent('1000'); /**/
queryParams += '&' + encodeURIComponent('dataType') + '=' + encodeURIComponent('XML'); /**/
queryParams += '&' + encodeURIComponent('base_date') + '=' + encodeURIComponent('20210628'); /**/
queryParams += '&' + encodeURIComponent('base_time') + '=' + encodeURIComponent('0600'); /**/
queryParams += '&' + encodeURIComponent('nx') + '=' + encodeURIComponent('55'); /**/
queryParams += '&' + encodeURIComponent('ny') + '=' + encodeURIComponent('127'); /**/

xhr.open('GET', url + queryParams);
xhr.onreadystatechange = function () {
    if (this.readyState == 4) {
        alert(
        'Status: '+this.status
        +'nHeaders: '+JSON.stringify(this.getAllResponseHeaders())
        +'nBody: '+this.responseText
        );
    }
};

xhr.send('');

제공된 예제는 복잡하다. XMLhttp은 어느 브라우저에서나 사용가능하지만 코드가 길고 비동기 지원이 되지 않아 콜백지옥이 발생한다.

* 콜백지옥: 함수 안에 함수가 중복되어 코드가 복잡해지는 코드 형태

코드가 복잡해지는 문제는 fetch와 axios로 해결할 수 있다.

* Promise, async-await를 알고 있어야 아래 코드를 이해할 수 있음

그전에 ES6에 맞도록 url의 쿼리스트링 코드를 자바스크립트로 수정해 보자.

 

1. URI 

1.1. 쿼리스트링이란

* 쿼리스트링
인터넷 주소(URI)에서 물음표 뒤에 있는 문자열

* URI
인터넷 주소라는 의미가 'URL'과 상충되어 'Locator'(리소스의 위치) 보다 포괄적인 'Identifier'(리소스 식별자)로 바뀌었다.

* 물음표(?)는 쿼리스트링 시작, 등호(=)는 key-value 선언, 앰퍼샌드(&)는 key-value 연결고리

예시)
www.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=0&ie=utf8&query=맛집

- 쿼리스트링: ?where=nexearch&sm=top_hty&fbm=0&ie=utf8&query=맛집
- key-value: where=nexearch, sm=top_hty, fbm=0&ie=utf8, query=맛집

 

1.1. URI  쿼리스트링 생성 코드

// 1번

let queryString = '?' + 'serviceKey' + '='+`${serviceKey}`; /*Service Key*/
queryString += '&' + 'pageNo' + '=' + '1';
queryString += '&' + 'numOfRows' + '=' + '1000';
queryString += '&' + 'dataType' + '=' + 'XML';
queryString += '&' + 'base_date' + '=' + '20210628';
queryString += '&' + 'base_time' + '=' + '0600';
queryString += '&' + 'nx' + '=' + '55';
queryString += '&' + 'ny' + '=' + '127';

// 변수명 queryParams를 queryString으로 수정.
// 2번

const SERVICE_KEY = 'Your service key'; /*Service Key*/
const queryParams = {}; // 객체 선언
queryParams['serviceKey'] = SERVICE_KEY;
queryParams['pageNo'] = '1'
queryParams['numOfRows'] = '1000'
queryParams['dataType'] = 'XML'
queryParams['base_date'] = '20210628'
queryParams['base_time'] = '0600'
queryParams['nx'] = '55'
queryParams['ny'] = '127'

let queryString = Object.keys(queryParams).reduce((previous, current) => (
	previous + current + '=' + queryParams[current] + '&'
), '?');
queryString = queryString.substring(0, queryString.length - 1); // 마지막 '&' 제거

콘솔로그를 실행하면 1, 2번 쿼리스트링 결과는 서로 똑같다. 쿼리스트링의 조건에 맞춰서 이외 다른 방법으로도 쿼리스트링을 생성할 수 있다.

 

2. 호출 방법들 (Fetch, Axios)

2.1. API Fetch 적용하는 방법

// fetch
  
(async () => {
  try {
    const response = await fetch(url + queryString);
    const data = await response.json();
    // console.log(data); 확인가능, 즉시실행 함수 밖에서 확인 불가

    if (data.response.header.resultCode !== '00') {
      // 에러 문구 'Error Code (숫자코드), (오류내용)'
      // 해당 OpenAPI 정보 참고자료에서 오류코드 확인가능
      throw new Error(
      `Code ${data.response.header.resultCode}, 
      ${data.response.header.resultMsg}`
      );
    }
  } catch (err) {
    console.error('first_api.js', err);
  }
})();

// (() => {})(); 즉시함수
// function으로 감싸주어 async function으로 사용 가능

별도 설치 없이 fetch를 불러올 수 있어서 사용성이 좋다. try-catch로 오류를 잡을 수 있지만 http 오류는 별도로 확인해야 한다.

 

2.2. API Axios 적용하는 방법

// axios
import axios from 'axios'; // npm i axios
  
(async () => {
  try {
    const response = await axios.get(url + queryString);
    // console.log(response); 확인가능, 즉시실행 함수 밖에서 확인 불가
    
    if (response.header.resultCode !== '00') {
      // 에러 문구 'Error Code (숫자코드), (오류내용)'
      // 해당 OpenAPI 정보 참고자료에서 오류코드 확인가능
      throw new Error(
      `Code ${data.response.header.resultCode}, 
      ${data.response.header.resultMsg}`
      );
    }
  } catch (err) {
    console.error(err);
  }
})();

// fetch와 다르게 response.json() 안 해도 response 바로 사용가능

axios는 npm으로 별도 설치해야 사용가능하다. 파일이 무겁지만 http 오류를 try-catch에서 잡을 수 있다.

 

3. .js 파일 분리

3.1. Fetch 파일 분리하기 - getAPIFetch.js, index.js

// getAPIFetch.js
async function getAPIFetch() {
  // URI
  const url = 'http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst';

  // QueryString
  const SERVICE_KEY = 'Your service key'; /*Service Key*/
  const queryParams = {}; // 객체 선언
  queryParams['serviceKey'] = SERVICE_KEY;
  queryParams['pageNo'] = '1'
  queryParams['numOfRows'] = '1000'
  queryParams['dataType'] = 'XML'
  queryParams['base_date'] = '20210628'
  queryParams['base_time'] = '0600'
  queryParams['nx'] = '55'
  queryParams['ny'] = '127'

  let queryString = Object.keys(queryParams).reduce((previous, current) => (
    	previous + current + '=' + queryParams[current] + '&'
  ), '?');
  queryString = queryString.substring(0, queryString.length - 1); // 마지막 '&' 제거
  
  // fetch
  try {
     const response = await fetch(url + queryString);
     const data = await response.json();

     if (data.response.header.resultCode !== '00') {
       // 에러 문구 'Error Code (숫자코드), (오류내용)'
       throw new Error(
       `Code ${data.response.header.resultCode}, 
       ${data.response.header.resultMsg}`
       );
     }
     return data; // API 응답 반환
   } catch (err) {
     console.error(err);
   }
}
// index.js
import getAPIFetch from './getAPIFetch.js';

getAPIFetch()
  .then((response) => {
    // response = getAPIFetch()에서 반환되는 값
    console.log('Fetch response', response);
   })

 

3.2 Axios 파일 분리하기 - getAPIAxios.js, index.js

// getAPIAxios.js
import axios from 'axios';

async function getAPIAxios() {
  // URI
  const url = 'http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst';

  // QueryString
  const SERVICE_KEY = 'Your service key'; /*Service Key*/
  const queryParams = {}; // 객체 선언
  queryParams['serviceKey'] = SERVICE_KEY;
  queryParams['pageNo'] = '1'
  queryParams['numOfRows'] = '1000'
  queryParams['dataType'] = 'XML'
  queryParams['base_date'] = '20210628'
  queryParams['base_time'] = '0600'
  queryParams['nx'] = '55'
  queryParams['ny'] = '127'

  let queryString = Object.keys(queryParams).reduce((previous, current) => (
    	previous + current + '=' + queryParams[current] + '&'
  ), '?');
  queryString = queryString.substring(0, queryString.length - 1); // 마지막 '&' 제거
  
  // axios
  try {
     const response = await fetch(url + queryString);

     if (data.response.header.resultCode !== '00') {
       // 에러 문구 'Error Code (숫자코드), (오류내용)'
       throw new Error(
       `Code ${data.response.header.resultCode}, 
       ${data.response.header.resultMsg}`
       );
     }
     
     return response; // API 응답 반환
   } catch (err) {
     console.error(err);
   }
}
// index.js
import getAPIAxios from './getAPIAxios.js';

getAPIAxios()
  .then((response) => {
    // response = getAPIAxios()에서 반환되는 값
    console.log('Axios response', response);
   })

 

참고자료
기상청_단기예보 ((구)_동네예보) 조회서비스 | 공공데이터포털 (data.go.kr)  
URI와 URL, 어떤 차이점이 있나요? | 이랜서 블로그 (elancer.co.kr) 

 

🙂 🙂