서버리스 함수: AWS 람다로 코드 한 조각 실행하기! λ

콘텐츠 대표 이미지 - 서버리스 함수: AWS 람다로 코드 한 조각 실행하기! λ

 

 

안녕, 친구들! 오늘은 정말 흥미진진한 주제로 여러분과 함께할 거야. 바로 서버리스 함수AWS 람다(Lambda)에 대해 깊이 파헤쳐볼 거거든. 😎 이 주제가 왜 중요하냐고? 현대 웹 개발의 트렌드이자, 효율적인 리소스 관리의 핵심이기 때문이지! 자, 그럼 우리 함께 서버리스의 세계로 뛰어들어볼까? 🏊‍♂️

🚀 잠깐! 알고 가자!
서버리스(Serverless)라고 해서 서버가 아예 없는 게 아니야. 단지 우리가 서버 관리에 신경 쓰지 않아도 된다는 뜻이지. 마치 재능넷에서 다양한 재능을 거래할 때, 플랫폼 관리는 신경 쓰지 않고 오로지 재능 교환에만 집중할 수 있는 것처럼 말이야!

1. 서버리스 아키텍처란 뭐야? 🤔

서버리스 아키텍처는 말 그대로 '서버 없는' 구조를 의미해. 하지만 실제로는 서버가 없는 게 아니라, 개발자가 서버 관리에 신경 쓰지 않아도 되는 구조를 말하는 거야. 즉, 서버 인프라를 클라우드 제공업체가 관리하고, 개발자는 순수하게 코드 작성에만 집중할 수 있는 환경이지.

서버리스의 핵심은 '함수'야. 전통적인 서버 기반 아키텍처에서는 애플리케이션 전체를 하나의 큰 덩어리로 배포하고 관리했다면, 서버리스에서는 애플리케이션을 작은 함수 단위로 쪼개서 필요할 때만 실행하는 방식을 사용해.

서버리스 vs 전통적 아키텍처 비교 전통적 아키텍처 서버리스 아키텍처 모놀리식 앱 함수들

위 그림을 보면 차이가 확 와닿지? 전통적인 방식은 하나의 큰 애플리케이션을 통째로 관리해야 했어. 반면 서버리스는 작은 함수들의 집합으로 이루어져 있지. 이렇게 하면 각 기능을 독립적으로 개발하고 배포할 수 있어서 유연성이 크게 향상돼.

서버리스의 장점 🌟

  • 비용 효율성: 사용한 만큼만 지불하면 돼. 함수가 실행되지 않을 때는 비용이 들지 않아.
  • 확장성: 트래픽이 증가해도 자동으로 확장되니까 걱정 없어!
  • 개발 속도 향상: 서버 관리에 시간을 쓰지 않아도 되니까, 순수하게 비즈니스 로직 개발에만 집중할 수 있어.
  • 유지보수 간소화: 작은 함수 단위로 관리하니까, 문제가 생겨도 빠르게 해결할 수 있지.

💡 재능넷 팁!
서버리스 아키텍처는 재능넷 같은 플랫폼에서도 활용할 수 있어. 예를 들어, 사용자 인증이나 결제 처리 같은 기능을 서버리스 함수로 구현하면 더 효율적인 리소스 관리가 가능해질 거야.

2. AWS Lambda: 서버리스의 강력한 도구 🛠️

자, 이제 AWS Lambda에 대해 자세히 알아볼 차례야. AWS Lambda는 아마존 웹 서비스(AWS)에서 제공하는 서버리스 컴퓨팅 서비스야. Lambda를 사용하면 서버를 프로비저닝하거나 관리하지 않고도 코드를 실행할 수 있어. 정말 편리하지?

AWS Lambda의 작동 원리 🔍

Lambda의 작동 원리는 생각보다 단순해. 다음과 같은 순서로 진행돼:

  1. 함수 작성: 원하는 프로그래밍 언어로 함수를 작성해.
  2. 함수 업로드: 작성한 함수를 AWS Lambda에 업로드해.
  3. 트리거 설정: 함수를 실행할 조건(트리거)을 설정해.
  4. 함수 실행: 설정한 트리거 조건이 충족되면 함수가 자동으로 실행돼.
  5. 결과 반환: 함수 실행 결과가 필요한 곳으로 전달돼.
AWS Lambda 작동 원리 함수 작성 함수 업로드 함수 실행 결과 반환

이 과정이 자동으로 이뤄지기 때문에, 개발자는 함수 로직에만 집중할 수 있어. 서버 관리나 스케일링에 대해서는 전혀 신경 쓸 필요가 없지. 👍

Lambda 함수의 특징 🌈

  • 이벤트 기반 실행: HTTP 요청, 데이터베이스 변경, 파일 업로드 등 다양한 이벤트에 반응해 실행돼.
  • 자동 스케일링: 동시에 여러 요청이 들어와도 자동으로 확장되어 처리해.
  • 다양한 언어 지원: Node.js, Python, Java, C#, Go 등 다양한 프로그래밍 언어를 지원해.
  • 타임아웃 제한: 기본적으로 함수 실행 시간은 최대 15분으로 제한돼. 긴 작업은 다른 방식으로 처리해야 해.
  • 상태 비저장(Stateless): 함수는 상태를 저장하지 않아. 필요한 경우 외부 저장소를 사용해야 해.

🔔 알림!
Lambda 함수는 재능넷 같은 플랫폼에서 다양하게 활용될 수 있어. 예를 들어, 사용자가 새로운 재능을 등록할 때마다 자동으로 검증하는 함수를 만들 수 있지. 이렇게 하면 관리자의 수동 작업을 줄이고 효율성을 높일 수 있어!

3. Lambda 함수 만들기: 실전 예제 👨‍💻

이제 실제로 Lambda 함수를 만들어볼 거야. 간단한 예제로 시작해서, 점점 복잡한 함수로 발전시켜 나가볼게. 준비됐니? 그럼 시작해볼까! 🚀

3.1 Hello, Lambda! 👋

가장 기본적인 Lambda 함수부터 시작해보자. 이 함수는 단순히 "Hello, Lambda!"라는 메시지를 반환할 거야.


exports.handler = async (event) => {
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello, Lambda!'),
    };
    return response;
};

이 코드를 설명해줄게:

  • exports.handler: 이것이 Lambda 함수의 진입점이야. AWS가 이 함수를 호출할 거야.
  • async (event) => { ... }: 비동기 함수로, event 파라미터를 받아. 이 event에는 함수 호출 시 전달된 데이터가 들어있어.
  • response 객체: HTTP 응답을 모방한 구조야. statusCode는 HTTP 상태 코드, body는 응답 본문이야.
  • JSON.stringify(): 문자열을 JSON 형식으로 변환해. API Gateway와 함께 사용할 때 필요해.

이 함수를 Lambda에 업로드하고 테스트해보면, "Hello, Lambda!"라는 메시지를 받을 수 있을 거야.

3.2 이벤트 데이터 활용하기 🎭

이번에는 이벤트 데이터를 활용해서 조금 더 동적인 함수를 만들어볼게. 사용자의 이름을 받아서 개인화된 인사를 반환하는 함수를 만들어보자.


exports.handler = async (event) => {
    let name = "Guest";
    if (event.queryStringParameters && event.queryStringParameters.name) {
        name = event.queryStringParameters.name;
    }
    
    const response = {
        statusCode: 200,
        body: JSON.stringify(`Hello, ${name}! Welcome to Lambda.`),
    };
    return response;
};

이 함수는 조금 더 복잡해 보이지? 하나씩 설명해줄게:

  • event.queryStringParameters: API Gateway를 통해 전달된 쿼리 문자열 파라미터를 포함해.
  • if (event.queryStringParameters && event.queryStringParameters.name) { ... }: 쿼리 문자열에 'name' 파라미터가 있는지 확인해.
  • name = event.queryStringParameters.name;: 'name' 파라미터가 있으면, 그 값을 사용해.
  • `Hello, ${name}! Welcome to Lambda.`: 템플릿 리터럴을 사용해 동적 메시지를 생성해.

이 함수를 API Gateway와 연결하면, https://your-api-endpoint.com/path?name=John 같은 URL로 요청을 보냈을 때 "Hello, John! Welcome to Lambda."라는 응답을 받을 수 있어.

💡 Pro Tip:
실제 프로덕션 환경에서는 사용자 입력을 항상 검증하고 살균(sanitize)해야 해. 악의적인 입력으로부터 시스템을 보호하는 것이 중요해!

3.3 외부 서비스와 통합하기: 날씨 정보 가져오기 🌤️

이번에는 좀 더 실용적인 예제를 만들어볼게. 외부 API를 호출해서 날씨 정보를 가져오는 Lambda 함수를 만들어보자. 이를 위해 axios 라이브러리를 사용할 거야.

먼저, Lambda 함수에 axios를 설치해야 해. Lambda 콘솔에서 직접 할 수도 있지만, 로컬에서 개발하고 업로드하는 것이 더 편리할 거야.


const axios = require('axios');

exports.handler = async (event) => {
    let city = "Seoul";
    if (event.queryStringParameters && event.queryStringParameters.city) {
        city = event.queryStringParameters.city;
    }
    
    try {
        const apiKey = 'your_openweathermap_api_key';
        const url = `http://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`;
        
        const response = await axios.get(url);
        const weatherData = response.data;
        
        return {
            statusCode: 200,
            body: JSON.stringify({
                city: weatherData.name,
                temperature: weatherData.main.temp,
                description: weatherData.weather[0].description
            }),
        };
    } catch (error) {
        return {
            statusCode: 500,
            body: JSON.stringify({ error: 'Failed to fetch weather data' }),
        };
    }
};

와! 이 함수는 꽤 많은 일을 하고 있어. 하나씩 살펴보자:

  • 도시 이름을 쿼리 파라미터로 받아와. 기본값은 "Seoul"이야.
  • OpenWeatherMap API를 사용해 해당 도시의 날씨 정보를 가져와.
  • axios.get()을 사용해 HTTP GET 요청을 보내.
  • 받아온 데이터에서 필요한 정보(도시 이름, 온도, 날씨 설명)만 추출해 반환해.
  • 에러 처리도 해. API 호출이 실패하면 500 에러를 반환해.

이 함수를 사용하면, https://your-api-endpoint.com/path?city=London 같은 URL로 요청을 보내 런던의 현재 날씨 정보를 받아올 수 있어!

⚠️ 주의사항:
실제 프로덕션 환경에서는 API 키를 코드에 직접 넣지 마! AWS Secrets Manager나 환경 변수를 사용해 안전하게 관리해야 해.

3.4 데이터베이스와 연동하기: DynamoDB 사용 예제 📊

서버리스 아키텍처에서 데이터를 영구적으로 저장하려면 외부 데이터베이스를 사용해야 해. AWS에서는 DynamoDB라는 NoSQL 데이터베이스를 제공하는데, 이것과 Lambda를 연동해보자.

이 예제에서는 간단한 방명록 시스템을 만들어볼 거야. 사용자가 메시지를 남기면 DynamoDB에 저장하고, 모든 메시지를 조회할 수 있는 기능을 구현할 거야.


const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB.DocumentClient();

exports.handler = async (event) => {
    const { httpMethod, body } = event;
    const TABLE_NAME = 'Guestbook';

    switch (httpMethod) {
        case 'POST':
            const { message, author } = JSON.parse(body);
            const timestamp = new Date().toISOString();
            
            const params = {
                TableName: TABLE_NAME,
                Item: {
                    id: timestamp,
                    message,
                    author,
                    createdAt: timestamp
                }
            };
            
            try {
                await dynamodb.put(params).promise();
                return {
                    statusCode: 200,
                    body: JSON.stringify({ message: 'Message added successfully' })
                };
            } catch (error) {
                console.error('Error adding message:', error);
                return {
                    statusCode: 500,
                    body: JSON.stringify({ error: 'Failed to add message' })
                };
            }
        
        case 'GET':
            try {
                const result = await dynamodb.scan({ TableName: TABLE_NAME }).promise();
                return {
                    statusCode: 200,
                    body: JSON.stringify(result.Items)
                };
            } catch (error) {
                console.error('Error fetching messages:', error);
                return {
                    statusCode: 500,
                    body: JSON.stringify({ error: 'Failed to fetch messages' })
                };
            }
        
        default:
            return {
                statusCode: 400,
                body: JSON.stringify({ error: 'Unsupported HTTP method' })
            };
    }
};

우와, 이 함수는 정말 많은 일을 하고 있어! 하나씩 뜯어보자:

  • AWS.DynamoDB.DocumentClient(): DynamoDB와 상호작용하기 위한 클라이언트를 생성해.
  • switch (httpMethod): HTTP 메서드에 따라 다른 동작을 수행해. POST는 메시지 추가, GET은 메시지 조회야.
  • POST 요청 처리:
    • 요청 본문에서 메시지와 작성자 정보를 추출해.
    • 현재 시간을 ID로 사용해 고유성을 보장해.
    • dynamodb.put()을 사용해 새 항목을 DynamoDB에 추가해.
  • GET 요청 처리:
    • dynamodb.scan()을 사용해 테이블의 모든 항목을 조회해.
    • 조회된 항목들을 JSON 형태로 반환해.
  • 에러 처리: 데이터베이스 작업 중 발생할 수 있는 에러를 잡아서 적절한 응답을 반환해.

이 함수를 사용하면, POST 요청으로 새 메시지를 추가하고 GET 요청으로 모든 메시지를 조회할 수 있어. 재능넷 같은 플랫폼에서 사용자 리뷰 시스템을 구현할 때 이런 방식을 활용할 수 있을 거야!

🔧 개선 포인트:
실제 애플리케이션에서는 페이지네이션, 정렬, 필터링 등의 기능을 추가하는 것이 좋아. 또한, 입력 값 검증과 보안 처리도 꼭 해야 해!

4. Lambda 함수의 고급 기능 🚀

지금까지 기본적인 Lambda 함수 작성법을 배웠어. 이제 좀 더 고급 기능들을 살펴볼 차례야. 이 기능들을 활용하면 더욱 강력하고 효율적인 서버리스 애플리케이션을 만들 수 있을 거야!

4.1 환경 변수 사용하기 🌿

API 키나 데이터베이스 연결 문자열 같은 민감한 정보는 코드에 직접 넣지 않는 것이 좋아. 대신 Lambda 함수의 환경 변수를 사용할 수 있어.


const API_KEY = process.env.API_KEY;
const DATABASE_URL = process.env.DATABASE_URL;

exports.handler = async (event) => {
    // API_KEY와 DATABASE_URL을 사용하는 코드
    ...
};

Lambda 콘솔에서 이러한 환경 변수를 설정할 수 있어. 이렇게 하면 코드를 변경하지 않고도 다양한 환경(개발, 테스트, 프로덕션 등)에서 서로 다른 설정을 사용할 수 있지.

4.2 Lambda 레이어 활용하기 🧅

Lambda 레이어를 사용하면 함수 코드와 종속성을 분리할 수 있어. 이는 코드 크기를 줄이고 배포 속도를 높이는 데 도움이 돼.

예를 들어, axios 같은 라이브러리를 레이어로 만들어 여러 함수에서 공유할 수 있어:


// Lambda 함수 코드
const axios = require('axios');  // 이 라이브러리는 레이어에서 제공됨

exports.handler = async (event) => {
    const response = await axios.get('https://api.example.com/data');
    // 응답 처리
    ...
};

레이어를 사용하면 함수 코드가 더 간결해지고, 라이 브러리 업데이트도 더 쉬워져. 한 번 레이어를 업데이트하면 모든 연결된 함수에 변경사항이 적용되니까.

4.3 VPC 내에서 Lambda 실행하기 🔒

보안이 중요한 리소스(예: 프라이빗 데이터베이스)에 접근해야 할 때는 Lambda 함수를 VPC(Virtual Private Cloud) 내에서 실행할 수 있어. 이렇게 하면 함수가 VPC 내의 리소스에 안전하게 접근할 수 있지.


const mysql = require('mysql');

exports.handler = async (event) => {
    const connection = mysql.createConnection({
        host: 'your-private-db-host',
        user: 'username',
        password: 'password',
        database: 'your_database'
    });
    
    // 데이터베이스 쿼리 실행
    ...
};

이 함수를 VPC 내에서 실행하도록 설정하면, 프라이빗 서브넷에 있는 데이터베이스에 안전하게 접근할 수 있어.

4.4 Step Functions로 워크플로우 만들기 🔗

AWS Step Functions를 사용하면 여러 Lambda 함수를 조합해 복잡한 워크플로우를 만들 수 있어. 예를 들어, 재능넷에서 새로운 재능이 등록되면 다음과 같은 과정을 자동화할 수 있지:

  1. 재능 정보 검증
  2. 이미지 처리 (리사이징, 워터마크 추가 등)
  3. 관련 태그 자동 생성
  4. 검색 인덱스 업데이트
  5. 알림 발송

각 단계를 별도의 Lambda 함수로 구현하고, Step Functions로 이들을 연결하면 돼.