CORS가 뭐길래? 웹 개발자가 반드시 알아야 할 교차 출처 리소스 공유 (2부)

1부에서는 CORS의 개념과 등장 배경, 프리플라이트 요청에 대해 알아보았습니다. 이제 2부에서는 실제 개발 환경에서 CORS를 어떻게 구현하고, 문제를 어떻게 디버깅하며, 보안을 고려한 모범 사례는 무엇인지 알아보겠습니다.

CORS 문제는 웹 개발에서 자주 마주치게 되는 난관 중 하나입니다. 하지만 체계적인 접근 방법과 올바른 구현 지식을 갖추면 쉽게 해결할 수 있습니다. 이 글을 통해 CORS 관련 문제를 효과적으로 해결하는 방법을 배워보세요!


⚙️ 다양한 환경에서 CORS 설정하기

CORS 문제를 해결하려면 서버 측 설정이 필요합니다. 주요 환경별 설정 방법을 알아봅시다:

Node.js + Express

Node.js의 Express 프레임워크에서는 cors 미들웨어를 사용하면 간단합니다:

const express = require('express');
const cors = require('cors');
const app = express();

// 모든 출처 허용 (개발용)
app.use(cors());

// 또는 특정 출처만 허용 (프로덕션 권장)
app.use(cors({
  origin: 'https://lazybug.io',
  methods: ['GET', 'POST'],
  allowedHeaders: ['Content-Type', 'Authorization']
}));

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Python + Flask

Python의 Flask 프레임워크에서는 flask-cors 확장을 사용합니다:

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)

# 모든 라우트에 CORS 적용
CORS(app)

# 또는 특정 라우트에만 적용
CORS(app, resources={r"/api/*": {"origins": "https://lazybug.io"}})

@app.route('/api/data')
def get_data():
    return {"message": "Hello, CORS!"}

if __name__ == '__main__':
    app.run(debug=True)

Django

Django에서는 django-cors-headers 패키지를 사용할 수 있습니다:

# settings.py
INSTALLED_APPS = [
    # ...
    'corsheaders',
    # ...
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    # 다른 미들웨어보다 가능한 앞에 위치시키세요
    # ...
]

# 개발 환경 (모든 출처 허용)
CORS_ALLOW_ALL_ORIGINS = True

# 프로덕션 환경 (특정 출처만 허용)
CORS_ALLOWED_ORIGINS = [
    "https://lazybug.io",
    "https://api.lazybug.io",
]

Apache 서버

Apache에서는 .htaccess 파일을 사용하여 CORS를 설정할 수 있습니다:

Header set Access-Control-Allow-Origin "https://lazybug.io"
Header set Access-Control-Allow-Methods "GET, POST, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type, Authorization"

# OPTIONS 요청 처리
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]

Nginx 서버

Nginx에서는 설정 파일에 다음과 같이 추가합니다:

server {
    location / {
        add_header 'Access-Control-Allow-Origin' 'https://lazybug.io';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
        
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' 'https://lazybug.io';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
            add_header 'Content-Type' 'text/plain charset=UTF-8';
            add_header 'Content-Length' 0;
            return 204;
        }
    }
}

🔍 CORS 문제 해결을 위한 디버깅 전략

CORS 오류가 발생했을 때, 다음 단계로 문제를 해결해보세요:

1. 오류 메시지 정확히 파악하기

브라우저의 개발자 도구 콘솔에 표시되는 오류 메시지를 자세히 읽어보세요. 대부분의 경우 문제의 원인이 명확히 표시됩니다.

CORS 오류 예시

CORS 오류 메시지는 일반적으로 다음과 같은 형태입니다:

Access to fetch at 'https://api.example.com/data' from origin 'https://lazybug.io' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

이 메시지에서 중요한 정보는:

  • 요청하는 출처 (https://lazybug.io)
  • 요청 대상 URL (https://api.example.com/data)
  • 구체적인 CORS 오류 원인 (No 'Access-Control-Allow-Origin' header is present)

2. 네트워크 탭에서 요청/응답 헤더 확인

개발자 도구의 네트워크 탭에서 요청을 선택하고 헤더 탭을 확인합니다. 프리플라이트(OPTIONS) 요청과 응답에 적절한 CORS 헤더가 있는지 확인하세요.

특히 다음 헤더를 확인하세요:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods
  • Access-Control-Allow-Headers
  • Access-Control-Allow-Credentials (자격 증명을 사용하는 경우)

3. 서버 측 로그 확인

서버 로그를 확인하여 CORS 관련 헤더가 올바르게 설정되고 있는지 확인합니다. 로그에서 OPTIONS 요청이 어떻게 처리되고 있는지 살펴보세요.

4. 일반적인 CORS 문제 해결 방법

  • Origin 불일치: 요청 출처와 Access-Control-Allow-Origin 헤더 값이 정확히 일치하는지 확인
  • 헤더 누락: 필요한 모든 헤더가 Access-Control-Allow-Headers에 포함되었는지 확인
  • 메서드 누락: 사용 중인 HTTP 메서드가 Access-Control-Allow-Methods에 포함되었는지 확인
  • 자격 증명 문제: credentials: 'include'를 사용하는 경우 서버에서 Access-Control-Allow-Credentials: true로 설정되었는지 확인 (이 경우 Access-Control-Allow-Origin: *는 사용할 수 없음)

🧪 로컬 개발 환경에서 CORS 우회하기

개발 중에 CORS 문제를 임시로 우회하는 방법도 있습니다:

1. 프록시 서버 사용

Create React App, Vue CLI, Angular CLI 등 대부분의 프론트엔드 개발 환경에서는 개발 서버 프록시 설정을 제공합니다.

React (package.json):

{
  "proxy": "http://localhost:5000"
}

Vue (vue.config.js):

module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:5000',
        changeOrigin: true
      }
    }
  }
}

Angular (proxy.conf.json):

{
  "/api": {
    "target": "http://localhost:5000",
    "secure": false,
    "changeOrigin": true
  }
}

2. 브라우저 확장 프로그램

Chrome에서 “CORS Unblock”과 같은 확장 프로그램을 사용하면 개발 중에 CORS 제한을 우회할 수 있습니다. ⚠️ 하지만 이는 개발 용도로만 사용해야 합니다!

3. 브라우저 보안 비활성화 (매우 위험)

Chrome을 다음과 같이 실행할 수 있습니다:

chrome --disable-web-security --user-data-dir="[임시 디렉토리]"

⚠️ 경고: 이 방법은 보안에 매우 취약하므로 격리된 개발 환경에서만 사용하세요!


🛡️ CORS와 보안: 올바른 구현의 중요성

CORS를 올바르게 구현하는 것은 웹 애플리케이션 보안에 매우 중요합니다. 몇 가지 핵심 보안 고려사항을 살펴봅시다:

와일드카드(*) 사용 제한

Access-Control-Allow-Origin: *는 편리하지만, 모든 도메인의 요청을 허용하므로 프로덕션 환경에서는 피해야 합니다. 대신 신뢰할 수 있는 도메인만 명시적으로 허용하세요.

민감한 API에 자격 증명 요구

민감한 데이터를 다루는 API의 경우, CORS에서 인증된 요청만 허용하도록 설정하세요:

// 클라이언트
fetch('https://api.example.com/sensitive-data', {
  credentials: 'include' // 쿠키 포함
});

// 서버
app.use(cors({
  origin: 'https://lazybug.io',
  credentials: true
}));

동적 Origin 처리

여러 환경(개발, 테스트, 프로덕션)을 지원해야 하는 경우 동적으로 Origin을 확인할 수 있습니다:

app.use(cors({
  origin: function(origin, callback) {
    const allowedOrigins = [
      'https://lazybug.io',
      'https://dev.lazybug.io',
      'http://localhost:3000'
    ];
    
    // 허용된 출처에 포함되거나, 서버-서버 요청(origin이 null)인 경우 허용
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('CORS policy violation'));
    }
  }
}));

📝 CORS 정책 구현의 모범 사례

CORS를 효과적으로 구현하기 위한 모범 사례를 정리해보겠습니다:

  1. 최소 권한 원칙 적용: 필요한 출처, 헤더, 메서드만 허용하세요
  2. 와일드카드 사용 자제: 특히 프로덕션 환경에서는 구체적인 출처를 지정하세요
  3. 인증이 필요한 API에 자격 증명 요구: 보안이 중요한 API에는 인증을 요구하세요
  4. 프리플라이트 캐싱 활용: Access-Control-Max-Age 헤더를 사용하여 불필요한 프리플라이트 요청을 줄이세요
  5. 환경별 설정 분리: 개발, 테스트, 프로덕션 환경에 맞는 CORS 설정을 유지하세요
  6. 정기적인 테스트: 새로운 API 엔드포인트를 추가할 때마다 CORS 설정이 올바른지 확인하세요

CORS의 이해, 웹 개발의 필수 요소

지금까지 1부에서의 기본 개념과 함께 2부에서는 CORS의 실제 구현 방법, 디버깅 전략, 그리고 모범 사례까지 살펴보았습니다. CORS는 처음에는 귀찮은 장애물처럼 느껴질 수 있지만, 웹의 보안을 위한 중요한 메커니즘임을 이해하셨을 겁니다.

요약하자면:

  • CORS는 브라우저의 동일 출처 정책을 안전하게 우회할 수 있는 표준 메커니즘입니다
  • 서버 측 설정을 통해 특정 출처의 요청을 허용하거나 거부할 수 있습니다
  • 적절한 HTTP 헤더 설정이 CORS 구현의 핵심입니다
  • 보안을 위해 필요한 출처만 명시적으로 허용하는 것이 좋습니다
  • 디버깅은 오류 메시지와 네트워크 요청을 분석하는 것부터 시작해야 합니다

이제 여러분은 CORS 오류를 마주했을 때 당황하지 않고 체계적으로 문제를 해결할 수 있을 것입니다. 무엇보다 CORS가 웹 보안에 기여하는 중요한 역할을 이해하게 되었습니다.

웹 개발 여정에서 CORS 관련 문제를 만난다면, 이 글을 참고하여 해결해보세요. 그리고 기억하세요 – CORS는 적이 아니라 😅 여러분의 애플리케이션과 사용자를 보호하는 동맹입니다! 🛡️

“CORS가 뭐길래? 웹 개발자가 반드시 알아야 할 교차 출처 리소스 공유 (2부)”에 대한 1개의 생각

댓글 남기기