Django - s3를 사용한 이미지 업로드 (feat.boto3)

이주명·2021년 12월 17일
4

프로젝트를 하면서 이미지 업로드를 기능을 구현해야해서 멘토님이 추천한 s3를 사용한 이미지 업로드를 해보았다.

s3란 아마존에서 제공하는 서비스로 클라우드 스토리지이다.
s3를 사용해서 이미지를 업로드한다는 말은

database에 직접 이미지파일을 저장하지 않고 클라우드 스토리지에 저장하고 저장된 url만 데이터베이스에 저장하는 방법이다.

s3를 사용하는데 몇 가지 준비해야할 스텝이 있다.

AWS가입 및 IAM 생성

기본적으로 AWS 가입 후 IAM을 생성해줘야한다. 여기서 IAM은 여러 사용자들을 만들수 있다. root가 있고 users가 있는 개념이라고 생각하면된다.

AWS가입 및 IAM생성 튜토리얼
공식문서를 보면서 가입을 진행하면 된다.
나는 IAM user생성은 됬지만 IAM 사용자로 접속이 안됬다.. 비밀번호를 잘못 입력한것 같지만 루트로 로그인해서 이용할수 있으니 만약 안되더라도 스킵해도 될것 같다.

가입후 콘솔에 로그인하여 s3를 이용해보자!

버킷 생성

공식문서를 보고 따라하면 정말 좋겠지만.. 너무 어렵다 그래서 유튜브를 뒤지다가 좋은 동영상을 찾았다. 이걸 보고 해보세요

개꿀팁 눌러서 영상 보며 따라하면된다. 하지만 영상에서는 django-storage를 사용하는데 우리는 boto3를 이용할 것이니 모든 과정을 따라하지 않아도된다.

4분 45 초부터 끝까지 보면서 설명하는 것을 끝까지 들으며 버킷을 생성한다. 권한에 대해서도 간단히 설명하니 꼭 들어본다.

boto3를 사용해서 s3에 내 파일 업로드하기

영상을 보고 버킷을 생성했으면 settings 또는 my_settings.py에 자신의 access_key를 만들어 놨을 것이다. 이것을 이용해 shell을 통하여 내 컴퓨터에 있는 사진을 먼저 s3에 올려본다.

install pip boto3

자신의 환경에 boto3를 설치한다.

shell 에서

import boto3

s3r = boto3.resource('s3', aws_access_key_id='너의 접근키', aws_secret_access_key='너의 비밀키')

(여기서 두가지 방법이 있다 client로 접근하는 방법과 resource로 하는 방법 나는 resource를 선택했다)
s3r를 쳤을 때

이와 같이 나왔다면 접근한 것이다

장고에서 model을 import 하고 사용하는 것과 비슷하다.
s3r를 이용하여 업로드가 가능하다 .

s3r의 함수중 Bucket() 이라는 함수가 있는데 내가 앞서 만든 bucket에 접근할 수 있다. 그 뒤에 Put_object를 사용하면 파일을 업로드할 수 있다.

data = open('/home/user/Downloads/'+'36059_46941_4950'+'.jpg','rb')

파일업로드를 실행 시키기 위해 내 컴퓨터에 있는 사진 파일 하나를 data변수에 넣어준다.

전체적인 파일 업로드 코드는 아래와 같다.

s3r.Bucket('jumyeongfirstbuket').put_object( Key='second', body=data,ContentType='jpg')

차례차례 보면
1. s3r.Bucket('jumyeongfirstbuket')는 jumyeongfirstbuket에 접근한다.
2. put_object는 파일을 업로드 하는데 내부적인 키값들을 살펴보면

  • Key = 파일 이름및 경로(버킷안에 폴더를 생성했다면 폴더를 타고 들어갈수 있음)
  • Body = 파일자체가 들어감
    (파일경로 또는 파일명이 아닌 파일이 들어가야한다.)
  • ContentType = 파일 타입을 지정해줌

이렇게

second파일이 올라 간 것이 보인다.
안에 들어가보면

객체 URL이라는게 있는데 이를 눌러서 이미지가 나오면 성공이다.
여기 나온 URL을 DB에 저장시키는게 우리의 목표다.

이제 테스트를 해서 s3 저장소가 잘 작동하는지 알았으니
django로 넘어가서 View파일을 작성해 보자.

django에서 파일 업로드

파일을 받기 위해서는 평소 이용하던 GET방식으로 받을수 없다.
request에 FILES메소드를 사용하여 받아야한다.
아래 코드를 보자

class ImageUploader(View) :
    
    def post(self, request) :
        try :
            files = request.FILES.getlist('files')
            host_id = request.GET.get('host_id')
            s3r = boto3.resource('s3', aws_access_key_id= AWS_ACCESS_KEY_ID, aws_secret_access_key= AWS_SECRET_ACCESS_KEY)
            key = "%s" %(host_id)

            for file in files :
                file._set_name(str(uuid.uuid4()))
                s3r.Bucket(AWS_STORAGE_BUCKET_NAME).put_object( Key=key+'/%s'%(file), Body=file, ContentType='jpg')
                Image.objects.create(
                    image_url = IMAGE_URL+"%s/%s"%(host_id, file),
                    host_id = host_id
                )
            return JsonResponse({"MESSGE" : "SUCCESS"}, status=200)

        except Exception as e :
            return JsonResponse({"ERROR" : e.message})

하나씩 뜯어보자

files = request.FILES.getlist('files')

처음 시작 files를 FILES메소드를 사용해서 접근한다. 내가 짠 코드는 여러개의 파일들을 받아 올리는 관계로 getlist를 사용했으며 키 값은 'files'로 정했다.

s3r = boto3.resource('s3', aws_access_key_id= AWS_ACCESS_KEY_ID, aws_secret_access_key= AWS_SECRET_ACCESS_KEY)

s3r 부분은 앞서 shell에서 테스트했던 코드와 똑같다. 다만 access_key와 secret_key를 my_settings.py에 따로 저장해서 불러온 상태이다.

key = "%s" %(host_id)

key값을 만든 이유는 s3 버킷에 host_id와 연결된 폴더를 만들기 위해 선언했다.

for file in files :
                file._set_name(str(uuid.uuid4()))
                s3r.Bucket(AWS_STORAGE_BUCKET_NAME).put_object( Key=key+'/%s'%(file), Body=file, ContentType='jpg')
                Image.objects.create(
                    image_url = IMAGE_URL+"%s/%s"%(host_id, file),
                    host_id = host_id
                )

files는 InMemoryUploadedFile이라는 형태의 리스트로 되어있다.

리스트 형태를 for문을 사용하여 하나씩 꺼내이용하면 하나의 이미지씩 저장할 수 있게된다.

file._set_name(str(uuid.uuid4()))

여기서 _set_name()을 사용하면 파일의 이름을 변경할 수 있다. 한글 파일일 경우 글씨가 깨질수 있고 같은 중복된 파일의 이름이 올라오게되면 s3는 덮어쓰기를 해서 이전의 사진이 지워질수 있기때문에 업로드 전에 자체적으로 이름을 바꿔서 s3에 올려주는 것이 좋다. uuid방법은 좋은 방법은 아니지만 편법을 사용한것.

s3r.Bucket(AWS_STORAGE_BUCKET_NAME).put_object( Key=key+'/%s'%(file), Body=file, ContentType='jpg')

이 코드도 shell에서 테스트한 코드와 같다.
Key 값을 보면 앞에 만든 key변수+ 내가 uuid로 바꾼 파일명으로 생성한다는 뜻이다.

대망의 create

image_url = IMAGE_URL+"%s/%s"%(host_id, file),

s3에 파일 업로드를 하게되면 정해진 형식으로 객체 URL이 생성된다. 그러므로 업로드에 성공하면 이에 맞는URL을 내가 만들어 DB에 넣어주면 끝!
IMAGE_URL역시 my_settings에 미리 선언해 뒀다.

my_settings.py

AWS_REGION = 'ap-northeast-2'

IMAGE_URL = "https://%s.s3.%s.amazonaws.com/" % (AWS_STORAGE_BUCKET_NAME, AWS_REGION)

객체 url 형식은 다음과 같다.
"https://버킷이름.+s3.+.amazonaws.com/+AWS지역+key"

s3에서 생성해준 객체 url과 같은 형식으로 만들어주면 된다.

이 형식을 database image_url컬럼에 넣어주면 끝!

테스트 (postman)

Postman 으로 실제 이미지 파일을 전달하려면 Body에 이미지 업로드를 해주고 request를 보내야한다.

처음 해본 s3 업로드라서 happy path로 진행하였다.
업로드 중 어떤 오류들이 발생할지 고민해봐야겠따.

profile
oh yeah

1개의 댓글

comment-user-thumbnail
2023년 3월 9일

도움 많이 됐습니다. 감사합니다.

답글 달기