본문 바로가기
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,
  timeoutDuration = TIMEOUT_DURATION,
  FallbackComponent,
}: PropsWithChildren<{
  id: FeatureFlagId
  timeoutDuration?: number
  FallbackComponent?: ReactNode
}>) {
  const brazeInitState = useBrazeInitStateContext()
  const [isLoading, setIsLoading] = useState(true)
  const [featureFlagEntity, setFeatureFlagEntity] =
    useState<FeatureFlagEntity | null>()

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

        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)
          })

          return
        }

        handleFeatureFlagInit(featureFlag)
        clearTimeout(timeoutId)

        return () => {
          clearTimeout(timeoutId)
        }
      } else {
        setIsLoading(false)
        setFeatureFlagEntity(null)
      }
    }
  }, [brazeInitState, id, isLoading, handleFeatureFlagInit, timeoutDuration])

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

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

추가 고려사항

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

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

댓글