Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .github/workflows/deploy-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Deploy to AWS Lambda Test

on:
workflow_dispatch:

env:
AWS_REGION: ap-northeast-2
ECR_CRAWL_REPO_NAME: dev/crawl
LAMBDA_FUNCTION_NAME: withins-playwright-crawler-test

jobs:
deploy:
name: Deploy to AWS Lambda
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ECR_LAMBDA_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_ECR_LAMBDA_SECRET_ACCESS_KEY_ID }}
aws-region: ${{ env.AWS_REGION }}

- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2

- name: Build and push Docker image
id: build-image
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
REPOSITORY: ${{ env.ECR_CRAWL_REPO_NAME }}
IMAGE_TAG: ${{ github.sha }}
run: |
IMAGE_URI=$REGISTRY/$REPOSITORY:$IMAGE_TAG
docker build -t $IMAGE_URI .
docker push $IMAGE_URI
echo "image-uri=$IMAGE_URI" >> $GITHUB_OUTPUT

- name: Deploy to Lambda
uses: int128/deploy-lambda-action@v1
with:
function-name: ${{ env.LAMBDA_FUNCTION_NAME }}
image-uri: ${{ steps.build-image.outputs.image-uri }}
153 changes: 0 additions & 153 deletions CrawlingService_temp.ts

This file was deleted.

68 changes: 25 additions & 43 deletions src/aws/lambda/CrawlingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,9 @@ import { JobRegistry } from '../../entity/job/JobRegistry';
import { Job } from '../../entity/job/Job';
import { JobExecutor } from '../../entity/job/JobExecutor';
import { getKoreaTimeISO } from '../../utils/DateUtils';
import { CrawlingEvent } from './handler';
import { validateJobName } from './LambdaEventValidator';
import { TargetDate } from '../../entity/TargetDate';
import {
Result,
withErrorHandling,
withSyncErrorHandling,
isSuccess,
isFailure,
success,
failure,
} from '../../utils/ErrorHandling';
import { HandleErrors } from '../../utils/ErrorHandling';
import { AppError } from '../../errors/AppError';
import { ERROR_MESSAGES } from '../../constants/ErrorMessages';
import { OPERATION_CONTEXT } from '../../constants/OperationContext';
Expand All @@ -32,7 +23,7 @@ export class CrawlingService {
private browser: Browser | null = null;
private jobExecutor: JobExecutor | null = null;

async executeCrawling(targetDate: TargetDate, jobName: string): Promise<Result<CrawlingResult>> {
async executeCrawling(targetDate: TargetDate, jobName: string): Promise<CrawlingResult> {
const startTime = Date.now();
console.log(`크롤링 시작 at ${getKoreaTimeISO()}`);

Expand All @@ -42,42 +33,36 @@ export class CrawlingService {
const parsedDate = targetDate.dateObject;

// 1단계: 브라우저 초기화
const browserResult = await this.initializeBrowser();
if (isFailure(browserResult)) {
return failure(browserResult.error, OPERATION_CONTEXT.BROWSER_INIT);
}
await this.initializeBrowser();

// 2단계: Job 찾기
const jobResult = this.findJob(jobName);
if (isFailure(jobResult)) {
return failure(jobResult.error, OPERATION_CONTEXT.JOB_LOOKUP);
}
const job = this.findJob(jobName);

// 3단계: JobExecutor 실행
this.jobExecutor = new JobExecutor(this.browser!);
const executionResult = await this.executeJob(jobResult.data, {
const executionResult = await this.executeJob(job, {
targetDate: parsedDate,
});

if (isFailure(executionResult)) {
return failure(executionResult.error, OPERATION_CONTEXT.JOB_EXECUTION);
}

const endTime = Date.now();
console.log(`Crawling completed in ${endTime - startTime}ms`);

return success({
processedJobs: executionResult.data.processedJobs,
results: executionResult.data.results,
itemCount: executionResult.data.itemCount,
});
return this.createCrawlingResult(executionResult);
} finally {
await this.cleanup();
}
}

// HOF로 래핑된 브라우저 초기화
private initializeBrowser = withErrorHandling(async (): Promise<void> => {
private createCrawlingResult(executionResult: { processedJobs: string[]; results: any[]; itemCount: number }): CrawlingResult {
return {
processedJobs: executionResult.processedJobs,
results: executionResult.results,
itemCount: executionResult.itemCount,
};
}

@HandleErrors(OPERATION_CONTEXT.BROWSER_INIT, ERROR_MESSAGES.BROWSER_INIT_FAILED)
private async initializeBrowser(): Promise<void> {
console.log('Initializing browser...');
this.browser = await chromium.launch({
headless: true,
Expand All @@ -103,10 +88,10 @@ export class CrawlingService {
],
});
console.log('Browser initialized successfully');
}, OPERATION_CONTEXT.BROWSER_INIT);
}

// HOF로 래핑된 Job 찾기
private findJob = withSyncErrorHandling((jobName: string): Job => {
@HandleErrors(OPERATION_CONTEXT.JOB_LOOKUP, ERROR_MESSAGES.JOB_NOT_FOUND)
private findJob(jobName: string): Job {
const job = JobRegistry.getJobByName(jobName);
if (!job) {
throw new AppError(
Expand All @@ -121,16 +106,13 @@ export class CrawlingService {
}
console.log(`Found job: ${job.jobName}`);
return job;
}, OPERATION_CONTEXT.JOB_LOOKUP);
}

// HOF로 래핑된 Job 실행
private executeJob = withErrorHandling(
async (job: Job, context: { targetDate: Date }) => {
const result = await this.jobExecutor!.execute(job, context);
return result;
},
OPERATION_CONTEXT.JOB_EXECUTION
);
@HandleErrors(OPERATION_CONTEXT.JOB_EXECUTION, ERROR_MESSAGES.JOB_EXECUTION_FAILED)
private async executeJob(job: Job, context: { targetDate: Date }) {
const result = await this.jobExecutor!.execute(job, context);
return result;
}

private async cleanup(): Promise<void> {
this.jobExecutor = null;
Expand Down
Loading