[태그:] 최적화

  • Core Web Vitals 대응

    Core Web Vitals?

    구글이 사용자 경험을 수치화하기 위해 제시하는 핵심 성능 지표.

    2021년부터는 검색 순위에도 직접 반영되고 있어서 단순한 성능 최적화를 넘어 비즈니스 성과와도 직결된다.

    이 지표들은 단순히 빠르고 느리고에서 결정되는 것이 아닌, 실제 사용자 경험에서 반영, 비롯된다.

    Core Web Vitals의 세 가지 주요 지표

    LCP(Largest Contentful Paint)

    사용자가 페이지에서 가장 큰 콘텐츠 요소(이미지, 동영상, 큰 텍스트 블록 등)를 보는데 소요되는 시간.

    페이지를 인식하는 순간을 의미한다.

    (권장 ≤ 2.5초)

    로딩이 늦으면 사용자는 사이트가 느리다고 느끼고,
    첫인상이 중요한 페이지(e.g. 랜딩 페이지)에서 LCP 지연은 이탈율에 직결된다.

    INP(Interaction to Next Paint)

    사용자가 페이지에서 클릭/탭/키 입력을 했을 때, 화면이 반응하는 속도.

    (권장 ≤ 200ms)

    단순히 클릭에 의한 반응 속도가 아닌, 사용자의 모든 입력을
    브라우저가 화면에 그 변화를 반영하는 데 걸린 전체 시간을 측정하는 지표.

    → 사용자가 200ms 이상 기다리면 버벅거린다고 느끼게 된다.

    CLS(Cumulative Layout Shift)

    페이지 로딩 중 예상치 못한 레이아웃 이동 정도.

    (권장 ≤ 0.1)

    페이지 로딩 중 갑자기 버튼이 아래로 밀리거나, 텍스트가 튀면서 광고가 도중에 삽입되는 현상,

    이러한 예기치 못한 움직임으로 인해 사용자 경험이 저해되는지를 측정하는 지표.

    LCP 최적화

    문제 원인

    • 첫 화면에 있는 큰 이미지나 Hero 배너 로딩이 느린 경우
    • 렌더링 차단 리소스(CSS/JS)가 많아 페인트가 지연되는 경우

    *Hero 배너

    웹 사이트 첫 화면의 가장 눈에 띄는 대형 영역(이미지, 비디오 등).

    해결 방법

    1. 이미지 최적화


      • Next.js 의 next/image 컴포넌트 활용. → 자동 압축/리사이징/WebP 변환

      • Hero 영역 이미지는 반드시 priority 속성 추가.
      • next/image는 기본적으로 이미지를 lazy loading 처리. but 첫화면 렌더 이미지는 즉시 불러와야 하는데,priority를 추가하면 네트워크 요청 우선 순위를 올려 해당 이미지를 먼저 다운로드하게 됨.

    *WebP

    구글이 개발한 차세대 이미지 포맷, JPG/PNG 대비 평균 30% ~ 40% 용량 절감.같은 화질을 더 작은 용량으로 표현 가능 → 네트워크 전송 시간을 단축함으로 LCP를 개선할 수 있다.

    대부분의 브라우저에서 지원 중.

    1. 폰트 최적화

      • next/font 를 활용하여 Google Fonts를 직접 번들에 포함시킴
      • font-display: swap으로 폰트 로딩 시 FOUT(깜빡임) 최소화
    2. Critical CSS 최소화


    페이지가 처음 그려지는데 꼭 필요한 최소한의 CSS를 Critical CSS라 한다.

    즉 필수 스타일만 먼저 로드하고, 나머지는 lazy loading 처리하는 것이 효율적.

    • 사용하지 않는 CSS 제거(Tree Shaking)
    • CSS-in-JS 라이브러리 사용 시, 서버사이드에서 Critical CSS를 추출.
    1. 서버 응답 속도 개선

    API 요청 지연을 줄이고, CDN 캐싱 적극 활용.

    INP 최적화

    문제 원인

    • 메인 스레드를 오래 점유하는 무거운 JavaScript 실행.
    • 이벤트 핸들러 내부에서 비효율적인 연산 발생.
    • 애니메이션을 GPU가 아닌 CPU로 처리, 동작.

    해결 방법

    1. JavaScript 최소화

      • dynamic import 로 페이지 진입 시 불필요한 JS 로딩 지연
      • 이벤트 핸들러에서 무거운 연산은 requestIdleCallback 또는 Web Worker로 분리.
    2. Re-render 줄이기

      • React 상태 관리 시 Context 남용 금지 → useMemo, useCallback으로 불필요한 렌더링 방지
      • Zustand, Jotai 같은 fine-grained 상태 관리 도구 고려
    3. 애니메이션 최적화

      • transform/opacity 기반 애니메이션 사용 → GPU 가속 활용
      • requestAnimationFrame으로 프레임 제어

    CLS 최적화

    문제 원인

    • 이미지/광고 영역에 고정 크기 지정 x
    • 웹 폰트 로딩 지연으로 텍스트가 갑자기 재배치.
    • 동적으로 삽입되는 배너, 알림 영역

    해결 방법

    1. 고정 크기 지정

      • 가능하면 이미지/비디오/컴포넌트에 width, height 명시

        but, 반응형 광고가 필요할 수도 있음. 최소/최대 크기 범위를 지정하는 대처가 필요.
    2. 폰트 로딩 전략

      • next/font 를 사용해 FOUT 최소화
      • 또는 fallback 폰트와 유사한 폭을 가진 폰트 선택.
    3. UI 요소 안정화

      • 레이지 로딩(Lazy loading) 요소는 placeholder/skeleton 미리 확보
      • 상단 배너 삽입 시, 최초 레이아웃에서 공간 확보.

    측정과 모니터링

    개발 단계

    Chrome DevTools → Performance 패널 / Lighthouse → 성능 시뮬레이션

    실서비스 단계

    • Google Search Console → Core Web Vitals 리포트
    • Web Vitals JS 라이브러리 → 실제 사용자 데이터(RUM, Real User Monitoring) 수집
  • 구글 크롤러 최적화

    구글 크롤러의 동작 방식

    구글봇(Googlebot)은 단순한 텍스트 수집기가 아니라,

    실제로 크롬 기반의 렌더링 엔진을 이용해

    페이지를 처리한다. 이 과정은 보통 세 단계로 이루어진다.

    크롤링(Crawling)

    새로운 URL을 찾아내고, 서버에 요청을 보내 HTML 문서를 받아온다.

    robots.txt와 같은 파일을 참고하여 접근 가능한 URL만 수집한다.

    렌더링(Rendering)

    받은 HTML을 파싱하고, 필요한 경우 JavaScript도 실행하여 DOM을 완성한다.

    CSS, 이미지, 폰트 등 외부 리소스도 요청하기 때문에, 네트워크 환경이나 차단 정책에 따라

    일부가 누락될 수 있다.

    인덱싱(Indexing)

    완성된 DOM 트리와 메타데이터를 기반으로 페이지를 데이터베이스에 저장한다.

    이후 검색 결과에 노출될 수 있게 된다.

    렌더링 최적화

    CSR 페이지의 문제

    CSR(Client-Side Rendering) 페이지는 초기 HTML이 비어 있어,

    구글봇이 렌더링 큐에서 JS를 실행해야만 인덱싱할 수 있다.

    이 과정이 지연되면 검색 노출이 늦어지거나 일부 페이지는 아예 인덱싱되지 X

    SSR/SSG의 강점

    SSR(Server-Side Rendering)이나 SSG(Static Site Generation)는 완성된 HTML을 제공하므로,

    크롤러가 즉시 콘텐츠를 인식할 수 있다.

    Critical Rendering Path 최적화

    구글봇은 리소스 요청에 제한이 있고, JS/CSS 파일이 크면 크롤링 비용이 증가한다.

    따라서, 필수 리소스만 우선 제공하고, 나머지는 lazy 로딩하는 것이다.

    *next/script의 strategy=”lazyOnload”를 활용하면 비핵심 JS를 늦게 로딩시킬 수 있다.

    인덱싱 최적화

    메타 태그 관리

    <title>, <meta name=”description”>은 검색 결과에서 직접 노출되는 요소다.

    Next.js 13+에서는 app/ 디렉토리의 metadata API를 사용하여 페이지별 메타데이터를 선언할 수 있다.

    Canonical URL

    동일 콘텐츠가 여러 경로로 접근 가능할 경우, rel=”canonical” 태그를 통해 대표 URL을 지정해야 한다.

    e.g. /products/macbook-pro 와 /products?item=123 → canonical을 /products/macbook-pro로 통일

    구조화 데이터 (Structured Data)

    JSON-LD를 이용해 구글에 콘텐츠 의미를 전달한다.

    e.g. 제품 페이지에는 가격, 재고 상태, 리뷰 평점을 포함한 schema.org 마크업을 추가

    Core Web Vitals와 SEO

    구글은 사용자 경험 지표를 검색 랭킹에 반영한다. 대표적인 지표는 다음과 같다.

    • LCP (Largest Contentful Paint): 가장 큰 요소가 화면에 그려지는 시간 (권장 2.5초 이내)
    • FID (First Input Delay): 첫 사용자 입력에 반응하는 시간 (권장 100ms 이내)
    • CLS (Cumulative Layout Shift): 레이아웃이 얼마나 안정적인지 (권장 0.1 이내)

    Next.js에서 할 수 있는 최적화 예시:

    • next/image로 이미지 최적화
    • 코드 스플리팅과 dynamic import
    • prefetch를 통한 라우팅 최적화

    국제화(i18n)와 SEO(글로벌)

    글로벌 서비스를 운영한다면 hreflang 속성을 통해 언어/지역별 페이지를 명확히 구분해야 하며

    Next.js i18n 라우팅을 활용하면 /en, /ko, /jp와 같은 경로를 쉽게 관리할 수 있다.

    그렇지 않으면 다국어 페이지가 중복 콘텐츠로 인식될 수 있다.

  • 리눅스 파일 시스템과 스왑 메모리 완전 정복: 서버 성능 최적화의 핵심

    안녕하세요, 성장하는 개발자 여러분!

    개발을 하다 보면 “디스크 용량이 부족해요”, “메모리가 부족하다고 나오는데요”, “파일이 갑자기 사라졌어요” 같은 말을 자주 듣게 됩니다. 이런 문제들은 모두 파일 시스템메모리 관리에 대한 이해 부족에서 비롯됩니다.

    특히 서버를 운영하게 되면, 파일 시스템의 구조를 이해하고 스왑 메모리를 적절히 설정하는 것은 선택이 아닌 필수입니다. 오늘은 제가 실무에서 겪은 경험을 바탕으로, 리눅스 파일 시스템과 스왑 메모리를 완전히 정복해 보겠습니다!

    1. 리눅스 파일 시스템의 세계: 모든 것은 파일이다

    파일 시스템의 종류와 특징

    리눅스는 “모든 것은 파일이다”라는 철학을 가지고 있습니다. 하드웨어 정보도, 프로세스 정보도 모두 파일로 관리하죠. 이런 파일들을 효율적으로 저장하고 관리하기 위해 다양한 파일 시스템이 존재합니다.

    🗄️ 디스크 기반 파일 시스템

    EXT4 (Fourth Extended Filesystem)

    • 현재 가장 널리 사용되는 파일 시스템
    • 최대 16TB 파일, 1EB 파일 시스템 지원
    • 저널링 기능으로 시스템 크래시 시 데이터 복구 가능
    # EXT4 파일 시스템 생성
    sudo mkfs.ext4 /dev/sdb1
    
    # 파일 시스템 정보 확인
    sudo tune2fs -l /dev/sdb1

    XFS (X File System)

    • 대용량 파일과 파일 시스템에 최적화
    • B+트리 구조로 빠른 파일 검색
    • RHEL/CentOS 7부터 기본 파일 시스템
    # XFS 파일 시스템 생성
    sudo mkfs.xfs /dev/sdb1
    
    # XFS 정보 확인
    xfs_info /mount/point

    💾 메모리 기반 파일 시스템 (Pseudo File System)

    tmpfs (Temporary File System)

    # 메모리에 임시 파일 시스템 마운트
    sudo mount -t tmpfs -o size=1G tmpfs /tmp/ramdisk
    
    # 임시 파일 시스템 확인
    df -h | grep tmpfs

    procfs & sysfs

    # 현재 실행 중인 프로세스 정보
    cat /proc/cpuinfo
    cat /proc/meminfo
    
    # 시스템 하드웨어 정보
    ls /sys/class/

    실무에서 파일 시스템 관리하기

    파일 시스템 생성과 마운트

    # 1. 사용 가능한 디스크 확인
    lsblk
    fdisk -l
    
    # 2. 파티션 생성 (예: /dev/sdb에 새 파티션 생성)
    sudo fdisk /dev/sdb
    # n (new partition) -> p (primary) -> 1 -> Enter -> Enter -> w (write)
    
    # 3. 파일 시스템 생성
    sudo mkfs.ext4 /dev/sdb1
    
    # 4. 마운트 포인트 생성
    sudo mkdir /data
    
    # 5. 임시 마운트
    sudo mount /dev/sdb1 /data
    
    # 6. 영구 마운트를 위한 fstab 등록
    echo "/dev/sdb1 /data ext4 defaults 0 2" | sudo tee -a /etc/fstab
    
    # 7. fstab 테스트
    sudo mount -a

    파일 시스템 상태 모니터링

    # 디스크 사용량 확인
    df -h
    
    # inode 사용량 확인 (중요!)
    df -i
    
    # 디스크 I/O 모니터링
    iostat -x 1
    
    # 특정 디렉토리 크기 확인
    du -sh /var/log/*

    실무 팁: inode가 부족하면 디스크 용량이 남아있어도 파일을 생성할 수 없습니다!

    2. 스왑 메모리: 가상 메모리의 핵심

    스왑 메모리란?

    스왑 메모리는 물리 RAM이 부족할 때 디스크의 일부를 메모리처럼 사용하는 기술입니다.

    전체 가상 메모리 = 물리 RAM + 스왑 공간

    스왑의 동작 원리

    • 페이지 아웃(Page Out): 사용 빈도가 낮은 메모리 페이지를 스왑 영역으로 이동
    • 페이지 인(Page In): 스왑 영역의 데이터를 다시 물리 메모리로 가져옴

    실전 스왑 관리

    현재 스왑 상태 확인

    # 스왑 사용량 확인
    free -h
    swapon -s
    
    # 더 자세한 메모리 정보
    cat /proc/meminfo | grep -i swap

    스왑 파일 생성하기

    # 1. 2GB 스왑 파일 생성
    sudo dd if=/dev/zero of=/swapfile bs=1G count=2
    
    # 2. 파일 권한 설정 (보안상 중요!)
    sudo chmod 600 /swapfile
    
    # 3. 스왑 영역 초기화
    sudo mkswap /swapfile
    
    # 4. 스왑 활성화
    sudo swapon /swapfile
    
    # 5. 영구 설정을 위한 fstab 등록
    echo "/swapfile none swap sw 0 0" | sudo tee -a /etc/fstab

    스왑 파티션 생성하기

    # 1. 파티션 생성 (fdisk 사용)
    sudo fdisk /dev/sdb
    # n -> p -> 2 -> Enter -> Enter -> t -> 2 -> 82 -> w
    
    # 2. 스왑 파티션 초기화
    sudo mkswap /dev/sdb2
    
    # 3. 스왑 활성화
    sudo swapon /dev/sdb2
    
    # 4. fstab에 등록
    echo "/dev/sdb2 none swap sw 0 0" | sudo tee -a /etc/fstab

    스왑 성능 최적화

    Swappiness 조정

    # 현재 swappiness 값 확인 (기본값: 60)
    cat /proc/sys/vm/swappiness
    
    # 임시로 swappiness 변경 (값이 낮을수록 스왑 사용을 적게 함)
    sudo sysctl vm.swappiness=10
    
    # 영구 설정
    echo "vm.swappiness=10" | sudo tee -a /etc/sysctl.conf

    실무 가이드라인:

    • 서버용: swappiness 1-10 (메모리 우선 사용)
    • 데스크톱용: swappiness 10-20
    • 기본값: 60

    스왑 우선순위 설정

    # 우선순위와 함께 스왑 활성화
    sudo swapon -p 1 /swapfile        # 높은 우선순위
    sudo swapon -p 0 /dev/sdb2        # 낮은 우선순위
    
    # fstab에서 우선순위 설정
    /swapfile none swap sw,pri=1 0 0
    /dev/sdb2 none swap sw,pri=0 0 0

    3. 실무에서 마주치는 문제와 해결책

    문제 1: “디스크 용량은 있는데 파일을 만들 수 없어요”

    # inode 사용량 확인
    df -i
    
    # 해결: inode가 부족한 경우
    # 방법 1: 불필요한 파일 삭제
    find /var/log -name "*.log" -mtime +30 -delete
    
    # 방법 2: 파일 시스템 재생성 (데이터 백업 후)
    sudo mkfs.ext4 -N 200000 /dev/sdb1  # inode 수 증가

    문제 2: “시스템이 느려져요”

    # 스왑 사용량 확인
    free -h
    
    # I/O 대기 시간 확인
    iostat -x 1
    
    # 해결책
    # 1. 메모리 증설
    # 2. 스왑을 SSD로 이동
    # 3. swappiness 값 조정

    문제 3: “파일 시스템 마운트가 안 돼요”

    # 파일 시스템 체크
    sudo fsck /dev/sdb1
    
    # 강제 체크 (읽기 전용 모드)
    sudo fsck -f -r /dev/sdb1
    
    # 자동 복구 시도
    sudo fsck -y /dev/sdb1

    4. 자동화 스크립트: 시스템 모니터링

    디스크 사용량 모니터링 스크립트

    #!/bin/bash
    # disk_monitor.sh
    
    THRESHOLD=80
    
    df -h | awk '
    NR>1 {
        gsub(/%/, "", $5)
        if ($5 > threshold) {
            print "경고: " $6 " 파티션이 " $5 "% 사용 중입니다!"
            system("logger 디스크 사용량 경고: " $6 " " $5 "%")
        }
    }' threshold=$THRESHOLD
    
    # inode 사용량도 체크
    df -i | awk '
    NR>1 {
        gsub(/%/, "", $5)
        if ($5 > 80) {
            print "경고: " $6 " 파티션의 inode가 " $5 "% 사용 중입니다!"
        }
    }'

    메모리 사용량 모니터링

    #!/bin/bash
    # memory_monitor.sh
    
    TOTAL_MEM=$(free | awk '/^Mem:/ {print $2}')
    USED_MEM=$(free | awk '/^Mem:/ {print $3}')
    SWAP_USED=$(free | awk '/^Swap:/ {print $3}')
    
    MEM_USAGE=$((USED_MEM * 100 / TOTAL_MEM))
    
    if [ $MEM_USAGE -gt 90 ]; then
        echo "메모리 사용량이 ${MEM_USAGE}%입니다!"
    
        # 메모리 사용량이 높은 프로세스 출력
        echo "Top 5 메모리 사용 프로세스:"
        ps aux --sort=-%mem | head -6
    fi
    
    if [ $SWAP_USED -gt 0 ]; then
        echo "스왑이 사용되고 있습니다: $(($SWAP_USED / 1024))MB"
    fi

    Cron으로 자동 모니터링 설정

    # crontab 편집
    crontab -e
    
    # 5분마다 디스크 사용량 체크
    */5 * * * * /usr/local/bin/disk_monitor.sh
    
    # 10분마다 메모리 사용량 체크
    */10 * * * * /usr/local/bin/memory_monitor.sh
    
    # 매일 새벽 3시에 로그 정리
    0 3 * * * find /var/log -name "*.log" -mtime +7 -delete

    마치며: 안정적인 시스템 운영을 위한 체크리스트

    일일 점검 사항

    • [ ] 디스크 사용량 확인 (df -h)
    • [ ] inode 사용량 확인 (df -i)
    • [ ] 메모리 사용량 확인 (free -h)
    • [ ] 스왑 사용량 확인 (swapon -s)

    주간 점검 사항

    • [ ] 파일 시스템 무결성 검사 (fsck)
    • [ ] 로그 파일 정리
    • [ ] 불필요한 파일 삭제
    • [ ] 시스템 성능 지표 분석

    비상 상황 대응

    • [ ] 백업 스크립트 작동 확인
    • [ ] 응급 복구 절차 숙지
    • [ ] 연락망 및 에스컬레이션 프로세스 준비

    파일 시스템과 스왑 메모리 관리는 서버 안정성의 기초입니다. 오늘 배운 내용들을 실제 환경에서 차근차근 실습해 보시고, 모니터링 스크립트를 활용해서 proactive한 시스템 관리자가 되시길 바랍니다!

    기억하세요: 예방이 최고의 해결책입니다. 🛡️