소피 알퍼트의 날카로운 시선

102025년 08월 25일4

세바스티안이 던진 근본적인 질문에 회의실이 다시 침묵에 잠겼을 때, 그 침묵을 깬 것은 소피 알퍼트(Sophie Alpert)였다. 그녀는 리액트 팀의 엔지니어링 매니저였지만, 단순한 관리자가 아니었다. 그녀의 코드는 누구보다 간결하고 명료했으며, 특히 최종 사용자인 ‘개발자’의 입장에서 문제를 바라보는 데 타의 추종을 불허했다.

“세바스티안의 말에 동의합니다. 우리는 잘못된 도구로 문제를 풀고 있어요.”

소피는 자리에서 일어나 화이트보드로 걸어갔다. 그녀는 HOC와 렌더 프롭 예제 옆에, 아주 복잡하게 얽힌 클래스 컴포넌트의 구조를 간략하게 그리기 시작했다.

class BigComplexComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: null,
      isScrolling: false,
      width: window.innerWidth,
      // ... 10개 이상의 상태 ...
    };
  }

  componentDidMount() {
    // 1. 데이터 패칭 시작
    // 2. 스크롤 이벤트 리스너 등록
    // 3. 창 크기 변경 이벤트 리스너 등록
    // 4. 실시간 소켓 연결
  }

  componentWillUnmount() {
    // 2. 스크롤 이벤트 리스너 해제
    // 3. 창 크기 변경 이벤트 리스너 해제
    // 4. 실시간 소켓 연결 해제
  }

  // ... 수많은 헬퍼 메서드와 렌더링 로직 ...
}

“이 코드를 보세요.”
그녀의 목소리는 차분했지만 힘이 있었다.
“이 컴포넌트의 가장 큰 문제가 뭐라고 생각하세요? 단순히 코드가 길다는 것? 아닙니다.”

소피는 빨간색 마커를 들어, componentDidMount 안에 있는 ‘스크롤 이벤트 리스너 등록’ 코드와 componentWillUnmount 안에 있는 ‘스크롤 이벤트 리스너 해제’ 코드에 동그라미를 쳤다.

“이 두 코드는 논리적으로 하나의 세트입니다. 스크롤을 감지하는 기능이죠. 하지만 이 둘은 클래스라는 구조 때문에 물리적으로 완전히 다른 공간에 흩어져 있습니다. 하나는 컴포넌트의 시작에, 다른 하나는 컴포넌트의 끝에 있죠.”

그녀는 이어서 ‘데이터 패칭’ 로직과 ‘소켓 연결’ 로직에도 각각 다른 색깔로 동그라미를 쳤다.

“마찬가지입니다. 데이터 패칭과 관련된 로직, 소켓 연결과 관련된 로직도 각각의 생명주기 메서드 안에 파편처럼 흩어져 있습니다. 유지보수를 위해 스크롤 관련 기능을 수정하려면, 개발자는 이 거대한 파일의 맨 위와 맨 아래를 정신없이 오가야 합니다.”

그녀는 잠시 말을 멈추고, 팀원들을 향해 돌아섰다.
“반대로 componentDidMount라는 하나의 메서드 안을 들여다보죠. 여기에는 데이터 패칭, 스크롤 감지, 창 크기 감지, 소켓 연결… 서로 아무 관련 없는 네 가지 기능이 한데 엉켜 있습니다. 이것이 바로 ‘관심사의 분리’가 완벽하게 실패한 증거입니다.”

소피의 설명은 명쾌했다.
클래스 컴포넌트는 코드를 ‘기능’ 단위가 아니라, ‘시간(생명주기)’ 단위로 묶도록 강제했다. 그 결과, 관련 있는 코드는 흩어지고, 관련 없는 코드는 뭉쳐버리는 모순적인 상황이 발생했다. 컴포넌트가 커지고 복잡해질수록, 이 문제는 걷잡을 수 없이 심각해졌다.

“우리가 정말로 원하는 것은 이런 모습일 겁니다.”
소피는 화이트보드의 빈 공간에 새로운 그림을 그렸다.

// 스크롤 관련 로직
{
  // 스크롤 이벤트 등록과 해제 로직이 여기에 함께!
}

// 데이터 패칭 관련 로직
{
  // 데이터 가져오기와 상태 업데이트 로직이 여기에 함께!
}

// 소켓 관련 로직
{
  // 소켓 연결과 해제 로직이 여기에 함께!
}

“관련 있는 코드끼리 묶여 있는 것. 이것이 우리가 지향해야 할 목표입니다. 이 기능 덩어리들을 자유롭게 다른 컴포넌트에서 가져다 쓸 수 있다면, 로직 재사용 문제도 자연스럽게 해결되겠죠.”

그녀는 세바스티안이 던진 철학적인 질문에, ‘개발자 경험’이라는 현실적인 렌즈를 들이댔다.
새로운 해결책은 단순히 기술적으로 우월할 뿐만 아니라, 개발자가 코드를 더 쉽게 읽고, 더 쉽게 수정하고, 더 쉽게 추론할 수 있도록 만들어야 했다.

댄은 두 사람의 말을 들으며, 문제의 윤곽이 완벽하게 맞춰지는 것을 느꼈다.
세바스티안이 지적한 ‘컴포넌트라는 틀의 한계’와, 소피가 지적한 ‘관심사 분리의 실패’.
이 두 가지가 바로 클래스 컴포넌트의 심장을 관통하는 아킬레스건이었다.

이제 목표는 명확해졌다.
컴포넌트라는 무거운 갑옷을 벗어던지고, 흩어진 로직들을 기능 단위로 다시 조립할 수 있는, 완전히 새로운 방법을 찾아야만 했다.

그날 이후, 리액트 팀의 회의실에는 전에 없던 깊은 정적과 뜨거운 논쟁이 공존하기 시작했다. 그들은 리액트의 근본을 뒤흔들, 위대하고도 위험한 여정의 첫걸음을 내딛고 있었다.