개발/aws

[AWS] 중간 서버 세팅하기

iris3455 2024. 12. 14. 15:13

일단 굴린 후에 생각한다.

 

저번에 테스트 환경과 프로덕션 환경을 분리하면서 한가지 걸림돌이 있었는데 바로 앱 심사 과정이었다. 앱 심사시의 백엔드 환경이 테스트 환경이 되어야 된다는 것이다. 현재 사용하고 있는 유저들은 prod 사용 / 앱 심사시에는 변경 사항이 있는 test 사용. 이런식으로 되어야 환경 분리가 완벽히 된다. supabase_url과 supabase_api_key를 각각 환경에 맞게 가져와야 했고 물론 환경 변수 파일에 따로 저장해서 버전에 따라 다르게 가져올수도 있었지만 좀더 확실한 관리를 위해 중간 서버를 세팅하기로 하였다.

굴러가기만 하면 되잖아

 

 

1. AWS 선택

내가 고안한 방식은 api 호출을 통해 key를 가져올때 현재 클라이언트의 버전을 확인하고 만약 출시가 안된 버전이라면 (심사시에만 적용) test project의 key를 가져오고 출시가 된 버전이라면 prod project 의 key를 가져오게 하는 것이었다.

ex)
latestVersion = '1.0.0'
-> test supabase의  supabase_url과 supabase_api_key를 반환
testVersion = '1.0.1' 
-> prod supabase의  supabase_url과 supabase_api_key를 반환

앱 스토어에 버전 업데이트후에는 
latestVersion  -> '1.0.1'로 변경
testVersion -> '1.0.2'로 변경

 

고민을 하다AWS Lambda와 API Gateway를 이용해서 간단한 Serverless API 환경을 구축하기로 하였다. 이유는 일단 가격적인 부분에서 메리트가 있었고 추후에 aws s3를 사용할 예정이라서 이왕이면 같은 플랫폼이 관리 부분에서 편하지 않을까 싶어 aws로 결정했다.

 

AWS Lambda란?

서버 관리 주체가 aws로 백엔드를 작은 함수 단위로 쪼개어 aws 내부의 서버에 업로드 하고 요청 발생시 요청에 맞는 람다 함수를 실행시켜준다.
서버를 프로비저닝하거나 관리할 필요 없이 코드를 실행하게 해주면서 모든 유형의 애플리케이션이나 백엔드 서비스에 대한 코드를 별도의 관리 없이 실행 가능하다. 사용자는 람다에다가 원하는 함수를 작성하고, 필요할 때 그 함수를 사용할 수 있다.

비용
AWS Lambda의 비용은 요청 수( Lambda 함수가 호출될 때마다 ), 실행 시간( 함수가 실행되기 시작해서 반환되거나 종료될 때까지의 시간 ), 메모리 크기( 함수에 할당된 메모리 크기 ) 에 의해 결정된다.  월 1백만 회의 요청과 400,000 GB-초의 컴퓨팅 시간과 512MB의 메모리까지는 추가 비용 없고 그 이후부터는 사용한 만큼 비용을 추가로 내야 한다.

 

2. Lamda 함수 생성

 

1. Lambda 함수를 만들어야 한다. 일단 가장 범용적으로 사용되는 자바스크립트 언어로 함수를 짠다.

쿼리 파라미터로 appversion을 가져온 후 버전을 비교하여 body에 supabase url과 supabase api key를 반환한다. 추가로 나는 업데이트 체크를 위해 appVersion도 같이 반환하였는데 이를 통해서 클라이언트의 appversion과 최신 appversion이 일치하지않으면 업데이트 팝업을 띄우게 하였다.

export const handler = async (event) => {
  try {
    const SUPABASE_TEST_URL = process.env.SUPABASE_TEST_URL;
    const SUPABASE_TEST_API_KEY = process.env.SUPABASE_TEST_API_KEY;
    const SUPABASE_URL = process.env.SUPABASE_URL;
    const SUPABASE_API_KEY = process.env.SUPABASE_API_KEY;
    const LATEST_APP_VERSION = '1.1.0';
    const TEST_APP_VERSION = '1.1.1';
 
    const appVersion = event.queryStringParameters?.appVersion || '';

    let supabaseUrl;
    let supabaseApiKey;
    let currentAppVersion;
    
    if (appVersion === TEST_APP_VERSION) {
      supabaseUrl = SUPABASE_TEST_URL;
      supabaseApiKey = SUPABASE_TEST_API_KEY;
      currentAppVersion = TEST_APP_VERSION;
    } else {
       supabaseUrl = SUPABASE_URL;
       supabaseApiKey = SUPABASE_API_KEY;
       currentAppVersion = LATEST_APP_VERSION;
    }

    const response = {
      statusCode: 200,
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        supabaseUrl: supabaseUrl,
        supabaseApiKey: supabaseApiKey,
        latestAppVersion: currentAppVersion,
      }),
    };

    return response;
  } catch (error) {
    console.error("Error fetching environment variables:", error);

    return {
      statusCode: 500,
      body: JSON.stringify({ error: "Internal Server Error" }),
    };
  }
};

 

 

2.  환경 변수 설정

'구성 -> 환경변수' 에 들어가 반환할 test와 prod의  환경변수를 지정해준다. 

 

3. Lambda 함수 배포를 해준 후 트리거에 API Gateway를 추가해주어야 한다.

 

 

3. API 구축

 

1. Api Gateway로 이동후 REST API를 생성해준다.

 

 

2. 이제 해당 API의 매서드를 생성해주어야하는데  GET 메서드로 유형을 선택해 준 후 위에서 생성한 람다 함수를 선택하여 연결하여 준다. 

 

여기서 중요한 점은 Lambda 프록시 통합 부분이다.

Lambda 프록시 통합이란?

API Gateway와 Lambda 함수 간의 직접적인 연결 방식이다. API Gateway는 HTTP 요청의 전체 내용을 이벤트 객체로 Lambda 함수에 전달하는데 Lambda 함수는 응답을 API Gateway에 전달하고, API Gateway는 이를 클라이언트에 반환한다.

프록시 통합시
-> Lambda 함수가 HTTP 요청 처리의 모든 부분을 제어.
요청 메서드(GET, POST 등), 헤더, 쿼리 매개변수, 경로 매개변수 등을 포함한 요청 데이터가 Lambda로 그대로 전달

응답구조
{
  "statusCode": 200,
  "headers": { "Content-Type": "application/json" },
  "body": "stringified JSON or plain text"
}​


비프록시 통합
비프록시 통합에서는 API Gateway가 요청을 받아 이를 특정 형식으로 Lambda 함수에 전달하고, 응답 역시 API Gateway가 미리 정의된 방식으로 처리한다. 따라서 이 방식에서는 요청 및 응답 매핑 템플릿이 필요하다.
(매핑 템플릿이 없으면 요청 데이터가 올바르게 전달되지 않거나 응답이 제대로 반환되지 않을 수 있음.)

매핑 템플릿
{
  "queryStringParameters": {
    "appVersion": "$input.params('appVersion')"
  }
}​

 

const appVersion = event.queryStringParameters?.appVersion || '';

나는 Lambda 함수를 생성할때 이 코드를 사용하였기때문에 Lambda 함수가 요청의 쿼리 매개변수를 직접 읽도록 되어있다. 따라서 프록시 통합을 체크해야 한다.

 

 

3. 또한 보안 강화를 위해 api key를 통하여 api 요청을 할 수 있게 해주기로 하였다. 생성한 메서드를 API 키가 필요함으로 수정한다. api 호출시 header에 api key를 요청하여 연결해주지 않으면 아래 이미지처럼 접근이 금지될 것이다. 
(그러니 추후에 발급받을 api key는 절대 노출되어선 안된다.)

 

 

4. API 키로 들어가서 API KEY를 생성해준다.

 

 

5. 여기서 끝나는게 아니라 사용량 계획에 추가를 해주어야 한다. 사용량 계획으로 들어가서 적당한 요율, 버스트,할당량을 맞춰서 plan을 하나 생성해주고 연결된 스테이지에 스테이지를 추가해주어야한다. 위에서 생성한 API를 추가준 후 마지막으로 생성한 API 를 배포하면 api 구축은 끝이다.

 

 

4. API 호출

1. 아래 코드와 같이 클라이언트에서 해당 API 를 호출을 해주면 끝이다.
(api key는 따로 암호화를 해서 불러와야 한다.)

static Future<Map<String, String>> getConfigs(
      String clientAppVersion, String apiKey) async {
    try {
      final Uri url = Uri.parse(
          "https://aws-api-url?appVersion=$clientAppVersion"); //호출할 api url
      final response = await http.get(
        url,
        headers: {
          "x-api-key": "$apiKey",
        },
      );
      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);

        Map<String, String> configs = {
          'supabaseUrl': data['supabaseUrl'],
          'supabaseApiKey': data['supabaseApiKey'],
          'latestAppVersion': data['latestAppVersion'],
        };
        return configs;
      } else {
        throw Exception('Failed to fetch Supabase config');
      }
    } catch (e, stackTrace) {
      throw Exception('Failed to fetch Supabase config');
    }
  }

 

 

 

 

끝으로

사실 하면서도 이게 맞는건가 하는 생각이 많이 든다. 세상에 정답은 없다지만 정답과 가까운 답과 오답은 존재하므로.. 내가 내린 답이 오답만 하니었으면 하는 마음과, 점점 나아지기를 바라는 마음 뿐이다. 사실 오답이어도 괜찮다. 다시 하면 되고 그걸 통해서 배움이 있고 그걸 통해서 더 나아지다 보면 정답에 가까워지지 않을까.

 

'개발 > aws' 카테고리의 다른 글

[AWS] CloudFront, ACM 사용하여 S3 버킷 정책 강화하기  (1) 2024.12.23
[AWS] S3  (2) 2024.12.20