개발자 도구
Angular CLI

AOT 메타데이터 오류

발생할 수 있는 메타데이터 오류와 그 원인, 수정 방법은 다음과 같습니다.

지원하지 않는 표현식 형식

도움말: 컴파일러가 Angular 메타데이터를 평가하는 과정에서 해석할 수 없는 표현식을 만났습니다.

다음 예제처럼 컴파일러의 제한된 표현식 문법에서 벗어난 언어 기능을 사용하면 이 오류가 발생할 수 있습니다:

// 오류
export class Fooish { … }
…
const prop = typeof Fooish; // 메타데이터에서는 typeof를 사용할 수 없습니다.
  …
  // 메타데이터에서는 대괄호 표기법을 사용할 수 없습니다.
  { provide: 'token', useValue: { [prop]: 'value' } };
  …

일반 애플리케이션 코드에서는 typeof와 대괄호 표기법을 사용할 수 있습니다. 하지만 Angular 메타데이터를 정의하는 표현식 안에서는 사용할 수 없습니다.

이 오류를 방지하려면 Angular 메타데이터를 작성할 때 컴파일러의 제한된 표현식 문법을 따르고, 새롭거나 일반적이지 않은 TypeScript 기능을 사용할 때 주의하세요.

내보내지 않은 로컬 심볼 참조

도움말: 내보내지 않은 로컬 심볼 'symbol name'을 참조했습니다. 해당 심볼을 내보내는 것을 고려하세요.

컴파일러가 로컬에 정의된 심볼의 참조를 발견했지만 해당 심볼을 내보내지 않았거나 초기화하지 않았습니다.

다음 provider 예제에서 이 문제가 발생합니다.

// 오류
let foo: number; // 내보내지도 초기화하지도 않았습니다.

@Component({
  selector: 'my-component',
  template: … ,
  providers: [
    { provide: Foo, useValue: foo }
  ]
})
export class MyComponent {}

컴파일러는 useValue 프로바이더 코드를 포함하는 컴포넌트 팩토리를 별도 모듈에 생성합니다. 해당 팩토리 모듈은 이 소스 모듈의 내보내지 않은 로컬 변수 foo에 접근할 수 없습니다.

foo를 초기화하면 문제를 해결할 수 있습니다.

let foo = 42; // 초기화했습니다.

컴파일러는 표현식을 접어서 다음과 같이 작성한 것처럼 프로바이더에 반영합니다.

providers: [{provide: Foo, useValue: 42}];

또는 실제 값을 알 수 있는 실행 시점에 foo에 값을 할당할 계획이라면 foo를 내보내서 해결할 수도 있습니다.

// 수정한 코드
export let foo: number; // 내보냈습니다.

@Component({
  selector: 'my-component',
  template: … ,
  providers: [
    { provide: Foo, useValue: foo }
  ]
})
export class MyComponent {}

providers, animations와 같은 메타데이터에서 참조하는 변수는 export를 추가하면 해결되는 경우가 많습니다. 이러한 표현식에서 컴파일러는 내보낸 변수에 대한 참조 를 생성할 수 있기 때문에 변수의 값 은 필요하지 않습니다.

하지만 컴파일러가 코드를 생성하기 위해 실제 값 을 필요로 한다면 export를 추가해도 해결되지 않습니다. 예를 들어 template 프로퍼티에서는 이 방법을 사용할 수 없습니다.

// 오류
export let someTemplate: string; // 내보냈지만 초기화하지 않았습니다.

@Component({
  selector: 'my-component',
  template: someTemplate,
})
export class MyComponent {}

컴포넌트 팩토리를 생성하려면 컴파일러는 template 프로퍼티의 값을 바로 지금 알아야 합니다. 변수 참조만으로는 충분하지 않습니다. 선언 앞에 export를 추가하면 "Only initialized variables and constants can be referenced"라는 새로운 오류가 발생할 뿐입니다.

초기화한 변수와 상수만 참조할 수 있습니다

도움말: 템플릿 컴파일러가 변수의 값을 알아야 하므로 초기화한 변수와 상수만 참조할 수 있습니다.

컴파일러가 내보낸 변수나 정적 필드의 참조를 발견했지만 해당 변수나 필드를 초기화하지 않았습니다. 코드를 생성하려면 해당 변수의 값이 필요합니다.

다음 예제는 선언만 하고 값을 할당하지 않은 someTemplate 변수를 내보낸 뒤 그 값으로 컴포넌트의 template 프로퍼티를 설정하려고 합니다.

// 오류
export let someTemplate: string;

@Component({
  selector: 'my-component',
  template: someTemplate,
})
export class MyComponent {}

다른 모듈에서 someTemplate을 가져오더라도 해당 모듈에서 초기화하지 않았다면 같은 오류가 발생합니다.

import {Component} from '@angular/core';
// 오류: 가져온 모듈에서도 초기화하지 않았습니다.
import {someTemplate} from './config';

@Component({
  selector: 'my-component',
  template: someTemplate,
})
export class MyComponent {}

컴파일러는 템플릿 정보를 얻기 위해 실행 시점까지 기다릴 수 없습니다. 소스 코드에서 someTemplate 변수의 값을 정적으로 알아내야 템플릿에 따라 엘리먼트를 구성하는 명령을 포함한 컴포넌트 팩토리를 생성할 수 있습니다.

이 오류를 수정하려면 선언과 같은 줄에서 초기화 구문으로 변수의 초기값을 지정하세요.

// 수정한 코드
export let someTemplate = '<h1>Greetings from Angular</h1>';

@Component({
  selector: 'my-component',
  template: someTemplate,
})
export class MyComponent {}

내보내지 않은 클래스 참조

도움말: 내보내지 않은 클래스 <class name>을 참조했습니다. 해당 클래스를 내보내는 것을 고려하세요.

메타데이터가 내보내지 않은 클래스를 참조했습니다.

예를 들어 클래스를 정의하고 프로바이더 배열에서 주입 토큰으로 사용했지만 해당 클래스를 내보내지 않았을 수 있습니다.

// 오류
abstract class MyStrategy { }

  …
  providers: [
    { provide: MyStrategy, useValue: … }
  ]
  …

Angular는 별도 모듈에 클래스 팩토리를 생성하며 이 팩토리는 내보낸 클래스에만 접근할 수 있습니다. 이 오류를 수정하려면 참조하는 클래스를 내보내세요.

// 수정한 코드
export abstract class MyStrategy { }

  …
  providers: [
    { provide: MyStrategy, useValue: … }
  ]
  …

구조 분해한 변수나 상수는 지원하지 않습니다

도움말: 템플릿 컴파일러는 내보낸 구조 분해 변수나 상수의 참조를 지원하지 않습니다. 구조 분해를 사용하지 않도록 코드를 단순하게 작성해 보세요.

컴파일러는 구조 분해로 값을 할당한 변수의 참조를 지원하지 않습니다.

예를 들어 다음과 같이 작성할 수 없습니다:

// 오류
import { configuration } from './configuration';

// 구조 분해로 foo와 bar에 값을 할당합니다.
const {foo, bar} = configuration;
  …
  providers: [
    {provide: Foo, useValue: foo},
    {provide: Bar, useValue: bar},
  ]
  …

이 오류를 수정하려면 구조 분해하지 않은 값을 참조하세요.

// 수정한 코드
import { configuration } from './configuration';
  …
  providers: [
    {provide: Foo, useValue: configuration.foo},
    {provide: Bar, useValue: configuration.bar},
  ]
  …

타입을 확인할 수 없습니다

도움말: 컴파일러가 타입을 발견했지만 어느 모듈이 해당 타입을 내보내는지 확인할 수 없습니다.

주변 환경 타입(ambient type)을 참조하면 이 문제가 발생할 수 있습니다. 예를 들어 Window 타입은 전역 .d.ts 파일에 선언된 주변 환경 타입입니다.

컴파일러가 정적으로 분석해야 하는 컴포넌트 생성자에서 이 타입을 참조하면 오류가 발생합니다.

// 오류
@Component({ })
export class MyComponent {
  constructor (private win: Window) { … }
}

TypeScript는 주변 환경 타입을 이해하기 때문에 따로 가져올 필요가 없습니다. 하지만 Angular 컴파일러는 내보내거나 가져오지 않은 타입을 이해하지 못합니다.

이 경우 컴파일러는 Window 토큰으로 어떻게 의존성을 주입해야 하는지 알지 못합니다.

메타데이터 표현식에서 주변 환경 타입을 참조하지 마세요.

주변 환경 타입의 인스턴스를 반드시 주입해야 한다면 다음 네 단계로 해결할 수 있습니다:

  1. 주변 환경 타입의 인스턴스를 위한 주입 토큰을 만듭니다.
  2. 해당 인스턴스를 반환하는 팩토리 함수를 만듭니다.
  3. 이 팩토리 함수를 사용하는 useFactory 프로바이더를 추가합니다.
  4. @Inject로 인스턴스를 주입합니다.

다음 예제를 참고하세요.

// 수정한 코드
import {Inject, InjectionToken, Component} from '@angular/core';

export const WINDOW = new InjectionToken('Window');
export function _window() { return window; }

@Component({
  …
  providers: [
    { provide: WINDOW, useFactory: _window }
  ]
})
export class MyComponent {
  constructor (@Inject(WINDOW) private win: Window) { … }
}

컴파일러는 @Inject(WINDOW)를 사용해서 주입 코드를 생성하므로 생성자의 Window 타입은 더 이상 문제가 되지 않습니다.

Angular는 DOCUMENT 토큰에도 비슷한 방식을 사용합니다. 따라서 브라우저의 document 객체 또는 애플리케이션이 실행되는 플랫폼에 따른 추상화 객체를 주입할 수 있습니다.

import {Inject, Component} from '@angular/core';
import { DOCUMENT } from '@angular/common';

@Component({ … })
export class MyComponent {
  constructor (@Inject(DOCUMENT) private doc: Document) { … }
}

이름이 필요합니다

도움말: 컴파일러가 평가하는 표현식에 이름이 필요합니다.

다음 예제처럼 프로퍼티 이름에 숫자를 사용하면 이 문제가 발생할 수 있습니다.

// 오류
provider: [{provide: Foo, useValue: {0: 'test'}}];

프로퍼티 이름을 숫자가 아닌 값으로 변경하세요.

// 수정한 코드
provider: [{provide: Foo, useValue: {'0': 'test'}}];

지원하지 않는 열거형 멤버 이름

도움말: Angular가 메타데이터에서 참조한 열거형 멤버의 값을 확인할 수 없습니다.

컴파일러는 단순한 열거형 값은 이해하지만 계산된 프로퍼티에서 얻는 값과 같은 복잡한 값은 이해하지 못합니다.

// 오류
enum Colors {
  Red = 1,
  White,
  Blue = "Blue".length // 계산된 값
}

  …
  providers: [
    { provide: BaseColor,   useValue: Colors.White } // 정상
    { provide: DangerColor, useValue: Colors.Red }   // 정상
    { provide: StrongColor, useValue: Colors.Blue }  // 오류
  ]
  …

복잡한 초기화 구문이나 계산된 프로퍼티를 가진 열거형은 참조하지 마세요.

태그드 템플릿 표현식은 지원하지 않습니다

도움말: 메타데이터에서는 태그드 템플릿 표현식을 지원하지 않습니다.

컴파일러가 다음과 같은 JavaScript ES2015 태그드 템플릿 표현식을 발견했습니다.

// 오류
const expression = 'funky';
const raw = String.raw`A tagged template ${expression} string`;
 …
 template: '<div>' + raw + '</div>'
 …

String.raw()는 JavaScript ES2015에 내장된 태그 함수 입니다.

AOT 컴파일러는 태그드 템플릿 표현식을 지원하지 않으므로 메타데이터 표현식에서 사용하지 마세요.

심볼 참조가 필요합니다

도움말: 오류 메시지에 지정된 위치에서 컴파일러가 심볼 참조를 예상했습니다.

클래스의 extends 절에 표현식을 사용하면 이 오류가 발생할 수 있습니다.