Vue.js 컴포넨트 생명주기 관리 및 상태 유지기법

Vue.js 프레임워크 내에서 특정 컴포넨트의 상태를 유지하는 기법인 keep-alive의 내부 동작 방식을 기술적으로 살펴봅니다. 이는 단순히 DOM 조작이 아닌, 인스턴스 레지스트리 관리를 통해 성능을 최적화하는 핵심 패턴입니다.

  1. 가상 캐시 관리 알고리즘 <span class="token keyword">class</span> ViewCache { constructor(limit) { this.limit = limit; this.storage = Object.create(null); // WeakSet 유사체 대신 객체 사용 this.accessOrder = []; // 최근 접근 순서 추적 } get(id) { const entry = this.storage[id]; if (!entry) return undefined; // LRU 전략: 기존 순서에서 제거 후 재추억 (최신순으로 이동) const index = this.accessOrder.indexOf(id); if (index > -1) this.accessOrder.splice(index, 1); this.accessOrder.push(id); return entry.value; } set(id, data) { if (this.storage.hasOwnProperty(id)) { this.accessOrder.push(id); // 이미 존재하면 다시 큐末尾로 추가 } else if (Object.keys(this.storage).length >= this.limit && this.limit !== null) { // Limit 초과 시, 가장 오래된 항목 제거 (선입선출 원리) if (this.accessOrder.length > 0) { const victimKey = this.accessOrder.shift(); delete this.storage[victimKey]; } } this.storage[id] = { value: data }; this.accessOrder.push(id); }}

2. 렌더링 플로우 재설계

기본 렌더링 로직은 조건부 포함/배제 로직을 거쳐 실제 도메네르 호출 여부를 결정합니다.

const KeepAlive = {
name: 'KeepAliveProvider',
abstract: true,

props: {
maxCacheSize: [String, Number],
includeTargets: [String, RegExp, Array],
excludeTargets: [String, RegExp, Array]
},

created() {
this.cacheMap = {};
this.cachedKeys = [];
},

destroyed() {
// 메모리 누수 방지를 위한 전체 스냅샷 초기화
for (const k in this.cacheMap) {
this.releaseCache(k);
}
},

mounted() {
// 반응형 바인딩을 통한 동적 규칙 적용
this.$watch('includeTargets', this.purgeByFilter.bind(this));
},

render() {
const children = this.$slots.default && this.$slots.default[0];
if (!children) return;

const compOptions = children.componentOptions;
const componentName = compOptions ? (compOptions.Ctor && compOptions.tag) : undefined;

// 필터링 로직 적용
if (!this.shouldKeep(children, this.includeTargets, this.excludeTargets)) {
return this.renderChild(children, true);
}

const uniqueId = children.key || `${compOptions.cid}:${Date.now()}`;

// 캐시 조회 및 갱신
const existingEntry = this.cacheMap[uniqueId];
if (existingEntry) {
// 활성 상태로 복구
return this.reactivate(existingEntry, children);
} else {
// 신규 인스턴스 생성 후 등록
return this.initiateAndCache(uniqueId, children);
}
},

shouldKeep(node, include, exclude) {
if (!node.type.name) return false;
if (include && !this.matchesFilter(node.type.name, include)) return false;
if (exclude && this.matchesFilter(node.type.name, exclude)) return false;
return true;
}
};

3. 특수 라이프사이클 처리

일반적인 생명주기와 달리, 캐시에 저장되어 있지 않은 상태에서 복원되는 순간 activated 이 트리거되며, 반대로 비활성화될 때 deactivated 가 실행됩니다. 이는 디바인딩 없이 상태만 잠ざ우는 역할을 합니다.

function processVnodeLifeCycle(vnode, active) {
if (!vnode.data.keepAlive) return;

const inst = vnode.componentInstance;
if (!inst) return;

// 인스턴스가 이미 파기되었는지 확인
if (inst._isDestroyed) return;

if (active) {
// 재사용 모드 활성화
inst._inactive = false;
inst.__proto__.$options.methods.activated && inst.activated();
} else {
// 대기 상태로 전환
inst._inactive = .true;
inst.__proto__.$options.methods.deactivated && inst.deactivated();
}
}

4. 시나리오 구현 예시

실제 화면에서 탭切換이나 페이지네이션 시 활용하는 패턴입니다.


프로필 편집
설정 메뉴





태그: vuejs performance-optimization lru-cache component-lifecycle

6월 19일 16:00에 게시됨