2025.8.19: Amazon S3, Boto3

jiyongg·2025년 8월 19일

TIL: Today I Learned

목록 보기
25/30

동아리에서 TIL 활동을 같이하는 형이 있는데, 그 형이 S3에 대한 이야기를 올린 적이 있다. 이번 해커톤 프로젝트로 개발중인 서비스에는 이미지 파일을 업로드하고 읽어오는 기능이 필요하다. 그래서 그 글을 읽었던 경험이 떠올랐고, S3에 대해 알아봐야겠다는 생각을 했다.

원래 오늘 django-storages까지 알아보려 했는데, 해커톤 프로젝트랑 병행하니깐 시간이 없어서, django-storages부터는 내일 알아보고자 한다. 역시 이번에도 옵시디언 노트를 복붙한 것이다. 여담으로, 이번 옵시디언 업데이트로 Base라는 기능이 생겼다. 그래서 이것에 대해 알아볼까 생각했는데, 조금 만져보니까 딱히 사용법이 어려운 것도 아니라서 굳이 정리할 필요는 없을 것 같다. 간단히 써봤을 때 들었던 느낌은 뭔가 옵시디언을 노션처럼 활용하고 싶은 사람들에게 유용할 것 같다는 느낌이었다.

Amazon S3

  • Amazon Simple Storage Service의 약자
  • 객체 스토리지 서비스
  • 프리 티어에서 1년 간 무료로 사용 가능
    • 매달 5GB의 사용량 지급
    • Glacier의 경우 항상 무료이고 10GB의 사용량 지급하나, 아카이빙 목적의 서비스
      • 검색 속도가 일반 S3에 비해 매우 느리므로 실사용은 무리

작동 방식

  • 버킷과 객체
  • 버킷: 객체의 컨테이너
  • 객체: 파일과 그 파일을 설명하는 메타데이터
    • 각 객체에는 고유한 키(키 이름)가 있음

Boto3

액세스 키 발급

Boto3를 이용하기 위해선 액세스 키와 시크릿 키가 필요함

루트에서 액세스 키를 발급할 수도 있지만 IAM에서 발급하는 것을 추천하고 있음

IAM 생성

  • 1단계: 사용자 세부 정보
    • AWS Management Console 액세스 필요 없음
  • 2단계: 권한 설정
    • 각 권한 정책의 의미를 정확하게는 모르겠으나, 읽기 + 쓰기를 원한다면 AmazonS3FullAccess를, 읽기만 원한다면 AmazonS3ReadOnlyAccess 권한 정책 연결하면 될듯

액세스 키 발급

  • 위에서 만든 사용자 클릭하고 보안 자격 증명 클릭
  • 액세스 키 부분에서 액세스 키 만들기 클릭
    • 아마존에서는 장기 액세스 키 대신 단기 보안 도구(예: STS(Security Token Service)) 등을 이용해 임시 액세스 키 생성을 권장함
    • 일단 STS에 대해선 나중에 알아보고 지금은 장기 액세스 키로 진행
  • 사용 사례는 무엇을 눌러도 상관없음
    • 각 사용 사례에 대해 액세스 키의 대안을 제시해 줌
  • 생성된 액세스 키와 비밀 액세스 키를 잘 보관 (비밀 액세스 키는 다시 볼 수 없음)

Boto3 사용법 (S3)

1. 설치

$ pip install boto3

2. 설정

Boto3은 여러 location에서 설정값을 탐색할 수 있는데, 탐색 순서는 후술할 각 방법들의 제목 순서와 같음 (1->2->3)

1) Config 객체

botocore.configConfig 객체를 이용하는 방법

공식 문서의 예시 코드

import boto3
from botocore.config import Config


my_config = Config(
    region_name = 'us-west-2',
    signature_version = 'v4',
    retries = {
        'max_attempts' = 'v4'
    }
)

2) 환경 변수

3) 설정 파일

Boto3는 기본적으로 ~/.aws/config 파일에서 설정을 검색함

이 파일은 ini 포맷으로 작성된 파일이며, [default] 섹션을 반드시 포함

이 설정 파일로 인한 설정값은 환경 변수나 Config 객체에 의해 덮어씌워질 수 있음(앞서 서술한 탐색 순서에 의해 무시될 수 있음)을 유의

액세스 키와 시크릿 키

1) 클라이언트 또는 리소스스 생성 시 지정하는 방법
s3 = boto3.resource('s3', aws_access_key_id=(액세스 키), aws_secret_access_key=(시크릿 키))
2) export
$ export AWS_ACCESS_KEY_ID=(액세스 키)
$ export AWS_SECRET_ACCESS_KEY=(시크릿 키)
3) 위의 설정 값에 따름

⚠️ 주의: Config 객체의 생성자에는 액세스 키와 시크릿 키 매개변수가 없음

3. 파일 업로드 및 다운로드

import boto3


s3 = boto3.resource('s3', 
                    aws_access_key_id='액세스 키',
                    aws_secret_access_key='시크릿 키',
                    config=Config(
                        region_name='ap-northeast-2',
                        signature_version='s3v4'),)
)
  • s3 리소스를 사용함
    • 리소스와 클라이언트의 차이: python - Difference in Boto3 between resource, client, and session? - Stack Overflow
    • 간단히 정리하면 아래와 같음
      • 리소스가 조금 더 추상적이고 고수준
        • 리소스에 대한 추가적인 기능 지원은 중단되었음
        • 클라이언트에서 가능한 일부 기능들이 아래의 Presigned URL처럼 리소스에선 불가능하기도 함
      • 클라이언트는 조금 더 구체적이고 저수준
  • Config에서 region_namesignature_version을 위와 같이 지정해야 Presigned URL 생성했을 때 오류가 생기지 않았음
with open('helloworld.txt', 'rb') as data:
    s3.Bucket('버킷이름').put_object(Key='helloworld.txt', Body=data)
  • helloworld.txt 파일을 버킷에 업로드
    • KeyBody가 대문자로 시작하는 점 유의
obj = s3.Object(bucket_name='버킷이름', key='helloworld.txt')
response = obj.get()
data = response['Body'].read()

with open('helloworld2.txt', 'wb') as f:
    f.write(data)
  • 버킷의 helloworld.txt 파일을 읽어서 로컬에 helloworld2.txt라는 파일로 저장
  • 여기에서도 body가 아니라 Body인 점에 주의해야 하며, read 메소드는 bytes 객체를 반환
obj = s3.Object(bucket_name='버킷이름', key='image.jpg')
obj.download_file('test.jpg')
  • 직접 write 메소드를 쓸 수도 있지만, s3.Object에는 download_file 메소드가 있음
  • 버킷에서 image.jpg 파일을 다운로드해서 로컬에 test.jpg라는 이름으로 저장

4. Presigned URL

  • 기본적으로 버킷 내 파일의 url은 아래와 같음
    • https://버킷명.s3.리전명.amazonaws.com/[폴더명/]파일명

모든 사람에 대한 읽기 권한이 없는 객체의 경우 위 url로 들어가면 액세스가 거부되었다고 나옴

이럴 때, Presigned URL (미리 서명된 URL)을 생성하여 지정된 시간동안 파일 공유 가능

그런데 이 기능은 resource에서는 불가능하고 client에서만 가능(generate_presigned_url)함

client = s3.meta.client
response = client.generate_presigned_url('get_object',
                                         Params={'Bucket': '버킷이름', 'Key': 'image.jpg'},
                                         ExpiresIn=300,)

print(response)
  • 클라이언트를 새로 만들지 않아도 됨. meta.client 속성을 통해 가져올 수 있음
  • 열람 권한이 있는 Presigned URL을 생성
  • Presigned URL이 출력됨
  • ExpiresIn의 값은 초 단위를 기준으로 함. 즉, 이 링크는 5분 후 만료됨

참고 자료

profile
그냥 쓰고 싶은 것 쓰는 개발(?) 블로그

0개의 댓글