개발자들의 고통은 충분히 감지되었다. this
의 혼란, 로직 재사용의 어려움, 뒤섞인 관심사. 이 모든 것은 개발자 경험(DX, Developer Experience)을 저해하는 명백한 문제였다.
하지만 리액트 팀의 고민은 거기서 그치지 않았다. 세바스티안 마크바게는 개발자뿐만 아니라, ‘기계’의 입장에서 문제를 바라보고 있었다. 그가 주목한 것은 자바스크립트 코드가 브라우저에 전달되기까지의 과정, 즉 컴파일과 번들링 단계였다.
“클래스는… 최적화에 불리합니다.”
어느 날 오후, 세바스티안이 자신의 모니터를 팀원들에게 보여주며 입을 열었다. 그의 화면에는 같은 기능을 하는 두 개의 코드 조각이 나란히 떠 있었다. 하나는 클래스로, 다른 하나는 단순한 함수로 작성된 코드였다.
“우리 눈에는 클래스가 더 체계적으로 보일지 모릅니다. 하지만 자바스크립트 엔진이나 번들러 같은 도구들에게, 클래스는 예측하기 어려운 검은 상자와 같습니다.”
그의 설명이 이어졌다.
클래스는 내부에 수많은 메서드와 상속 관계, 그리고 보이지 않는 this
문맥을 품고 있다. 코드를 분석하고 최적화하려는 도구 입장에서, 어떤 메서드가 실제로 사용될지, 어떤 메서드가 상속 과정에서 덮어쓰일지 미리 파악하기가 매우 까다로웠다.
특히 ‘죽은 코드 제거(Dead Code Elimination)’ 과정에서 문제가 두드러졌다. 죽은 코드 제거란, 최종 결과물에 포함될 필요가 없는, 즉 전혀 사용되지 않는 코드를 빌드 과정에서 삭제하여 파일 크기를 줄이는 중요한 최적화 기술이다.
“이 클래스의 unusedMethod
를 보세요.”
세바스티안이 클래스 코드의 한 부분을 가리켰다.
“우리 눈에는 이 메서드가 어디에서도 호출되지 않는다는 게 보입니다. 하지만 번들러는 확신할 수 없습니다. 어딘가 다른 곳에서 동적으로 이 메서드를 호출할 가능성을 배제할 수 없기 때문이죠. 그래서 결국, 이 불필요한 코드 조각을 최종 번들에 포함시키는 경우가 많습니다.”
반면, 함수로 작성된 코드는 훨씬 투명했다.
입력과 출력이 명확하고, 외부 세계와의 상호작용이 적은 ‘순수 함수’에 가까울수록, 도구들은 코드를 분석하고 최적화하기가 훨씬 용이했다. 사용되지 않는 함수는 안심하고 제거할 수 있었다.
“파일 크기만의 문제가 아닙니다.”
세바스티안은 말을 이었다.
“컴포넌트의 리렌더링 방식에도 영향을 줍니다. 클래스 인스턴스는 매번 새로 생성하는 비용이 비싸기 때문에, 우리는 인스턴스를 유지하면서 내부 상태만 바꾸는 방식을 택했죠. 하지만 이 방식은 미래에 우리가 도입하고 싶은 더 진보된 렌더링 기법에 걸림돌이 될 수 있습니다.”
그의 시선은 이미 동시성(Concurrency) 렌더링과 같은, 당시로서는 상상에 가까웠던 미래 기술을 향해 있었다. 렌더링 과정을 잠시 멈추거나, 우선순위를 바꾸거나, 심지어는 일부 렌더링 결과를 폐기하는 등의 유연한 제어를 위해서는, 컴포넌트가 더 가볍고 예측 가능한 단위가 되어야만 했다. 무거운 클래스 인스턴스는 그러한 변화에 족쇄가 될 터였다.
결국, 문제는 하나로 모아졌다.
클래스는 인간 개발자에게도 혼란스러웠지만, 코드를 처리하는 기계에게도 비효율적이었다.
댄은 세바스티안의 설명을 들으며, 문제의 조각들이 거대한 그림으로 완성되는 것을 느꼈다.
개발자 경험의 문제.
유지보수성의 문제.
그리고 이제, 코드 최적화와 미래 기술 도입의 문제까지.
클래스 컴포넌트라는 견고한 성벽은 이제 리액트의 발전을 가로막는 명백한 장애물이었다. 이 성벽을 그대로 둔 채 내부를 수리하는 것만으로는 부족했다.
성벽을 허물고, 완전히 새로운 구조의 도시를 설계해야 할 때가 온 것이다.
리액트 팀은 마침내, 그들이 풀어야 할 문제가 무엇인지 명확하게 정의할 수 있었다. 그들의 눈앞에는 넘어야 할 세 개의 거대한 벽이 서 있었다.