Angular 19 Zoneless: Zone.js ์์ด ๊ตฌํํ๋ ์ฑ๋ฅ๊ณผ ๋ณ๊ฒฝ ๊ฐ์ง
Angular์ zoneless ๋ณ๊ฒฝ ๊ฐ์ง๋ Zone.js๋ฅผ ์์ ํ ์ ๊ฑฐํ์ฌ ๋ ์์ ๋ฒ๋ค ํฌ๊ธฐ, ๋น ๋ฅธ ๋ ๋๋ง, ๊ทธ๋ฆฌ๊ณ ์๊ทธ๋์ ํตํ ๋ช ์์ ๋ฐ์์ฑ์ ์ ๊ณตํฉ๋๋ค. ์ด ์ฌ์ธต ๊ฐ์ด๋๋ Angular 19์ provideExperimentalZonelessChangeDetection๋ถํฐ Angular 20+ ์์ ํ API๊น์ง Zone.js์์ zoneless Angular๋ก์ ๋ง์ด๊ทธ๋ ์ด์ ๊ฒฝ๋ก๋ฅผ ๋ค๋ฃน๋๋ค.

Angular์ zoneless ๋ณ๊ฒฝ ๊ฐ์ง๋ standalone ์ปดํฌ๋ํธ ๋์ ์ดํ ํ๋ ์์ํฌ์์ ๊ฐ์ฅ ์ค์ํ ์ํคํ ์ฒ ๋ณํ๋ฅผ ๋ํ๋ ๋๋ค. Zone.js๋ฅผ ์์ ํ ์ ๊ฑฐํจ์ผ๋ก์จ Angular ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ ์์ ๋ฒ๋ค ํฌ๊ธฐ(์ฝ 33KB ๊ฐ์), ๋ถํ์ํ ๋ณ๊ฒฝ ๊ฐ์ง ์ฌ์ดํด 30-40% ๊ฐ์, ๊ทธ๋ฆฌ๊ณ Zone ๊ด๋ จ ๋ ธ์ด์ฆ๊ฐ ์๋ ๊น๋ํ ์คํ ํธ๋ ์ด์ค๋ฅผ ์ป๊ฒ ๋ฉ๋๋ค.
Angular 18์์ zoneless๊ฐ ์คํ์ ์ผ๋ก ๋์
๋์์ต๋๋ค. Angular 19๋ provideExperimentalZonelessChangeDetection()์ผ๋ก ์คํ์ API๋ฅผ ๊ฐ์ ํ์ต๋๋ค. Angular 20์ provideZonelessChangeDetection()์ผ๋ก ์์ ํ๋์์ต๋๋ค. Angular 21์ ์ ํ๋ก์ ํธ์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก zoneless๋ฅผ ์ค์ ํฉ๋๋ค.
Zone.js ๋ณ๊ฒฝ ๊ฐ์ง์ ๋ด๋ถ ๋์ ์๋ฆฌ
zoneless๋ฅผ ์ดํดํ๊ธฐ ์ ์ Zone.js ๋ฉ์ปค๋์ฆ์ ๋ํ ๋ช
ํํ ์ค๋ช
์ด ํ์ํฉ๋๋ค. Zone.js๋ ๋ชจ๋ ๋น๋๊ธฐ ๋ธ๋ผ์ฐ์ API๋ฅผ ๋ชฝํค ํจ์นญํฉ๋๋ค: setTimeout, setInterval, Promise.then, addEventListener, XMLHttpRequest ๋ฑ ์์ญ ๊ฐ์ API๋ฅผ ๊ฐ๋ก์ฑ๋๋ค. ์ด๋ฌํ API ์ค ํ๋๊ฐ ์๋ฃ๋ ๋๋ง๋ค Zone.js๋ Angular์ ์๋ฆฌ๊ณ , Angular๋ ์ ์ฒด ์ปดํฌ๋ํธ ํธ๋ฆฌ์์ ๋ณ๊ฒฝ ๊ฐ์ง๋ฅผ ์คํํฉ๋๋ค.
์ด ์ ๊ทผ ๋ฐฉ์์๋ ๊ทผ๋ณธ์ ์ธ ๊ฒฐํจ์ด ์์ต๋๋ค: Zone.js๋ ์ ํ๋ฆฌ์ผ์ด์
์ํ๊ฐ ์ค์ ๋ก ๋ณ๊ฒฝ๋์๋์ง์ ๋ํ ํต์ฐฐ๋ ฅ์ด ์์ต๋๋ค. ์์ํ๊ฒ ์ ๋๋ฉ์ด์
ํ์ด๋ฐ์ ์ํด ์ฌ์ฉ๋ setTimeout๋ ์ฌ์ ํ ์ ์ฒด ๋ณ๊ฒฝ ๊ฐ์ง ์ฌ์ดํด์ ํธ๋ฆฌ๊ฑฐํฉ๋๋ค. ์๋ฐฑ ๊ฐ์ ์ปดํฌ๋ํธ๊ฐ ์๋ ๋๊ท๋ชจ ์ ํ๋ฆฌ์ผ์ด์
์์ ์ด๋ฌํ ์ค๋ฒํค๋๋ ์ธก์ ๊ฐ๋ฅํด์ง๋๋ค.
import { ApplicationConfig } from '@angular/core';
import { provideZoneChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
// Zone.js๋ ์ฝ 130๊ฐ ์ด์์ ๋ธ๋ผ์ฐ์ API๋ฅผ ํจ์นํฉ๋๋ค
// ๋ชจ๋ ๋น๋๊ธฐ ์ฝ๋ฐฑ์ด ๋ณ๊ฒฝ ๊ฐ์ง๋ฅผ ํธ๋ฆฌ๊ฑฐํฉ๋๋ค
]
};์ด๋ฒคํธ ๋ณํฉ(Angular 14์์ ๋์ )์ ์ฌ๋ฌ ์ด๋ฒคํธ๋ฅผ ๋จ์ผ ๋ณ๊ฒฝ ๊ฐ์ง ์ฌ์ดํด๋ก ๋ฐฐ์นํ์ฌ ์ผ๋ถ ์ค๋ฒํค๋๋ฅผ ์ค์ด์ง๋ง, ํต์ฌ ๋ฌธ์ ๋ ์ฌ์ ํ ๋จ์ ์์ต๋๋ค: ๋ณ๊ฒฝ ๊ฐ์ง๊ฐ ํ์ํ ๊ฒ๋ณด๋ค ํจ์ฌ ๋ ์์ฃผ ์คํ๋ฉ๋๋ค.
Angular 19์ 20์์ Zoneless ๋ณ๊ฒฝ ๊ฐ์ง ํ์ฑํํ๊ธฐ
๋ง์ด๊ทธ๋ ์ด์ ๊ฒฝ๋ก๋ Angular ๋ฒ์ ์ ๋ฐ๋ผ ๋ค๋ฆ ๋๋ค. Angular 19๋ ์คํ์ API๋ฅผ ์ฌ์ฉํ๊ณ , Angular 20์ ์์ ํ๋ ๋ฒ์ ์ ์ ๊ณตํฉ๋๋ค.
import { ApplicationConfig } from '@angular/core';
import { provideExperimentalZonelessChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
provideExperimentalZonelessChangeDetection(),
// ๋ ์ด์ Zone.js ํจ์นญ์ด ์์ต๋๋ค
]
};import { ApplicationConfig } from '@angular/core';
import { provideZonelessChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
provideZonelessChangeDetection(),
]
};ํ๋ก๋ฐ์ด๋๋ฅผ ์ ํํ ํ, angular.json์ ๋น๋ ๋ฐ ํ
์คํธ ํ๊ฒ ๋ชจ๋์์ polyfills ๋ฐฐ์ด์์ zone.js๋ฅผ ์ ๊ฑฐํ ๋ค์ ํจํค์ง๋ฅผ ์ ๊ฑฐํฉ๋๋ค:
# angular.json์ ๋น๋ ๋ฐ ํ
์คํธ ํ๊ฒ์์ zone.js polyfill ์ ๊ฑฐ
# ๊ทธ๋ฐ ๋ค์ ํจํค์ง ์ ๊ฑฐ
npm uninstall zone.js๋ฒ๋ค ํฌ๊ธฐ ๊ฐ์๋ ์ฆ๊ฐ์ ์ ๋๋ค: Zone.js๋ ์ฆ์ ๋ก๋๋๋ ์ฝ๋์ ์ฝ 33KB ์๋ณธ(10KB gzip)์ ์ฐจ์งํฉ๋๋ค.
Zoneless ๋ชจ๋์์ ๋ณ๊ฒฝ ๊ฐ์ง๋ฅผ ํธ๋ฆฌ๊ฑฐํ๋ ๊ฒ
Zone.js๊ฐ ๋ชจ๋ ๋น๋๊ธฐ ์์ ์ ๊ฐ๋ก์ฑ์ง ์์ผ๋ฉด Angular๋ ๋ช ์์ ์๋ฆผ์ ์์กดํฉ๋๋ค. ํ๋ ์์ํฌ๋ ๋ค์ ์กฐ๊ฑด ์ค ํ๋๊ฐ ๋ฐ์ํ ๋ ๋ณ๊ฒฝ ๊ฐ์ง๋ฅผ ์์ฝํฉ๋๋ค:
- ํ ํ๋ฆฟ์์ ์ฝ์ ์๊ทธ๋์ด ๊ฐ์ ์ ๋ฐ์ดํธํจ
ChangeDetectorRef.markForCheck()๊ฐ ํธ์ถ๋จ (AsyncPipe์ ์ํด ์๋์ผ๋ก)ComponentRef.setInput()์ ํตํด ์ปดํฌ๋ํธ ์ ๋ ฅ์ด ๋ณ๊ฒฝ๋จ- ํ ํ๋ฆฟ ๋๋ ํธ์คํธ ๋ฆฌ์ค๋ ์ฝ๋ฐฑ์ด ์คํ๋จ (ํด๋ฆญ, ์ ๋ ฅ ๋ฑ)
- ์ด์ ์ ๋ํฐํ๋ ๋ทฐ๊ฐ ์ปดํฌ๋ํธ ํธ๋ฆฌ์ ์ฒจ๋ถ๋จ
import { Component, signal, computed } from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<div class="counter">
<button (click)="decrement()">-</button>
<span>{{ count() }}</span>
<button (click)="increment()">+</button>
<p>Double: {{ doubled() }}</p>
</div>
`
})
export class CounterComponent {
// ์๊ทธ๋ ์
๋ฐ์ดํธ๋ ํ
ํ๋ฆฟ์ ์๋์ผ๋ก ์๋ฆฝ๋๋ค
count = signal(0);
doubled = computed(() => this.count() * 2);
increment() {
this.count.update(v => v + 1);
// markForCheck() ํ์ ์์ - ์๊ทธ๋์ด ์๋ฆผ ์ฒ๋ฆฌ
}
decrement() {
this.count.update(v => v - 1);
}
}์๊ทธ๋์ zoneless ๋ชจ๋์ ์์ฐ์ค๋ฌ์ด ๋๋ฐ์์ ๋๋ค. ์๊ทธ๋ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด Angular๋ ์ ํํ ์ด๋ค ํ ํ๋ฆฟ์ด ์ด์ ์์กดํ๋์ง ์๊ณ ํด๋น ๋ทฐ์ ๋ํด์๋ง ํ๊ฒ ๋ณ๊ฒฝ ๊ฐ์ง๋ฅผ ์์ฝํฉ๋๋ค.
ChangeDetectionStrategy.OnPush๋ฅผ ์ฑํํ๋ ๊ฒ์ zoneless ํธํ์ฑ์ ์ํ ๊ถ์ฅ ๋จ๊ณ์ด์ง๋ง ์๊ฒฉํ๊ฒ ํ์๋ ์๋๋๋ค. ๊ธฐ๋ณธ ๋ณ๊ฒฝ ๊ฐ์ง ์ ๋ต์ ์ฌ์ ํ zoneless ๋ชจ๋์์ ์๋ํฉ๋๋ค. ๊ทธ๋ฌ๋ OnPush๋ ์ปดํฌ๋ํธ๊ฐ ์
๋ ฅ์ด ๋ณ๊ฒฝ๋๊ฑฐ๋ markForCheck()๊ฐ ํธ์ถ๋ ๋๋ง ๋ค์ ๋ ๋๋ง๋๋๋ก ๋ณด์ฅํ์ฌ zoneless ๋ฉํ ๋ชจ๋ธ๊ณผ ์์ฐ์ค๋ฝ๊ฒ ์ ๋ ฌ๋ฉ๋๋ค.
๊ธฐ์กด ์ ํ๋ฆฌ์ผ์ด์ ๋ง์ด๊ทธ๋ ์ด์ : ์ผ๋ฐ์ ์ธ ํจ์
Zone.js์์ zoneless๋ก์ ์ ํ์ ๊ธฐ์กด ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ํด ๊ฑฐ์ ํ ์ค ๋ณ๊ฒฝ์ด ์๋๋๋ค. Zone.js์ ์๋ฌต์ ๋์์ ์์กดํ๋ ์ฌ๋ฌ ํจํด์ ๋ช ์์ ์ฒ๋ฆฌ๊ฐ ํ์ํฉ๋๋ค.
setTimeout๊ณผ setInterval์ ๋ ์ด์ ์ ๋ฐ์ดํธ๋ฅผ ํธ๋ฆฌ๊ฑฐํ์ง ์์ต๋๋ค
Zone.js ๋ชจ๋์์ setTimeout ์ฝ๋ฐฑ์ ์๋์ผ๋ก ๋ณ๊ฒฝ ๊ฐ์ง๋ฅผ ํธ๋ฆฌ๊ฑฐํฉ๋๋ค. zoneless ๋ชจ๋์์๋ ๊ทธ๋ ์ง ์์ต๋๋ค.
@Component({
selector: 'app-user-status',
template: `<span>{{ statusMessage }}</span>`
})
export class UserStatusComponent {
statusMessage = 'Loading...';
ngOnInit() {
setTimeout(() => {
// Zone.js๋ ์ฌ๊ธฐ์ CD๋ฅผ ํธ๋ฆฌ๊ฑฐํ์ ๊ฒ์
๋๋ค - zoneless๋ ์๋๋๋ค
this.statusMessage = 'Ready';
}, 2000);
}
}import { Component, signal } from '@angular/core';
@Component({
selector: 'app-user-status',
template: `<span>{{ statusMessage() }}</span>`
})
export class UserStatusComponent {
statusMessage = signal('Loading...');
ngOnInit() {
setTimeout(() => {
// ์๊ทธ๋ ์
๋ฐ์ดํธ๋ Angular์ ์๋์ผ๋ก ์๋ฆฝ๋๋ค
this.statusMessage.set('Ready');
}, 2000);
}
}Reactive Forms๋ ๋ช ์์ ์๋ฆผ์ด ํ์ํฉ๋๋ค
FormControl.setValue() ๋๋ patchValue()๋ฅผ ํตํ ํผ ์ํ ๋ณ๊ฒฝ์ zoneless ๋ชจ๋์์ ์๋์ผ๋ก ๋ณ๊ฒฝ ๊ฐ์ง๋ฅผ ํธ๋ฆฌ๊ฑฐํ์ง ์์ต๋๋ค. ๋ ๊ฐ์ง ์ ๊ทผ ๋ฐฉ์์ด ์ด๋ฅผ ํด๊ฒฐํฉ๋๋ค: ํผ ์ต์ ๋ฒ๋ธ์ markForCheck()์ ์ฐ๊ฒฐํ๊ฑฐ๋ ์๊ทธ๋์ ํตํด ํผ ๋ฐ์ดํฐ๋ฅผ ๋
ธ์ถํฉ๋๋ค.
import { Component, inject, signal } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { toSignal } from '@angular/core/rxjs-interop';
import { debounceTime, distinctUntilChanged } from 'rxjs';
@Component({
selector: 'app-search',
imports: [ReactiveFormsModule],
template: `
<input [formControl]="searchControl" placeholder="Search..." />
<p>Results for: {{ searchTerm() }}</p>
`
})
export class SearchComponent {
searchControl = new FormControl('');
// ์ต์ ๋ฒ๋ธ์ ์๊ทธ๋๋ก ๋ณํํ์ฌ ์๋ ํ
ํ๋ฆฟ ์
๋ฐ์ดํธ
searchTerm = toSignal(
this.searchControl.valueChanges.pipe(
debounceTime(300),
distinctUntilChanged()
),
{ initialValue: '' }
);
}Angular ๋ฉด์ ์ค๋น๊ฐ ๋์ จ๋์?
์ธํฐ๋ํฐ๋ธ ์๋ฎฌ๋ ์ดํฐ, flashcards, ๊ธฐ์ ํ ์คํธ๋ก ์ฐ์ตํ์ธ์.
Zone.js ์๋ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง
zoneless Angular๋ฅผ ์ฌ์ฉํ SSR์ ํน๋ณํ ์ฃผ์๊ฐ ํ์ํฉ๋๋ค. Zone.js๋ ์ด์ ์ Angular๊ฐ ์ง๋ ฌํ์ ์ ํฉํ "์์ ์ ์ธ" ์ํ์ ๋๋ฌํ๋์ง ํ๋จํ๋ ๋ฐ ๋์์ ์ฃผ์์ต๋๋ค. ์ด๊ฒ์ด ์์ผ๋ฉด PendingTasks ์๋น์ค๊ฐ ๊ทธ ์ญํ ์ ์ฑ์๋๋ค.
import { Component, inject, signal } from '@angular/core';
import { PendingTasks } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';
@Component({
selector: 'app-data-loader',
template: `
@if (data()) {
<div>{{ data()!.title }}</div>
} @else {
<div>Loading...</div>
}
`
})
export class DataLoaderComponent {
private http = inject(HttpClient);
private pendingTasks = inject(PendingTasks);
data = signal<{ title: string } | null>(null);
ngOnInit() {
// PendingTasks.run()์ ์๋ฃ๋ ๋๊น์ง SSR ์ง๋ ฌํ๋ฅผ ๋ฐฉ์งํฉ๋๋ค
this.pendingTasks.run(async () => {
const result = await firstValueFrom(
this.http.get<{ title: string }>('/api/data')
);
this.data.set(result);
});
}
}PendingTasks ์์ด Angular๋ ๋น๋๊ธฐ ๋ฐ์ดํฐ๊ฐ ๋ก๋๋๊ธฐ ์ ์ ํ์ด์ง๋ฅผ ์ง๋ ฌํํ์ฌ ํด๋ผ์ด์ธํธ์ ๋น ์ฝํ
์ธ ๋ฅผ ๋ณด๋
๋๋ค.
์ฑ๋ฅ ๋ฒค์น๋งํฌ: Zone.js vs Zoneless
Zone.js ์ ๊ฑฐ๋ก ์ธํ ์ฑ๋ฅ ํฅ์์ ์ธ ๊ฐ์ง ๋ฒ์ฃผ๋ก ๋๋ฉ๋๋ค:
| ์งํ | Zone.js | Zoneless | ๊ฐ์ | |--------|---------|----------|-------------| | ์ด๊ธฐ ๋ฒ๋ค ํฌ๊ธฐ | +33KB ์๋ณธ / +10KB gzip | 0KB ์ค๋ฒํค๋ | 100% ๊ฐ์ | | ๋ณ๊ฒฝ ๊ฐ์ง ์ฌ์ดํด (์ผ๋ฐ์ ์ธ ์ฑ) | ์ํธ์์ฉ๋น 150-300ํ | ์ํธ์์ฉ๋น 5-15ํ | 80-95% ๊ฐ์ | | ์คํ ํธ๋ ์ด์ค ๊น์ด | 8-12๊ฐ์ ์ถ๊ฐ Zone ํ๋ ์ | ๊น๋ํ ๋ค์ดํฐ๋ธ ํธ๋ ์ด์ค | ์ฆ๊ฐ์ ์ธ ๋ช ํ์ฑ | | Time to Interactive (TTI) | ๊ธฐ์ค์ | 15-25% ๋ ๋น ๋ฆ | Zone ๋ถํธ์คํธ๋ฉ ์ ๊ฑฐ |
๊ฐ์ฅ ๊ทน์ ์ธ ๊ฐ์ ์ ๋ฌด๊ฑฐ์ด ๋น๋๊ธฐ ํ๋์ด ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ํ๋ฉ๋๋ค: HTTP ํด๋ง, WebSocket ์ฐ๊ฒฐ, ํ์ด๋จธ, ๋ณต์กํ ์ด๋ฒคํธ ํธ๋ค๋ฌ. ์ด๋ค ๊ฐ๊ฐ์ ์ด์ ์ zoneless ๋ชจ๋๊ฐ ์์ ํ ์ ๊ฑฐํ๋ ๋ถํ์ํ ๋ณ๊ฒฝ ๊ฐ์ง ์ฌ์ดํด์ ํธ๋ฆฌ๊ฑฐํ์ต๋๋ค.
์ผ๋ถ ์๋ํํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ฌ์ ํ ๋ด๋ถ์ ์ผ๋ก Zone.js์ ์์กดํฉ๋๋ค. ๋ชจ๋ฌ ๋ํ ์์, ํน์ Web Component ๋ํผ, ์ผ๋ถ ์ ๋๋ฉ์ด์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ zoneless ๋ชจ๋์์ ์๊ธฐ์น ์๊ฒ ๋์ํ ์ ์์ต๋๋ค. ํ๋ก๋์ ์ zoneless๋ฅผ ๋ฐฐํฌํ๊ธฐ ์ ์ ํญ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํตํฉ์ ์ฒ ์ ํ ํ ์คํธํ์ญ์์ค.
์ ํ์์ ์ด์๋จ๋ NgZone API
์ผ๋ฐ์ ์ธ ์คํด: Zone.js๋ฅผ ์ ๊ฑฐํ๋ฉด ๋ชจ๋ NgZone ์ฐธ์กฐ๋ฅผ ์ ๊ฑฐํด์ผ ํฉ๋๋ค. ์ด๊ฒ์ ์๋ชป๋์์ต๋๋ค. NgZone.run()๊ณผ NgZone.runOutsideAngular()๋ zoneless ์ ํ๋ฆฌ์ผ์ด์
๊ณผ ํธํ๋๋ฉฐ ๊ณต์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ ์ง๋์ด์ผ ํฉ๋๋ค. ์ด๋ค์ ์ ๊ฑฐํ๋ฉด ์ฌ์ ํ Zone.js๋ฅผ ์ฌ์ฉํ๊ณ ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์๋นํ๋ ์ ํ๋ฆฌ์ผ์ด์
์์ ์ฑ๋ฅ ์ ํ๋ฅผ ์ผ์ผํฌ ์ ์์ต๋๋ค.
๊ทธ๋ฌ๋ ์ธ ๊ฐ์ง NgZone ์ต์ ๋ฒ๋ธ์ ์ ๊ฑฐํด์ผ ํฉ๋๋ค:
NgZone.onMicrotaskEmpty- zoneless ๋ชจ๋์์ ๋ฐฉ์ถ๋์ง ์์NgZone.onUnstable- zoneless ๋ชจ๋์์ ๋ฐฉ์ถ๋์ง ์์NgZone.onStable- zoneless ๋ชจ๋์์ ๋ฐฉ์ถ๋์ง ์์
์ด๋ฌํ ์ต์ ๋ฒ๋ธ์ ์ฌ์ฉํ ํ์ด๋ฐ ์ข
์ ๋ก์ง์ @angular/core์ afterNextRender() ๋๋ afterEveryRender()๋ก ๊ต์ฒดํ์ญ์์ค.
Angular 19 ์คํ์ ์์ Angular 21 ๊ธฐ๋ณธ๊ฐ๊น์ง
zoneless API ์งํ๋ ๋ช ํํ ์์ ํ ๊ฒฝ๋ก๋ฅผ ๋ฐ๋ฆ ๋๋ค:
- Angular 18.1:
provideExperimentalZonelessChangeDetection()์ด ์คํ์ ์ผ๋ก ๋์ ๋จ - Angular 19: ์คํ์ API ๊ฐ์ , ๊ด๋ฒ์ํ ์ํ๊ณ ํ ์คํธ
- Angular 20:
provideZonelessChangeDetection()์ผ๋ก ์ด๋ฆ ๋ณ๊ฒฝ, ์์ ํ๋ก ์น๊ฒฉ - Angular 20.2: API ์์ ํ ์์ ํ๋์ด ๋์ ๋ณ๊ฒฝ ์์ ์์
- Angular 21: zoneless๊ฐ
ng newํ๋ก์ ํธ์ ๊ธฐ๋ณธ๊ฐ์ด ๋๋ฉฐ ํ๋ก๋ฐ์ด๋ ํธ์ถ ๋ถํ์
Angular 19๋ฅผ ์ฌ์ฉํ๋ ํ์ ์
๊ทธ๋ ์ด๋ ๊ฒฝ๋ก๋ ๊ฐ๋จํฉ๋๋ค: Angular 20์ผ๋ก ์
๋ฐ์ดํธํ๊ณ , provideExperimentalZonelessChangeDetection์ provideZonelessChangeDetection์ผ๋ก ๊ต์ฒดํ๊ณ , zone.js polyfill์ ์ ๊ฑฐํฉ๋๋ค. Angular 19๋ 2026๋
5์ 19์ผ์ ์ง์ ์ข
๋ฃ๋๋ฏ๋ก ์ด ๋ง์ด๊ทธ๋ ์ด์
์ ์๊ฐ์ ๋ฏผ๊ฐํฉ๋๋ค.
Angular ๋ฉด์ ์ ์ค๋นํ๊ณ ๊ณ์ ๊ฐ์? SharpSkill์ Angular ๋ฉด์ ์ง๋ฌธ ๋ชจ๋์ ๋ฉด์ ๊ด์ด ์ ์ ๋ ๋ฌป๋ zoneless ์๋๋ฆฌ์ค๋ฅผ ํฌํจํ ๋ณ๊ฒฝ ๊ฐ์ง ํจํด์ ์ฌ๋ ์๊ฒ ๋ค๋ฃน๋๋ค. ๋ ๊ด๋ฒ์ํ ๊ฐ์๋ Angular ๋ฉด์ ์ง๋ฌธ ์์ 25๊ฐ ๊ฐ์ด๋๋ฅผ ์ฐธ์กฐํ์ญ์์ค. Angular ์๊ทธ๋ ๋ชจ๋์ zoneless๋ฅผ ๊ฐ๋ฅํ๊ฒ ํ๋ ๋ฐ์์ฑ ๋ชจ๋ธ๋ ๋ค๋ฃน๋๋ค.
๊ฒฐ๋ก
- Zone.js๋ฅผ ์ ๊ฑฐํ๋ฉด 33KB์ ๋ฒ๋ค ๋ฌด๊ฒ๋ฅผ ์ ๊ฑฐํ๊ณ 130๊ฐ ์ด์์ ๋ธ๋ผ์ฐ์ API๋ฅผ ๊ฐ๋ก์ฑ๋ ๋ชฝํค ํจ์นญ ๋ ์ด์ด๋ฅผ ์ ๊ฑฐํฉ๋๋ค
- Angular ์๊ทธ๋์ zoneless๋ฅผ ์ค์ฉ์ ์ผ๋ก ๋ง๋๋ ๋ช ์์ ๋ฐ์์ฑ ๋ชจ๋ธ์ ์ ๊ณตํ๋ฉฐ, ์ํ๊ฐ ๋ณ๊ฒฝ๋ ๋ ํ ํ๋ฆฟ์ ์๋์ผ๋ก ์๋ฆฝ๋๋ค
- ๋ง์ด๊ทธ๋ ์ด์
์
setTimeout/setIntervalํจํด, reactive forms, ํ์ด๋ฐ ์ข ์ ๋ก์ง์ ์๊ทธ๋ ๊ธฐ๋ฐ ๋๋markForCheck()์ ๊ทผ ๋ฐฉ์์ผ๋ก ๋ณํํด์ผ ํฉ๋๋ค - SSR ์ ํ๋ฆฌ์ผ์ด์
์ Zone.js ์์ ์ฑ ๊ฐ์ง๋ฅผ ๋์ฒดํ๊ธฐ ์ํด
PendingTasks๋ฅผ ์ฑํํด์ผ ํฉ๋๋ค NgZone.run()๊ณผNgZone.runOutsideAngular()๋ ์ด์ ๋ฒ์ ๊ณผ์ ํธํ์ฑ์ ์ํด ๊ณต์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ณด์กด๋์ด์ผ ํฉ๋๋ค- Angular 19์ ์คํ์ API(
provideExperimentalZonelessChangeDetection)๋ Angular 20์ ์์ ํ๋provideZonelessChangeDetection()์ ์ง์ ๋งคํ๋์ด ์ ๊ทธ๋ ์ด๋๊ฐ ์ด๋ฆ ๋ณ๊ฒฝ ์์ ์ด ๋ฉ๋๋ค - ์๋ํํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํธํ์ฑ์ ์ฌ์ ํ ์ฃผ์ ์ํ ์์์ด๋ฉฐ ํ๋ก๋์ ๋ฐฐํฌ ์ ์ ์ฒ ์ ํ ํ ์คํธ๊ฐ ํ์ํฉ๋๋ค
์ฐ์ต์ ์์ํ์ธ์!
๋ฉด์ ์๋ฎฌ๋ ์ดํฐ์ ๊ธฐ์ ํ ์คํธ๋ก ์ง์์ ํ ์คํธํ์ธ์.
ํ๊ทธ
๊ณต์
๊ด๋ จ ๊ธฐ์ฌ

Angular @defer ์์ ๊ฐ์ด๋ 2026: ์ ์ธ์ ์ง์ฐ ๋ก๋ฉ๊ณผ ๋ฉด์ ์ง๋ฌธ
Angular @defer ๋ธ๋ก์ ํ์ฉํ ์ ์ธ์ ์ง์ฐ ๋ก๋ฉ์ ์๋ ์๋ฆฌ, ํธ๋ฆฌ๊ฑฐ ์ ํ, ํ๋ฆฌํ์น ์ ๋ต, ์ ์ง์ ํ์ด๋๋ ์ด์ , ๋ฉด์ ์ง๋ฌธ์ ์์ธํ ๋ค๋ฃน๋๋ค.

Angular 20 Resource API ํต์ฌ ์ ๋ฆฌ: httpResource, rxResource ํ์ฉ๋ฒ๊ณผ ์ค์ ๋ฉด์ ๋๋น
Angular 20์์ ๋์ ๋ Resource API์ ํต์ฌ ๊ฐ๋ ์ ์ ๋ฆฌํฉ๋๋ค. httpResource์ rxResource๋ฅผ ํ์ฉํ ์๊ทธ๋ ๊ธฐ๋ฐ ๋ฐ์ดํฐ ํ์นญ, Zod ์คํค๋ง ๊ฒ์ฆ, ResourceStatus ๋ฌธ์์ด ๋ฆฌํฐ๋ด, HttpClient ๊ตฌ๋ ํจํด ๋ง์ด๊ทธ๋ ์ด์ , ๊ทธ๋ฆฌ๊ณ ์ค์ ๊ธฐ์ ๋ฉด์ ์ง๋ฌธ๊น์ง ์ฝ๋ ์์ ์ ํจ๊ป ์์ธํ ๋ค๋ฃน๋๋ค.

Angular 19 ๋ฉด์ ์ง๋ฌธ: Signals, SSR๊ณผ ๋ฐ๋์ ์์์ผ ํ ๊ฐ๋
๊ฐ์ฅ ์์ฃผ ๋์ค๋ Angular 19 ๋ฉด์ ์ง๋ฌธ: Signals, ์ ์ง์ ํ์ด๋๋ ์ด์ , zoneless ๋ณ๊ฒฝ ๊ฐ์ง, ์๋ก์ด ๋ฆฌ์กํฐ๋ธ API. ์ฝ๋ ์์ ์ ๊ธฐ๋๋๋ ๋ต๋ณ๊น์ง ํจ๊ป ์ ๋ฆฌํฉ๋๋ค.