3 분 소요

개요

  • 사이트 / 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/govaluatekey 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 지원


관련 포스트