🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

S3 권한과 정책 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 19. · 8 Views

S3 권한과 정책 완벽 가이드

AWS S3의 권한 모델부터 버킷 정책, ACL, 퍼블릭 접근 차단, 정적 웹 호스팅, CORS 설정까지 실무에서 꼭 필요한 S3 보안과 권한 관리를 초급 개발자도 쉽게 이해할 수 있도록 스토리텔링으로 풀어냅니다.


목차

  1. S3_권한_모델
  2. 버킷_정책_작성
  3. ACL_이해하기
  4. 퍼블릭_접근_차단
  5. 정적_웹_호스팅
  6. CORS_설정

1. S3 권한 모델

입사 2개월 차 김개발 씨가 처음으로 S3 버킷을 만들고 파일을 업로드했습니다. 그런데 이상하게도 브라우저에서 파일에 접근하려니 "Access Denied" 오류가 발생합니다.

분명히 업로드는 성공했는데 왜 볼 수 없는 걸까요?

S3 권한 모델은 누가, 어떤 리소스에, 무엇을 할 수 있는지를 결정하는 체계입니다. 마치 아파트 출입 시스템처럼, 입주민인지, 방문객인지, 택배 기사인지에 따라 접근 권한이 다르게 부여됩니다.

S3는 IAM 정책, 버킷 정책, ACL이라는 세 가지 주요 권한 메커니즘을 제공합니다.

다음 코드를 살펴봅시다.

import boto3

# S3 클라이언트 생성
s3 = boto3.client('s3')

# 객체 업로드 (기본적으로 private)
s3.upload_file(
    'local_file.txt',
    'my-bucket',
    'uploaded_file.txt'
)

# 특정 사용자에게 읽기 권한 부여
s3.put_object_acl(
    Bucket='my-bucket',
    Key='uploaded_file.txt',
    ACL='public-read'  # 퍼블릭 읽기 권한
)

김개발 씨는 당황했습니다. 분명히 파일 업로드는 성공했는데 왜 접근이 안 되는 걸까요?

선배 개발자 박시니어 씨가 모니터를 보며 말했습니다. "아, S3 권한 모델을 이해하지 못해서 생긴 문제네요." 그렇다면 S3 권한 모델이란 정확히 무엇일까요?

쉽게 비유하자면, S3 권한 모델은 마치 대형 건물의 출입 통제 시스템과 같습니다. 건물 전체에 대한 규칙이 있고, 각 층마다 별도의 규칙이 있으며, 개별 방에도 각자의 잠금장치가 있습니다.

누군가 특정 방에 들어가려면 이 세 가지 관문을 모두 통과해야 합니다. S3도 마찬가지로 여러 계층의 권한 검사를 거칩니다.

S3가 처음 등장했을 때는 어땠을까요? 초기에는 권한 관리가 매우 단순했습니다.

버킷을 만들면 소유자만 접근할 수 있었고, 다른 사람과 공유하려면 복잡한 설정을 해야 했습니다. 더 큰 문제는 팀 단위로 작업할 때였습니다.

개발자마다 서로 다른 접근 권한이 필요한데 이를 세밀하게 제어하기가 어려웠습니다. 바로 이런 문제를 해결하기 위해 현재의 다층 권한 모델이 등장했습니다.

S3는 세 가지 주요 권한 메커니즘을 제공합니다. 첫 번째는 IAM 정책으로, 특정 사용자나 역할이 무엇을 할 수 있는지 정의합니다.

두 번째는 버킷 정책으로, 버킷 레벨에서 누가 접근할 수 있는지 제어합니다. 세 번째는 **ACL(Access Control List)**로, 개별 객체마다 권한을 설정할 수 있습니다.

위의 코드를 한 줄씩 살펴보겠습니다. 먼저 boto3 클라이언트를 생성합니다.

이것은 파이썬에서 AWS 서비스를 제어하는 공식 SDK입니다. upload_file 메서드로 파일을 업로드하면 기본적으로 private 권한이 설정됩니다.

즉, 소유자만 접근할 수 있습니다. 다음으로 put_object_acl 메서드를 사용하면 특정 객체에 대한 권한을 변경할 수 있습니다.

여기서는 'public-read'로 설정하여 누구나 읽을 수 있게 만들었습니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 이커머스 서비스를 개발한다고 가정해봅시다. 상품 이미지는 모든 고객이 볼 수 있어야 하지만, 주문 영수증 PDF는 해당 고객만 다운로드할 수 있어야 합니다.

이럴 때 IAM 정책으로 내부 직원의 권한을 관리하고, 버킷 정책으로 전체적인 접근 규칙을 설정하며, ACL로 개별 파일의 공개 범위를 조정합니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 모든 객체를 public-read로 설정하는 것입니다. 이렇게 하면 민감한 정보가 외부에 노출될 수 있습니다.

AWS는 최소 권한 원칙을 권장합니다. 즉, 꼭 필요한 권한만 부여하고 나머지는 차단하는 것이 안전합니다.

다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다.

"그래서 기본적으로 접근이 차단되어 있었군요!" S3 권한 모델을 제대로 이해하면 보안을 유지하면서도 유연하게 리소스를 공유할 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - 기본적으로 모든 S3 객체는 private이므로, 퍼블릭 접근이 필요한 경우에만 명시적으로 권한을 부여하세요

  • IAM 정책과 버킷 정책 중 하나라도 거부하면 접근이 차단됩니다 (명시적 거부가 우선)
  • CloudTrail을 활성화하여 S3 접근 로그를 모니터링하면 보안 사고를 예방할 수 있습니다

2. 버킷 정책 작성

김개발 씨가 회사 블로그용 이미지를 S3에 저장하고 있습니다. 그런데 마케팅 팀에서 요청이 왔습니다.

"특정 IP에서만 접근 가능하게 해주세요." IAM 사용자를 일일이 만들기는 번거롭고, 더 간단한 방법이 없을까요?

버킷 정책은 S3 버킷에 직접 연결되는 JSON 형식의 권한 규칙입니다. 마치 건물 출입구에 붙은 게시판처럼, 누가 들어올 수 있고 무엇을 할 수 있는지 명확하게 적혀 있습니다.

IAM 정책이 사용자 중심이라면, 버킷 정책은 리소스 중심입니다.

다음 코드를 살펴봅시다.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowPublicRead",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-bucket/public/*",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": "203.0.113.0/24"
        }
      }
    }
  ]
}

박시니어 씨가 말했습니다. "버킷 정책을 사용하면 됩니다.

IAM 사용자 없이도 IP 기반으로 접근을 제어할 수 있어요." 그렇다면 버킷 정책이란 정확히 무엇일까요? 쉽게 비유하자면, 버킷 정책은 마치 도서관 입구의 이용 규칙 게시판과 같습니다.

"회원증을 가진 사람만 입장 가능", "18세 이상만 특정 서적 열람 가능"처럼 명확한 규칙이 적혀 있습니다. 도서관 직원이 매번 사람을 확인할 필요 없이, 게시된 규칙에 따라 자동으로 접근이 제어됩니다.

버킷 정책도 마찬가지로 버킷에 연결된 규칙을 통해 모든 요청을 자동으로 검사합니다. 버킷 정책이 없던 시절에는 어땠을까요?

모든 접근 제어를 IAM 사용자와 그룹으로 관리해야 했습니다. 외부 파트너나 임시 사용자에게 권한을 주려면 AWS 계정을 만들어야 했고, 이는 관리 부담을 증가시켰습니다.

더 큰 문제는 특정 조건(예: IP 주소, 시간대)에 따른 동적 접근 제어가 어려웠다는 점입니다. 바로 이런 문제를 해결하기 위해 버킷 정책이 등장했습니다.

버킷 정책을 사용하면 IAM 사용자 없이도 접근을 제어할 수 있습니다. 또한 IP 주소, VPC, 시간, MFA 여부 등 다양한 조건을 설정할 수 있습니다.

무엇보다 JSON 형식으로 작성되어 코드로 관리하고 버전 관리하기 쉽다는 장점이 있습니다. 위의 JSON 정책을 한 줄씩 살펴보겠습니다.

먼저 Version은 정책 언어의 버전으로, 항상 "2012-10-17"을 사용합니다. Statement는 하나 이상의 권한 규칙을 담는 배열입니다.

Effect는 "Allow" 또는 "Deny"로 허용할지 거부할지 결정합니다. Principal은 누구에게 적용할지 지정하며, "*"는 모든 사용자를 의미합니다.

Action은 허용할 작업으로, 여기서는 s3:GetObject(객체 읽기)를 지정했습니다. Resource는 대상 리소스의 ARN으로, public 폴더의 모든 객체를 가리킵니다.

Condition은 추가 조건으로, 특정 IP 대역에서만 접근을 허용합니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 기업 내부 데이터를 S3에 저장한다고 가정해봅시다. 회사 사무실 IP에서만 접근 가능하게 하려면 Condition에 회사 IP 대역을 지정합니다.

또한 특정 폴더는 읽기만 가능하게, 다른 폴더는 쓰기도 가능하게 여러 Statement를 조합할 수 있습니다. 많은 기업이 이런 방식으로 세밀한 접근 제어를 구현합니다.

하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 Principal을 "*"로 설정하고 Condition 없이 Allow를 주는 것입니다.

이렇게 하면 전 세계 누구나 버킷에 접근할 수 있게 됩니다. 반드시 Condition이나 구체적인 Principal로 범위를 제한해야 합니다.

또한 Effect: "Deny"는 모든 Allow보다 우선하므로, Deny를 사용할 때는 신중해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨가 작성해준 버킷 정책을 적용하자, 지정된 IP에서만 이미지에 접근할 수 있게 되었습니다. "정책 하나로 이렇게 간단하게 해결되는군요!" 버킷 정책을 제대로 이해하면 IAM 사용자 관리 부담을 줄이고 더 유연한 접근 제어를 구현할 수 있습니다.

여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - AWS Policy Generator를 사용하면 GUI로 쉽게 정책을 생성할 수 있습니다

  • 정책을 적용하기 전에 IAM Policy Simulator로 테스트하여 의도대로 동작하는지 확인하세요
  • 버킷 정책은 최대 20KB 크기 제한이 있으니, 너무 복잡한 정책은 여러 버킷으로 분리하세요

3. ACL 이해하기

김개발 씨가 고객별 프로필 사진을 S3에 저장하고 있습니다. 대부분은 비공개지만, 일부 사용자는 프로필을 공개로 설정했습니다.

버킷 정책으로는 객체마다 다른 권한을 주기 어려운데, 어떻게 해야 할까요?

**ACL(Access Control List)**은 개별 S3 객체나 버킷에 대한 접근 권한을 설정하는 레거시 방식입니다. 마치 파일마다 붙은 메모지처럼, 각 객체에 대해 누가 읽거나 쓸 수 있는지 간단하게 지정합니다.

최신 방식인 버킷 정책과 IAM 정책보다 기능은 제한적이지만, 간단한 용도로는 여전히 유용합니다.

다음 코드를 살펴봅시다.

import boto3

s3 = boto3.client('s3')

# 특정 객체에 퍼블릭 읽기 권한 부여
s3.put_object_acl(
    Bucket='my-bucket',
    Key='profile/user123.jpg',
    ACL='public-read'
)

# 특정 AWS 계정에만 읽기 권한 부여
s3.put_object_acl(
    Bucket='my-bucket',
    Key='private/document.pdf',
    AccessControlPolicy={
        'Grants': [
            {
                'Grantee': {
                    'ID': 'canonical-user-id-here',
                    'Type': 'CanonicalUser'
                },
                'Permission': 'READ'
            }
        ],
        'Owner': {
            'ID': 'owner-canonical-id'
        }
    }
)

박시니어 씨가 설명했습니다. "객체별로 다른 권한이 필요하면 ACL을 사용할 수 있어요.

다만 요즘은 버킷 정책을 더 권장하긴 합니다." 그렇다면 ACL이란 정확히 무엇일까요? 쉽게 비유하자면, ACL은 마치 개인 사물함에 붙인 라벨과 같습니다.

"이 사물함은 김철수만 열 수 있음", "이 사물함은 누구나 볼 수 있음"처럼 간단한 메모가 붙어 있습니다. 복잡한 규칙은 없지만, 누가 접근할 수 있는지 한눈에 알 수 있습니다.

S3의 ACL도 마찬가지로 각 객체에 대해 기본적인 권한만 설정합니다. ACL은 S3가 처음 나왔을 때부터 있던 기능입니다.

당시에는 버킷 정책이나 IAM 정책이 없었기 때문에, ACL이 유일한 권한 관리 방법이었습니다. 하지만 ACL은 기능이 제한적입니다.

복잡한 조건(IP, 시간 등)을 설정할 수 없고, 권한 종류도 READ, WRITE, READ_ACP, WRITE_ACP, FULL_CONTROL 정도로 단순합니다. 더 큰 문제는 수천 개의 객체마다 ACL을 설정하면 관리가 매우 어려워진다는 점입니다.

그렇다면 왜 아직도 ACL을 사용할까요? 간단한 경우에는 ACL이 오히려 편리하기 때문입니다.

예를 들어 파일 업로드 시 바로 공개/비공개를 결정하거나, 특정 객체 하나만 다른 AWS 계정과 공유할 때는 ACL이 빠릅니다. 또한 레거시 시스템과의 호환성 때문에 ACL을 유지하는 경우도 많습니다.

하지만 AWS는 새로운 프로젝트에서는 버킷 정책과 IAM 정책을 사용할 것을 권장합니다. 위의 코드를 한 줄씩 살펴보겠습니다.

첫 번째 예시에서는 put_object_acl 메서드를 사용하여 특정 객체에 'public-read' ACL을 설정합니다. 이것은 사전 정의된 ACL로, 소유자는 모든 권한을 가지고 나머지 모든 사람은 읽기만 가능합니다.

두 번째 예시는 더 세밀한 제어를 보여줍니다. AccessControlPolicy를 사용하여 특정 AWS 계정(Canonical User ID)에게만 READ 권한을 부여합니다.

이렇게 하면 특정 파트너 계정과만 파일을 공유할 수 있습니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 사용자가 업로드한 프로필 사진을 저장하는 서비스를 개발한다고 가정해봅시다. 사용자가 "공개 프로필" 옵션을 선택하면 해당 이미지에 public-read ACL을 설정하고, "비공개"를 선택하면 private ACL을 설정합니다.

이렇게 하면 객체 업로드와 동시에 권한을 설정할 수 있어 편리합니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 모든 객체에 ACL을 설정하는 것입니다. 수만 개의 파일이 있다면 ACL 관리가 악몽이 됩니다.

이런 경우 버킷 정책으로 폴더 단위로 권한을 관리하는 것이 훨씬 효율적입니다. 또한 AWS는 ACL 비활성화를 권장하고 있습니다.

S3 버킷 생성 시 "Object Ownership" 설정에서 ACL을 비활성화하면 더 안전하고 간단하게 관리할 수 있습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨는 덧붙였습니다. "요즘 프로젝트라면 ACL 대신 버킷 정책을 쓰는 게 좋아요.

하지만 레거시 코드에서는 ACL을 많이 보게 될 거예요." ACL을 이해하면 레거시 시스템을 유지보수할 때 도움이 됩니다. 하지만 새로운 프로젝트에서는 버킷 정책과 IAM 정책을 우선적으로 사용하세요.

실전 팁

💡 - AWS는 2023년 4월부터 새 버킷에 대해 기본적으로 ACL을 비활성화합니다

  • 사전 정의된 ACL: private, public-read, public-read-write, authenticated-read 등을 활용하세요
  • ACL은 버킷 정책이나 IAM 정책과 함께 평가되므로, 하나라도 거부하면 접근이 차단됩니다

4. 퍼블릭 접근 차단

어느 날 회사 보안팀에서 경고 메일이 왔습니다. "S3 버킷이 퍼블릭으로 노출되어 있습니다!" 김개발 씨는 당황했습니다.

분명히 민감한 데이터는 private으로 설정했는데, 어떻게 외부에 노출된 걸까요?

**퍼블릭 접근 차단(Block Public Access)**은 S3 버킷과 객체가 의도치 않게 공개되는 것을 방지하는 안전장치입니다. 마치 아파트 정문에 설치된 이중 잠금장치처럼, 설령 실수로 문을 열어두더라도 메인 게이트가 막아줍니다.

이 기능을 켜두면 실수로 설정한 퍼블릭 권한이 무시됩니다.

다음 코드를 살펴봅시다.

import boto3

s3 = boto3.client('s3')

# 버킷에 퍼블릭 접근 차단 설정 적용
s3.put_public_access_block(
    Bucket='my-bucket',
    PublicAccessBlockConfiguration={
        'BlockPublicAcls': True,  # 새로운 퍼블릭 ACL 차단
        'IgnorePublicAcls': True,  # 기존 퍼블릭 ACL 무시
        'BlockPublicPolicy': True,  # 퍼블릭 버킷 정책 차단
        'RestrictPublicBuckets': True  # 퍼블릭 버킷 접근 제한
    }
)

# 현재 설정 확인
response = s3.get_public_access_block(Bucket='my-bucket')
print(response['PublicAccessBlockConfiguration'])

박시니어 씨가 모니터를 보며 한숨을 쉬었습니다. "역시 퍼블릭 접근 차단을 설정하지 않았군요.

이거 정말 중요한 건데..." 그렇다면 퍼블릭 접근 차단이란 정확히 무엇일까요? 쉽게 비유하자면, 퍼블릭 접근 차단은 마치 스마트폰의 자녀 보호 기능과 같습니다.

아이가 실수로 유해한 앱을 설치하거나 결제를 하려고 해도, 부모가 설정한 보호 기능이 이를 막아줍니다. S3의 퍼블릭 접근 차단도 마찬가지로, 개발자가 실수로 버킷이나 객체를 퍼블릭으로 설정하더라도 상위 레벨의 차단 설정이 이를 무효화합니다.

퍼블릭 접근 차단이 없던 시절에는 어땠을까요? 수많은 기업이 실수로 S3 버킷을 퍼블릭으로 노출하여 개인정보 유출 사고를 겪었습니다.

2017년에는 한 기업이 1억 명 이상의 고객 데이터가 담긴 S3 버킷을 실수로 공개하여 엄청난 손해를 입었습니다. 문제는 개발자가 테스트 중에 임시로 퍼블릭 권한을 주고 나중에 되돌리는 것을 잊는 경우가 많았다는 점입니다.

바로 이런 사고를 예방하기 위해 AWS는 2018년 퍼블릭 접근 차단 기능을 도입했습니다. 이 기능을 활성화하면 네 가지 보호막이 생깁니다.

BlockPublicAcls는 새로운 퍼블릭 ACL을 차단합니다. IgnorePublicAcls는 이미 설정된 퍼블릭 ACL을 무시합니다.

BlockPublicPolicy는 퍼블릭 접근을 허용하는 버킷 정책을 차단합니다. RestrictPublicBuckets는 인증되지 않은 사용자의 접근을 제한합니다.

이 네 가지를 모두 켜두면 거의 모든 의도치 않은 퍼블릭 노출을 막을 수 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.

put_public_access_block 메서드를 사용하여 버킷에 퍼블릭 접근 차단 설정을 적용합니다. 각 옵션을 True로 설정하면 해당 보호 기능이 활성화됩니다.

모든 옵션을 True로 설정하는 것이 가장 안전합니다. get_public_access_block 메서드로 현재 설정을 확인할 수 있으며, 이를 통해 보안 감사를 수행할 수 있습니다.

실제 현업에서는 어떻게 활용할까요? 대부분의 기업은 조직 레벨에서 모든 S3 버킷에 대해 퍼블릭 접근 차단을 기본으로 활성화합니다.

AWS Organizations를 사용하면 조직 내 모든 계정의 모든 버킷에 이 설정을 강제할 수 있습니다. 정말로 퍼블릭 접근이 필요한 경우(예: 정적 웹사이트 호스팅)에만 특정 버킷에 대해 일부 옵션을 해제합니다.

이렇게 하면 기본적으로 안전하고, 예외는 명시적으로 관리됩니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 퍼블릭 접근 차단을 켜두고 왜 파일이 안 보이는지 고민하는 것입니다. 정적 웹사이트 호스팅이나 CloudFront와 함께 사용할 때는 적절히 설정을 조정해야 합니다.

또한 이 기능은 ACL과 버킷 정책만 차단하며, IAM 정책을 통한 접근은 차단하지 않습니다. 따라서 IAM 권한 관리도 함께 신경 써야 합니다.

다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨가 퍼블릭 접근 차단을 활성화하자, 보안팀의 경고가 사라졌습니다.

"앞으로는 모든 버킷에 이걸 기본으로 켜두세요!" 퍼블릭 접근 차단을 제대로 이해하면 데이터 유출 사고를 예방하고 안심하고 S3를 사용할 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - AWS는 2023년 4월부터 새 버킷에 대해 기본적으로 퍼블릭 접근 차단을 활성화합니다

  • AWS Organizations의 SCP(Service Control Policy)를 사용하면 조직 전체에 이 설정을 강제할 수 있습니다
  • S3 버킷 대시보드에서 "Public" 라벨이 있는 버킷을 정기적으로 점검하세요

5. 정적 웹 호스팅

김개발 씨가 간단한 포트폴리오 웹사이트를 만들었습니다. HTML, CSS, JavaScript 파일만 있는 정적 사이트인데, 서버를 따로 구축하기는 부담스럽습니다.

S3로 웹사이트를 호스팅할 수 있다는 얘기를 들었는데, 어떻게 설정해야 할까요?

정적 웹 호스팅은 S3 버킷을 웹 서버처럼 사용하여 HTML, CSS, JavaScript 파일을 제공하는 기능입니다. 마치 책장에 책을 꽂아두고 누구나 가져다 볼 수 있게 하는 것처럼, S3에 파일을 올려두면 인터넷에서 접근할 수 있습니다.

서버 설정이나 관리 없이 웹사이트를 운영할 수 있습니다.

다음 코드를 살펴봅시다.

import boto3

s3 = boto3.client('s3')

# 버킷을 정적 웹사이트로 설정
s3.put_bucket_website(
    Bucket='my-website-bucket',
    WebsiteConfiguration={
        'IndexDocument': {
            'Suffix': 'index.html'  # 기본 페이지
        },
        'ErrorDocument': {
            'Key': 'error.html'  # 404 에러 페이지
        }
    }
)

# 퍼블릭 읽기 권한을 위한 버킷 정책 추가
bucket_policy = {
    "Version": "2012-10-17",
    "Statement": [{
        "Sid": "PublicReadGetObject",
        "Effect": "Allow",
        "Principal": "*",
        "Action": "s3:GetObject",
        "Resource": f"arn:aws:s3:::my-website-bucket/*"
    }]
}

import json
s3.put_bucket_policy(
    Bucket='my-website-bucket',
    Policy=json.dumps(bucket_policy)
)

# 웹사이트 엔드포인트 URL 출력
print(f"http://my-website-bucket.s3-website-ap-northeast-2.amazonaws.com")

박시니어 씨가 말했습니다. "정적 사이트라면 S3 웹 호스팅이 딱이에요.

비용도 저렴하고 설정도 간단합니다." 그렇다면 정적 웹 호스팅이란 정확히 무엇일까요? 쉽게 비유하자면, 정적 웹 호스팅은 마치 전시회와 같습니다.

미리 만들어진 작품(HTML 파일)을 전시장(S3)에 걸어두면, 관람객(사용자)이 와서 볼 수 있습니다. 작품이 실시간으로 변하거나 상호작용하지는 않지만, 보여주기에는 충분합니다.

반면 동적 웹사이트는 라이브 공연과 같아서 매번 다른 내용을 보여줄 수 있지만 더 복잡한 준비가 필요합니다. 정적 웹 호스팅이 인기를 끈 이유는 무엇일까요?

과거에는 간단한 웹사이트도 Apache나 Nginx 같은 웹 서버를 설치하고 관리해야 했습니다. 서버가 다운되지 않도록 모니터링하고, 보안 패치를 적용하고, 트래픽이 증가하면 서버를 증설해야 했습니다.

이런 관리 부담이 컸습니다. 하지만 S3 정적 웹 호스팅은 파일만 업로드하면 AWS가 알아서 서빙하고, 자동으로 확장되며, 99.99% 가용성을 보장합니다.

S3 정적 웹 호스팅을 사용하면 어떤 이점이 있을까요? 첫째, 비용이 저렴합니다.

서버를 24시간 돌리는 것보다 훨씬 싸며, 실제 트래픽만큼만 과금됩니다. 둘째, 확장성이 뛰어납니다.

갑자기 트래픽이 몰려도 자동으로 처리됩니다. 셋째, 관리가 쉽습니다.

파일 업로드와 버킷 설정만으로 웹사이트를 운영할 수 있습니다. 넷째, CDN과 결합하면 전 세계 어디서나 빠른 속도를 제공할 수 있습니다.

위의 코드를 한 줄씩 살펴보겠습니다. put_bucket_website 메서드로 버킷을 정적 웹사이트로 설정합니다.

IndexDocument는 루트 경로(/)에 접근했을 때 보여줄 기본 파일을 지정합니다. ErrorDocument는 존재하지 않는 페이지에 접근했을 때 보여줄 에러 페이지입니다.

다음으로 버킷 정책을 설정하여 모든 사람이 객체를 읽을 수 있게 합니다. 이 정책이 없으면 웹사이트 설정을 해도 파일에 접근할 수 없습니다.

마지막으로 S3가 자동으로 생성한 웹사이트 엔드포인트 URL로 접속하면 웹사이트를 볼 수 있습니다. 실제 현업에서는 어떻게 활용할까요?

많은 기업이 회사 소개 페이지, 제품 랜딩 페이지, 문서 사이트를 S3로 호스팅합니다. React, Vue, Angular 같은 프레임워크로 만든 SPA(Single Page Application)도 빌드 후 S3에 배포합니다.

GitHub Actions나 GitLab CI와 연동하면 코드를 푸시할 때마다 자동으로 S3에 배포되도록 설정할 수 있습니다. CloudFront를 앞단에 두면 HTTPS, 커스텀 도메인, 캐싱까지 모두 해결됩니다.

하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 정적 웹 호스팅에서 서버 사이드 로직을 실행하려는 것입니다.

S3는 정적 파일만 제공하므로, PHP, Python, Node.js 같은 서버 언어는 실행할 수 없습니다. 이런 경우 API Gateway + Lambda를 함께 사용하거나, Amplify 같은 서비스를 고려해야 합니다.

또한 S3 웹사이트 엔드포인트는 HTTPS를 지원하지 않으므로, HTTPS가 필요하면 반드시 CloudFront를 사용해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 도움으로 포트폴리오 사이트를 S3에 배포한 김개발 씨는 감탄했습니다. "이렇게 간단하게 웹사이트를 올릴 수 있다니!" 정적 웹 호스팅을 제대로 이해하면 서버 관리 부담 없이 저렴하고 안정적으로 웹사이트를 운영할 수 있습니다.

여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - S3 웹사이트 엔드포인트는 http://버킷이름.s3-website-리전.amazonaws.com 형식입니다

  • CloudFront와 함께 사용하면 HTTPS, 커스텀 도메인, 전 세계 빠른 속도를 모두 얻을 수 있습니다
  • SPA 라우팅을 위해 모든 404 에러를 index.html로 리다이렉트하는 에러 문서 설정을 활용하세요

6. CORS 설정

김개발 씨가 프론트엔드 앱에서 S3에 저장된 이미지를 불러오려고 합니다. 그런데 브라우저 콘솔에 빨간색 에러가 떴습니다.

"CORS policy: No 'Access-Control-Allow-Origin' header is present." 무슨 뜻일까요?

**CORS(Cross-Origin Resource Sharing)**는 다른 도메인에서 S3 리소스에 접근할 수 있도록 허용하는 설정입니다. 마치 아파트 택배함에 "가족 외 수령 가능" 스티커를 붙이는 것처럼, S3에 "다른 출처에서 접근 가능"이라고 표시해두는 것입니다.

이 설정이 없으면 브라우저가 보안상 접근을 차단합니다.

다음 코드를 살펴봅시다.

import boto3
import json

s3 = boto3.client('s3')

# CORS 설정 추가
cors_configuration = {
    'CORSRules': [
        {
            'AllowedOrigins': ['https://mywebsite.com'],  # 허용할 도메인
            'AllowedMethods': ['GET', 'PUT', 'POST'],  # 허용할 HTTP 메서드
            'AllowedHeaders': ['*'],  # 허용할 헤더
            'ExposeHeaders': ['ETag'],  # 노출할 응답 헤더
            'MaxAgeSeconds': 3000  # 브라우저 캐시 시간(초)
        }
    ]
}

s3.put_bucket_cors(
    Bucket='my-bucket',
    CORSConfiguration=cors_configuration
)

# CORS 설정 확인
response = s3.get_bucket_cors(Bucket='my-bucket')
print(json.dumps(response['CORSRules'], indent=2))

박시니어 씨가 에러 메시지를 보며 설명했습니다. "CORS 문제네요.

브라우저가 보안상 다른 도메인의 리소스 접근을 막고 있어요." 그렇다면 CORS란 정확히 무엇일까요? 쉽게 비유하자면, CORS는 마치 은행의 대리인 출금 규정과 같습니다.

본인이 아닌 다른 사람이 돈을 찾으려면 사전에 "이 사람에게 대리 출금을 허용함"이라는 서류를 제출해야 합니다. 웹 브라우저도 마찬가지로, 사이트 A에서 실행되는 JavaScript가 사이트 B의 리소스에 접근하려면 사이트 B가 "사이트 A의 접근을 허용함"이라고 명시해야 합니다.

이것이 바로 CORS입니다. CORS가 왜 필요할까요?

과거에는 동일 출처 정책(Same-Origin Policy) 때문에 다른 도메인의 리소스에 접근할 수 없었습니다. 이는 보안을 위한 것으로, 악의적인 사이트가 사용자 정보를 몰래 훔쳐가는 것을 막기 위함입니다.

하지만 현대 웹 애플리케이션은 여러 도메인의 리소스를 조합해서 사용합니다. 예를 들어 myapp.com에서 실행되는 앱이 mycdn.s3.amazonaws.com의 이미지나 API를 불러와야 하는 경우가 많습니다.

CORS는 이런 필요를 충족하면서도 보안을 유지하는 메커니즘입니다. S3에서 CORS를 설정하지 않으면 어떤 문제가 생길까요?

브라우저에서 실행되는 JavaScript로 S3 파일을 요청하면 브라우저가 이를 차단합니다. 콘솔에 "CORS policy" 에러가 표시되고 파일을 불러올 수 없습니다.

특히 Canvas API로 이미지를 조작하거나, Fetch API로 파일을 다운로드하거나, XMLHttpRequest로 데이터를 요청할 때 이 문제가 자주 발생합니다. img 태그로 이미지를 보여주는 것은 CORS 없이도 가능하지만, JavaScript로 이미지 데이터를 읽으려면 반드시 CORS가 필요합니다.

위의 코드를 한 줄씩 살펴보겠습니다. CORSRules 배열에 하나 이상의 규칙을 정의합니다.

AllowedOrigins는 어떤 도메인에서의 접근을 허용할지 지정합니다. 와일드카드(*)를 사용하면 모든 도메인을 허용하지만, 보안상 구체적인 도메인을 명시하는 것이 좋습니다.

AllowedMethods는 허용할 HTTP 메서드를 지정합니다. GET은 읽기, PUT은 업로드, POST는 생성, DELETE는 삭제입니다.

AllowedHeaders는 브라우저가 보낼 수 있는 헤더를 지정하며, *는 모든 헤더를 허용합니다. ExposeHeaders는 JavaScript가 읽을 수 있는 응답 헤더를 지정합니다.

MaxAgeSeconds는 브라우저가 preflight 요청 결과를 캐시하는 시간입니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 React 앱을 app.example.com에서 서비스하고, S3 버킷을 assets.example.com으로 설정했다고 가정해봅시다. React 앱에서 Fetch API로 S3의 JSON 파일을 읽으려면 S3 버킷에 CORS 설정을 추가해야 합니다.

AllowedOrigins에 'https://app.example.com'을 추가하고, AllowedMethods에 GET을 포함시킵니다. 또한 사용자가 직접 파일을 업로드하는 기능이 있다면 PUT도 허용해야 합니다.

하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 AllowedOrigins를 '*'로 설정하는 것입니다.

이렇게 하면 어떤 도메인에서든 리소스에 접근할 수 있어 보안 위험이 커집니다. 개발 중에는 편의상 '*'를 사용할 수 있지만, 프로덕션에서는 반드시 구체적인 도메인을 명시해야 합니다.

또한 CORS는 브라우저에서만 적용되며, curl이나 서버 사이드 요청에는 영향을 주지 않습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨가 CORS 설정을 추가하자, 에러가 사라지고 이미지가 정상적으로 로드되었습니다. "브라우저 보안 때문에 이런 설정이 필요했군요!" CORS를 제대로 이해하면 다양한 도메인의 리소스를 안전하게 활용할 수 있습니다.

여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - 개발 환경에서는 AllowedOrigins에 'http://localhost:3000' 같은 로컬 주소를 추가하세요

  • preflight 요청(OPTIONS 메서드)을 줄이려면 MaxAgeSeconds를 충분히 크게 설정하세요
  • CloudFront를 사용하면 CloudFront에서도 CORS 헤더를 설정할 수 있습니다

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#AWS#S3#IAM#BucketPolicy#Security

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.