📝

비동기 통신 실습 : 공공 API를 활용한 리스트 기능 구현

 

1. 비동기 통신을 위한 준비

1.1 공공데이터 포털 사이트에서 API 사전 준비

1.1.1 공공데이터란?

초창기 코로나 바이러스로 인하여 전 세계가 떠들썩하던 때가 있었다. 당시 많은 사람이 마스크를 구하기 위하여 마스크 재고가 있는 약국을 찾아 동네방네 돌아다녔다. 이때 어떤 사이트는 약국과 마트에 남은 마스크 재고를 알려주는 서비스를 운영했고 덕분에 그 사이트의 이용자들은 헛걸음하지 않고 마스크를 좀 더 편하게 구할 수 있었다. 이 사이트가 마스크 재고를 알 수 있었던 이유는 바로 “마스크 재고”라는 공공데이터의 Open API를 활용했기 때문이다.
공공데이터란 공공기관이 만들어내는 모든 자료나 정보, 국민 모두의 소통과 협력을 이끌어낸 공적인 정보를 말한다. 각 공공기관이 보유한 공공데이터 목록과 국민에게 개방할 수 있는 공공데이터를 포털에 등록하면 모두가 공유할 수 있는 양질의 공공데이터로 재탄생하게 된다.
공공데이터 포털 사이트에서 제공하는 공공데이터 Open API를 활용하여 각 공공기관에서 많은 리소스를 들여 모은 데이터를 손쉽게 활용해 보는 실습 사이트를 만들어 볼 것이다. 이 실습을 마친 후 방대한 공공데이터를 활용하여 재밌고 색다른 서비스를 만드는 것도 가능할 것이다.

1.1.2 공공데이터 포털 사이트

아래 사이트를 통하여 다양한 공공기관의 공공데이터를 활용할 수 있다.
우리는 부산광역시의 공공데이터 Open API를 활용하여 부산 맛집 리스트를 불러오는 실습을 하려고 한다.

1.1.3 실습 전 주의사항

실습에 앞서 우리가 사용할 프로그래밍 언어는 JavaScript이며 활용할 데이터 형식은 JSON이다. 본 실습에서는 다른 프로그래밍 언어나 XML 데이터 형식은 사용하지 않으니 주의하기 바란다.

2. API 실습

2.1 공공데이터 포털 사이트에서 데이터 사용 승인받기

먼저 로그인을 한 후, 공공데이터 포털에 들어가 부산 맛집 정보 서비스를 검색한다.
notion imagenotion image
 
오픈 API 란에서 XMLJSON 2가지 데이터 형태를 제공하는 ‘부산광역시_부산 맛집 정보 서비스’를 찾을 수 있다. 해당 서비스를 클릭하여 오픈 API 상세 페이지로 들어간다.
 
notion imagenotion image
 
데이터의 종류에 따라 활용신청을 해야 하기도 하는데, 이때 활용 신청란을 클릭하여 데이터 활용 신청을 해주면 된다. 활용 목적을 간단하게 기입한 후 라이선스 동의란을 체크하고 활용신청 버튼을 클릭하면 별도로 기다리는 시간 없이 바로 활용 승인이 난다.
자세한 내용은 한국지능정보사회진흥원의 안내 동영상을 참고하면 쉽게 따라 할 수 있다.
 

2.2 공공데이터 샘플코드 활용방법

활용 승인이 된 데이터의 상세 설명 페이지에 들어가면 “요청 주소”, 요청 변수(Request Parameter)”, “출력 결과(Response Element)”, “샘플 코드”를 확인할 수 있다. 우리는 JavaScript 샘플 코드를 활용하여 부산 맛집 정보를 받아오도록 할 것이다.
var xhr = new XMLHttpRequest(); var url = 'http://apis.data.go.kr/6260000/FoodService/getFoodKr'; /*URL*/ var queryParams = '?' + encodeURIComponent('serviceKey') + '='+'서비스키'; /*Service Key*/ queryParams += '&' + encodeURIComponent('pageNo') + '=' + encodeURIComponent('1'); queryParams += '&' + encodeURIComponent('numOfRows') + '=' + encodeURIComponent('10'); queryParams += '&' + encodeURIComponent('resultType') + '=' + encodeURIComponent(''); queryParams += '&' + encodeURIComponent('UC_SEQ') + '=' + encodeURIComponent(''); 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('');
먼저 복사해온 샘플 코드에서 Fetch를 이용한 API 통신 실습에 필요한 코드만 사용하도록 하겠다.
var xhr = new XMLHttpRequest(); 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('');
XMLHttpRequest은 서버와 상호작용(동기/비동기) 하기 위하여 사용하는 객체이다. 하지만 앞서 설명했듯이 우리가 활용할 예제는 Fetch이므로 언급하지 않은 XMLHttpRequest를 활용하는 상단의 코드는 본 실습에서 사용하지 않는다.
var url = 'http://apis.data.go.kr/6260000/FoodService/getFoodKr'; /*URL*/ var queryParams = '?' + encodeURIComponent('serviceKey') + '='+'서비스키'; /*Service Key*/ queryParams += '&' + encodeURIComponent('pageNo') + '=' + encodeURIComponent('1'); queryParams += '&' + encodeURIComponent('numOfRows') + '=' + encodeURIComponent('10'); queryPar ams += '&' + encodeURIComponent('resultType') + '=' + encodeURIComponent(''); queryParams += '&' + encodeURIComponent('UC_SEQ') + '=' + encodeURIComponent('');
우리가 사용할 코드는 위와 같다. 먼저 여기서 ‘서비스키’에 해당하는 부분은 공공데이터 포털사이트의 마이페이지에서 확인한 후 복사해 붙여 넣으면 된다. 마이페이지의 일반 인증키는 인코딩(Encoding)과 디코딩(Decoding) 두 종류가 있다. 우리는 encodeURIComponent 로 인코딩 된 요청변수를 받고 있다. 따라서 우리는 인코딩된 요청변수를 디코딩 해주어야 하기 때문에 디코딩 하는 서비스키를 사용해야 한다.
notion imagenotion image
 
이때 아래에 있는 pageNo ,numOfRows, resultType 은 공공데이터 포털 사이트의 오픈 API 상세 페이지에서 설명하고 있는 요청변수들이다.
notion imagenotion image
//공공 데이터 가져오기 let queryParams = `?${encodeURIComponent( "serviceKey" )}=${"서비스키"}`; // 마이페이지에서 일반 인증키(decoding)를 받아옵니다. queryParams += `&${encodeURIComponent("pageNo")}=${encodeURIComponent("1")}`; queryParams += `&${encodeURIComponent("numOfRows")}=${encodeURIComponent( "50" )}`; queryParams += `&${encodeURIComponent("resultType")}=${encodeURIComponent( "json" )}`;
따라서 요청변수에 나와 있는 항목설명을 보고 위와 같이 요청하고자 하는 데이터의 내용과 형식을 작성하면 된다.앞선 Fetch 예제에서 사용했던 방법을 통하여 비동기통신을 하여 공공 데이터 API를 불러보겠다. 이때 공공데이터에서 출력하고 싶은 데이터의 항목명을 작성하면 원하는 데이터를 출력할 수 있다. 항목명은 마찬가지로 오픈 API 상세페이지에 나와 있는 출력 결과(Response Element)표를 참고한다.
notion imagenotion image
// 공공 데이터 가져오기 let queryParams = `?${encodeURIComponent( "serviceKey" )}=${"서비스키"}`; // 마이페이지에서 일반 인증키를 받아옵니다. queryParams += `&${encodeURIComponent("pageNo")}=${encodeURIComponent("2")}`; queryParams += `&${encodeURIComponent("numOfRows")}=${encodeURIComponent( "50" )}`; queryParams += `&${encodeURIComponent("resultType")}=${encodeURIComponent( "json" )}`; // 책 앞부분의 Fetch 예제를 이용하여 데이터 가져오기 async function getData() { try { const foods = await fetch( `http://apis.data.go.kr/6260000/FoodService/getFoodKr${queryParams}` ).then((res) => res.json()); const foodList = foods.getFoodKr.item; foodList.map((item, index) => { console.log( `${index + 1}번째 맛집은 ${item.MAIN_TITLE}입니다. 주소: ${item.ADDR1} 전화번호: ${item.CNTCT_TEL} 설명: ${item.ITEMCNTNTS} 대표 이미지: ${item.MAIN_IMG_NORMAL}` ); }); } catch (err) { console.error(err); } } getData();
해당 코드를 실행하면 비동기 통신이 성공적으로 실행되며 공공데이터에서 다음과 같은 내용을 출력 받을 수 있다.
notion imagenotion image

2.3 실습

앞에서 설명했던 API 활용 방법을 참고하여 원하는 API를 받아와 직접 구현한 UI에 적용해보려고 한다. API 결과물에 대한 key 값은 다음과 같이 나타나도록 하였다.
notion imagenotion image
API 응답 결과API 응답 결과
API 응답 결과
pageNo ,numOfRows, resultType 요청변수들을 사용하여 발급받은 서비스 키를 적용한다.
let queryParams = `?${encodeURIComponent( "serviceKey" )}=${"서비스키"}`; // 마이페이지에서 일반 인증키를 받아옵니다. queryParams += `&${encodeURIComponent("pageNo")}=${encodeURIComponent("1")}`; queryParams += `&${encodeURIComponent("numOfRows")}=${encodeURIComponent( "50" )}`; queryParams += `&${encodeURIComponent("resultType")}=${encodeURIComponent( "json" )}`;
Fetch를 이용하여 공공 데이터 API를 가져왔다.
const dataObj = { datasPerPage: 9, currentPage: 1, datas: null, }; window.addEventListener("load", () => { init(); }); // 책 앞부분의 Fetch 예제를 이용하여 데이터 가져오기 async function init() { try { const foods = await fetch( `http://apis.data.go.kr/6260000/FoodService/getFoodKr${queryParams}` ).then((res) => res.json()); dataObj.datas = foods.getFoodKr.item; console.log(dataObj.datas); // 콘솔로 데이터 확인 loadPage(1); } catch (err) { console.error(err); } }
성공적으로 통신이 이루어지면 console에 응답받은 데이터를 찍어 어떤 형식으로 받아왔는지 확인할 수 있다. 데이터에 있는 key 값을 통해 원하는 value 값을 출력할 수 있다.
notion imagenotion image
받아 온 데이터를 for 문으로 순환하여 HTML 안에 원하는 데이터의 key 값을 넣어준다.
// 페이지를 로딩하는 함수입니다. function loadPage(page) { dataObj.currentPage = page; let startPost = (dataObj.currentPage - 1) * dataObj.datasPerPage; let totalPages = Math.ceil(dataObj.datas.length / dataObj.datasPerPage); let endPost = startPost + dataObj.datasPerPage > dataObj.datas.length ? dataObj.datas.length : startPost + dataObj.datasPerPage; output.innerHTML = ""; // 받아온 데이터를 순환합니다. for (let i = startPost; i < endPost; i++) { console.log(dataObj.datas[i]); let item = dataObj.datas[i]; let card = document.createElement("article"); card.innerHTML = ` <div class="card"> <span class="gu-name" id="gugun_name">${item.GUGUN_NM}</span> <img src=${item.MAIN_IMG_NORMAL} alt="맛집 사진" class="main-img" id="main-img-thumb" > <div class="main-info"> <strong class="main-title" id="main-title">${item.MAIN_TITLE}</strong> <strong class="main-tel" id="cntct-tel">${item.CNTCT_TEL}</strong> </div> <p class="content" id="itemcntnts">${item.ITEMCNTNTS}</p> <dl class="info-list"> <div class="info-wrap"> <dt class="info-addr">주 소</dt> <dd id="addr1">${item.ADDR1}</dd> </div> <div class="info-wrap"> <dt>영업시간</dt> <dd id="usage-day-week-and-time">${item.USAGE_DAY_WEEK_AND_TIME}</dd> </div> <div class="info-wrap"> <dt>대표메뉴</dt> <dd id="rpesntv-menu">${item.RPRSNTV_MENU}</dd> </div> </dl> <img class="like-wrap" src="./src/images/icon/heart-full.svg" alt="좋아요"> </div> `; output.append(card); } paginationMaker(totalPages); }
※ 직관적으로 보여주기 위해 innerHTML을 사용하였다.
결과는 다음과 같이 부산 맛집 리스트를 성공적으로 불러온 것을 확인할 수 있다.
notion imagenotion image
자세한 코드는 아래 깃허브 주소로 들어가서 참고할 수 있다.
 

참고 자료
  1. https://www.data.go.kr/data/15063472/openapi.do