Stay hungry, Stay foolish

S3 Image 업로드를 구현하며 깨닫게 된 것들 본문

개발 이야기

S3 Image 업로드를 구현하며 깨닫게 된 것들

Jake2 2021. 8. 21. 16:08

어느덧 인턴 중인 프로젝트의 3주차가 끝났다.

사실 2주차까지 필수기능의 구현이 어느정도 끝나고 재미있는 로직을 만들어보고자 하였는데 어김없이 튀어나오는 버그들과 

사내에서 대표님들 및 팀원들에게 시연하기 위한 베타버전 배포로 추천로직 구현은 어렵게 되었다 😭

 

그러면 배운게 없느냐 아니다 개발자는 어김없이 에러로 부터 배운다.

 

내가 왔을 당시 유저의 이미지 파일 CRUD에 대한 부분의 코드는 구현되지 않아 해당 부분을 구현 하였는데 단순 S3 에 이미지 파일 업로드 및 삭제는 이전 프로젝트에서도 해봤던 부분이라 쉽게 작성 하였다.

근데 문제는 이미지를 리사이즈 한 후 해당 이미지를 업로드 할때

S3 의 upload_fileobj 메소드에서 에러가 나는 것

 

이유는 파일을 리사이즈 후 <PIL.Image.Image image mode=RGB size=1600x2560 at 0x7FA0295B7A00>

와 같은 객체로 convert 되기 때문

그렇다면 이미지 업로드 메소드에서 읽을 수 있는 형태로 변경을 해줘야 한다. 

이 부분에서 일단 내가 작성할 수 있는 방법 두가지는

첫 째 AWS lambda 를 활용하여 S3 이미지 Thumbnail에 대한 함수 트리거를 구현하는 것

둘 째 해당 부분의 파일이 이미지 업로드 메소드에서 읽을 수 있는 Byte 화를 시켜서 업로드 하는 것

일단 S3 image resize 라는 키워드로 구글링 했을 때는 압도적으로 lambda 트리거 활용에 대한 자료도 많고 갓AWS 답게 위의 이미지 처럼 개발자 안내서에 예제와 개발 단계 까지 친절하게 가이드를 제시해줘서 구현자체는 어렵지 않았다. 하지만 문제는 

Lambda 함수가 외부데이터 전송을 할 때와 S3 스토리지에 접근할때 등 추가 요금 발생에 대한 리스크가 있어서 사수와 의논 후

실제 개발 당시는 후자의 방법을 택했다. 

 

해당 부분은 AWS class 를 따로 생성하여 resized_image 함수를 추가 하여 작성해주었다.

class AWSAPI:
    def __init__(self, aws_access_key, aws_secret_key, bucket, path):
                              :
                              :
                              :
                              
    def upload_image(self, file):
                              :
                              :
                              :
                              
    def resized_image(self, file):
                              :
                              :
                              :

 

원래의 코드에서는 해당 코드의 외부에서 aws = AWSAPI( @, $, %, &)

로 객체를 생성해준뒤 해당 view에서 resize 및 byte 로 변경까지 한 뒤 aws.upload_image(resized_file) 로 업로드를 시키려 했는데 해당 file이 close 되지 않았는지 method에서 bytes를 읽을 수 없다는 err 가 나오기도 했고 어짜피 resize 가 해당 api에서만 쓰이는 것이 아니기에 따로 함수를 분리 시켜줬다.

 

사실 이때까지는 간단.?했다.

resize 되는 것까지 확인하고 편안한 마음으로 있었는데 아니 이미지 업로드를 하다보니 이미지가 자동 회전이 되는 것이 아닌가 

아니 분명 핸드폰에서 업로드 전에는 정상적인 이미지였는데 왜 돌아가는지 확인해 봤더니 스마트 폰을 가로로 기울여 사진을 찍을 경우 이 사진은 가로로 기울여서 찍었다는 각도에 대한 값이 IMG 의 exif 태그에 저장되는데 그 회전값에 변경이 반영되지 않았기 때문이라고 한다.

따라서 PIL package 파일을 보면 아래와 같이 회전값에 대한 orientation 이 아래의 값으로 날라오는데

dict 로 저장 되어 있는 image 파일의 정보에서 orientation 값을 뽑은 뒤 회전값을 아래와 같이 딕셔너리로 만들어 주어 해당 이미지의 회전값이 위에 해당 되면 변경을 해서 저장 시키는 방식으로 구현 하였다.

 

이제 자동 회전 되는 사진도 정상적으로 돌아왔겠다 진짜 편한 마음으로 있으려고 했는데... 😂

특정 사진 업로드 시에만 아래와 같은 err 가 나오는 것 아니겠나

Exif IFD 값이 int라서 나오는 err 라는데 패키지 파일 안쪽에 들어가서 확인해보니 exif 안의 dict의 key의 value에 정해진 특정 format type 이 잘못 되었기 때문이라는 이유이다. .

근데 오른쪽 패키지 파일을 보니 아니 이미지 파일 안에 Humidity, pressure, waterdepth 같은 정보도 들어있다구요..?

와우 재밌는 정보다

여하튼 사실 이때 모든 값이 byte 로 있어야 하는데 해당 태그만 무슨 이유에서인지 int 로 나와 에러가 있는것인가 그렇다면 추후에 다른 태그값에서도 이런 에러가 있을 수 있겠구나 라고 생각해서 exif ifd 태그의 모든 value 값들을 뽑은뒤 Int가 있으면 전부 byte 화 시켜야 하겠구나 라는 삽질도 했다

그래서 이 삽질을 하며 41729 key값에 대한 value 를 확인 해 보기 위해 아래와 같이 프린트를 찍어봤는데

image3.jpeg / image4.jpg

사진하나에는  정말 다양한 정보가 다양한 형태로 존재하는구나.. 그래서 위의 가설은 실패 일단은 아래 사진 처럼 41729 의 value 값은 byte 가 정해진 format인데 int 로 들어와 에러가 났던것이고 해당 value를 뽑아서 int라면 byte화 시켜주는 로직을 추가 해주었다. 근데 구글링을 해보니 해당 태그에만 존재하는 에러가 아니라 다른 태그값들에 대해서도 한번씩 발생되는 에러인데 인스타그램과 같이 이미지가 비즈니스의 중요한 부분을 차지하는 서비스에서는 해당 에러를 어떻게 핸들링 하는지 너무 궁금해졌다. 

 

일단 내가 생각한 것은 각 태그에 정해진 포맷이 정해져 있으니 해당 포맷의 내용을 dict 로 만들어 업로드 되는 이미지의 Exif 값을 추출해서 byte여야 하는데 다른 type이라면 byte 화를 시켜줘야 하나, 다른사진을 이용하라는 massage를 내보내줘야하나.. 어떤 방법이 있을지 고민중인 상태이다. 

 

마지막으로 file을 다룰때 해당 file을 byte 화 시키는 작업을 끝내면 file을 닫기 전에 커서의 위치를 변경시켜줘야 한다. 

 

이전 회사에서 보고서 작성할때면 커서를 꼭 맨처음으로 옮겨놓고 저장해서 올리라는 매뉴얼이 있었는데ㅋㅋ 그때가 생각 났다.

 

위의 코드에서 byte화 시키고 커서의 위치를 찍어보니 아래 사진 처럼 456434 라고 맨 뒤를 가리킨다.

근데 seek(0) 이라고 커서를 0의 값으로 이동 시키고 난 뒤 커서가 0으로 맨 앞에 위치한 것을 확인 가능 하다.

 

커서를 이동시켜야 하는 이유는 커서가 맨뒤에 있을때 파일을 open 후 read 하려 하면 그 뒤에 내용이 없기 때문에  ''을 return 할 것이다. 그렇기 때문에 파일 수정 후 작성 후 꼭 커서 값을 돌려주고 닫아주자

 

그래서 위의 사진 중 오른쪽 사진은 orientation 이 돌아간 사진이려나 하고 뽑아봤는데 1이네 ..? 내가 이미지를 세로로 놓고 저렇게 찍은 사진인가 보다. 

 

일단 내가 일하게 될 분야가 어디가 될지 아직은 모르겠으나 어디를 가든 유저와 교류가 있는 비즈니스라면 image 나 video 파일에 대해서도 많이 접하게 될것이다. 개발을 하기 이전에는 image3.jpg 는 그저 내눈에는 image3 str 이었고 이제는 image3.jpg 안에 얼마나 많은 정보들이 들어있는지 알게 되었다. 저 패키지 파일만 해도 양이 어마어마 한데 video 에 대한 것은 어떠려나 궁금하다.

 

사실 이것 말고도 해당 코드를 구현하며 원래 알았다고 생각한 개념과 몰랐던 개념들을 많이 깨우쳤는데 정리하자면

 

1. 객체와 인스턴스, parameter

- 이미지 업로드를 위한 객체의 인스턴스 메소드로 접근하는 부분과 위의 class AWSAPI 의 생성자를 보면 path 라는 parameter를 받는 부분을 생각하며 뭔가 객체와 인스턴스 parameter에 대해 한번 더 생각해 볼 수 있는 기회였다.

- path 를 parameter로 받는 이유 : S3 이미지 업로드 시 유니크 네임으로 보내는데 파일 경로를관리 해줘야 될 거라 생각하여 업로드 시 이미지가 업로드가 이뤄지는 API가 review 라면 'review/' 라는 path 값을 전달해줘 directory를 review 에 관리 해줬다.

 

2. python file method

 - python에서 file을 다룰 때 필요한 메소드들에 대해 생각해볼 수 있었다. 항상 python 개념서적을 보면 필수로 등장하는 부분이었는데 막상 사용할때면 단순하게만 사용하는것 같다. 그런데 다행히 취업 준비중에 해당 내용이 들어가는 코딩 사전과제를 부여받아 더 고민 해보고 공부해볼 수 있는 기회도 있었다.

 

3. Image

 - Image 는 날잡고 공부해 볼만 할 것 같다. 핀터레스트나 인스타그램 등 이미지가 비즈니스에서 중요한 서비스에서는 해당 이미지의 관리를 어떻게 할지도 고민해 봐야겠다.

 

4. AWS lambda 

- 우연히도 접하게 된 lambda..! serverless 라니 분명히 유저가 몰려 많은 트래픽이 발생하면 server가 단운될 수도 있다. 근데 이 부분에 대한 고려는 여태 생각해본적이 없는데 사실 실제 배포시에 저렇게 함수를 분리시킬 수 있는 부분은 lambda 를 이용해서 최대한 분리 시킨다면? 서버에 가해지는 부하량이 확실하게 줄어들것이다 이건 Apach Jmeter 를 통해 한번 실험해 봐야겠다.

 

다음주에도 마지막 주차 답게 배우는게 많은 한주가 되기를 기도하며 마무리 해보겠습니다. 읽어주셔서 감사합니다. 

Comments