IT Knowledge/Development/images/성능-최적화-diagram.svg

📌 핵심 개념

쉬운 비유: 패스트푸드점 주문. 빨리 받으려면:

  1. 주방이 빨라야 함 (서버 성능)
  2. 줄이 짧아야 함 (네트워크)
  3. 자주 주문하는 메뉴는 미리 준비 (캐싱)

AI 요약 보고서

  • 웹 성능 최적화를 위한 핵심 전략과 구체적 방법론을 제시한다.
  • 프론트엔드 최적화에서는 이미지 압축(WebP 포맷, 반응형 이미지, Lazy Loading), 코드 스플리팅(라우트별 번들 분리), 메모이제이션을 통한 재계산 방지 등을 강조한다.
  • 백엔드에서는 데이터베이스 쿼리 최적화(JOIN 활용), 캐싱(Redis 활용), CDN 도입이 성능 향상에 중요하다고 설명한다.
  • 특히, N+1 문제 해결과 캐시 전략을 통해 쿼리 수와 응답 시간을 크게 단축할 수 있으며, CDN 활용으로 전송 속도를 개선
  • 성능 측정 지표로는 웹 Vitals(LCP, FID, CLS)를 제시하며, 응답 시간 모니터링을 통해 병목 구간을 파악하는 방법도 포함
  • 전반적으로 이미지 최적화, 코드 분할, 캐싱, 데이터베이스 쿼리 최적화, CDN 활용 등 다각적 접근이 필요하며, 이를 위한 체크리스트와 참고 자료도 제공한다. 이러한 전략들은 웹사이트의 로드 속도와 사용자 경험을 극대화하는 데 필수적이며, 실무 적용을 위한 구체적 예제와 권장 사항을 포함한다.

🎯 프론트엔드 최적화

1. 이미지 최적화

쉬운 비유: 전화로 사진 공유할 때 원본(10MB)이 아닌 압축본(100KB)을 보내기. 화질은 비슷한데 훨씬 빠릅니다.

Before:

<!-- ❌ 5MB 원본 이미지 -->
<img src="/images/product.jpg" />

After:

<!-- ✅ 최적화 -->
<!-- 1. WebP 포맷 (50% 더 작음) -->
<picture>
  <source srcset="/images/product.webp" type="image/webp" />
  <source srcset="/images/product.jpg" type="image/jpeg" />
  <img src="/images/product.jpg" alt="상품" />
</picture>
 
<!-- 2. 반응형 이미지 -->
<img
  srcset="
    /images/product-small.jpg 400w,
    /images/product-medium.jpg 800w,
    /images/product-large.jpg 1200w
  "
  sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
  src="/images/product-medium.jpg"
  alt="상품"
/>
 
<!-- 3. Lazy Loading -->
<img src="/images/product.jpg" loading="lazy" />

2. 코드 스플리팅

쉬운 비유: 책 전체를 다운로드하지 않고 필요한 챕터만 먼저 받기.

Before:

// ❌ 모든 컴포넌트 한 번에 로드 (번들 크기: 2MB)
import Dashboard from './Dashboard';
import Settings from './Settings';
import Analytics from './Analytics';
 
function App() {
  return (
    <Router>
      <Route path="/dashboard" component={Dashboard} />
      <Route path="/settings" component={Settings} />
      <Route path="/analytics" component={Analytics} />
    </Router>
  );
}

After:

// ✅ 라우트별 코드 스플리팅
import { lazy, Suspense } from 'react';
 
const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));
const Analytics = lazy(() => import('./Analytics'));
 
function App() {
  return (
    <Router>
      <Suspense fallback={<Loading />}>
        <Route path="/dashboard" component={Dashboard} />  {/* 이 페이지만 로드 */}
        <Route path="/settings" component={Settings} />
        <Route path="/analytics" component={Analytics} />
      </Suspense>
    </Router>
  );
}
 
// 초기 로딩: 500KB → 나머지는 필요할 때

3. 메모이제이션

쉬운 비유: 계산기로 “123 × 456” 계산 결과를 메모장에 적어두기. 다음에 같은 계산하면 메모 보고 바로 답변.

Before:

// ❌ 매번 재계산
function ProductList({ products }) {
  const expensiveProducts = products.filter(p => p.price > 100000);  // 매 렌더링마다 실행
 
  return (
    <div>
      {expensiveProducts.map(p => <ProductCard key={p.id} product={p} />)}
    </div>
  );
}

After:

import { useMemo, memo } from 'react';
 
// ✅ 결과 캐싱
function ProductList({ products }) {
  const expensiveProducts = useMemo(
    () => products.filter(p => p.price > 100000),
    [products]  // products가 바뀔 때만 재계산
  );
 
  return (
    <div>
      {expensiveProducts.map(p => (
        <MemoizedProductCard key={p.id} product={p} />
      ))}
    </div>
  );
}
 
// ProductCard도 메모이제이션
const MemoizedProductCard = memo(function ProductCard({ product }) {
  return (
    <div>
      <h3>{product.name}</h3>
      <p>{product.price}원</p>
    </div>
  );
});

🔧 백엔드 최적화

1. 데이터베이스 쿼리 최적화

쉬운 비유: 슈퍼마켓에서 물건 하나씩 10번 사는 것(N+1 문제) vs 장바구니에 담아 한 번에 계산(JOIN).

Before:

// ❌ N+1 문제
app.get('/posts', async (req, res) => {
  const posts = await db.posts.findAll();  // 쿼리 1번
 
  // 각 게시글마다 작성자 조회 (쿼리 100번)
  for (const post of posts) {
    post.author = await db.users.findById(post.authorId);
  }
 
  res.json(posts);
});
 
// 총 쿼리: 101번 (1 + 100)

After:

// ✅ JOIN으로 한 번에
app.get('/posts', async (req, res) => {
  const posts = await db.query(`
    SELECT
      posts.*,
      users.name as author_name,
      users.email as author_email
    FROM posts
    JOIN users ON posts.author_id = users.id
  `);
 
  res.json(posts);
});
 
// 총 쿼리: 1번 (100배 빠름!)

2. 캐싱

쉬운 비유: 자주 묻는 질문(FAQ)을 게시판에 올려두기. 매번 직원에게 물어보지 않아도 됨.

Before:

// ❌ 매번 DB 조회
app.get('/api/popular-products', async (req, res) => {
  const products = await db.query(`
    SELECT * FROM products
    ORDER BY sales_count DESC
    LIMIT 10
  `);
 
  res.json(products);
});

After:

const redis = require('redis');
const client = redis.createClient();
 
// ✅ Redis 캐싱
app.get('/api/popular-products', async (req, res) => {
  const cacheKey = 'popular_products';
 
  // 1. 캐시 확인
  const cached = await client.get(cacheKey);
  if (cached) {
    return res.json(JSON.parse(cached));  // 즉시 응답 (매우 빠름)
  }
 
  // 2. 캐시 없으면 DB 조회
  const products = await db.query(`
    SELECT * FROM products
    ORDER BY sales_count DESC
    LIMIT 10
  `);
 
  // 3. 캐시 저장 (10분)
  await client.setex(cacheKey, 600, JSON.stringify(products));
 
  res.json(products);
});
 
// 첫 요청: 500ms
// 이후 요청: 5ms (100배 빠름!)

3. CDN 활용

쉬운 비유: 서울에 있는 서버에서 부산 사용자가 파일 받으면 느림. 부산에도 복사본 두면 빠름.

// ❌ 서버에서 직접 제공
app.get('/images/:filename', (req, res) => {
  res.sendFile(`/uploads/${req.params.filename}`);
});
 
// ✅ CDN 사용
// S3 + CloudFront
const imageUrl = `https://cdn.myapp.com/images/${filename}`;
 
// 효과:
// - 서울→부산: 100ms → 10ms
// - 서버 부하 감소

📊 성능 측정

웹 Vitals (중요 지표)

// 1. LCP (Largest Contentful Paint)
// 가장 큰 콘텐츠 표시 시간
// 목표: 2.5초 이내
 
// 2. FID (First Input Delay)
// 첫 입력 반응 시간
// 목표: 100ms 이내
 
// 3. CLS (Cumulative Layout Shift)
// 레이아웃 이동
// 목표: 0.1 이하
 
// 측정
import { getCLS, getFID, getLCP } from 'web-vitals';
 
getCLS(console.log);
getFID(console.log);
getLCP(console.log);

백엔드 성능

// 응답 시간 측정
app.use((req, res, next) => {
  const start = Date.now();
 
  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.url}: ${duration}ms`);
 
    if (duration > 1000) {
      console.warn('Slow request detected!');
    }
  });
 
  next();
});

💡 성능 체크리스트

프론트엔드

  • 이미지 최적화 (WebP, 압축)
  • Lazy Loading
  • 코드 스플리팅
  • 번들 크기 확인 (webpack-bundle-analyzer)
  • React.memo, useMemo 사용

백엔드

  • 데이터베이스 인덱스
  • N+1 쿼리 제거
  • Redis 캐싱
  • gzip 압축
  • CDN 사용

공통

  • HTTPS/2 사용
  • 불필요한 라이브러리 제거
  • 프로덕션 빌드 사용

🔗 참고 자료