작은 실험, 거대한 가능성

11

발행일: 2025년 04월 29일

회의실에서의 싸늘한 반응은 조던 워크에게 깊은 내상을 남겼다. 하지만 동시에 그의 투지를 불태우는 기폭제가 되었다. '몽상가'라는 보이지 않는 낙인이 찍히기 전에, 그는 자신의 아이디어가 단순한 공상이 아님을 증명해야만 했다.

"실험장이 필요해… 리스크는 적으면서도, FaxJS의 장점을 확실히 보여줄 수 있는 곳."

그는 페이스북 내부의 수많은 프로젝트와 기능들을 샅샅이 훑기 시작했다. 마치 먹잇감을 찾는 굶주린 늑대처럼. 너무 핵심적인 기능은 위험 부담이 컸다. 실패했을 경우의 파장이 너무 클 터였다. 반대로 너무 사소하거나 중요하지 않은 기능은, 성공한다 해도 아무런 주목을 받지 못할 가능성이 높았다.

"적당한 크기, 적당한 복잡성, 그리고… 명확한 문제점을 안고 있는 곳."

며칠간의 탐색 끝에, 그의 레이더망에 한 가지 기능이 포착되었다. 바로 '타입어헤드(Typeahead)' 검색 기능. 사용자가 페이스북 검색창에 무언가를 입력하기 시작하면, 실시간으로 관련 친구, 페이지, 그룹 등의 추천 목록을 보여주는 기능이었다.

"이거다!"

조던의 눈이 빛났다. 타입어헤드 기능은 몇 가지 측면에서 FaxJS의 실험장으로 완벽해 보였다.

  1. 높은 상호작용성: 사용자의 키 입력 하나하나에 즉각적으로 반응하여 추천 목록을 업데이트해야 했다. 상태 변화가 빈번하고 UI 업데이트가 빨라야 했다.
  2. 관리의 어려움: 기존의 타입어헤드 구현은 제이쿼리와 복잡한 비동기 로직으로 뒤엉켜 있었다. 성능 문제와 예측 불가능한 버그가 종종 보고되었다. 특히 추천 목록을 렌더링하고 관리하는 부분이 비효율적이었다.
  3. 적절한 중요도: 핵심 기능은 아니지만, 사용자 경험에 꽤 중요한 영향을 미치는 기능이었다. 여기서 성능 개선과 안정성 향상을 보여준다면 충분히 주목받을 수 있었다.
  4. 격리 가능성: 검색창이라는 비교적 독립된 영역에서 작동하므로, 다른 시스템에 미치는 영향을 최소화하면서 실험하기 용이했다.

"좋아. 이걸 FaxJS로 다시 만들어 보는 거야."

문제는 이 작업을 어떻게 '공식적으로' 진행할 수 있느냐였다. 그는 자신의 팀 리더인 마크를 찾아갔다. 이전 회의에서의 냉담한 반응이 마음에 걸렸지만, 정면 돌파 외에는 방법이 없었다.

"마크, 타입어헤드 검색 기능 말입니다. 최근 성능 이슈가 좀 있지 않습니까? 제가 제안했던 FaxJS 방식으로 이 부분을 한번 개선해 보면 어떨까 합니다."

마크는 미간을 찌푸리며 조던을 바라보았다. "조던, 아직 그… 팩스인가 뭔가 하는 거 포기 안 했나? 지난번에 이야기했지만, 검증되지 않은 기술을 도입하는 건…"

"알고 있습니다. 그래서 말씀드리는 겁니다." 조던은 물러서지 않았다. "기존 코드를 건드리지 않겠습니다. 타입어헤드 로직을 FaxJS 기반으로 완전히 새로 구현해서, 기존 버전과 A/B 테스트를 통해 비교해 보는 겁니다. 만약 성능이나 안정성 면에서 유의미한 개선이 없다면, 제 코드는 그냥 폐기하면 됩니다. 하지만 만약… 더 나은 결과가 나온다면, 그때 다시 논의해 볼 수 있지 않겠습니까?"

그의 제안은 합리적이었다. 기존 시스템에 영향을 주지 않고, 실패 시의 리스크를 최소화하면서 새로운 기술의 가능성을 검증해 보자는 것. 마크는 잠시 고민하는 듯했다. 타입어헤드 기능의 자잘한 버그와 성능 불만은 그 역시 인지하고 있는 문제였다. 밑져야 본전이라는 생각이 들었을까.

"…좋네. 단, 조건이 있네." 마크가 마침내 입을 열었다. "개인 시간에 진행하게. 공식 업무 시간에는 할당된 다른 일에 집중해야 하네. 그리고 결과는 반드시 데이터로 증명해야 할 걸세. 감성적인 이야기는 필요 없네."

"네! 알겠습니다! 감사합니다!"

비록 '개인 시간 활용'이라는 족쇄가 붙었지만, 조던은 기회를 얻었다는 사실 자체에 기뻤다. 그는 마치 비밀 임무를 부여받은 첩보원처럼, 밤과 주말을 이용해 타입어헤드 기능을 FaxJS로 재구현하는 작업에 착수했다.

그 과정은 결코 순탄치 않았다. FaxJS 자체도 아직 완성된 상태가 아니었기에, 타입어헤드 기능을 만들면서 동시에 FaxJS 엔진 자체를 개선하고 버그를 잡아야 했다. 가상 DOM 비교 알고리즘은 예상보다 복잡했고, 실제 사용자의 빠른 키 입력 속도를 따라잡기 위한 최적화 작업은 까다로웠다.

하지만 그는 멈추지 않았다. 머릿속에서 'UI = f(State)'라는 명제가 맴돌았다. 사용자의 입력(inputText)이 상태가 되고, 그 상태를 기반으로 추천 목록(suggestions)을 API로부터 받아와 또 다른 상태로 관리하고, 최종적으로 이 상태들을 조합하여 UI(renderSuggestionsList)를 그리는 간결하고 선언적인 흐름.

// FaxJS 기반 타입어헤드 로직 (초간단 개념)

function TypeaheadSearch(props) {
  let state = {
    inputText: '',
    suggestions: [],
    isLoading: false,
  };

  function handleInputChange(event) {
    state.inputText = event.target.value;
    state.isLoading = true;
    // 입력값으로 API 호출해서 추천 목록 받아오기 (비동기)
    fetchSuggestions(state.inputText).then((results) => {
      state.suggestions = results;
      state.isLoading = false;
      FaxJS.rerender(this); // 상태 변경 후 리렌더링 요청!
    });
    FaxJS.rerender(this); // 로딩 상태 먼저 반영
  }

  function render() {
    return {
      type: 'div',
      children: [
        {
          /* 입력창 (input) 컴포넌트 */
        },
        state.isLoading
          ? {
              /* 로딩 스피너 컴포넌트 */
            }
          : {
              /* 추천 목록 (ul/li) 컴포넌트 (suggestions 기반) */
            },
      ],
    };
  }

  return render();
}

기존의 복잡했던 제이쿼리 코드와 비교했을 때, FaxJS로 작성된 코드는 훨씬 구조적이고 이해하기 쉬웠다. 상태 변화의 원인과 결과가 명확하게 드러났다.

며칠 밤낮의 사투 끝에, 마침내 FaxJS 기반의 새로운 타입어헤드 기능이 완성되었다. 겉보기에는 기존 기능과 거의 동일했지만, 그 속을 움직이는 엔진은 완전히 달랐다.

이제 남은 것은… 운명의 A/B 테스트였다. 과연 이 작은 실험은, 거대한 가능성의 문을 열 수 있을 것인가? 회의론자들의 코를 납작하게 만들 압도적인 결과가 나올 것인가? 조던은 긴장감 속에 테스트 시작 버튼을 눌렀다.