Angular @defer 완전 가이드 2026: 선언적 지연 로딩과 면접 질문

Angular @defer 블록을 활용한 선언적 지연 로딩의 작동 원리, 트리거 유형, 프리페치 전략, 점진적 하이드레이션, 면접 질문을 상세히 다룹니다.

Angular @defer 선언적 지연 로딩 가이드

Angular의 @defer 블록은 라우터 기반 지연 로딩으로는 해결할 수 없었던 문제를 해결합니다. 애플리케이션을 별도의 라우트로 재구성하지 않고도 개별 컴포넌트, 디렉티브, 파이프를 온디맨드로 로딩할 수 있습니다. Angular 17에서 도입되고 Angular 22에서 안정화된 @defer는 동적 임포트, *ngIf 플래그, webpack 설정을 조합해야 했던 작업을 하나의 선언적 템플릿 블록으로 변환합니다.

@defer의 역할

@defer는 래핑된 컴포넌트를 별도의 JavaScript 청크로 분할하고, 트리거 조건이 발동될 때만 로딩하도록 Angular 컴파일러에 지시합니다. 브라우저가 초기에 다운로드하는 JavaScript가 줄어들어 수동 코드 분할 설정 없이 Largest Contentful Paint(LCP)와 Time to First Byte(TTFB)가 개선됩니다.

@defer의 내부 작동 메커니즘

Angular 컴파일러가 @defer 블록을 만나면, 내부의 모든 스탠드얼론 컴포넌트, 디렉티브, 파이프를 별도의 청크로 추출합니다. 메인 번들은 이러한 의존성 없이 제공됩니다. 런타임에 Angular는 트리거 조건을 평가하고 동적 import() 호출을 통해 청크를 가져옵니다.

이 블록은 로딩 라이프사이클 동안 사용자에게 표시되는 내용을 제어하는 4개의 서브 블록을 지원합니다:

app.component.htmltypescript
@defer (on viewport) {
  <analytics-dashboard />
} @placeholder {
  <div class="h-64 bg-muted animate-pulse"></div>
} @loading (minimum 200ms) {
  <loading-spinner />
} @error {
  <p>Failed to load the dashboard. Please refresh.</p>
}

@placeholder는 트리거가 발동되기 전에 렌더링됩니다. @loading은 청크가 다운로드되는 동안 표시되며, 깜빡임을 방지하기 위한 선택적 minimum 기간을 설정할 수 있습니다. @error는 네트워크 장애나 청크 오류를 포착합니다. @loadingminimum 파라미터는 청크가 캐시에서 빠르게 로딩될 때 스피너가 잠깐 표시되는 현상을 방지합니다.

중요한 제약 조건으로, @defer 내부의 모든 의존성은 스탠드얼론이어야 합니다. NgModule에 선언된 비스탠드얼론 컴포넌트는 지연 로딩이 불가능하며, @defer 래퍼와 관계없이 즉시 로딩됩니다.

트리거 유형: 컴포넌트 로딩 시점 제어

Angular는 7가지 내장 트리거를 제공하며, 각각 다른 로딩 전략에 대응합니다. 여러 트리거를 세미콜론으로 결합할 수 있으며 OR 조건으로 평가됩니다.

product-page.component.htmltypescript
// 브라우저가 유휴 상태일 때 로딩 (기본값)
@defer {
  <recommendation-engine />
}

// 요소가 뷰포트에 들어올 때 로딩
@defer (on viewport) {
  <customer-reviews [productId]="product.id" />
}

// 클릭 또는 키다운 시 로딩
@defer (on interaction) {
  <product-configurator />
} @placeholder {
  <button>Configure this product</button>
}

// 마우스 오버 또는 포커스 시 로딩
@defer (on hover) {
  <quick-preview />
}

// 3초 후 로딩
@defer (on timer(3s)) {
  <chat-widget />
}

// 초기 렌더링 직후 로딩
@defer (on immediate) {
  <notification-center />
}

on viewport 트리거는 내부적으로 Intersection Observer API를 사용합니다. on idle 트리거는 requestIdleCallback에 위임하므로 폴드 아래 콘텐츠에 가장 안전한 기본값입니다. on timer 트리거는 밀리초(500ms) 또는 초(3s) 단위로 기간을 지정할 수 있습니다.

when 트리거를 통한 리액티브 조건

이벤트 기반 트리거 외에도 when은 시그널 읽기를 포함한 모든 boolean 표현식을 받아들입니다:

dashboard.component.tstypescript
export class DashboardComponent {
  showAdvanced = signal(false);
}

// dashboard.component.html
@defer (when showAdvanced()) {
  <advanced-analytics />
} @placeholder {
  <button (click)="showAdvanced.set(true)">Show advanced analytics</button>
}

onwhen의 조합은 유효합니다. Angular는 이를 OR로 처리하며, 어느 조건이든 충족되면 블록이 로딩됩니다.

프리페치: 다운로드와 표시의 분리

프리페치는 청크의 다운로드 시점과 컴포넌트의 렌더링 시점을 분리합니다. 사용자가 트리거를 발동시키기 전에 JavaScript를 미리 가져와 체감 지연 시간을 제거합니다.

settings.component.htmltypescript
@defer (on interaction; prefetch on idle) {
  <account-settings />
} @placeholder {
  <button>Open settings</button>
}

이 패턴에서 브라우저는 유휴 시간 동안 account-settings 청크를 다운로드합니다. 사용자가 클릭하면 코드가 이미 사용 가능하므로 컴포넌트가 즉시 렌더링됩니다. 프리페치는 메인 on 절과 동일한 트리거 유형을 지원합니다.

대시보드에서 일반적인 패턴으로, 무거운 컴포넌트를 유휴 시 프리페치하고 뷰포트 진입 시 렌더링을 지연하는 방법이 있습니다.

analytics.component.htmltypescript
@defer (on viewport; prefetch on idle) {
  <chart-widget [data]="salesData()" />
} @placeholder (minimum 100ms) {
  <div class="h-48 bg-muted rounded-none"></div>
}

@placeholderminimum은 사용자가 빠르게 스크롤하여 지연 컴포넌트가 거의 즉시 로딩될 때 레이아웃 깜빡임을 방지합니다.

Angular 면접 준비가 되셨나요?

인터랙티브 시뮬레이터, flashcards, 기술 테스트로 연습하세요.

@defer와 서버 사이드 렌더링

서버에서 @defer 블록은 기본적으로 @placeholder 콘텐츠를 렌더링합니다. 지연 컴포넌트는 서버 사이드에서 렌더링되지 않습니다. 이 동작은 의도적인 것으로, 청크 다운로드는 브라우저 컨텍스트에서만 의미가 있기 때문입니다.

SEO에 중요한 콘텐츠의 경우, @placeholder에는 스피너만이 아닌 의미 있는 HTML이 포함되어야 합니다:

blog-post.component.htmltypescript
@defer (on viewport) {
  <comment-section [postId]="post.id" />
} @placeholder {
  <section aria-label="Comments">
    <h2>Comments</h2>
    <p>Loading comments...</p>
  </section>
}

검색 엔진은 SSR 시 플레이스홀더 콘텐츠를 인덱싱합니다. 설명적인 플레이스홀더를 통해 페이지의 시맨틱 완전성이 유지됩니다.

점진적 하이드레이션과 @defer

Angular 19에서 개발자 프리뷰로 도입된 점진적 하이드레이션은 Angular 22에서 프로덕션 준비 기능으로 안정화되었습니다. 점진적 하이드레이션은 @deferhydrate 트리거로 확장하여, 서버에서 렌더링된 컴포넌트가 클라이언트에서 언제 인터랙티브하게 되는지 제어합니다.

product-page.component.htmltypescript
@defer (hydrate on viewport) {
  <product-gallery [images]="product.images" />
}

@defer (hydrate on interaction) {
  <product-reviews [productId]="product.id" />
}

표준 @defer와 달리, 점진적 하이드레이션은 서버에서 완전한 컴포넌트 HTML을 렌더링합니다. 클라이언트는 완전한 마크업을 수신하지만 트리거가 발동될 때까지 컴포넌트의 하이드레이션을 건너뜁니다. 결과적으로 브라우저는 완전한 페이지를 즉시 그리지만, JavaScript 실행은 지연됩니다.

Angular 팀은 콘텐츠가 많은 페이지에서 점진적 하이드레이션을 사용할 경우 LCP 점수가 일관되게 40~50% 개선된다고 보고하고 있습니다. Angular 22의 Angular DevTools는 컴포넌트 인스펙터에서 하이드레이션 상태(대기 중, 하이드레이션 완료, 오류)를 직접 시각화합니다.

실전 패턴과 성능 전략

패턴 1: 복수의 지연 패널을 가진 대시보드

dashboard.component.htmltypescript
<div class="grid grid-cols-2 gap-4">
  @defer (on viewport; prefetch on idle) {
    <sales-chart />
  } @placeholder {
    <skeleton-card height="300px" />
  }

  @defer (on viewport; prefetch on idle) {
    <user-activity-feed />
  } @placeholder {
    <skeleton-card height="300px" />
  }

  @defer (on interaction) {
    <export-panel />
  } @placeholder {
    <button>Export data</button>
  }
</div>

각 패널은 독립적으로 로딩됩니다. 내보내기 패널은 사용자가 명시적으로 요청할 때까지 로딩되지 않은 상태를 유지하여, 내보내기를 사용하지 않는 사용자의 청크 다운로드를 절약합니다.

패턴 2: 시그널을 활용한 조건부 기능 로딩

editor.component.tstypescript
export class EditorComponent {
  user = inject(UserService).currentUser;
  isPremium = computed(() => this.user()?.plan === 'premium');
}

// editor.component.html
@defer (when isPremium()) {
  <ai-assistant />
} @placeholder {
  <upgrade-banner />
}

무료 요금제 사용자는 AI 어시스턴트 청크를 다운로드하지 않습니다. 업그레이드 배너는 플레이스홀더와 전환 촉진의 두 가지 역할을 수행합니다.

흔한 실수 피하기

동일한 트리거를 가진 @defer 블록을 중첩하면 동시에 청크가 다운로드됩니다. 중첩된 블록 간에 트리거를 분산시키는 것이 바람직합니다:

typescript
// 피해야 할 예: 둘 다 유휴 시 동시 발동
@defer {
  @defer {
    <nested-heavy-component />
  }
}

// 권장: 외부는 유휴, 내부는 뷰포트
@defer (on idle) {
  <wrapper-component />
}
// wrapper-component 템플릿 내부:
@defer (on viewport) {
  <nested-heavy-component />
}

또 다른 실수는 크리티컬 렌더링 경로에 이미 존재하는 컴포넌트에 @defer를 사용하는 것입니다. 폴드 위의 콘텐츠를 지연 로딩하면 브라우저가 페인팅 전에 청크를 다운로드하고 실행해야 하므로 LCP가 증가합니다. @defer는 폴드 아래 또는 사용자가 트리거하는 콘텐츠에만 사용해야 합니다.

@defer vs 라우터 기반 지연 로딩

라우터 기반 지연 로딩과 @defer는 상호 보완적입니다. 라우터 지연 로딩은 라우트 수준에서 분할하여 전체 기능 모듈을 지연시킵니다. @defer는 템플릿 내에서 분할하여 개별 컴포넌트를 지연시킵니다.

| 관점 | 라우터 지연 로딩 | @defer | |------|-----------------|--------| | 세분성 | 라우트 수준 | 컴포넌트 수준 | | 트리거 | 내비게이션 이벤트 | viewport, idle, interaction, hover, timer, 조건 | | SSR 동작 | 완전한 서버 렌더링 | 서버에서 플레이스홀더 | | 스탠드얼론 필수 | 아니오 (NgModules 지원) | 예 (스탠드얼론만) | | 프리페치 | preloadingStrategy | prefetch on 절 | | 사용 사례 | 기능 모듈, 페이지 | 위젯, 패널, 조건부 UI |

일반적인 애플리케이션에서는 둘 다 사용합니다: 최상위 기능 영역에는 라우터 지연 로딩을, 해당 영역 내의 무거운 컴포넌트에는 @defer를 적용합니다.

Angular @defer 면접 질문

기술 면접에서 @defer가 Angular 성능 전략의 핵심이 됨에 따라, 이 기능에 대한 질문이 증가하고 있습니다. 다음은 Angular 면접 질문에서 자주 등장하는 패턴입니다.

Q: 비스탠드얼론 컴포넌트를 @defer 안에 배치하면 어떻게 됩니까? 컴포넌트는 @defer 블록과 관계없이 즉시 로딩됩니다. Angular 컴파일러는 NgModule에 선언된 컴포넌트를 별도 청크로 추출할 수 없습니다. 오류는 발생하지 않지만, 최적화가 조용히 실패합니다.

Q: SSR에서 @defer는 어떻게 동작합니까? 서버는 @placeholder 콘텐츠를 렌더링합니다. 지연 컴포넌트는 서버 사이드에서 렌더링되지 않습니다. 하이드레이션은 트리거가 발동될 때 클라이언트에서 수행됩니다(점진적 하이드레이션의 경우 hydrate on 사용).

Q: @defer는 OnPush 변경 감지와 함께 사용할 수 있습니까? 사용할 수 있습니다. @defer는 템플릿 수준의 기능이며 어떤 변경 감지 전략과도 호환됩니다. 지연 컴포넌트는 로딩된 후 자체 변경 감지 설정을 따릅니다.

Q: on idleon immediate의 차이점은 무엇입니까? on idlerequestIdleCallback을 통해 브라우저가 모든 보류 중인 작업을 완료할 때까지 대기합니다. on immediate는 Angular가 초기 렌더링 패스를 완료한 직후, 유휴 상태를 기다리지 않고 트리거됩니다. on immediate는 첫 번째 페인트를 차단해서는 안 되지만 빨리 로딩해야 하는 컴포넌트에 적합합니다.

더 넓은 범위의 Angular 면접 준비에 대해서는 Angular 변경 감지 모듈Angular 시그널 모듈을 참고할 수 있습니다.

연습을 시작하세요!

면접 시뮬레이터와 기술 테스트로 지식을 테스트하세요.

결론

  • @defer는 스탠드얼론 컴포넌트를 별도의 청크로 분할하여 라우트 재구성 없이 초기 번들 크기를 줄입니다
  • 7가지 트리거 유형(idle, viewport, interaction, hover, timer, immediate, when)이 수동적 로딩부터 사용자 주도 로딩까지 모든 전략을 포괄합니다
  • prefetch on은 청크 다운로드와 컴포넌트 렌더링을 분리하여, 사용자 트리거 컴포넌트의 체감 지연 시간을 제거합니다
  • 점진적 하이드레이션(hydrate on)은 서버에서 완전한 HTML을 렌더링하면서 JavaScript 실행을 클라이언트로 지연시켜 콘텐츠가 많은 페이지에서 40~50%의 LCP 개선을 달성합니다
  • @placeholder 콘텐츠는 SSR 시 렌더링되므로, SEO를 위해 시맨틱적으로 의미 있는 내용이어야 합니다
  • @defer는 라우터 기반 지연 로딩을 보완합니다: 라우트는 기능 수준 분할에, @defer는 템플릿 내 컴포넌트 수준 분할에 사용됩니다
  • 비스탠드얼론 컴포넌트는 지연 로딩이 불가능하며 암묵적으로 즉시 로딩됩니다

연습을 시작하세요!

면접 시뮬레이터와 기술 테스트로 지식을 테스트하세요.

태그

#angular
#angular-defer
#lazy-loading
#performance
#ssr
#signals
#deep-dive

공유

관련 기사