?
사용자

CORS (Cross-Origin Resource Sharing) 발생 원인 및 해결 원리 완벽 가이드

웹 개발 중 자주 접하는 CORS 오류의 근본적인 원인을 이해하고, 브라우저와 서버의 보안 메커니즘을 바탕으로 해결하는 방법을 상세히 안내합니다.

#cors#web development#security#api#web browser
recipe.sh
## CORS (Cross-Origin Resource Sharing) 란?

CORS는 웹 브라우저의 '동일 출처 정책(Same-Origin Policy)'으로 인해 발생하는 보안 제한을 완화하기 위한 메커니즘입니다. 동일 출처 정책은 웹 페이지의 스크립트가 다른 출처(도메인, 프로토콜, 포트가 다른 경우)의 리소스에 접근하는 것을 기본적으로 차단하여, 악의적인 스크립트가 사용자 정보를 탈취하거나 시스템을 오염시키는 것을 방지합니다.

CORS는 이러한 동일 출처 정책의 예외를 두어, 서버가 명시적으로 허용하는 경우에만 다른 출처의 리소스 접근을 허용합니다.

## CORS가 발생하는 이유

CORS 오류는 기본적으로 브라우저가 안전을 위해 리소스 요청을 차단할 때 발생합니다. 다음과 같은 조건에서 CORS 오류가 발생할 수 있습니다.

*   **다른 출처(Origin) 간의 요청:** 클라이언트(브라우저)가 로드된 웹 페이지의 출처와 다른 출처의 API 서버에 HTTP 요청을 보낼 때 발생합니다.
    *   출처는 프로토콜(http, https), 도메인(example.com), 포트(:80, :443, :3000 등)가 모두 같아야 동일 출처로 간주됩니다.
    *   예시: `http://localhost:3000`에서 실행 중인 프론트엔드 앱이 `https://api.example.com/data`로 요청을 보낼 때.
*   **서버의 명시적 허용 부재:** 요청을 받은 서버가 응답 헤더에 `Access-Control-Allow-Origin`과 같은 CORS 관련 헤더를 포함하여 해당 출처의 요청을 허용하지 않을 때 브라우저가 요청을 차단합니다.

## CORS 해결 원리

CORS 문제는 클라이언트(프론트엔드)와 서버(백엔드) 양쪽 모두의 협력을 통해 해결됩니다. 핵심은 서버가 클라이언트의 출처를 신뢰하고, 응답 헤더를 통해 접근을 허용한다고 명시하는 것입니다.

### 1. 브라우저의 CORS 사전 요청 (Preflight Request)

모든 CORS 요청이 사전 요청을 보내는 것은 아닙니다. 브라우저는 특정 조건(예: `PUT`, `DELETE`와 같은 메소드 사용, `Content-Type`이 `application/json` 등 일반적인 값과 다른 경우, 커스텀 헤더 추가 등)에서 실제 요청을 보내기 전에, 서버에게 이 요청이 안전한지 확인하기 위해 **Preflight Request**라는 OPTIONS 메소드 요청을 먼저 보냅니다. 이 요청은 다음과 같은 정보를 담습니다.

*   `Origin`: 요청을 보내는 클라이언트의 출처.
*   `Access-Control-Request-Method`: 실제 요청에서 사용할 HTTP 메소드 (GET, POST, PUT 등).
*   `Access-Control-Request-Headers`: 실제 요청에 포함될 헤더들의 목록.

### 2. 서버의 응답 및 허용

서버는 Preflight Request를 받으면, 해당 요청이 허용 가능한지 판단하고 다음과 같은 응답 헤더를 포함하여 클라이언트에게 회신합니다.

*   `Access-Control-Allow-Origin`: 허용할 클라이언트의 출처를 명시합니다. `*`는 모든 출처를 허용하며, 특정 출처(예: `https://your-frontend.com`)를 명시할 수도 있습니다.
*   `Access-Control-Allow-Methods`: 허용할 HTTP 메소드 목록 (예: `GET, POST, PUT, DELETE`).
*   `Access-Control-Allow-Headers`: 허용할 HTTP 헤더 목록 (예: `Content-Type, Authorization`).
*   `Access-Control-Max-Age`: Preflight Request의 결과를 얼마나 오래 캐싱할지 시간(초)을 지정합니다.

브라우저는 서버로부터 받은 응답 헤더를 확인하고, 요청이 허용되었다면 실제 HTTP 요청을 보냅니다. 만약 서버가 허용 응답을 보내지 않거나, 클라이언트의 출처가 허용 목록에 없다면 브라우저는 CORS 오류를 발생시키고 실제 요청을 차단합니다.

### 3. 실제 요청 및 응답 (Simple Request vs Actual Request)

*   **Simple Request:** `GET`, `HEAD`, `POST` 메소드를 사용하고, `Content-Type`이 `application/x-www-form-urlencoded`, `multipart/form-data`, `text/plain` 중 하나이며, 커스텀 헤더가 없는 경우 Preflight Request 없이 바로 실제 요청이 이루어집니다. 서버는 이 요청에 대해 `Access-Control-Allow-Origin` 헤더를 포함한 응답을 반환해야 합니다.
*   **Actual Request:** Preflight Request를 거친 후, 서버의 허가를 받아 실제 요청이 전송됩니다. 이 요청에 대한 응답에도 `Access-Control-Allow-Origin` 헤더가 포함되어야 브라우저가 응답 내용을 파싱하고 사용할 수 있습니다.

## CORS 해결 방법 (주요 시나리오)

### 1. 서버 측에서 CORS 허용 설정

가장 근본적이고 권장되는 방법입니다. API 서버의 백엔드 코드에서 CORS 관련 헤더를 설정합니다.

*   **Express (Node.js) 예시:**
    ```javascript
    const express = require('express');
    const cors = require('cors'); // npm install cors
    const app = express();

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

    // 특정 출처만 허용
    // const corsOptions = {
    //   origin: 'https://your-frontend.com',
    //   optionsSuccessStatus: 204
    // };
    // app.use(cors(corsOptions));

    // GET 요청 처리
    app.get('/api/data', (req, res) => {
      res.json({ message: 'Hello from API!' });
    });

    app.listen(5000, () => {
      console.log('Server listening on port 5000');
    });
    ```

*   **Spring Boot (Java) 예시:**
    ```java
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

    @Configuration
    public class WebConfig implements WebMvcConfigurer {

        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/api/**") // /api/** 경로에 대해 CORS 설정
                    .allowedOrigins("http://localhost:3000", "https://your-frontend.com") // 허용할 출처
                    .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 허용할 메소드
                    .allowedHeaders("*") // 허용할 헤더 (보안상 특정 헤더 명시 권장)
                    .a
15
스크랩
20
좋아요
0
댓글
CORS (Cross-Origin Resource Sharing) 발생 원인 및 해결 원리 완벽 가이드