✅ CORS 완전 정복
CORS(Cross-Origin Resource Sharing)는 웹 보안 정책과 클라이언트-서버 통신의 실제 구현이 맞닿는 지점으로,
실무에서 API 개발·프론트엔드 연동·보안 설정에서 매우 중요하게 다뤄집니다.
1. 📌 CORS란?
🔒 Same-Origin Policy (SOP)란?
- 브라우저 보안 정책의 일환으로, 출처(origin)가 다른 리소스에 대한 접근을 기본적으로 제한함.
- 출처는 다음 3가지 조합으로 구성됨:
protocol + domain + port
ex) https://api.example.com:443
🧭 CORS란?
- SOP의 예외를 만들기 위한 HTTP 기반 보안 메커니즘
- 서버가 명시적으로 허용한 도메인에만 리소스 접근을 허용하는 방식
즉, 클라이언트가 다른 출처로 요청을 보냈을 때, 서버가 허용하는 경우에만 응답을 받을 수 있도록 함.
2. 💬 CORS 동작 흐름 요약
[Client] 브라우저 → [Server] API 요청
|
├─ CORS 요청 헤더 포함 (Origin, etc)
↓
[Server] 응답 헤더에 CORS 허용 여부 명시
└─ Access-Control-Allow-Origin: https://myapp.com
🙋 CORS 요청의 종류
요청 타입 |
설명 |
예시 |
Simple Request |
GET, POST (with safe headers) 등 제한된 요청 |
일반적인 GET, POST |
Preflight Request |
CORS 조건이 복잡할 경우 사전 OPTIONS 요청 |
Content-Type: application/json , PUT, DELETE 등 |
Credentialed Request |
쿠키, 인증정보 포함 요청 |
withCredentials: true 사용 시 |
3. 🧪 CORS 요청 조건 예시
✅ Simple Request 조건 (예: 브라우저가 사전 검사 없이 바로 요청 가능)
- 메서드:
GET
, POST
, HEAD
- Header:
Content-Type
이 application/x-www-form-urlencoded
, multipart/form-data
, text/plain
중 하나
withCredentials
를 사용하지 않음
❌ Preflight이 필요한 경우
- 메서드가
PUT
, DELETE
, PATCH
일 때
Authorization
등 커스텀 헤더 사용 시
application/json
등의 Content-Type
4. 🛠️ 서버에서 CORS 설정 방법 (언어별 실무 예시)
Node.js (Express)
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: 'https://frontend.example.com',
credentials: true, // 쿠키/세션 허용
}));
Spring Boot
@CrossOrigin(origins = "https://frontend.example.com", allowCredentials = "true")
@RestController
public class ApiController {
@GetMapping("/data")
public ResponseEntity<?> getData() {
return ResponseEntity.ok(...);
}
}
Nginx (프록시 CORS 허용 설정)
location /api/ {
add_header 'Access-Control-Allow-Origin' 'https://frontend.example.com';
add_header 'Access-Control-Allow-Credentials' 'true';
...
}
5. 🧩 주요 CORS 응답 헤더 설명
헤더 |
의미 |
Access-Control-Allow-Origin |
허용할 출처 도메인 (예: https://example.com 또는 * ) |
Access-Control-Allow-Methods |
허용할 HTTP 메서드들 (예: GET, POST, PUT ) |
Access-Control-Allow-Headers |
허용할 요청 헤더 (예: Authorization, Content-Type ) |
Access-Control-Allow-Credentials |
쿠키 및 인증 포함 여부 (true ) |
Access-Control-Max-Age |
Preflight 응답을 브라우저가 캐시할 시간 (초 단위) |
6. ⚠️ 실무에서 자주 발생하는 문제 및 해결 전략
문제 |
원인 |
해결법 |
CORS policy error: No 'Access-Control-Allow-Origin' |
서버가 요청 도메인을 허용하지 않음 |
서버에서 Access-Control-Allow-Origin 설정 |
Preflight response not successful |
OPTIONS 요청에 대한 응답 없음 |
서버에 OPTIONS 메서드 처리 추가 |
쿠키가 전송되지 않음 |
서버 또는 클라이언트에서 credentials 설정 누락 |
withCredentials: true + Access-Control-Allow-Credentials: true |
'*' 와 credentials 동시 사용 |
보안 제한 |
'Access-Control-Allow-Origin' 에 명시적인 도메인 사용 필요 |
7. 💡 실무 팁
- API 서버와 프론트엔드가 분리된 구조(e.g., React + Express, Vue + Spring)에서는 반드시 CORS 설정 필요
- OAuth2 토큰 발급, 로그인 세션 기반 API의 경우 credentials 설정 필수
- 사내망 API, BFF(Backend for Frontend) 구조에서는 Nginx에서 CORS 처리하는 것도 성능상 유리
- CORS 문제가 발생하면 브라우저의 네트워크 탭에서 OPTIONS 요청과 응답 헤더를 반드시 확인
8. 🔐 CORS와 보안
- CORS는 보안 기능이 아니라 브라우저의 보호 메커니즘입니다.
- 서버는
Access-Control-Allow-Origin
을 통해 어떤 도메인에 데이터를 공개할지 선택함
- 중요한 API의 경우 API Gateway 또는 인증 토큰으로 추가적인 인증/인가 처리를 반드시 병행해야 함
✅ 요약
항목 |
내용 |
CORS 목적 |
브라우저에서 다른 출처의 리소스 접근 제한 완화 |
주요 요소 |
Origin, Allow-Origin, Credentials, Preflight |
적용 시기 |
API 서버와 프론트엔드 도메인이 다를 때 |
설정 방법 |
서버 (Express, Spring), 프록시 (Nginx 등) |
보안 주의 |
* 사용 제한, 토큰/세션 기반 인증 병행 필요 |
댓글