

# 교차 오리진 리소스 공유(CORS) 사용
<a name="cors"></a>

CORS(Cross-origin 리소스 공유)는 한 도메인에서 로드되어 다른 도메인에 있는 리소스와 상호 작용하는 클라이언트 웹 애플리케이션에 대한 방법을 정의합니다. CORS 지원을 통해 Amazon S3으로 다양한 기능의 클라이언트 측 웹 애플리케이션을 구축하고, Amazon S3 리소스에 대한 Cross-Origin 액세스를 선택적으로 허용할 수 있습니다.

이 섹션에서는 CORS 개요를 다룹니다. 하위 섹션에서는 Amazon S3 콘솔을 사용하거나 프로그래밍 방식으로 Amazon S3 REST API 및 AWS SDK를 사용하여 CORS를 사용하는 방법을 설명합니다.

## CORS(Cross-Origin Resource Sharing): 사용 사례 시나리오
<a name="example-scenarios-cors"></a>

다음은 CORS 사용에 대한 예제 시나리오입니다.

**시나리오 1**  
`website`에서 설명한 대로 [Amazon S3를 사용하여 정적 웹 사이트 호스팅](WebsiteHosting.md)라는 이름의 Amazon S3 버킷에서 웹 사이트를 호스팅한다고 가정해 보겠습니다. 사용자는 웹 사이트 엔드포인트를 로드합니다.

```
http://website.s3-website.us-east-1.amazonaws.com
```

이제 이 버킷에 저장된 웹 페이지의 JavaScript를 사용하여, `website.s3.us-east-1.amazonaws.com` 버킷에 대한 Amazon S3의 API 엔드포인트를 사용하여 같은 버킷에 대해 GET 및 PUT 요청 인증을 가능하게 하려고 합니다. 일반적으로 브라우저에서 이러한 요청을 허용하지 못하도록 JavaScript를 차단하지만, CORS를 사용하여 `website.s3-website.us-east-1.amazonaws.com`으로부터의 cross-origin 요청을 사용 설정하도록 버킷을 구성할 수 있습니다.

**시나리오 2**  
S3 버킷에서 웹 글꼴을 호스팅한다고 가정해 보겠습니다. 브라우저는 웹 글꼴을 불러오기 위해 CORS 검사(preflight check라고도 함)가 필요하므로, 이 웹 글꼴을 호스팅하는 버킷이 이러한 요청을 하는 오리진을 허용하도록 구성합니다.

## Amazon S3은 버킷의 CORS 구성을 어떻게 평가하나요?
<a name="cors-eval-criteria"></a>

Amazon S3이 브라우저에서 preflight 요청을 받으면 버킷에 대한 CORS 구성을 평가하고 수신된 브라우저 요청과 일치하는 첫 번째 `CORSRule` 규칙을 사용하여 Cross-Origin 요청을 허용합니다. 규칙이 일치하려면 다음 조건이 충족되어야 합니다.
+ 버킷에 대한 CORS 요청의 `Origin` 헤더는 CORS 구성의 `AllowedOrigins` 요소에 있는 오리진과 일치해야 합니다.
+ 버킷에 대한 CORS 요청의 `Access-Control-Request-Method`에 지정된 HTTP 메서드는 CORS 구성의 `AllowedMethods` 요소에 나열된 메서드와 일치해야 합니다.
+ 사전 요청의 `Access-Control-Request-Headers` 헤더에 나열된 헤더는 CORS 구성의 `AllowedHeaders` 요소에 있는 헤더와 일치해야 합니다.

**참고**  
버킷에 대해 CORS를 허용하면 ACL과 정책이 계속 적용됩니다.

## 객체 Lambda 액세스 포인트가 CORS를 지원하는 방법
<a name="cors-olap-cors"></a>

브라우저로부터 요청을 받거나 요청에 `Origin` 헤더가 포함된 경우 S3 Object Lambda는 항상 `"AllowedOrigins":"*"` 헤더 필드를 추가합니다.

CORS 사용에 대한 자세한 내용은 다음 주제를 참조하십시오.

**Topics**
+ [CORS(Cross-Origin Resource Sharing): 사용 사례 시나리오](#example-scenarios-cors)
+ [Amazon S3은 버킷의 CORS 구성을 어떻게 평가하나요?](#cors-eval-criteria)
+ [객체 Lambda 액세스 포인트가 CORS를 지원하는 방법](#cors-olap-cors)
+ [CORS 구성의 요소](ManageCorsUsing.md)
+ [교차 오리진 리소스 공유(CORS) 구성](enabling-cors-examples.md)
+ [CORS 테스트](testing-cors.md)
+ [CORS 문제 해결](cors-troubleshooting.md)

# CORS 구성의 요소
<a name="ManageCorsUsing"></a>

Cross-Origin 요청을 허용하도록 버킷을 구성하려면 CORS 구성을 생성합니다. CORS 구성은 버킷에 액세스할 수 있도록 허용할 오리진, 각 오리진에 대해 지원되는 작업(HTTP 메서드) 및 기타 작업별 정보를 식별하는 요소가 포함된 문서입니다. 구성당 최대 100개의 규칙을 추가할 수 있습니다. CORS 구성을 `cors` 하위 리소스로 버킷에 추가할 수 있습니다.

S3 콘솔에서 CORS를 구성하는 경우 JSON을 사용하여 CORS 구성을 생성해야 합니다. 새로운 S3 콘솔은 JSON CORS 구성만 지원합니다.

CORS 구성 및 구성 내 요소에 대한 자세한 내용은 아래 주제를 참조하십시오. CORS 구성을 추가하는 방법에 대한 지침은 [교차 오리진 리소스 공유(CORS) 구성](enabling-cors-examples.md) 섹션을 참조하십시오.

**중요**  
S3 콘솔에서 CORS 구성은 JSON이어야 합니다.

**Topics**
+ [`AllowedMethods` 요소](#cors-allowed-methods)
+ [`AllowedOrigins` 요소](#cors-allowed-origin)
+ [`AllowedHeaders` 요소](#cors-allowed-headers)
+ [`ExposeHeaders` 요소](#cors-expose-headers)
+ [`MaxAgeSeconds` 요소](#cors-max-age)
+ [CORS 구성의 예](#cors-example-1)

## `AllowedMethods` 요소
<a name="cors-allowed-methods"></a>

CORS 구성에서 `AllowedMethods` 요소에 대한 다음 값을 지정할 수 있습니다.
+ GET
+ PUT
+ POST
+ DELETE
+ HEAD

## `AllowedOrigins` 요소
<a name="cors-allowed-origin"></a>

`AllowedOrigins` 요소에 교차 도메인 요청을 허용하려는 오리진을 지정합니다(예: ` http://www.example.com`). 오리진 문자열에는 `*` 와일드 문자 한 개만 포함할 수 있습니다(예: `http://*.example.com`). `*`를 모든 오리진이 교차 오리진 요청을 보낼 수 있는 오리진으로 지정할 수도 있습니다. 또한 `https`를 지정하여 안전한 오리진만 허용할 수도 있습니다.

## `AllowedHeaders` 요소
<a name="cors-allowed-headers"></a>

`AllowedHeaders` 요소는 `Access-Control-Request-Headers` 헤더를 통해 preflight 요청에 허용되는 헤더를 지정합니다. `Access-Control-Request-Headers` 헤더의 각 헤더 이름은 요소의 해당 항목과 일치해야 합니다. Amazon S3은 요청된 응답에서 허용된 헤더만을 보냅니다. Amazon S3에 대한 요청에 사용할 수 있는 샘플 헤더 목록을 보려면 *Amazon Simple Storage Service API 참조* 가이드의 [공통 요청 헤더](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonRequestHeaders.html)를 참조하십시오.

구성의 각 AllowedHeaders 문자열에는 최대 한 개의 \$1 와일드카드 문자를 포함할 수 있습니다. 예를 들어, `<AllowedHeader>x-amz-*</AllowedHeader>`는 모든 Amazon별 헤더를 허용합니다.

## `ExposeHeaders` 요소
<a name="cors-expose-headers"></a>

각 `ExposeHeader` 요소는 응답에서 고객이 해당 애플리케이션(예: JavaScript `XMLHttpRequest` 객체)으로부터 액세스할 수 있도록 하려는 헤더를 식별합니다. 공통 Amazon S3 응답 헤더 목록을 보려면 *Amazon Simple Storage Service API 참조* 가이드의 [공통 응답 헤더](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html)를 참조하십시오.

## `MaxAgeSeconds` 요소
<a name="cors-max-age"></a>

`MaxAgeSeconds` 요소는 브라우저가 리소스, HTTP 메서드, 오리진으로 식별되는 preflight 요청에 대한 응답을 캐시할 수 있는 시간(초)을 지정합니다.

## CORS 구성의 예
<a name="cors-example-1"></a>

Amazon S3 웹 사이트 엔드포인트를 사용하여 웹 사이트에 액세스하는 대신, `example1.com`과 같은 자체 도메인을 사용하여 콘텐츠를 제공할 수 있습니다. 자체 도메인 사용에 대한 자세한 내용은 [자습서: Route 53에 등록된 사용자 지정 도메인을 사용하여 정적 웹 사이트 구성](website-hosting-custom-domain-walkthrough.md) 섹션을 참조하세요.

다음 예시 CORS 구성에는 `CORSRule` 요소로 지정된 규칙이 3개 있습니다.
+ 첫 번째 규칙은 `http://www.example1.com` 오리진에서 cross-origin PUT, POST, DELETE 요청을 허용합니다. 이 규칙은 또한 `Access-Control-Request-Headers` 헤더를 통해 preflight OPTIONS 요청의 모든 헤더를 허용합니다. Amazon S3은 preflight OPTIONS 요청에 대한 응답으로 요청된 헤더를 반환합니다.
+ 두 번째 규칙은 첫 번째 규칙과 같이 cross-origin 요청을 허용하지만 다른 오리진인 `http://www.example2.com`에 적용됩니다.
+ 세 번째 규칙은 모든 오리진에서 cross-origin GET 요청을 허용합니다. `*` 와일드카드 문자는 모든 오리진을 나타냅니다.

------
#### [ JSON ]

```
[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            "http://www.example1.com"
        ],
        "ExposeHeaders": []
    },
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            "http://www.example2.com"
        ],
        "ExposeHeaders": []
    },
    {
        "AllowedHeaders": [],
        "AllowedMethods": [
            "GET"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": []
    }
]
```

------
#### [ XML ]

```
<CORSConfiguration>
 <CORSRule>
   <AllowedOrigin>http://www.example1.com</AllowedOrigin>

   <AllowedMethod>PUT</AllowedMethod>
   <AllowedMethod>POST</AllowedMethod>
   <AllowedMethod>DELETE</AllowedMethod>

   <AllowedHeader>*</AllowedHeader>
 </CORSRule>
 <CORSRule>
   <AllowedOrigin>http://www.example2.com</AllowedOrigin>

   <AllowedMethod>PUT</AllowedMethod>
   <AllowedMethod>POST</AllowedMethod>
   <AllowedMethod>DELETE</AllowedMethod>

   <AllowedHeader>*</AllowedHeader>
 </CORSRule>
 <CORSRule>
   <AllowedOrigin>*</AllowedOrigin>
   <AllowedMethod>GET</AllowedMethod>
 </CORSRule>
</CORSConfiguration>
```

------

CORS 구성은 다음에서 볼 수 있듯이 선택적인 구성 파라미터도 허용합니다. 이 예제에서 CORS 구성은 `http://www.example.com` 오리진으로부터의 cross-origin PUT, POST 및 DELETE 요청을 허용합니다.

------
#### [ JSON ]

```
[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            "http://www.example.com"
        ],
        "ExposeHeaders": [
            "x-amz-server-side-encryption",
            "x-amz-request-id",
            "x-amz-id-2"
        ],
        "MaxAgeSeconds": 3000
    }
]
```

------
#### [ XML ]

```
<CORSConfiguration>
 <CORSRule>
   <AllowedOrigin>http://www.example.com</AllowedOrigin>
   <AllowedMethod>PUT</AllowedMethod>
   <AllowedMethod>POST</AllowedMethod>
   <AllowedMethod>DELETE</AllowedMethod>
   <AllowedHeader>*</AllowedHeader>
  <MaxAgeSeconds>3000</MaxAgeSeconds>
  <ExposeHeader>x-amz-server-side-encryption</ExposeHeader>
  <ExposeHeader>x-amz-request-id</ExposeHeader>
  <ExposeHeader>x-amz-id-2</ExposeHeader>
 </CORSRule>
</CORSConfiguration>
```

------

이전 구성에서 `CORSRule` 요소는 다음의 선택적 요소를 포함합니다.
+ `MaxAgeSeconds` - 브라우저가 지정된 리소스에 대한 Amazon S3 preflight OPTIONS 요청 응답을 캐시하는 시간을 초 단위(이 예에서는 3000)로 지정합니다. 원래 요청이 반복될 경우, 브라우저는 이 응답을 캐시함으로써 Amazon S3에 preflight 요청을 보낼 필요가 없습니다.
+ `ExposeHeaders` - 고객이 자체 애플리케이션(예: JavaScript `x-amz-server-side-encryption` 객체)에서 액세스할 수 있는 응답 헤더(이 예에서는 `x-amz-request-id`, `x-amz-id-2`, `XMLHttpRequest`)를 식별합니다.

# 교차 오리진 리소스 공유(CORS) 구성
<a name="enabling-cors-examples"></a>

CORS(Cross-origin 리소스 공유)는 한 도메인에서 로드되어 다른 도메인에 있는 리소스와 상호 작용하는 클라이언트 웹 애플리케이션에 대한 방법을 정의합니다. CORS 지원을 통해 Amazon S3으로 다양한 기능의 클라이언트 측 웹 애플리케이션을 구축하고, Amazon S3 리소스에 대한 Cross-Origin 액세스를 선택적으로 허용할 수 있습니다.

이 섹션에서는 Amazon S3 콘솔, Amazon S3 REST API 및 AWS SDK를 사용하여 CORS를 사용하는 방법을 보여줍니다. 교차 오리진 요청을 허용하도록 버킷을 구성하려면 해당 버킷에 CORS 구성을 추가합니다. CORS 구성은 버킷에 대한 액세스를 허용할 오리진과 각 오리진에 대해 지원되는 작업(HTTP 메서드) 그리고 기타 작업별 정보를 각각 식별하는 규칙과 기타 작업별 정보를 포함하는 문서입니다. S3 콘솔에서 CORS 구성은 JSON 문서여야 합니다.

JSON 및 XML의 CORS 구성 예제는 [CORS 구성의 요소](ManageCorsUsing.md) 섹션을 참조하십시오.

## S3 콘솔 사용
<a name="add-cors-configuration"></a>

이 섹션에서는 Amazon S3 콘솔을 사용하여 S3 버킷에 CORS(Cross-Origin 리소스 공유) 구성을 추가하는 방법을 설명합니다.

버킷에 대해 CORS를 사용 설정하면 ACL(액세스 제어 목록)과 기타 액세스 권한 정책이 계속 적용됩니다.

**중요**  
S3 콘솔에서 CORS 구성은 JSON이어야 합니다. JSON 및 XML의 CORS 구성 예제는 [CORS 구성의 요소](ManageCorsUsing.md) 섹션을 참조하십시오.

**S3 버킷에 CORS 구성 추가**

1. AWS Management Console에 로그인한 후 [https://console.aws.amazon.com/s3/](https://console.aws.amazon.com/s3/)에서 Amazon S3 콘솔을 엽니다.

1. 왼쪽 탐색 창에서 **범용 버킷**을 선택합니다.

1. 버킷 목록에서 버킷 정책을 만들 버킷의 이름을 선택합니다.

1. **Permissions**를 선택합니다.

1. **CORS(Cross-Origin 리소스 공유)** 섹션에서 **편집**을 선택합니다.

1. **CORS 구성 편집기** 텍스트 상자에 새 CORS 구성을 입력하거나 복사하여 붙여넣거나, 기존 구성을 편집합니다.

   CORS 구성은 JSON 파일입니다. 편집기에 입력하는 텍스트는 유효한 JSON이어야 합니다. 자세한 내용은 [CORS 구성의 요소](ManageCorsUsing.md) 섹션을 참조하세요.

1. [**변경 사항 저장(Save changes)**]을 선택합니다.
**참고**  
Amazon S3는 **CORS 구성 편집기** 제목 옆에 버킷의 Amazon 리소스 이름(ARN)을 표시합니다. ARN에 대한 자세한 내용은 *Amazon Web Services 일반 참조*의 [Amazon 리소스 이름(ARN) 및 AWS 서비스 네임스페이스](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html)를 참조하세요.

## AWS SDK 사용
<a name="ManageCorsUsingSDK"></a>

AWS SDK를 사용하여 버킷에 대한 cross-origin 리소스 공유(CORS)를 관리할 수 있습니다. CORS에 대한 자세한 내용은 [교차 오리진 리소스 공유(CORS) 사용](cors.md) 섹션을 참조하십시오.

 다음 예제를 고려하십시오.
+ CORS 구성을 만들고 버킷에 구성 설정
+ 구성을 검색하고 규칙을 추가하여 구성 수정
+ 버킷에 수정된 구성 추가
+ 구성 삭제

------
#### [ Java ]

**Example**  

**Example**  
 실제 예제를 작성하여 테스트하는 방법에 대한 자세한 내용은 AWS SDK for Java 개발자 안내서에서 [시작하기](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/getting-started.html) 섹션을 참조하세요.  

```
import com.amazonaws.AmazonServiceException;
import com.amazonaws.SdkClientException;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.BucketCrossOriginConfiguration;
import com.amazonaws.services.s3.model.CORSRule;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class CORS {

    public static void main(String[] args) throws IOException {
        Regions clientRegion = Regions.DEFAULT_REGION;
        String bucketName = "*** Bucket name ***";

        // Create two CORS rules.
        List<CORSRule.AllowedMethods> rule1AM = new ArrayList<CORSRule.AllowedMethods>();
        rule1AM.add(CORSRule.AllowedMethods.PUT);
        rule1AM.add(CORSRule.AllowedMethods.POST);
        rule1AM.add(CORSRule.AllowedMethods.DELETE);
        CORSRule rule1 = new CORSRule().withId("CORSRule1").withAllowedMethods(rule1AM)
                .withAllowedOrigins(Arrays.asList("http://*.example.com"));

        List<CORSRule.AllowedMethods> rule2AM = new ArrayList<CORSRule.AllowedMethods>();
        rule2AM.add(CORSRule.AllowedMethods.GET);
        CORSRule rule2 = new CORSRule().withId("CORSRule2").withAllowedMethods(rule2AM)
                .withAllowedOrigins(Arrays.asList("*")).withMaxAgeSeconds(3000)
                .withExposedHeaders(Arrays.asList("x-amz-server-side-encryption"));

        List<CORSRule> rules = new ArrayList<CORSRule>();
        rules.add(rule1);
        rules.add(rule2);

        // Add the rules to a new CORS configuration.
        BucketCrossOriginConfiguration configuration = new BucketCrossOriginConfiguration();
        configuration.setRules(rules);

        try {
            AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
                    .withCredentials(new ProfileCredentialsProvider())
                    .withRegion(clientRegion)
                    .build();

            // Add the configuration to the bucket.
            s3Client.setBucketCrossOriginConfiguration(bucketName, configuration);

            // Retrieve and display the configuration.
            configuration = s3Client.getBucketCrossOriginConfiguration(bucketName);
            printCORSConfiguration(configuration);

            // Add another new rule.
            List<CORSRule.AllowedMethods> rule3AM = new ArrayList<CORSRule.AllowedMethods>();
            rule3AM.add(CORSRule.AllowedMethods.HEAD);
            CORSRule rule3 = new CORSRule().withId("CORSRule3").withAllowedMethods(rule3AM)
                    .withAllowedOrigins(Arrays.asList("http://www.example.com"));

            rules = configuration.getRules();
            rules.add(rule3);
            configuration.setRules(rules);
            s3Client.setBucketCrossOriginConfiguration(bucketName, configuration);

            // Verify that the new rule was added by checking the number of rules in the
            // configuration.
            configuration = s3Client.getBucketCrossOriginConfiguration(bucketName);
            System.out.println("Expected # of rules = 3, found " + configuration.getRules().size());

            // Delete the configuration.
            s3Client.deleteBucketCrossOriginConfiguration(bucketName);
            System.out.println("Removed CORS configuration.");

            // Retrieve and display the configuration to verify that it was
            // successfully deleted.
            configuration = s3Client.getBucketCrossOriginConfiguration(bucketName);
            printCORSConfiguration(configuration);
        } catch (AmazonServiceException e) {
            // The call was transmitted successfully, but Amazon S3 couldn't process
            // it, so it returned an error response.
            e.printStackTrace();
        } catch (SdkClientException e) {
            // Amazon S3 couldn't be contacted for a response, or the client
            // couldn't parse the response from Amazon S3.
            e.printStackTrace();
        }
    }

    private static void printCORSConfiguration(BucketCrossOriginConfiguration configuration) {
        if (configuration == null) {
            System.out.println("Configuration is null.");
        } else {
            System.out.println("Configuration has " + configuration.getRules().size() + " rules\n");

            for (CORSRule rule : configuration.getRules()) {
                System.out.println("Rule ID: " + rule.getId());
                System.out.println("MaxAgeSeconds: " + rule.getMaxAgeSeconds());
                System.out.println("AllowedMethod: " + rule.getAllowedMethods());
                System.out.println("AllowedOrigins: " + rule.getAllowedOrigins());
                System.out.println("AllowedHeaders: " + rule.getAllowedHeaders());
                System.out.println("ExposeHeader: " + rule.getExposedHeaders());
                System.out.println();
            }
        }
    }
}
```

------
#### [ .NET ]

**Example**  
코드 예제 설정 및 실행에 대한 자세한 내용은 *AWS SDK for .NET 개발자 안내서*의 [AWS SDK for .NET 시작하기](https://docs.aws.amazon.com/sdk-for-net/latest/developer-guide/net-dg-setup.html)를 참조하세요.  

```
using Amazon;
using Amazon.S3;
using Amazon.S3.Model;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Amazon.DocSamples.S3
{
    class CORSTest
    {
        private const string bucketName = "*** bucket name ***";
        // Specify your bucket region (an example region is shown).
        private static readonly RegionEndpoint bucketRegion = RegionEndpoint.USWest2; 
        private static IAmazonS3 s3Client;

        public static void Main()
        {
            s3Client = new AmazonS3Client(bucketRegion);
            CORSConfigTestAsync().Wait();
        }
        private static async Task CORSConfigTestAsync()
        {
            try
            {
                // Create a new configuration request and add two rules    
                CORSConfiguration configuration = new CORSConfiguration
                {
                    Rules = new System.Collections.Generic.List<CORSRule>
                        {
                          new CORSRule
                          {
                            Id = "CORSRule1",
                            AllowedMethods = new List<string> {"PUT", "POST", "DELETE"},
                            AllowedOrigins = new List<string> {"http://*.example.com"}
                          },
                          new CORSRule
                          {
                            Id = "CORSRule2",
                            AllowedMethods = new List<string> {"GET"},
                            AllowedOrigins = new List<string> {"*"},
                            MaxAgeSeconds = 3000,
                            ExposeHeaders = new List<string> {"x-amz-server-side-encryption"}
                          }
                        }
                };

                // Add the configuration to the bucket. 
                await PutCORSConfigurationAsync(configuration);

                // Retrieve an existing configuration. 
                configuration = await RetrieveCORSConfigurationAsync();

                // Add a new rule.
                configuration.Rules.Add(new CORSRule
                {
                    Id = "CORSRule3",
                    AllowedMethods = new List<string> { "HEAD" },
                    AllowedOrigins = new List<string> { "http://www.example.com" }
                });

                // Add the configuration to the bucket. 
                await PutCORSConfigurationAsync(configuration);

                // Verify that there are now three rules.
                configuration = await RetrieveCORSConfigurationAsync();
                Console.WriteLine();
                Console.WriteLine("Expected # of rulest=3; found:{0}", configuration.Rules.Count);
                Console.WriteLine();
                Console.WriteLine("Pause before configuration delete. To continue, click Enter...");
                Console.ReadKey();

                // Delete the configuration.
                await DeleteCORSConfigurationAsync();

                // Retrieve a nonexistent configuration.
                configuration = await RetrieveCORSConfigurationAsync();
            }
            catch (AmazonS3Exception e)
            {
                Console.WriteLine("Error encountered on server. Message:'{0}' when writing an object", e.Message);
            }
            catch (Exception e)
            {
                Console.WriteLine("Unknown encountered on server. Message:'{0}' when writing an object", e.Message);
            }
        }

        static async Task PutCORSConfigurationAsync(CORSConfiguration configuration)
        {

            PutCORSConfigurationRequest request = new PutCORSConfigurationRequest
            {
                BucketName = bucketName,
                Configuration = configuration
            };

            var response = await s3Client.PutCORSConfigurationAsync(request);
        }

        static async Task<CORSConfiguration> RetrieveCORSConfigurationAsync()
        {
            GetCORSConfigurationRequest request = new GetCORSConfigurationRequest
            {
                BucketName = bucketName

            };
            var response = await s3Client.GetCORSConfigurationAsync(request);
            var configuration = response.Configuration;
            PrintCORSRules(configuration);
            return configuration;
        }

        static async Task DeleteCORSConfigurationAsync()
        {
            DeleteCORSConfigurationRequest request = new DeleteCORSConfigurationRequest
            {
                BucketName = bucketName
            };
            await s3Client.DeleteCORSConfigurationAsync(request);
        }

        static void PrintCORSRules(CORSConfiguration configuration)
        {
            Console.WriteLine();

            if (configuration == null)
            {
                Console.WriteLine("\nConfiguration is null");
                return;
            }

            Console.WriteLine("Configuration has {0} rules:", configuration.Rules.Count);
            foreach (CORSRule rule in configuration.Rules)
            {
                Console.WriteLine("Rule ID: {0}", rule.Id);
                Console.WriteLine("MaxAgeSeconds: {0}", rule.MaxAgeSeconds);
                Console.WriteLine("AllowedMethod: {0}", string.Join(", ", rule.AllowedMethods.ToArray()));
                Console.WriteLine("AllowedOrigins: {0}", string.Join(", ", rule.AllowedOrigins.ToArray()));
                Console.WriteLine("AllowedHeaders: {0}", string.Join(", ", rule.AllowedHeaders.ToArray()));
                Console.WriteLine("ExposeHeader: {0}", string.Join(", ", rule.ExposeHeaders.ToArray()));
            }
        }
    }
}
```

------

## REST API 사용
<a name="EnableCorsUsingREST"></a>

버킷에 CORS 구성을 설정하려면 AWS Management Console을 사용할 수 있습니다. 하지만 애플리케이션에서 요구할 경우 REST 요청을 직접 전송할 수도 있습니다. *Amazon Simple Storage Service API 참조*의 다음 섹션에서는 CORS 구성과 관련된 REST API 작업에 대해 설명합니다.
+ [PutBucketCors](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTcors.html)
+ [GetBucketCors](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETcors.html)
+ [DeleteBucketCors](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketDELETEcors.html)
+ [OPTIONS 객체](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTOPTIONSobject.html)

# CORS 테스트
<a name="testing-cors"></a>

CORS 구성을 테스트하려면 `OPTIONS` 메서드와 함께 CORS 사전 요청을 전송하여 요청 전송이 허용되는 경우 서버가 응답하도록 할 수 있습니다. Amazon S3가 사전 요청을 받으면 S3에서 버킷에 대한 CORS 구성을 평가하고 수신된 요청과 일치하는 첫 번째 `CORSRule` 규칙을 사용하여 교차 오리진 요청을 허용합니다. 규칙이 일치하려면 다음 조건이 충족되어야 합니다.
+ 버킷에 대한 CORS 요청의 `Origin` 헤더는 CORS 구성의 `AllowedOrigins` 요소에 있는 오리진과 일치해야 합니다.
+ 버킷에 대한 CORS 요청의 `Access-Control-Request-Method`에 지정된 HTTP 메서드는 CORS 구성의 `AllowedMethods` 요소에 나열된 메서드와 일치해야 합니다.
+ 사전 요청의 `Access-Control-Request-Headers` 헤더에 나열된 헤더는 CORS 구성의 `AllowedHeaders` 요소에 있는 헤더와 일치해야 합니다.

다음은 CORS 구성 예제입니다. CORS 구성을 만들려면 [CORS 구성](https://docs.aws.amazon.com/AmazonS3/latest/userguide/enabling-cors-examples.html)을 참조하세요. CORS 구성의 자세한 예제는 [CORS 구성의 요소](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ManageCorsUsing.html)를 참조하세요.

CORS 규칙 구성 및 문제 해결에 대한 지침은 AWS re:Post 지식 센터의 [Amazon S3에서 CORS를 구성하고 cURL을 사용하여 CORS 규칙을 확인하려면 어떻게 해야 하나요?](https://repost.aws/knowledge-center/s3-configure-cors)를 참조하세요.

------
#### [ JSON ]

```
[
    {
        "AllowedHeaders": [
            "Authorization"
        ],
        "AllowedMethods": [
            "GET",
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            "http://www.example1.com"
        ],
        "ExposeHeaders":  [
             "x-amz-meta-custom-header"
        ]
    
    }
]
```

------

CORS 구성을 테스트하려면 다음 CURL 명령을 사용하여 사전 요청 `OPTIONS` 검사를 보낼 수 있습니다. CURL은 S3와 상호 작용하는 데 사용할 수 있는 명령줄 도구입니다. 자세한 내용은 [CURL](https://curl.se/)을 참조하세요.

```
 curl -v -X OPTIONS \
  -H "Origin: http://www.example1.com" \
  -H "Access-Control-Request-Method: PUT" \
  -H "Access-Control-Request-Headers: Authorization" \
  -H "Access-Control-Expose-Headers: x-amz-meta-custom-header"\
     "http://bucket_name.s3.amazonaws.com/object_prefix_name"
```

위 예제에서 `curl -v -x OPTIONS` 명령은 S3에 사전 요청을 보내 S3가 교차 오리진 `http://www.example1.com`에서 객체에 대한 `PUT` 요청을 보내는 것을 허용하는지 문의하는 데 사용됩니다. 헤더 `Access-Control-Request-Headers` 및 `Access-Control-Expose-Headers`는 선택 사항입니다.
+ Amazon S3는 사전 `OPTIONS` 요청의 `Access-Control-Request-Method` 헤더에 대한 응답으로 요청된 메서드가 일치하는 경우 허용된 메서드 목록을 반환합니다.
+ Amazon S3는 사전 `OPTIONS` 요청의 `Access-Control-Request-Headers` 헤더에 대한 응답으로 요청된 헤더가 일치하는 경우 허용된 헤더 목록을 반환합니다.
+ Amazon S3는 사전 `OPTIONS` 요청의 `Access-Control-Expose-Headers` 헤더에 대한 응답으로 요청된 헤더가 브라우저에서 실행되는 스크립트에서 액세스할 수 있는 허용 헤더와 일치하는 경우 허용된 헤더 목록을 반환합니다.

**참고**  
사전 요청을 전송할 때 CORS 요청 헤더 중 하나라도 허용되지 않는 경우 응답 CORS 헤더는 반환되지 않습니다.

이 사전 `OPTIONS` 요청에 대한 응답으로 `200 OK` 응답을 받게 됩니다. CORS를 테스트할 때 수신되는 일반적인 오류 코드와 CORS 관련 문제 해결을 위한 자세한 내용은 [CORS 문제 해결](https://docs.aws.amazon.com/AmazonS3/latest/userguide/cors-troubleshooting.html)을 참조하세요.

```
< HTTP/1.1 200 OK
< Date: Fri, 12 Jul 2024 00:23:51 GMT
< Access-Control-Allow-Origin: http://www.example1.com
< Access-Control-Allow-Methods: GET, PUT, POST, DELETE 
< Access-Control-Allow-Headers: Authorization
< Access-Control-Expose-Headers: x-amz-meta-custom-header
< Access-Control-Allow-Credentials: true
< Vary: Origin, Access-Control-Request-Headers, Access-Control-Request-Method
< Server: AmazonS3
< Content-Length: 0
```

# CORS 문제 해결
<a name="cors-troubleshooting"></a>

다음 주제는 S3와 관련된 몇 가지 일반적인 CORS 문제를 해결하는 데 도움이 될 수 있습니다.

**Topics**
+ [403 Forbidden 오류 - 이 버킷에 대해 CORS가 활성화되지 않음](#cors-not-enabled)
+ [403 Forbidden 오류 - 이 CORS 요청은 허용되지 않음](#cors-not-enabled)
+ [CORS 응답에서 헤더를 찾을 수 없음](#Headers-not-found)
+ [S3 프록시 통합의 CORS 관련 고려 사항](#cors-in-proxy)

## 403 Forbidden 오류: 이 버킷에 대해 CORS가 활성화되지 않음
<a name="cors-not-enabled"></a>

교차 오리진 요청이 Amazon S3로 전송되었지만 CORS가 S3 버킷에 구성되지 않은 경우 다음 `403 Forbidden` 오류가 발생합니다.

 오류: HTTP/1.1 403 Forbidden CORS Response: CORS is not enabled for this bucket.  

CORS 구성은 버킷에 액세스할 수 있도록 허용할 오리진, 각 오리진에 대해 지원되는 작업(HTTP 메서드) 및 기타 작업별 정보를 식별하는 규칙이 포함된 문서 또는 정책입니다. Amazon S3 콘솔, AWS SDK 및 REST API를 사용하여 S3에 [CORS를 구성](https://docs.aws.amazon.com/AmazonS3/latest/userguide/enabling-cors-examples.html)하는 방법을 알아보세요. CORS에 대한 자세한 내용과 CORS 구성의 예는 [CORS의 요소](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ManageCorsUsing.html#cors-example-1)를 참조하세요.

## 403 Forbidden 오류: 이 CORS 요청은 허용되지 않음
<a name="cors-not-enabled"></a>

CORS 구성의 CORS 규칙이 요청의 데이터와 일치하지 않는 경우 다음 `403 Forbidden` 오류가 발생합니다.

오류:  HTTP/1.1 403 Forbidden CORS Response: This CORS request is not allowed.

따라서 다음과 같은 여러 가지 이유로 이 `403 Forbidden` 오류가 발생할 수 있습니다.
+ 오리진이 허용되지 않습니다.
+ 메서드가 허용되지 않습니다.
+ 요청된 헤더가 허용되지 않습니다.

Amazon S3가 받은 각 요청에 대해 해당 요청의 데이터와 일치하는 CORS 규칙이 CORS 구성에 있어야 합니다.

### 오리진이 허용되지 않음
<a name="Origin-not-allowed"></a>

 버킷에 대한 CORS 요청의 `Origin` 헤더는 CORS 구성의 `AllowedOrigins` 요소에 있는 오리진과 일치해야 합니다. `AllowedOrigins` 요소의 와일드카드 문자(`"*"`)는 모든 HTTP 메서드와 일치합니다. `AllowedOrigins` 요소를 업데이트하는 방법에 대한 자세한 내용은 [교차 오리진 리소스 공유(CORS) 구성](https://docs.aws.amazon.com/AmazonS3/latest/userguide/enabling-cors-examples.html)을 참조하세요.

 예를 들어 `AllowedOrigins` 요소에 `http://www.example1.com` 도메인만 포함된 경우 `http://www.example2.com` 도메인에서 보낸 CORS 요청에서 `403 Forbidden` 오류가 발생합니다.

다음 예시에서는 `AllowedOrigins` 요소에 `http://www.example1.com` 도메인이 포함된 CORS 구성의 일부를 보여줍니다.

```
"AllowedOrigins":[
   "http://www.example1.com"
]
```

`http://www.example2.com` 도메인에서 보낸 CORS 요청이 성공하려면 해당 `http://www.example2.com` 도메인이 CORS 구성의 `AllowedOrigins` 요소에 포함되어야 합니다.

```
"AllowedOrigins":[
   "http://www.example1.com"
   "http://www.example2.com"
]
```

### 메서드가 허용되지 않음
<a name="Methods-not-allowed"></a>

 버킷에 대한 CORS 요청의 `Access-Control-Request-Method`에 지정된 HTTP 메서드는 CORS 구성의 `AllowedMethods` 요소에 나열된 메서드와 일치해야 합니다. `AllowedMethods`의 와일드카드 문자(`"*"`)는 모든 HTTP 메서드와 일치합니다. `AllowedOrigins` 요소를 업데이트하는 방법에 대한 자세한 내용은 [교차 오리진 리소스 공유(CORS) 구성](https://docs.aws.amazon.com/AmazonS3/latest/userguide/enabling-cors-examples.html)을 참조하세요.

CORS 구성에서 `AllowedMethods` 요소에 다음 메서드를 지정할 수 있습니다.
+ `GET`
+ `PUT`
+ `POST`
+ `DELETE`
+ `HEAD`

다음 예시에서는 `AllowedMethods` 요소에 `GET` 메서드가 포함된 CORS 구성의 일부를 보여줍니다. `GET` 메서드를 포함한 요청만 성공합니다.

```
"AllowedMethods":[
   "GET"
]
```

 HTTP 메서드(예:`PUT`)가 CORS 요청에 사용되었거나 버킷에 대한 사전 CORS 요청에 포함되었으나 해당 메서드가 CORS 구성에 없는 경우 요청에서 `403 Forbidden` 오류가 발생합니다. 이 CORS 요청 또는 CORS 사전 요청을 허용하려면 `PUT` 메서드를 CORS 구성에 추가해야 합니다.

```
"AllowedMethods":[
   "GET"
   "PUT"
]
```

### 요청된 헤더가 허용되지 않음
<a name="Headers-not-allowed"></a>

 사전 요청의 `Access-Control-Request-Headers` 헤더에 나열된 헤더는 CORS 구성의 `AllowedHeaders` 요소에 있는 헤더와 일치해야 합니다. Amazon S3에 대한 요청에 사용할 수 있는 공통 헤더 목록은 [공통 요청 헤더](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonRequestHeaders.html)를 참조하세요. `AllowedHeaders` 요소를 업데이트하는 방법에 대한 자세한 내용은 [교차 오리진 리소스 공유(CORS) 구성](https://docs.aws.amazon.com/AmazonS3/latest/userguide/enabling-cors-examples.html)을 참조하세요.

다음 예시에서는 `AllowedHeaders` 요소에 `Authorization` 헤더가 포함된 CORS 구성의 일부를 보여줍니다. `Authorization` 헤더에 대한 요청만 성공합니다.

```
"AllowedHeaders":  [
    "Authorization"
]
```

 헤더(예: `Content-MD5`)가 CORS 요청에 포함되었지만 해당 헤더가 CORS 구성에 없는 경우 요청에서 `403 Forbidden` 오류가 발생합니다. 이 CORS 요청을 허용하려면 `Content-MD5` 헤더를 CORS 구성에 추가해야 합니다. CORS 요청의 `Authorization` 헤더와 `Content-MD5` 헤더를 모두 버킷으로 전달하려면 두 헤더가 모두 CORS 구성의 `AllowedHeaders` 요소에 포함되어 있는지 확인합니다.

```
"AllowedHeaders":  [
    "Authorization"
    "Content-MD5"
]
```

## CORS 응답에서 헤더를 찾을 수 없음
<a name="Headers-not-found"></a>

 CORS 구성의 `ExposeHeaders` 요소는 CORS 요청에 대한 응답으로 브라우저에서 실행되는 스크립트와 애플리케이션이 액세스할 수 있도록 설정할 응답 헤더를 식별합니다.

S3 버킷에 저장된 객체에 응답 데이터와 함께 사용자 정의 메타데이터(예:`x-amz-meta-custom-header`)가 있는 경우 이 사용자 지정 헤더에는 클라이언트측 JavaScript 코드에서 액세스할 추가 메타데이터 또는 정보가 포함될 수 있습니다. 하지만 기본적으로 브라우저는 보안상의 이유로 사용자 지정 헤더에 대한 액세스를 차단합니다. 클라이언트측 JavaScript가 사용자 지정 헤더에 액세스할 수 있도록 하려면 CORS 구성에 해당 헤더를 포함해야 합니다.

 아래 예시에서 `x-amz-meta-custom-header1` 헤더는 `ExposeHeaders` 요소에 포함되어 있습니다. `x-amz-meta-custom-header2`는 `ExposeHeaders` 요소에 포함되지 않았으며 CORS 구성에서 누락되었습니다. 응답에서는 `ExposeHeaders` 요소에 포함된 값만 반환됩니다. 요청에서 `Access-Control-Expose-Headers` 헤더에 `x-amz-meta-custom-header2` 헤더가 포함된 경우에도 응답은 여전히 `200 OK`를 반환합니다. 하지만 허용된 헤더(예: `x-amz-meta-custom-header`)만 반환되어 응답에 표시됩니다.

```
"ExposeHeaders":  [
    "x-amz-meta-custom-header1"
]
```

 모든 헤더가 응답에 표시되도록 하려면 아래와 같이 허용된 모든 헤더를 CORS 구성의 `ExposeHeaders` 요소에 추가합니다.

```
"ExposeHeaders":  [
    "x-amz-meta-custom-header1",
    "x-amz-meta-custom-header2"
]
```

## S3 프록시 통합의 CORS 관련 고려 사항
<a name="cors-in-proxy"></a>

오류가 발생하며 이미 S3 버킷의 CORS 구성을 확인했고 교차 오리진 요청이 AWS CloudFront와 같은 프록시로 전송되는 경우 다음 작업을 시도해 봅니다.
+ `OPTIONS` 메서드에서 HTTP 요청을 허용하도록 설정을 구성합니다.
+ `Origin`, `Access-Control-Request-Headers` 및 `Access-Control-Request-Method` 헤더를 전달하도록 프록시를 구성합니다.
+ 캐시 키에 오리진 헤더를 포함하도록 프록시 설정을 구성합니다. 이는 캐시 키에 오리진 헤더를 포함하지 않는 캐싱 프록시가 다른 오리진에 적절한 CORS 헤더를 포함하지 않는 캐싱된 응답을 제공할 수 있기 때문에 중요합니다.

일부 프록시는 CORS 요청에 대한 사전 정의된 기능을 제공합니다. 예를 들어 CloudFront에서 헤더를 포함하는 정책을 구성할 수 있습니다.

 이러한 헤더는 오리진이 Amazon S3 버킷일 때 Cross-Origin Resource Sharing(CORS) 요청을 활성화하는 헤더입니다.

 이 정책에는 다음 설정이 포함되어 있습니다.
+ 오리진 요청에 포함된 헤더:

   `Origin`

   `Access-Control-Request-Headers`

   `Access-Control-Request-Method`
+ 오리진 요청에 포함된 쿠키: 없음
+ 오리진 요청에 포함된 쿼리 문자열: 없음

자세한 내용은 **Amazon CloudFront 개발자 안내서의 [정책을 통한 오리진 요청 제어](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/controlling-origin-requests.htm) 또는 [관리형 오리진 요청 정책 사용](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-origin-request-policies.html#managed-origin-request-policy-cors-s3)을 참조하세요.