pip install boto3 django-storages pillowpoetry add boto3 django-storages pillowImageField는 기본적으로 poetry add pillowS3는 쉽게 말해 "무한대로 늘어나는 클라우드 USB 드라이브"
[버킷 만들기] 버튼을 클릭my-django-blog-images-1234 처럼 숫자 섞으면 좋음 ap-northeast-2를 선택[ACL 됨(ACLs enabled)]을 선택django-storages가 파일을 올릴 때 [모든 퍼블릭 액세스 차단]의 체크를 해제[버킷 만들기] 버튼을 누르면 완성ap-northeast-2)



Django 서버가 방금 만든 S3 버킷에 파일을 올릴 수 있도록
내 최고 관리자(Root) 계정을 그대로 쓰면 해킹 시 큰일 나기 때문에
IAM 서비스 이동

사용자 메뉴 이동
[사용자]를 클릭하고, 오른쪽 위의 주황색 [사용자 생성] 버튼을 누름
사용자 세부 정보 지정
django-s3-uploader) → [다음] 클릭
권한 설정 (알바생에게 권한 부여)
[직접 정책 연결]이라는 네모 박스를 클릭
아래 검색창에 AmazonS3FullAccess를 검색
검색 결과로 나온 AmazonS3FullAccess 옆의 체크박스를 체크 → [다음] 클릭

고민
검토 및 생성
[사용자 생성] 버튼을 클릭
액세스 키 발급 (출입증 받기 - 아주 중요)
방금 만든 사용자(django-s3-uploader)의 이름을 클릭
[보안 자격 증명] 탭을 클릭하고, 아래로 살짝 내려서 [액세스 키 만들기] 버튼을 누름

사용 사례에서 [AWS 외부에서 실행되는 애플리케이션] (또는 기타)을 선택하고 [다음] 클릭
설명 태그는 비워두고 [액세스 키 만들기] 클릭
키 복사 및 보관
화면에 액세스 키(Access Key)와 비밀 액세스 키(Secret Access Key)가 나타남

경고 🚨
import os
INSTALLED_APPS += [
'storages', # S3 연동을 위해 설치한 패키지를 활성화합니다.
]
# AWS S3 기본 설정
# .env 파일에서 'AWS_ACCESS_KEY_ID' 값을 엄격하게 가져옵니다. 값이 누락되면 서버 실행 시 즉시 에러를 띄워줍니다.
AWS_ACCESS_KEY_ID = env("AWS_ACCESS_KEY_ID")
# S3 접근을 위한 '비밀 액세스 키'를 가져옵니다. 외부에 절대 노출되면 안 되는 값이므로 확실하게 검증합니다.
AWS_SECRET_ACCESS_KEY = env("AWS_SECRET_ACCESS_KEY")
# 이미지가 저장될 실제 '버킷 이름'을 가져옵니다.
AWS_STORAGE_BUCKET_NAME = env("AWS_STORAGE_BUCKET_NAME")
# 버킷이 위치한 '리전 코드'를 가져옵니다.
# default 값을 설정해두면 .env에 값이 없을 때 기본값(서울 리전)으로 동작하게 하여 안정성을 높일 수 있음
AWS_S3_REGION_NAME = env("AWS_REGION", default="ap-northeast-2")
# 추가 옵션 설정
AWS_DEFAULT_ACL = 'public-read' # S3에 업로드된 파일에 접근할 수 있는 기본 권한을 '누구나 읽기 가능'으로 설정(블로그 썸네일이기 때문)
AWS_S3_FILE_OVERWRITE = False # 동일한 이름의 파일이 올라오면, 기존 파일을 덮어쓰지 않고 파일명 뒤에 랜덤한 문자를 붙여서 저장 (안전성 확보)
# S3 주소 체계를 설정 (boto3가 이 형식에 맞춰 이미지 URL을 만들어줌)
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.{AWS_S3_REGION_NAME}.amazonaws.com'
# Django 미디어 파일 저장소 변경
# 원래는 서버 하드디스크(FileSystemStorage)에 저장하던 것을, 이제부터 S3(S3Boto3Storage)에 저장하라고 장고에게 명령
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
class ~ APIView(APIView):
permission_classes = []
pagination_class =
parser_classes = [JSONParser, MultiPartParser, FormParser]