REST API에서 자주 사용하는 API Key, 세션, JWT, OAuth2와 Authorization 헤더를 한눈에 정리한 개념 일러스트입니다.
REST API 7일차 : 인증/인가 70% 이해하기 – API Key, 세션, JWT, OAuth2와 Authorization 헤더, Bearer 토큰 실습
한눈에 보는 요약
REST API를 실무에서 안전하게 쓰려면 “인증(누구인가)”과 “인가(무엇까지 허용할 것인가)”를 구분해서 이해하는 것이 중요합니다. 이 둘이 전체 API 설계와 운영의 70%를 차지한다고 봐도 과언이 아닙니다.
가장 기본적인 방식인 API Key와 세션/쿠키, 토큰 기반 인증의 대표 주자인 JWT, 그리고 외부 서비스 로그인을 가능하게 하는 OAuth2까지의 큰 그림을 먼저 잡아두면, 어떤 프로젝트에서 어떤 방식을 선택해야 할지 감이 잡힙니다.
모든 방식은 결국 HTTP 요청의 헤더, 특히 Authorization 헤더에 “누가, 어떤 권한으로” 요청하는지를 담는 구조를 가집니다. HTTPS 사용, 토큰 만료 시간, 저장 위치만 잘 지켜도 보안 수준이 크게 올라갑니다.
마지막으로 실습에서는 Bearer 토큰을 이용해 실제 요청을 구성하는 방법을 curl 예제와 함께 단계별로 정리합니다.
목차
- 1. 인증과 인가, 개념부터 구분하기
- 2. API Key, 세션, JWT, OAuth2 한눈에 보기
- 3. Authorization 헤더와 보안 기본 원칙
- 4. 실습: Bearer 토큰으로 REST API 호출하기
- 5. 따라 하기: 내 프로젝트에 적용하는 순서
- 6. 추가로 생각해볼 점
- 7. 블로그 최적화 정보
핵심 포인트
- 인증(Authentication)은 “당신이 누구인가”를 확인하는 과정이고, 인가(Authorization)는 “당신이 무엇까지 할 수 있는지”를 결정하는 과정입니다.
- API Key는 간단하지만, 보안과 권한 관리 측면에서는 한계가 있어 보통 서버 간 통신이나 내부 시스템에서 많이 사용합니다.
- 세션/쿠키 방식은 전통적인 웹 로그인 방식으로, 서버가 세션을 저장하고 클라이언트는 세션 ID만 보냅니다.
- JWT(Json Web Token)는 토큰 자체에 사용자 정보와 권한을 담는 방식으로, 주로 SPA + 백엔드 API 구조에서 많이 사용됩니다.
- OAuth2는 “카카오/구글/깃허브로 로그인” 같은 외부 인증 위임을 가능하게 하는 프로토콜입니다.
Authorization헤더의 형태(예:Bearer <token>,Basic <value>)만 이해해도 대부분의 REST API 인증 문서를 읽고 구현할 수 있습니다.- 실전에서는 항상 HTTPS를 사용하고, 토큰/키를 코드에 하드코딩하지 않으며, 만료 시간과 권한 범위(scope)를 최소화하는 것이 중요합니다.
상세 설명
1. 인증과 인가, 왜 나누어 생각해야 할까?
먼저 용어부터 분리해 보겠습니다. 인증(Authentication)은 “이 사용자가 정말 이 계정의 주인인가?”를 확인하는 절차입니다. 예를 들어, 아이디/비밀번호 입력, 휴대폰 본인 인증, OTP 입력 등이 모두 인증에 해당합니다.
인가(Authorization)는 “인증이 끝난 사용자가 어떤 기능까지 사용할 수 있는지”를 결정하는 단계입니다. 관리자만 주문 취소가 가능하다거나, 특정 프로젝트에 초대된 사용자만 그 데이터를 조회할 수 있게 하는 것이 인가입니다.
실무에서 API 설계를 하다 보면, “로그인만 되면 다 되는” 구조로 설계했다가 나중에 권한을 세분화해야 해서 시스템을 크게 고치는 경우가 많습니다. 시작 단계에서부터 인증과 인가를 구분해 모델링해 두면, 권한이 복잡해져도 구조를 유지하기 쉬워집니다.
2. API Key, 세션, JWT, OAuth2 한눈에 비교
REST API에서 많이 쓰는 네 가지 인증 방식을 간단히 정리하면 다음과 같습니다.
인증 방식 비교 표
아래 표는 주요 인증/인가 방식의 특징과 사용 예를 정리한 것입니다.
| 방식 | 주요 용도 | 어디에 저장? | 장점 | 단점/주의점 | 예시 |
|---|---|---|---|---|---|
| API Key | 서버 간 통신, 간단한 외부 연동 | 서버 환경 변수, 클라이언트 설정 | 구현이 매우 단순, 빠르게 적용 가능 | 노출 시 큰 피해, 사용자별 정교한 권한 관리 어렵고 회전(rotation) 관리 필요 | 외부 결제 API, 지도 API 호출 |
| 세션/쿠키 | 전통적인 웹 로그인 | 세션: 서버 메모리/DB, 쿠키: 브라우저 | 서버가 상태를 관리해 보안·제어가 상대적으로 쉬움 | 서버 확장 시 세션 공유 필요, 모바일/SPA와 궁합이 떨어질 수 있음 | 일반 웹사이트 로그인, 관리자 페이지 |
| JWT | 토큰 기반 REST API 인증 | 브라우저 로컬스토리지/쿠키, 모바일 앱 저장소 | 서버가 별도 세션 저장 없이 토큰만 검증하면 됨, 분산 환경에 적합 | 토큰 탈취 시 만료까지 위험, 페이로드에 민감한 정보 저장 금지 | SPA(React, Vue) + 백엔드 API 인증 |
| OAuth2 | 외부 계정으로 로그인, 권한 위임 | Access Token/Refresh Token을 클라이언트에 저장 | 카카오/구글/깃허브 계정을 재사용, 권한 범위(scope) 세분화 | 프로토콜이 상대적으로 복잡, 구현 실수 시 보안 이슈 | “구글 계정으로 로그인”, “깃허브 권한 허용” |
2-1. API Key
API Key는 말 그대로 “열쇠”에 해당합니다. 특정 서비스를 사용하기 위해 발급받는 긴 문자열로, 보통 HTTP 헤더나 쿼리 파라미터로 함께 전송합니다. 사용자는 키를 알고 있는지만 증명하면 되기 때문에, 별도의 로그인 과정 없이도 호출이 가능합니다.
장점은 매우 간단하다는 점입니다. 하지만 키가 유출되면 상대가 해당 서비스에 마음대로 접근할 수 있으므로, 반드시 서버 환경 변수에 저장하고 클라이언트 코드에 직접 노출하지 않는 것이 중요합니다.
2-2. 세션/쿠키
세션/쿠키 방식은 가장 오래되고 널리 쓰이는 웹 인증 방식입니다. 사용자가 아이디/비밀번호로 로그인하면 서버가 세션 ID를 생성하고, 이 ID를 브라우저의 쿠키에 담아 돌려보냅니다. 이후 요청마다 쿠키가 자동으로 전송되므로, 서버는 세션 저장소에서 해당 ID를 찾아 사용자 정보를 읽습니다.
서버가 세션 상태를 들고 있기 때문에, “강제 로그아웃”이나 “세션 만료” 같은 제어가 비교적 쉽습니다. 다만 서버를 여러 대로 확장할 경우 세션을 공유하는 고민이 필요하고, 모바일 앱이나 순수 API 클라이언트에서는 쿠키 기반 흐름이 다소 번거로울 수 있습니다.
2-3. JWT(Json Web Token)
JWT는 “자기소개서가 붙어 있는 출입증”에 비유할 수 있습니다. 토큰 안에 누가(사용자 ID), 어떤 권한을 가지고 있는지, 언제 만료되는지 등의 정보를 JSON 형태로 담고, 이것을 인코딩 + 전자서명해서 전달합니다.
서버는 이 토큰을 받아 서명만 검증한 뒤, 토큰 안에 들어있는 정보를 그대로 신뢰합니다(물론 올바른 서명일 경우). 따라서 서버에 별도의 세션 저장소가 필요 없고, 여러 서버가 동시에 토큰을 검증하기 쉬워 분산 환경에 적합합니다.
하지만 토큰을 한 번 발급하면 만료 전까지는 서버가 강제 폐기가 어렵다는 점(별도의 블랙리스트 관리 필요), 토큰 탈취 시 위험하다는 점 등을 반드시 고려해야 합니다.
2-4. OAuth2
OAuth2는 인증 수단이라기보다는 “권한 위임을 위한 프로토콜”입니다. 가장 익숙한 사례는 “구글 계정으로 로그인”, “카카오 계정으로 로그인” 같은 소셜 로그인이며, 사용자는 A 서비스에 로그인해 있으면서 B 서비스에 자신의 일부 정보(이메일, 프로필 등)에 접근할 수 있는 권한을 위임합니다.
OAuth2 흐름에서는 “Authorization Code”, “Access Token”, “Refresh Token” 등의 개념이 등장합니다. 처음에는 복잡해 보이지만, 큰 흐름은 다음과 같이 요약할 수 있습니다.
- 사용자를 인증 서버(구글/카카오 등) 로그인 페이지로 보낸다.
- 사용자가 로그인 및 권한 허용을 완료하면, 우리 서버는 Authorization Code를 받는다.
- 이 코드를 사용해 인증 서버에 Access Token을 요청한다.
- 이후에는 Access Token을
Authorization: Bearer <token>형태로 API에 첨부한다.
즉, OAuth2도 결국 마지막 단계에서는 “Bearer 토큰을 헤더에 실어 보내는 패턴”으로 귀결됩니다.
3. Authorization 헤더와 보안 기본
지금까지 본 모든 방식은 결국 HTTP 요청에 “누가, 어떤 권한으로” 접근하는지 표시해야 합니다. 이때 가장 자주 쓰이는 헤더가 Authorization 헤더입니다.
3-1. Authorization 헤더 형태
대표적인 Authorization 헤더 형태는 다음과 같습니다.
Authorization: Bearer <access_token>– JWT나 OAuth2에서 많이 사용Authorization: Basic <base64(username:password)>– 간단한 Basic 인증Authorization: ApiKey <api_key_value>– 일부 서비스에서 정의한 커스텀 스킴
공통점은 “스킴(Scheme) + 공백 + 인증 값” 형태라는 점입니다. 서버는 스킴을 보고 어떤 방식으로 이 값을 해석할지 결정합니다.
3-2. 보안 기본 수칙
인증/인가를 설계할 때, 최소한 다음 네 가지는 기본으로 지켜야 합니다.
- 반드시 HTTPS 사용 – 토큰과 패스워드는 네트워크 구간에서 평문으로 보이면 안 됩니다.
- 토큰/키를 코드에 하드코딩하지 않기 – 환경 변수(.env 파일), Secret Manager 등에 보관합니다.
- 만료 시간(exp) 설정 – Access Token은 짧게, Refresh Token이나 API Key는 주기적으로 교체합니다.
- 필요 최소 권한만 부여 – OAuth2의 scope, 롤(Role) 등을 활용해 “할 수 있는 일”을 제한합니다.
REST API 개념이 아직 낯설다면, 이 글을 다 읽은 뒤 API 설계 시리즈의 앞 글(REST API 기본 개념 정리)을 함께 보시면 큰 그림을 잡는 데 도움이 됩니다.
실습: Bearer 토큰 요청 구성
이제 직접 Authorization: Bearer <token> 헤더를 붙여 API를 호출해 보겠습니다. 여기서는 “이미 어딘가에서 Access Token을 발급받았다”고 가정하고, 토큰을 활용해 실제 사용자 정보를 조회하는 예제를 사용합니다.
4-1. 순수 HTTP 요청 형태 이해하기
GET /v1/me HTTP/1.1 Host: api.example.com Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.... Accept: application/json 위 예시는 /v1/me 엔드포인트로 현재 사용자 정보를 조회하는 HTTP 요청입니다. 핵심은 세 번째 줄의 Authorization 헤더로, Bearer 스킴 뒤에 Access Token(JWT 등)을 그대로 붙여 보낸다는 점입니다.
4-2. curl로 Bearer 토큰 요청 보내기
ACCESS_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...."
curl -X GET "https://api.example.com/v1/me"
-H "Authorization: Bearer ${ACCESS_TOKEN}"
-H "Accept: application/json"
위 스크립트는 리눅스/맥/WSL 환경에서 실행할 수 있는 예시입니다. 먼저 ACCESS_TOKEN 환경 변수에 토큰 값을 저장하고, curl 명령에서 Authorization 헤더에 Bearer 스킴과 함께 붙여 보냅니다. 실무에서는 토큰 값을 직접 노출하기보다 환경 변수나 설정 파일에서 읽어오는 방식으로 관리합니다.
4-3. JavaScript(fetch)로 Bearer 토큰 요청 보내기
const accessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....";
fetch("https://api.example.com/v1/me", {
method: "GET",
headers: {
"Authorization": Bearer ${accessToken},
"Accept": "application/json"
}
})
.then((res) => {
if (!res.ok) {
throw new Error(HTTP error! status: ${res.status});
}
return res.json();
})
.then((data) => {
console.log("현재 사용자 정보:", data);
})
.catch((error) => {
console.error("API 호출 실패:", error);
});
프론트엔드 코드에서도 패턴은 동일합니다. headers 객체에 Authorization 필드를 추가하고, 값으로 Bearer와 토큰 문자열을 함께 넣습니다. 이때 토큰은 보통 로그인 직후 백엔드에서 받은 값을 로컬스토리지나 쿠키 등에 저장해두었다가 꺼내 사용합니다.
실행 단계: 내 서비스에 인증/인가 적용하기
- 서비스 특성에 맞는 인증 방식 선택
순수 백엔드–백엔드 통신 위주라면 API Key, 일반 웹 서비스라면 세션/쿠키, SPA나 모바일 앱이라면 JWT 또는 OAuth2 기반 토큰 방식을 우선 후보로 정합니다. - 인증과 인가 도메인 모델 정의
사용자(User), 권한(Roles/Permissions), 그룹/조직 등 핵심 엔터티를 도식화합니다. “어떤 화면/기능을 누가 쓸 수 있어야 하는가?”를 표로 작성해 두면 나중에 정책 변경에 대응하기 쉽습니다. - 토큰/키 발급 및 검증 로직 구현
로그인 성공 시 어떤 조건으로 토큰을 발급하고, 만료/갱신을 어떻게 처리할지 결정합니다. JWT를 사용할 경우 서명 알고리즘(예: HS256)과 비밀키 관리 전략을 함께 설계합니다. - Authorization 헤더 처리 미들웨어 작성
모든 API 요청 앞단에 인증 미들웨어를 두고, 여기서 토큰을 파싱해 사용자 정보를 컨텍스트(예:req.user)에 주입합니다. 인가(권한 체크)는 각 핸들러나 별도 권한 미들웨어에서 수행합니다. - 테스트 및 모니터링
정상 로그인/로그아웃뿐 아니라, 만료된 토큰, 잘못된 서명, 권한 부족 등 에러 케이스를 테스트합니다. 로그로 누가 언제 어떤 API를 호출했는지 남겨두면 보안 사고 대응에 도움이 됩니다.
추가로 생각해볼 점
- JWT를 사용할 경우, 토큰의 페이로드에는 주민등록번호, 비밀번호 등 민감한 정보를 절대 넣지 않고, 꼭 필요한 최소 정보만 담는 것이 좋습니다.
- OAuth2/OpenID Connect를 직접 구현하기보다, 검증된 라이브러리나 인증 서버(Keycloak, Auth0 등)를 활용하는 것이 장기적으로는 더 안전하고 유지보수에 유리합니다.
- 인증/인가 로직은 비즈니스 로직과 분리된 모듈로 설계해두면, 정책이 바뀌거나 인증 공급자를 교체할 때 코드 변경 범위를 크게 줄일 수 있습니다.

0 댓글