콘텐츠로 건너뛰기

cURL 명령을 JavaScript fetch·Python requests로 변환하는 방법|DevTools 연동

카테고리: HTTP·API 개발

API 디버깅에서 흔한 작업은 「Chrome DevTools에서 확인한 cURL 명령을 코드로 그대로 변환하는」것입니다. 이 문서에서는 cURL 옵션 체계, DevTools의 「Copy as cURL」기능의 작동 원리, 주요 언어(JavaScript fetch / axios / Python requests / PHP cURL / Go net/http)로의 변환 패턴을 설명합니다. 끝에 소개된 cURL 변환 도구를 사용하면 이들을 복사 붙여넣기 한 번으로 변환할 수 있습니다.

cURL을 시작점으로 삼는 이유

API를 구현하고 디버깅할 때, 일반적인 워크플로우는 먼저 「curl로 작동하는지 확인한 후 코드로 작성한다」는 것입니다. 이유는 다음과 같습니다:

  • 언어 독립적: curl은 거의 모든 환경에서 사용 가능한 공통 언어입니다
  • DevTools에서 즉시 복사: Chrome / Firefox / Safari의 Network 탭에서 요청을 우클릭 → Copy as cURL로 완전한 재현 명령 획득 가능
  • 재현성 보장: 버그 보고나 Stack Overflow 질문에 curl 명령을 첨부하면, 환경 차이 없이 상대방이 동일한 요청을 시도해볼 수 있습니다

cURL의 주요 옵션

최소한 알아야 할 cURL 옵션은 다음과 같습니다.

옵션용도예시
-X / --requestHTTP 메서드 지정-X POST
-H / --header요청 헤더 추가-H "Content-Type: application/json"
-d / --data요청 본문-d '{"id":1}'
--data-raw이스케이프 해석을 하지 않는 원본 데이터--data-raw 'a=1&b=2'
-F / --formmultipart/form-data-F "file=@./a.png"
-u / --userBasic 인증-u user:pass
-b / --cookieCookie 전송-b "session=abc"
-L / --location리다이렉트 추적
-k / --insecureSSL 증명서 검증 건너뛰기 (개발 시에만)
--compressedgzip 수락

DevTools의 "Copy as cURL"에서 얻을 수 있는 것

Chrome / Firefox / Edge에서 Network 탭을 열고 임의의 요청을 마우스 오른쪽으로 클릭하면 Copy → Copy as cURL이라는 메뉴가 있습니다. 이를 클릭하면 완전히 재현된 명령이 클립보드에 복사됩니다.

  • HTTP 메서드 및 전체 URL
  • 전송된 모든 요청 헤더 (Authorization / User-Agent / Accept / Cookie 등)
  • 요청 본문 (JSON / form-data / binary)
  • ANSI-C quoting ($'...')을 통한 이스케이프

주의할 점으로, Firefox의 "Copy as cURL"은 --data-raw를 사용하고 Chrome은 --data-binary를 사용하는 등 미묘한 차이가 있습니다. 변환 도구 측에서 둘 다 인식할 수 있는 구현이 필요합니다.

cURL 옵션과 fetch 옵션 매핑 cURL fetch -X POST method: 'POST' -H "Key: Val" headers: { Key: 'Val' } -d "body" body: 'body' -F file=@a.txt body: FormData --cookie credentials: 'include' -u user:pass Authorization: Basic
그림 1: 주요 cURL 옵션과 fetch 옵션의 대응 관계

JavaScript fetch로 변환

일반적인 JSON POST 변환 예를 보여줍니다.

# cURL
curl -X POST https://api.example.com/users \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -d '{"name":"Alice","email":"alice@example.com"}'
// JavaScript fetch
fetch('https://api.example.com/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_TOKEN',
  },
  body: JSON.stringify({ name: 'Alice', email: 'alice@example.com' }),
})
  .then(r => r.json())
  .then(console.log);

axios 버전

import axios from 'axios';

axios.post(
  'https://api.example.com/users',
  { name: 'Alice', email: 'alice@example.com' },
  { headers: { 'Authorization': 'Bearer YOUR_TOKEN' } }
).then(r => console.log(r.data));

Python requests로 변환

import requests

response = requests.post(
    'https://api.example.com/users',
    headers={
        'Content-Type': 'application/json',
        'Authorization': 'Bearer YOUR_TOKEN',
    },
    json={'name': 'Alice', 'email': 'alice@example.com'},
)
print(response.json())

data= 대신 json=을 사용하는 것이 중요합니다. json=을 지정하면 requests가 자동으로 JSON을 직렬화하고 Content-Type도 자동으로 설정됩니다.

PHP cURL로 변환

<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.example.com/users');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'name'  => 'Alice',
    'email' => 'alice@example.com',
]));

$response = curl_exec($ch);
curl_close($ch);
echo $response;

Go net/http로의 변환

package main

import (
    "bytes"
    "io"
    "net/http"
)

func main() {
    body := bytes.NewReader([]byte(`{"name":"Alice","email":"alice@example.com"}`))
    req, _ := http.NewRequest("POST", "https://api.example.com/users", body)
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")

    res, _ := http.DefaultClient.Do(req)
    defer res.Body.Close()
    data, _ := io.ReadAll(res.Body)
    println(string(data))
}

주의: 토큰의 하드코딩은 절대 금지

cURL 명령을 코드로 변환할 때 Authorization 헤더 값(Bearer 토큰이나 API 키)을 그대로 코드에 작성해서 커밋하지 않도록 주의하세요.

  • 프로덕션 토큰을 환경 변수를 통해 읽기 (process.env.API_TOKEN / os.environ['API_TOKEN'] / getenv())
  • 커밋 전에 git diff로 비밀 정보가 섞여 있지 않은지 확인
  • 이미 커밋한 경우 토큰을 즉시 무효화(rotation)하고 히스토리를 다시 작성(git filter-repo)

DevLab cURL 변환 도구

DevLab의 cURL ⇄ fetch / axios 변환 도구는 위의 모든 변환을 브라우저에서 완결로 한 번의 클릭으로 실행합니다. 지원 대상은 13가지:

  • JavaScript: fetch / axios / node-fetch / Node.js 표준 https
  • Python: requests / urllib
  • PHP: cURL / file_get_contents
  • Go: net/http
  • Rust: reqwest
  • wget / HTTPie / PowerShell Invoke-WebRequest

cURL 옵션은 -X -H -d --data-raw --data-binary --data-urlencode -F -u -b -A -e -L -k -I 및 ANSI-C quoting $'...'을 지원하며 Chrome과 Firefox의 "Copy as cURL" 출력을 그대로 받아들입니다.

요약

cURL은 언어 비의존적인 HTTP 재현 공통 언어로 매우 편리하며, DevTools와 결합하면 실운영 중인 API 요청을 즉시 재현할 수 있습니다. 이 기사에서 보여주는 변환 패턴을 습득하면 어떤 언어에서든 동일한 동작을 재현할 수 있습니다. 수작업으로 변환하면 누락(특히 Content-Type이나 따옴표 이스케이프)이 발생하기 쉬우므로 자동화된 변환 도구 사용을 강력히 권장합니다.

❓ 자주 묻는 질문

DevTools 의 Copy as cURL 을 그대로 써도 되나요?
재현 확인에는 편리하지만 그대로 공유하면 안 됩니다. Cookie 헤더와 Authorization 헤더에 실제 세션이 들어 있어 붙여 넣은 상대가 당신으로 로그인할 수 있습니다. 이슈나 채팅에 올리기 전에 인증 관련 헤더는 반드시 지우거나 플레이스홀더로 바꾸세요.
curl -d 와 fetch 의 body 는 같은 것인가요?
내용은 같아도 Content-Type 이 다릅니다. curl -d 는 지정이 없으면 application/x-www-form-urlencoded 를 자동으로 붙이지만 fetch 는 아무것도 붙이지 않습니다. JSON 을 보낸다고 생각하면서 headers 에 Content-Type: application/json 을 빠뜨리면 서버에서 본문이 비어 보입니다. 이식할 때 가장 흔한 함정입니다.
curl 의 -L 이나 -k 는 fetch 로 어떻게 쓰나요?
-L(리다이렉트 추종)는 fetch 의 기본 동작이라 아무것도 쓸 필요가 없고, 멈추고 싶을 때만 redirect: 'manual' 을 지정합니다. -k(인증서 검증 비활성화)에 해당하는 것은 브라우저 fetch 에 없습니다. 브라우저가 TLS 검증을 포기할 수단을 갖고 있지 않기 때문이며, 자체 서명 인증서는 Node.js 쪽이나 개발용 신뢰 인증서로 다루세요.