본문으로 건너뛰기

안동민 개발노트

본문 시작

코드 리뷰 및 최적화

북스토어 코드를 책임·가독성·안전성 기준으로 리뷰하고 이미지·폰트·데이터·번들 성능을 측정해 개선합니다.

실전 프로젝트를 마무리할 때 코드 리뷰최적화는 반드시 거쳐야 하는 단계입니다.

기능이 동작하는 수준을 넘어 코드를 더 효율적이고 유지보수하기 쉽게 만들고, 성능까지 끌어올리는 데 직접 기여하기 때문입니다.

이 절에서는 코드 리뷰의 핵심 관점과 일반적인 최적화 기법, 성능 개선 도구 활용법을 다룹니다.

아래 다이어그램은 코드 리뷰와 최적화를 기능 완료 뒤의 반복 품질 루프로 정리합니다.

HTML 다이어그램: /docs/next/ch17/ch17-3/1.html

코드 리뷰의 중요성 및 방법

코드 리뷰는 변경된 코드의 정확성, 유지보수성, 성능 영향, 테스트 범위를 검토하는 과정입니다.

리뷰에서는 단순한 스타일보다 요구사항 충족 여부와 회귀 위험을 먼저 확인합니다.

코드 리뷰의 이점

  • 버그 조기 발견: 개발 초기 단계에서 논리적 오류나 잠재적 버그를 발견하여 수정 비용을 절감합니다.
  • 코드 품질 향상: 가독성, 유지보수성, 재사용성 측면에서 더 나은 코드를 작성하도록 유도합니다.
  • 지식 공유 및 팀 역량 강화: 팀원 간에 기술과 모범 사례를 공유하고, 서로의 강점과 약점을 파악하여 학습 기회를 제공합니다.
  • 일관성 유지: 코딩 스타일, 아키텍처 패턴 등 프로젝트 전반의 일관성을 유지하는 데 도움이 됩니다.
  • 보안 취약점 발견: 잠재적인 보안 취약점을 식별하고 개선합니다.

효과적인 코드 리뷰 방법

  • 명확한 목표 설정: 무엇을 중점적으로 볼 것인지(예: 기능 정확성, 성능, 보안, 가독성) 미리 정합니다.
  • 작은 단위로 리뷰: 한 번에 너무 많은 코드를 리뷰하지 않도록 합니다. PR(Pull Request) 크기를 작게 유지하는 것이 좋습니다.
  • 긍정적인 분위기 조성: 비판보다는 건설적인 피드백에 집중하고, 존중하는 태도로 의견을 교환합니다.
  • 체크리스트 활용: 일반적인 코딩 표준, 보안 고려사항, 성능 가이드라인 등을 포함하는 체크리스트를 활용하여 누락 없이 검토합니다.
  • 자동화 도구 활용: ESLint, Prettier와 같은 린터(Linter)와 포맷터(Formatter)를 사용하여 기본적인 코드 스타일과 잠재적 오류를 자동으로 검사합니다. 이는 수동 리뷰의 부담을 줄여줍니다.
  • 테스트 코드 확인: 관련 테스트 코드가 충분한지, 그리고 테스트가 통과하는지 확인합니다.

리뷰 항목은 기능, 경계, 성능, 유지보수성을 나누어 보면 피드백이 감정이 아니라 근거 중심으로 정리됩니다.

HTML 다이어그램: /docs/next/ch17/ch17-3/4.html

Next.js 성능 최적화 기법

코드 리뷰 및 최적화에서는 정확성, 유지보수성, 성능 지표, 회귀 확인 기준을 정리합니다.

HTML 다이어그램: /docs/next/ch17/ch17-3/2.html

Next.js는 기본적으로 빠른 성능을 제공하지만, 복잡한 애플리케이션에서는 추가적인 최적화가 필요합니다.

이미지 최적화 (next/image)

next/image 컴포넌트는 이미지 크기 최적화, 자동 포맷 변환(WebP 등), 레이지 로딩, 뷰포트 기반 로딩 등을 자동으로 처리하여 성능을 크게 향상시킵니다.

  • 적용 방법: 일반 <img> 태그 대신 next/image를 사용하고 src와 의미에 맞는 alt를 지정합니다. 이미지 공간은 정적 import가 추론한 크기, widthheight, 또는 fill 중 하나로 확보합니다.
  • 예시
    import Image from 'next/image';
    
    function BookImage({ src, alt }) {
      return (
        <Image
          src={src}
          alt={alt}
          width={500}
          height={600}
          sizes="(max-width: 768px) 100vw, 500px"
          style={{ width: '100%', height: 'auto' }}
          placeholder="blur"
          blurDataURL="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="
          preload
        />
      );
    }

폰트 최적화 (next/font)

next/font는 웹 폰트를 자동으로 최적화하여 CLS(Cumulative Layout Shift)를 방지하고 성능을 개선합니다.

폰트 파일을 직접 다운로드하여 로컬에 저장하므로 네트워크 요청을 줄입니다.

  • 적용 방법: next/font/google 또는 next/font/local에서 폰트를 임포트하여 사용합니다.
  • 예시
    app/layout.tsx
    import { Inter } from 'next/font/google';
    
    const inter = Inter({ subsets: ['latin'], display: 'swap' });
    
    export default function RootLayout({ children }) {
      return (
        <html lang="ko" className={inter.className}>
          <body>{children}</body>
        </html>
      );
    }

스크립트 최적화 (next/script)

서드파티 스크립트(Google Analytics, 광고 스크립트 등)는 페이지 로딩 성능에 큰 영향을 미칠 수 있습니다.

next/script 컴포넌트를 사용하면 스크립트 로딩 전략을 제어할 수 있습니다.

  • 전략
    • beforeInteractive: 페이지가 상호작용하기 전에 로드 (높은 우선순위)
    • afterInteractive (기본값): 페이지가 상호작용 가능한 상태가 된 후 로드
    • lazyOnload: 브라우저가 유휴 상태일 때 낮은 우선순위로 로드
    • worker: 웹 워커에서 로드하는 실험적 전략이며 App Router에서는 지원하지 않음
  • 예시
    import Script from 'next/script';
    
    function MyAnalytics() {
      return (
        <>
          <Script
            src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
            strategy="afterInteractive"
          />
          <Script id="google-analytics" strategy="afterInteractive">
            {`
              window.dataLayer = window.dataLayer || [];
              function gtag(){dataLayer.push(arguments);}
              gtag('js', new Date());
              gtag('config', 'GA_MEASUREMENT_ID');
            `}
          </Script>
        </>
      );
    }

데이터 페칭 최적화 (캐싱 및 재검증)

Next.js의 캐싱 메커니즘을 이해하고 효율적으로 활용합니다.

  • Data Cache: Next.js 15 이후 fetch 응답은 기본적으로 캐시되지 않습니다. 반복해서 재사용할 데이터는 cache: 'force-cache'로 명시해 캐싱합니다. Next.js 16에서 cacheComponents: true를 활성화한 프로젝트라면 use cache 지시어도 사용할 수 있습니다.
  • Full Route Cache: 정적으로 렌더링한 라우트의 HTML과 React Server Component payload를 서버에 저장합니다. 개발 모드에서는 페이지를 요청 시점에 렌더링하므로 운영 캐시 동작과 구분해야 합니다.
    • Incremental Static Regeneration (ISR): 정적 생성된 페이지는 유효기간이 지난 뒤 다음 요청을 계기로 재검증하거나, 명시적인 on-demand 재검증으로 업데이트할 수 있습니다. 이는 빌드 시간을 단축하면서도 최신 콘텐츠를 제공하는 데 유용합니다.
      • App Router에서 ISR: fetchrevalidate 옵션을 사용합니다. 필요할 때 경로는 revalidatePath, 태그의 SWR 갱신은 revalidateTag(tag, 'max'), Server Action의 즉시 만료는 updateTag로 처리합니다.
      • 예시 (app/books/[id]/page.tsx에서 재검증 추가)
        app/books/[id]/page.tsx
        export const revalidate = 60; // 60초가 지난 뒤 다음 요청을 계기로 재검증
  • Static Assets Cache: /_next/static 아래의 해시 기반 빌드 자산은 장기 캐시에 적합합니다. public 파일은 같은 이름으로 교체될 수 있으므로 필요한 캐시 헤더를 배포 환경에서 별도로 정합니다.

페이지 단위 분할과 지연 로딩 설계

Next.js는 페이지 단위 코드 스플리팅을 자동으로 처리합니다.

즉, 사용자가 특정 페이지를 요청할 때 해당 페이지에 필요한 JavaScript 코드만 로드됩니다.

추가적으로, 특정 컴포넌트나 라이브러리를 필요할 때만 로드하도록 동적 임포트를 사용할 수 있습니다.

  • next/dynamic: 클라이언트 컴포넌트나 무거운 라이브러리를 레이지 로드하는 데 사용합니다.
  • 예시
    src/components/DashboardChart.tsx (클라이언트 컴포넌트)
    "use client";
    
    // 무거운 차트 라이브러리를 동적으로 로드
    import dynamic from 'next/dynamic';
    import Loading from './Loading';
    
    const DynamicChart = dynamic(() => import('../components/Chart'), {
      loading: () => <Loading />, // 로딩 중 표시할 컴포넌트
      ssr: false, // 이 컴포넌트는 클라이언트에서만 렌더링 (SSR 제외)
    });
    
    function MyDashboard() {
      return (
        <div>
          <DynamicChart />
        </div>
      );
    }

다음 다이어그램은 각 최적화 기법을 어떤 측정 신호가 보일 때 먼저 적용할지 정리합니다.

HTML 다이어그램: /docs/next/ch17/ch17-3/5.html

성능 측정 및 디버깅 도구

아래 다이어그램은 최적화 질문에 따라 Lighthouse, Chrome DevTools, Vercel Speed Insights, Bundle Analyzer를 어떻게 나눠 쓰는지 보여줍니다.

HTML 다이어그램: /docs/next/ch17/ch17-3/3.html

최적화의 효과를 확인하고 병목 현상을 식별하기 위해 다양한 도구를 활용합니다.

Lighthouse 기준선 진단

  • 용도: 웹 페이지의 성능, 접근성, SEO, PWA 점수를 종합적으로 측정하고 개선 방안을 제시합니다.
  • 사용법: Chrome 개발자 도구에서 Lighthouse 탭을 클릭하고 Analyze page load를 실행합니다.
  • 활용: Lighthouse 보고서에서 제시하는 권장 사항을 따라 코드와 설정을 개선합니다. 특히 Core Web Vitals(LCP, INP, CLS) 지표에 주목합니다.

Chrome DevTools (성능 탭, 네트워크 탭)

  • Performance 탭: 런타임 성능을 기록하고 분석하여 JavaScript 실행, 렌더링, 레이아웃 계산 등에서 발생하는 병목 현상을 시각적으로 파악합니다.
  • Network 탭: 페이지 로딩 시 발생하는 모든 네트워크 요청(JS, CSS, 이미지, API 호출)을 분석하고, 각 리소스의 크기, 로딩 시간, 캐싱 여부 등을 확인합니다. 이를 통해 불필요한 요청이나 큰 리소스를 식별할 수 있습니다.

Vercel Analytics / Speed Insights

Vercel은 배포된 Next.js 애플리케이션에 대한 내장 분석 및 성능 지표를 제공합니다.

  • Vercel Analytics: 방문자 수, 페이지 뷰, 트래픽 소스 등 일반적인 웹사이트 분석 데이터를 제공합니다.
  • Speed Insights: 실제 사용자 데이터(RUM - Real User Monitoring)를 기반으로 Core Web Vitals를 포함한 페이지 성능 지표를 모니터링합니다. Vercel 대시보드에서 기능을 활성화하고 @vercel/speed-insights를 설치한 뒤 Root Layout에 <SpeedInsights />를 렌더링해야 데이터를 수집합니다.

번들 분석기 (Bundle Analyzer)

@next/bundle-analyzer와 같은 도구를 사용하여 Next.js 애플리케이션의 JavaScript 번들 크기를 시각적으로 분석할 수 있습니다.

어떤 모듈이 가장 많은 공간을 차지하는지 파악하여 불필요한 코드나 큰 라이브러리를 식별하고 제거할 수 있습니다.

  • 설치: npm install --save-dev @next/bundle-analyzer
  • next.config.js 설정
    next.config.js
    const withBundleAnalyzer = require('@next/bundle-analyzer')({
      enabled: process.env.ANALYZE === 'true',
    });
    
    module.exports = withBundleAnalyzer({
      // Next.js config options
    });
  • macOS·Linux에서 실행: ANALYZE=true npm run build
  • PowerShell에서 실행: $env:ANALYZE='true'; npm run build

빌드가 끝나면 번들 분석 보고서가 웹 브라우저에 열립니다.

코드 리뷰와 최적화는 변경 범위, 성능 측정, 유지보수성을 기준으로 반복 점검해야 합니다.

프로젝트가 커질수록 성능 지표와 코드 품질 기준을 함께 관리합니다.

HTML 다이어그램: /docs/next/ch17/ch17-3/6.html

아래 다이어그램은 next/image, next/font, next/script, 데이터 캐싱을 요구사항, 구현 단위, 검토 신호가 이어지는 코드 리뷰 항목으로 묶어 보여줍니다.

HTML 다이어그램: /docs/next/ch17/ch17-3/8.html