무거운 초기화 처리를 첫 접근 시에만 실행하고, 이후에는 생성된 값을 재사용하고 싶은 경우가 있다. 메모리 캐시만큼 무겁지 않고, 간단하게 구현하는 패턴이다.
코드
export interface Reusable<T> { value: T;}
export function reusable<T>(init: () => T): Reusable<T> { return { get value() { const value = init(); Object.defineProperty(this, "value", { value }); return value; }, };}사용 예시
타임스탬프 고정
const timestamp = reusable(() => new Date());
// 첫 접근 시 Date가 생성됨console.log(timestamp.value); // 2025-01-18T09:00:00.000Z
// 두 번째 이후부터는 같은 값이 반환됨console.log(timestamp.value); // 2025-01-18T09:00:00.000Z무거운 설정 로딩
const config = reusable(() => { // 파일 로딩이나 환경 변수 파싱 등 return loadConfigFromFile();});
// 필요할 때 처음으로 설정을 로딩함function processData() { const settings = config.value; // 첫 접근 시에만 로딩 실행 // ...}구현 원리
- 첫 getter 접근 시 init 함수를 실행
- Object.defineProperty로 value를 데이터 프로퍼티로 덮어씀
- 두 번째 이후부터는 getter가 호출되지 않고, 값이 직접 반환됨
이를 통해 첫 접근 시에만 초기화 비용이 발생하고, 이후에는 일반적인 프로퍼티 접근과 동등한 성능이 된다.
메모리 캐시와의 비교
메모리 캐시(Map 등)와의 차이점:
- 이 패턴은 단일 값에 특화됨
- Map과 같은 키 관리가 필요 없음
- 가비지 컬렉션도 일반적인 객체와 동일함
반대로 여러 값을 캐시하고 싶은 경우는 Map이나 WeakMap을 사용해야 한다.
주의사항
한 번 초기화되면 값은 변경되지 않는다. 재초기화하고 싶은 경우 새로운 reusable 인스턴스를 만들어야 한다. 또한 init 함수가 예외를 던지면, 이후 접근에서도 같은 예외가 재발생한다.
hsb.horse