Skip to content

리액트를 배우셨군요 유감입니다 1편 (어느 냉소적인 개발자의 리액트 입문서)

Published: at 오후 03:34

이 글은 리액트를 어쩔 수 없이 배워야만 하는 개발자들을 위한, 일종의 ‘스톡홀름 증후군 환자 지원 그룹’ 같은 글로 생각하시면 됩니다.

리액트를 배우는 과정을 ‘리액트 애도의 5단계’에 비유하는데, 이게 정말 기가 막힙니다.

1단계는 ‘부정’이죠.

”설마 이게 웹을 만드는 최선의 방법일 리가 없어.”

2단계는 ‘분노’입니다.

”아니 ‘Hello, World’ 하나 띄우는 데 파일이 왜 세 개나 필요한 건데?“

3단계는 ‘타협’이고요.

”음, 이 컴포넌트 하나에만 리액트를 써볼까…?“

4단계는 ‘우울’입니다.

”기능 개발보다 빌드 도구 설정하는 데 시간을 더 많이 쓰네… 내 node_modules 폴더 용량이 애플리케이션보다 크잖아.”

그리고 마지막 5단계는 바로 ‘수용’이죠.

”그래, 이게 내 인생이구나. 뭐, 취업은 잘 되겠지.”

이 강연, 아니 이 책의 신랄하면서도 유머러스한 시선을 따라가며, 우리가 왜 리액트를 증오하면서도 결국 배우게 되는지 그 웃픈 이유를 함께 파헤쳐 볼까 합니다.

리액트는 대체 왜 태어났을까

2013년에 페이스북에는 큰 문제가 하나 있었는데요.

서버가 불타거나 사용자가 떠나가는 그런 심각한 문제는 아니었습니다.

바로 ‘알림 카운터 숫자가 가끔 틀리는’ 문제였죠.

새 메시지가 3개라고 해서 클릭했더니 2개만 보이는 바로 그 상황, 그게 바로 리액트를 탄생시킨 세기의 위기였습니다.

당시 웹 세상은 IE8이 아직도 쓰이고, 모바일 웹은 거의 작동하지 않던 시절이었는데요.

그런 시대에 페이스북의 최우선 과제는 바로 그 작은 빨간 원 안의 숫자를 정확하게 맞추는 것이었죠.

‘jQuery 스파게티 코드’라는 거대한 신화

리액트 전도사들이 퍼뜨린 가장 큰 거짓말 중 하나는 바로 ‘jQuery 스파게티 코드’ 신화인데요.

그들이 주장하는 ‘유지보수 불가능한 스파게티 코드’가 실제로는 어땠는지 한번 보시죠.

// 10년간 인터넷을 지탱해 온 바로 그 '스파게티 코드'
$("#submit-button").on("click", function () {
  var username = $("#username").val();
  $.post("/api/login", { username: username }, function (response) {
    if (response.success) {
      $("#welcome-message").text("Welcome, " + response.name);
      $("#login-form").hide();
      $("#dashboard").show();
    } else {
      $("#error-message").text(response.error).show();
    }
  });
});

정말 끔찍하지 않나요?

코드를 읽는 것만으로도 무슨 일을 하는지 바로 이해가 되다니 말이죠.

빌드 과정도, 트랜스파일도, 가상돔 비교 알고리즘도 없이 그냥 ‘코드가 말하는 그대로’ 동작합니다.

이제 이와 똑같은 기능을 하는 2013년 당시의 리액트 코드를 한번 보실까요.

var LoginComponent = React.createClass({
  getInitialState: function () {
    /* ... */
  },
  handleUsernameChange: function (e) {
    /* ... */
  },
  handleSubmit: function (e) {
    /* ... */
  },
  render: function () {
    if (this.state.isLoggedIn) {
      return React.createElement("div", null /* ... */);
    }
    return React.createElement(
      "form",
      { onSubmit: this.handleSubmit } /* ... */
    );
  },
});

이 아름다운 모습을 보세요.

똑같은 일을 하는 데 코드 양은 3배로 늘어났고, 이제 우리는 컴포넌트 생명주기, 상태 관리, 이벤트 바인딩, React.createElement 문법 같은 것들을 추가로 이해해야만 하죠.

하지만 괜찮습니다.

덕분에 페이스북의 알림 카운터는 정확해졌으니까요!

조직의 문제를 기술의 문제로

사실 페이스북의 알림 문제는 기술의 문제가 아니었는데요.

페이스북의 규모가 너무 커지면서 서로 다른 팀들이 각자의 코드를 건드리다 보니 충돌이 났던 ‘조직의 문제’였죠.

하지만 그들은 “우리 좀 더 협력을 잘해볼까?”라고 말하는 대신, “웹 개발의 개념 자체를 처음부터 다시 만들자!”라고 외쳤습니다.

열쇠를 못 찾겠다고 자기 집을 불태워버린 격이죠.

결국 리액트는 페이스북이 가진 특정 문제를 해결하기 위한 특정 솔루션이었는데요.

어쩌다 보니 그 솔루션이, 그 문제가 있든 없든 상관없이, 모두의 솔루션이 되어버린 겁니다.

자바스크립트 피로감이라는 대재앙

2009년에는 메모장 하나만으로 웹사이트를 만들 수 있었는데요.

2015년쯤 되자 ‘Hello, World’ 하나를 화면에 띄우기 위해 컴퓨터 과학 학위가 필요할 지경이 되었습니다.

jQuery 하나면 충분했던 황금기는 지나가고, Angular, Backbone, Ember 같은 프레임워크들이 등장하며 ‘프레임워크 대전쟁’이 시작되었죠.

여기에 Node.js가 등장하면서 상황은 걷잡을 수 없이 복잡해졌습니다.

프론트엔드 개발자들이 npm, 빌드 도구, 모듈 시스템, 트랜스파일러 같은 서버 생태계의 도구들을 손에 넣게 되면서 판도라의 상자가 열린 거죠.

npm은 처음엔 코드 공유를 쉽게 하자는 순수한 의도로 시작했는데요.

하지만 지금은 npm install react 명령어 한 번에 수백 개의 패키지가 설치되는 시대가 되었습니다.

수백 개의 잠재적 실패 지점, 수백 개의 잠재적 보안 취약점이 생겨난 셈이죠.

2016년에 있었던 ‘left-pad’ 사건을 기억하시나요?

고작 11줄짜리 문자열 패딩 라이브러리가 npm에서 삭제되자 Babel, React를 포함한 인터넷의 절반이 멈춰 섰습니다.

그 누구도 11줄짜리 패딩 함수를 직접 만들지 않게 된 거죠.

빌드 도구의 역사도 눈물 없이는 볼 수 없는데요.

Grunt는 설정이 너무 복잡했고, Gulp는 스트림 기반으로 조금 나아졌지만 여전히 복잡했습니다.

그리고 Webpack이 등장해서 모든 것을 해결해 줄 것 같았지만, 결국 더 거대한 복잡성을 낳았죠.

리액트가 JSX를 들고나오면서 HTML은 더 이상 순수한 HTML이 아니게 되었는데요.

class가 자바스크립트의 예약어라는 이유로 className이 되어버린 순간, 우리의 HTML은 ‘HTML인 척하는 자바스크립트’가 되어버린 겁니다.

이 모든 과정이 바로 ‘자바스크립트 피로감’입니다.

너무나 많은 도구들이 너무나 빠르게 변하고, 각자 자기가 필수라고 주장하는 바람에, 정작 무언가를 만들기보다는 새로운 도구를 배우는 데 모든 시간을 쏟게 되는 현상이죠.

가상돔이라는 아름다운 거짓말

가상돔(Virtual DOM)은 리액트의 왕관에 박힌 보석이자, 리액트를 다른 모든 것보다 빠르게 만들어준다는 ‘킬러 기능’인데요.

하지만 이건 리액트가 스스로 만들어낸 문제를 해결하기 위한 해결책에 가깝습니다.

자기 다리를 부러뜨려 놓고 정말 멋진 목발을 발명한 것과 같죠.

가상돔의 작동 방식을 거실 가구 재배치에 비유하면 이렇습니다.

일반적인 방법은 그냥 옮겨야 할 가구만 옮기는 건데요.

리액트의 방식은 이렇습니다.

1. 현재 거실의 완벽한 청사진을 그린다.

2. 바꾸고 싶은 거실의 청사진을 또 그린다.

3. 두 청사진을 아주 세세하게 비교한다.

4. 최소한의 변경 사항 목록을 만든다.

5. 그리고 오직 그때서야 가구를 옮긴다.



리액트 개발자들은 이게 더 효율적이라고 말하는데요.

”가상돔이 실제돔보다 빠르다!”는 주장은 리액트의 가장 큰 슬로건이었습니다.

하지만 브라우저 벤더들이 수십 년간 최적화해 온 실제돔 조작보다, 브라우저 위에서 돌아가는 자바스크립트 라이브러리가 더 빠를 수 있다는 생각 자체가 좀 이상하지 않나요?

// "느리다"고 알려진 직접적인 DOM 조작
document.getElementById("counter").textContent = "1"; // 약 0.01ms 소요

// "빠르다"고 알려진 리액트 방식
// 1. 가상돔 트리 생성: ~0.1ms
// 2. 가상돔 트리 비교(diffing): ~0.1ms
// 3. 실제돔 업데이트: ~0.01ms
// 총합: ~0.21ms

단순한 업데이트에서는 직접 DOM을 조작하는 게 압도적으로 빠릅니다.

물론 리액트는 여러 업데이트를 묶어서 처리(batching)하기 때문에 복잡한 상황에서는 더 효율적일 수 있는데요.

하지만 웹 애플리케이션의 99%는 그 정도의 복잡성을 가지고 있지 않죠.

가상돔의 진짜 목적

가상돔의 진짜 목적은 ‘성능’이 아니라 ‘개발자 경험’에 있는데요.

가상돔 덕분에 우리는 이렇게 ‘선언적으로’ UI를 작성할 수 있게 되었습니다.

return <div>{user.name}</div>;

”여기에 사용자 이름이 보여야 해”라고 선언만 하면, 리액트가 그 상태를 만들기 위한 구체적인 방법(DOM 조작)을 알아서 처리해주는 거죠.

이건 분명히 더 우아하고 생각하기 쉬운 방식입니다.

하지만 그 대가로 우리는 더 느리고, 더 복잡하며, 우리와 브라우저 사이에 거대한 추상화 계층을 두게 된 거죠.

Svelte 같은 프레임워크는 가상돔 없이도 선언적 UI를 구현할 수 있다는 것을 증명했는데요.

Svelte는 컴파일 시점에 효율적인 DOM 조작 코드를 생성해내기 때문에, 런타임 오버헤드가 거의 없고 리액트보다 훨씬 빠릅니다.

마치며

이 책은 리액트가 악마의 산물이라고 말하는 게 아닌데요.

단지 ‘과잉 기술’이라는 점을 지적하고 있죠.

액자 하나를 걸기 위해 슬레지해머를 사용하는 것과 같다는 겁니다.

물론 액자는 벽에 걸리겠지만, 벽도 함께 박살 나겠죠.

리액트는 페이스북 같은 거대하고 복잡한 애플리케이션에는 합리적인 선택일 수 있습니다.

하지만 대부분의 웹사이트는 그 정도의 복잡성을 필요로 하지 않죠.

우리는 리액트가 있기 전에도 훌륭한 웹사이트를 만들어왔고, 그 간단했던 방법들이 틀렸던 것이 아닙니다.

하지만 어쩌겠어요.

리액트는 이미 이겼고, 우리는 그걸 배워야만 합니다.

이 책의 냉소적인 시선을 통해 우리가 잃어버린 단순함을 한번쯤 되돌아보고, 우리가 지금 다루고 있는 기술의 본질을 더 깊이 이해하게 되는 계기가 되었으면 좋겠네요.

여러분의 영혼에 신의 가호가 있기를.