Feature Flag 패턴이란?

2026. 6. 13. 23:22·Etc

Feature Flag는 왜 필요할까?

서비스를 운영하다 보면 코드를 배포하는 일과 기능을 사용자에게 공개하는 일을 항상 같은 시점에 하고 싶지는 않다. 기능 개발은 끝났지만 아직 전체 사용자에게 열기에는 부담스러운 경우가 있다. 내부 계정이나 특정 조직에만 먼저 열어보고 싶은 기능도 있다. 반대로 이미 배포된 기능에서 문제가 생겼을 때, 코드를 다시 배포하지 않고 기능만 빠르게 끄고 싶은 경우도 있다.

이런 문제를 해결하기 위해 사용하는 방법 중 하나가 Feature Flag다. Feature Flag는 배포된 코드의 동작을 런타임에 바꿀 수 있게 해주는 방법이다. 

Feature Flag의 핵심은 “코드를 다시 배포하지 않고 동작을 바꿀 수 있다”는 점이다. Feature Flag를 사용하면 기능 공개 시점을 배포 시점과 분리할 수 있고, 특정 사용자에게만 기능을 열 수도 있고, 문제가 생긴 기능을 빠르게 끌 수도 있다.

Feature Flag 사용 사례

운영 중 시스템 동작을 바꾸는 스위치

배포와 릴리즈의 분리

Feature Flag의 가장 큰 특징은 배포와 릴리즈를 분리할 수 있다는 점이다.

(배포: 코드가 운영 환경에 올라가는 것 / 릴리즈: 사용자가 그 기능을 실제로 사용하는 것)

일반적인 상황에서는 이 둘이 거의 동시에 일어난다. 코드를 배포하면 기능도 함께 열린다. 하지만 Feature Flag를 사용하면 코드는 먼저 배포하고, 기능 노출은 나중에 결정할 수 있다.

 

당근에서도 Feature Flag 패턴을 활용하여 배포와 릴리즈 시점을 분리하여 배포를 진행한다. (관련 링크)

내가 작업한 코드가 mainline에 들어갔지만 아직 테스트 중이라 사용자에게 노출되면 안 되는 상황이다. 하지만 이 상태에서 다른 동료가 배포를 진행하면 mainline에 있는 내 코드도 함께 배포된다. 때문에 테스트가 끝날 때까지 팀원이 배포를 기다리거나, 이미 merge한 코드를 revert해야 한다. 이러한 방식들은 자주 배포하는 팀에는 부담스러운 방식일 수 밖에 없다.

그렇다면 코드는 mainline에 포함하고 배포하되, 사용자에게 노출되지 않도록 설정할 수는 없을까?

fun getHome(user: User): HomeResponse {
    if (featureFlag.isEnabled("home.new-layout", user)) {
    	return newHomeService.getHome(user) 
    } 
    return legacyHomeService.getHome(user) 
}

플래그가 꺼져 있으면 기존 홈 화면만 동작한다. 신규 홈 화면 코드는 운영 환경에 올라가 있지만 사용자에게는 보이지 않는다.

이처럼 Feature Flag 방식을 활용하면 배포와 릴리즈를 분리할 수 있기 때문에 피쳐들을 배포 시점까지 오랫동안 브랜치에 쌓아두지 않게 해준다. 변경사항을 작게 나눠 mainline에 계속 통합할 수 있고, 실제 공개 여부는 나중에 결정할 수 있다.

즉, Feature Flag의 가치는 기능을 배포하지 않는 것이 아니라, 배포는 하되 아직 릴리즈하지 않는 상태를 만들 수 있다는 것이다.

장애 대응을 위한 스위치로 사용

운영 중 특정 기능에서 문제가 생겼을 때, 이를 롤백하고 코드를 수정하고 다시 배포하는 방식은 느릴 수 있다. 이미 우회 경로가 준비되어 있다면 플래그를 끄는 것만으로 문제 기능을 빠르게 비활성화할 수 있다. 예를 들어 홈 화면의 추천 영역이 있다고 해보자. 추천 서버가 불안정해져서 홈 화면 전체 응답 시간이 느려지고 있다면, 추천 영역만 잠시 끄는 선택을 할 수 있다.

fun getHome(user: User): HomeResponse {
    val banners = bannerService.getBanners(user)
    val recommendations =
    if (featureFlag.isEnabled("home.recommendation.enabled")) {
    	recommendationService.getRecommendations(user)
    } else {
    	emptyList()
    }
    
    return HomeResponse(
        banners = banners,
        recommendations = recommendations,
    )
}

이런 플래그는 신규 기능 릴리즈용 플래그와 성격이 다르다. 이러한 성격을 띄는 Feature Flag를 킬 스위치 라고 부른다. 킬 스위치는 릴리즈용 플래그와 달리 장기적으로 유지하기도 한다. 장애 상황에서 특정 기능을 빠르게 끄기 위한 운영 장치이기 때문이다.

다만 킬 스위치를 만들 때는 반드시 fallback이 있어야 한다. Feature Flag는 기능을 꺼주는 스위치일 뿐이다. 기능을 껐을 때 시스템이 안전하게 동작하도록 만드는 것은 별도의 설계 문제다. 스위치만 있고 우회 경로가 없다면, 그것은 킬 스위치가 아니라 또 다른 장애 포인트가 될 수 있다.

때문에 추천 영역을 끄면 화면이 어떻게 보여야 하는지, 외부 API 호출을 끄면 데이터는 어디에 남길지, 나중에 재처리할 수 있는지 같은 fallback 설계가 먼저 있어야 한다.

fun publishOrderCompleted(event: OrderCompletedEvent) { 
    if (!featureFlag.isEnabled("crm.order-completed-publish")) { 
        pendingCrmEventRepository.save(event) 
        return 
    } 
    crmClient.publish(event) 
}

 

실험 설정을 코드 배포 없이 바꾸기

A/B 테스트를 하기 위해서는 보통 조건을 코드에 직접 매핑해야 한다. 때문에 실험 조건이 조금만 바뀌어도 코드 수정과 배포가 필요하다. 배포 주기가 2주라면, 작은 실험 하나를 수정하는 데도 리드타임이 길어질 수밖에 없다.

{ 
    "control": { 
        "title": "추천 콘텐츠", 
        "initialCount": 30 
    }, 
    "treatment": { 
        "title": "회원님을 위한 추천", 
        "initialCount": 10 
    } 
}

위와 같이 값이 코드에 하드코딩되어 있으면 실험을 조정할 때마다 배포가 필요하다. 반대로 Remote Configuration으로 관리하면 배포 없이 실험 설정을 바꿀 수 있다. 이때 Feature Flag는 “이 기능을 켤 것인가”를 결정하고, Remote Configuration은 “이 기능을 어떤 값으로 동작시킬 것인가”를 결정한다. 실제 서비스에서는 둘이 함께 쓰이는 경우가 많다. (관련 링크)

특정 대상에게만 기능 제공하기

모든 기능이 모든 사용자에게 동시에 열릴 필요는 없다. 내부 직원에게만 실험용으로 먼저 열어볼 수도 있고(CBT), 특정 조직이나 특정 앱 버전 이상에서만 기능을 제공할 수도 있다. 유료 요금제나 관리자 권한을 가진 사용자에게만 보여야 하는 기능도 있다. 이런 경우 Feature Flag는 단순한 boolean 값이 아니라, 현재 요청의 context를 기반으로 평가된다.

val context = FeatureContext( 
    userId = user.id, 
    organizationId = user.organizationId, 
    appVersion = request.appVersion, 
    plan = user.plan, 
) 

if (featureFlag.isEnabled("dashboard.new-admin", context)) { 
    return newAdminDashboardService.getDashboard(user) 
} 
return legacyDashboardService.getDashboard(user)

여기서 중요한 것은 “누구에게 켤 것인가”를 코드에 명시적으로 박아두지 않는다는 점이다. 코드에는 플래그 평가 지점만 남기고, 실제 대상 조건은 외부 설정으로 관리한다. 그래야 운영 중에도 대상 조직을 추가하거나, 특정 앱 버전만 제외하거나, 내부 사용자에게만 기능을 열 수 있기 때문이다.

안전하게 마이그레이션하기

기존 구조에서 새로운 구조로 점진적으로 마이그레이션할 때도 Feature Flag를 사용할 수 있다. 기존 로직을 한 번에 모두 제거하고 새 로직으로 바꾸는 것은 위험하다. 때문에 기존 로직과 신규 로직을 함께 두고, 플래그로 실행 경로를 제어하는 방식으로 코드를 작성할 수 있다.

fun transfer(command: TransferCommand): TransferResult { 
	if (featureFlag.isEnabled("transfer.use-new-usecase")) { 
    	return newTransferUseCase.execute(command) 
    } 
    return legacyTransferService.transfer(command) 
}

이처럼 기존 시스템을 한 번에 갈아엎는 대신, 기능 단위로 조금씩 안전하게 새 구조로 옮겨가도록 제어할 수 있다.

Feature Flag 관리

플래그 서빙 경로는 가벼워야 한다

Feature Flag는 사용자 요청 흐름 안에서 사용된다. 그래서 플래그 값을 확인할 때마다 DB를 조회하거나, 복잡한 어드민 서버를 호출하게 된다면 Feature Flag 자체가 병목이 될 수 있고, 플래그 시스템 장애가 서비스 장애로 전파될 수 있다. 때문에 요청 처리 경로에서는 가능한 한 가볍게 사용해야 한다. 로컬 캐시나 인메모리 설정을 사용하고, 설정 변경은 이벤트나 주기적 동기화로 반영하는 방식이 일반적이다.

즉, Feature Flag는 런타임 제어 장치이기 때문에 실시간성이 중요하며 동시에 사용자 요청 흐름을 느리게 만들면 안 된다.

플래그는 운영 데이터다

Feature Flag는 특히 관리가 소홀해지기 쉽다. 따라서 플래그에는 최소한 아래 정보가 필요하며 공유되어야 한다.

이 플래그는 어떤 목적으로 만들어졌는가?
누가 소유자인가?
기본값은 무엇인가?
어떤 환경에서 사용할 수 있는가?
누가 변경할 수 있는가?
언제 제거해야 하는가?
변경 이력은 남고 있는가?

특히 변경 이력은 중요하다. 장애가 발생하면 보통 최근 배포부터 확인하여 변경 사항을 파악하지만, Feature Flag를 사용하는 시스템에서는 최근 플래그 변경도 함께 확인해야 한다. 배포는 없었는데 서비스 동작이 바뀌었다면, 원인은 플래그 변경일 수 있다.

플래그는 반드시 정리해야 한다

Feature Flag는 편리하지만 코드에 분기를 남긴다.

if (featureFlag.isEnabled("some-feature")) {
    newPath()
} else {
    oldPath()
}

플래그가 하나일 때는 괜찮다. 하지만 시간이 지나면서 이런 분기가 늘어나면 코드 경로가 복잡해지며 이미 사용하지 않는 레거시 경로들이 계속 남아 있을 수도 있다. 때문에 사용하지 않는 Feature Toggle은 바로 제거해야 하며 장기 운영 플래그라면 왜 계속 유지해야 하는지 설명이 있어야 한다. Feature Flag를 잘 쓴다는 것은 플래그를 많이 만드는 팀이 아니라, 필요 없어진 플래그를 잘 지우고 관리하는 것이다.

정리

Feature Flag는 배포된 코드의 동작을 런타임에 제어하기 위한 장치다. 이를 통해 배포와 기능 공개를 분리할 수 있으며, 문제가 발생한 기능을 즉시 비활성화하는 등 다양한 작업을 보다 안전하고 유연하게 수행할 수 있다.

하지만 Feature Flag를 사용하는 것만큼이나 이를 체계적으로 관리하는 것도 중요하다. 무분별하게 추가하면 어떤 플래그가 어떤 기능을 제어하는지 파악하기 어려워지고, 운영 중에도 배포 없이 서비스의 동작이 변경될 수 있어 장애 원인 분석과 문제 추적이 복잡해질 수 있다. 또한 더 이상 사용하지 않는 Feature Flag를 방치하면 코드의 복잡도가 높아지고 유지보수 비용도 함께 증가한다.

실습 코드

https://github.com/wldks1008/feature-flag-pattern

 

GitHub - wldks1008/feature-flag-pattern: feature flag 패턴 실습

feature flag 패턴 실습. Contribute to wldks1008/feature-flag-pattern development by creating an account on GitHub.

github.com

참고자료

  • https://docs.getunleash.io/get-started/what-is-a-feature-flag
 

What is a Feature Flag? Complete Guide to Feature Flags

Feature flags let you release, test, and manage features without redeploying. Here's what they are, why teams use them, and how they work.

docs.getunleash.io

 

  • https://tech.kakaopay.com/post/feature-flag/
 

피처 플래그 개발기: 실시간 데이터 동기화를 향한 여정 | 카카오페이 기술 블로그

원활한 배포 및 장애 대응을 위한 피처 플래그를 개발하며 실시간 데이터 동기화를 위해 직면한 문제를 해결하기 위한 여정을 공유합니다.

tech.kakaopay.com

  • https://medium.com/daangn/%EB%A7%A4%EC%9D%BC-%EB%B0%B0%ED%8F%AC%ED%95%98%EB%8A%94-%ED%8C%80%EC%9D%B4-%EB%90%98%EB%8A%94-%EC%97%AC%EC%A0%95-2-feature-toggle-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0-b52c4a1810cd
 

매일 배포하는 팀이 되는 여정(2) — Feature Toggle 활용하기

효율적이고 안정적인 배포를 위해 고민했던 것 중 하나인 Feature Toggle(Feature Flag)에 대한 이야기

medium.com

  • https://techblog.lycorp.co.jp/en/20231005a
 

How We Rearchitected A/B Testing at LINE NEWS

A/B tests play a crucial role in decision-making at LINE NEWS as they help determine what modificati...

techblog.lycorp.co.jp

  • https://techblog.lycorp.co.jp/en/deployment-strategies-by-feature-toggle
 

Easier, Flexible, and Lower Resource Cost Deployment Strategies by Feature Toggle

It's crucial to have various deployment strategies to ensure that new versions of your software is d...

techblog.lycorp.co.jp

'Etc' 카테고리의 다른 글

Flyway 도입기  (0) 2025.08.19
AWS Lambda를 활용한 일괄 인증 자동화 시스템 구축기  (0) 2025.08.06
[EduKit] 멀티모듈 도입기  (2) 2025.06.26
캐시(Cache) 전략  (0) 2025.05.03
우당탕탕 서브모듈 도입기  (0) 2025.03.17
'Etc' 카테고리의 다른 글
  • Flyway 도입기
  • AWS Lambda를 활용한 일괄 인증 자동화 시스템 구축기
  • [EduKit] 멀티모듈 도입기
  • 캐시(Cache) 전략
wing1008
wing1008
휘발을 막기 위한 기록
  • wing1008
    차곡차곡
    wing1008
  • 전체
    오늘
    어제
    • 분류 전체보기 (72)
      • Spring (22)
      • JPA&JDBC (13)
      • Java & Kotlin (2)
      • Computer Science (20)
        • 데이터베이스 (9)
        • 운영체제 (2)
        • 네트워크 (4)
        • 자료구조&알고리즘 (5)
      • Observability (6)
      • Etc (6)
        • Kafka (0)
      • 끄적끄적 (3)
  • 블로그 메뉴

    • 홈
    • 태그
  • 링크

    • Github
  • 공지사항

    • 블로그 정착 완료!
  • 인기 글

  • 태그

    JPA
    read through
    write around
    알고리즘
    realmysql1
    edumate
    write back
    실전! 스프링 부트와 jpa 활용 2
    write through
    한끼족보
    Transaction
    카카오 코테 기출
    look aside
    자바 orm 표준 jpa 프로그래밍 - 기본편
    프로그래머스
    spring
    스프링 JPA
    쓰기 전략
    백준
    실전! 스프링 부트와 jpa 활용1
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
wing1008
Feature Flag 패턴이란?
상단으로

티스토리툴바