Next js 기반 포트폴리오
초기 트러블 슈팅 컴포넌트는 모든 데이터를 하나의 Props 객체로 전달받는 Flat한 구조였습니다. 하지만 요구사항이 늘어남에 따라 유지보수 지옥에 빠졌습니다.
// Before: 모든 케이스를 하나의 Props로 처리 <TroubleShooting title="제목" code={codeSnippet} videoSrc={...} />
컴포넌트의 제어권을 부모에게 넘겨주는 Compound Component 패턴을 도입하여 구조를 전면 리팩터링했습니다.
// After: 필요한 요소만 선택적으로 조합 <ProjectDetail> <ProjectDetail.Header>제목</ProjectDetail.Header> <ProjectDetail.Body> <ProjectDetail.Section title="문제">...</ProjectDetail.Section> <ProjectDetail.Code>{code}</ProjectDetail.Code> </ProjectDetail.Body> </ProjectDetail>
페이지 이동 후 뒤로가기를 했을 때, 브라우저의 기본 스크롤 복원 동작이 완벽하지 않은 문제가 있었습니다.
Session Storage와 History API를 활용한 커스텀 훅 useScrollRestoration을 구현했습니다.
// useScrollRestoration.tsx useIsomorphicLayoutEffect(() => { if ('scrollRestoration' in window.history) { window.history.scrollRestoration = 'manual'; } }, []);
기존에는 상세 화면 진입 시 이미지를 요청하여 로딩 딜레이가 발생했습니다. 초기에는 Hover 방식으로 시도했으나, 모바일 환경(터치 인터페이스) 에서는 Hover 가 동작하지 않아 최적화 효과를 볼 수 없는 한계가 있었습니다.
IntersectionObserver 기반의 컴포넌트를 구현하여 In-View 의 프리로딩 시스템을 구축했습니다.
new Image() API와 decoding="async" 속성을 사용하여 DOM 렌더링 비용 없이 브라우저 캐시에만 이미지를 적재합니다.const observer = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting) { // DOM 렌더링 없이 브라우저 캐시에만 적재 const img = new Image(); img.decoding = 'async'; // 메인 스레드 블로킹 방지 img.src = src; observer.disconnect(); } }, { rootMargin: '200px' } // 뷰포트 진입 전 미리 로딩 );