
API 토큰 인증, 실무에서는 어떻게 쓰나?
Python 예제로 이해하는 Authorization, Accept, Content-Type 완전 정리
API를 처음 붙일 때 가장 먼저 마주치는 부분이 인증입니다.
특히 아래처럼 헤더를 만드는 코드는 거의 모든 프로젝트에서 한 번쯤 보게 됩니다.
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json",
"Content-Type": "application/json",
}
겉보기에는 단순하지만, 실제로는 다음과 같은 질문이 생깁니다.
- 왜
Authorization에는Bearer를 붙일까? Accept와Content-Type은 뭐가 다를까?application/json말고 다른 값은 언제 쓰나?- 토큰 인증 방식은 이것 말고도 뭐가 있을까?
이 글에서는 API 인증 방식 중 가장 많이 쓰이는 방식을 중심으로,
실무에서 자주 보는 헤더들의 의미와 선택 기준을 Python 예제와 함께 정리해보겠습니다.
1. API 인증 방식, 어떤 것들이 있나?
API 인증에는 여러 방식이 있지만, 많이 쓰이는 대표 방식은 아래 정도로 정리할 수 있습니다.
1) Bearer Token 방식
가장 흔하게 보는 형태입니다.
Authorization: Bearer <token>
주로 다음 상황에서 많이 사용합니다.
- REST API
- SaaS 서비스 API
- OpenAPI
- OAuth 2.0 기반 인증
- 서버 간 통신
특징
- 헤더에 토큰만 넣으면 되어서 사용이 간단함
- 세션 기반보다 API 친화적임
- 모바일 앱, 프론트엔드, 백엔드 모두에서 널리 사용됨
2) API Key 방식
토큰과 비슷하지만, 보통 서비스에서 발급한 고정 키를 전달하는 방식입니다.
예:
Authorization: ApiKey <api_key>
또는
X-API-Key: <api_key>
특징
- 구현이 단순함
- 서버 간 간단한 인증에 자주 사용
- 권한 범위나 만료 관리가 Bearer/OAuth보다 약한 경우가 많음
3) Basic Authentication
아이디와 비밀번호를 Base64로 인코딩해서 보내는 방식입니다.
Authorization: Basic <base64(username:password)>
특징
- 오래된 시스템이나 간단한 내부 서비스에서 여전히 사용
- 비밀번호 자체에 가까운 정보가 오가므로 보안상 주의 필요
- 반드시 HTTPS와 함께 써야 함
4) OAuth 2.0 / OpenID Connect
사실 이것은 “헤더 형식”이라기보다 인증/인가 체계에 가깝습니다.
실제로 API 호출 시점에는 결과적으로 이런 형태가 많습니다.
Authorization: Bearer <access_token>
즉, OAuth 2.0을 써도 실제 호출 헤더는 Bearer Token인 경우가 많습니다.
2. 가장 많이 사용하는 방식은?
실무 기준으로 가장 흔하게 보는 방식은 보통 다음입니다.
Authorization: Bearer <token>
이 방식이 많이 쓰이는 이유는 간단합니다.
- 표준적이고
- 라이브러리 지원이 많고
- 프론트/백엔드/모바일 어디서든 쓰기 쉽고
- OAuth 2.0과도 자연스럽게 연결되기 때문입니다
즉,
“가장 많이 쓰는 인증 헤더 형식”을 꼽으라면 Bearer Token이라고 보면 됩니다.
3. 예제 코드부터 먼저 보기
가장 기본적인 Python requests 예제입니다.
import requests
token = "your_access_token"
url = "https://api.example.com/v1/users"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json",
"Content-Type": "application/json",
}
response = requests.get(url, headers=headers)
print(response.status_code)
print(response.text)
이 코드에서 핵심 헤더는 3개입니다.
AuthorizationAcceptContent-Type
이제 하나씩 정확히 보겠습니다.
4. Authorization 헤더란?
Authorization은 말 그대로 **“내가 누구이고, 어떤 자격으로 요청하는지”**를 서버에 전달하는 헤더입니다.
가장 흔한 형태
1) Bearer Token
headers = {
"Authorization": f"Bearer {token}"
}
의미:
- “이 토큰을 가진 사용자의 권한으로 요청합니다.”
실무에서 가장 많이 보게 되는 형태입니다.
2) Basic Authentication
import base64
username = "my_id"
password = "my_password"
credentials = f"{username}:{password}"
encoded = base64.b64encode(credentials.encode()).decode()
headers = {
"Authorization": f"Basic {encoded}"
}
의미:
- 아이디/비밀번호 기반 인증
3) API Key 계열
서비스마다 형식이 다릅니다.
headers = {
"Authorization": f"ApiKey {api_key}"
}
또는
headers = {
"X-API-Key": api_key
}
왜 형식이 다를까?
이 부분은 표준 + 서비스 정책의 차이입니다.
Bearer는 비교적 널리 통일된 형태ApiKey,Token,X-API-Key등은 서비스마다 다를 수 있음- 결국 API 문서에 정의된 방식을 따라야 함
Authorization 값의 대표 후보
Bearer <token>Basic <base64(username:password)>ApiKey <key>Token <token>- 사용자 정의 헤더 방식:
X-API-Key: <key>
어떤 기준으로 선택하나?
Bearer
다음일 때 많이 선택합니다.
- 최신 REST API
- OAuth 2.0 기반
- 액세스 토큰 사용
- 권한 분리, 만료 시간 관리가 필요한 경우
Basic
다음일 때 볼 수 있습니다.
- 오래된 시스템
- 내부망 서비스
- 빠르게 붙이는 간단한 인증
X-API-Key / ApiKey
다음일 때 자주 씁니다.
- 간단한 서버 간 호출
- 외부 파트너 API
- 사용자보다는 애플리케이션 단위 인증
5. Accept 헤더란?
Accept는 **“응답을 어떤 형식으로 받고 싶은지”**를 서버에 알려주는 헤더입니다.
즉,
Content-Type은 내가 보내는 데이터 형식Accept는 내가 받고 싶은 데이터 형식
이라는 차이가 있습니다.
가장 흔한 값
1) JSON 응답 원할 때
headers = {
"Accept": "application/json"
}
가장 흔한 형태입니다.
대부분의 REST API는 JSON을 기본으로 주고받습니다.
2) XML 응답 원할 때
headers = {
"Accept": "application/xml"
}
레거시 시스템이나 일부 기업용 API에서 여전히 사용됩니다.
3) 여러 형식을 허용할 때
headers = {
"Accept": "application/json, text/plain"
}
의미:
- JSON이면 가장 좋고, 아니면 plain text도 괜찮음
4) 아무 형식이나 허용
headers = {
"Accept": "*/*"
}
의미:
- 어떤 형식이든 받겠다
다만 실무에서는 응답 형식을 명확히 하기 위해 application/json처럼 구체적으로 쓰는 편이 좋습니다.
Accept 값의 대표 후보
application/jsonapplication/xmltext/plaintext/html*/*image/pngapplication/pdf
어떤 기준으로 선택하나?
application/json
- REST API 대부분
- 응답을 Python dict로 바로 처리하기 좋음
- 가장 일반적
application/xml
- XML 기반 시스템 연동
- 공공기관/레거시 시스템에서 종종 사용
text/plain
- 단순 문자열 응답
- 상태 메시지, 로그성 응답 등
application/pdf, image/png
- 파일 다운로드 API
- 보고서, 이미지, 문서 응답
*/*
- 응답 형식이 정해져 있지 않거나 크게 상관없을 때
- 하지만 디버깅과 유지보수 측면에서는 구체적으로 쓰는 것이 더 낫습니다
6. Content-Type 헤더란?
Content-Type은 **“내가 지금 서버로 보내는 본문(body)의 형식이 무엇인지”**를 알려주는 헤더입니다.
예를 들어 JSON 데이터를 보낸다면:
headers = {
"Content-Type": "application/json"
}
서버는 이 값을 보고
“아, 요청 본문을 JSON으로 파싱해야겠구나”
라고 판단합니다.
가장 흔한 값
1) JSON
headers = {
"Content-Type": "application/json"
}
가장 많이 사용합니다.
2) HTML Form 전송
headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
폼 입력값처럼 key=value 형태로 전송할 때 사용합니다.
예:
payload = {
"username": "alice",
"password": "secret"
}
3) 파일 업로드
headers = {
"Content-Type": "multipart/form-data"
}
파일과 일반 필드를 함께 보낼 때 사용합니다.
단, Python requests에서는 직접 이 값을 넣지 않는 경우가 많습니다.files= 파라미터를 쓰면 boundary까지 포함해서 자동으로 잡아주는 경우가 일반적입니다.
4) XML
headers = {
"Content-Type": "application/xml"
}
XML 본문을 보낼 때 사용합니다.
5) Plain Text
headers = {
"Content-Type": "text/plain"
}
텍스트 자체를 본문으로 보낼 때 사용합니다.
Content-Type 값의 대표 후보
application/jsonapplication/x-www-form-urlencodedmultipart/form-dataapplication/xmltext/plainapplication/octet-stream
어떤 기준으로 선택하나?
application/json
- REST API 본문 데이터 전송
- 객체/배열 구조가 필요할 때
- 가장 일반적
application/x-www-form-urlencoded
- 로그인 폼처럼 단순 key-value 전송
- HTML form 전통 방식
multipart/form-data
- 파일 업로드 필요
- 이미지, PDF, CSV 등 첨부 전송
application/xml
- XML 기반 외부 시스템 연동
text/plain
- 문자열 그대로 전송
application/octet-stream
- 바이너리 데이터 전송
- 파일 내용 자체를 raw로 보낼 때
7. Accept와 Content-Type의 차이, 헷갈리면 이렇게 기억하면 된다
이 둘은 정말 자주 헷갈립니다.
아주 단순하게 구분하면 아래와 같습니다.
Accept
나는 응답을 이 형식으로 받고 싶다
Content-Type
내가 보내는 요청 본문은 이 형식이다
예를 들어 JSON을 보내고 JSON을 받고 싶다면:
headers = {
"Accept": "application/json",
"Content-Type": "application/json",
}
반대로 파일을 업로드하고 JSON 응답을 받고 싶다면:
headers = {
"Accept": "application/json",
# Content-Type은 requests가 multipart/form-data로 자동 처리하는 경우가 많음
}
8. Python 실전 예제
예제 1) Bearer 토큰으로 GET 요청 보내기
import requests
token = "your_access_token"
url = "https://api.example.com/v1/profile"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json",
}
response = requests.get(url, headers=headers)
print("status:", response.status_code)
print("body:", response.json())
포인트
- GET 요청은 보통 body가 없으므로
Content-Type이 꼭 필요하지 않은 경우가 많음 - 대신
Accept는 명시해두면 응답 형식이 분명해짐
예제 2) JSON 데이터를 POST로 보내기
import requests
token = "your_access_token"
url = "https://api.example.com/v1/users"
payload = {
"name": "Alice",
"email": "alice@example.com"
}
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json",
"Content-Type": "application/json",
}
response = requests.post(url, headers=headers, json=payload)
print("status:", response.status_code)
print("body:", response.json())
포인트
json=payload를 쓰면 requests가 JSON 직렬화를 도와줌- 이 경우
Content-Type: application/json과 잘 맞음
예제 3) form-urlencoded 방식 전송
import requests
url = "https://api.example.com/login"
payload = {
"username": "alice",
"password": "secret"
}
headers = {
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded",
}
response = requests.post(url, headers=headers, data=payload)
print("status:", response.status_code)
print("body:", response.text)
이런 경우에 사용
- 로그인 API가 옛 방식 폼 데이터를 요구할 때
- 외부 솔루션 연동 시 문서에 form 전송이라고 되어 있을 때
예제 4) 파일 업로드
import requests
token = "your_access_token"
url = "https://api.example.com/v1/upload"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json",
}
files = {
"file": open("sample.pdf", "rb")
}
response = requests.post(url, headers=headers, files=files)
print("status:", response.status_code)
print("body:", response.text)
포인트
multipart/form-data는requests가 자동으로 처리하게 두는 편이 안전함- 직접
Content-Type을 수동 지정하면 boundary 문제로 실패할 수 있음
9. 실무에서 자주 하는 선택 기준 정리
API 문서를 볼 때 아래 순서로 판단하면 편합니다.
1) 인증 방식 확인
문서에 다음 중 무엇이 적혀 있는지 먼저 봅니다.
Authorization: Bearer <token>X-API-Key: <key>Authorization: Basic ...
대부분 여기서 이미 답이 나옵니다.
2) 요청 본문 형식 확인
본문을 보내는 API라면 다음을 봅니다.
- JSON 객체 예시가 있나?
→Content-Type: application/json - form 필드 나열 방식인가?
→application/x-www-form-urlencoded - 파일 업로드인가?
→multipart/form-data
3) 응답 형식 확인
응답 예시가 JSON이면 보통 이렇게 갑니다.
"Accept": "application/json"
응답이 파일이면 예를 들어:
- PDF 다운로드 →
Accept: application/pdf - 이미지 응답 →
Accept: image/png
10. 많이 쓰는 헤더 템플릿
가장 무난한 JSON API 템플릿은 아래입니다.
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json",
"Content-Type": "application/json",
}
이 템플릿이 잘 맞는 경우:
- Bearer 토큰 인증
- JSON 요청 본문
- JSON 응답
즉, 대부분의 현대적인 REST API에서 가장 먼저 시도해볼 수 있는 기본형입니다.
11. 주의할 점
1) GET 요청에는 Content-Type이 불필요할 수 있음
본문이 없는 GET 요청에서 Content-Type은 큰 의미가 없을 수 있습니다.
예:
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json",
}
2) 파일 업로드 시 Content-Type을 직접 박지 말 것
multipart/form-data는 boundary 정보가 필요해서,requests가 자동 생성하도록 두는 게 일반적입니다.
3) 토큰은 코드에 하드코딩하지 말 것
예제에서는 이해를 위해 문자열로 넣었지만, 실제로는 환경 변수로 관리하는 편이 좋습니다.
import os
token = os.getenv("API_TOKEN")
12. 마무리
API 인증 방식은 여러 가지가 있지만,
실무에서 가장 자주 보는 형태는 결국 이것입니다.
Authorization: Bearer <token>
그리고 함께 자주 쓰는 헤더는 아래 두 개입니다.
Accept: 응답을 어떤 형식으로 받을지Content-Type: 내가 어떤 형식으로 보낼지
정리하면 다음처럼 이해하면 됩니다.
- Authorization: 누구 자격으로 요청하는가
- Accept: 어떤 응답 형식을 원하는가
- Content-Type: 어떤 형식의 데이터를 보내는가
가장 흔한 조합은 아래입니다.
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json",
"Content-Type": "application/json",
}
이 조합은 토큰 인증 + JSON 요청/응답이라는 현대적인 API 환경에서 거의 표준처럼 쓰입니다.
보너스: 바로 가져다 쓸 수 있는 예제
import os
import requests
token = os.getenv("API_TOKEN")
url = "https://api.example.com/v1/resource"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json",
"Content-Type": "application/json",
}
payload = {
"name": "example",
"enabled": True
}
response = requests.post(url, headers=headers, json=payload)
print("status_code:", response.status_code)
print("response_text:", response.text)