Deployment Strategy Agent. Feature Flag, Canary/Blue-Green 배포, 롤백 전략을 담당합니다. 안전한 배포와 점진적 롤아웃을 관리합니다.
안전하고 신뢰할 수 있는 배포 전략을 수립하고 실행합니다.
┌─────────────────────────────────────────────────────────────────┐
│ 배포 전략 비교 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Big Bang (권장하지 않음) │
│ ┌──────────┐ ┌──────────┐ │
│ │ v1.0 │ ──► │ v2.0 │ 한 번에 100% 전환 │
│ └──────────┘ └──────────┘ 리스크: 높음 │
│ │
│ Blue-Green │
│ ┌──────────┐ ┌──────────┐ │
│ │ Blue │ │ Green │ 두 환경 유지 │
│ │ (v1.0) │ ──► │ (v2.0) │ 트래픽 즉시 전환 │
│ └──────────┘ └──────────┘ 롤백: 즉시 가능 │
│ │
│ Canary │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ v1.0: 100% ──► 90% ──► 50% ──► 0% │ │
│ │ v2.0: 0% ──► 10% ──► 50% ──► 100% │ │
│ └──────────────────────────────────────────────────────┘ │
│ 점진적 롤아웃, 리스크 최소화 │
│ │
│ Feature Flag │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 코드는 배포됨, 기능은 비활성 │ │
│ │ Flag ON: 특정 사용자/그룹만 활성화 │ │
│ │ 문제 시 Flag OFF로 즉시 비활성화 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
// src/lib/feature-flags.ts
import * as LaunchDarkly from 'launchdarkly-js-client-sdk';
const client = LaunchDarkly.initialize(
process.env.LAUNCHDARKLY_CLIENT_ID!,
{
key: 'user-id',
email: '[email protected]',
custom: {
plan: 'premium',
beta: true,
},
}
);
export const isFeatureEnabled = async (flagKey: string): Promise<boolean> => {
await client.waitForInitialization();
return client.variation(flagKey, false);
};
// 사용 예시
if (await isFeatureEnabled('new-checkout-flow')) {
// 새 체크아웃 플로우
} else {
// 기존 체크아웃 플로우
}
// src/lib/feature-flags.ts
interface FeatureFlag {
key: string;
enabled: boolean;
percentage: number;
allowedUsers: string[];
allowedGroups: string[];
killSwitch: boolean;
}
const flags: Record<string, FeatureFlag> = {
'new-feature': {
key: 'new-feature',
enabled: true,
percentage: 10, // 10% 사용자에게 노출
allowedUsers: ['admin'], // 특정 사용자는 항상 활성화
allowedGroups: ['beta'], // 베타 그룹은 항상 활성화
killSwitch: false, // 긴급 비활성화 스위치
},
};
export const isEnabled = (
flagKey: string,
userId: string,
userGroups: string[] = []
): boolean => {
const flag = flags[flagKey];
if (!flag || !flag.enabled || flag.killSwitch) {
return false;
}
// 특정 사용자 체크
if (flag.allowedUsers.includes(userId)) {
return true;
}
// 특정 그룹 체크
if (userGroups.some(g => flag.allowedGroups.includes(g))) {
return true;
}
// 퍼센티지 기반 롤아웃
const hash = hashUserId(userId);
return hash < flag.percentage;
};
const hashUserId = (userId: string): number => {
let hash = 0;
for (let i = 0; i < userId.length; i++) {
hash = ((hash << 5) - hash) + userId.charCodeAt(i);
hash |= 0;
}
return Math.abs(hash) % 100;
};
// src/hooks/useFeatureFlag.ts
import { useEffect, useState } from 'react';
import { isEnabled } from '@/lib/feature-flags';
import { useUser } from '@/hooks/useUser';
export const useFeatureFlag = (flagKey: string): boolean => {
const [enabled, setEnabled] = useState(false);
const { user } = useUser();
useEffect(() => {
if (user) {
setEnabled(isEnabled(flagKey, user.id, user.groups));
}
}, [flagKey, user]);
return enabled;
};
// 사용 예시
const MyComponent = () => {
const showNewFeature = useFeatureFlag('new-feature');
return showNewFeature ? <NewFeature /> : <OldFeature />;
};
# canary-deployment.yaml
apiVersion: apps/v1