Computer >> 컴퓨터 >  >> 프로그래밍 >> Redis

Upstash Redis로 Next.js 기능 플래그 구현하기: 안전하고 유연한 릴리스 전략

소프트웨어 개발에서 업데이트는 필수적인 과정입니다. 버그를 수정하고, 새로운 기능을 추가하며, 더 안전하고 빠르며 안정적인 제품을 만들기 위해 끊임없이 반복 개선해야 하죠.

하지만 새로운 기능이나 변경 사항을 배포와 묶어두는 것이 항상 좋은 선택은 아닙니다. 정기 배포 일정에 따라 코드를 배포했는데, 마케팅 팀이 아직 해당 기능을 공개할 준비가 되지 않은 경우를 생각해 보세요. 변경 사항이 코드에 하드코딩되어 있다면, 마케팅 팀은 개발자의 도움 없이는 기능을 켜거나 끌 수 없습니다.

기능 플래그(Feature Flag), 또는 피처 토글(Feature Toggle)은 바로 이런 문제를 해결하는 방법입니다. 기능 플래그를 사용하면 배포와 기능 출시를 분리(decouple)할 수 있습니다. 변경 사항을 구현한 후 if문 뒤에 숨겨두는 방식으로, 변수 값이 false면 기존 버전이 동작하고 true면 새 버전이 동작합니다. 만약 이 변수의 값을 마케팅 팀도 접근할 수 있는 데이터베이스에서 가져온다면, 코드 수정 없이도 비개발자가 직접 기능을 제어할 수 있습니다.

Upstash Redis®는 이런 용도에 딱 맞는 데이터베이스입니다. 기능 플래그를 저장할 수 있는 간단한 키-값(Key-Value) 스토어로, 놀라울 정도로 빠르기 때문에 애플리케이션에서 여러 개의 플래그를 확인하더라도 지연 시간(latency)에 큰 영향을 주지 않습니다.

이 튜토리얼에서는 Next.js 앱을 위한 간단한 기능 플래그 메커니즘을 구축하는 방법을 알아보겠습니다. Vercel에 배포하고, 저장소로는 Upstash Redis®를 사용합니다.

사전 준비 사항

이 튜토리얼을 따라 하려면 다음 세 가지가 필요합니다.

  • GitHub 계정
  • Vercel 계정
  • Upstash 계정

또한 NPM을 사용하기 때문에 최신 버전의 Node.js가 설치되어 있어야 합니다.

Next.js 프로젝트 초기화

첫 번째 단계는 새로운 Next.js 프로젝트를 생성하는 것입니다. 아래 명령어를 실행하세요.

$ npx create-next-app upstash-next-feature-flags

Upstash Redis® 연결 설정

다음으로, Next.js 앱과 Upstash Redis® 데이터베이스를 연결해야 합니다. 이를 위해 @upstash/redis 패키지를 설치하고, 간단한 래퍼(wrapper)를 작성합니다.

$ npm i @upstash/redis

이제 upstash-next-feature-flags/lib/featureFlags.js 파일을 생성하고 아래 코드를 입력하세요.

import { Redis } from "@upstash/redis";
 
const { UPSTASH_TOKEN, UPSTASH_URL } = process.env;
const redis = new Redis({
 token: UPSTASH_TOKEN,
 url: UPSTASH_URL,
});
 
export async function flagIsActive(flagName) {
 const flag = await redis.get(flagName);
 return Boolean(flag);
}

API 자격 증명(credentials)은 환경 변수에서 가져오도록 했으며, 실제 값은 튜토리얼 후반부에 설정합니다. flagIsActive 함수는 Redis®에 쿼리를 보낸 뒤 반환된 값을 boolean 타입으로 변환합니다. 이 변환이 필요한 이유는 플래그 값을 Redis에 숫자 형태로 저장하기 때문입니다.

API 라우트 생성

이번에는 두 가지 서로 다른 구현체를 담은 API 라우트를 만들 차례입니다. 기능 플래그를 사용해 두 구현체 사이를 전환할 것입니다.

upstash-next-feature-flags/pages/api/sort-numbers.js 파일을 생성하고 아래 코드를 작성하세요.

import { flagIsActive } from "../../lib/featureFlags";
 
export default async function handler(request, response) {
 let sort = bucketSort;
 
 const newSortingAlgorithm = await flagIsActive("newSortingAlgorithm");
 
 if (newSortingAlgorithm) sort = selectionSort;
 
 const { numbers } = request.body;
 const sorted = sort(numbers);
 
 response.status(200).json({
 numbers: sorted,
 newSortingAlgorithm,
 });
}
 
// 기존 정렬 알고리즘
const bucketSort = (arr, size = 5) => {
 const min = Math.min(...arr);
 const max = Math.max(...arr);
 const buckets = Array.from(
 { length: Math.floor((max - min) / size) + 1 },
 () => [],
 );
 arr.forEach((val) => {
 buckets[Math.floor((val - min) / size)].push(val);
 });
 return buckets.reduce((acc, b) => [...acc, ...b.sort((a, b) => a - b)], []);
};
 
// 새로운 정렬 알고리즘
const selectionSort = (arr) => {
 const a = [...arr];
 for (let i = 0; i < a.length; i++) {
 const min = a
 .slice(i + 1)
 .reduce((acc, val, j) => (val < a[acc] ? j + i + 1 : acc), i);
 if (min !== i) [a[i], a[min]] = [a[min], a[i]];
 }
 return a;
};

이 API 라우트는 간단한 숫자 정렬기입니다. 정렬되지 않은 숫자 배열을 받아, 두 가지 정렬 알고리즘 중 하나를 사용해 정렬합니다.

여기서 두 정렬 알고리즘은 여러분이 애플리케이션에서 "토글"하고 싶은 어떤 변경 사항을 대표합니다. 두 알고리즘을 모두 코드에 넣어두고, Redis의 값을 변경하여 flagIsActive 함수로 둘 사이를 전환하는 방식입니다.

확인하고 싶은 플래그의 이름을 인자로 flagIsActive 함수를 호출합니다. Redis에서 해당 플래그가 1로 설정되어 있으면 함수는 true를 반환하고, 그렇지 않으면 false를 반환합니다.

정렬이 완료되면 API 라우트는 정렬된 배열과 함께 응답합니다.

GitHub으로 프로젝트 푸시

모든 구현이 끝나면, 프로젝트를 GitHub 저장소에 푸시해야 합니다. Vercel의 배포 서비스가 접근할 수 있도록 하기 위함입니다. GitHub 공식 문서에서 저장소 생성 방법을 자세히 설명하고 있으니 참고하세요.

빈 저장소를 생성하면, 방금 만든 로컬 프로젝트를 연결하는 방법이 화면에 자동으로 표시됩니다.

Upstash Redis® 데이터베이스 생성

Upstash 콘솔에서 새로운 데이터베이스를 생성할 수 있습니다. 그림 1은 이 튜토리얼에 적합한 설정 예시를 보여줍니다.

Upstash Redis로 Next.js 기능 플래그 구현하기: 안전하고 유연한 릴리스 전략

그림 1에서 볼 수 있듯이 무료 티어 데이터베이스를 하나 제공하므로, 이 튜토리얼을 진행하는 데 비용은 전혀 들지 않습니다.

나중에 데이터베이스 자격 증명이 필요하니, Upstash 콘솔이 열려 있는 브라우저 탭은 닫지 말고 유지하세요.

GitHub 저장소와 Vercel 연동

GitHub 계정으로 Vercel에 로그인했다면, 클릭 몇 번만으로 GitHub 저장소를 추가할 수 있습니다. 그림 2는 Vercel 대시보드의 왼쪽 상단을 보여주는데, 여기서 "Add New…" 버튼을 클릭하고 "Project"를 선택하세요.

Upstash Redis로 Next.js 기능 플래그 구현하기: 안전하고 유연한 릴리스 전략

"Continue with GitHub"을 선택한 뒤, 이전 단계에서 생성한 저장소에 해당하는 "import" 버튼 중 하나를 누릅니다.

Upstash Redis로 Next.js 기능 플래그 구현하기: 안전하고 유연한 릴리스 전략

마지막으로 프로젝트 구성 단계가 나타나면, 여기서 환경 변수를 추가해야 합니다. 나머지 설정은 기본값을 그대로 사용해도 됩니다. 그림 4는 환경 변수를 입력해야 하는 위치를 보여줍니다.

Upstash Redis로 Next.js 기능 플래그 구현하기: 안전하고 유연한 릴리스 전략

환경 변수 이름은 UPSTASH_TOKENUPSTASH_URL이며, 두 값 모두 앞서 데이터베이스를 생성한 Upstash 콘솔에서 찾을 수 있습니다. 그림 5는 필요한 값을 복사할 때 사용하는 버튼 위치를 보여줍니다.

Upstash Redis로 Next.js 기능 플래그 구현하기: 안전하고 유연한 릴리스 전략

큰 "Deploy" 버튼을 누르면, Vercel이 GitHub 저장소를 클론(clone)한 뒤 자체 인프라에 배포합니다.

Redis로 구현체 전환하기

이제 API 엔드포인트를 호출할 준비가 되었으며, Redis에서 값을 하나 설정하는 것만으로 두 구현체 사이를 전환할 수 있습니다. 가장 간단한 방법은 Upstash 콘솔에 내장된 Redis CLI를 활용하는 것입니다.

그림 6은 CLI 위치와 값을 설정하는 데 필요한 명령어를 보여줍니다.

set newSortingAlgorithm 1

Upstash Redis로 Next.js 기능 플래그 구현하기: 안전하고 유연한 릴리스 전략

newSortingAlgorithm 키를 삭제하거나 0으로 설정하면 플래그가 다시 false로 돌아가고, API 라우트는 기존 알고리즘을 사용하게 됩니다.

API 라우트 호출하기

마지막으로 남은 것은 API 라우트를 실제로 호출하는 것입니다. POST 요청으로 JSON 객체를 라우트에 전송해야 하는데, 가장 손쉬운 방법은 cURL을 사용하는 것입니다.

아래 명령어가 JSON 객체를 전송합니다.

$ curl --header "Content-Type: application/json" \
 --request POST \
 --data '{"numbers":[1, 100, 10, 1000, 100000, 10000]}' \
 API_ROUTE_URL

API_ROUTE_URL에는 올바른 URL을 입력해야 합니다. 그림 7처럼 Vercel 대시보드의 프로젝트 페이지에서 "Domains" 항목을 찾을 수 있습니다.

Upstash Redis로 Next.js 기능 플래그 구현하기: 안전하고 유연한 릴리스 전략

응답으로는 정렬된 numbers와, 어떤 알고리즘이 사용되었는지 알려주는 newSortingAlgorithm 필드가 포함된 JSON 객체가 반환됩니다.

마무리

기능 플래그는 배포와 기능 출시를 분리하는 훌륭한 방법입니다. 코드에 하드코딩된 변경 사항을 데이터베이스에서 간단한 값을 설정하는 작업으로 바꿔줌으로써, 팀에 더 큰 유연성을 부여하고 비개발자에게도 강력한 통제 권한을 제공합니다.

Upstash Redis처럼 빠른 데이터베이스를 활용하면 성능 저하 없이도 애플리케이션에 기능 플래그 패턴을 손쉽게 도입할 수 있습니다. Upstash Redis는 밀리초 미만의 응답 속도를 자랑하며, 필요하다면 FaaS(FaaS 함수) 근처에 리전을 배치할 수도 있습니다. 게다가 종량제(pricing on demand) 요금제를 사용하기 때문에 사용하지 않는 리소스에 대해 비용을 지불할 걱정도 없습니다.