첫 번째 컴포넌트, 가능성을 보다

9

발행일: 2025년 04월 28일

가상 DOM(Virtual DOM). 그 마법 같은 개념은 더 이상 조던 워크의 머릿속에만 존재하는 추상적인 이론이 아니었다. 그는 지난 며칠간 밤낮을 잊고 코드를 다듬고 또 다듬었다. 기본적인 가상 DOM 트리 생성, 두 트리 간의 차이를 비교하는 원시적인 비교 알고리즘(Diffing Algorithm), 그리고 그 차이점을 실제 DOM에 적용하는 패치(Patch) 기능까지. FaxJS의 심장부가 될 엔진이 마침내 희미하게나마 동작하기 시작한 것이다.

"좋아… 이제 진짜 '부품'을 만들어 볼 차례다."

조던의 눈빛이 다시 한번 타올랐다. 지금까지는 숫자 카운터 같은 단순한 테스트에 불과했다. 이제는 XHP에서 영감을 얻었던 바로 그 아이디어, '재사용 가능한 UI 컴포넌트'를 FaxJS 위에서 실현해야 했다. 그의 머릿속에 페이스북의 상징과도 같은 UI 조각 하나가 떠올랐다.

'좋아요(Like)' 버튼.

이 작고 단순해 보이는 버튼 안에는 생각보다 많은 상태와 로직이 숨어 있었다.

  • 상태: '좋아요'를 눌렀는가? (isLiked: true/false), '좋아요' 개수는 몇 개인가? (likeCount: number)
  • 표현: 상태에 따라 버튼의 텍스트('좋아요' / '좋아요 취소')나 아이콘, 색상이 바뀌어야 한다. 옆에는 '좋아요' 개수도 표시되어야 할 수 있다.
  • 행동: 버튼을 클릭하면 상태가 바뀌고(isLiked 토글, likeCount 증감), 서버에 요청을 보내야 할 수도 있다.

만약 이걸 제이쿼리로 만든다면?

"생각만 해도 끔찍하군."

조던은 고개를 저었다. 버튼 요소 찾고, 클릭 이벤트 걸고, 이벤트 핸들러 안에서 상태 확인하고, 조건에 따라 클래스 넣었다 뺐다 하고, 텍스트 바꾸고, 숫자 업데이트하고… 만약 페이지에 '좋아요' 버튼이 수십 개 있다면? 그 모든 버튼에 각각 이벤트 리스너를 달고 상태를 관리해야 했다. 코드는 금방 지저분해지고, 유지보수는 악몽이 될 터였다.

하지만 FaxJS라면?

그의 손가락이 다시 춤을 추기 시작했다. 그는 새로운 자바스크립트 함수를 정의했다. 이름하여 LikeButton.

// FaxJS의 첫 번째 컴포넌트: LikeButton

function LikeButton(props) {
  // props: 컴포넌트가 외부로부터 받는 데이터 (예: 초기 좋아요 개수)

  // 컴포넌트 자체의 상태 (State) 정의
  let state = {
    isLiked: false,
    likeCount: props.initialCount || 0,
  };

  // 클릭 시 실행될 함수: 상태를 변경한다!
  function handleClick() {
    state.isLiked = !state.isLiked; // 좋아요 상태 토글
    state.likeCount += state.isLiked ? 1 : -1; // 개수 증감

    // !!! 중요: 상태가 변경되었으니, UI를 다시 그리도록 시스템에 알린다 !!!
    // FaxJS.rerender(this); // 대략 이런 느낌의 내부 함수 호출
  }

  // 현재 상태(state)에 따라 UI '묘사'를 반환하는 함수 (render 역할)
  function render() {
    return {
      type: 'button',
      props: {
        className: state.isLiked ? 'like-button liked' : 'like-button',
        onClick: handleClick, // 클릭 이벤트 연결
      },
      children: [
        state.isLiked ? '좋아요 취소' : '좋아요',
        {
          // 좋아요 개수 표시 (span 태그)
          type: 'span',
          props: { className: 'like-count' },
          children: ` (${state.likeCount})`,
        },
      ],
    };
  }

  // 컴포넌트가 최종적으로 반환하는 것은 현재 상태에 따른 UI 묘사!
  return render();
}

// 사용 예시: 초기 좋아요 10개인 버튼 생성
let myLikeButton = LikeButton({ initialCount: 10 });
// FaxJS 엔진이 myLikeButton 묘사를 실제 DOM으로 화면에 그린다.

"…됐다!"

코드를 완성하고 실행하는 순간, 조던은 숨을 죽였다. 화면에는 그가 설계한 대로 '좋아요 (10)' 버튼이 나타났다. 떨리는 마음으로 마우스 커서를 가져가 클릭!

찰칵!

마치 마법처럼, 버튼의 텍스트가 '좋아요 취소 (11)'로 부드럽게 바뀌었다! 다시 클릭하자 '좋아요 (10)'으로 돌아왔다. 내부적으로는 handleClick 함수가 호출되어 state가 변경되었고, FaxJS 엔진은 render 함수를 다시 호출하여 새로운 가상 DOM 묘사를 생성했다. 그리고 이전 가상 DOM과의 차이점(버튼 텍스트와 클래스, 숫자 텍스트 변경)만을 정확히 감지하여 실제 DOM에 최소한으로 반영한 것이다!

"이거… 정말 되잖아?"

조던은 모니터 앞에서 멍하니 서 있었다. 그가 작성한 LikeButton 코드는 놀랍도록 간결하고 명확했다. 상태(state)와 그 상태를 기반으로 한 UI 묘사(render), 그리고 상태를 변경하는 로직(handleClick). 이 모든 것이 컴포넌트라는 하나의 단위 안에 깔끔하게 캡슐화되어 있었다.

더 놀라운 것은 재사용성이었다.

// 다른 곳에서도 LikeButton 컴포넌트를 쉽게 가져다 쓸 수 있다!
let anotherButton = LikeButton({ initialCount: 5 });
let zeroButton = LikeButton(); // 초기값 없으면 0

마치 레고 블록처럼, LikeButton 컴포넌트를 필요한 곳 어디든 가져다 쓸 수 있었다. 각 버튼은 자신만의 독립적인 상태를 가질 것이고, 서로에게 영향을 주지 않았다.

"스파게티 코드는… 이제 없어."

조던의 입가에 희미한 미소가 번졌다. 이것은 단순한 버튼 하나가 아니었다. 그가 오랫동안 갈망해왔던, 웹 개발의 혼돈 속에서 찾아 헤맸던 '가능성' 그 자체였다. 복잡하게 얽힌 종속성 대신 독립적인 컴포넌트들의 조합. 예측 불가능한 부작용 대신 명확한 데이터 흐름.

FaxJS는 아직 거칠고 다듬어야 할 부분이 많았지만, 그 심장부에는 이미 혁명의 불씨가 타오르고 있었다. 그는 이 작은 성공에 만족하지 않았다. 그의 시선은 더 먼 곳, 페이스북의 거대한 UI 전체를 이 새로운 방식으로 재구축하는 미래를 향하고 있었다.

물론, 그 길은 험난할 터였다. 회의적인 시선, 기술적인 난관, 그리고… 어쩌면 내부의 정치적인 장벽까지도.