CPU 캐시와 성능의 이해
연사는 2013년에는 고려하지 않았던 CPU 캐시의 중요성을 강조합니다. CPU는 L1, L2, L3 (일부 L4)와 같은 내부 캐시를 통해 RAM보다 훨씬 빠르게 데이터에 접근합니다. L1 캐시는 5 CPU 사이클 이내인 반면, RAM은 100~500 사이클, SSD는 훨씬 더 많은 사이클이 소요됩니다. 1+1
연산이 5 사이클, 문자열 이니셜 추출이 12 사이클이 걸리는 것을 예로 들어, 단순 연산은 CPU에서 직접 처리하는 것이 캐싱보다 빠르고 효율적일 수 있음을 설명합니다. 따라서 애플리케이션의 특성과 하드웨어 성능에 따라 ‘만능’ 캐싱 전략은 없으며, 벤치마킹을 통한 최적화가 필수적입니다.
Rails 애플리케이션 최적화 기본 전략
성능 최적화는 다음과 같은 기본 단계부터 시작합니다.
select
활용: Active Record에서 필요한 컬럼만 명시적으로 선택하여 로드함으로써 불필요한 데이터 로딩을 방지합니다. 예를 들어,category
의description
이나created_at
처럼 사용하지 않는 컬럼을 제외하여 메모리 사용량을 줄입니다.- 데이터 타입 최적화: 모델의 데이터 타입을 애플리케이션의 요구사항에 맞게 세분화하여 저장 공간을 절약합니다. 예를 들어, 0~5점 범위의
rating
은integer
대신tinyint
를 사용하여 저장 공간을 75% 절약할 수 있습니다. 이는 시스템 내 정보량을 줄여 L1/L2 캐시 활용도를 높여 전반적인 속도 향상에 기여합니다. - 인덱스 추가: 데이터베이스 쿼리 성능 향상을 위해 적절한 인덱스를 추가합니다. 복합 인덱스(
last_name
,first_name
)가 있는 경우,last_name
단일 인덱스는 중복이므로 제거하여 공간을 절약할 수 있습니다.
고급 캐싱 전략
기본 최적화 후에는 다양한 캐싱 기법을 적용하여 성능을 극대화합니다.
- 프래그먼트 캐싱 (Fragment Caching) 및 러시안 돌 캐싱 (Russian Doll Caching): 뷰의 특정 부분을 캐싱하여 HTML 렌더링 시간을 단축합니다. 제품의
updated_at
속성을 캐시 키로 활용하며, 연관된 모델(belongs_to
관계)이 변경될 때 부모 객체의touch
옵션을 사용하여updated_at
을 업데이트함으로써 캐시 무효화를 효율적으로 관리합니다. 이 단계에서 60%의 성능 향상을 달성했습니다. - HTTP 캐싱 (ETag, Last-Modified): 컨트롤러 수준에서 HTTP 헤더(
ETag
,Last-Modified
)를 사용하여 클라이언트(브라우저)가 이미 가지고 있는 페이지가 변경되지 않았을 경우, 서버가 HTML을 다시 렌더링하지 않고304 Not Modified
응답을 보내도록 합니다. 이는 서버의 부하를 줄이고 클라이언트의 로딩 속도를 향상시킵니다. - 정적 페이지 생성 (Autobahn): 가장 빠른 페이지는 Rails 애플리케이션을 거치지 않고 Nginx나 Apache가 직접 서빙하는 정적 페이지입니다. 50,000개의 제품 페이지를 예시로 들어, 제품 뷰의 정적 사본을 파일 시스템에 저장하고, Brotli와 같은 최신 압축 포맷으로 미리 압축해 둡니다. 사용자의 로그인 상태나 장바구니 활성화 여부에 따라 쿠키를 설정하고, Nginx 설정에서 이 쿠키를 기반으로 동적/정적 페이지를 분리하여 서빙합니다. 만료일이 있는 제품의 경우, 매일 자정에 크론 잡을 통해 정적 파일을 재생성하여 최신 정보를 유지합니다. 이 전략을 통해 초기 30,000ms에서 3,500ms로 약 10배의 성능 향상을 이루었습니다.
캐싱 구현 시 주의사항
캐싱은 강력하지만, 구현 오류에 취약하며 디버깅이 어렵습니다. 특히 개발 환경에서는 캐싱을 사용하지 않다가 프로덕션에서 문제가 발생하는 경우가 많으므로, 철저한 테스트 환경 구축이 필수적입니다.