이미지 다루기
우리가 접하는 많은 서비스에서 이미지는 모두 온라인상 어딘가에 저장되어 있고, 클라이언트가 요청할 때마다 이미지를 전송합니다.
아주 단순한 시스템에서는 서버내 폴더 어딘가에 저장해 놓았다가 내려주기도 하고, Web Server 와 WAS 가 분리된 구조에서는 정적인 파일(html, js, image)들을 Web Server 에 저장하는 방식으로 서버의 부하를 분산시키고 캐시를 사용하기도 합니다.
AWS 를 사용하는 경우에는 S3 를 image 서버처럼 활용하기도 합니다. 이와 같은 경우에는 image 를 서버로 업로드하는게 아니라, presigned URL 을 활용하여 클라이언트에서 S3로 직접 이미지를 upload 하는 방법을 주로 사용합니다. 다음 그림은 presigned URL 을 생성하는 람다를 활용한 s3 upload 방법입니다.
CloudFront 와 Lambda@Edge
저희 호갱노노에서는 AWS 를 기본 서버 환경으로 사용하고있고 S3 에 이미지를 저장하고 있습니다.
그리고, 저장된 이미지는 AWS CloudFront 를 통해 사용자에게 전달됩니다. 다음은 CloudFront (이후 CF) 에 대한 AWS 의 설명입니다.
Amazon CloudFront는 .html, .css, .js 및 이미지 파일과 같은 정적 및 동적 웹 콘텐츠를 사용자에게 더 빨리 배포하도록 지원하는 웹 서비스입니다.
그럼 이제 S3 에 있는 이미지를 워터마킹해서 내려주는 방법을 알아보겠습니다.
저희는 CloudFront 를 통해 내려주는 이미지에 워터마킹을 표시하기 위해서 lambda@edge 를 사용하였습니다.
다음은 lambda@edge 에 대한 AWS 에 설명입니다.
Lambda@Edge를 사용하면 Node.js 및 Python Lambda 함수를 실행하여 CloudFront가 제공하는 콘텐츠를 사용자 지정하여 AWS 위치의 함수를 최종 사용자와 더 가깝게 실행할 수 있습니다. 이 함수는 서버 프로비저닝 또는 관리 없이 CloudFront 이벤트에 응답하여 실행됩니다. Lambda 함수를 사용하여 CloudFront 요청 및 응답을 다음과 같이 변경할 수 있습니다.
그러니까 CF 에 들어오는 요청 전후로 람다함수(에지함수)를 이용하여 req/res 를 변경할 수 있습니다. 그리고, 워터마킹은 바로 이 람다함수를 작성하여 실행합니다.
워터 마킹을 위한 Lambda@Edge 설정
- lambda 페이지로 이동 후 Region을 Virginia로 바꿉니다. 그리고 Create function을 클릭합니다. (lambda edge는 Virginia에서만 사용 가능)
- 필요한 내용을 채우고 Create Function 을 클릭합니다. 내용은 이름만 적어도 됩니다. (기존에 사용하던 role을 그대로 사용하고 싶다면 Execution role에서 Use an existing role을 선택합니다.)
- Configuration > Permissions에서 해당 Role을 클릭합니다.
- 아래 화면에서 Policy 왼쪽 +를 누르면 json이 나오고 Edit을 눌러 아래와 같이 적어줍니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "{resource arn을 적어주세요}"
},
{
"Effect": "Allow",
"Action": [
"iam:CreateServiceLinkedRole",
"lambda:GetFunction",
"lambda:EnableReplication*",
"cloudfront:UpdateDistribution",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
],
"Resource": "*"
}
]
}
- Trust relationships 탭으로 이동해서 아래와 같이 수정합니다.
Service 목록에 “edgelambda.amazonaws.com”이 꼭 있어야 합니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com",
"edgelambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
- 코드를 작성합니다.코드가 너무 크다면 업로드 할 수 있습니다.
예제 함수들 및 response 생성 및 업데이트 을 확인할 수 있습니다.
다음은 실제 코드중 워터마킹하는 부분만 발췌하였습니다.
/**
* watermark 생성 로직
**/
let resizedWaterMarkImage = sharp('watermark.png').rotate();
const width = imageMetaData.width;
const heigth = imageMetaData.height;
// ....
// 원본이미지에 맞게 워터마크의 크기를 조절한 후,
// ....
resizedWaterMarkImage = await resizedWaterMarkImage.resize(waterWidth, waterHeight, { fit: 'inside' }).toBuffer();
await zigbangImage.composite([{ input: resizedWaterMarkImage, gravity: 'center' }]);
- 그리고, 테스트를 해봅니다. (이벤트 함수 예제)
Test할 때 생기는 log는 확인이 가능합니다. CloudWatch Log group에 없다면 생성하면 됩니다.
- 새 버전을 publish 합니다.
- publish 한 람다의 ARN을 복사 후 CF 에 붙입니다.
behavior 까지 새롭게 만드려면 Create behavior 를 하고 그게 아니라면 해당 Behavior를 수정합니다.
- 적용시 캐시 때문에 시간이 다소 소요될 수 있습니다. 그럼, 다음과 같이 호갱노노 이미지가 워터마크로 이미지에 표시됩니다.
결과물
이상으로 S3 에 저장된 이미지를 CloudFront 의 lambda@edge 를 통해서 워터마킹 하는 방법에 대해서 알아봤습니다.
확실히 AWS 를 활용하면 많은 노력 없이도 쉽고 편리하게 서버에서 이미지를 다룰 수 있다는 점이 참 매력적인거 같습니다.
감사합니다.