정적 콘텐츠 비중이 높은 사이트의 특성상, 페이지 로딩 속도와 SEO 최적화가 중요했습니다. 초기에는 CSR을 사용하였으나, 이는 로딩 속도 저하와 검색 엔진 최적화에 문제가 있었습니다. SSG를 통해 빌드 시점에 모든 정적 페이지를 미리 생성하였습니다.
Pagination
페이지네이션을 라이브러리를 사용하지 않고 경량화를 목표로 Context API를 사용했습니다. 간결한 코드로 구현하여 개발 효율성을 높이고 향후 유지 보수를 용이하게 하였습니다.
Blurred Image
이미지 로딩 시 공백화면이나 로딩스피너 보다 더 나은 시각적 경험을 제공하고자 했습니다. Next Image에서 placeholder="blur" 옵션을 사용하여 이미지가 로드되기 전까지 블러 이미지를 표시하였습니다. Sharp를 사용해 원본 이미지로부터 작은 크기의 블러 이미지를 생성하고, 이를 Base64 형식으로 인코딩하였습니다.
Google model-viewer & Three.js
가볍게 보여주기만 해야하는 GLB 모델의 경우 구글의 model-viewer를 사용했습니다. 광원 효과 등 여러 효과를 적용하려한 모델의 경우는 Three.js 라이브러리를 사용해 작품 3D 뷰어를 만들었습니다.
Puppeteer와 Github Action
특정 선수의 모든 골 수를 실시간으로 표시하고자 했는데 관련 API들이 대부분 유료이거나 사용법이 복잡하여 적합하지 않았습니다. Puppeteer를 사용하여 위키백과에서 해당 선수의 최신 골 정보를 자동으로 수집하였습니다. GitHub Actions를 활용하여 스크립트를 6시간마다 자동으로 실행되도록 설정하였습니다. 스크립트 실행 결과를 JSON 형식의 파일로 저장하고, 이를 API 형태로 제공하였습니다. 이를 통해 별도의 서버 관리 없이도 지속적인 데이터 업데이트가 가능하도록 하였습니다.
💥 트러블 슈팅
Next.js Image의 성능이슈
Next Image를 사용하여 최적화 및 성능 향상을 기대하였으나, 실제로는 페이지 로딩 속도가 느려지고 LCP 지표가 악화되는 문제가 발생하였습니다. priority 옵션을 추가하여 주요 이미지의 우선순위를 높여 LCP 지표 개선을 시도하였습니다. 추가 성능 문제의 경우는 unoptimized 옵션을 설정하여 최적화 기능을 비활성화하였습니다. 다양한 시도를 통해 로딩 속도를 향상시켰고 Lighthouse 퍼포먼스 점수가 80점 대에서 95점 이상으로 상승하였습니다.
Request Animation Frame 관련 문제
클래스로 작성된 Three.js 인스턴스를 생성하고 render 메서드를 호출할 때 requestAnimationFrame이 정상적으로 작동하지 않는 문제가 발생하였습니다. 클래스 메서드 내에서 rAF을 호출할 때, this의 참조가 올바르게 유지되지 않고, 컴포넌트 생명주기와 Three.js의 애니메이션 루프가 제대로 조화되지 않아 rAF이 예상대로 작동하지 않았습니다. 클래스 인스턴스의 메서드에서 호출하는 대신, useEffect 내부에서 직접 rAF을 호출하여 애니메이션 루프를 관리했습니다.