AWS Lambda + API GateWay 를 활용한 프록시 서버 구축하기 (feat. CORS)

@Ryan· June 02, 2024 · 5 min read

서버 없이 클라이언트만으로 구축되어 있는 토이프로젝트를 진행하고 있습니다.

이 때, 네이버의 백과사전 API 기능을 활용하고 있는데, 서버 없이 클라이언트에서 바로 API 콜을 하게 되면 악명 높은 CORS 에러를 마주하게 됩니다.

잘 알려진 cors-anywhere와 같은 기능을 사용할 수도 있지만, 이는 배포 환경에서는 동작하지 않고 근본적인 해결책이 아니기 때문에 AWS Lambda를 활용한 서버리스 프록시 함수를 구현하기로 하였고, 해결 과정에 대해 매우 간단히 소개하려 합니다.

_이 글에서, naver api 사용에 대한 애플리케이션 설정은 완료했다고 가정합니다. 만약 설정하지 않았다면 본인이 사용할 기능에 대한 설정을 완료해주세요. _

AWS Lambda 설정

1. AWS 콘솔에서, AWS Lambda를 찾아갑니다.

2. 함수 탭으로 이동 후 함수 생성 버튼을 클릭합니다.

3. 함수 이름을 적고 생성을 누릅니다.

4. 함수가 생성되면, 트리거 추가를 누릅니다.

5. API 게이트웨이 생성을 선택하고,

의도: 새 API 생성,

API 유형: REST API,

보안: 열기 를 선택합니다.

6. API 엔드포인트가 생성됩니다.

7. 환경 변수를 설정합니다.

7-1. 구성 탭의 환경변수 탭으로 들어갑니다 7-2. 편집을 클릭합니다. 7-3. 네이버 개발자 센터에서 받아온 Client Id, Client Secret 값을 적어줍니다.

8. 코드 탭으로 돌아가 서버 코드를 작성합니다.

import https from "https"

export const handler = async event => {
  const key = encodeURI(event.queryStringParameters.key)

  const options = {
    protocol: "https:",
    hostname: "openapi.naver.com",
    path: `/v1/search/encyc.json?query=${key}`,
    headers: {
      "X-Naver-Client-Id": process.env.NAVER_CLIENT_ID,
      "X-Naver-Client-Secret": process.env.NAVER_CLIENT_SECRET,
    },
  }

  try {
    const result = await fetchNaverData(options)
    return formatResponse(200, result)
  } catch (error) {
    return formatResponse(500, { error: error.message })
  }
}

const fetchNaverData = options => {
  return new Promise((resolve, reject) => {
    https
      .get(options, response => {
        let result = ""

        response.on("data", chunk => {
          result += chunk
        })

        response.on("end", () => {
          try {
            resolve(JSON.parse(result))
          } catch (error) {
            reject(new Error("Failed to parse response"))
          }
        })

        response.on("error", error => {
          reject(new Error(error.message))
        })
      })
      .on("error", error => {
        reject(new Error(error.message))
      })
  })
}

const formatResponse = (statusCode, body) => {
  return {
    headers: {
      "Access-Control-Allow-Headers": "Content-Type",
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "OPTIONS,POST,GET",
    },
    statusCode: statusCode,
    body: JSON.stringify(body),
  }
}

9. Deploy 버튼을 클릭합니다.

10. API 게이트웨이를 클릭합니다.

11. API 를 배포해줍니다.

12. 클라이언트에서 api를 연결해줍니다.

// 예시 코드
import axios from "axios"

export const client = axios.create({
  // AWS Lambda의 API 엔드포인트
  baseURL:
    "https://xw1t98pkkf.execute-api.ap-northeast-2.amazonaws.com/default/",
})

export const searchWord = async (word: string) => {
  try {
    const response = await client.get("/searchNaverDictionary", {
      params: {
        key: word,
      },
    })
    return response.data
  } catch (error) {
    console.error(error)
  }
}

서버 에러 확인

람다 함수 호출 시 서버 에러가 의심되는 경우, CloudWatch를 활용하여 로그를 확인해 볼 수 있습니다.


타임아웃 세팅

간혹, 외부 api요청의 프록시 서버로 람다를 활용할 경우 외부 api 요청이 생각보다 길어지는 경우가 있습니다. 제 프로젝트의 경우도 간혹 이런 오류가 발생했는데, 여러 번 테스트해 본 결과 응답 시간이 3초가 지나면 자동으로 502 에러를 발생시키는 것으로 생각했습니다.

람다의 구성을 확인 해보면, 기본 값으로 3초의 타임아웃이 걸려있는 것을 확인할 수 있었는데요, 제 가설이 맞다고 생각을 했고, 타임아웃을 5초로 변경하였습니다.

변경 후, 3초 이상의 시간이 걸려도 더 이상 해당 버그가 발생하지 않는 것을 확인하였습니다.

이상으로 간단하게 서버리스 함수를 구현하는 법을 알아보았습니다. 아마 토이프로젝트에서 공공API 등을 활용할 때 자주 사용하게 될 것 같읍니다.

@Ryan
일단 해보자
© ryanbae.dev, Built with Gatsby