개발일기

OPTIONS 요청이 왜 먼저 보내질까? Preflight 완전 정리

뱅우 2025. 5. 30. 11:33
반응형

"POST 요청을 보냈는데, 웬 OPTIONS 요청이 먼저 가더니 CORS 에러가 났어요!"
프론트와 백엔드 협업 중 자주 듣는 말이죠. 이 글에서 그 정체를 파헤쳐 보겠습니다.


🚀 Preflight 요청이란?

Preflight는 말 그대로 "사전 요청"입니다.
브라우저는 보안상 민감하거나 위험할 수 있는 요청에 대해, 먼저 서버에게 OPTIONS 메서드
"이 요청을 보내도 괜찮을까요?" 하고 물어보는 절차를 거칩니다.

즉, 본 요청 전에 보내는 사전 검사용 요청입니다.


🔍 언제 Preflight가 발생할까?

  • 요청 방식이 GET/POST 외의 메서드일 때 (예: PUT, DELETE)
  • Content-Type이 application/json 등 일반적인 타입이 아닐 때
  • 사용자 지정 헤더 (예: Authorization) 를 사용할 때

→ 이 조건에 해당하면 브라우저는 자동으로 OPTIONS 요청을 먼저 보냅니다.


📡 실제 네트워크 요청 흐름 예시

리액트에서 다음과 같은 POST 요청을 보냈다고 가정해보세요:


fetch('http://localhost:8080/api/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token'
  },
  body: JSON.stringify({ name: 'John' })
});

→ 브라우저는 먼저 아래와 같은 OPTIONS 요청을 서버에 보냅니다:


OPTIONS /api/data HTTP/1.1
Origin: http://localhost:3000
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization

서버는 이 요청에 대해 다음과 같이 응답해야 합니다:


HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization

🧱 서버가 응답을 누락하면?

브라우저는 본 요청을 아예 보내지 않고 CORS 에러를 발생시킵니다.


Access to fetch at 'http://localhost:8080/api/data' from origin 'http://localhost:3000'
has been blocked by CORS policy...

🔧 해결 방법

✅ Spring Boot


@Configuration
public class WebConfig implements WebMvcConfigurer {
  @Override
  public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**")
            .allowedOrigins("http://localhost:3000")
            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
            .allowedHeaders("*");
  }
}

✅ Node.js (Express)


const cors = require('cors');
app.use(cors({
  origin: 'http://localhost:3000',
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization']
}));

✅ Nginx 프록시 설정


location / {
  if ($request_method = OPTIONS ) {
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS, PUT, DELETE';
    add_header Access-Control-Allow-Headers 'Content-Type, Authorization';
    return 204;
  }
}

🙅‍♂️ 잘못된 해결 방식

  • OPTIONS 요청을 백엔드에서 막아버림 ❌
  • 브라우저 확장 프로그램으로 우회 ❌
  • Access-Control-Allow-Origin: * 로 모두 허용 (보안상 위험) ❌

📌 정리

항목 내용
Preflight란? 본 요청 전에 OPTIONS로 서버 허용 여부 확인
발생 조건 PUT/DELETE, application/json, Authorization 헤더 등
누가 보냄? 브라우저 (자동)
해결 방법 서버에서 OPTIONS 허용 및 응답 헤더 추가

반응형