SCRUM-15521 | 커밋 수: 4개 | 기간: 2025.09
문제상황
이미지 캐시 정책이 일괄 적용되어 유연성이 부족했습니다. 오더픽처럼 재고 변동이 잦은 도메인과 배너처럼 변경이 적은 도메인의 캐시 요구사항이 상이했지만, 동일한 정책이 적용되고 있었습니다.
주요 개선사항
Cache-Control 헤더를 통해 TTL을 커스텀으로 설정할 수 있는 기능을 추가했습니다. 오더픽과 스퀘어 목록에 이미지 캐시 TTL 커스텀을 적용했으며, loadImage 함수를 단일책임 원칙에 따라 분리했습니다.
정량적 성과
이미지 로딩 함수를 단일책임 원칙에 따라 리팩토링했습니다. 도메인별로 캐시 TTL을 개별 설정할 수 있게 되어, 오더픽과 스퀘어는 5분, 상품은 60분, 배너는 1440분(24시간)으로 차등 적용했습니다.
| 항목 | 비교 | 값 (분) |
|---|---|---|
| 오더픽 | 5 분 | |
| 스퀘어 | 5 분 | |
| 상품 | 60 분 | |
| 배너 | 1440 분 |
비즈니스 임팩트
도메인 특성에 맞는 캐시 정책을 적용하여 이미지 로딩 성능을 최적화했습니다. 자주 변경되는 이미지는 짧은 TTL로 최신성을 보장하고, 변경이 적은 이미지는 긴 TTL로 네트워크 비용을 절감했습니다.
기술적 구현 상세
AS-IS
장점
- 단순한 구현
고려할 점
- 모든 이미지에 동일 캐시 정책
- 도메인별 요구사항 반영 불가
- 자주 변경되는 이미지도 오래 캐시
TO-BE
장점
- 도메인별 TTL 커스텀 설정
- loadImage 단일책임 분리
- 네트워크 비용 절감
고려할 점
- 설정 관리 포인트 증가
AS-IS
기존에는 모든 이미지에 동일한 캐시 정책이 적용되었습니다.
// 기존 구조 (더미 예시)
const loadImage = (src) => {
const img = new Image();
img.src = src;
return img;
};
문제점:
자주 변경되는 오더픽 이미지도 오래 캐시되어 최신 상태를 반영하지 못했습니다. 도메인별 캐시 요구사항을 반영할 수 있는 구조가 아니었습니다.
TO-BE
Cache-Control 헤더를 통해 도메인별 TTL을 커스텀 설정합니다.
- 이미지 요청
- 도메인 판별
- TTL 적용
- 캐시 로딩
핵심 구현
1. loadImage 함수 리팩토링 (단일책임 원칙)
// public/js/common.js
// 이미지 로딩 함수 (단일책임: 이미지 로딩만 담당)
const loadImage = (src, options = {}) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject(new Error(`Failed to load image: ${src}`));
// 캐시 TTL 설정
if (options.cacheTTL) {
img.src = appendCacheControl(src, options.cacheTTL);
} else {
img.src = src;
}
});
};
// 캐시 제어 함수 (단일책임: 캐시 설정만 담당)
const appendCacheControl = (src, ttl) => {
const url = new URL(src, window.location.origin);
url.searchParams.set('cache-control', `max-age=${ttl}`);
return url.toString();
};
// 이미지 프리로딩 함수 (단일책임: 프리로딩만 담당)
const preloadImages = async (srcs, options = {}) => {
const promises = srcs.map(src => loadImage(src, options));
return Promise.allSettled(promises);
};
2. Cache-Control 헤더 설정 로직
// public/js/common.js
const ImageCacheConfig = {
// 자주 변경되는 이미지 (짧은 TTL)
SHORT: 60 * 5, // 5분
// 일반 이미지 (중간 TTL)
MEDIUM: 60 * 60, // 1시간
// 거의 변경되지 않는 이미지 (긴 TTL)
LONG: 60 * 60 * 24, // 24시간
};
// 도메인별 캐시 TTL 설정
const getDomainCacheTTL = (domain) => {
const domainConfig = {
orderpick: ImageCacheConfig.SHORT, // 오더픽: 재고 변동 잦음
esquare: ImageCacheConfig.SHORT, // 스퀘어: 피드 업데이트 잦음
product: ImageCacheConfig.MEDIUM, // 상품: 중간 빈도
banner: ImageCacheConfig.LONG, // 배너: 변경 적음
};
return domainConfig[domain] ?? ImageCacheConfig.MEDIUM;
};
3. 컴포넌트에서 사용
<!-- components/orderpick/ProductListItem.vue -->
<script setup lang="ts">
const props = defineProps<{
product: OrderPickProduct;
}>();
// 오더픽 이미지는 짧은 TTL 적용
const imageOptions = {
cacheTTL: 300, // 5분
};
</script>
<template>
<div class="product-item">
<img
:src="product.imageUrl"
:data-cache-ttl="imageOptions.cacheTTL"
loading="lazy"
/>
</div>
</template>
<!-- components/esquare/PopularFeedListItem.vue -->
<script setup lang="ts">
const props = defineProps<{
feed: SquareFeed;
}>();
// 스퀘어 피드 이미지는 짧은 TTL 적용
const imageOptions = {
cacheTTL: 300, // 5분
};
</script>
<template>
<article class="feed-item">
<img
:src="feed.thumbnailUrl"
:data-cache-ttl="imageOptions.cacheTTL"
loading="lazy"
/>
</article>
</template>
회고
잘한 점
loadImage 함수를 단일책임 원칙에 따라 로딩, 캐시 제어, 프리로딩으로 분리했습니다. 도메인별 캐시 TTL 설정으로 유연성을 확보하여, 각 도메인의 특성에 맞는 캐시 전략을 적용할 수 있게 되었습니다.
개선할 점
서버 측 Cache-Control 헤더와의 연동에 대한 검토가 필요합니다. 클라이언트 측 설정과 서버 측 헤더가 충돌하지 않도록 정책을 정리해야 합니다.
배운 점
이미지 캐시 전략이 사용자 경험에 미치는 영향의 중요성을 인식했습니다. 단일책임 원칙을 함수 단위에 적용하는 방법을 체득했고, 도메인별 캐시 요구사항을 분석하여 차등 적용하는 패턴을 익혔습니다.