본문 바로가기
FE/React

Optimistic-UI

by ideal_string 2022. 10. 25.

옵티미스틱 UI(Optimistic-UI)는 멘털 모델 중 하나다. 인터페이스 형태를 말하는 게 아니다. API와 통신 중 UI를 그려내는 과정에서 모든 것을 낙관적으로 보고 UI를 먼저 그리는 모델이다. 다시 정리하자면, 옵티미스틱 UI는 모든 API 통신이 성공할 것이라 여기고 화면에 먼저 결과 값을 그리는 것을 말한다.

 

비 낙관적(왼쪽) vs 낙관적(오른쪽) 메신저 UI (출처 : https://uxplanet.org/optimistic-1000-34d9eefe4c05#.i3zxqxrim)

 

5G 시대에도 필요?

우리는 언제부턴가 인터넷 속도가 느려서 힘들어하는 경우가 많이 사라졌다. 서버 통신을 낙관적으로 보고 UI를 먼저 그려야하는 일이 없어 보인다. 결론부터 말하자면 옵티미스틱 UI은 필요하다. 옵티미스틱 UI 장점은 빠른 피드백이다. 우리 주변에서 흔히 볼 수 있는 결과 중 하나로 페이스북 버튼의 좋아요가 옵티미스틱 UI로 이뤄져 있다. 사용자가 좋아요 버튼을 계속 클릭하면, 통신이 응답하기도 전에 UI가 왔다 갔다 변화하는 걸 볼 수 있다. 이는 사용자에게 페이스북은 빠른 소식을 가져온다는 것을 느끼게 하는 도구로 사용자 경험을 높인다.

 

트래픽과 비용

작은 서비스라면 사실 트래픽 자체가 큰 문제 되지 않는다. 다만, 페이스북이나 아마존 같은 웹 서비스라면 트래픽에 매우 민감하다. 트래픽을 조금이라도 줄인다면 그 영업이익을 꽤나 높일 수 있기 때문이다. 즉, 통신 비용을 줄일 수 있다는 말이다. 수천 개라면 모르지만, 수억 개 트래픽이 발생한다면 그 트래픽은 모두 비용이다. 위에서 언급한 좋아요를 예로 들어본다. 1. 프론트엔드는프런트엔드는 사용자가 좋아요를 누르면 서버에 수정 요청한다. 2. 서버는 값을 수정하고, 수정된 값을 반환한다. 3. 프런트엔드는 서버에서 반환받은 값을 화면에 그린다. 한 번 누를 때마다 단계별 1원씩 총 3원의 통신비가 발생한다 치자. 페이스북 이용자 1천만 명이 한 번만 눌러도 3천만 원인 셈이다.  이때, 옵티미스틱 UI를 적용해 1번과 2번까지 실행하고, 3번을 생략한다면 어떨까? 바로 천만 원을 아낄 수 있다.

 

무분별한 사용은... NO

장점이 있는 만큼 부작용도 크다. 이름처럼 모든 것을 낙관으로 바라봤을 때는 좋다. 결과가 낙관이지 않다면, 꽤나 큰 이슈다. 좋아요 정도 서비스라면 문제가 적을 수도 있다. 결제 서비스였다면 어떨까? 결제를 시도했고 90% 단계까지 진행되었고 99%까지 문제가 없어 바로 충전금액을 반영했다. 그런데 최종 허가 직전 도난 신고가 확인되었다면? 사업자는 결제 금액만큼 손해본다. 비유지만, 사업자 입장에서 생각하면 끔찍한 일이다. 따라서, 옵티미스틱 UI를 사용하려면 결과가 좋지 않을 때에는 어떻게 처리할 것인가에 대하여도 필히 준비해야 한다.

 

ApolloClient로 옵티미스틱UI를 사용한 모습

export default function OptimisticUIPage() {
  useEffect(() => {}, []);
  const { data } = useQuery<Pick<IQuery, "fetchBoard">, IQueryFetchBoardArgs>(
    FETCH_BOARD,
    { variables: { boardId: "634eef5f6cf469002995d589" } }
  );
  const [likeBoard] = useMutation<
    Pick<IMutation, "likeBoard">,
    IMutationLikeBoardArgs
  >(LIKE_BOARD);

  const onClickLike = () => {
    void likeBoard({
      variables: {
        boardId: "634eef5f6cf469002995d589",
      },
      optimisticResponse: {
        // api 요청을 하지만, 일단 먼저 카운트를 올림
        likeBoard: (data?.fetchBoard.likeCount || 0) + 1,
      },
      update(cache, { data }) {
        cache.writeQuery({
          query: FETCH_BOARD,
          variables: { boardId: "634eef5f6cf469002995d589" },
          data: {
            fetchBoard: {
              _id: "634eef5f6cf469002995d589",
              __typename: "Board",
              likeCount: data?.likeBoard,
            },
          },
        });
      },
    });
  };

  return (
    <>
      <div>좋아요 : {data?.fetchBoard.likeCount}</div>
      <button onClick={onClickLike}>좋아요</button>
    </>
  );
}

ApolloClient에서 옵티미스틱UI를 사용해봤다. 먼저 업데이트를 캐시로 먼저 받아오는 update를 사용했고 여기에 Apollo에서 지원하는 optimisticResponse를 이용해 값을 프런트단에서 먼저 처리해 값을 반환했다. 

 

마무리

'잘쓰면 약, 잘못 쓰면 독약'이란 말이 떠오른다. 아무리 좋은 기술이어도 만들어진 의도를 모르고 사용하거나, 남용한다면 결국 손해는 자기에게 돌아온다. 옵티미스틱 UI 관련 글을 읽으면서 계속 드는 생각이다. 내가 알고 있는 기술이든 기법이든 무작정 배운대로만 사용할 게 아니었다. 그게 왜 나오게 되었고 무엇을 보완했는 지 그리고 무엇이 부족한 지까지 인지하고 사용해야 잘 사용하는 것이였다. 이정도면 다 안 거라고 수없이 착각했던 오만들이 떠오른다. 이만하면 다 됐다고 여긴 마음을 옵티미스틱 UI를 배우다 다시 겸손함을 가져야함을 또 깨닫는다. 기술을 아는 것도 인생도 겸손해야 한다.

 

※ 잘못된 내용이 있을 경우 댓글로 알려주세요. 배우고 익히고 수정하겠습니다:)

'FE > React' 카테고리의 다른 글

memoization(feat. useMemo, useCallback)  (0) 2022.10.26
Curring  (0) 2022.10.26
Shallow Routing  (0) 2022.10.25
LazyLoad & PreLoad  (0) 2022.10.24
HOC & HOF  (0) 2022.10.19