CORS 없이 SOP 우회하기: 프록시 서버를 활용한 스마트한 해결책

2025. 6. 24. 21:30·Frontend Development
반응형

문제 상황: 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 서버 사이의 통신을 중개합니다.

작동 원리

  1. 브라우저 → 같은 도메인의 프록시 서버로 요청
  2. 프록시 서버 → 외부 API 서버로 요청 전달
  3. 외부 API 서버 → 프록시 서버로 응답
  4. 프록시 서버 → 브라우저로 응답 전달

프록시 서버 동작 원리

구체적인 예시를 통해 프록시 서버의 동작 원리를 살펴보겠습니다.

시나리오

  • 클라이언트 도메인: 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를 하나의 엔드포인트로 통합하고 싶은 경우

핵심 인사이트

  1. 서버 간 통신에는 SOP가 적용되지 않는다는 점을 활용
  2. 클라이언트와 같은 도메인의 프록시 서버를 통해 우회
  3. 추가적인 보안과 제어를 얻을 수 있는 장점
  4. 성능 오버헤드를 고려한 설계 필요

프록시 서버는 단순히 CORS 문제를 해결하는 것을 넘어서, 더 안전하고 제어 가능한 API 통신 아키텍처를 구축할 수 있게 해주는 강력한 패턴입니다.


참고 자료

  • MDN - Same-origin policy
  • MDN - CORS
  • Express HTTP Proxy Middleware
  • Next.js API Routes
반응형
저작자표시 비영리 변경금지 (새창열림)

'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
'Frontend Development' 카테고리의 다른 글
  • JavaScript 프로토타입 상속: 객체 간 상속의 핵심 메커니즘 완전 정복
  • HTML DOCTYPE 완전 정복: 웹 브라우저의 첫 번째 선택지
  • URI vs URL vs URN: 웹 자원 식별의 핵심 개념 완전 정복
  • event.target vs event.currentTarget: JavaScript 이벤트 처리의 핵심 개념 완전 정복
Kun Woo Kim
Kun Woo Kim
안녕하세요, 김건우입니다! 웹과 앱 개발에 열정적인 전문가로, React, TypeScript, Next.js, Node.js, Express, Flutter 등을 활용한 프로젝트를 다룹니다. 제 블로그에서는 개발 여정, 기술 분석, 실용적 코딩 팁을 공유합니다. 창의적인 솔루션을 실제로 적용하는 과정의 통찰도 나눌 예정이니, 궁금한 점이나 상담은 언제든 환영합니다.
  • Kun Woo Kim
    WhiteMouseDev
    김건우
  • 깃허브
    포트폴리오
    velog
  • 전체
    오늘
    어제
  • 공지사항

    • [인사말] 이제 티스토리에서도 만나요! WhiteMouse⋯
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 분류 전체보기 (100) N
      • Frontend Development (39) N
      • Backend Development (21) N
      • Algorithm (33) N
        • 백준 (11) N
        • 프로그래머스 (17)
        • 알고리즘 (5)
      • Infra (1)
      • 자료구조 (3)
  • 링크

    • Github
    • Portfolio
    • Velog
  • 인기 글

  • 태그

    tailwindcss
    frontend development
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Kun Woo Kim
CORS 없이 SOP 우회하기: 프록시 서버를 활용한 스마트한 해결책
상단으로

티스토리툴바