C언어에서 REST API를 사용하려면 HTTP 요청을 보내고 응답을 처리할 수 있는 라이브러리가 필요합니다. 대표적인 라이브러리로 libcurl이 있으며, 이를 활용하면 GET, POST, PUT, DELETE 등의 HTTP 요청을 손쉽게 수행할 수 있습니다.
본 기사에서는 libcurl을 사용하여 REST API 클라이언트를 구현하는 방법을 다룹니다. 기본적인 설치 방법부터 GET 및 POST 요청 처리, JSON 데이터 전송 및 응답 처리, 오류 디버깅 방법까지 상세히 설명합니다. 또한, 실제 REST API와 상호작용하는 예제를 제공하여 실전 활용에 도움을 드립니다.
이 글을 읽고 나면 C언어에서 REST API와 통신하는 클라이언트를 직접 구현할 수 있으며, 다양한 네트워크 기반 애플리케이션을 개발하는 데 필요한 기초 지식을 쌓을 수 있을 것입니다.
libcurl 소개 및 설치 방법
libcurl이란?
libcurl은 다양한 프로토콜(HTTP, HTTPS, FTP 등)을 지원하는 강력한 네트워크 통신 라이브러리입니다. C언어로 작성된 프로그램에서 쉽게 REST API와 통신할 수 있도록 도와줍니다. libcurl을 사용하면 GET, POST, PUT, DELETE와 같은 HTTP 요청을 쉽게 보낼 수 있으며, SSL/TLS 인증, 사용자 인증, 쿠키 관리 등의 기능도 제공합니다.
libcurl의 주요 기능
- 다양한 프로토콜 지원: HTTP, HTTPS, FTP, SCP, SMTP 등
- HTTP 요청 지원: GET, POST, PUT, DELETE, PATCH
- SSL/TLS 지원: HTTPS 요청 가능
- 사용자 인증: 기본 인증(Basic Auth), 토큰 인증(Bearer Token)
- 쿠키 및 세션 관리
- 프록시 서버 지원
libcurl 설치 방법
1. 리눅스(Ubuntu/Debian)
리눅스에서는 apt
패키지 관리자를 이용해 libcurl을 쉽게 설치할 수 있습니다.
sudo apt update
sudo apt install libcurl4-openssl-dev
2. CentOS/RHEL
CentOS와 RHEL에서는 yum
을 이용해 libcurl을 설치할 수 있습니다.
sudo yum install libcurl-devel
3. macOS (Homebrew 사용)
macOS 사용자는 Homebrew를 이용해 설치할 수 있습니다.
brew install curl
4. Windows
Windows에서는 vcpkg 또는 MSYS2를 이용해 libcurl을 설치할 수 있습니다.
vcpkg 이용
vcpkg install curl
MSYS2 이용
pacman -S mingw-w64-x86_64-curl
5. 소스 코드에서 직접 빌드
libcurl을 직접 빌드하려면 공식 웹사이트에서 최신 소스를 다운로드한 후 다음과 같이 컴파일하면 됩니다.
./configure --with-ssl
make
sudo make install
설치 확인
libcurl이 올바르게 설치되었는지 확인하려면 다음 명령을 실행합니다.
curl-config --version
위 명령을 실행하면 libcurl의 버전이 출력됩니다. 만약 오류가 발생하면 환경 변수를 설정해야 할 수도 있습니다.
이제 libcurl이 준비되었으므로, 다음 단계에서는 기본적인 HTTP 요청 방식(GET, POST 등)에 대해 설명합니다.
HTTP 요청 기본 개념
REST API와 상호작용하려면 HTTP 요청의 개념을 이해해야 합니다. HTTP 요청은 웹 서버와 데이터를 주고받는 방식이며, RESTful API는 이를 활용하여 클라이언트와 서버 간 통신을 수행합니다.
HTTP 요청의 주요 메서드
메서드 | 설명 |
---|---|
GET | 서버에서 데이터를 조회할 때 사용 (예: 게시글 목록 조회) |
POST | 서버에 새로운 데이터를 추가할 때 사용 (예: 회원 가입) |
PUT | 기존 데이터를 수정할 때 사용 (예: 프로필 정보 수정) |
DELETE | 데이터를 삭제할 때 사용 (예: 계정 삭제) |
1. GET 요청
- 데이터를 가져올 때 사용
- 요청 본문(Body)이 필요하지 않음
- 예제:
https://api.example.com/users
GET /users HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>
2. POST 요청
- 데이터를 서버에 전송하여 새로운 리소스를 생성할 때 사용
- 본문(Body)에 JSON 데이터를 포함
POST /users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer <token>
{
"name": "John Doe",
"email": "john@example.com"
}
3. PUT 요청
- 기존 리소스를 수정할 때 사용
- 전체 데이터를 업데이트할 경우 활용
PUT /users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer <token>
{
"name": "John Doe Updated",
"email": "john.updated@example.com"
}
4. DELETE 요청
- 특정 데이터를 삭제할 때 사용
- 본문(Body)은 필요하지 않음
DELETE /users/123 HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>
HTTP 응답 구조
서버는 클라이언트의 요청을 처리한 후 HTTP 응답을 반환합니다. 주요 요소는 다음과 같습니다.
- 상태 코드(Status Code): 요청 성공 여부 표시
- 응답 헤더(Headers): 응답 메타데이터 포함
- 응답 본문(Body): 요청한 데이터 포함
주요 HTTP 상태 코드
상태 코드 | 의미 |
---|---|
200 OK | 요청이 성공적으로 처리됨 |
201 Created | 리소스가 성공적으로 생성됨 (POST 요청) |
400 Bad Request | 요청이 잘못됨 (잘못된 파라미터 포함 등) |
401 Unauthorized | 인증이 필요함 |
403 Forbidden | 권한이 부족함 |
404 Not Found | 요청한 리소스를 찾을 수 없음 |
500 Internal Server Error | 서버 내부 오류 발생 |
다음 단계에서는 libcurl을 사용하여 GET 요청을 수행하는 방법을 설명합니다.
libcurl을 사용한 GET 요청
GET 요청 개요
GET 요청은 REST API에서 가장 많이 사용되는 요청 방식으로, 데이터를 조회하는 데 사용됩니다. libcurl을 이용하면 간단한 코드로 GET 요청을 수행할 수 있습니다.
기본 GET 요청 예제
아래 코드는 https://jsonplaceholder.typicode.com/posts/1
에 GET 요청을 보내고 응답을 출력하는 예제입니다.
#include <stdio.h>
#include <curl/curl.h>
// 응답 데이터를 저장할 함수
size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata) {
size_t total_size = size * nmemb;
fwrite(ptr, size, nmemb, stdout); // 응답 데이터를 화면에 출력
return total_size;
}
int main(void) {
CURL *curl;
CURLcode res;
// libcurl 초기화
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
// 요청할 URL 설정
curl_easy_setopt(curl, CURLOPT_URL, "https://jsonplaceholder.typicode.com/posts/1");
// 응답 데이터를 처리할 콜백 함수 설정
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
// 요청 실행
res = curl_easy_perform(curl);
// 오류 확인
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() 실패: %s\n", curl_easy_strerror(res));
}
// 리소스 정리
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
코드 설명
- libcurl 초기화
curl_global_init(CURL_GLOBAL_ALL)
을 호출하여 libcurl을 초기화합니다.curl_easy_init()
을 사용하여CURL
핸들을 생성합니다.
- 요청 설정
curl_easy_setopt(curl, CURLOPT_URL, "URL")
로 요청할 URL을 지정합니다.CURLOPT_WRITEFUNCTION
을 사용하여 응답을 처리할 콜백 함수를 설정합니다.
- 요청 실행
curl_easy_perform(curl)
을 호출하면 GET 요청이 수행됩니다.
- 오류 처리
- 요청이 실패할 경우
curl_easy_strerror(res)
를 사용하여 오류 메시지를 출력합니다.
- 정리 및 종료
curl_easy_cleanup(curl)
을 호출하여 CURL 핸들을 정리합니다.curl_global_cleanup()
을 호출하여 libcurl을 종료합니다.
실행 예제 출력
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum..."
}
이제 다음 단계에서는 libcurl을 이용한 POST 요청 및 JSON 데이터 전송을 다룹니다.
libcurl을 사용한 POST 요청
POST 요청 개요
POST 요청은 클라이언트가 서버에 데이터를 전송할 때 사용됩니다. 보통 새로운 리소스를 생성할 때 사용되며, 본문(Body)에 데이터를 포함할 수 있습니다.
libcurl을 사용하면 JSON 데이터를 포함한 POST 요청을 쉽게 보낼 수 있습니다.
기본 POST 요청 예제
아래 코드는 https://jsonplaceholder.typicode.com/posts
에 JSON 데이터를 포함하여 POST 요청을 보내는 예제입니다.
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
int main(void) {
CURL *curl;
CURLcode res;
// 전송할 JSON 데이터
const char *json_data = "{\"title\": \"foo\", \"body\": \"bar\", \"userId\": 1}";
// libcurl 초기화
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
// 요청할 URL 설정
curl_easy_setopt(curl, CURLOPT_URL, "https://jsonplaceholder.typicode.com/posts");
// HTTP POST 요청 설정
curl_easy_setopt(curl, CURLOPT_POST, 1L);
// 요청 본문(JSON 데이터) 설정
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_data);
// 헤더 설정 (JSON 데이터임을 명시)
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
// 요청 실행
res = curl_easy_perform(curl);
// 오류 확인
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() 실패: %s\n", curl_easy_strerror(res));
}
// 리소스 정리
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
코드 설명
- libcurl 초기화
curl_global_init(CURL_GLOBAL_ALL)
을 호출하여 libcurl을 초기화합니다.curl_easy_init()
을 사용하여 CURL 핸들을 생성합니다.
- 요청 설정
curl_easy_setopt(curl, CURLOPT_URL, "URL")
로 요청할 URL을 지정합니다.curl_easy_setopt(curl, CURLOPT_POST, 1L)
을 설정하여 POST 요청을 사용합니다.curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_data)
을 사용하여 JSON 데이터를 요청 본문에 포함합니다.
- 헤더 설정
Content-Type: application/json
헤더를 설정하여 서버가 JSON 데이터임을 인식하도록 합니다.
- 요청 실행
curl_easy_perform(curl)
을 호출하면 POST 요청이 수행됩니다.
- 오류 처리 및 정리
- 요청이 실패할 경우
curl_easy_strerror(res)
를 사용하여 오류 메시지를 출력합니다. curl_slist_free_all(headers)
를 호출하여 헤더를 정리합니다.curl_easy_cleanup(curl)
을 호출하여 CURL 핸들을 정리합니다.
실행 예제 출력
서버가 성공적으로 요청을 처리하면 다음과 같은 JSON 응답을 반환합니다.
{
"title": "foo",
"body": "bar",
"userId": 1,
"id": 101
}
이는 서버가 새로운 데이터를 생성했으며, id: 101
로 저장되었음을 의미합니다.
이제 다음 단계에서는 libcurl에서 헤더 설정 및 인증 처리 방법을 다룹니다.
libcurl의 헤더 설정 및 인증 처리
헤더(Header) 설정 개요
HTTP 요청에서 헤더(Header) 는 서버와 클라이언트 간의 메타데이터를 포함하는 중요한 요소입니다. REST API를 호출할 때는 다음과 같은 헤더가 필요할 수 있습니다.
헤더 | 설명 |
---|---|
Content-Type | 요청 본문의 데이터 형식을 지정 (예: JSON) |
Accept | 응답 데이터 형식을 지정 |
Authorization | API 인증 토큰 또는 자격 증명 전달 |
User-Agent | 클라이언트의 정보를 전달 |
Cache-Control | 캐시 관련 정책 설정 |
헤더 추가 방법
libcurl에서는 curl_slist
구조체를 사용하여 여러 개의 HTTP 헤더를 추가할 수 있습니다.
#include <stdio.h>
#include <curl/curl.h>
int main(void) {
CURL *curl;
CURLcode res;
// libcurl 초기화
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
// 요청할 URL 설정
curl_easy_setopt(curl, CURLOPT_URL, "https://api.example.com/data");
// 헤더 설정
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "User-Agent: libcurl-example/1.0");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
// GET 요청 실행
res = curl_easy_perform(curl);
// 오류 확인
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() 실패: %s\n", curl_easy_strerror(res));
}
// 리소스 정리
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
인증(Authentication) 처리
REST API는 보안 강화를 위해 인증(Authentication) 을 요구하는 경우가 많습니다. 일반적으로 사용되는 인증 방식은 다음과 같습니다.
인증 방식 | 설명 |
---|---|
Basic Authentication | username:password 를 Base64로 인코딩하여 전달 |
Bearer Token | JWT 또는 OAuth 토큰을 Authorization 헤더에 포함 |
API Key | API 키를 헤더 또는 URL 파라미터에 추가 |
1. Basic Authentication (기본 인증)
기본 인증 방식에서는 username:password
를 Base64로 인코딩하여 Authorization
헤더에 포함해야 합니다.
libcurl에서는 CURLOPT_USERPWD
옵션을 사용하여 쉽게 설정할 수 있습니다.
curl_easy_setopt(curl, CURLOPT_USERPWD, "username:password");
예제 코드:
#include <stdio.h>
#include <curl/curl.h>
int main(void) {
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://api.example.com/secure-data");
// 기본 인증 (Basic Auth)
curl_easy_setopt(curl, CURLOPT_USERPWD, "myusername:mypassword");
// 요청 실행
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() 실패: %s\n", curl_easy_strerror(res));
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
2. Bearer Token 인증 (OAuth 2.0 / JWT)
OAuth 2.0 또는 JWT 기반 인증에서는 Authorization
헤더에 Bearer 토큰을 추가해야 합니다.
#include <stdio.h>
#include <curl/curl.h>
int main(void) {
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://api.example.com/secure-data");
// Bearer Token 설정
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Authorization: Bearer my_access_token");
headers = curl_slist_append(headers, "Accept: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
// 요청 실행
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() 실패: %s\n", curl_easy_strerror(res));
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
3. API Key 인증
일부 API는 API 키를 사용하여 요청을 인증합니다. API 키는 헤더 또는 URL에 포함될 수 있습니다.
헤더 방식
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "x-api-key: YOUR_API_KEY");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
URL 파라미터 방식
curl_easy_setopt(curl, CURLOPT_URL, "https://api.example.com/data?api_key=YOUR_API_KEY");
정리
curl_slist_append()
을 사용하여 여러 개의 HTTP 헤더를 추가할 수 있음.- 기본 인증(Basic Auth)은
CURLOPT_USERPWD
옵션을 사용하여 간단히 처리 가능. - OAuth 2.0 / JWT 기반 인증은
Authorization: Bearer <토큰>
헤더를 추가해야 함. - API Key는 헤더 또는 URL 파라미터 방식으로 전달 가능.
다음 단계에서는 응답 데이터 처리 및 JSON 파싱 방법을 설명합니다.
응답 처리 및 JSON 파싱
응답 처리 개요
libcurl을 사용하여 HTTP 요청을 보낼 때, 서버의 응답을 적절히 처리해야 합니다. 응답에는 HTTP 상태 코드, 헤더 정보, 본문(JSON, XML, HTML 등) 이 포함됩니다. REST API의 경우 응답 데이터가 일반적으로 JSON 형식으로 제공되므로, 이를 파싱하여 활용하는 것이 중요합니다.
1. 응답 데이터 저장하기
libcurl에서는 CURLOPT_WRITEFUNCTION
을 사용하여 응답 데이터를 처리할 콜백 함수를 설정할 수 있습니다.
응답을 버퍼에 저장하는 예제
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
// 응답 데이터를 저장할 구조체
struct ResponseData {
char *memory;
size_t size;
};
// 응답 데이터를 동적 할당하여 저장하는 콜백 함수
size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata) {
size_t total_size = size * nmemb;
struct ResponseData *response = (struct ResponseData *)userdata;
char *new_memory = realloc(response->memory, response->size + total_size + 1);
if (new_memory == NULL) {
fprintf(stderr, "메모리 할당 실패\n");
return 0;
}
response->memory = new_memory;
memcpy(&(response->memory[response->size]), ptr, total_size);
response->size += total_size;
response->memory[response->size] = '\0';
return total_size;
}
int main(void) {
CURL *curl;
CURLcode res;
struct ResponseData response = {NULL, 0};
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://jsonplaceholder.typicode.com/posts/1");
// 응답을 write_callback 함수로 처리
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
res = curl_easy_perform(curl);
if (res == CURLE_OK) {
printf("응답 데이터:\n%s\n", response.memory);
} else {
fprintf(stderr, "요청 실패: %s\n", curl_easy_strerror(res));
}
free(response.memory);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
2. JSON 데이터 파싱
C언어에서는 JSON 데이터를 다루기 위해 cJSON과 같은 외부 라이브러리를 사용할 수 있습니다.
cJSON은 경량 JSON 파서로, 간단한 API를 통해 JSON 데이터를 다룰 수 있습니다.
cJSON 설치
- 리눅스 (Ubuntu/Debian)
sudo apt install libcjson-dev
- macOS (Homebrew 사용)
brew install cjson
- Windows (vcpkg 사용)
vcpkg install cjson
JSON 데이터 파싱 예제 (cJSON 사용)
#include <stdio.h>
#include <stdlib.h>
#include <curl/curl.h>
#include <cjson/cJSON.h>
// 응답 데이터를 저장할 구조체
struct ResponseData {
char *memory;
size_t size;
};
// 응답 데이터를 저장하는 콜백 함수
size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata) {
size_t total_size = size * nmemb;
struct ResponseData *response = (struct ResponseData *)userdata;
char *new_memory = realloc(response->memory, response->size + total_size + 1);
if (new_memory == NULL) {
fprintf(stderr, "메모리 할당 실패\n");
return 0;
}
response->memory = new_memory;
memcpy(&(response->memory[response->size]), ptr, total_size);
response->size += total_size;
response->memory[response->size] = '\0';
return total_size;
}
int main(void) {
CURL *curl;
CURLcode res;
struct ResponseData response = {NULL, 0};
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://jsonplaceholder.typicode.com/posts/1");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
res = curl_easy_perform(curl);
if (res == CURLE_OK) {
printf("서버 응답: %s\n", response.memory);
// JSON 파싱
cJSON *json = cJSON_Parse(response.memory);
if (json) {
cJSON *title = cJSON_GetObjectItemCaseSensitive(json, "title");
if (cJSON_IsString(title) && (title->valuestring != NULL)) {
printf("게시글 제목: %s\n", title->valuestring);
}
cJSON_Delete(json);
} else {
fprintf(stderr, "JSON 파싱 실패\n");
}
} else {
fprintf(stderr, "요청 실패: %s\n", curl_easy_strerror(res));
}
free(response.memory);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
3. 코드 설명
- 응답을 저장하는 구조체(
ResponseData
)
memory
: 응답 데이터를 저장할 메모리 버퍼size
: 현재 저장된 데이터 크기
- 응답을 처리하는
write_callback
함수
- libcurl의
CURLOPT_WRITEFUNCTION
을 설정하여 응답 데이터를 동적으로 저장 realloc
을 사용하여 메모리를 동적으로 증가시킴
- 응답 데이터(JSON) 파싱
cJSON_Parse(response.memory)
: JSON 데이터를 파싱cJSON_GetObjectItemCaseSensitive(json, "title")
: 특정 JSON 키의 값을 가져옴cJSON_Delete(json)
: JSON 객체 메모리 해제
4. 실행 결과 (예제 응답)
서버 응답: {
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum..."
}
게시글 제목: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
5. 정리
✅ libcurl의 CURLOPT_WRITEFUNCTION
을 활용해 응답 데이터를 저장
✅ cJSON을 사용하여 JSON 응답 데이터를 파싱하고 필요한 정보 추출
✅ REST API와 상호작용할 때 데이터를 효과적으로 처리하는 방법 습득
다음 단계에서는 libcurl을 사용한 오류 처리 및 디버깅 기법을 다룹니다.
오류 처리 및 디버깅 기법
1. libcurl에서 발생하는 주요 오류
libcurl을 사용하여 HTTP 요청을 수행할 때, 네트워크 문제나 잘못된 요청 등의 이유로 다양한 오류가 발생할 수 있습니다. 주요 오류 코드는 다음과 같습니다.
오류 코드 | 설명 |
---|---|
CURLE_OK | 요청이 정상적으로 수행됨 |
CURLE_UNSUPPORTED_PROTOCOL | 지원되지 않는 프로토콜을 사용함 |
CURLE_COULDNT_CONNECT | 서버에 연결할 수 없음 |
CURLE_HTTP_RETURNED_ERROR | 서버에서 HTTP 오류 코드 반환 |
CURLE_READ_ERROR | 데이터 읽기 실패 |
CURLE_OPERATION_TIMEDOUT | 요청이 시간 초과됨 |
CURLE_SSL_CONNECT_ERROR | SSL/TLS 연결 오류 |
이러한 오류를 적절히 처리하지 않으면 프로그램이 예기치 않게 종료될 수 있습니다. 따라서, 에러 코드 확인 및 로깅이 중요합니다.
2. 오류 코드 확인 및 기본 처리
libcurl의 curl_easy_perform()
함수는 CURLcode
반환값을 제공하며, 이를 통해 오류를 감지할 수 있습니다.
기본 오류 처리 예제
#include <stdio.h>
#include <curl/curl.h>
int main(void) {
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://invalid-url.example.com");
// 요청 실행
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
// 오류 메시지 출력
fprintf(stderr, "요청 실패: %s\n", curl_easy_strerror(res));
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
실행 결과 (유효하지 않은 URL 요청 시)
요청 실패: Couldn't resolve host name
curl_easy_strerror(res)
를 사용하면 사람이 읽을 수 있는 오류 메시지를 출력할 수 있습니다.
3. HTTP 응답 코드 확인
libcurl을 사용하면 HTTP 응답 상태 코드(예: 200, 404, 500)를 확인할 수 있습니다.
이를 통해 서버에서 반환한 오류를 감지하고 적절한 대응을 할 수 있습니다.
HTTP 응답 코드 확인 예제
#include <stdio.h>
#include <curl/curl.h>
int main(void) {
CURL *curl;
CURLcode res;
long http_code = 0;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://jsonplaceholder.typicode.com/invalid_endpoint");
// 요청 실행
res = curl_easy_perform(curl);
if (res == CURLE_OK) {
// HTTP 응답 코드 확인
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
printf("HTTP 응답 코드: %ld\n", http_code);
if (http_code == 404) {
fprintf(stderr, "오류: 요청한 리소스를 찾을 수 없음 (404)\n");
}
} else {
fprintf(stderr, "요청 실패: %s\n", curl_easy_strerror(res));
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
실행 결과 (존재하지 않는 리소스를 요청한 경우)
HTTP 응답 코드: 404
오류: 요청한 리소스를 찾을 수 없음 (404)
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
를 사용하면 HTTP 응답 코드를 확인할 수 있습니다.
4. 요청 시간 초과 처리
네트워크가 불안정한 경우 요청이 무한히 대기하는 것을 방지하기 위해 타임아웃 설정이 필요합니다.
요청 시간 초과 설정 예제
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); // 전체 요청 타임아웃 10초
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L); // 연결 대기 시간 5초
이를 적용한 전체 코드:
#include <stdio.h>
#include <curl/curl.h>
int main(void) {
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); // 전체 요청 타임아웃 10초
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L); // 연결 대기 시간 5초
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "요청 실패: %s\n", curl_easy_strerror(res));
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
5. 요청 디버깅 (디버깅 모드 활성화)
libcurl은 디버깅 모드를 제공하여 요청 및 응답의 상세 정보를 출력할 수 있습니다.
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
전체 코드:
#include <stdio.h>
#include <curl/curl.h>
int main(void) {
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); // 디버깅 모드 활성화
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "요청 실패: %s\n", curl_easy_strerror(res));
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
실행 결과 (디버깅 모드 활성화)
* Trying 93.184.216.34...
* Connected to example.com (93.184.216.34) port 443 (#0)
> GET / HTTP/1.1
> Host: example.com
> User-Agent: curl/7.68.0
> Accept: */*
CURLOPT_VERBOSE
를 활성화하면, 요청 및 응답 과정이 상세하게 출력됩니다.
6. 정리
✅ curl_easy_perform()
의 반환값을 확인하여 오류 감지
✅ curl_easy_getinfo()
를 사용하여 HTTP 응답 코드 확인 가능
✅ CURLOPT_TIMEOUT
을 설정하여 요청 시간 초과 방지
✅ CURLOPT_VERBOSE
를 사용하여 요청 디버깅 가능
다음 단계에서는 실제 REST API 예제 및 활용 방법을 설명합니다.
실제 REST API 예제 및 활용
1. REST API를 이용한 실제 시나리오
libcurl을 사용하여 REST API와 상호작용하는 예제를 실전 환경에 맞춰 구현해 보겠습니다.
다음과 같은 기능을 수행하는 예제를 작성합니다.
✅ 사용자 목록을 가져오는 GET 요청
✅ 새로운 사용자를 추가하는 POST 요청
✅ 특정 사용자의 정보를 수정하는 PUT 요청
✅ 사용자를 삭제하는 DELETE 요청
사용할 API: JSONPlaceholder (테스트용 무료 API)
2. GET 요청: 사용자 목록 조회
다음 코드는 https://jsonplaceholder.typicode.com/users
API를 호출하여 사용자 목록을 가져옵니다.
#include <stdio.h>
#include <stdlib.h>
#include <curl/curl.h>
// 응답을 저장할 구조체
struct ResponseData {
char *memory;
size_t size;
};
// 응답 데이터를 저장하는 콜백 함수
size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata) {
size_t total_size = size * nmemb;
struct ResponseData *response = (struct ResponseData *)userdata;
char *new_memory = realloc(response->memory, response->size + total_size + 1);
if (new_memory == NULL) {
fprintf(stderr, "메모리 할당 실패\n");
return 0;
}
response->memory = new_memory;
memcpy(&(response->memory[response->size]), ptr, total_size);
response->size += total_size;
response->memory[response->size] = '\0';
return total_size;
}
int main(void) {
CURL *curl;
CURLcode res;
struct ResponseData response = {NULL, 0};
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://jsonplaceholder.typicode.com/users");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
res = curl_easy_perform(curl);
if (res == CURLE_OK) {
printf("사용자 목록: \n%s\n", response.memory);
} else {
fprintf(stderr, "GET 요청 실패: %s\n", curl_easy_strerror(res));
}
free(response.memory);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
✅ 사용자 목록을 성공적으로 조회하면 JSON 형식의 응답이 반환됩니다.
3. POST 요청: 새로운 사용자 추가
새로운 사용자를 추가하는 API 호출 코드입니다.
#include <stdio.h>
#include <curl/curl.h>
int main(void) {
CURL *curl;
CURLcode res;
const char *json_data = "{\"name\": \"John Doe\", \"email\": \"johndoe@example.com\"}";
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://jsonplaceholder.typicode.com/users");
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_data);
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "POST 요청 실패: %s\n", curl_easy_strerror(res));
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
✅ 실행하면 새로운 사용자가 생성되었음을 확인할 수 있습니다.
4. PUT 요청: 사용자 정보 수정
사용자의 이메일을 업데이트하는 예제입니다.
#include <stdio.h>
#include <curl/curl.h>
int main(void) {
CURL *curl;
CURLcode res;
const char *json_data = "{\"name\": \"John Doe\", \"email\": \"john.updated@example.com\"}";
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://jsonplaceholder.typicode.com/users/1");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_data);
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "PUT 요청 실패: %s\n", curl_easy_strerror(res));
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
✅ 실행하면 특정 사용자의 정보가 업데이트됩니다.
5. DELETE 요청: 사용자 삭제
특정 사용자를 삭제하는 DELETE 요청 예제입니다.
#include <stdio.h>
#include <curl/curl.h>
int main(void) {
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://jsonplaceholder.typicode.com/users/1");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "DELETE 요청 실패: %s\n", curl_easy_strerror(res));
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
✅ 실행하면 특정 사용자가 삭제됩니다.
6. 정리
✅ libcurl을 사용하여 실제 REST API와 상호작용하는 방법을 배움
✅ GET, POST, PUT, DELETE 요청을 활용하여 CRUD 작업 수행
✅ JSON 데이터를 처리하고 HTTP 요청을 보낼 수 있도록 구현
다음 단계에서는 요약 및 최종 정리를 진행합니다.
요약
본 기사에서는 C언어에서 libcurl을 사용하여 REST API 클라이언트를 구현하는 방법을 다루었습니다.
✅ libcurl 설치 및 기본 개념을 소개하고,
✅ GET, POST, PUT, DELETE 요청을 수행하는 방법을 설명했으며,
✅ 헤더 설정 및 인증 처리, 오류 디버깅, JSON 파싱 기법까지 다뤘습니다.
✅ 마지막으로 실제 REST API와 상호작용하는 예제를 통해 CRUD 작업을 수행하는 방법을 실습했습니다.
이제 libcurl을 활용하여 C언어 기반의 네트워크 애플리케이션을 개발할 수 있으며, API를 활용한 다양한 서비스와 연동할 수 있습니다. RESTful API를 사용하는 프로젝트에서 libcurl을 적극적으로 활용해 보세요! 🚀