Feature Flagging: GO Feature Flag
개요
- 사이트 / GitHub
- 오픈소스 Feature Flag 솔루션 — 외부 서비스 없이 파일(YAML/TOML/JSON) 기반으로 플래그 관리
- OpenFeature 표준을 만족하는 공식 Provider
- 두 가지 사용 방식:
- OpenFeature SDK: OpenFeature 표준 API 사용 (멀티 언어 지원)
- GO module: Go 전용 네이티브 API 사용
| 특징 | 설명 |
|---|---|
| 파일 기반 플래그 정의 | YAML/TOML/JSON — 별도 DB 불필요 |
| 다양한 저장소 지원 | S3, GCS, GitHub, HTTP, K8s ConfigMap, 로컬 파일 등 |
| 타겟팅 & 퍼센티지 분배 | 사용자 속성 기반 규칙 + 비율 기반 점진적 배포 |
| 배포 전략 | Canary, A/B Testing, Progressive, Scheduled Rollout |
| 사용 데이터 수집 | 플래그 평가 이벤트를 Parquet/JSON/CSV로 내보내기 |
| OpenFeature 준수 | 표준 SDK로 언어 무관하게 동일 플래그 파일 참조 |
| Relay Proxy | REST API 서버로 배포해 Go 외 언어에서도 사용 가능 |
플래그 타입
GO Feature Flag는 5가지 값 타입을 지원:
| 타입 | 평가 함수 (GO module) | OpenFeature SDK 메서드 |
|---|---|---|
| Boolean | BoolVariation |
BooleanValue |
| String | StringVariation |
StringValue |
| Integer | IntVariation |
IntValue |
| Float | Float64Variation |
FloatValue |
| JSON | JSONVariation |
ObjectValue |
Feature Flag 구성
저장소 (Retrievers)
| 저장소 | 설명 |
|---|---|
| 로컬 파일 | fileretriever.Retriever{Path: "..."} |
| HTTP/HTTPS | URL에서 주기적으로 플래그 파일 다운로드 |
| AWS S3 | S3 버킷의 파일 참조 |
| Google Cloud Storage | GCS 버킷의 파일 참조 |
| GitHub / GitLab | 저장소의 파일 직접 참조 |
| K8s ConfigMap | 클러스터 내 ConfigMap 사용 |
PollingInterval로 변경 감지 주기 설정 (기본 60초)- 여러 Retriever를 조합해 플래그 파일을 병합 가능
플래그 포맷 (YAML)
# This is your configuration for your first flag
first-flag:
variations: # 가능한 반환값 정의
A: false
B: true
targeting: # 특정 사용자를 다른 variation으로 타겟팅
- query: key eq "random-key"
percentage:
A: 0
B: 100
defaultRule: # 타겟팅 미매칭 시 기본 규칙
variation: A
metadata:
issue_link: https://github.com/thomaspoignant/go-feature-flag/issues/XXX
description: this is my first feature flag
variations: 플래그가 반환할 수 있는 모든 값 정의targeting: 조건(query)에 맞는 사용자에게 적용할 규칙query문법: nikita/govaluate —key eq "value",attr gt 5등percentage: 조건 매칭 사용자 중 비율 분배 가능
defaultRule: 타겟팅 조건에 매칭되지 않는 경우 적용disable:true로 설정 시 플래그 비활성화 — 항상 defaultRule 반환
배포 전략 (Rollout)
| 전략 | 설명 |
|---|---|
| Canary Release | 일부 사용자에게만 새 변형 노출 (퍼센티지) |
| Experimentation (A/B) | 기간 지정 A/B 테스트 — 시작/종료 시간 설정 |
| Progressive Rollout | 시간에 따라 비율을 점진적으로 증가 |
| Scheduled Rollout | 특정 시각에 플래그 상태를 자동으로 변경 |
사용 데이터 수집 (Data Export)
- 플래그 평가 이벤트를 외부 저장소로 내보내기
- 포맷:
parquet,JSON,CSV - 지원 Exporter: 로컬 파일, S3, GCS, BigQuery, SQS, Kafka, Webhook
Relay Proxy
- GO Feature Flag를 REST API 서버로 배포 — Go 외 언어(Java, Python, Node.js 등)에서도 사용 가능
- OpenFeature Provider 방식으로 각 언어 SDK와 연동
-
Docker로 간편하게 배포:
docker run -d \ -p 1031:1031 \ -v $(pwd)/flags.yaml:/flags.yaml \ thomaspoignant/go-feature-flag-relay-proxy:latest - 엔드포인트:
POST /ofrep/v1/evaluate/flags/{flagKey} - 플래그 변경 시 Relay Proxy가 자동 폴링으로 갱신
예제
flags.yaml
flag-1:
variations:
Variation_1: true
Variation_2: false
targeting:
- name: Rule 1
query: key eq "key-1"
variation: Variation_1
defaultRule:
variation: Variation_2
OpenFeature SDK (Go)
package main
import (
"context"
"strconv"
"time"
gofeatureflag "github.com/open-feature/go-sdk-contrib/providers/go-feature-flag/pkg"
"github.com/open-feature/go-sdk/openfeature"
ffclient "github.com/thomaspoignant/go-feature-flag"
"github.com/thomaspoignant/go-feature-flag/retriever/fileretriever"
)
func getProvider() (openfeature.FeatureProvider, error) {
options := gofeatureflag.ProviderOptions{
GOFeatureFlagConfig: &ffclient.Config{
PollingInterval: 10 * time.Second,
Context: context.Background(),
Retriever: &fileretriever.Retriever{
Path: "./flags.yaml",
},
},
}
return gofeatureflag.NewProvider(options)
}
func main() {
if provider, err := getProvider(); err != nil {
panic(err)
} else if err := openfeature.SetProvider(provider); err != nil {
panic(err)
}
client := openfeature.NewClient("application name")
defer openfeature.Shutdown()
for i := 0; i < 5; i++ {
key := "key-" + strconv.Itoa(i)
evaluationContext := openfeature.NewEvaluationContext(key, nil)
if value, err := client.BooleanValue(context.TODO(), "flag-1", true, evaluationContext); err != nil {
panic(err)
} else {
println(key, "-", value)
}
}
}
실행 결과:
key-0 - false
key-1 - true
key-2 - false
key-3 - false
key-4 - false
GO module (네이티브 API + Data Export)
package main
import (
"context"
"strconv"
"time"
ffclient "github.com/thomaspoignant/go-feature-flag"
"github.com/thomaspoignant/go-feature-flag/exporter/fileexporter"
"github.com/thomaspoignant/go-feature-flag/ffcontext"
"github.com/thomaspoignant/go-feature-flag/retriever/fileretriever"
)
func main() {
if err := ffclient.Init(ffclient.Config{
PollingInterval: 10 * time.Second,
Context: context.Background(),
Retriever: &fileretriever.Retriever{
Path: "./flags.yaml",
},
DataExporter: ffclient.DataExporter{
FlushInterval: 10 * time.Second,
MaxEventInMemory: 100,
Exporter: &fileexporter.Exporter{
OutputDir: "./",
Format: "csv",
Filename: "flag-variation--.",
CsvTemplate: ";;;;;;;;\n",
},
},
}); err != nil {
panic(err)
}
defer ffclient.Close()
for i := 0; i < 5; i++ {
key := "key-" + strconv.Itoa(i)
evaluationContext := ffcontext.NewEvaluationContext(key)
if value, err := ffclient.BoolVariation("flag-1", evaluationContext, true); err != nil {
panic(err)
} else {
println(key, "-", value)
}
}
}
실행 결과 — 표준 출력:
key-0 - false
key-1 - true
key-2 - false
key-3 - false
key-4 - false
생성된 flag-variation-localhost.localdomain-1712026275.csv:
feature;user;key-0;1712026275;flag-1;Variation_2;false;false;SERVER
feature;user;key-1;1712026275;flag-1;Variation_1;true;false;SERVER
feature;user;key-2;1712026275;flag-1;Variation_2;false;false;SERVER
feature;user;key-3;1712026275;flag-1;Variation_2;false;false;SERVER
feature;user;key-4;1712026275;flag-1;Variation_2;false;false;SERVER
유사 도구 비교
| 도구 | 방식 | 특징 |
|---|---|---|
| GO Feature Flag | 파일 기반 | 외부 서비스 불필요, OpenFeature 지원, 경량 |
| LaunchDarkly | SaaS | 완성도 높은 UI, 실시간 업데이트, 유료 |
| Flagsmith | 오픈소스 SaaS | 셀프호스팅 가능, 원격 설정(Remote Config) 포함 |
| Unleash | 오픈소스 | 셀프호스팅, 풍부한 전략, 별도 서버 필요 |
| Flipt | 오픈소스 | gRPC + REST API, 경량 서버, OpenFeature 지원 |