프리플라이트(Preflight) 요청이란? CORS와의 관계 및 예시 코드로 알아보기
웹 개발을 하다 보면, 특히 서로 다른 도메인 간의 자원 요청이 필요한 경우, 우리는 CORS(Cross-Origin Resource Sharing) 문제를 자주 마주하게 됩니다.
이때 등장하는 것이 바로 프리플라이트(Preflight) 요청입니다.
프리플라이트 요청은 CORS와 밀접한 관계가 있으며, 이를 이해하는 것은 웹 애플리케이션의 보안과 성능에 매우 중요합니다.
이번 포스팅에서는 프리플라이트 요청이 무엇인지, CORS와의 관계는 어떻게 되는지, 그리고 예시 코드를 통해 쉽게 설명해 보겠습니다.
CORS와 프리플라이트 요청이란?
우선, CORS(Cross-Origin Resource Sharing)부터 간단히 설명하겠습니다.
CORS는 한 도메인에서 다른 도메인의 리소스를 요청할 때 발생하는 보안 메커니즘입니다.
예를 들어, http://example.com
에서 http://api.example.com
의 데이터를 요청하는 것은 도메인 간 자원 공유에 해당하며, 기본적으로 웹 브라우저는 이러한 요청을 제한합니다.
이는 보안상의 이유로 Same-Origin Policy(동일 출처 정책)이 적용되기 때문입니다.
하지만 웹 애플리케이션에서는 종종 서로 다른 도메인 간의 데이터를 요청해야 할 때가 있습니다.
이를 허용하기 위해 CORS 헤더를 통해 서버가 특정 도메인에 자원을 제공할 수 있는지 여부를 결정하게 됩니다.
프리플라이트(Preflight) 요청은 무엇일까요?
프리플라이트 요청은 브라우저가 실제 요청 전에 서버에 보내는 사전 검사 요청입니다.
이 요청을 통해 브라우저는 서버가 해당 요청을 허용하는지 확인합니다.
프리플라이트 요청은 주로 다음과 같은 경우에 발생합니다.
- PUT, DELETE 등 비표준 HTTP 메서드를 사용하는 경우.
- 커스텀 헤더를 포함하는 경우.
- Content-Type이
application/json
또는multipart/form-data
와 같은 경우.
프리플라이트 요청은 OPTIONS 메서드를 사용해 서버로 보내지며, 서버는 이 요청에 대한 응답으로 CORS 헤더를 설정하여 클라이언트가 실제 요청을 할 수 있는지 알려줍니다.
프리플라이트 요청의 중요성
프리플라이트 요청은 웹 애플리케이션의 보안을 강화하고, 악성 요청으로부터 서버를 보호하는 중요한 역할을 합니다.
서버는 프리플라이트 요청을 통해 어떤 출처에서, 어떤 메서드로, 어떤 헤더가 포함된 요청이 올지 미리 확인할 수 있으며, 요청을 허용할지 거부할지 결정할 수 있습니다.
이를 통해 서버는 안전하게 CORS 정책을 관리하고, 클라이언트는 서버가 해당 요청을 허용하는지 사전에 알 수 있게 됩니다.
프리플라이트 요청의 예시 코드
이제 프리플라이트 요청이 어떻게 동작하는지 간단한 예시 코드로 살펴보겠습니다.
클라이언트 측 코드 (JavaScript - Fetch API)
fetch('https://api.example.com/data', {
method: 'POST', // 비표준 메서드
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer my-token' // 커스텀 헤더
},
body: JSON.stringify({ key: 'value' })
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
위 코드에서 POST 메서드로 데이터를 전송하며, 커스텀 헤더인 Authorization
을 포함하고 있습니다.
이때 브라우저는 먼저 OPTIONS 메서드를 사용하여 프리플라이트 요청을 서버로 보냅니다.
서버 측 코드 (Node.js - Express)
const express = require('express');
const app = express();
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*'); // 모든 출처 허용
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); // 허용하는 메서드
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization'); // 허용하는 헤더
next();
});
app.options('*', (req, res) => {
res.send(); // 프리플라이트 요청에 대한 응답
});
app.post('/data', (req, res) => {
res.json({ message: 'Data received successfully' });
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
위의 서버 코드는 클라이언트의 프리플라이트 요청에 대해 적절한 CORS 헤더를 반환합니다.
- Access-Control-Allow-Origin: 모든 출처에서의 요청을 허용.
- Access-Control-Allow-Methods: GET, POST, PUT, DELETE 등 다양한 메서드를 허용.
- Access-Control-Allow-Headers:
Content-Type
,Authorization
등 필요한 헤더를 허용.
클라이언트가 프리플라이트 요청에 대해 허가를 받으면, 이후 실제 POST 요청이 서버로 전송됩니다.
프리플라이트 요청 최적화 팁
프리플라이트 요청은 중요한 보안 요소이지만, 성능 저하를 유발할 수 있습니다.
특히, 프리플라이트 요청이 빈번하게 발생하면 네트워크 대기 시간이 증가할 수 있습니다.
이를 최적화하는 방법은 다음과 같습니다.
- HTTP 메서드와 헤더 최소화: 프리플라이트 요청은 비표준 메서드(POST, PUT, DELETE 등)와 커스텀 헤더로 인해 발생합니다. 가능하다면 GET 요청을 사용하거나, 불필요한 커스텀 헤더 사용을 피하면 프리플라이트 요청을 줄일 수 있습니다.
- 서버 캐싱 사용: 서버에서
Access-Control-Max-Age
헤더를 설정하면, 프리플라이트 요청의 결과를 브라우저에 캐싱할 수 있습니다. 이를 통해 동일한 출처에 대해 반복되는 프리플라이트 요청을 줄일 수 있습니다.
res.header('Access-Control-Max-Age', '600'); // 10분 동안 프리플라이트 결과 캐싱
프리플라이트 요청, 왜 중요한가?
프리플라이트 요청은 CORS 정책의 중요한 부분으로, 서버와 클라이언트 간의 안전한 통신을 보장합니다.
브라우저는 이를 통해 서버가 요청을 허용할지 여부를 사전 확인하며, 서버는 필요한 정책을 설정해 악의적인 요청을 차단할 수 있습니다.
프리플라이트 요청은 성능 측면에서 주의해야 하지만, 적절한 최적화를 통해 효율적인 시스템을 구현할 수 있습니다.
이를 통해 더 안전하고 신뢰할 수 있는 웹 애플리케이션을 구축해보세요!
'Programming & Platform > Web' 카테고리의 다른 글
HTTP 메시지 구조 이해하기 - 요청과 응답의 기본 구성 요소 (1) | 2024.11.16 |
---|---|
웹 통신의 핵심 프로토콜, HTTP의 특징 알아보기 (1) | 2024.11.16 |
웹 개발에서 캐시 무효화를 위한 쿼리 문자열 활용하기 - 꼭 알아야 할 실전 팁 (0) | 2024.11.13 |
CSS란 무엇인가요 사용자가 알아야 할 CSS의 모든 것 (0) | 2024.07.04 |
HTML 태그의 대표적인 종류와 사용법 (0) | 2024.06.21 |