uesEffect를 사용해서 특정 상태가 업데이트된 이후의 동작을 핸들링 하려고 했는데, 결과적으로 실행 순서를 제어하는 게 쉽지는 않았다. 의도치 않게 두 개 이상의 useEffect가 관리하는 상태가 물리면서 상태 업데이트 동작이 두 번씩 실행되기도 하고, A 상태가 업데이트돼서 DOM에 반영된 이후에 후속 작업을 하고 싶은데 이런 디테일한 실행 순서 관리가 마음대로 되지 않아 애를 먹었다.
useEffect 사용 경험이 많지 않아서, 계속 연습이 필요하겠다. 여러 자료들에서 'useEffect`를 두고 굉장히 다루기 까다로운 훅이라고 하는 걸 봤는데, 오늘 개발하면서 그게 좀 와닿았다. 분명히 이유가 있는 상황에서 목적에 맞게 잘 사용해야겠다.
단순 구현보단 설계에 대해 좀 더 많은 고민을 해야겠다. 분명 요구사항을 받았을 때 어떤 순서로, 어떤 내용에 대해 정리하고, 구조를 어떤 방향성으로 짜야할지 생각하는 잡힌 프레임이 좀 있을 것 같은데, 아직은 잘 모르니 시행착오를 겪기 전엔 어떤 형태로 뭘 어떻게 해야 할지 감이 안 온다. 설계가 잘 돼있어야 단단하고 견고한 프로그램을 빠르게 만들 수 있다. 설계가 중요하자. 잘 고민하고 개발에 들어가자.
훅, 커스텀 훅의 사용 위치는 함수형 컴포넌트 또는 다른 훅-커스텀 훅의 최상단이다. 그 이외의 조건문, 반복문, 중첩 함수 등에서는 훅-커스텀 훅을 사용할 수 없다. 책이나 이런저런 문서에서 여러 번 봤는데 막상 개발하다 보면 아직도 실수를 많이 한다. 다시 한번 잘 기억해 두자.
HTML 요소에 대해 특정한 데이터를 정의해 두고, 필요에 따라 그 값을 가져와 사용하고 싶을 땐 data 속성을 이용할 수 있다. 주로 data-* 형태로 사용되며, * 위치에는 어떠한 값도 올 수 있다. 예를 들어 리액트 JSX에서 data-space={0}이라고 속성을 추가할 수 있으며, 해당 값에 접근하려면 e.target.dataset 속성을 사용하면 된다. 참고로, JSX는 자바스크립트의 변수-함수명 표기법을 따르기 때문에 원래는 data-space는 dataSpace 형태로 사용해야 하겠지만, 어떠한 이유에선지 해당 속성은 data-space와 같이 구분자로 -로 사용한다.
모던 리액트 Deep Dive
SPA(Single Page Application)는 처음 HTML 파일을 서버로부터 전달받은 이후부터, 사용자 인터랙션 처리의 많은 부분을 클라이언트에서 관리하는 방식이다. 화면에 표시해야 할 데이터를 서버에 요청하는 경우는 발생할 수 있지만, 전혀 새로운 HTML 파일을 다시 받아와서 DOM을 처음부터 새롭게 리렌더링 하는 경우는 드물기 때문에, 사용자 입장에서는 깔끔한 화면 전환과 좋은 상호작용 경험을 얻을 수 있게 됐다.
자바스크립트 언어의 발전으로 웹 애플리케이션의 많은 로직 처리가 서버에서 클라이언트로 넘어왔다. 서버 입장에선 비싼 서버 비용을 들여가면서 여러 로직을 처리해 주는 게 부담이었을 수 있는데, 자바스크립트의 모듈 시스템 등장과 다양한 발전으로 인해 여러 로직을 클라이언트가 받아 보는 브라우저에서 동작하도록 할 수 있게 됐다. 그러면서 클라이언트에서 자바스크립트로 DOM을 거의 대부분 조작하는 SPA 방식은 유행하게 됐다.
SPA 방식은 클라이언트에서 자바스크립트로 많은 부분을 처리하기 때문에 많은 연산을 사용자의 컴퓨터(CPU)에서 수행하도록 만들고, 웹이 더 복잡해지고 자바스크립트 파일의 크기가 점점 더 커질수록 사용자는 이러한 불편감을 느낄 가능성이 높아진다. 실제로 지난 몇 년간 애플리케이션 사용자가 보유한 컴퓨터 기기의 성능이 엄청나게 좋아지고, 네트워크 성능(인터넷 속도)도 눈에 띄게 좋아졌지만, 실제로 사용자가 웹 애플리케이션을 로딩하거나 콘텐츠를 확인하기 위해 대기해야 하는 시간은 오히려 증가했다고 한다. 하드웨어의 발전을 상쇄할 만큼 클라이언트의 비중이 더 크게 무거워졌다는 의미이다.
그런 상황에 대안이 되고 있는 게 서버 사이드 렌더링(Server Side Rendering), 줄여서 SSR이다. SSR은 대부분 MPA(Multi Pages Application)이다. 그리고, 화면의 전환이 필요한 상황에 서버에서 미리 필요한 정적 파일(HTML, CSS 등)을 렌더링 한 다음, 렌더링 된 결과를 클라이언트로 내려준다.
클라이언트는 직접 정적 파일을 파싱 한다거나, 빈 HTML 파일에 필요한 DOM 요소를 추가 및 관리하는 것을 직접 해야 하는 부담에서 자유로울 수 있다. 이런 특징으로 인해 점점 더 복잡도가 올라가는 웹 애플리케이션 생태계에서 SPA의 단점을 커버하기 위한 대한으로 각광받고 있다.
많은 기술들이 그렇지만, SSR도 마찬가지로 장단점이 있다.
장점:
최초 페이지 진입 시, 서버에서 필요한 정적 파일을 전부 분석하고 사전에 렌더링 해 결과물을 내려주기 때문에 조금 더 첫 렌더링이 빠르다는 장점이 있다. 클라이언트에서 정적 파일을 분석하고 렌더 트리를 만들려면 여러 차례의 HTTP 요청이 서버로 오가야 하기 때문에 처리 속도가 좀 더 늦어지고(API 요청에 의존되고), 많은 경우 클라이언트인 사용자가 자신의 PC 환경에서 정적 파일들을 처리하는 것보다 서버에 처리하는 게 더 빠르기도 하다. 하지만, 서버에 부하가 크게 걸리거나(접속자가 갑자기 몰리거나), 다른 이유로 서버에서 사전에 렌더링 하는 데 문제가 있을 수도 있기 때문에, 모든 경우에서 최초 페이지 진입 시 렌더링이 더 빠른 것은 아니다.
CSR(클라이언트 사이드 렌더링)에선 어떤 DOM 요소는 먼저 준비돼서 화면에 먼저 보이고, 다른 DOM 요소는 나중에 처리돼서 화면에도 늦게 보일 수 있다. 이때, DOM 요소들 사이에 잡고 있는 자리에는 서로의 위치값이 영향을 미치기 때문에, 늦게 추가되는 요소가 있으면 기존에 먼저 렌더링 된 요소가 자리에서 밀리거나, 또는 빨리 렌더링된 요소의 모양이 처음에 이상하다 나중에 정상으로 돌아오는 문제(이런 문제를 '누적 레이아웃 문제'라고 한다)가 발생할 수 있다. 렌더링 과정의 순서를 개발자가 잘 조정해서 모든 요소가 준비됐을 때 한 번에 화면을 바꾸면 좋지만, 그런 처리를 클라이언트에서 해주는 게 그렇게 간단하진 않다. 하지만 SSR은 서버에서 모든 요소를 렌더링 하고, 완료되면 한 번에 클라이언트로 보내지기 때문에, 누적 레이아웃 문제에서 상대적으로 자유롭다. 물론, 클라이언트에서 React useEffect처럼 실행 순서를 동적으로 제어하는 코드가 있다면 SSR에서도 해당 문제가 발생할 수 있기 때문에 완전한 것은 아니다.
CSR은 사용자 디바이스에서 정적 파일을 분석하고 렌더링 하기 때문에, 사용자 디바이스 성능에 많이 의존한다. 하지만, SSR은 일관되게 설정해둘 수 있는 서버의 성능에 의존해서 렌더링하기 때문에 상대적으로 안정적인 렌더링 성능을 가질 수 있다.
CSR은 클라이언트에서 로직을 처리하기 위한 다양한 정보들이 브라우저 환경에 저장이 되며, 이는 보안을 취약하게 하는 요인이 된다. 클라이언트에서도 이러한 보안 이슈를 대응해 주는 게 가능하긴 하지만, 그럴수록 클라이언트 코드가 점점 더 비대하고 무거워질 것이다. SSR은 사용자에게 노출되지 않는 서버에서 민감 정보를 관리할 수 있기 때문에 이러한 보안 문제에 있어서 CSR 대비 좀 더 자유롭다.
SEO 최적화에 유리하다. SEO를 위해 도는 구글/네이버 등의 크롤러는 웹 페이지의 정적 파일을 다운로드해서 읽어가지만, 그 안에 있는 자바스크립트 코드를 실행하진 않는다. CSR 같은 경우 기본 HTML은 사실상 빈 껍데기이고, 동적으로 DOM에 필요한 요소들을 추가하거나 관리해 준다. 그러니, 자바스크립트 코드를 실행하지 않는 크롤러 입장에서 CSR 페이지는 사실상 콘텐츠가 거의 없는 빈 껍데기에 가까울 수 있다. 반면, SSR은 요소들이 꽉 찬 페이지가 클라이언트로 내려가고, SEO를 위한 머신들이 해당 페이지의 메타 정보를 가져가기 때문에, 상대적으로 SEO 최적화에 있어 유리하다는 장점이 있다.
단점:
코드를 작성할 때 서버 환경을 고려해야 한다. 대표적으로 SSR 코드에 브라우저의 전역 객체인 window를 호출하면 에러가 날 수 있다. 이런 상황에 대해 미리 어떤 문제가 발생할지 파악하고 미리 꼼꼼한 처리를 해두는 게 필요하다.
CSR은 웹 애플리케이션의 목적에 따라 서버가 없이도 의미 있는 기능을 할 수도 있다. 하지만, SSR은 정적 파일을 렌더링 해줄 서버가 반드시 존재해야 한다. 서버 구현이 필수라는 점은 서비스의 목적에 따라서는 부담이 되는 요소일 수 있다.
CSR은 렌더링이 다 끝나지 않더라도 일부 요소라도 화면에 미리 보여줄 수 있기 때문에 사용자에게 '지금 로딩 중이다'라는 걸 명확하게 인지시키는 데 유리하다. 하지만, SSR은 서버에 어떤 문제가 생겨 최초 렌더링에 시간이 오래 지연될 경우 이에 대한 커스텀한 안내를 내려주는 것이 곤란하다는 단점이 있다.
당연한 얘기겠지만, CSR과 SSR 중 어떤 방법이 절대적으로 우월한 것은 아니다. 각각의 장단점이 있기 때문에, 상황에 맞게 적절하게 선택해서 애플리케이션을 만들어야 한다.
특별히, Next.js 등 최신 SSR 프레임워트들은 두 방법을 적절하게 절충한다. 최초 페이지 진입 시에는 SSR로 서버에서 렌더링 된 결과를 한 번에 받고, 이후 사용자가 웹 애플리케이션에서 인터랙션을 할 땐 적절하게 라우팅 처리를 하면서 CSR로 필요한 정보를 요청하는 방식이다. 이렇게 Next.js는 사용자가 최상의 웹 애플리케이션 사용 경험을 가질 수 있게 적절히 CSR과 SSR 요소를 섞을 수 있게 만들어졌다.