https://www.youtube.com/watch?v=heNblrXVIcs
그레고르 반: 안녕하세요, 일리아. 소프트웨어 엔지니어링 데일리에 오신 것을 환영합니다.
일리아 구레비치: 고맙습니다, 그레고르. 여기에 오게 되어 기쁩니다.
그레고르 반: 뉴욕 타임즈의 시니어 소프트웨어 엔지니어시죠. 오늘은 당신과 당신 팀이 최근에 진행한 React 18 업그레이드라는 꽤 구체적인 주제에 대해 자세히 알아볼 예정입니다. 하지만 뉴욕 타임즈 이전의 경력부터 뉴욕 타임즈까지의 소프트웨어 경력에 대해 간략하게 듣고 싶습니다.
일리아 구레비치: 저는 2016년 애틀랜타의 조지아텍을 졸업했습니다. 당시 엘리베이터 회사인 티센크루프에서 엘리베이터용 터치스크린과 엘리베이터의 새로운 시스템을 개발하는 일을 했습니다. 그런데 제 동생이 트럭 운송 및 배송 최적화, 물류에 중점을 둔 로드스마트라는 스타트업의 초기 멤버였습니다. 동생은 2년 동안 그곳에서 일했고 저도 함께 일하기를 바랐습니다. 그래서 뉴욕에서 로드스마트에서 일하게 되었습니다. 동생과 저는 둘 다 그곳에서 일했고, 동생은 4-5년, 저는 3년 반 동안 일했습니다. 정말 엄청난 기회였습니다. 마치 집에 항상 불이 나고 있는데, 동시에 집 안에 있는 아기를 돌봐야 하는 것과 같은 정신없는 곳이었지만, 그런 상황에서 엄청나게 많은 것을 배웠습니다. 자신의 프로젝트에 대한 책임감을 갖고 스스로 문제를 해결하는 방법을 배웠습니다. 회사는 여러 면에서 성장했지만, 저는 결국 회사를 떠나 뉴욕 타임즈에 추천을 받게 되었습니다. 브라질에서 로드스마트를 Angular에서 React로 변환하는 것에 대한 발표를 했는데, 뉴욕 타임즈의 한 직원이 저를 보고 감명을 받았는지 뉴욕 타임즈에서 함께 일하자고 추천해 주었습니다. 그 후 2019년부터 지금까지 5년 넘게 뉴욕 타임즈에서 일하고 있습니다. 스타트업에서 본질적으로 꽤 큰 회사, 매우 높은 가시성을 가진 회사로 옮기는 것은 정말 흥미로운 경험이었습니다. 완전히 다른 영역이지만, 스타트업 경험을 통해 문제의 크고 작음에 관계없이 스스로 동기를 부여하여 정면으로 해결하는 방법을 배운 것이 뉴욕 타임즈에서 성공하는 데 큰 도움이 되었다고 생각합니다. 어떤 사람들은 "그냥 간단한 웹사이트, 텍스트, 이미지인데 얼마나 어렵겠어?"라고 생각할 수도 있지만, 실제로는 꽤 복잡한 작업입니다. CMS부터 결제 구독, 미터링, 페이월 등 많은 팀이 관련되어 있습니다.
그레고르 반: 말씀하신 대로 가시성이 높은 플랫폼입니다. 매일 뉴욕 타임즈에 접속하는 사람들의 수만큼 많은 사람들에게 영향을 미치는 플랫폼에서 일할 수 있는 기회는 많지 않죠.
일리아 구레비치: 물론입니다. 연간 수십억 건의 요청, 매일 수백만 건의 조회수를 기록하는 시스템입니다. 전 세계의 CDN, 캐싱 레이어 등 효과적이고 빠르게 콘텐츠를 배포하는 방법은 우리가 자주 고민하는 문제입니다.
그레고르 반: 뉴욕 타임즈 오픈 팀이 운영하는 블로그에 글을 쓰셨죠?
일리아 구레비치: 네, 기술 문서를 작성하는 블로그입니다.
그레고르 반: 네, 청취자들은 뉴욕 타임즈 오픈 팀을 검색해 볼 수 있습니다. Medium에 있는 것 같네요. 이 기사는 뉴욕 타임즈가 React 18로 업그레이드한 방법에 대한 것입니다. 그럼 이제 기본적인 질문부터 시작해 보죠. 왜 18로 업그레이드했나요? 뉴욕 타임즈가 예전에 Angular를 사용했고, React 16이 뉴욕 타임즈가 처음으로 사용한 React 버전이라는 것을 알고 있습니다. 틀린 부분이 있다면 정정해 주시고 계속 말씀해 주세요.
일리아 구레비치: 제가 들어왔을 때 웹사이트, 적어도 핵심 뉴스 웹사이트는 React 16을 사용하고 있었습니다. 하지만 그 전에는 많은 콘텐츠가 실제로 PHP로 렌더링되었다고 알고 있습니다. 다행히도 그 코드를 살펴볼 기회는 없었습니다. 저는 PHP에서 React로 페이지를 마이그레이션하는 마지막 단계에 합류했습니다. 베스트셀러, 비디오 페이지, PHP로 작성된 몇 가지 작은 페이지를 React로 마이그레이션했습니다. 18로 마이그레이션하기로 한 결정은 시스템을 최신 상태로 유지하려는 자연스러운 충동이었다고 생각합니다. Node 18을 사용하고 있다면 Node 20으로 업그레이드하는 것과 같습니다. 단지 모범 사례이기 때문에 업그레이드하는 경우가 많습니다. 처음에는 React 18이 16과 얼마나 다른지, 어떤 접근 방식이 필요한지 제대로 인식하지 못했고, 단지 의무감에서 의존성을 업그레이드해야 한다고 생각했을 뿐입니다. 하지만 React 18이 React의 패러다임을 크게 전환했다는 것을 알게 되면서, 특히 새로운 기능을 활용하기 위해 업그레이드가 더욱 중요해졌습니다. 동시성, 더 효율적인 방식으로 상태 업데이트 일괄 처리, 많은 사람들이 아직 탐색 중인 React 서버 컴포넌트와 관련된 미래 등이 업그레이드를 추진하게 된 이유입니다.
그레고르 반: 업그레이드를 통해 얻은 이점과 뉴욕 타임즈 플랫폼의 콘텐츠 제공 방식 및 기자들이 원하는 콘텐츠를 제공하는 데 도움이 되는 방식의 핵심 부분인 임베디드 인터랙티브에 대해 자세히 알아보기 전에 업그레이드에 필요한 단계와 몇 가지 주의 사항에 대해 간략하게 설명해 주시겠습니까?
일리아 구레비치: 처음부터 업그레이드 자체를 시작하기 전에 React 18과 호환되지 않는 Enzyme 테스트 라이브러리를 제거해야 한다는 것이 분명해졌습니다. 코드베이스를 보면 "이거 엄청난 작업이 되겠군"이라고 생각하게 됩니다. CSS 스타일을 한 시스템에서 다른 시스템으로 마이그레이션하는 것과 같고, 수천 개의 클래스가 있다고 생각해 보세요. React 18을 실제로 살펴보고 싶었지만, 모든 테스트 파일이 실패할 것이고, 이러한 중요한 통합 및 단위 테스트를 무시할 수 없기 때문에 Enzyme을 제거해야 한다는 것을 깨달았습니다. 그래서 시간이 지남에 따라 React 18을 구체적으로 다루는 것은 아직 시기상조이고 동시에 관리해야 할 다른 우선 순위가 많기 때문에 천천히 Enzyme에서 벗어나 새로운 표준이 되고 있는 React 테스트 라이브러리로 이동하기로 결정했습니다. 이 작업을 1년 또는 1년 반에 걸쳐 분산하기로 했습니다. 실제로 수백 개의 테스트 파일을 새 라이브러리로 마이그레이션하면서 일부 테스트 파일이 처음부터 제대로 작성되지 않았다는 것을 깨달았습니다. 컴포넌트를 테스트하는 방법에 대한 직관력을 개발하고 이전에 작성된 방식을 보면 단위 테스트에서 실제로 얻는 가치가 없다는 것을 깨닫게 됩니다. 그래서 테스트를 다시 작성하여 실제로 검증하고 싶은 동작을 검증할 수 있도록 합니다. 더 이상 유용하지 않은 일부 테스트를 다시 살펴보고 동시에 마이그레이션을 진행했습니다. 이 과정은 앞서 말씀드린 대로 1년 반 이상 지속되었지만, 마무리 단계에 가까워지면서 "100개의 테스트만 남았으니 다 해치우자"는 식의 미니 마라톤이 되었습니다. 그 후 23주 동안 최대한 많은 테스트를 마무리했습니다. 결국 그 작업을 마쳤고, 이제 React와 관련된 의존성을 실제로 업그레이드하는 것이 어떤 모습일지 알아보기 시작했습니다. 최신 API를 구현하지 않고 React와 관련된 의존성을 업그레이드하고 싶었습니다. React에는 서버 측 애플리케이션이 브라우저에서 렌더링될 때 콘텐츠를 하이드레이션할 수 있도록 하는 두 가지 새로운 API가 있습니다. 3개월이 걸렸습니다. 웹사이트의 다른 기능은 대부분 영향을 받지 않았기 때문입니다.hydrateRoot
와 클라이언트에서 무언가를 렌더링하려는 경우 createRoot
라는 API입니다. reactdom.render
및 reactdom.hydrate
와는 다릅니다. 우리는 모든 의존성을 업그레이드하고 싶었지만 새로운 API는 사용하고 싶지 않았습니다. 새로운 API를 사용하지 않고 의존성을 업그레이드하려면 모든 유형 정의를 수정해야 하고, 그 과정에서 몇 가지 테스트가 중단되어 수정해야 했습니다. 하지만 매우 천천히 하지만 확실하게 그 작업을 완료하고 모든 의존성을 업데이트하고 프로덕션 환경에 배포했는데 아무런 문제가 발생하지 않았습니다. 이제 새로운 API를 실제로 사용해 보고 어떤 일이 일어나는지 확인해 보자는 생각이 들었습니다. 실제로 다른 몇 가지 시스템을 React 18로 업그레이드했는데, 대부분 별 문제가 없었습니다. Google Docs와 같은 엔진을 사용하고 ProseMirror, React, Firestore 등을 사용하는 복잡한 시스템인 CMS를 16에서 18로 업그레이드했는데, 자동 일괄 처리와 관련된 사소한 문제가 있었지만 대부분 별 문제가 없었습니다. 그래서 뉴욕 타임즈 웹사이트는 실시간 편집기가 아니고 Google Docs처럼 댓글을 지원하지 않기 때문에 업그레이드가 좀 더 간단할 것이라고 생각했습니다. 하지만 실제로는 정반대였습니다. 훨씬 더 복잡했습니다. 엔진을 켜면 앞으로 나아가는 것처럼 느껴지고 모든 것이 순조롭게 진행되는 것 같지만, 엔진이 갑자기 덜컹거리기 시작합니다. 바로 그런 일이 일어났습니다. 특히 임베디드 인터랙티브에서 그랬습니다. 프로덕션 웹사이트의 거의 모든 임베디드 인터랙티브가 제대로 표시되지 않았고, 뉴스룸과 그래픽 편집자들이 엄청난 공을 들여 만든 중요한 뉴스 기사들이 제대로 표시되지 않아 즉시 경고가 울리기 시작했습니다. "무슨 일이지? 뉴스룸은 뉴스를 올바른 형식으로 표시해야 하는데, 특히 앞서 언급한 가시성 때문에 더욱 그렇다"라고 생각했습니다. 즉시 "아, 실사를 제대로 하지 않았나 보다. 되돌리자. 복잡성을 줄이자. 예전 방식으로 돌아가자. 별일 아니다. 아무 문제 없다. 조사해 보자"라고 생각했습니다. 그렇게 문제를 해결하는 긴 과정이 시작되었습니다. 앞서 언급한 임베디드 인터랙티브 문제를 해결하는 데 2
그레고르 반: 몇 가지 질문을 드리겠습니다. 하나는 좀 더 부드러운 질문이고, 다른 하나는 엔지니어링 측면에 대한 질문입니다. 빨리 되돌릴 수 있었기 때문에 많은 일을 할 필요가 없었을 수도 있지만, 엔지니어링 팀으로서 왜 이 문제를 배포했는지에 대한 보고서를 작성해야 했습니까? 저도 비슷한 경험이 있습니다. 웹사이트의 구매 버튼을 완전히 차단해 버린 적이 있었는데, 다국적 기업의 아르헨티나 팀을 위해 일하고 있었는데, 아르헨티나 팀이 저에게 매우 화가 났습니다. 왜 이 문제를 매우 심각하게 받아들이고 있는지, 왜 이런 일이 일어났는지 보여주기 위해 긴 보고서를 작성해야 했습니다. 그러자 상황이 완전히 바뀌었습니다. 혹시 그런 경험이 있으신지 궁금합니다.
일리아 구레비치: 물론입니다. 확인도 하지 않고 프로덕션 환경에 배포한 것은 아닙니다. QA 프로세스를 거쳤는데, QA 프로세스는 비디오가 포함된 기사, 세션 쇼가 포함된 기사, 특정 기능이 포함된 기사, 의견 기사 등 모든 주요 자산 유형을 살펴보는 것이었습니다. 기사 유형을 포괄한다고 생각하는 목록을 작성하고 QA 팀에 전달하여 테스트를 진행했습니다. 실제로 NYTimes.com을 운영하는 웹 서버는 하나가 아닙니다. 앱 내부의 기사 및 홈페이지 콘텐츠를 운영하는 추가 웹 서버도 있습니다. 웹 뷰 구현이지만 실제로는 네이티브로 구축된 것이 아닙니다. 즉, NY Times 콘텐츠를 운영하는 웹 서버가 두 개 있습니다. QA 프로세스는 철저하게 진행되었지만, 실제로는 오래된 기사와 오래된 콘텐츠를 사용하고 있었던 것 같습니다. 일반적인 QA 프로세스에서는 사이트 전체에 걸쳐 포괄적인 변경을 하는 경우는 드뭅니다. 대부분의 경우 버튼 하나, 기능 하나를 테스트합니다. 수백만 건의 게시된 기사에 영향을 미칠 수도 있고 미치지 않을 수도 있는 포괄적인 변경을 하는 것은 매우 드문 일입니다. 그러한 변경을 하는 것은 꽤 어렵기도 합니다. QA 프로세스가 있었고 따랐지만 분명히 부족했습니다. 뉴스룸과 그래픽 디자인 팀은 그 결과에 분명히 만족하지 못했고, 문제가 처음 예상했던 것보다 훨씬 크다는 것이 분명해지자 다음 번에는 최대한 철저하게 하기 위해 완전히 집중했습니다. 뉴욕 타임즈의 문화적 측면을 말씀드리자면, 뉴스의 속도에 맞춰 일해야 하는 경우가 많습니다. 모두 알다시피 뉴스는 순식간에 바뀔 수 있고, 배포 프로세스가 메인으로 푸시되기 때문에 10분 후에 프로덕션 환경으로 전환될 예정인 프리뷰 브랜치에서 QA 프로세스를 따라야 하는 경우가 많습니다. 좋은 일이라고는 할 수 없지만, 뉴욕 타임즈의 문화는 빠르게 일하는 것입니다. 저도 개인적으로 그런 잘못을 저질렀지만, 이 문제가 더 큰 문제라는 것을 알게 된 후 완전히 재평가하여 핵심 그래픽 유형, 핵심 임베디드 그래픽 유형 중에서 가장 포괄적인 QA 목록을 작성하는 데 2~3개월을 보냈습니다. 임베디드 그래픽 유형은 매우 다양합니다. 여기서 문제의 핵심으로 들어가 보겠습니다. 그래픽 편집자와 뉴스룸에서 일회성의 자체 포함된 HTML, CSS, JS 정보 덩어리를 게시할 수 있는 임베디드 인터랙티브가 있습니다. CMS에서 게시하거나 핵심 인프라를 다시 배포할 필요 없이 시각적 정보를 게시할 수 있습니다.
그레고르 반: 그래프, 지도, 차트 같은 것 말씀이시죠?
일리아 구레비치: 네, 어떤 것은 매우 간단할 수도 있습니다. 정보가 표시된 위성 지도일 수도 있는데, 이미지일 수도 있지만 정보가 약간 다른 방식으로 표시되어 SVG로 렌더링하는 것이 더 좋고, SVG는 이미지로 업로드하는 것보다 훨씬 빠르게 렌더링됩니다. 이미지의 품질이 떨어질 수도 있기 때문입니다. 때로는 페이지 전체를 차지하는 훨씬 더 복잡한 임베디드 인터랙티브일 수도 있습니다. 아래로 스크롤하면 요소가 날아다니고, 텍스트가 강조 표시되고 해제되고, 텍스트가 위아래로 움직이고, 스크롤하는 동안 백그라운드에서 비디오가 재생될 수도 있습니다. 가장 간단한 것부터 가장 복잡한 것까지 다양합니다. 하지만 그래픽 개발자들이 항상 좋아하는 것은 컴포넌트를 어떻게 작성했는지 근본적으로 신경 쓸 필요가 없다는 것입니다. 기사에 래핑되지 않은 자체 포함된 환경에서 작동한다면, 기사에 넣으면 테스트할 때 보았던 대로 작동할 것이라고 믿고 있습니다. 바로 그 점이 장점입니다. Vanilla JS를 사용하든, Svelte를 사용하든, 아니면 다른 임베디드 인터랙티브 유형인 AI to HTML을 사용하든 자유롭게 사용할 수 있습니다. AI to HTML은 Adobe Illustrator 임베디드 인터랙티브 유형으로, Adobe Illustrator에서 차트를 만들어 HTML로 내보내면 기사에 바로 들어갑니다. 엄청난 워크플로우입니다. 거의 완전한 자유를 누릴 수 있습니다. 그래픽 개발자들이 정말 좋아하는 기능입니다. 하지만 문제가 발생한 부분은 하이드레이션 개념과 그 의미를 조금 설명해야 할 것 같습니다. 서버에서 페이지를 렌더링할 때 웹 서버에 HTML을 생성하여 CDN에 업로드하도록 지시합니다. 그런 다음 독자가 URL에 액세스하면 서버 측에서 생성된 HTML이기 때문에 매우 빠르게 응답을 받게 됩니다. 그런 다음 HTML이 하이드레이션되는데, 일반적으로 헤드 맨 위에 인라인된 스크립트가 하이드레이션 이벤트를 호출하여 시작됩니다. 하이드레이션 이벤트는 페이지에 인터랙티브 기능을 제공하는 것을 의미합니다. 정적 HTML 덩어리만 제공하는 것이 아니라 일반적으로 Hook 형태로 제공되는 React의 클라이언트 측 기능을 원합니다. Hook이 작동하도록 하려면 서버 측에서 콘텐츠를 생성합니다. 사용자가 브라우저에서 HTML을 렌더링하기 위해 계산해야 하는 전처리 시간 없이 최대한 빨리 기사나 홈페이지를 볼 수 있도록 하기 위해서입니다. 이 방식은 우리에게 매우 잘 맞았습니다. 현재 우리가 사용하는 모델이며, SEO에 의존하는 트래픽이 많은 다른 웹사이트들도 일반적으로 이 방식을 사용합니다. 그런데 문제는 React가 하이드레이션되기 전에 하이드레이션 이벤트가 발생하기 전에 DOM의 전체 구조가 변경되면 하이드레이션 불일치가 발생한다는 것입니다. 매우 간단한 예를 들어 보겠습니다. 서버에서 날짜를 생성하고 클라이언트에서 날짜를 계산했는데, 일반적으로 현재 시간 형식으로 표시되는 서버 측 날짜가 브라우저에서 사용되는 현재 시간과 다르다고 가정해 보겠습니다. 이렇게 되면 불일치가 발생하여 오류가 발생합니다. 또 다른 예로, 타사 확장 프로그램이 표시되는 즉시 페이지의 콘텐츠를 조작하는 경우가 있습니다. React는 이를 좋아하지 않습니다. "하이드레이션하려는 것과 다르다. 조정해야 한다. 따라서 클라이언트에서 처음부터 다시 렌더링하겠다"라고 말합니다. 즉, 브라우저 수준에서 HTML이 렌더링되고, 확장 프로그램, 잘못된 패턴 사용, 날짜 사용 등 여러 가지 이유로 하이드레이션 불일치가 발생할 수 있습니다. React는 조정하고 하이드레이션하려고 시도하다가 문제가 발생했다는 것을 깨닫고 전체 React 트리를 다시 렌더링합니다. 사용자나 독자는 이 과정을 알아차리지 못할 것입니다. 하이드레이션 이벤트는 몇 밀리초 내에 발생하고 문제가 있으면 렌더링 이벤트도 일반적으로 매우 빠르기 때문입니다. 하지만 단점은 임베디드 인터랙티브를 렌더링할 때 React는 임베디드 인터랙티브에 대해 아무것도 모른다는 것입니다. 임의의 HTML이 주어지고 페이지에 렌더링하라는 지시만 받습니다. 따라서 메인 React 트리뿐만 아니라 페이지에 존재할 수 있는 임베디드 인터랙티브의 다른 모든 독립적인 하위 트리도 HTML을 생성합니다. 이러한 하위 트리는 하이드레이션 이벤트보다 훨씬 빠르게 실행되는 자체 스크립트 태그를 가지고 있습니다. 페이지 너비에 따라 임베디드 인터랙티브의 크기를 조정하는 스크립트 태그가 있다면 실제로 크기 조정이 발생하는 것을 알아차리지 못할 것입니다. 스크립트가 처리할 수 있는 속도보다 훨씬 빠르게 작동하여 CSS에 따라 임베디드 인터랙티브의 크기를 자동으로 조정합니다. 때로는 스크립트가 비동기 요청으로 가져오는 API에서 데이터로 인터랙티브를 즉시 채우기 시작합니다. 우리는 이를 알 방법이 없습니다. 하지만 메인 React 트리와 독립적으로 자동으로 실행됩니다. 문제는 전체 트리가 렌더링될 때 하이드레이션 불일치가 발생하면 임의의 HTML 콘텐츠를 렌더링하는 데 React에서 사용하는 dangerouslySetInnerHTML
내부의 모든 것이 포함된다는 것입니다. dangerouslySetInnerHTML
을 생각하지 않고 HTML 덩어리를 가지고 있고 JavaScript를 통해 DOM에 추가하는 경우 스크립트 태그가 있다면 자동으로 실행되지 않습니다. 스크립트 태그를 수동으로 실행해야 합니다. 스크립트 태그를 수동으로 실행하려면 스크립트 태그만 가져와서 JavaScript를 통해 DOM의 자식 객체에 추가하면 브라우저가 "이 스크립트를 실행하라고 명시적으로 지시했으니 실행해도 괜찮다"라고 인식합니다. 그렇지 않으면 일반 HTML로 취급합니다. 이는 브라우저의 제약 사항입니다.
그레고르 반: 네, 브라우저의 보안 측면에서 그렇습니다.
일리아 구레비치: 네, 그렇게 하면 XSS 공격이 너무 많이 발생할 것입니다. 양식에 스크립트를 삽입한 다음 브라우저에서 해당 스크립트를 실행하도록 하는 것은 끔찍한 일입니다. 하지만 자식으로 추가하라고 브라우저에 명시적으로 지시하면 브라우저는 해당 스크립트를 실행합니다. 이제 우리는 곤경에 처했습니다. 수백만 개의 기사 유형이 있고, 수만 개의 임베디드 인터랙티브가 있으며, 이러한 임베디드 인터랙티브는 완전히 다른 방식으로 구축되어 있습니다. 재사용되는 템플릿이 몇 개 있을 수도 있지만, 모두 일회성이라고 가정해야 합니다. 우리가 직면한 딜레마는 React 18이 하이드레이션 불일치에 훨씬 더 민감하다는 것을 알고 있지만, React 18을 출시하고 싶다는 것입니다. 이미 많은 시간과 노력을 투자했기 때문에 포기하고 싶지 않았습니다. "수동으로 실행하면 임베디드 인터랙티브를 어떻게 작동시킬 수 있을까?"라는 고민을 하게 되었습니다. 이것이 기사의 핵심입니다. 발생하는 하이드레이션 불일치 오류를 해결하려고 노력하는 것입니다.
그레고르 반: 잘 설명해 주셨습니다. 기사에서 언급한 GitHub 스레드에 대해 이야기해 보겠습니다. 이 문제에 관심이 있든 없든, 이 스레드를 읽어보시는 것을 추천합니다. 핵심 팀과의 훌륭한 커뮤니케이션의 좋은 예이기도 하고, 매우 까다로운 문제에 대해 매우 자세하게 설명하고 해결 방법을 제시한 좋은 커뮤니케이션의 예이기도 합니다. 핵심 팀과의 소통 없이 이 문제를 해결할 수 있었을까요?
일리아 구레비치: 해결할 수 있었을지도 모르지만, 솔직히 말해서 React 18은 하이드레이션 불일치가 발생하는 이유를 잘 알려주지 않습니다. 이 문제는 아직 베타 버전인 React 19에서 해결되었습니다. React 19로 곧 업그레이드할 예정입니다. React 19는 DOM에서 구체적으로 무엇이 변경되었는지에 대한 명확한 차이점을 보여줍니다. 하지만 당시에는 그런 기능이 없었기 때문에 오랫동안 "임베디드 인터랙티브 자체가 불일치를 일으키는 것일까?"라고 생각했습니다. 페이지가 하이드레이션되기 전에 스크립트가 임베디드 인터랙티브의 DOM을 조작하기 때문입니다. 하지만 실제로는 그렇지 않았습니다. 핵심 팀이 말하기를 임베디드 인터랙티브 내부에서 발생하는 DOM 관련 변경 사항은 무시한다고 합니다. 따라서 임베디드 인터랙티브가 브라우저가 로드되는 즉시 콘텐츠를 변경해도 불일치가 발생하지 않습니다. 불일치는 방대한 모놀리식 코드베이스 내에 있는 React 컴포넌트가 잘못된 관행을 사용하기 때문에 발생합니다. React 16에서는 보이지 않았지만 18에서는 보이는 문제입니다. 이제 우리는 React 18이 더 민감하고 전체 렌더링을 수행한다는 것을 알고 있습니다. 자산 유형은 매우 다양하지만 React 18이 우리가 가고자 하는 방향이라는 것을 알고 있습니다. 이제 문제는 어떻게 앞으로 나아갈 것인가입니다. 문제가 무엇인지 알았으니 어떻게 해결해야 할까요? 문제는 자산 유형이 너무 많아서 로컬에서 개별적으로 테스트할 수 없다는 것입니다. 불가능합니다. 따라서 모든 하이드레이션 불일치를 해결할 수 없습니다. 언제 나타날지 확실하지 않기 때문입니다. 하지만 React 18을 출시하고 싶습니다. 이미 많은 시간과 노력을 투자했기 때문에 포기하고 싶지 않습니다. 이제 "수동으로 실행하면 임베디드 인터랙티브를 어떻게 작동시킬 수 있을까?"라는 고민을 하게 되었습니다. 이것이 기사의 핵심입니다. 발생하는 하이드레이션 불일치 오류를 해결하려고 노력하는 것입니다.
그레고르 반: 네, 지난 5~10분 동안 이야기한 내용에 관심이 있는 분들은 일리아의 기사를 읽어보시면 더 자세한 내용을 알 수 있습니다. 앞서 말씀드린 스레드도 확인해 보세요. 물론 마지막에는 많은 이점이 있었습니다. 성능 면에서 기사에서 inp 점수와 뉴욕 타임즈에서 inp 점수가 얼마나 중요한지 언급했습니다. inp 점수가 무엇인지, React 18이 어떻게 기여했는지, 결과가 어떠했는지 간략하게 설명해 주시겠습니까?
일리아 구레비치: inp는 사람들이 아직 이해하고 있는 새로운 지표입니다. inp는 Interaction to Next Paint의 약자로, CLS, LCP, TBT와 같은 모든 핵심 웹 바이탈 지표를 대체하는 것은 아닙니다. 이러한 지표는 여전히 관련성이 있지만 Google은 올해 3월부터 웹사이트 순위를 매기는 새로운 지표로 inp를 사용하기 시작했습니다. 우리에게 높은 SEO 점수는 가장 중요한 우선 순위 중 하나입니다. 전 세계 모든 사람들에게 가장 눈에 띄는 뉴스 사이트가 되고 싶기 때문입니다. 사람들이 Google에서 특정 주제를 검색했을 때 뉴욕 타임즈가 최상위 결과로 표시되기를 바랍니다. Wikipedia와 비슷합니다. 따라서 처음부터 이 새로운 inp 점수가 우리에게 중요하다는 것을 알았습니다. 어떻게 관리해야 할까요? 실제로 React 18 이전에도 inp 점수는 좋지 않았습니다. inp는 수천 밀리초였습니다. 이는 웹사이트의 응답성이 얼마나 좋은지를 나타냅니다. 예를 들어 버튼을 누르거나 모달을 닫거나 드롭다운을 누르거나 메뉴 화면을 누르거나 페이지를 탭하여 스크롤하는 경우 모두 Google에서 추적하는 이벤트입니다. Google의 핵심 목표는 사용자가 작업을 시작했을 때 페이지에서 무언가가 변경되었다는 것을 알려주고, 얼마나 빨리 알려주고, 페이지에서 무언가가 변경되었다는 피드백을 제공했는지 확인하는 것입니다. 로그인 버튼을 클릭했을 때 비동기 요청이 얼마나 빨리 발생했는지가 중요한 것은 아닙니다. 비동기 요청은 1~2초가 걸릴 수도 있기 때문입니다. 중요한 것은 버튼을 클릭했을 때 로딩 아이콘이 페이지에 표시되었는지, 드롭다운이 채워졌는지와 같은 피드백을 제공했는지 여부입니다. 초기 로딩 시간뿐만 아니라 페이지에서의 사용자 경험에 대한 것입니다. React 18은 기본적으로 inp 점수를 크게 향상시켰습니다. 적어도 좋은 경험을 하지 못하는 사람들의 p75 범위에서 30% 향상되었습니다. 블로그 게시물의 차트는 약간 오래된 것입니다. 회사 전체의 여러 팀이 소셜 팀, 스토리 팀 등 페이지의 여러 부분에 대한 우선 순위를 변경했기 때문입니다. p75 범위의 inp 점수는 이제 목표 지표에 근접하거나 거의 일치합니다. 목표 지표는 200밀리초였던 것 같은데 정확한 숫자는 기억나지 않습니다. 이제 수천에서 목표 지표에 근접하게 되었습니다. 이는 우리에게 정말 좋은 소식이고, React 18 없이는 이룰 수 없었을 것입니다.
그레고르 반: 이전에 Million.js 에피소드를 진행했었습니다. Million.js는 React 위에 붙여서 모든 인터랙션을 컴파일하여 DOM 다시 렌더링을 피하는 모델에 더 가깝게 만드는 레이어입니다. 일부 청취자들은 알고 있을 수도 있고 모를 수도 있지만, 저는 Svelte를 더 좋아해서 의도적으로 약간 다른 관점에서 접근하고 있습니다. Svelte는 DOM 다시 렌더링 방식이 아닌 컴파일 방식을 옹호하는 프레임워크입니다. React 18이 컴파일 자체는 아니지만 다른 프레임워크들이 성능 측면에서 어떻게 접근하는지 인지하고 있었던 버전인가요? View도 실제로 구현하지는 않았지만 이에 대해 많이 이야기했습니다. 불행히도 이름은 기억나지 않지만 Svelte 경쟁업체와도 에피소드를 진행했습니다. React 18은 성능 이벤트 시퀀스에서 어디에 위치합니까?
일리아 구레비치: 이 질문에는 두 가지 관점이 있습니다. 첫째, React 18은 구현 결과로 코드가 어떻게 변경되었는지 정확히 알 수 없지만, 자동 일괄 처리라는 새로운 기능을 구현했습니다. 이전에는 React가 상태 업데이트를 시작할 때 여러 컴포넌트에서 여러 번 연속으로 상태를 업데이트하는 경우 "여기 상태 변경, 렌더링, 여기 상태 변경, 렌더링, 여기 상태 변경, 렌더링"과 같은 팬케이크처럼 생각했습니다. React 18은 이제 렌더링이 실제로 얼마나 자주 발생하는지 더 똑똑하게 처리합니다. 예를 들어 8개의 다른 렌더링에서 연속으로 여러 가지 상태 변경이 있는 경우 React는 "1에서 8까지의 상태가 어떻게 될지 알고 있으므로 모든 다시 렌더링을 건너뛰고 바로 8로 이동하겠다"라고 말할 수 있습니다. 이것이 핵심 성능 이점의 원동력이라고 생각합니다. 페이지에서 계산이 줄어들고, 가상 DOM 조작이 줄어들고, 실제 DOM 조작이 줄어들기 때문입니다. A에서 B로 이동하는 대신 A에서 A, A에서 I로 바로 이동하면 분명히 개선될 것입니다. 제가 이해한 바로는 핵심 기능이 변경된 부분입니다. Million.js와 React 19의 새로운 React 컴파일러는 React 컴파일러가 실제로 어떻게 코드를 최적화할 수 있는지 직관적으로 이해하는지에 대한 것입니다. React 19가 베타 버전에서 벗어나면 자세히 살펴보고 싶은 부분입니다. 컴파일러는 기본적으로 페이지의 어떤 부분이나 코드의 어떤 부분이 메모리에 저장된 데이터를 사용할 수 있는지 식별하여 변경되지 않을 데이터에 대한 변경을 시작하지 않습니다. 직접 개입할 필요 없이 컴파일 시 코드를 어떻게 변경할 수 있는지 직관적으로 이해할 수 있다고 합니다. 정말 멋진 기능이라고 생각합니다. 하지만 이러한 기능, 특히 React 서버 컴포넌트와 같은 새로운 기능은 업그레이드 후에야 논의하기 시작했습니다.
그레고르 반: 네, 바로 그 부분에 대해 질문하려던 참이었습니다. React 18에서 어떤 부분을 살펴보고 있고, 감히 React 19로 언제쯤 넘어갈지도 질문해도 될까요?
일리아 구레비치: 실제로 React 19에 대한 실험적인 브랜치를 이미 열어두고 있습니다. React 19를 가지고 놀아보고 있는데, 새로운 하이드레이션 차이점이 마음에 듭니다. 실제로 임베디드 인터랙티브 유형에서 하이드레이션 불일치를 일으키는 사소한 문제가 발생하고 있는데, 임베디드 인터랙티브 자체가 문제를 일으키는 경우는 드뭅니다. 무슨 일이 일어나고 있는지 잘 알 수 없었는데, React 19 덕분에 이 문제에 접근할 새로운 각도가 생겼습니다. 하지만 우리의 미래는 꽤 오래되고 더 이상 사용되지 않는 빌드 패턴에 있습니다. 우리는 여전히 적극적으로 사용하고 있는 키트라는 오픈 소스 프로젝트를 가지고 있지만, 꽤 오래된 것이 분명하고 빌드 프로세스를 새롭게 해야 합니다. 우리는 기본적으로 next.js의 사내 버전을 구축했습니다. 이 버전은 우리에게 잘 맞았지만 이제 업그레이드해야 합니다. 업그레이드에는 React 서버 컴포넌트를 살펴보는 작업이 포함됩니다. 서버 컴포넌트는 CDN에 로드된 정적 HTML 덩어리를 브라우저에 로드한 다음 클라이언트 측 React를 통해 하이드레이션하여 페이지에 기능을 제공하는 대신 브라우저로 스트리밍 업데이트를 도입하는 근본적으로 다른 새로운 패러다임을 도입합니다. 이제 초기 서버 측 로드에서 꼭 필요하지 않은 작은 인터랙티브 섬을 삽입하고 있습니다. 이러한 작은 HTML 덩어리를 에지에 저장할 수 있으므로 이제 에지 컴퓨팅과 Vercel이 답하려고 하는 질문을 생각하게 됩니다. 이러한 것들은 이제 막 탐험하기 시작한 것들입니다. next.js, Remix를 사용할지, 아니면 도구가 비교적 맞춤형이기 때문에 직접 프레임워크를 구현할지 아직 확실하지 않습니다. 하지만 이것이 우리가 나아가고 있는 방향입니다.
그레고르 반: 흥미롭네요. 빌드 프로세스 업그레이드에 대해 이야기했는데, 뉴욕 타임즈가 기술적 관점에서 어떤 방향으로 나아가고 있는지 공유해 주실 수 있나요?
일리아 구레비치: 웹 플랫폼 팀의 개인적인 목표는 시스템을 더 공유된 방식으로 정렬하는 것입니다. 뉴욕 타임즈에는 핵심 뉴스 사이트, 게임, The Athletic, Wirecutter, Cooking, 앱에 있는 웹 뷰 구현, 뉴욕 타임즈 앱에서 콘텐츠를 렌더링하는 별도의 웹 서버 등이 있습니다. 핵심 과제 중 하나는 플랫폼을 활용하는 방법뿐만 아니라 수익 창출과 가치 창출에 실질적으로 도움이 되는 시스템 전반에서 플랫폼을 의미 있게 활용하는 방법입니다. 게임, Cooking, Wirecutter를 모두 React 서버 컴포넌트와 next.js에 올리고 싶지만, 그렇게 하는 것이 실제로 수익에 어떤 도움이 되는지, 더 빠르게 작업하는 데 어떤 도움이 되는지, 한 가지 구현 방식에 얽매이지 않고 다른 에코시스템에 대한 이해를 공유하는 데 어떤 도움이 되는지 고민해야 합니다. 다른 시스템을 모두 새로운 시스템으로 마이그레이션해야 하는 상황을 피하고 싶습니다. 다양한 도구를 자유롭게 탐색할 수 있으면서도 회사로서 공유된 정체성을 갖고 싶습니다. 이것이 앞으로 몇 년 동안 집중해야 할 부분이라고 생각합니다. 더 공유된 정체성을 개발하여 팀이 서로 독립적으로 작업하지 않고 우리가 제공하는 모든 제품을 하나로 묶는 뉴욕 타임즈 에코시스템의 일부가 되도록 하는 것입니다.
그레고르 반: 흥미롭네요. 저는 The Athletic 구독자입니다. 앱이 마음에 듭니다. 인수 전의 앱이 얼마나 반영되었는지는 모르겠지만, 앱이 마음에 듭니다. 뉴욕 타임즈 앱도 마음에 듭니다. 두 앱에서 더 많은 것을 공유할 수 있다면 유료 구독자로서 좋을 것 같습니다.
일리아 구레비치: 그렇게 말씀해 주시니 기쁩니다. 마지막으로 뉴욕 타임즈의 시니어 소프트웨어 엔지니어로서 하루 일과는 어떤지 간단히 말씀해 주시겠습니까?
일리아 구레비치: 뉴욕 타임즈는 우리가 기술 회사이면서 기술 회사가 아니라는 점에서 독특한 위치에 있다고 생각합니다. 우리의 제품은 뉴스이고, 우리의 기자들은 세상을 위해, 그리고 저를 포함한 모든 사람을 위해 훌륭한 일을 하고 있습니다. 하지만 동시에 기술 회사의 요소도 가지고 있습니다. 모든 사람이 Slack을 사용하고, GitHub, 스탠드업 미팅, Jira 등을 사용합니다. 대부분의 소프트웨어 엔지니어에게는 놀라운 일이 아닙니다. 하지만 뉴스에 가까운 팀이기 때문에 경험이 다릅니다. 앞서 임베디드 인터랙티브에서 심각한 오류를 범했다고 말씀드렸는데, 웹사이트의 안정성을 보장해야 하는 일상적인 수준의 압박감뿐만 아니라 선거와 같은 큰 뉴스 이벤트로 인한 주기적인 압박감도 있습니다. 뉴욕 타임즈에게 선거는 슈퍼볼과 같습니다. 지금도 엄청난 트래픽이 발생할 시나리오에 대비하여 적극적으로 준비하고 있습니다. 뉴욕 타임즈는 가시성이 매우 높지만, 모든 시스템이 한꺼번에 20배, 30배, 40배의 트래픽을 받는다고 상상해 보세요. 시스템을 그렇게 빨리 확장할 수 있어야 합니다. 뉴욕 타임즈는 서버 측 React 애플리케이션이기 때문에 클라이언트 측 번들을 제공하는 것뿐만 아니라 AWS에 배포된 Kubernetes 애플리케이션이기도 하고, 에지에 자산을 가지고 있습니다. 서로 의존하고 연결된 시스템이 있고, 엄청난 양의 트래픽이 한꺼번에 몰려오면 적극적으로 해결해야 할 문제입니다. 이것이 최근에 우리가 집중하고 있는 부분입니다. 우리는 소프트웨어 엔지니어로서 일하고, 풀 리퀘스트, 병합, 배포 등을 수행한다는 점에서 기술 회사입니다. 하지만 뉴스 옆에 있다는 것은 근본적으로 다른 경험입니다. 저는 개인적으로 항상 뉴욕 타임즈에서 일하는 것에 자부심을 느껴왔고, 뉴욕 타임즈의 많은 엔지니어들도 그렇게 느끼고 있다고 생각합니다.
그레고르 반: 정말 흥미롭네요. 선거를 그런 식으로 생각해 본 적이 없습니다. 뉴욕 타임즈의 아마존 프라임 데이와 같은 날이지만, 1년에 한 번이 아니라 4년에 한 번씩 발생합니다.
일리아 구레비치: 네, 하지만 토론회, 중간 선거와 같은 작은 이벤트도 트래픽이 많습니다. 트래픽 패턴 추세선을 보면 안정적이다가 갑자기 급증하고 다시 안정적이다가 급증하는 것을 볼 수 있습니다. 항상 처리해야 하는 문제입니다. 마지막 질문입니다. 지금까지 알게 된 것을 바탕으로 기술 분야에 처음 입문했을 때 자신에게 해주고 싶은 조언은 무엇입니까?
일리아 구레비치: 대학 졸업 후 바로 스타트업에서 일하기 시작했을 때 엄청난 책임과 권한이 주어졌습니다. 저는 모든 것을 알고 있다고 생각했는데, 그것은 아마도... 제가 모든 것을 알고 있다고 생각했다는 것은 스타트업에서 일했고, 모든 것을 다 했고, 모든 페이지를 만들었기 때문입니다. 주니어 엔지니어였지만 약간의 자존심이 있었습니다. 어느 정도까지는 모든 사람이 이런 문제를 겪는다고 생각합니다. 제가 모르는 것들이 너무나 많다는 것을 깨닫는 데 시간이 걸렸습니다. 결코 알 수 없는 것들, 아주 잠깐 알고 있다가 모든 것을 알고 있다고 생각하는 것들도 있습니다. 겸손한 태도를 갖는 것이 매우 중요하다고 생각합니다. 소프트웨어 엔지니어는 까다로운 사람들이 될 수 있습니다. 이미 결론을 내린 상태로 대화에 참여하거나 매우 독단적이 되기 쉽습니다. 예를 들어, Reddit에 블로그 게시물을 올렸는데 "뉴욕 타임즈에 왜 React를 사용했나요? Vanilla JS를 사용하지 않은 이유는 무엇인가요?"라는 댓글이 달렸습니다. 물론 답이 있어야 합니다. 저는 "왜 그렇게 해야 하지? React가 정답인데"라고 생각했습니다. 자신이 선택한 기술에 집착하고 "그들의 말이 맞을 수도 있다"라고 생각하지 않는 것은 충동적인 행동입니다. Vanilla JS를 사용하는 것이 더 나을 수도 있습니다. 모든 것을 다시 작성할 것이라는 말은 아닙니다. 하지만 호기심을 가지고 아이디어를 살펴보고 열린 마음으로 토론하는 것은 제가 일찍 배웠으면 하는 것이고, 다른 사람들도 그렇게 하기를 바랍니다.
그레고르 반: 저도 완전히 공감합니다. 소프트웨어 엔지니어링 회사를 처음 시작했을 때 불행히도 처음 10년 동안은 공동 설립자로서 사장이었기 때문에 모든 것을 알고 있다고 생각했지만, 곧 그렇지 않다는 것을 알게 되었습니다. 호기심을 갖는 것이 중요합니다. 특히 개발자들은 까다롭기 때문에 무언가에 도전할 준비가 되어 있습니다. 하지만 도전하는 방식은 매우 다를 수 있고, 매우 협력적일 수도 있습니다.
일리아 구레비치: 네, 맞습니다.
그레고르 반: 정말 좋은 마무리 말씀입니다. 많은 엔지니어들에게 도움이 될 것이라고 생각합니다. 특히 이제 막 시작하는 엔지니어들에게 도움이 될 것입니다. 물론 경력이 많은 엔지니어들도 이런 사고방식을 채택하지 않은 경우가 있습니다. 정말 멋진 말씀입니다. 사람들이 기사를 어디서 찾을 수 있고, 다른 홍보하고 싶은 것이 있습니까?
일리아 구레비치: Google에서 "nyt open medium"을 검색하면 모든 콘텐츠를 게시하는 nyt open 팀을 찾을 수 있습니다. 기사 제목은 "React 18로 뉴욕 타임즈 웹 성능 향상"입니다. 그 외에 홍보할 것은 없습니다. 저에게는 큰 작업이었고, 지금까지 이룬 성과가 자랑스럽습니다.
그레고르 반: 수고하셨습니다. 시간 내주셔서 감사합니다. 앞으로 다시 만날 수 있기를 바랍니다.
일리아 구레비치: 감사합니다.