클래스(Class), 그 견고한 성벽

22025년 08월 17일3

댄의 눈앞에 떠오른 복잡한 UserProfile 코드는 빙산의 일각일 뿐이었다. 문제의 본질을 파고들기 위해선, 그는 리액트의 가장 기초적인 단위, 가장 작은 벽돌부터 다시 살펴봐야 했다.

그의 손이 키보드 위를 가볍게 스쳤다. 몇 줄의 코드가 화면에 그려졌다.

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

카운터(Counter).
리액트를 배우는 사람이라면 누구나 한 번쯤은 만들어보는, 가장 단순한 형태의 생명을 가진 컴포넌트.
이 짧은 코드 안에, 클래스라는 거인의 모든 유전자가 담겨 있었다.

class Counter extends React.Component

모든 것은 이 선언에서 시작되었다. 리액트가 제공하는 Component라는 위대한 설계도를 상속받아, ‘카운터’라는 새로운 존재를 정의하는 행위. 이것은 객체 지향 프로그래밍의 오랜 유산이자, 자바스크립트에 체계와 질서를 부여하려는 숭고한 시도였다.

constructor(props)super(props).

컴포넌트가 세상에 태어나는 순간, 반드시 거쳐야 하는 탄생의 의식. 개발자들은 주문처럼 이 두 줄을 써넣었다. 왜 필요한지 깊이 이해하지 못해도, 이것이 규칙이었기에 모두가 따랐다.

this.state = { count: 0 };

그리고 마침내, 컴포넌트의 심장이라 할 수 있는 ‘상태(state)’가 정의되었다. state는 평범한 객체였지만, 리액트의 생태계 안에서는 특별한 의미를 가졌다. 이 값이 변할 때, 리액트는 마법처럼 화면을 다시 그려 컴포넌트에 생명을 불어넣었다. count라는 이름의 상태는 ‘0’이라는 초기값을 품고 잠들었다.

render()

클래스의 여러 메서드 중 가장 신성한 영역. 이곳은 state라는 데이터를 받아, 눈에 보이는 UI, 즉 HTML 구조로 바꾸는 연금술이 일어나는 공간이었다. render 함수가 반환하는 JSX 코드가 바로 사용자가 화면에서 보게 될 결과물이었다.

마지막으로, this.setState({ count: this.state.count + 1 }).

잠든 상태를 깨우는 유일한 방법. 개발자는 절대로 this.state.count = 1 과 같이 직접 상태를 건드려서는 안 되었다. 반드시 setState라는 정해진 절차를 통해 리액트에게 상태 변경을 ‘요청’해야 했다. 그래야만 리액트가 변화를 감지하고, render 함수를 다시 호출해 주었다.

논리 정연했다. 체계적이었다.
과거, 제멋대로 날뛰던 ‘제이쿼리(jQuery)’ 코드에 비하면 이것은 가히 혁명적인 질서였다. 모든 것이 클래스라는 견고한 성벽 안에서 보호받고 관리되었다.

하지만 댄은 이 완벽해 보이는 구조 속에서 미세한 삐걱거림을 느꼈다.
단지 숫자를 하나 올리기 위해, 이 모든 의식과 절차를 거쳐야만 했다. constructor, super, this.state, this.setState

그것은 마치 작은 못 하나를 박기 위해, 잘 설계된 거대한 공작기계를 통째로 가동시키는 기분이었다.
견고함의 대가는 ‘무거움’이었다.

댄은 잠시 생각에 잠겼다. 이 코드에는 아직 드러나지 않은, 더 교활하고 근본적인 함정이 숨어 있었다. 그가 onClick 핸들러를 별도의 메서드로 분리하는 순간, 모든 초보 개발자들을 절망에 빠뜨리는 그 저주받은 단어가 모습을 드러낼 터였다.

바로 this였다.