본문으로 바로가기

과거에 이미지 서비스를 구현했을 때는 multer-s3-transform을 통해서, 이미지를 sharp로 리사이징한 후 s3에 저장해서 cloudfront를 붙여 서빙하곤 했다. 이 방법이 크게 문제가 되는 것은 아니지만, 조금 더 세련되고 간편한 방법이 없을까 싶어서 방법을 탐구해보았다. 

 

1. 일반적인 이미지 서비스를 위한 플로우 (미리 리사이징해서 서빙하기)

 

이미지 변환용 ec2 서버를 한대 돌려서, 업로드 되면 SQS 메세지를 통해 해당 이미지를 리사이징하는 방법입니다.

아마존 웹 서비스를 다루는 기술의 pyrasis 님의 실습 부분을 참고하였습니다.

s3에 일어난 이벤트들을 리스닝하지 못해서 SQS를 사용하는 것이 어쩐지 복잡해보이네요.

람다를 활용해 특정 이벤트를 감지하여 작동시키는 것이 좋아보입니다.

 

솔직히 서버리스로 인해 발생하는 비용과 비교해서 더 싼지 어쩐지는 모르겠습니다. 근데 서버리스는 비용이 문제라기보다는, 간편함, 스케일링에서의 자유로움, 개발자의 수명 연장 등이 관건이라. ㅋㅋ 무조건 이 방식 보다는 서버리스 쓰세요. 수명이 연장됩니다.

 

  1. 유저가 사진을 업로드 한다.
  2. 받은 이미지 파일을 S3에 저장하고 이미지 주소(s3 주소)를 RDS에 저장하고, SQS 메세지로 보낸다.
  3. 이미지 변환 전용 EC2 인스턴스는 SQS 메세지를 통해 s3 주소를 받아 해당 이미지의 리사이징 작업 및 해상도 저하 작업을 한다.
  4. 작업된 이미지 파일을 S3에 저장하고 작업된 이미지의 주소를 RDS에 저장한다. 잊지 말고 SQS 메세지는 삭제해주자.
  5. 웹 서버에서는 요청이 들어오면 RDS에서 리사이징 된 이미지를 찾아 유저에게 보여준다.

 

2. 서버리스를 곁들인 이미지 리사이징 방법 (일반적인 방법 : 미리 리사이징했다가, 요청하면 리사이징된 이미지를 보여준다.)

 

여기를 좀 읽어보세요 : https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/welcome.html

 

 

  1. 유저가 이미지를 S3에 업로드한다. 원본 이미지를 보여주고 싶다면 이 주소를 DB에 저장해두자.
  2. s3:ObjectCreated:Put 이벤트에 따라 Lambda가 트리거 되어 리사이징 작업을 한다. 한 이미지당 여러 해상도의 thumbnail을 생성한다고 보자. 모바일 화면, 타블렛 화면, 웹 화면 최소 3장은 만들어서 리사이징 결과물이 나와야 한다.
  3. 작업된 이미지는 다시 S3에 저장하고 해당 주소를 DB에 저장한다.
  4. 요청이 들어온 경우 DB에서 불러와서 해당 이미지를 보여준다.

이 방법이 온디맨드 리사이징과 비교해 가지는 장점은,

리사이징 시간을 굳이 이용자측에서 대기할 필요 없고 리사이징된 이미지를 곧바로 확인할 수 있다는 겁니다.

솔직히 저는 웬만하는 이 방법을 사용할 것 같습니다.

 

 

물론 단점도 있긴 합니다.

(1) 이미지 업로드 즉시 리사이징되므로 모든 이미지를 리사이징하게 된다는 것,

(2) 만약 서비스 UI가 개편되어 필요한 이미지 사이즈가 변경되면, 지금까지 저장된 이미지 전부를 다시 규격에 맞게 리사이징해야 한다는 것.

 

(2)번의 문제는 쉽게 보일 수도 있지만, VCNC에서는 온디맨드 리사이징으로 방식을 변경했을 때 기존의 11억장 원본 사진을 다른 포맷으로 변경하고 50억장의 썸네일 사진을 지우기 위해서 별도의 작업을 했어야 했습니다. 처음 설계할 때부터 잘 짜놔야 좋습니다.

 

어쨌거나, 이러한 문제에서 자유롭고 싶다면 온디맨드 이미지 리사이징을 사용하는 편입니다.

 

 

3. 서버리스를 곁들인 온디맨드 이미지 리사이징 (유저가 이미지를 요청할 때에 실시간으로 생성.)

 

리사이징을 요청을 보냈을 때 하느냐, 미리 하느냐의 차이밖에 없다.

 

  1. 유저가 이미지를 S3에 업로드한다. 원본 이미지를 보여주고 싶다면 이 주소를 DB에 저장해두자.
  2. 유저가 요청을 보내면 Lambda가 트리거 되어 리사이징 작업을 한다. 
  3. 작업된 이미지는 다시 S3에 저장하고 해당 주소를 DB에 저장한다.
  4. 요청이 들어온 경우 DB에서 불러와서 해당 이미지를 보여준다.

 

이 방법의 장점은 명확합니다.

(1) 이미지를 미리 생성해서 저장해두지 않아서 용량 절약

(2) 필요한 이미지 사이즈가 변경되어도 요청 시에 리사이징 되므로 기존 이미지를 재 변환할 필요가 없다 점이 장점입니다.

 

이 방법을 사용하려면 미리 리사이징한 이미지를 보여달라는 요청 자체가 적을 때가 유리합니다.

어차피 보여줄거면 미리 리사이징해두는게 빠를 수도 있스빈다.

 

온 디맨드 리사이징의 단점도 있습니다.

(1) 한번에 요청이 너무 많이 들어오게 된 경우 이미지 변환이 실패할 수 있다는 점

(2) CDN에서 캐시되기 전까지는 같은 이미지 리사이즈 요청을 보낼 수도 있다는 점 (불필요한 작업)

(3) 리사이징에 시간이 오래 소요되는 경우 이용자는 "이미지 왜 안뜨지?"를 경험할 가능성.

 

온디맨드 리사이징으로 바꾼 사례

http://engineering.vcnc.co.kr/2016/05/ondemand-image-resizing/

 

 

 

4. aws lambda가 아니라 lambda@edge를 사용하여 성능을 해선하자는 안.

 

아직 안 써봐서 몰겠음 ㅎ

 

당근마켓은 labmda@edge를 사용

https://medium.com/daangn/lambda-edge%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%98%EB%8A%94-on-the-fly-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A6%AC%EC%82%AC%EC%9D%B4%EC%A7%95-f4e5052d49f3

 

 


 

무슨 리사이징 도구를 사용할까?

node를 기준으로 살펴보면...

 

gm

이 분야의 전통 강자다. gm은 의존성으로 graphicsmagick과 imagemagick를 의존성으로 가지므로 별도 설치해야 한다.

www.npmjs.com/package/gm

 

sharp

코딩 배우고 처음 만든 프로젝트에서 사용했는데, 이상하게 의존성 관련 에러나 설치 에러가 종종나서 안 쓰고는 있다. 다만, 매우 직관적.

www.npmjs.com/package/sharp

 

 


이 외에 주의할 점들)

iOS는 WebP가 지원이 안됩니다. 확장자가…

 

(파이썬 or Node 선호 하시면) - CloudFront + Lambda@Edge를 많이 추천합니다.
(Golang도 괜찮으시면) CloudFront + ALB + Docker Lambda(golang - imageproxy)

 

reference)

github.com/amazon-archives/serverless-image-resizing

aws.amazon.com/ko/blogs/compute/resize-images-on-the-fly-with-amazon-s3-aws-lambda-and-amazon-api-gateway/


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체