이벤트 로그 서버 구축 (3) - 데이터 수집 파트 2

2025. 1. 18. 12:08이벤트 로그

반응형

지난 글에서는 Serverless Framework를 사용해 프로젝트를 생성하고, 이를 테스트 및 배포하는 방법을 살펴보았습니다. 이제 본격적으로 코드를 작성해보겠습니다. 이벤트 로그를 Firehose에 전송하는 기능은 Firehose와 S3 설정이 필요하므로, 이와 관련된 코드는 [데이터 적재 파트]에서 함께 작성하도록 하겠습니다. 이번 섹션에서는 요청 파라미터를 검증하고, 전달받은 데이터를 콘솔에 출력하여 확인하는 코드만 작성하겠습니다.

 

1. 이벤트 로그 스키마

먼저, 이벤트 로그의 스키마를 정의합니다. 앞서 설명한 대로, 이벤트 로그는 이벤트명, 이벤트 발생 시점, 그리고 이벤트 관련 파라미터로 구성됩니다. 이를 기반으로 정의한 스키마는 아래와 같습니다.

type EventLog = {
  event_name: string;
  event_date: Date;
  event_params: Record<string, any>;
}

 

예를 들어, 로그인 이벤트, 상품 구매 이벤트는 아래와 같습니다.

// 로그인 이벤트
{
  "event_name": "login",
  "event_date": "2024-06-24",
  "event_params": {
    "user_id": "1234",
    "login_type": "kakao"
  }
}

// 상품 구매 이벤트
{
  "event_name": "purchase_product",
  "event_date": "2024-11-23",
  "event_params": {
    "user_id": "1234",
    "product_id": "921"
  }
}

 

2. 타입스크립트 설정

타입스크립트를 사용하기 위해 타입스크립트를 설치하고 설정을 진행하겠습니다. 저는 타입스크립트의 기본 설정을 그대로 사용하려고 합니다. 필요한 경우 tsconfig.json을 수정하시면 됩니다.

$ pnpm add -D typescript @types/node
$ pnpm tsc --init

 

서버리스 프레임워크에서는 v4부터 esbuild를 사용하여 배포 시 자동으로 타입스크립트 파일을 빌드합니다. 따라서 별도의 빌드 도구나 추가적인 트랜스파일링 작업이 필요하지 않습니다. 자세한 내용은 공식 문서에서 확인할 수 있습니다. 

 

3. 코드 작성

프로젝트 루트에 있는 handler.js는 삭제합니다. 그리고 src 폴더를 만들고 handler.ts 파일을 추가합니다. 그리고 AWS Lambda 핸들러와 이벤트 객체의 타입을 명확히 정의하기 위해 @types/aws-lambda 패키지를 설치합니다. 이 패키지는 Lambda 함수에서 사용되는 다양한 이벤트와 컨텍스트의 타입 정의를 제공하여 타입 안정성을 높이고, 코드 작성 시 자동 완성 기능을 활용할 수 있게 해줍니다.

$ pnpm add -D @types/aws-lambda

 

이제 이벤트 로그 데이터를 받아 콘솔에 출력하는 코드를 작성해보겠습니다. 이 코드는 클라이언트로부터 POST 요청을 통해 데이터를 수신하며, 요청 데이터는 HTTP 요청의 body에서 가져옵니다.

import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";

export const recordEventLog = async (
  event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
  const { body } = event;

  console.log(body);

  return {
    statusCode: 200,
    body: JSON.stringify({
      message: "success",
    }),
  };
};

 

이 함수를 사용하기 위해서 serverless.yml 파일의 functions 부분을 아래와 같이 수정합니다.

...

functions:
  recordEventLog:
    handler: src/handler.recordEventLog
    events:
      - httpApi:
          path: /
          method: POST

 

이제 sls dev 명령어 또는 sls offline 통해서 실행한 뒤 curl로 요청을 전송해보면 잘 출력되는 것을 확인할 수 있습니다.

$ sls dev

$ curl -X POST \
-H "Content-Type: application/json" \
-d '{"event_name": "login", "event_date": "2024-12-23", "event_params": { "login_type": "kakao" }' \
<YOUR_URL>

 

4. 요청 파라미터 검증

이제 요청 데이터를 검증하는 코드를 추가하겠습니다. 파라미터 검증은 클라이언트에서 전달받은 데이터가 필수 데이터인지, 형식이 올바른지 확인하는 중요한 작업입니다. 파라미터의 값과 형식을 검증하는 코드를 직접 작성할 수도 있지만, 이러한 작업을 편리하게 처리할 수 있는 여러 라이브러리가 존재합니다. 여기서는 타입스크립트와의 호환성이 뛰어나고, 직관적인 스키마 정의를 지원하는 zod를 사용하겠습니다. (이외에도 joi, class-validator 등 다양한 라이브러리가 있으니 원하는 라이브러리를 사용하시면 됩니다.)

$ pnpm add zod

 

zod를 사용하여 검증 스키마를 생성하고 검증을 수행하는 코드를 추가합니다.

import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
// +++
import { z, Zoderror } from 'zod';

// +++
const eventLogSchema = z.preprocess(
  (input) => {
    if (typeof input === "string") {
      return JSON.parse(input);
    }
    throw new Error("Invalid JSON format");
  },
  z.object({
    event_name: z.string().nonempty(),
    event_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
    event_params: z.record(z.any()),
  })
);

export const recordEventLog = async (
  event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
  const { body } = event;

  // +++
  try {
    const eventLog = eventLogSchema.parse(body);

    console.log(eventLog);

    return {
      statusCode: 200,
      body: JSON.stringify({
        message: "success",
      }),
    };
  } catch (e: unknown) {
    let statusCode: number = 500;
    let message: string = "Interval server error";
    if (e instanceof ZodError) {
      statusCode = 400;
      message = e.errors.map((e) => `[${e.path}: ${e.message}]`).join(",");
    }
    if (e instanceof Error) {
      message = e.message;
    }

    return {
      statusCode,
      body: JSON.stringify({
        message,
      }),
    };
  }
};

 

위에서 작성한 zod 스키마에 대해서 자세히 살펴보겠습니다. z.preprocess 는 스키마 검증 전에 입력값을 변환할 때 사용합니다. 여기서는 event.body의 값이 문자열이기 때문에 JSON으로 변환하기 위해서 사용합니다. event_name은 문자열이며 빈 값을 허용하지 않습니다. event_date는 문자열이며 ‘YYYY-MM-DD’ 형식이어야 합니다. event_params는 객체이며 값은 어떤 타입이든 상관없습니다.

const eventLogSchema = z.preprocess(
  (input) => {
    if (typeof input === "string") {
      return JSON.parse(input);
    }
    throw new Error("Invalid JSON format");
  },
  z.object({
    event_name: z.string().nonempty(),
    event_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
    event_params: z.record(z.any()),
  })
);

 

다시 sls dev 명령어 또는 sls offline 통해서 실행한 뒤 curl로 데이터를 변경하면서 전송해보면 에러 응답을 확인할 수 있습니다.

# event_name이 빈 문자열임.
$ curl -X POST \
-H "Content-Type: application/json" \
-d '{"event_name": "", "event_date": "2024-12-23", "event_params": { "login_type": "kakao" }' \
<YOUR_URL>

# event_date가 형식에 맞지 않음.
$ curl -X POST \
-H "Content-Type: application/json" \
-d '{"event_name": "login", "event_date": "20241223", "event_params": { "login_type": "kakao" }' \
<YOUR_URL>

# event_params가 누락됨.
$ curl -X POST \
-H "Content-Type: application/json" \
-d '{"event_name": "login", "event_date": "2024-12-23"}' \
<YOUR_URL>

 

5. 배포

이제 작성한 코드를 dev 환경으로 배포해보겠습니다. 현재 이벤트 로그를 적재하는 부분은 아직 구현되지 않았기 때문에, 우선 dev 환경에서 API가 정상적으로 호출되고 올바른 응답을 반환하는지 확인해보겠습니다. 이후 운영 환경(prod) 배포는 적재 로직을 구현한 뒤 진행할 예정입니다.

별다른 설정을 하지 않으면 기본적으로 dev 환경으로 배포되기 때문에 sls deploy 명령어를 사용하면 됩니다. 필요하다면 —-stage=development 로 명시할 수 있습니다.

$ sls deploy
# 또는
$ sls deploy --stage dev

 

배포가 완료되면 함수를 트리거 할 수 있는 URL이 출력되는데 이 URL은 API Gateway를 통해 생성된 엔드포인트입니다. 이 엔드포인트를 호출하여 함수가 정상적으로 동작하는지 테스트 해봅니다.

$ curl -X POST \
-H "Content-Type: application/json" \
-d '{"event_name": "login", "event_date": "2024-12-23", "event_params": { "login_type": "kakao" }' \
<YOUR_URL>

 

다음 글에서는

이번 글에서는 이벤트 로그의 스키마를 정의하고 데이터를 검증하는 코드를 작성하였습니다. 다음 글에서는 데이터 적재를 위한 인프라(AWS Kinesis Firehose, AWS S3)를 설정하고 데이터를 전송하는 방법에 대해 다뤄보겠습니다.

 

2025.01.04 - [이벤트 로그] - 이벤트 로그 서버 구축 (1) - 개요

2025.01.11 - [이벤트 로그] - 이벤트 로그 서버 구축 (2) - 데이터 수집 파트 1

2025.01.18 - [이벤트 로그] - 이벤트 로그 서버 구축 (3) - 데이터 수집 파트 2

2025.01.25 - [이벤트 로그] - 이벤트 로그 서버 구축 (4) - 데이터 적재 파트 1

2025.02.02 - [이벤트 로그] - 이벤트 로그 서버 구축 (5) - 데이터 적재 파트 2

2025.02.09 - [이벤트 로그] - 이벤트 로그 서버 구축 (6) - 데이터 분석 파트

반응형