각 페이지마다 사용자의 인증 정보를 요구하여 반복해서 서버에 사용자 인증 정보를 요청하고 있는 문제가 있었습니다. 이로 인해서 각 페이지마다 모두 동일한 네트워크 요청을 반복하는 문제가 발생하여 개선할 필요가 있다는 것을 느꼈습니다.
한 페이지에서 헤더에 사용자 정보를 표시하기 위한 요청과 페이지에서 사용될 사용자 정보를 요청하며 중복이 발생하고 있습니다. 이러한 문제가 페이지를 이동할 때마다 중첩되어서 너무 많고 불필요한 네트워크 요청이 쌓이고 있는 것을 확인할 수 있었습니다.
개선하기 위한 CS 지식의 선정
네트워크 효율성
선정 이유 : 불필요한 네트워크 요청이 증가하면 대역폭 사용량이 증가하며, 이는 클라이언트와 서버 간의 네트워크 리소스를 비효율적으로 소모합니다. 이는 사이트의 성능 저하로 이어지기 때문에 중요한 CS적 지식이라 판단하여 선정
학습 내용 : 네트워크 오버헤드를 줄이기 위한 압축(예: Gzip, Brotli) 적용 및 이미지 최적화, 리소스 로딩 순서, 지연 로딩(lazy loading) 등으로 초기 렌더링을 빠르게 함
상태관리
선정 이유 : 중앙에서 데이터 흐름을 제어하고, 필요한 곳에만 효율적으로 전달할 수 있는 상태관리 기법(Redux, MobX, Recoil 등)은 애플리케이션 구조를 단순화하고 유지보수를 쉽게 해줌으로 가치가 있다 판단하여 선정하였습니다.
학습 내용
문제 해결 과정
문제 해결을 위해 코드를 작성하기 이전에 문제를 해결 했을 경우에 대한 성공 시나리오를 테스트 코드로 작성하였습니다. 사용자 시나리오에 따른 결과 테스트틑 e2e 테스트에 해당하고 테스트를 위해서는 playwright로 테스트를 진행하는 것이 가장 편리할 것이라고 판단하였기 때문에 playwright를 이용하여 테스트 코드를 작성하였습니다.
프로젝트의 문제를 해결 했을 경우에는 인증이 되지 않은 상태로 페이지를 이동하더라도 위와 같은 결과를 만들어낼 수 있을 것이라고 판단하여 테스트 코드를 작성하였습니다.
사용자 정보 중 노출이 되어도 상관이 없고 강제로 변경이 되더라도 지장이 없을만한 정보를 추출 후 Recoil을 이용하여 사용자 정보 상태 생성 했습니다. Recoil을 이용하여 만들어낸 전역 상태를 활용하기 위해서는 기존의 라우트 계층에서 이루어지던 검증을 클라이언트 계층에 분산 시켜줄 필요가 있다고 판단 했습니다.
불필요한 네트워크 정보를 줄이기 위해서는 사용자의 인증 정보를 어딘가에 저장해 놓을 필요가 있다고 생각 했습니다..Recoil은 atom을 활용하여 코드 분할을 손상시키지 않고 앱 전체의 모든 상태 변경을 관찰하여 관리가 가능함으로 적합하다고 판단했습니다.
ProtectedRoute에서는 사용자 인증 정보에 관련한 검증을 상태 변수를 이용하여 검증하고 이외의 서버의 요청이 필요할 경우에만 라우트 계층에서 네트워크 요청을 이용하여 검증을 진행하였습니다.
문제 해결
테스트 코드를 작성할 당시에 기대했던 결과를 만들어 냈음을 테스트 코드를 이용하여 확인할 수 있었습니다.
문제 해결 방법 적용 이전의 상태
문제 해결을 적용한 후의 상태
RecoilRoot를 상위 트리에 배치하여 모든 페이지에서 필요한 정보를 업데이트하고 반환할 수 있게 끔 구조를 변경 했습니다. 특정 상황 예를 들어 새로고침을 하거나 사용자 정보가 갱신 되었을 경우에는 네트워크 요청을 이용하여 정보를 갱신할 수 있게 끔 수정
기존의 초기 페이지 로딩 시 2번의 네트워크 요청, 이후의 페이지를 이동할 때마다 추가로 필요로 하는 네트워크 요청이 계속 2번씩 중첩되던 문제를 사용자 정보가 갱신되지 않는 이상 1번의 네트워크 요청을 이용하여 상태를 관리할 수 있게 개선하였습니다.
또 다른 프로젝트 내의 문제
프로젝트 빌드 시 최적화가 적절하게 이루어지지 않아 페이지 로드 시간을 저하 시키는 문제가 발생했습니다.
Lighthouse를 이용하여 성능을 분석한 결과 개선이 필요한 점수인 65점이 평가가 되었음을 확인되었습니다.페이지에서 로드하는 데이터가 많지 않았음에도 불구하고 좋지 못한 점수가 나온다는 것은 최적화를 잘 진행하지 못했다는 것을 의미 합니다. 성능 저하의 문제는 결국 사용자 경험을 저하 시키는 중요한 문제이기 때문에 개선이 필요하다 판단했습니다.
개선하기 위한 CS 지식의 선정
Lighthouse 성능 측정의 의미
선정 이유 : Lighthouse는 실제 사용자가 사용했을 경우의 시나리오에 따라 성능에 대한 가중치를 더하여 점수를 계산하기 때문에 이 점수가 어떤 것에 영향을 받고 어떻게 개선하는지 학습하는 것이 가치가 있을 것이라고 판단
학습 내용 :
웹 성능 주요 지표(First Contentful Paint, Largest Contentful Paint, 등)의 의미와 개선 방법 이해
Lighthouse 리포트를 분석하여 웹페이지 로딩 과정에서 병목이 되는 영역(예: 이미지, 스크립트 로딩 등)을 파악하고 개선 방법 찾기
청크 스플릿
선정 이유 : buld 시 성능에 영향을 주는 것이 코드를 어떻게 분리 했는지에 따라 성능의 많은 영향을 주기 때문에 학습하고 개선하는 것이 가치가 있을 것이라고 판단
학습 내용 :
Vite 번들러에서 제공하는 코드 스플리팅 기법과 설정 이해
정적 자산 분할, 지연 로딩(lazy loading), 동적 임포트 등을 통해 페이지별 혹은 기능별 스플릿 전략 설계에 대한 이해
문제 해결 과정
Rollup visualizer를 이용하여 현재 프로젝트의 번들 트리 구조, 번들의 크기와 모듈 간의 관계를 시각 정보를 활용하여 분석 했습니다.
Rollup visualizer을 이용하여 현재 프로젝트의 트리 구조등을 시각화 한 결과입니다.
위의 정보를 이용하여 분석을 한 결과 현재 3D 모델을 위해서 사용하는 Three.js 패키지가 적절히 분리되지 않았고 react 패키지 또한 많은 패키지가 포함되어 있음을 확인 가능 했습니다.
build 시 현재 패키지에 1000kB를 넘어가는 청크가 존재하기 때문에 최적화가 필요함을 경고하고 있어 vite.config 설정을 이용하여 청크 스플릿이 필요하다 판단하였습니다.
위와 같이 청크 스플릿을 수정하였지만 오히려 성능이 저하되고 있음을 발견 했습니다. 문제 분석 결과 node_modules 이외의 애플리케이션 코드를 스플릿 할 경우 크기가 너무 작은 코드를 스플릿 하는 것은 오히려 구조를 복잡하게 만들어서 성능을 저하 시킬 수 있다는 것을 발견하여 수정하였습니다.
애플리케이션 코드를 제외하고 패키지에서 많은 비중을 차지하고 있는 패키지를 대상으로 청크 스플릿을 세분화를 진행하였습니다.
애플리케이션 코드에서 icon 코드들이 불필요하게 큰 비중을 차지하고 있는 문제
해결 방법을 적용하여 src 전체 크기가 감소하고 icon이 차지하는 비율도 현저하게 감소한 결과
이외에도 트리 구조, 번들의 크기가 불필요하게 크거나 복잡한 경우를 발견하고 개선하는 작업을 반복해서 진행하여 최적화를 진행하였습니다.
문제 해결
Lighthouse 점수 개선
앞선 Lighthouse 점수가 65점에서 90점으로 개선되었음을 확인할 수 있었습니다. 성능 개선을 위한 청크 스플릿을 통해 번들의 크기를 줄이고, 필요한 코드만 로드하여 페이지 로딩 시간을 단축시키는 데 성공하였습니다.
manualChunks(id) { if (id.includes("node_modules")) { if (id.includes("@tanstack")) return "vendor-tanstack"; if (id.includes("three")) return "vendor-three"; if (id.includes("react")) return "vendor-react"; if (id.includes("@socket")) return "vendor-socket"; return "vendor"; } if (id.includes("/features/")) { const feature = id.split("/features/")[1].split("/")[0]; return `feature-${feature}`; }}
(!) Some chunks are larger than 1000 kB after minification. Consider:- Using dynamic import() to code-split the application- Use build.rollupOptions.output.manualChunks to improve chunking: <https://rollupjs.org/configuration-options/#output-manualchunks>- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
manualChunks(id) { if (id.includes("node_modules")) { if (id.includes("@tanstack")) return "vendor-tanstack"; if (id.includes("@react-three/cannon")) return "vendor-react-three-cannon"; if (id.includes("@react-three/drei")) return "vendor-react-three-drei"; if (id.includes("@react-three/fiber")) return "vendor-react-three-fiber"; if (id.includes("three")) return "vendor-three"; if (id.includes("@socket")) return "vendor-socket"; if (id.includes("react")) return "vendor-react"; return "vendor"; }}