Devin.KR
로그인

리눅스 curl 명령어 사용법 - 옵션 정리와 API 테스트 실전 예제

개발자 조회 2

이 명령어를 언제 쓰는가

배포 직후 정말 200 이 떨어지는지 확인할 때, API 가 이상한 응답을 준다고 프론트가 말할 때, 인증서 만료로 접속이 막혔는지 볼 때 curl을 친다. 브라우저는 캐시, 쿠키, 확장 프로그램이 끼어들어 진실을 가리지만 curl 은 요청 한 번을 그대로 보여준다.

서버 안에서 curl localhost:8080/health가 되는데 밖에서 안 되면 문제는 애플리케이션이 아니라 앞단(프록시, 방화벽, DNS)이다. 이 한 줄로 원인 범위가 절반으로 줄어든다. 실무에서 curl 은 다운로드 도구가 아니라 진단 도구다.

기본 형식

curl [옵션] URL
  • URL — 프로토콜을 생략하면 http 로 간주한다. ?&가 들어가면 반드시 따옴표로 감싼다.
  • 옵션 — 순서는 상관없지만, 값이 필요한 옵션은 바로 뒤에 값을 붙여야 한다.
  • 옵션이 없으면 응답 본문만 표준출력으로 뱉는다. 헤더도, 상태코드도 보여주지 않는다.

자주 쓰는 옵션

옵션의미예시
-i응답 헤더와 본문을 함께 출력curl -i https://devin.kr/
-IHEAD 요청. 헤더만 확인할 때curl -I https://devin.kr/
-s / -sS진행률 숨김 / 숨기되 에러는 표시curl -sS https://devin.kr/
-f4xx·5xx 면 종료코드 22 로 실패 처리curl -fsS https://devin.kr/
-L리다이렉트를 따라간다curl -sIL http://devin.kr/
-o 파일 / -O파일로 저장 / URL 파일명 그대로 저장curl -o app.tgz https://.../app.tgz
-H '헤더'요청 헤더 추가. 여러 번 쓸 수 있다-H 'Content-Type: application/json'
-d '본문'POST 본문 전송(자동으로 POST 가 된다)-d '{"id":"test"}'
-X 메서드메서드 지정(PUT, DELETE 등)curl -X DELETE http://127.0.0.1:8080/api/v1/items/3
-u 아이디:비번기본 인증curl -u admin:secret http://127.0.0.1:9200/
--connect-timeout / --max-time연결 대기 / 전체 시간 상한(초)--connect-timeout 3 --max-time 10
--resolveDNS 를 무시하고 특정 IP 로 접속--resolve devin.kr:443:10.0.1.20
-w '형식'완료 후 지표 출력(상태코드, 소요시간 등)-w '%{http_code}\n'
-v요청·응답 헤더와 TLS 협상 과정을 전부 출력curl -v https://devin.kr/

실전 예제

1. 상태코드만 뽑아 배포 검증에 쓴다. 본문은 버리고 숫자만 받아 스크립트 조건문에 넣는다.

curl -s -o /dev/null -w '%{http_code}\n' https://devin.kr/
200

2. JSON API 에 POST 를 던진다. 프론트가 400 을 받는다고 할 때, 같은 요청을 서버에서 직접 재현해 본다.

curl -sS -i -X POST http://127.0.0.1:8080/api/login \
  -H 'Content-Type: application/json' \
  -d '{"userId":"tester","password":"1234"}'

본문이 길면 파일로 빼서 -d @payload.json 으로 넘긴다. 셸 따옴표 지옥을 피할 수 있다.

3. 리다이렉트 사슬을 확인한다. http 가 https 로, www 가 non-www 로 제대로 넘어가는지는 SEO 와 직결된다.

curl -sIL http://devin.kr/ | grep -iE '^(HTTP/|location:)'
HTTP/1.1 301 Moved Permanently
location: https://devin.kr/
HTTP/2 200

grep 에 -i 를 붙인 이유가 있다. curl 은 HTTP/2 응답 헤더는 소문자로 정규화해 보여주지만 HTTP/1.1 응답은 서버가 보낸 그대로 찍기 때문에, 앞단 서버에 따라 첫 줄이 Location: 대문자로 나온다. -i 가 없으면 정작 확인하려던 리다이렉트 대상이 걸러진다.

4. DNS 를 바꾸기 전에 새 서버를 검증한다. 도메인은 그대로 두고 접속 대상 IP 만 갈아끼우는 방식이라, Host 헤더와 TLS SNI 가 실제 서비스와 동일하게 동작한다. hosts 파일을 건드리는 것보다 안전하다.

curl -sI --resolve devin.kr:443:10.0.1.20 https://devin.kr/

5. 어느 구간이 느린지 나눈다. 응답이 느리다는 신고를 DNS, TCP, TLS, 서버 처리로 쪼갠다.

curl -s -o /dev/null -w 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' https://devin.kr/
dns=0.004512 connect=0.008901 tls=0.061233 ttfb=0.132877 total=0.133402

ttfbtls 의 차이가 크면 서버 처리 시간이고, dns가 크면 리졸버 문제다.

6. 인증서 만료일을 확인한다. 갱신 누락은 새벽에 터진다.

curl -sIv https://devin.kr/ 2>&1 | grep -i 'expire date'

함정과 주의점

-s 만 쓰면 에러가 통째로 사라진다. cron 에 curl -s를 넣어두면 DNS 실패든 연결 거부든 아무 소리 없이 넘어간다. 실무에서는 -sS를 짝으로 쓴다.

HTTP 500 을 받아도 curl 의 종료코드는 0 이다. curl 입장에서는 응답을 잘 받아온 것이기 때문이다. 배포 검증 스크립트에서 curl ... && echo OK 같은 식으로 쓰면 서버가 죽어도 OK 가 찍힌다. 반드시 -f를 붙이거나 -w '%{http_code}'로 숫자를 직접 비교한다.

URL 을 따옴표로 감싸지 않으면 잘린다. curl http://host/api?a=1&b=2 는 셸이 &를 백그라운드 실행으로 해석해 ?a=1 까지만 요청하고 b=2는 명령으로 실행하려 한다. 파라미터가 있으면 무조건 작은따옴표로 감싼다.

-k(인증서 검증 생략)를 습관으로 만들지 않는다. 한 번 쓰면 계속 쓰게 되고, 그러면 실제 인증서 문제를 영영 발견하지 못한다. 사설 인증서라면 --cacert 사내CA.pem 으로 제대로 검증하는 편이 낫다.

-X POST-L을 같이 쓸 때 조심한다. 301/302 를 따라갈 때 curl 은 원래 메서드를 유지하려 해서 의도치 않은 POST 재전송이 일어날 수 있다. 리다이렉트 뒤 GET 이 맞다면 -X를 빼고 -d만 쓴다.

함께 보면 좋은 명령어

  • wget — 파일을 받고 이어받는 용도에서는 curl 보다 손이 덜 간다.
  • jq — curl 이 뱉은 JSON 을 사람이 읽을 형태로 정리하고 특정 필드만 뽑는다.
  • ss — curl 이 연결 거부를 받았을 때, 서버 쪽에서 그 포트가 실제로 열려 있는지 확인한다.