logo hsb.horse
← 스니펫 목록으로 돌아가기

Snippets

지연 초기화 객체

getter를 사용해 첫 접근 시에만 값을 생성하는 TypeScript 패턴. 메모리 캐시보다 경량인 지연 평가.

게시일: 수정일:

무거운 초기화 처리를 첫 접근 시에만 실행하고, 이후에는 생성된 값을 재사용하고 싶은 경우가 있다. 메모리 캐시만큼 무겁지 않고, 간단하게 구현하는 패턴이다.

코드

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; // 첫 접근 시에만 로딩 실행
// ...
}

구현 원리

  1. 첫 getter 접근 시 init 함수를 실행
  2. Object.defineProperty로 value를 데이터 프로퍼티로 덮어씀
  3. 두 번째 이후부터는 getter가 호출되지 않고, 값이 직접 반환됨

이를 통해 첫 접근 시에만 초기화 비용이 발생하고, 이후에는 일반적인 프로퍼티 접근과 동등한 성능이 된다.

메모리 캐시와의 비교

메모리 캐시(Map 등)와의 차이점:

  • 이 패턴은 단일 값에 특화됨
  • Map과 같은 키 관리가 필요 없음
  • 가비지 컬렉션도 일반적인 객체와 동일함

반대로 여러 값을 캐시하고 싶은 경우는 Map이나 WeakMap을 사용해야 한다.

주의사항

한 번 초기화되면 값은 변경되지 않는다. 재초기화하고 싶은 경우 새로운 reusable 인스턴스를 만들어야 한다. 또한 init 함수가 예외를 던지면, 이후 접근에서도 같은 예외가 재발생한다.