마이그레이션

기존 Angular 프로젝트를 독립형으로 마이그레이션하기

독립형 컴포넌트는 Angular 애플리케이션을 더 간단하게 만드는 방법을 제공합니다. 독립형 컴포넌트, 디렉티브, 파이프는 NgModule의 필요성을 줄여 개발 과정을 간소화하는 것을 목표로 합니다. 기존 애플리케이션도 호환성을 깨뜨리지 않고 필요에 따라 점진적으로 새로운 독립형 방식을 도입할 수 있습니다.

이 스키매틱은 기존 프로젝트의 컴포넌트, 디렉티브, 파이프를 독립형으로 변환하도록 돕습니다. 가능한 한 많은 코드를 자동으로 변환하는 것을 목표로 하지만, 프로젝트 개발자가 일부 코드를 직접 수정해야 할 수도 있습니다.

다음 명령어로 스키매틱을 실행하세요.

ng generate @angular/core:standalone

업데이트 전 확인 사항

스키매틱을 사용하기 전에 프로젝트가 다음 조건을 만족하는지 확인하세요.

  1. Angular 15.2.0 이상을 사용합니다.
  2. 컴파일 오류 없이 빌드됩니다.
  3. 작업 트리가 깨끗한 Git 브랜치에 있으며 모든 작업을 저장했습니다.

스키매틱 옵션

옵션 설명
mode 수행할 변환입니다. 사용 가능한 옵션의 자세한 내용은 아래 마이그레이션 모드를 참고하세요.
path 프로젝트 루트 기준으로 마이그레이션할 상대 경로입니다. 이 옵션으로 프로젝트의 일부를 점진적으로 마이그레이션할 수 있습니다.

마이그레이션 단계

마이그레이션 과정은 세 단계로 구성됩니다. 마이그레이션을 여러 번 실행하고 프로젝트가 예상대로 빌드되고 동작하는지 직접 확인해야 합니다.

NOTE: 스키매틱은 대부분의 코드를 자동으로 업데이트할 수 있지만 일부 예외적인 경우에는 개발자가 직접 개입해야 합니다. 각 마이그레이션 단계 이후에 수동으로 수정하는 작업을 계획하세요. 또한 스키매틱이 생성하는 새 코드가 프로젝트의 코드 서식 규칙과 일치하지 않을 수 있습니다.

다음 순서대로 마이그레이션을 실행하고 각 단계 사이에 코드가 빌드되고 실행되는지 확인하세요.

  1. ng g @angular/core:standalone을 실행하고 "Convert all components, directives and pipes to standalone"(모든 컴포넌트, 디렉티브, 파이프를 독립형으로 변환)을 선택합니다.
  2. ng g @angular/core:standalone을 실행하고 "Remove unnecessary NgModule classes"(불필요한 NgModule 클래스 제거)를 선택합니다.
  3. ng g @angular/core:standalone을 실행하고 "Bootstrap the application using standalone APIs"(독립형 API로 애플리케이션 부트스트래핑)를 선택합니다.
  4. 린트와 서식 검사를 실행하고 오류를 수정한 뒤 결과를 커밋합니다.

마이그레이션 이후

축하합니다. 애플리케이션을 독립형으로 변환했습니다 🎉. 필요에 따라 다음 작업을 진행할 수 있습니다.

  • 남아 있는 NgModule 선언을 찾아 제거합니다. "불필요한 NgModule 제거" 단계는 모든 모듈을 자동으로 제거할 수 없으므로 남은 선언을 직접 제거해야 할 수 있습니다.
  • 프로젝트의 단위 테스트를 실행하고 실패한 테스트를 수정합니다.
  • 자동 서식 지정을 사용하는 프로젝트라면 코드 포매터를 실행합니다.
  • 프로젝트의 린터를 실행하고 새 경고를 수정합니다. 일부 린터는 경고를 자동으로 해결할 수 있는 --fix 플래그를 지원합니다.

마이그레이션 모드

이 마이그레이션에는 다음 모드가 있습니다.

  1. 선언을 독립형으로 변환합니다.
  2. 불필요한 NgModule을 제거합니다.
  3. 독립형 부트스트래핑 API로 전환합니다. 이 순서대로 마이그레이션을 실행해야 합니다.

선언을 독립형으로 변환하기

이 모드에서는 standalone: false를 제거하고 imports 배열에 의존성을 추가하여 모든 컴포넌트, 디렉티브, 파이프를 독립형으로 변환합니다.

HELPFUL: 이 단계에서 스키매틱은 컴포넌트를 부트스트래핑하는 NgModule을 무시합니다. 이러한 모듈은 독립형을 지원하는 bootstrapApplication 대신 bootstrapModule에서 사용하는 루트 모듈일 가능성이 높기 때문입니다. 스키매틱은 "독립형 부트스트래핑 API로 전환" 단계에서 이러한 선언을 자동으로 변환합니다.

이전:

shared.module.ts

@NgModule({
  imports: [CommonModule],
  declarations: [Greeter],
  exports: [Greeter],
})
export class SharedModule {}

greeter.ts

@Component({
  selector: 'greeter',
  template: '<div *ngIf="showGreeting">Hello</div>',
  standalone: false,
})
export class Greeter {
  showGreeting = true;
}

이후:

shared.module.ts

@NgModule({
  imports: [CommonModule, Greeter],
  exports: [Greeter],
})
export class SharedModule {}

greeter.ts

@Component({
  selector: 'greeter',
  template: '<div *ngIf="showGreeting">Hello</div>',
  imports: [NgIf],
})
export class Greeter {
  showGreeting = true;
}

불필요한 NgModule 제거하기

모든 선언을 독립형으로 변환하면 많은 NgModule을 안전하게 제거할 수 있습니다. 이 단계는 해당 모듈 선언과 관련 참조를 가능한 한 많이 삭제합니다. 참조를 자동으로 삭제할 수 없으면 다음 TODO 주석을 남기므로 직접 NgModule을 삭제할 수 있습니다.

/* TODO(standalone-migration): clean up removed NgModule reference manually */

마이그레이션은 다음 조건을 만족하는 모듈을 안전하게 제거할 수 있다고 판단합니다.

  • declarations가 없습니다.
  • providers가 없습니다.
  • bootstrap 컴포넌트가 없습니다.
  • ModuleWithProviders 심벌이나 제거할 수 없는 모듈을 참조하는 imports가 없습니다.
  • 클래스 멤버가 없습니다. 빈 생성자는 무시합니다.

이전:

importer.module.ts

@NgModule({
  imports: [FooComponent, BarPipe],
  exports: [FooComponent, BarPipe],
})
export class ImporterModule {}

이후:

importer.module.ts

// Does not exist!

독립형 부트스트래핑 API로 전환하기

이 단계는 모든 bootstrapModule 사용을 새로운 독립형 기반 bootstrapApplication으로 변환합니다. 루트 컴포넌트의 standalone: false도 제거하고 루트 NgModule을 삭제합니다. 루트 모듈에 providers나 imports가 있으면 해당 설정을 새로운 부트스트랩 호출로 최대한 복사하려고 시도합니다.

이전:

./app/app.module.ts

import {NgModule} from '@angular/core';
import {App} from './app';

@NgModule({
  declarations: [App],
  bootstrap: [App],
})
export class AppModule {}

./app/app.ts

@Component({
  selector: 'app',
  template: 'hello',
  standalone: false,
})
export class App {}

./main.ts

import {platformBrowser} from '@angular/platform-browser';
import {AppModule} from './app/app.module';

platformBrowser()
  .bootstrapModule(AppModule)
  .catch((e) => console.error(e));

이후:

./app/app.module.ts

// Does not exist!

./app/app.ts

@Component({
  selector: 'app',
  template: 'hello',
})
export class App {}

./main.ts

import {bootstrapApplication} from '@angular/platform-browser';
import {App} from './app/app';

bootstrapApplication(App).catch((e) => console.error(e));

자주 발생하는 문제

스키매틱의 정상 동작을 방해할 수 있는 일반적인 문제는 다음과 같습니다.

  • 컴파일 오류: 프로젝트에 컴파일 오류가 있으면 Angular가 코드를 올바르게 분석하고 마이그레이션할 수 없습니다.
  • tsconfig에 포함되지 않은 파일: 스키매틱은 프로젝트의 tsconfig.json 파일을 분석하여 마이그레이션할 파일을 결정합니다. tsconfig에 포함되지 않은 파일은 제외합니다.
  • 정적으로 분석할 수 없는 코드: 스키매틱은 정적 분석으로 코드를 이해하고 수정할 위치를 결정합니다. 빌드 시점에 정적으로 분석할 수 없는 메타데이터를 가진 클래스는 건너뛸 수 있습니다.

제약 사항

마이그레이션의 규모와 복잡성 때문에 스키매틱이 처리할 수 없는 경우가 있습니다.

  • 단위 테스트는 사전(AoT) 컴파일되지 않으므로 단위 테스트의 컴포넌트에 추가되는 imports가 완전히 정확하지 않을 수 있습니다.
  • 스키매틱은 Angular API를 직접 호출하는 코드를 기준으로 동작합니다. Angular API를 감싼 사용자 정의 래퍼는 인식할 수 없습니다. 예를 들어 TestBed.configureTestingModule을 감싼 customConfigureTestModule 함수를 정의하면 해당 함수에서 선언한 컴포넌트를 인식하지 못할 수 있습니다.