요즘 공모전을 보면 공공데이터를 활용하는 것들이 많다. 이 공공데이터는 데이터 자체를 이용할 수도 있지만, API를 이용하여 데이터를 서버에서 얻어올 수 있다.
PHP
에서는 JavaScript
의 fetch API
와 유사한 기능을 가진 cURL(Client URL Library)
가 존재한다.
이번 포스트에서는 식품의약품안전처 데이터활용서비스
의 식품영양성분DB(NEW)
API를 PHP
에서 사용해보도록 하겠다.
API
사용 준비식품 안전 나라 홈페이지에서 식품영양성분DB(NEW)
를 검색하면 해당 API
를 사용할 수 있다. OPEN-API
란의 API
인증키를 요청하는 버튼 OpenAPI 이용 신청 이 있다. 회원가입 및 로그인 후 이 버튼을 클릭해서 API
인증키를 발급받을 수 있다.
API
인증키는 홈페이지 상단의 인증키 관리
-> 인증키 신청 현황
에서 인증키 발급 정보 및 API
신청 목록 조회가 가능하다.
여기서 발급된 인증키를 복사해서 코드에서 사용할 것이다.
해당 API
에 대해 알아보면, URL
형식으로만 불러오므로 GET
요청으로 데이터를 받아올 수 있다.
Client Side
에서 JavaScript
코드로 API
를 사용하는 경우 CORS
때문에 요청이 불가능할 수 있다. 그렇기 때문에 PHP
를 사용해 Server Side
에서 API
를 사용하는 것이다.
요청인자의 keyId
는 요청주소의 샘플에 나온 I2790
이다.
fetch API
를 이용한 Ajax
클라이언트에서 데이터 요청을 하면 PHP
에 fetch API
를 이용해서 비동기적으로 요청을 한다(Ajax
).
위에 첨부한 이미지를 잘 보면,
필수 요청인자들은 /인자/인자/...
이런식으로 URL
에 붙이고, 선택 요청인자들은 Query String
으로 매개변수를 넣어주는 식으로 되어있다.
요청인자들은 객체에 담아서 이용하기 편하게 했다.
const essential_params = { // 필수 요청 인자들
keyId: '자신의 API 인증키 입력',
serviceId: 'I2790', // 샘플에 나와있음
dataType: 'json', // 중요!! 받은 데이터 object로 파싱하기
startIdx: 1,
endIdx: 15
};
// 필수 인자들 "/../../.." 형식으로 url에 붙여주기
let url = `http://openapi.foodsafetykorea.go.kr/api/`
for (const key in essential_params) {
url += `${essential_params[key]}/`
}
// 선택 요청 인자들
const params = {
DESC_KOR: '',
RESEARCH_YEAR: '',
MAKER_NAME: '',
FOOD_CD: '',
}
// fetch API에서 사용할 request body
// PHP에서 $객체->url, $객체->params 형태로 사용된다.
const request_body = {
url: url,
params: params
}
// fetch API에 넣을 init 객체
const init = {
method: 'POST',
mode: 'cors', // 기본값이 cors이다.
headers: { // json 형식과 인코딩 명시
'Content-type': 'application/json; charset=utf-8',
},
body: JSON.stringify(request_body), // json으로 파싱!!
}
fetch('api.php', init)
.then((res) => res.json()) // json을 돌려주므로 json()을 이용해 객체로 파싱
.then((data) => { // 이 내용은 바로 아래에서 설명
const res_obj = data.I2790;
console.log(res_obj.RESULT);
console.log(res_obj.row);
});
Postman
을 이용한 API
테스트이 프로그램을 이용하면 아주 간편하게 API
를 테스트할 수 있다.
Workspaces
-> 왼쪽의 Collections
-> +
를 눌러 새로운 Collection
생성 -> Collection
이름 옆의 ...
누르고 Add request
METHOD
의 DEFAULT
값은 GET
이다.
위의 첨부된 API
요청 주소 이미지를 참고해서 URL
을 넣어서 Send
하면 결과를 표시해준다.
받은 데이터는 JSON
형태로 값은 다음과 같다. 맨 밑줄 ...
는 생략
{
"I2790": {
"RESULT": {
"MSG": "정상처리되었습니다.",
"CODE": "INFO-000"
},
"total_count": "59886",
"row": [
{
"NUTR_CONT3": "33.5",
"NUTR_CONT2": "39.7",
"NUTR_CONT1": "368.8",
"SERVING_SIZE": "500",
"MAKER_NAME": "",
"NUTR_CONT9": "0.1",
"NUTR_CONT8": "1.9",
"FOOD_CD": "D000006",
"NUTR_CONT7": "106.18",
"NUTR_CONT6": "1264.31",
"NUTR_CONT5": "16.9",
"NUTR_CONT4": "8.5",
"DESC_KOR": "꿩불고기",
"SAMPLING_MONTH_NAME": "평균",
"SUB_REF_NAME": "식약처('16) 제4권",
"SAMPLING_REGION_NAME": "충주",
"GROUP_NAME": "",
"RESEARCH_YEAR": "2019",
"SAMPLING_REGION_CD": "94",
"SAMPLING_MONTH_CD": "AVG",
"NUM": "1"
},
...
이것을 통해 JavaScript
코드의 하단에 있던 fetch API
에서 다음과 같이 사용한 이유가 나온다.
fetch('api.php', init)
.then((res) => res.json()) // json을 돌려주므로 json()을 이용해 객체로 파싱
.then((data) => { // 이 내용은 바로 아래에서 설명
const res_obj = data.I2790;
console.log(res_obj.RESULT);
console.log(res_obj.row);
});
맨 처음 API
아이디(keyId
)가 나오고 그 아래 RESULT
, total_count
, row
가 있고, row
안에 데이터가 들어있는 형태이다.
그래서 data.I2790
을 res_obj
에 저장한 후 RESULT
와 row
를 출력한 것이다. 콘솔창을 확인해보면 다음과 같이 출력된다.
cURL - Client URL Library
PHP는 Daniel Stenberg가 만든 라이브러리인 libcurl을 지원하므로 다양한 유형의 프로토콜을 사용하여 다양한 유형의 서버에 연결하고 통신할 수 있습니다. libcurl은 현재 http, https, ftp, gopher, telnet, dict, file 및 ldap 프로토콜을 지원합니다. libcurl은 또한 HTTPS 인증서, HTTP POST, HTTP PUT, FTP 업로드(PHP의 ftp 확장으로도 수행 가능), HTTP 양식 기반 업로드, 프록시, 쿠키 및 사용자+암호 인증을 지원합니다.
위에 서술된 여러 프로토콜로 데이터를 create
, read
, update
, delete
할 수 있는 라이브러리다.
Basic curl example - PHP document
$ch = curl_init("http://www.example.com/"); $fp = fopen("example_homepage.txt", "w"); curl_setopt($ch, CURLOPT_FILE, $fp); curl_setopt($ch, CURLOPT_HEADER, 0); curl_exec($ch); if(curl_error($ch)) { fwrite($fp, curl_error($ch)); } curl_close($ch); fclose($fp);
curl_init()
: cURL session
초기화curl_setopt()
: 전송을 위한 옵션 지정curl_exec()
: session
실행curl_close()
: session
종료cURL
을 이용해 PHP
에서 API
사용앞의 JavaScript
코드로 Ajax
로 PHP
에 요청 시 해당 데이터를 가지고 API
를 사용하는 코드이다.
여기서 http_build_query()
함수가 사용되었는데, 객체의 내용을 Query String
으로 변경해주는 함수이다. 특정 반복문 필요 없이 간단하게 이 함수만 호출하면 되므로 매우 유용하다.
이를 이용하기 위해서 JS
에서 보낸 선택 요청 인자들을 객체로 저장해둔 것이다.
function empty_filter($obj) { // 선택 인자의 값이 없으면("") 제거
foreach ($obj as $key => $value) {
if (empty($value)) {
unset($obj->$key);
}
}
return $obj;
}
// fetch API로 보낸 request_body는 json 형태이므로 파싱 해서 PHP 객체로 사용
$ajax_data = json_decode(file_get_contents('php://input'));
// 위의 필터링 함수를 사용해 불필요한 값 제거
$filtered_params = empty_filter(array($ajax_data->params)[0]);
// http_build_query() 함수로 객체를 QueryString으로 만들어준다.
$query = http_build_query($filtered_params);
// 선택 요청 인자가 존재하는 경우 붙여주기
// 존재하지 않는다면 url은 그대로이다.
$url = $ajax_data->url .= $query ? '?'.$query : '';
// curl에 적용할 옵션들을 저장
$options = array(
CURLOPT_URL => $url, // 최종 url도 이때 넣어준다.
CURLOPT_RETURNTRANSFER => true, // 반환된 값을 string으로 변환해 저장
CURLOPT_SSL_VERIFYPEER => false, // true인 경우 https 통신이 불가한 경우 발생
);
// curl 세션 초기화
$ch = curl_init();
// 이것도 유용한 함수 array를 받아서 한번에 옵션을 지정
curl_setopt_array($ch, $options);
// 실행 -> API 서버에서 반환한 데이터를 $response 변수에 저장한다.
$response = curl_exec($ch);
// 세션 종료
curl_close($ch);
echo $response; // JSON 형태이므로 JavaScript에서 json()을 통해 파싱
http_build_query()
URL
에 Query String
을 넣어줄 때, 반복문이나 하드코딩 없이 http_build_query()
함수를 사용해서 매우 간편하게 넣어줄 수 있다.
key
, value
쌍을 array
객체에 저장하고 이것을 함수에 넣어주면 키=값&키=값&키=값&...
형태의 Query String
으로 변환되는 것이다.
curl_setopt_array()
인터넷에서 여러 코드들을 보면 curl
옵션 지정 시 이렇게 사용한다.
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
깔끔해보이지 않고, 여러 옵션들을 한번에 적용하고 싶어서 알아보니 curl_setopt_array()
가 존재했다.
이를 사용하면 설정할 옵션들을 한 변수에서 관리 가능하며, 옵션 적용도 한줄이면 되니 매우 간편하고 가독성도 증가했다.
// curl에 적용할 옵션들을 저장
$options = array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
);
$ch = curl_init();
// array를 받아서 한번에 옵션을 지정
curl_setopt_array($ch, $options);
이렇게 말이다.
유지보수와 가독성을 위해서 간단하고 명확한 코드 사용은 중요하다 생각한다. 비록 이건 큰 차이점이 없어보이지만 다음 상황에서는 큰 차이를 보일 수 있다.
옵션 추가 및 삭제를 자주 하는 경우
옵션에 대한 데이터를 객체로 받아오는 경우
데이터를 포함한 객체의
key
,value
를array
객체에 저장하고curl_setopt_array()
에array
객체 넣기
코드의 길이가 증가하는 경우 가독성을 유지하기 위해
CORS
때문에 API
사용(호출)은 서버쪽에서 진행 (여기서는 PHP)
API
사용 시 필요한 데이터는 객체로 저장하여 보내기 (코드 간결 & 가독성 증가)
쿼리 스트링은 http_build_query()
사용하기
cURL
의 옵션 설정은 옵션들을 array
에 저장 후 curl_setopt_array()
사용하기
API
예제 repository