fe.resolver.ts
fe.resolver.ts

Powered by Notion & Next.js

Navigate

  • 개인정보처리방침

Connect

  • GitHub

© 2026 Hanul Lee. All rights reserved.

Powered by Notion & Next.js

목록으로
Development2025년 1월 21일

[Git] Web 개발의 혁신, TBD + Feature Flag

웹 개발은 이게 맞아

#Tools#Infra

개요

이 회사에 와서 가장 좋은점이 뭐냐? 라고 누군가 묻는다면 정말 꼽기가 힘들 정도로 배울 점이 많은데, 그중에 하나만 꼽으라면 깃 브랜치 전략 문화이다.

닥터팔레트 프론트엔드 웹 서비스는 TBD 전략을 사용하고 있는데, 이게 아주 기가 막히다.

구글이 사용하는 TBD 전략이 뭔지, 그리고 닥팔팀에서 어떻게 사용하는지 정리해보려 한다.

익숙한 맛, Git Flow와 GitHub Flow

본격적으로 TBD를 찬양하기 전에, 흔히 접해왔던, 그리고 필자가 실제로 전회사까지 써왔던 브랜치 전략들을 잠깐 짚고 넘어가 보자.

왜 TBD가 새로운 맛인지, 왜

Git Flow

Vincent Driessen이 제안한 이 전략은 개발자라면 한 번쯤 들어봤을 '국룰'이다.

master (혹은 main), develop, feature, release, hotfix 등 브랜치의 역할이 엄격하게 나뉘어 있다.

이 과정은 안정적인 버전 관리가 필요한 패키지 소프트웨어(앱, 게임 등)에는 적합할지 몰라도, 하루에도 수십 번씩 배포가 일어나는 웹 서비스 환경에서는 너무 무겁다.

기능 하나 배포하려면 feature → develop → release → master의 기나긴 여정을 거쳐야 한다.

GitHub Flow: 훨씬 가볍다, 하지만...

Git Flow의 복잡함을 걷어내고, master 브랜치와 feature 브랜치만 남겼다. PR(Pull Request)을 통해 코드 리뷰를 하고, 머지되면 즉시 배포한다. CI/CD 친화적이고 빠르다. 하지만 팀 규모가 커지면 이야기가 달라진다.

  • 머지 지옥(Merge Hell): 여러 명이 동시에 feature를 따서 작업하다 보면, 배포 시점에 충돌(Conflict) 해결하느라 시간을 다 보낸다.
  • 리뷰 병목: "PR 리뷰 부탁드립니다"의 늪. 리뷰가 늦어지면 배포도 늦어지고, 그사이 master는 변해있다.

‘머지 스트레스 없이, 준비된 코드는 즉시 나간다’ 는 철학이 필요했다.

그게 바로 지금 소개할 Trunk Based Development (TBD) 다.

TBD (Trunk Based Development)


Trunk Based Development

Trunk Based Development

A portal on this practice

faviconhttps://trunkbaseddevelopment.com/

공식문서의 한줄 요약(One line summary)을 확인해보면 TBD의 철학은 명확하다.

💡
모든 개발자가 하나의 코드 브랜치인 ‘trunk’ 에서 작업을 진행하는 방식

Loading image...
Notion Image

Github Flow와 유사한듯 하지만, 훨씬 가볍고 빠르다.

TBD의 규칙을 정리해보면 다음과 같다.

  • 하나의 메인 브랜치(trunk)만을 관리한다.
  • 24시간 이하의 가장 짧은 작업 단위를 가져야 한다.
  • 미완성 기능은 피처 플래그로 관리한다.

간단 명료하지 않은가 ?

언제든 배포 가능한 하나의 trunk 브랜치를 가지고, 가능한 작은 작업(micro PR)로 trunk에 병합한다.

그러나 개발자로 살다보면 기획이 엎어지기도, 디자인이나 백엔드 일정이 예정보다 늦어지기도, 예상치 못한 문제에 피처를 오래 가져가는 일도 반드시 있는 법.

trunk based가 되는 메인 브랜치는 언제든 배포가 가능한 상태여야 한다.

그래서 TBD 전략을 사용하기 위해 반드시 필요한것이 세번째 규칙인 피처 플래그(Feature Flag) 다.

Feature Flag


Trunk Based Development

Trunk Based Development

A portal on this practice

faviconhttps://trunkbaseddevelopment.com/feature-flags/

기능을 개발했을때 그 기능을 특정 인원에게만 접근할 수 있게하는 방법론을 뜻한다.

피처 플래그의 예시로 구글의 유튜브와 깃허브를 들어볼 수 있다.

  • iPhone Youtube 앱 PIP 모드 실험
    • 한 때, 유튜브는 아이폰에서 PIP 모드를 정식 출시하기 전 몇몇 사용자에게만 experimental preview라는 이름으로 사용할 수 있게하다가 중간에 소리소문 없이 없애고 정식 출시를 한 적이 있다.
  • Github feature preview
    • Github에서 특정(Pro) 사용자에게 가끔 새로운 기능 미리보기를 제공한다.

여기서 다시한번 짚고가야 할 규칙은 위에 언급한 두번째 규칙이다.

  • 24시간 이하의 작은 작업 단위를 가져야 한다.
Trunk Based Development

Trunk Based Development

A portal on this practice

faviconhttps://trunkbaseddevelopment.com/short-lived-feature-branches/

복잡하고 큰 기능이어도 상관 없다. 그냥 구현한 코드를 정리해서 피처플래그를 꺼놓으면 된다.

그래서 피처플래그의 가장 중요한 특징은 ‘제품 배포 없이 플래그를 활성화/비활성화 할 수 있어야한다’ 라는 점이다.

그래서 우리는 LaunchDarkly 라는 유료 서비스를 사용해 피처플래그를 관리하고 있다.

React + LaunchDarkly 사용 예시

루트에 LDProvider 를 감싼다.

typescript
<LDProvider>
  <StrictMode>
    <App />
  </StrictMode>
</LDProvider>

useFlags 로 LaunchDarkly에 등록한 플래그를 가져온다.

(보통 단순 boolean 값만으로 관리가 충분하다.)

typescript
const HooksDemo = () => {
	// 키 값은 카멜케이스로 가져올 수 있다.
  const { devTestFlag } = useFlags(); 

  return (
    <Wrapper>
      <FlagDisplay>{devTestFlag ? <span>Flag on</span> : <span>Flag off</span>}</FlagDisplay>
    </Wrapper>
  );
};

MFE 환경을 위해 HOC패턴으로 만들기

닥팔 웹 서비스는 현재 60여개의 서브패키지로 관리되고 있다.

모듈 페더레이션의 호스트를 다르게 설정해서 켰을때, 상위에 LDProvider 에 감싸지지 않아서 에러가 나는 문제를 해결하고, 언제나 원하는 호스트로 켜기 위해 WithLD 라는 HOC 를 만들어서 적용했다.

typescript
import { asyncWithLDProvider } from 'launchdarkly-react-client-sdk';

export const WithLD = ({ children }: PropsWithChildren<unknown>) => {
  const root = document.getElementById('app');
  if (!root) {
    throw new Error('root is not found');
  }

  (async () => {
    const LDProvider = await asyncWithLDProvider({
      clientSideID: process.env.LAUNCH_DARKLY_KEY ?? '',
      context: {
	      // context values
      },
      options: {
        streaming: true,
      },
    });

    createRoot(root).render(
      <LDProvider>
        <StrictMode>{children}</StrictMode>
      </LDProvider>,
    );
  })();
};

이렇게 만든 HOC는 bootstrap.tsx 에서 App을 감싸면 된다.

typescript
async function bootstrap() {
  WithLD({ children: <App /> });
}

bootstrap();

  • 개요
  • 익숙한 맛, Git Flow와 GitHub Flow
  • Git Flow
  • GitHub Flow: 훨씬 가볍다, 하지만...
  • TBD (Trunk Based Development)
  • Feature Flag
  • React + LaunchDarkly 사용 예시
  • MFE 환경을 위해 HOC패턴으로 만들기