1P by GN⁺ | ★ favorite | 댓글 1개
  • Mux는 mux.com과 docs.mux.com을 React Server Components로 옮기며, 서버와 클라이언트 실행 경계를 나누는 일이 번들 크기와 hydration 비용에 직접 영향을 준다는 점을 확인함
  • RSC는 컴포넌트가 서버에서 직접 데이터를 가져오고 결과를 스트리밍할 수 있게 해, 느린 데이터 호출이 있어도 화면 일부를 먼저 보여줄 수 있음
  • 실제 마이그레이션의 가장 큰 걸림돌은 CSS-in-JS 미지원, Server Components의 React Context 제약, 서버/클라이언트 경계를 계속 추적해야 하는 복잡성이었음
  • Next.js 13의 app directory에서는 기본이 Server Component이며, 루트의 use client를 점차 아래로 내리는 방식으로 점진적 도입이 가능함
  • Suspense, loading.js, 서버 전용 라이브러리 유지, server-only 같은 패턴은 성능 이득이 필요한 곳에만 신중히 적용해야 하며, 팀의 인지 비용도 함께 계산해야 함

Mux가 RSC로 옮긴 범위

  • Mux는 문서 사이트 재구성과 브랜드 변경을 진행하면서 mux.comdocs.mux.comServer Components로 이동함
  • React Server Components는 실제 코드베이스에서도 적용 가능하고 쓸 가치가 있을 수 있지만, 제약과 복잡성도 함께 따라옴
  • 이 경험은 RSC가 왜 필요한지, 어디에 적합한지, 어떤 상황에서 까다로운지, 실제 코드베이스에 어떻게 점진적으로 들여올 수 있는지를 중심으로 정리됨

CSR, SSR/SSG 이후 RSC가 겨냥한 문제

  • 초기 서버 렌더링 방식은 PHP 같은 기술로 서버에서 데이터를 가져오고 무거운 CPU 작업을 처리한 뒤, 클라이언트에는 가벼운 HTML을 전달했음
  • CSR/SPA는 렌더링 코드를 JavaScript로 클라이언트에 보내 상호작용을 빠르게 처리할 수 있게 했지만, 검색 엔진이 JavaScript를 실행하지 않거나 서버에 비밀값을 유지해야 하거나 저성능 기기·느린 연결을 만나는 상황에서는 약점이 드러남
  • SSR/SSG는 Next.js와 Gatsby 같은 도구를 통해 서버에서 HTML과 JavaScript를 함께 생성해 클라이언트로 보내는 방식임
    • 사용자는 HTML을 즉시 볼 수 있음
    • JavaScript가 로드되면 사이트가 상호작용 가능해짐
    • 검색 엔진도 HTML을 읽을 수 있음
  • 기존 SSR/SSG에도 비용은 남아 있음
    • 페이지 생성에 쓰인 JavaScript 대부분을 클라이언트로 보내고, 클라이언트가 다시 실행해 HTML과 결합하는 hydration이 필요함
    • 서버 렌더링이 느린 데이터베이스 호출이나 많은 코드 실행 때문에 오래 걸리면 사용자가 기다려야 함

React Server Components가 바꾸는 지점

  • React Server Components는 클라이언트가 아니라 서버에서 실행되는 React 컴포넌트임
  • RSC 지원 프레임워크는 코드가 실행될 위치를 명시적으로 나눌 수 있게 함
    • Server Components: 서버에서만 실행되어야 하는 코드
    • Client Components: 클라이언트에서 실행되어야 하는 코드
  • 실행 위치가 분리되면 클라이언트로 보내는 JavaScript가 줄고, hydration 중 수행해야 하는 작업도 줄어듦
  • Server Component는 컴포넌트 내부에서 직접 데이터를 가져올 수 있음
    • Node 라이브러리나 fetch를 사용할 수 있음
    • 페이지 수준에서 getServerSideProps로 데이터를 한꺼번에 가져와 props로 계속 내려보내는 방식을 줄일 수 있음
    • useEffect로 복잡한 로딩 상태를 관리하는 경우도 줄어듦
  • 데이터 가져오기가 끝난 Server Component는 결과를 클라이언트로 스트리밍할 수 있음
    • 느린 컴포넌트를 기다리는 동안 나머지 사이트를 먼저 보여줄 수 있음
  • 클라이언트 사용자 동작에 대응해 서버에서 데이터를 가져오고 응답을 스트리밍하는 방식도 가능하지만, 이는 엄밀히는 RSC가 아니라 React Actions에 해당함

RSC가 까다로운 부분

  • CSS-in-JS는 현재 Server Components에서 동작하지 않음
    • Mux의 RSC 전환에서는 styled-components에서 Tailwind CSS로 옮기는 작업이 가장 큰 비중을 차지함
    • CSS-in-JS에 크게 의존한 코드베이스라면 별도 마이그레이션 작업이 필요함
  • React Context는 Client Components에서만 접근할 수 있음
    • Server Components 사이에서 props 없이 데이터를 공유하려면 일반 모듈을 사용해야 할 가능성이 큼
    • React 애플리케이션의 특정 하위 트리에만 데이터를 제한하는 좋은 메커니즘은 Server Components에 없음
  • Mux 문서 사이트에서는 Context 사용 영역이 상호작용이 많아 어차피 클라이언트로 보내야 했기 때문에 큰 문제가 되지 않았음
  • 마케팅 사이트에서는 테마 공유가 문제가 됨
    • pre-footer의 각 컴포넌트가 녹색 배경 위에 있다는 사실을 알아야 어두운 녹색 테두리를 쓸 수 있었음
    • Context 대신 CSS custom properties를 적극 활용해 우회함
  • RSC는 실행 위치와 데이터 가져오기 방식에 유연성을 주지만, 그만큼 복잡성도 늘어남
    • 새 개발자는 “무엇이 서버에서 실행되고 무엇이 클라이언트에서 실행되는지”를 계속 확인해야 함
    • PR마다 불필요하게 클라이언트로 보내진 코드에 대한 피드백이 발생함
    • 개발 중 서버 로그인지 브라우저 로그인지 확인하려고 console.log를 넣는 일이 잦았음
    • 캐싱도 별도의 복잡성을 더함

Next.js 13에서 RSC를 쓰는 기본 방식

  • 작성 시점 기준 프로덕션 준비가 된 RSC 구현은 Next.js 13의 app directory
  • Next.js 13 app directory에서는 기본적으로 작성하는 컴포넌트가 Server Component
    • 기본 상태에서는 페이지 코드가 클라이언트로 보내지지 않음
    • 클라이언트에는 HTML만 전달됨
  • Server Component에 async를 붙이면 컴포넌트 내부에서 데이터를 가져올 수 있음
  • 느린 데이터 가져오기가 있는 Server Component는 React.Suspense로 감쌀 수 있음
    • 클라이언트에는 fallback UI가 먼저 표시됨
    • 서버가 데이터를 가져오고 렌더링을 마치면 결과 컴포넌트가 스트리밍됨
  • Suspense boundary는 데이터 스트리밍뿐 아니라 사용자 상호작용에 따라 특정 영역의 hydration 우선순위를 조정하는 selective hydration에도 활용될 수 있음
  • 클라이언트에서 실행되어야 하는 코드는 파일 상단에 "use client"를 추가함
    • onClick 리스너나 useState처럼 클라이언트 상태와 상호작용이 필요한 컴포넌트에 사용함
    • "use client"가 붙은 컴포넌트가 import하는 모든 컴포넌트도 클라이언트로 보내짐
  • RSC를 지원하지 않는 라이브러리는 Client Component에서 import해 클라이언트 번들에 포함시킬 수 있음
    • 예시는 @mux/mux-player-react를 감싸는 ClientMuxPlayer 컴포넌트임

Server Component와 Client Component 선택 기준

  • Server Components는 클라이언트로 보낼 필요가 없는 코드에 적합함
    • 블로그 글 본문 렌더링
    • 코드 블록 구문 강조처럼 비용이 큰 작업
    • 데이터 가져오기
  • Client Components는 사용자의 입력에 반응하거나 시간이 지나며 상태가 바뀌는 UI에 적합함
    • useState
    • 이벤트 리스너
    • 클라이언트 측 상호작용
  • 전체 앱을 Client Components로 만들면 기존 SSR 프레임워크와 비슷하게 동작함
  • 전체 앱을 한 번에 Server Components로 바꿀 필요는 없고, 이득이 큰 위치부터 점진적으로 도입할 수 있음

실제 코드베이스에 점진적으로 들여오는 3단계

  • Mux가 사용한 플레이북은 세 단계임
    • 앱 루트에 "use client" 지시어를 추가함
    • 지시어를 렌더링 트리에서 가능한 한 아래로 이동함
    • 성능 문제가 드러날 때 고급 패턴을 적용함
  • 1단계에서는 Next.js 13의 최상위 page.tsx"use client"를 추가해 기존처럼 동작하게 함
  • 서버 측 데이터 가져오기가 필요하면 Server Component를 Client Component의 부모로 추가함
    • Server Component가 데이터를 가져옴
    • 가져온 데이터를 Client Component에 props로 전달함
    • 기존 getServerSideProps 역할을 대체할 수 있음
  • 2단계에서는 "use client"를 최상위 컴포넌트에서 자식 컴포넌트로 옮김
    • 클라이언트 코드가 필요 없는 <Title />에서는 지시어를 제거해 순수 HTML로 보낼 수 있음
    • 클라이언트 코드가 필요한 <Player />에서는 오류가 발생하므로 "use client"를 유지함
  • 이 방식은 새 컴포넌트와 기존 리팩터링 대상에서 Server Components를 고려하게 만들고, 번들 크기를 일부 줄이는 데 도움을 줌

성능 문제가 있을 때 적용한 패턴

  • Mux 문서 사이트는 대부분 정적으로 생성되지만, changelog sidebar는 CMS에서 가져옴
  • sidebar를 Suspense로 감싸면 CMS fetch가 끝날 때까지 앱 나머지 부분이 기다릴 필요가 없음
  • Next.js 13의 loading.js 관례도 Suspense와 streaming을 내부적으로 사용함
  • 큰 라이브러리를 서버에 남기려면 Client Components와 Server Components 배치를 조정해야 함
    • 예시로 구문 강조 라이브러리 Prism을 서버에 유지함

Client Component 안에 Server Component를 섞는 법

  • Client Component가 import한 컴포넌트는 함께 Client Component가 됨
  • Server Component를 Client Component의 자식으로 두고 싶다면 import하지 말고 children이나 props로 전달해야 함
    • Server Component는 서버에서 렌더링됨
    • 직렬화된 결과가 Client Component로 전달됨
  • 잘못된 방식은 Client Component 파일에서 Server Component를 직접 import하는 것임
  • 올바른 방식은 가장 가까운 상위 Server Component로 올라가서 Client Component에 Server Component를 자식이나 prop으로 넘기는 것임

한 파일을 서버/클라이언트 반반으로 나눌 수는 없음

  • 한 파일의 절반은 Server Component, 절반은 Client Component로 만드는 방식은 불가능함
  • Mux는 기능을 두 파일로 나누는 패턴을 자주 사용함
    • CodeBlock.server.js: 큰 구문 강조 라이브러리를 import하고 서버에서 렌더링함
    • CodeBlock.client.js: useStateonClick을 사용해 사용자가 코드 예제를 전환할 수 있게 함
  • 서버에서 렌더링한 예제들은 props로 Client Component에 전달되므로 서버 전용 작업이 클라이언트 번들로 넘어가지 않음
  • index.js에서 CodeBlock.server.js를 다시 export하면 사용자는 CodeBlock만 import해도 내부 서버/클라이언트 분리를 의식하지 않아도 됨

서버에서만 실행되게 보장하는 방법

  • 초기에는 개발 중 console.log를 추가해 로그가 서버에서 나오는지 브라우저에서 나오는지 확인함
  • 서버 전용 코드가 번들에 포함되지 않도록 보장하려면 server-only package를 import할 수 있음
  • server-only는 큰 라이브러리나 비밀 키가 잘못된 위치로 이동하지 않게 하는 데 유용함
  • Next.js는 환경 변수가 실수로 브라우저 번들에 포함되는 것을 막는 보호 장치를 제공함
  • 파일 상단의 server-only는 유지보수에도 도움을 줌
    • 관리자가 해당 파일이 서버에서 실행된다는 사실을 바로 알 수 있음

도입 여부를 판단할 때의 비용과 이득

  • React Server Components는 무료로 얻는 기능이 아님
  • 비용에는 CSS-in-JS와 React Context 제약뿐 아니라 다음이 포함됨
    • 서버와 클라이언트 실행 위치 이해
    • hydration 이해
    • 인프라 비용
    • Client Components와 Server Components 혼합으로 인한 코드 복잡성
  • 복잡성은 버그가 들어오고 코드 유지보수성이 낮아질 수 있는 표면을 늘림
  • 프레임워크는 복잡성을 줄여주지만 제거하지는 않음
  • 기대할 수 있는 이득은 다음과 같음
    • 더 작은 번들 크기
    • 더 빠른 실행
    • SEO에 중요한 성능 개선
    • 복잡하고 데이터가 많은 사이트를 위한 고급 데이터 로딩 패턴
  • 팀이 추가 인지 비용을 감당할 준비가 되어 있고 성능 이득이 충분하다면 RSC가 적합할 수 있음

댓글과 토론

Hacker News 의견들
  • 서버 사이드 렌더링에서는 클라이언트가 바로 볼 수 있는 HTML을 받음
    이것도 느꼈는데, 서버에 평문 파일을 올리면 브라우저로 꽤 빨리 전달됨
    .css로 끝나는 또 다른 평문 파일을 올리면 브라우저가 처리 방법을 알고 있어서 첫 화면의 요소들이 움직이고 꽤 보기 좋아질 수도 있음
    멋진 요령이긴 하지만, 첫 화면에서 읽을 수 있는 유용한 콘텐츠보다는 여전히 부차적임

    • 동생이 올해 웹 개발을 배우기 시작했는데, HTML을 HTTP로 보낼 수 있다고 알려주자 크게 놀라더라
    • 브라우저는 원래 하이퍼텍스트 클라이언트로 시작했는데, 어느새 그 안에서 맞춤형 하이퍼텍스트 클라이언트 같은 것까지 구현하는 애플리케이션 플랫폼으로 진화해 버림
      “하이퍼텍스트를 더 강력하게 만들 기능을 추가하자”가 어쩌다 “이제 이 크고 일관성 없는 기능 더미로 쓸 만한 애플리케이션은 알아서 구현하세요”가 됐는지 모르겠음
    • 다음엔 페이지별로 코드를 실행하는 텍스트 파일도 내려보낼 수 있다고 말하려는 건가?
    • 그런 기술은 잃어버린 줄 알았음
  • RSC로 들어가기 전에 잠깐 멈추는 게 좋음
    뭘 만들려 하든 진짜 풀스택 프레임워크나 고전적인 웹 프레임워크에서 훨씬 쉽고 빠르고 확장성 있게 처리 가능함
    Rails/Django/Laravel/…에 Turbolinks/Htmx/…를 붙이거나, 약간의 클라이언트 쪽 JavaScript만 뿌려도 됨
    Elixir/Phoenix를 안다면 여러 장점을 함께 가져갈 수도 있음
    아무리 많은 사람이 트윗하더라도 RSC로 더 내려가지는 말아야 함
    업계 경력 10년 미만인 사람들이 예전 vanilla PHP 사이트에서 겪었던 기본 문제들을 다시 밟게 될 것이고, 이미 React 컴포넌트 안에 인라인 SQL 훅을 넣은 것도 봤음
    이번엔 우발적 복잡성까지 훨씬 더 얹혀 있음
    제정신을 유지하고, 실제 제품을 빨리 출시해서 람보르기니 값을 벌면 됨

    • RSC는 CORBA가 떠오름
      CORBA는 로컬 컴포넌트와 원격 컴포넌트를 섞는 방식이고, 성숙했으며 여러 언어에서 동작함
      그런데 왜 모두가 쓰지 않을까? 오늘날 대부분의 개발자는 들어본 적도 없을 것임
      또 다른 분산 컴포넌트 아키텍처를 만들고 싶다면 CORBA와 그 후손들이 왜 널리 자리 잡지 못했는지 공부해야 함
      힌트는 숨겨진 컴포넌트 경계가 숨겨진 복잡성을 만든다는 것임
      한편 “서버 렌더링 HTML”과 “클라이언트 렌더링 HTML” 진영은 둘 다 잘 굴러가고 있음
      웹 프로젝트마다 두 선택지를 모두 쓸 수 있다는 건 꽤 행운이라고 봄
      RSC 작업이 순수 클라이언트 렌더링 앱에 대한 React 지원을 흐리지 않았으면 함
      1. https://en.wikipedia.org/wiki/Common_Object_Request_Broker_A...
    • Elixir/Phoenix를 안다면 정말 맞는 말임
      완전한 진보적 리치 프런트엔드 앱이 꼭 필요한 게 아니라면, 대부분은 LiveView가 최소한의 JavaScript로 서버 사이드 컴포넌트를 통해 다 처리해 줌
      그뿐 아니라 사용자마다 서버 쪽 스레드가 붙어서, 명시적인 JavaScript 핸들러를 쓰지 않아도 사용자의 프런트엔드로 변경 사항을 능동적으로 밀어 넣을 수 있음
    • 정말 이 말이 맞음
      업계가 10년 주기 기억상실에 걸린 것 같음
      서버 렌더링 UI에서 멀어진 데는 아주 타당한 이유가 있었음
      물론 검색 엔진 최적화 논리는 항상 있지만, 그게 걱정이면 그냥 전통적인 서버 템플릿 사이트를 만들면 됨
      대다수 단일 페이지 앱은 SSR과 그 복잡성을 전혀 필요로 하지 않음
    • Mux 직원인데, 재미있는 사실로 Elixir는 처음부터 우리 인프라의 핵심 구성요소였음
      초기 대시보드 인터페이스는 전부 Phoenix로 렌더링했고, 정말 고급 클라이언트 상호작용이 필요한 페이지만 개별적으로 React를 포함했음
      첫 제품이 분석 대시보드였기 때문에 그런 상황이 곧 거의 전체 대시보드가 되었고, 고객에게 노출하던 API를 활용해 완전한 단일 페이지 앱으로 옮기는 게 자연스러웠음
      그때가 2016년이라 LiveView는 없었지만, 지금 그 제품을 다시 만든다 해도 다른 결정을 했을지는 확신이 없음
      블로그 글은 공개 마케팅 사이트를 구동하는 애플리케이션에 대한 것이고 요구사항이 꽤 다르지만, 우리도 Elixir/Phoenix를 쓰고 좋아한다는 점은 말하고 싶었음
  • 내가 늙어가는 것 같음
    요즘 프레임워크는 너무 크고 복잡함
    단순한 웹 “Hello world”에도 거대한 빌드와 컴파일 파이프라인이 필요하고, 이제는 서버 사이드 컴포넌트까지 붙음
    오버헤드가 어느 정도인지 정말 궁금함
    Hello world 예제를 실행하려고 프런트엔드와 백엔드 프레임워크 코드가 몇 겹이나 지나가는지 모르겠음
    나는 F5만 누르면 다시 빌드되는 10KB짜리 단순 컴포넌트 프레임워크로 돌아가겠음

    • 이런 프레임워크들은 단순한 Hello world 앱을 해결하려고 만들어진 게 아님
    • 많은 웹사이트는 그냥 HTML과 CSS만으로 만들었을 때 모든 면에서 더 나아질 것 같아서 답답함
      나중에 생길지도 모르는 화려한 기능을 위해 너무 일찍 최적화하는 셈임
      닭장을 짓기 전에 코어 샘플을 뽑고 지진파 모델링을 하려면 엔지니어를 고용해야 한다고 말하는 것과 비슷함
    • 바로 그래서 복잡성에서 벗어나려는 움직임이 보이는 것임
      젊은 개발자들이 이제 보기 시작했음
      우리가 SOAP과 XML 같은 끔찍한 것들을 버리고 더 단순하고 쓰기 편한 기술로 옮겼듯, 이번 세대도 다시 복잡성은 해롭다는 걸 배우는 중임
      어쩌면 새 세대가 다시 엉망으로 만들기 전 몇 년 동안이라도 소프트웨어 개발이 다시 재미있어질지도 모름
    • 이런 식의 관점은 정말 짜증남
      파이프라인은 특정 사용 사례에 맞춰 필요한 만큼 복잡하거나 단순할 수 있음
      정적 파일만으로 만들 수도 있고, esbuild 명령 하나를 쓰는 작은 Makefile로도 가능하며, 플러그인 30개짜리 거대한 Webpack 설정도 가능함
      만들려는 대상의 요구와 복잡도에 맞춰 얼마든지 선택할 수 있음
      또한 도구를 간단한 Hello world 만들기 쉬운지로 평가하는 건, 일이 실제로 그런 앱을 만드는 경우에나 유용함
    • 이 블로그 글은 내가 본 대부분의 글보다 SSR을 잘 다뤘음
      SSR이나 그 파생물이 대화에 나오면 항상 불필요하게 더 복잡해지는 건 아닌지 자문하게 됨
      React 서버 렌더링 컴포넌트는 너무 멀리 간 것처럼 들리고, 자연스러운 개발자 경험의 흐름에 어긋남
      애플리케이션 복잡도가 두 배가 되고, 코딩 함정이 늘어나 개발자들이 느려지고 혼란스러워지는데 얻는 건 작은 성능 향상뿐이라면 그만한 가치가 있는지 의문임
      예전의 PHP 사이트와 단일 페이지 앱 없는 Rails 앱도 오랫동안 잘 동작해 왔음
  • Next.js와 새 app 디렉터리 구조로 새 애플리케이션을 만들면서 겪은 바가 있음
    첫째, 무엇이 서버에서 일어나고 무엇이 클라이언트에서 일어나는지 추론하기 어려움
    알려면 조사해야 하는데, 코드를 빠르게 작성할 때는 대부분 크게 신경 쓰지 않게 됨
    작은 변경 하나로 페이지의 큰 부분이 갑자기 서버에서 클라이언트로 옮겨가려는 일이 생기기 쉬움
    최종 출시 전에 페이지별로 조심스럽고 시간 많이 드는 검증을 해야 할 걸 알고 있고, 그게 별로임
    둘째, 기존 React 라이브러리 상당수는 훅을 쓰기 때문에 클라이언트에서 실행된다고 가정
    이 때문에 코드가 클라이언트로 끌려올 수 있음
    새 패러다임과 싸우는 목적은 빠른 로딩과 검색 엔진 최적화를 위한 서버 사이드 렌더링인데, 가져온 라이브러리가 잘 협조하지 않으면 완전히 낭비됨
    셋째, 새 Next.js app 디렉터리 패러다임에는 버그가 있음
    아직 새롭고 매우 복잡해서 동적 라우트와 병렬 라우트, 그리고 그 상호작용이 완전히 깨질 수 있음
    직접 Next.js GitHub에 이슈를 올렸고 “나도 그렇다”는 댓글이 많이 붙었음
    내가 쓰던 접근법 하나는 최근 Vercel 개발자가 고쳤지만, 이미 우회하려고 다른 접근법을 택한 뒤였음
    가장 짜증 나는 건 개발 환경이 지연 로딩과 캐싱 마법을 쓴다는 점임
    페이지 차이를 계산해 웹소켓 같은 것으로 부분 업데이트를 보내려는 것 같은데, 완전히 망가져 복구 불가능한 상태가 될 수 있음
    가끔 재컴파일이 서버에서 클라이언트로 어떤 통신을 유발하고, Chrome 탭으로 돌아가면 탭이 완전히 멈춰서 Chrome 작업 관리자로 프로세스를 죽여야 함
    전반적으로 아직 매우 새롭고 거친 모서리가 많은 단계임

    • “기존 React 라이브러리 상당수는 훅을 쓰기 때문에 클라이언트에서 실행된다고 가정된다”는 부분은 훅이 아니라 컨텍스트를 말한 건가?
      많은 훅은 서버 쪽에서도 동작하고, 실제로는 값을 초기화하는 것 말고 아무것도 하지 않음
  • PHP와 JavaScript가 사실상 같은 문법에 가까웠다는 점이 재미있음
    $ 기호나 var 키워드 정도 차이였는데, NodeJS는 “우리는 서버에서 JS를 실행하고 싶다”고 했음
    그리고 15년 뒤 JavaScript가 결국 따라잡아서 사실상 PHP와 비슷해졌는데, 약어는 더 많고 학습 곡선은 더 가팔라짐
    물론 Suspense로 서버에서 클라이언트 컴포넌트로 데이터를 스트리밍하는 건 멋짐
    NextJS 13을 쓰고 있는데, SSR을 PHP에서 늘 가능했던 것처럼 쉽게 만들어 주는 점이 좋고 강력히 추천함

    • 공정하게 말하면 PHP 7 이전은 시점에 따라 쓰레기장이거나 더 위험한 물건이었음
      사람들이 “나쁜 설계의 프랙털”이라고 부를 때 100% 그럴 만했고, 문제를 풀려고 다른 곳을 찾은 것도 타당했음
      오늘날 PHP는 훨씬 나은 언어이고 다시 살펴볼 가치가 있지만, 예전부터 지금만큼 좋았던 척해서는 안 됨
      그리고 React 생태계를 기준으로 NodeJS를 판단하지는 말아야 함
      React 기반 시스템을 돌리기 위해 필요한 API와 래퍼의 엄청난 양은 React 커뮤니티 책임
      전형적인 스톡홀름 증후군임
    • PHP에는 클라이언트 사이드 렌더링이 없으니 이상한 비교임
    • 브라우저 자체도 크게 개선됐다는 점을 자주 무시함
      많은 현대적 발전이 가능해진 것도 먼저 브라우저가 좋아졌기 때문임
      완전히 한 바퀴 돈 것이라기보다는, 아주 멀리서 보면 원처럼 보이는 덩어리에 가까움
    • NodeJS를 좋아한 적이 없음
      한편으로는 동시성 모델 덕분에 더 빠른 백엔드를 만들 수 있었다는 점에서 혁신적이었지만, Java나 PHP 같은 기존 백엔드 언어의 많은 기능이 빠져 있었고, 그래서 많은 패턴이 다시 발명됐음
      언어 자체도 Java가 이미 갖고 있었고 PHP가 향해 가던 “안전성” 수준에 도달하는 데 수년이 걸렸음
      XML과 그것이 줄 수 있던 계약적 보장 같은 검증되고 표준화된 기술도 무겁다는 이유와 JSON이 사람이 읽고 쓰기 좋다는 이유 같은 것으로 버려졌음
      XML을 버리면서 시간과 노력을 많이 잃었다고 느낌
      REST/JSON API 문서화는 여전히 고통스러움
      20~25년 전에는 XML 페이로드에서 데이터 모델과 파서를 이미 생성할 수 있었음
      XML의 무엇이 그렇게 문제였는지 아직도 모르겠음
      선로 위에서 JSON보다 조금 무거웠지만, 압축을 쓰거나 EXI(https://www.w3.org/TR/exi/)로 바이너리 프로토콜로 바꾸면 해결 가능한 문제였음
      EXI가 실제로 자리 잡았는지는 모르지만, 당시 XML이 얼마나 많이 오갔는지 알았기 때문에 꽤 기대했었음
    • NodeJS가 만들어진 원래 이유 중 상당수는 잊힌 것 같음
      당시에는 비차단 입출력이 큰 성능 향상을 만들었음
      그런데 현대 개발자들의 머릿속에서는 JSON을 뱉거나 도구 체인을 호스팅하는 멍청한 도구 정도로 밀려난 듯함
  • 익숙한 걸 쓰는 거겠지만, 문서 사이트에 캐싱이 있는 기성 정적 사이트 생성기나 CMS 대신 React를 쓰는 건 낭비처럼 보임
    개발자 입장에서는 React가 더 재미있을 수는 있음

    • 훌륭한 문서 사이트는 자잘하게 동적인 부분이 많음
      Stripe가 계정 API 키가 들어간 코드 조각을 보여줘 바로 테스트할 수 있게 하는 흐름을 시작했음
      프런트엔드 문서 사이트에는 거의 항상 문서 안에서 직접 만져볼 수 있는 실행 가능한 예제가 들어감
    • 이 얘기를 자주 듣지만 이해가 잘 안 됨
      새 프로젝트 부트스트랩은 너무 쉽고 빠르며, 말 그대로 순수 HTML 프로젝트를 시작하는 것보다 쉬움
      내가 뭘 놓치고 있는 걸까?
      덧붙여, 비추천 누르는 분들의 관점이 진심으로 궁금함
    • 동의함
      정적 콘텐츠인데, 왜 검색창용 JavaScript 약간을 붙인 HTML로 생성하지 않는지 모르겠음
    • 정적으로 생성되고 있음
      React를 쓰는 이유는 프런트엔드 전반에서 하나의 언어를 쓰기 위해서임
      어떤 사람은 한 사이트에서 React를 쓰고, 다른 사람은 Gatsby/Hugo를 쓰는 식으로 나뉘는 걸 피할 수 있음
      Next.JS는 Gatsby/Hugo와 같은 일을 할 수 있으면서 기능이 더 많고 React 기반임
    • “개발자 입장에서 더 재미있다”는 게 사실 소프트웨어 개발자와 고용주 모두에게 내리는 저주임
  • 서버가 모든 것을 렌더링하고, CSS와 Javascript는 렌더링된 페이지를 보강하는 데 쓰던 시절을 기억할 만큼 나이가 들었음
    웹은 너무 어둡고 과잉공학적인 곳이 됐음
    거의 믿기 어려울 정도임
    그래서 내 앱 제작 방식은 서버 렌더링을 먼저 하고, 나중에 보강하는 쪽임

    • 대체로 동의함
      접을 수 있는 드롭다운이나 jQuery 드래그 앤 드롭은 있으면 좋은 기능이지만, js/jQuery 시절의 상태 관리 지옥도 선명히 기억해서 거기로 돌아가고 싶지는 않음
    • JavaScript를 쓰고 있다고?
      농담이고, 첫 Geocities 페이지는 방문자 카운터나 marquee 같은 것만 있는 HTML이었음
      학교에서 만든 첫 PHP 애플리케이션/프로젝트도 아직 JS를 쓰지 않았고, 정적 메뉴와 헤더에는 프레임을 쓰고 데이터는 그냥 폼 제출로 백엔드에 보냈음
      그런 시절이 있었음
      대학 과정에 포함된 첫 1년짜리 인턴십에서는 Java 백엔드, 표현 계층의 JSX 템플릿, 그리고 다이얼로그나 애니메이션 아코디언 같은 것에 PrototypeJS를 썼음
      그때 애니메이션은 “이 요소의 높이를 초당 몇 번 바꾸기”였음
      첫 직장에서는 장바구니 담기, 이미지 캐러셀 같은 식으로 페이지를 보강하는 JS를 많이 썼고, 그게 jQuery 시대였음
      다음 직장에서는 고객 지원 직원이 SAP 같은 것을 들여다보는 UI를 BackboneJS로 형편없이 만들었음
      그다음 업무도 고객용 투자은행 프런트엔드를 BackboneJS로 다시 만드는 일이었음
      그건 당시 말하던 단일 페이지 애플리케이션에 아주 잘 맞는 사용 사례였음
      검색 엔진 최적화가 필요 없고, 순수 프런트엔드 렌더링으로 충분히 빠르며, API 중심이었고, 그때는 웹과 모바일에 같은 API를 쓸 수 있다는 걸 사람들이 깨닫던 시기였음
    • 다른 관점에서, CSS와 Javascript가 발명되던 시절도 기억하고 그때부터 웹사이트를 만들어 왔음
      내 생각에 웹 개발 도구는 지금보다 좋았던 적이 없고, 사용자 경험도 수년 동안 엄청나게 개선됐음
      우리가 AJAX라고 부르던 것은 깔끔한 부가 장난감에서 클라이언트 컴포넌트와 단일 페이지 앱 형태의 일상적 기본 요소로 성장했음
      서버는 원한다면 여전히 강력하지만, 대시보드, 지도, 게임, 포럼, 오피스 앱, 온라인 IDE 같은 상호작용형 앱에서는 강력한 클라이언트 측 능력이 좋음
      이 덕분에 일상 앱이 각 운영체제별 맞춤 데스크톱 앱에서 모든 노트북과 데스크톱을 아우르는 범용 플랫폼으로 대규모 이동할 수 있었음
      물론 그 힘에는 더 많은 복잡성이 필요했음
      HTML/CSS로 블로그나 랜딩 페이지를 쓰는 것과 전체 웹 앱을 쓰는 것은 매우 다름
      Angular와 React는 당시 서버 사이드 언어에 비해 JS 런타임과 언어 자체가 훨씬 원시적이던 시절, 이전보다 몇 배 복잡한 앱 개발을 돕기 위해 만들어졌음
      2010년대 후반에는 여러 JS 프레임워크가 문제의 아주 작은 일부씩만 해결하던 정말 고통스러운 시기가 있었음
      요즘은 덜함
      Next가 이겼고 기본값이 됐으며, 그럴 만함
      중간 복잡도의 앱에 적절한 추상화 수준을 제공하고, 서버 사이드 렌더링과 클라이언트 사이드 페이지를 잘 섞게 해 줌
      React 서버 컴포넌트는 그 구분을 더 깔끔한 일급 개념으로 만들어 줌
      다만 그건 일정 복잡도 이상에서만 의미가 있음
      필요 없으면 쓰지 않으면 됨
      대부분 정적인 블로그나 문서 사이트라면 더 단순한 아키텍처가 있음
      여전히 HTML을 쓰고 필요한 만큼 JS 몇 줄만 뿌릴 수 있고, 대부분의 소규모 비즈니스에는 WordPress나 Wix도 쓸 수 있음
      하지만 더 복잡한 앱을 만든다면, 사소한 상호작용마다 서버로 왕복해 UI를 다시 계산하고 전체 HTML 페이지를 매번 보내던 방식에 비해 React는 정말 꿈같음
      그런 방식은 컨텍스트와 페이지 위치, 반쯤 채운 폼 등을 잃게 만들었고, 폼 데이터를 상태로 쓰도록 부추겼으며, 실수로 뒤로 가기를 누르거나 손쉬운 클라우드 확장이 나오기 전 잦은 서버 장애로 작업을 잃는 일이 많았음
      내 생각에 잘못 적용될 때만 과잉공학임
      적절한 사용 사례에서는 이런 도구들이 정말 유용하고 때로는 필수적임
      안타까운 부분은 필요 없거나 오히려 해로운 상황에서도 너무 많이 가르치고 사용을 장려한다는 점일 수 있음
      결국 작업에 맞는 도구를 써야 함
      React를 Vue, Svelte, HTMX보다 밀려는 건 아니고, 클라이언트 측 복잡성에도 쓸모가 있다는 뜻임
    • 바로 이 접근법, 즉 서버 컴포넌트가 그러려고 있는 것임
  • React가 더 현대적이고 쉽고 빠르며 저렴한 대안들을 따라잡으려고 애쓰는 인상을 받음
    하지만 근본 문제인 재렌더링, 자주 필요한 메모이제이션, 새는 추상화를 고치는 대신 React는 더 복잡해지고 있음
    최종 결과가 훌륭하다면 그 노력을 이해하겠지만 그렇지 않음
    React는 실제 환경에서 벤치마크가 보여주는 것보다 더 느리고, Next는 더 심함
    최근 Next로 만든 매우 느린 웹사이트를 많이 봤음
    정말 이해가 안 됨
    React 팀이 React를 개선하고 싶다면 핵심을 고쳐야 함

    • 그들은 고칠 수 없음
      지금은 생태계 의존성이 너무 많아서 깨짐
      Target.com, Walmart.com, Microsoft Teams와 셀 수 없이 많은 사이트가 React임
      거대한 컴포넌트 생태계와 그 위에 세워진 회사들도 있음
      핵심 개념이 깨져 있지만, 고친다는 것은 나머지 모든 것을 깨뜨릴 수 있다는 뜻임
      어차피 다 깨뜨릴 거라면 다른 걸 쓰는 편이 나음
      지금은 의존성의 질량 때문에 React에 묶인 채 계속 굴러갈 수밖에 없음
      React는 재렌더링을 기본으로 하고 빠지는 방식인 반면, Vue, Solid, Preact, Svelte는 모두 필요한 곳에 들어가는 방식임
      이것이 제대로 쓰기 어렵고 특정한 종류의 버그에 취약한 핵심 이유 중 하나임
      겉으로는 평범한 JavaScript처럼 보여도 계속 빠질지 신경 써야 하고, 다른 프레임워크에서는 그런 버그가 드물거나 거의 없음
    • 재렌더링과 자주 필요한 메모이제이션에 대해서는 이 작업이 있음
      https://react.dev/blog/2023/03/22/react-labs-what-we-have-be...
  • 왜 이제 와서 백엔드에서 React를 사용해 HTML을 렌더링하는지 이해가 안 됨
    10년 전으로 돌아가자는 건가

    • “그 환경”에 템플릿 시스템이 있다는 건 정말 값어치가 큼
      요즘 Svelte와 Django 템플릿을 자주 오가는데, Svelte는 DOM을 알고 있다는 점만으로도 경험이 훨씬 좋음
      JS가 아닌 템플릿 시스템에서 이런 걸 본 적이 없음
      PHP와 jQuery 등도 겪어 봤음
    • React의 프로그래밍 모델은 사람들에게 매력적이고, React는 HTML처럼 예측 가능한 출력이 있는 문제에 잘 맞음
      게다가 프런트엔드가 React라면 모든 HTML 생성을 한곳에 두기가 훨씬 쉬움
    • 25살 “시니어” 개발자들이 자기 ego를 떠받치려면 깎아내릴 낡은 기술이 필요했고, PHP가 그 대상이었음
    • Vercel이 당신의 백엔드를 호스팅하고 싶어 하고, React에 손을 뻗고 있기 때문임
    • 대부분의 다른 템플릿 시스템보다 좋고, 상호작용을 추가하고 싶을 때 하나의 언어와 패러다임으로 처리할 수 있어서 좋음
      거기에 정적 타입 지원도 있음
  • RSC나 React가 역대 최고의 해법이라고 변호하려는 건 아니지만, 여기 반론들 중 일부는 덜 익었음
    React/RSC의 이점은 서버가 HTML/CSS와 약간의 JavaScript를 반환하는 것과 기술적으로 같지 않음
    여전히 하나의 앱이고, SSR/하이드레이션과 비교해 클라이언트/서버 경계를 더 지능적으로 다루는 방식임
    React가 스스로 막다른 골목으로 설계했는지, 탈출 경로가 무엇인지에 대한 더 잘 아는 반론은 얼마든지 읽고 싶지만, PHP로 돌아가는 건 답이 아님

    • 요즘 HN은 잘 아는 프런트엔드 관점을 얻기 좋은 곳이 아님
      React는 배우기 그렇게 어려운 것도 아니고, 비개발자도 부트캠프 몇 주면 기본을 익히는 이유가 있음
      JSX는 Django, PHP, Rails의 템플릿 시스템보다 객관적으로 우월함
      대충 던지는 반론 중 절반은 Lighthouse 같은 것으로 자기 프로젝트 벤치마크도 안 해봤을 것 같음