반응형
문제 상황: CORS와 SOP의 딜레마
웹 개발을 하다 보면 CORS(Cross-Origin Resource Sharing) 에러를 마주하게 됩니다. 이는 브라우저의 SOP(Same-Origin Policy) 보안 정책 때문인데, 서로 다른 도메인 간의 통신을 제한하는 중요한 보안 메커니즘입니다.
SOP란?
Same-Origin Policy는 웹 브라우저에서 실행되는 스크립트가 다른 출처(origin)의 리소스에 접근하는 것을 제한하는 보안 정책입니다. 여기서 출처(origin)는 다음 세 요소로 구성됩니다:
- 프로토콜 (http, https)
- 도메인 (example.com)
- 포트 (80, 443, 3000 등)
// 같은 출처 예시
https://example.com/page1
https://example.com/page2
// 다른 출처 예시
https://example.com (기본 사이트)
http://example.com (프로토콜 다름)
https://api.example.com (서브도메인 다름)
https://example.com:3000 (포트 다름)
해결책: 프록시 서버 패턴
CORS 설정 없이도 SOP를 우회할 수 있는 효과적인 방법이 바로 프록시 서버를 활용하는 것입니다.
프록시 서버란?
프록시 서버는 클라이언트와 서버 사이에서 중개자 역할을 수행하는 서버입니다. 마치 번역사가 서로 다른 언어를 사용하는 사람들 사이에서 의사소통을 돕는 것처럼, 프록시 서버는 브라우저와 외부 API 서버 사이의 통신을 중개합니다.
작동 원리
- 브라우저 → 같은 도메인의 프록시 서버로 요청
- 프록시 서버 → 외부 API 서버로 요청 전달
- 외부 API 서버 → 프록시 서버로 응답
- 프록시 서버 → 브라우저로 응답 전달
프록시 서버 동작 원리
구체적인 예시를 통해 프록시 서버의 동작 원리를 살펴보겠습니다.
시나리오
- 클라이언트 도메인:
https://myapp.com
- 외부 API 서버:
https://api.external.com
- 프록시 서버:
https://myapp.com/api/proxy
기존 방식 (CORS 에러 발생)
// ❌ CORS 에러 발생
fetch('https://api.external.com/users')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('CORS Error:', error));
프록시 서버 방식 (정상 동작)
// ✅ 정상 동작
fetch('/api/proxy/users') // 같은 도메인으로 요청
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
실제 구현 예시
Node.js + Express 프록시 서버 구현
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// 정적 파일 서빙 (클라이언트)
app.use(express.static('public'));
// 프록시 미들웨어 설정
const proxyOptions = {
target: 'https://api.external.com',
changeOrigin: true,
pathRewrite: {
'^/api/proxy': '', // /api/proxy를 제거하고 실제 API로 전달
},
onProxyRes: function (proxyRes, req, res) {
// 응답 헤더 수정 (필요시)
proxyRes.headers['Access-Control-Allow-Origin'] = '*';
}
};
// 프록시 라우트 설정
app.use('/api/proxy', createProxyMiddleware(proxyOptions));
app.listen(3000, () => {
console.log('프록시 서버가 포트 3000에서 실행 중입니다.');
});
Next.js에서의 프록시 구현
// pages/api/proxy/[...path].js
export default async function handler(req, res) {
const { path } = req.query;
const apiPath = Array.isArray(path) ? path.join('/') : path;
try {
const response = await fetch(`https://api.external.com/${apiPath}`, {
method: req.method,
headers: {
'Content-Type': 'application/json',
// 필요한 인증 헤더 추가
},
body: req.method !== 'GET' ? JSON.stringify(req.body) : undefined,
});
const data = await response.json();
res.status(response.status).json(data);
} catch (error) {
res.status(500).json({ error: '프록시 요청 실패' });
}
}
클라이언트 사용법
// React 컴포넌트에서 사용
const fetchUsers = async () => {
try {
const response = await fetch('/api/proxy/users');
const users = await response.json();
setUsers(users);
} catch (error) {
console.error('사용자 데이터 로드 실패:', error);
}
};
프록시 서버 vs CORS 비교
구분 | 프록시 서버 | CORS 설정 |
---|---|---|
설정 주체 | 클라이언트 측 | 서버 측 |
제어권 | 개발자가 완전 제어 | 외부 API 서버에 의존 |
보안 | 서버에서 추가 검증 가능 | 브라우저 정책에 의존 |
캐싱 | 서버에서 캐싱 구현 가능 | 브라우저 캐싱에 의존 |
에러 처리 | 세밀한 에러 처리 가능 | 제한적 에러 정보 |
성능 | 추가 홉(hop) 발생 | 직접 통신 |
주의사항과 고려사항
1. 성능 오버헤드
// 직접 통신 vs 프록시 통신
// 직접: 브라우저 → API 서버
// 프록시: 브라우저 → 프록시 서버 → API 서버 → 프록시 서버 → 브라우저
2. 서버 리소스 사용
프록시 서버는 추가적인 서버 리소스를 사용하므로, 트래픽이 많은 서비스에서는 성능 영향을 고려해야 합니다.
3. 보안 고려사항
// 민감한 API 키 보호
const proxyOptions = {
target: 'https://api.external.com',
changeOrigin: true,
onProxyReq: function (proxyReq, req, res) {
// 서버에서만 알고 있는 API 키 추가
proxyReq.setHeader('Authorization', `Bearer ${process.env.API_SECRET}`);
}
};
4. 개발 환경 설정
// webpack.config.js (개발 환경)
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'https://api.external.com',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
};
결론
프록시 서버를 활용한 SOP 우회는 CORS 설정이 불가능한 상황에서 매우 유용한 해결책입니다. 특히 다음과 같은 상황에서 효과적입니다:
언제 프록시 서버를 사용해야 할까?
- ✅ 외부 API의 CORS 설정을 변경할 수 없는 경우
- ✅ API 키와 같은 민감한 정보를 클라이언트에 노출하고 싶지 않은 경우
- ✅ API 응답을 서버에서 가공하거나 캐싱하고 싶은 경우
- ✅ 여러 외부 API를 하나의 엔드포인트로 통합하고 싶은 경우
핵심 인사이트
- 서버 간 통신에는 SOP가 적용되지 않는다는 점을 활용
- 클라이언트와 같은 도메인의 프록시 서버를 통해 우회
- 추가적인 보안과 제어를 얻을 수 있는 장점
- 성능 오버헤드를 고려한 설계 필요
프록시 서버는 단순히 CORS 문제를 해결하는 것을 넘어서, 더 안전하고 제어 가능한 API 통신 아키텍처를 구축할 수 있게 해주는 강력한 패턴입니다.
참고 자료
반응형
'Frontend Development' 카테고리의 다른 글
JavaScript 프로토타입 상속: 객체 간 상속의 핵심 메커니즘 완전 정복 (2) | 2025.06.20 |
---|---|
HTML DOCTYPE 완전 정복: 웹 브라우저의 첫 번째 선택지 (6) | 2025.06.12 |
URI vs URL vs URN: 웹 자원 식별의 핵심 개념 완전 정복 (0) | 2025.06.11 |
event.target vs event.currentTarget: JavaScript 이벤트 처리의 핵심 개념 완전 정복 (2) | 2025.06.11 |
CDN(Content Delivery Network) 완전 정복: 웹 성능 최적화의 핵심 기술 (0) | 2025.06.11 |