본문 바로가기
Frontend/React

타임 아웃 처리로 Braze Feature flag 로딩 UX 개선하기

by YWTechIT 2025. 8. 20.
728x90

Braze Feature flag 로딩 타임 아웃 처리로 UX 개선하기

문제 상황

Braze 툴로 A/B 테스트를 구현하는데, braze initializegetFeatureFlag() 호출 시점에서 featureFlag 데이터가 준비되지않아 undefined가 반환되는 이슈가 발생했다. 이로인해 FeatureFlagProvider 내부에서 children이 null로 반환되어 페이지 전체가 렌더링되지 않는 문제가 있었다.

접근/해결 방식

  1. 재시도 로직 추가: feature flag가 없을 경우 refreshFeatureFlags 메서드로 한번 더 호출
  2. 타임아웃 설정: 네트워크 지연으로 인한 무한 대기를 방지하기 위해 최대 대기 시간 설정
  3. 로딩 상태 관리: 사용자에게 로딩 중임을 알리고, 타임아웃 후에는 기존 컨텐츠 표시
// AS-IS
export function FeatureFlagProvider({
  children,
  id,
}: PropsWithChildren<{ id: FeatureFlagId }>) {
  const brazeInitState = useBrazeInitStateContext()
  const [featureFlagEntity, setFeatureFlagEntity] =
    useState<FeatureFlagEntity | null>()

  useEffect(() => {
    if (brazeInitState && id) {
      const braze = window.braze
      const featureFlag = braze.getFeatureFlag(id)
      if (!featureFlag) {
        setFeatureFlagEntity(null)
        return
      }

      braze.logFeatureFlagImpression(id)
    }
  }, [brazeInitState, id])

  if (featureFlagEntity === undefined) {
    return null
  }

  return (
    <FeatureFlagContext.Provider value={featureFlagEntity}>
      {children}
    </FeatureFlagContext.Provider>
  )
}

// TO-BE
const FEATURE_FLAG_TIMEOUT = 3000

export function FeatureFlagProvider({
  children,
  id,
}: PropsWithChildren<{ id: FeatureFlagId }>) {
  const brazeInitState = useBrazeInitStateContext()
  const [isLoading, setIsLoading] = useState(true)
  const [featureFlagEntity, setFeatureFlagEntity] =
    useState<FeatureFlagEntity | null>()

  useEffect(() => {
    if (brazeInitState && id) {
      const timeoutId = setTimeout(() => {
        setIsLoading(false)
      }, FEATURE_FLAG_TIMEOUT)

      const braze = window.braze
      const featureFlag = braze.getFeatureFlag(id)

      if (!featureFlag) {
        setFeatureFlagEntity(null)

        window.braze.refreshFeatureFlags(() => {
          const newFeatureFlag = window.braze.getFeatureFlag(id)

          if (newFeatureFlag) {
            handleFeatureFlagInit(newFeatureFlag)
          } else {
            setIsLoading(false)
          }

          clearTimeout(timeoutId) // setTimeout이 더 이상 필요없고, 브라우저에게 더 이상 추적하지 말라는 의미를 주기위해 추가
        })

        return
      }

      handleFeatureFlagInit(featureFlag)
      clearTimeout(timeoutId)

      return () => {
        clearTimeout(timeoutId)
      }
    }
  }, [brazeInitState, id, isLoading, handleFeatureFlagInit])

  if (isLoading) {
    return <>{LoadingComponent}</>
  }

  return (
    <FeatureFlagContext.Provider value={featureFlagEntity}>
      {children}
    </FeatureFlagContext.Provider>
  )
}

추가 고려사항

향후에는 이 코드를 다음과 같이 개선해볼 수 있다.

  1. 에러 바운더리 처리: refreshFeatureFlags 혹은 네트워크 요청 실패등의 에러 발생시 에러 바운더리로 위임한다.
  2. 재시도 횟수 제한: 지금은 1번만 재시도를 하게되는데 재시도 횟수를 임의로 설정하도록 개선이 가능하겠다
반응형

댓글