R에서 REST API 사용하기
API 키로 인증하고, 결과를 페이지별로 조회하며, API 응답을 저장합니다.
R에서 REST API 사용하기은(는) CoddyKit의 무료 R Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 R Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
REST API 개념
REST API는 리소스 URL에 HTTP 메서드(GET, POST, PUT, DELETE)를 사용합니다. 응답은 일반적으로 JSON 형식입니다. API에는 인증이 필요하거나 페이지 매김을 사용하거나 요청 빈도 제한이 적용될 수 있습니다. 좋은 R API 클라이언트는 이 세 가지를 모두 처리합니다.
library(httr2)
# REST API anatomy:
# Base URL: https://api.example.com/v1
# Resources: /users, /products, /orders
# Methods:
# GET /users -> list users
# POST /users -> create user
# GET /users/42 -> get user 42
# PUT /users/42 -> update user 42
# DELETE /users/42 -> delete user 42
# Query parameters for filtering/pagination:
# GET /users?page=2&size=20&sort=name
cat('REST = stateless + resource-based + HTTP methods')Bearer 토큰 인증
대부분의 최신 API는 Bearer 토큰(OAuth 2.0)을 사용합니다. Sys.setenv()으로 토큰을 환경 변수에 저장하거나 .Renviron 파일에 저장하세요. 스크립트에 토큰을 직접 작성하지 마세요.
library(httr2)
# Store token securely in .Renviron:
# GITHUB_PAT=ghp_your_token_here
# Access at runtime:
token <- Sys.getenv('GITHUB_PAT')
if (nchar(token) == 0) token <- 'demo_token'
# Use in requests:
# resp <- request('https://api.github.com/user') |>
# req_auth_bearer_token(token) |>
# req_headers('Accept' = 'application/vnd.github.v3+json') |>
# req_perform() |>
# resp_check_status()
# result <- resp_body_json(resp)
# result$login # your GitHub username
cat('Token from env:', if(nchar(token)>0) 'found' else 'missing')재사용 가능한 API 클라이언트 만들기
생성자 함수에 기본 URL, 인증, 오류 처리를 캡슐화하세요. 각 API 엔드포인트는 이 기본 함수를 호출하는 메서드가 됩니다. 이는 R API 패키지의 표준 패턴입니다.
library(httr2)
# API client constructor
new_api_client <- function(base_url, token) {
list(
base_req = request(base_url) |>
req_auth_bearer_token(token) |>
req_headers('Accept' = 'application/json') |>
req_retry(max_tries = 3)
)
}
# Method: GET /users
get_users <- function(client, page = 1, size = 20) {
resp <- client$base_req |>
req_url_path_append('users') |>
req_url_query(page = page, size = size) |>
req_perform() |>
resp_check_status()
resp_body_json(resp, simplifyVector = TRUE)
}
# client <- new_api_client('https://api.example.com', token)
# users <- get_users(client, page = 1)
cat('Reusable client pattern: base request + methods')resp_link_url()을 사용한 페이지 매김
많은 API는 페이지 매김에 Link 헤더를 사용합니다(RFC 5988). 응답에는 Link: <url>; rel="next" 헤더가 포함됩니다. resp_link_url(resp, 'next')는 다음 페이지의 URL을 자동으로 추출합니다.
library(httr2)
# Generic paginator using Link headers
fetch_all_pages <- function(initial_url, token, max_pages = 50) {
all_results <- list()
next_url <- initial_url
page <- 1
while (!is.null(next_url) && page <= max_pages) {
resp <- request(next_url) |>
req_auth_bearer_token(token) |>
req_perform() |>
resp_check_status()
all_results[[page]] <- resp_body_json(resp, simplifyVector = TRUE)
# Follow Link: <url>; rel='next' header
next_url <- tryCatch(
resp_link_url(resp, 'next'),
error = function(e) NULL
)
page <- page + 1
}
do.call(rbind, all_results)
}
cat('resp_link_url() follows RFC 5988 pagination')커서 기반 페이지 매김
일부 API(Twitter, Slack)는 페이지 번호 대신 커서를 사용합니다. 응답에는 next_cursor 또는 next_page_token 필드가 포함됩니다. 다음 요청의 쿼리 매개변수로 전달하세요.
library(httr2)
# Cursor-based pagination pattern
fetch_cursor_pages <- function(base_url, token, max_pages = 100) {
all_data <- list()
cursor <- NULL
page <- 1
repeat {
req <- request(base_url) |>
req_auth_bearer_token(token)
if (!is.null(cursor))
req <- req |> req_url_query(cursor = cursor)
resp <- req |> req_perform() |> resp_check_status()
body <- resp_body_json(resp)
all_data[[page]] <- body$results
cursor <- body$next_cursor # NULL if last page
if (is.null(cursor) || page >= max_pages) break
page <- page + 1
}
do.call(c, all_data)
}
cat('Cursor pagination: safer for large/changing datasets')오류를 우아하게 처리하기
운영 환경의 API 클라이언트는 HTTP 오류와 네트워크 실패를 구분해 처리합니다. req_perform()을 tryCatch()로 감싸고, 상태별 처리를 위해 httr2_http_* 오류 condition을 확인하세요.
library(httr2)
safe_api_call <- function(req) {
tryCatch(
req |> req_perform() |> resp_check_status(),
httr2_http_401 = function(e) {
stop('Authentication failed. Check your token.')
},
httr2_http_403 = function(e) {
stop('Forbidden. Insufficient permissions.')
},
httr2_http_404 = function(e) {
message('Resource not found, returning NULL')
return(NULL)
},
httr2_http_429 = function(e) {
stop('Rate limit exceeded. Try again later.')
},
error = function(e) {
stop(paste('Request failed:', conditionMessage(e)))
}
)
}
cat('Match on specific httr2_http_NNN conditions')API 응답 캐싱
개발 중 불필요한 요청을 피하려면 API 응답을 캐시하세요. httr2의 req_cache()는 Cache-Control 헤더를 준수하면서 응답을 디스크에 캐시합니다. 수동 캐싱은 모든 API에서 사용할 수 있습니다.
library(httr2)
# httr2 built-in disk cache
# resp <- request('https://api.example.com/static-data') |>
# req_cache(tempdir(), max_age = 3600) |> # 1 hour TTL
# req_perform()
# Manual cache pattern
cached_api_call <- function(url, cache_file, max_age = 3600) {
if (file.exists(cache_file)) {
age <- as.numeric(Sys.time() - file.mtime(cache_file))
if (age < max_age) {
cat('Cache hit\n')
return(readRDS(cache_file))
}
}
cat('Cache miss, fetching...\n')
# result <- resp_body_json(request(url) |> req_perform())
# saveRDS(result, cache_file)
# result
}
cached_api_call('https://example.com/api', 'cache.rds')req_perform_parallel()을 사용한 병렬 요청
req_perform_parallel()은 여러 요청을 동시에 보내 일괄 작업에 걸리는 전체 시간을 크게 줄입니다. 병렬 실행 중에도 요청 빈도 제한을 준수하려면 req_throttle()과 함께 사용하세요.
library(httr2)
# Build a list of requests
item_ids <- 1:5
reqs <- lapply(item_ids, function(id) {
request(paste0('https://jsonplaceholder.typicode.com/todos/', id))
})
# Execute all in parallel (requires internet)
# resps <- req_perform_parallel(
# reqs,
# on_error = 'continue', # skip failures
# progress = TRUE
# )
# results <- lapply(resps, resp_body_json)
# titles <- sapply(results, function(r) r$title)
# print(titles)
# on_error options:
# 'stop' -> abort on first failure
# 'continue' -> collect errors, keep going
cat('Parallel: much faster for many independent calls')요청 빈도 제한 다루기
API는 응답 헤더를 통해 요청 빈도 제한 상태를 전달합니다. X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After를 읽어 지능적인 재시도 지연을 구현하세요.
library(httr2)
respect_rate_limit <- function(resp) {
# Check remaining calls
remaining <- resp_header(resp, 'x-ratelimit-remaining')
if (!is.na(remaining) && as.integer(remaining) < 5) {
reset_at <- as.integer(
resp_header(resp, 'x-ratelimit-reset')
)
wait_secs <- max(0, reset_at - as.integer(Sys.time()))
cat(sprintf('Rate limit nearly exhausted. Waiting %ds\n', wait_secs))
# Sys.sleep(wait_secs)
}
# Handle 429 Retry-After header
if (resp_status(resp) == 429) {
retry_after <- resp_header(resp, 'retry-after')
cat(sprintf('Rate limited. Retry after %s seconds\n', retry_after))
}
resp
}
cat('Always respect X-RateLimit-* headers')전체 API 클라이언트: GitHub 예제
지금까지 배운 모든 패턴을 적용해 보겠습니다. 인증하고 페이지 매김된 저장소 목록을 가져오며 오류를 처리하는 완전한 GitHub API 클라이언트를 구현합니다.
library(httr2)
# GitHub API client
github_repos <- function(username, token = NULL, n_pages = 3) {
base <- 'https://api.github.com'
req_base <- request(base) |>
req_headers(
'Accept' = 'application/vnd.github.v3+json',
'User-Agent' = 'R-API-Client/1.0'
)
if (!is.null(token))
req_base <- req_base |> req_auth_bearer_token(token)
all_repos <- list()
for (page in seq_len(n_pages)) {
# resp <- req_base |>
# req_url_path_append('users', username, 'repos') |>
# req_url_query(page = page, per_page = 30, sort = 'updated') |>
# req_perform() |> resp_check_status()
# repos <- resp_body_json(resp, simplifyVector = TRUE)
# if (length(repos) == 0) break
# all_repos[[page]] <- repos
cat(sprintf('Would fetch page %d for %s\n', page, username))
}
do.call(rbind, all_repos)
}
github_repos('hadley')OAuth 2.0 클라이언트 자격 증명
일부 API에는 OAuth 2.0 클라이언트 자격 증명 흐름이 필요합니다. client_id + client_secret을 액세스 토큰으로 교환하는 방식입니다. httr2의 oauth_client()와 req_oauth_client_credentials()가 이 과정을 자동으로 처리합니다.
library(httr2)
# OAuth 2.0 Client Credentials flow:
# client <- oauth_client(
# id = Sys.getenv('CLIENT_ID'),
# secret = Sys.getenv('CLIENT_SECRET'),
# token_url = 'https://auth.example.com/oauth/token'
# )
# Automatic token management:
# resp <- request('https://api.example.com/data') |>
# req_oauth_client_credentials(client) |>
# req_perform()
# httr2 automatically:
# 1. Gets access token using client credentials
# 2. Adds 'Authorization: Bearer <token>' header
# 3. Refreshes token when expired
cat('req_oauth_client_credentials() handles token lifecycle')
cat('Token is cached in memory automatically')빠른 확인
httr2를 사용해 R에서 REST API 클라이언트를 만드는 방법에 대한 이해도를 확인해 보세요.
복습: REST API 사용
핵심 요점: 생성자에 기본 URL, 인증, 재시도 기능을 캡슐화해 재사용 가능한 클라이언트를 만드세요. 인증에는 req_auth_bearer_token()을 사용하고 토큰은 환경 변수에 저장하세요. resp_link_url()을 사용한 Link 헤더 방식이나 커서 기반 패턴으로 페이지 매김을 처리하세요. 항상 resp_check_status()와 tryCatch()로 HTTP 오류를 처리하세요. 일괄 호출에는 req_perform_parallel()을 사용하세요. 개발 중에는 결과를 캐시하세요.
library(httr2)
# REST API client template:
make_api_call <- function(url, token,
method = 'GET',
body = NULL,
query = list()) {
req <- request(url) |>
req_method(method) |>
req_auth_bearer_token(token) |>
req_headers('Accept' = 'application/json') |>
req_retry(max_tries = 3) |>
req_throttle(rate = 10/60)
if (length(query) > 0)
req <- do.call(req_url_query, c(list(req), query))
if (!is.null(body))
req <- req |> req_body_json(body)
req |> req_perform() |> resp_check_status()
}
cat('Template: auth + retry + throttle + error check')자주 묻는 질문
“R에서 REST API 사용하기” 강의는 무료인가요?
네 — “R에서 REST API 사용하기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 R Academy 강의 전체를 잠금 해제할 수 있습니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“R에서 REST API 사용하기”에서 뭘 배우나요?
API 키로 인증하고, 결과를 페이지별로 조회하며, API 응답을 저장합니다. 브라우저에서 직접 실행하는 실습 코드로 R Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
R Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 R Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“R에서 REST API 사용하기” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 R Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 R Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- jsonlite로 JSON 구문 분석
- httr2로 HTTP 요청 보내기
- R에서 REST API 사용하기
- 중첩된 JSON 구조 처리