공부/DevOps

[AWS] 서버리스 기반 URL 주소 단축 서비스 구현 - (1) 키 생성 람다 구현

leejinwoo1126 2024. 9. 24. 10:32
반응형

 

이번 포스팅에서는 AWS 인프라를 활용해서 URL 주소 단축 서비스를 만들어보려 한다.

 

긴 URL 주소를 단축했을 때의 장점은 아래와 같다

① URL 가독성 개선, 공유 가능

② 사용자 경험 개선 및 클릭률(CTR) 향상 

③ 브랜딩과 신뢰도 향상

 링크 분석 및 트래킹 가능

⑤ 관리 및 업데이트 용이성

⑥ 제한된 공간에서의 효율성

⑦ URL 관리 용이성 

 

대표적으로 https://bitly.com/와 같은 서비스를 예로 들수 있다. "백문불여일견" 아래를 보면 한 눈에 이해 될 것이다. 

 

AWS 공식 블로그에서는 CloudFront를 앞단에 위치하고 있는데, 이번 포스팅에서는 3단계로 나눠

직접 도메인을 발급하고 Route53 - API Gateway - Lambda - S3 연결하는 과정을 다뤄본다.

 


Infra Architecture

 

 

AWS Toolkit 

AWS CLI + VSCode AWS Toolkit Extension 사용하여 로컬에서 쉽게 람다, s3 업로드 할 수 있다

# 패키지 설치
$ sudo apt install unzip -y

$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"

$ sudo unzip awscliv2.zip

$ sudo ./aws/install

$ aws --version

 

 

VSCode 실행해서 AWS Toolkit Extension 설치해준다

 

설치가 완료되면 왼쪽 탭 하단에 "aws" 누르면 EXPLORER 통해 현재 profile 기준으로 서비스 목록이 출력된다. 

 

미리 AWS Lambda Function(shorten-url-lambda)을 생성해 두었기 때문에

마우스 우클릭 후 Download를 누르면 아래와 같이 스크립트 파일 내려받아 작업할 수 있게 된다.

 

 

간단하게 스크립트 수정 후 업로드를 해보도록 한다.

EXPLORER에서 우클릭 후 Upload Lambda... > Directory > No > 로컬 람다 디렉토리 선택 하면 업로드 된다

 

 

AWS 웹 콘솔에서 람다 테스트를 실행(파라미터 추가, {"original_url" : "주소"}), 아래와 같이 정상 실행하는 것을 확인할 수 있다.

 

S3 버킷 생성 

기본 설정에 버킷 이름만 도메인 주소로 지정하여 생성한다. 

 

 

키 생성 Lambda Function 

스크립트 절차는 아래와 같다 

① generateRandomKey(..) 사용하여 7자리 key 값을 생성한다 (알파벳 소문자/대문자, 숫자 0-9)

② key 값 중복 확인 후 s3 bucket에 저장한다 (*이때 WebRedirectionLocation에 원본 주소 값을 저장)

③ 정상 실행 완료되면 지정된 응답을 반환한다

import AWS from 'aws-sdk';
import crypto from 'crypto';

const s3 = new AWS.S3({
  region : 'ap-northeast-2'
});
const BUCKET_NAME = process.env.S3_BUCKET;
const FORWARD_HOST = process.env.FORWARD_HOST;
const SHORT_KEY_SIZE = parseInt(process.env.SHORT_KEY_SIZE, 10) || 7;
const CHAR_SET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

function generateRandomKey(length) {
  let key = '';
  const charsetLength = CHAR_SET.length;
  for(let i = 1; i <= length; i++) {
    const idx = crypto.randomInt(0, charsetLength);
    key += CHAR_SET[idx];
  }

  return key;
}

async function isKeyAvailable(bucket, key) {
  try {
    await s3.headObject({'Bucket': bucket, 'Key' : key}).promise();
    return false;
  } catch(error) {
    const statusCode = error.statusCode;
    if(statusCode === 403 || statusCode === 404) {
        return true;
    }
    throw error;
  }
}

async function putObjectOnS3(bucket, key, redirectLocation) {
  try {
    await s3.putObject({
      'Bucket': bucket,
      'Key': key,
      'Body': '',
      'ContentType': 'text/plain',
      'WebsiteRedirectLocation': redirectLocation
    }).promise();

    return true; 
  } catch(error) {
    console.error('Error:', error);
    return false;
  }
}

export const handler = async (event) => {
  const origin = event.native_url;

  let id;
  let key;
  while(true) {
    id = generateRandomKey(SHORT_KEY_SIZE);
    key = `url/${id}`;

    const keyAvailable = await isKeyAvailable(BUCKET_NAME, key);
    if(keyAvailable) {
      break;
    }
  }

  const isPutSuccess = await putObjectOnS3(BUCKET_NAME, key, origin);
  if(!isPutSuccess) {
    id = null;
  }

  return {
      'shortId': id,
      'forwardUrl': FORWARD_HOST,
      'nativeUrl': origin,
  }
};

 

로컬 디렉토리로 이동 후 아래 명령어 실행한 다음 업로드 한다. (node v20.11.0)

$ npm init -y
$ npm install aws-sdk

 

 

람다 함수 실행시 S3 Bucket에 대한 읽기와 쓰기 권한이 필요하다. 설정을 위해 권한 탭에 바로가기 링크로 이동한다.

 

 

아래와 같이 JSON 형식으로 S3:GetObject, S3:PutObject 권한을 추가한다 (Resource의 경우 S3의 주소를 기재)

 

 

람다 함수의 환경 변수 편집에서 스크립트 실행시 필요한 속성과 값들을 추가한다.

 

 

람다 함수에서 테스트 실행시 정상적으로 원하는 응답을 반환함을 확인할 수 있었다.

 

s3 bucket에 해당 key 객체의 메타 데이터 또한 의도한대로 원본 주소가 잘 저장되어 있는 것을 확인했다. 

 

 

API Gateway 생성

이제 API Gateway Trigger를 생성 및 연결한다. 생성시 REST API 선택 외에는 특별한게 없다. 

 

API 게이트웨이 링크를 클릭하여 이동한다.

 

메서드 생성 버튼을 눌러서 POST 메서드를 추가한다.

 

 

생성한 POST 메서드에 대한 테스트를 실행한다.

요청 본문(body)에 native_url 기재 후 테스트 실행하면 원하는 응답 결과를 확인할 수 있다.

 

 

API 배포

default 스테이지 선택 후 배포 버튼을 누르면 endpoint가 생성되고 외부에서 호출 가능하다.

 

참고. Postman으로 API Gateway endpoint 호출 결과

 

 

 


Reference.

AWS Blog - Build a Serverless, Private URL Shortener

유튜브 - AWS를 통해 서버 없이 단축 URL 서비스 만들기 #1

노아론 블로그 - AWS 서버리스 단축 URL 서비스

AWS - What is AWS Lambda?

AWS - AWS SDK for JavaScript

AWS - Invoking Lambda with events from other AWS services

AWS Document - Using the Lambda context object to retrieve Node.js function information

반응형