외부 API를 호출하거나 다른 서버의 페이지를 긁어올 때 PHP에서 가장 무난한 선택은 curl입니다. file_get_contents로도 URL을 열 수는 있지만, 호스팅에 따라 allow_url_fopen이 꺼져 있어서 아예 안 되는 경우가 많고, 타임아웃이나 헤더를 세밀하게 잡기도 어렵습니다. 그래서 GET용, POST용 함수를 하나씩 만들어 두고 돌려 쓰는 편이 편합니다.
GET과 POST, 실무에서의 차이
GET은 보낼 값을 주소 뒤에 ?a=1&b=2처럼 붙입니다. 주소창에 그대로 보이고, 북마크나 로그에도 남습니다. POST는 값을 요청 본문에 담아 보내서 주소에는 드러나지 않습니다. HTML 폼을 method="post"로 전송하는 것과 같은 방식이고, 브라우저 개발자 도구의 네트워크 탭을 열어야 내용이 보입니다.
"POST는 안 보이니까 안전하다"고 오해하기 쉬운데, 보이지 않을 뿐 암호화되는 건 아닙니다. 비밀번호나 개인정보를 보낸다면 HTTPS가 기본이고, 조회처럼 서버 상태를 바꾸지 않는 요청은 GET, 저장·수정처럼 상태를 바꾸는 요청은 POST로 나누는 게 일반적입니다. 상대 API가 어느 방식을 요구하는지가 결국 기준이 됩니다.
함수 코드
<?
// GET 방식 함수
function get($url, $params=array())
{
$url = $url.'?'.http_build_query($params, '', '&');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
// get함수 호출
get('http://itfresh.tistory.com', array('param1'=>'value1', 'param2'=>'value2'));
// POST 방식 함수
function post($url, $fields)
{
$post_field_string = http_build_query($fields, '', '&');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_field_string);
curl_setopt($ch, CURLOPT_POST, true);
$response = curl_exec($ch);
curl_close ($ch);
return $response;
}
// post함수 호출
post('http://itfresh.tistory.com', array('field1'=>'value1', 'field2'=>'value2'));
?>
두 함수 모두 배열로 받은 값을 http_build_query로 param1=value1¶m2=value2 형태의 문자열로 바꿉니다. 한글이나 특수문자도 이 함수가 URL 인코딩을 해 주기 때문에 직접 urlencode를 돌릴 필요가 없습니다. GET은 그 문자열을 주소 뒤에 붙이고, POST는 CURLOPT_POSTFIELDS에 넣은 뒤 CURLOPT_POST를 켭니다.
CURLOPT_RETURNTRANSFER를 빼먹으면
처음 curl을 쓸 때 가장 많이 당하는 부분입니다. 이 옵션이 없으면 curl_exec가 받은 응답을 변수에 돌려주지 않고 그대로 화면에 출력해 버립니다. 반환값은 성공 여부(true/false)만 남고요. 응답 JSON을 파싱하려는데 $response에 1만 들어 있고 페이지에는 엉뚱한 내용이 찍혀 있다면 거의 이 옵션 문제입니다. 결과를 변수로 받아서 가공할 생각이라면 반드시 true로 켜 둡니다.
그대로 쓰기 전에 손볼 곳
첫 줄의 <?는 short open tag라서 서버 설정에서 short_open_tag가 꺼져 있으면 PHP 코드로 인식되지 않고 소스가 그대로 출력됩니다. 새로 쓸 때는 <?php로 바꾸는 게 안전합니다.
CURLOPT_SSL_VERIFYPEER를 false로 두면 인증서 검증을 건너뜁니다. 로컬 개발 환경에서 인증서 오류 때문에 막힐 때 임시로 쓰기는 좋지만, 운영에서 결제나 인증 API를 이렇게 호출하면 중간자 공격에 그대로 노출됩니다. 운영 서버에서는 CA 인증서 경로를 제대로 잡고 이 줄을 빼는 쪽을 권합니다.
또 PHP 8.0부터 curl 핸들이 객체로 바뀌면서 curl_close는 사실상 아무 일도 하지 않고, 최신 버전에서는 사용 중단 경고 대상이 됐습니다. 7.x와 8.x를 같이 지원해야 한다면 그냥 두어도 동작에는 문제가 없지만, 경고 로그가 거슬리면 빼도 됩니다.
실제 코드에서는 여기에 curl_error($ch)와 curl_getinfo($ch, CURLINFO_HTTP_CODE)를 추가해서 실패했을 때 원인과 HTTP 상태 코드를 남겨 두면 디버깅이 훨씬 수월합니다. JSON을 보내야 하는 API라면 http_build_query 대신 json_encode로 본문을 만들고 Content-Type: application/json 헤더를 CURLOPT_HTTPHEADER로 넣어 주면 됩니다.
참고: itfresh 티스토리