바인딩(binding) 은 컴포넌트의 템플릿과 데이터를 연결하는 것을 의미합니다. 템플릿과 데이터를 바인딩하고 나면, 컴포넌트 데이터가 변경되더라도 템플릿에 언제나 최신 정보가 갱신됩니다.
문자열 바인딩
문자열을 바인딩하려면 이중 중괄호({{, }})를 사용하고 괄호 안에 표현식을 작성하면 됩니다.
@Component({
template: `
<p>Your color preference is {{ theme }}.</p>
`,
...
})
export class App {
theme = 'dark';
}
이렇게 구현하면 Angular는 {{ theme }}를 dark 로 변환해서 화면에 렌더링합니다.
<!-- 렌더링 결과 -->
<p>Your color preference is dark.</p>
시간이 경과하며 값이 변경된다면 시그널을 활용하면 됩니다. 시그널이 템플릿에 사용되면 Angular가 이 시그널을 추적하면서 값이 변경될 때마다 화면도 자동으로 갱신합니다.
@Component({
template: `
<!-- `welcomeMessage` 가 변경되더라도 갱신되지 않습니다. -->
<p>{{ welcomeMessage }}</p>
<p>Your color preference is {{ theme() }}.</p> <!-- `name` 시그널 값이 변경되면 언제나 갱신됩니다. -->
`
...
})
export class App {
welcomeMessage = "Welcome, enjoy this app that we built for you";
theme = signal('dark');
}
자세한 내용은 시그널 문서를 참고하세요.
예시를 좀 더 보면, 화면이 표시된 후에 사용자가 버튼을 클릭하면 theme 시그널이 'light' 값으로 변경됩니다:
<!-- 렌더링 결과 -->
<p>Your color preference is light.</p>
HTML에 문자열을 표시하고 싶은 곳이라면 어느 곳에든 문자열 바인딩을 활용할 수 있습니다.
문자열 바인딩 표현식의 값은 문자열 타입으로 변환됩니다.
null과 undefined는 빈 문자열로 렌더링됩니다.
표현식의 결과가 객체나 배열이라면 toString() 메서드 실행 결과가 사용됩니다.
동적 프로퍼티, 어트리뷰트 바인딩
대괄호([, ])를 사용하면 객체 프로퍼티와 HTML 어트리뷰트를 바인딩할 수 있습니다.
이때 프로퍼티는 HTML 엘리먼트의 DOM 인스턴스이거나, 컴포넌트 인스턴스이거나, 디렉티브 인스턴스일 수 있습니다.
기본 엘리먼트 프로퍼티
HTML 엘리먼트는 DOM 표현과 연결됩니다.
예를 들어 <button> HTML 엘리먼트는 DOM에서 HTMLButtonElement 인스턴스와 대응됩니다.
Angular 측면에서 보면, 프로퍼티와 바인딩된 값은 엘리먼트의 DOM 인스턴스와 직접 연결됩니다.
<!-- `disabled` 프로퍼티는 버튼 엘리먼트의 DOM 객체와 바인딩 됩니다. -->
<button [disabled]="isFormValid()">Save</button>
이렇게 구현하면 isFormValid 값이 변경될 때마다 Angular가 자동으로 HTMLButtonElement 인스턴스에 있는 disabled 프로퍼티 값을 변경합니다.
컴포넌트와 디렉티브 프로퍼티
바인딩하려는 프로퍼티가 Angular 컴포넌트라면, 대괄호를 사용하는 프로퍼티 바인딩은 컴포넌트의 입력 프로퍼티와 바인딩됩니다.
<!-- `MyListbox` 컴포넌트 인스턴스에 있는 `value` 프로퍼티와 바인딩 됩니다. -->
<my-listbox [value]="mySelection()" />
이렇게 구현하면 mySelection 값이 변경될 때마다 Angular가 자동으로 MyListbox 인스턴스의 value 프로퍼티 값을 변경합니다.
디렉티브도 같은 방식으로 바인딩됩니다.
<!-- `NgOptimizedImage` 디렉티브의 `ngSrc` 프로퍼티와 바인딩 됩니다. -->
<img [ngSrc]="profilePhotoUrl()" alt="The current user's profile photo" />
어트리뷰트
SVG 어트리뷰트와 같이 연관된 DOM 프로퍼티가 존재하지 않는 경우에는 템플릿 엘리먼트에 attr. 접두사를 붙여서 어트리뷰트와 바인딩할 수 있습니다.
<!-- Bind the `role` attribute on the `<ul>` element to the component's `listRole` property. -->
<ul [attr.role]="listRole()"></ul>
이렇게 구현하면 listRole 값이 변경될 때마다 Angular가 자동으로 <ul> 엘리먼트의 setAttribute를 실행해서 role 어트리뷰트 값을 변경합니다.
이때 어트리뷰트에 바인딩되는 값이 null이나 undefined이면, Angular가 removeAttribute를 실행해서 어트리뷰트를 제거합니다.
프로퍼티와 어트리뷰트를 바인딩할 때 문자열 바인딩
프로퍼티나 어트리뷰트 이름에 대괄호를 사용하는 대신 이중 중괄호를 사용하면 프로퍼티나 어트리뷰트에 문자열 바인딩을 연결할 수 있습니다.
<!-- 이미지 엘리먼트의 DOM 객체와 `alt` 프로퍼티를 바인딩합니다. -->
<img src="profile-photo.jpg" alt="Profile photo of {{ firstName() }}" />
CSS 클래스 바인딩, 스타일 프로퍼티 바인딩
Angular는 CSS 클래스 바인딩과 CSS 스타일 프로퍼티 바인딩도 지원합니다.
CSS 클래스
바인딩되는 값이 참인지, 거짓인지에 따라 CSS 클래스를 조건부로 적용할 수 있습니다.
<!-- When `isExpanded` is truthy, add the `expanded` CSS class. -->
<ul [class.expanded]="isExpanded()"></ul>
class 프로퍼티를 직접 바인딩하는 방법도 있습니다.
이 경우 세 가지 타입을 사용할 수 있습니다:
class 값 설명 |
TypeScript 타입 |
|---|---|
| 띄어쓰기로 구분하는 1개 이상의 CSS 클래스 문자열 | string |
| CSS 클래스 문자열의 배열 | string[] |
| 프로퍼티 이름을 CSS 클래스 이름으로 하고, 개별 값이 참으로 평가되는지 표현하는 객체 | Record<string, any> |
@Component({
template: `
<ul [class]="listClasses"> ... </ul>
<section [class]="sectionClasses()"> ... </section>
<button [class]="buttonClasses()"> ... </button>
`,
...
})
export class UserProfile {
listClasses = 'full-width outlined';
sectionClasses = signal(['expandable', 'elevated']);
buttonClasses = signal({
highlighted: true,
embiggened: false,
});
}
위 코드처럼 구현하면 DOM은 다음과 같이 렌더링됩니다:
<ul class="full-width outlined">
...
</ul>
<section class="expandable elevated">...</section>
<button class="highlighted">...</button>
Angular는 문자열 값을 공백으로 나누고 빈 문자열은 무시합니다.
정적 CSS 클래스를 사용하거나 class를 직접 바인딩하거나, 특정 클래스를 바인딩하는 경우가 겹치면 Angular는 지능적으로 결과를 조합합니다.
@Component({
template: `<ul class="list" [class]="listType()" [class.expanded]="isExpanded()"> ...`,
...
})
export class Listbox {
listType = signal('box');
isExpanded = signal(true);
}
위 코드처럼 구현하면 Angular는 ul 엘리먼트에 3개의 CSS 클래스를 모두 적용합니다.
<ul class="list box expanded"></ul>
엘리먼트에 CSS 클래스가 적용되는 순서는 보장하지 않습니다.
class를 바인딩하면서 배열이나 객체를 사용한 경우에는, Angular가 이전 값과 현재 값을 동등 연산자(===)로 비교합니다.
그래서 객체나 배열 내부의 값이 변경되었다면 새 객체와 새 배열을 사용해야 변경사항이 제대로 반영됩니다.
엘리먼트에 같은 CSS가 여러 번 바인딩되면, 우선순위에 따라 충돌을 해결합니다.
참고: 클래스 바인딩은 키 하나에 공백으로 구분되는 여러 개 클래스 이름을 지정할 수 없습니다. 그리고 바인딩 객체의 참조가 동일하게 유지되기 때문에 객체 변경도 지원하지 않습니다. 둘 중 하나가 필요한 경우에는 ngClass 디렉티브를 사용하세요.
CSS 스타일 프로퍼티
엘리먼트에 CSS 스타일 프로퍼티를 직접 바인딩할 수도 있습니다.
<!-- `isExpanded` 프로퍼티 값에 따라 CSS `display` 프로퍼티 값을 설정합니다. -->
<section [style.display]="isExpanded() ? 'block' : 'none'"></section>
이때 단위를 함께 지정할 수 있습니다.
<!-- `sectionHeightInPixels` 프로퍼티 값에 따라 CSS `height` 프로퍼티를 픽셀 단위로 설정합니다. -->
<section [style.height.px]="sectionHeightInPixels()"></section>
바인딩 구문 하나에 여러 스타일을 바인딩할 수도 있습니다. 이 경우 세 가지 타입을 사용할 수 있습니다:
style 값 설명 |
TypeScript 타입 |
|---|---|
"display: flex; margin: 8px"와 같은 CSS 선언 문자열 |
string |
| 프로퍼티 이름을 CSS 프로퍼티 이름으로 하고, 개별 값이 해당 CSS 프로퍼티 값인 객체 | Record<string, any> |
@Component({
template: `
<ul [style]="listStyles()"> ... </ul>
<section [style]="sectionStyles()"> ... </section>
`,
...
})
export class UserProfile {
listStyles = signal('display: flex; padding: 8px');
sectionStyles = signal({
border: '1px solid black',
'font-weight': 'bold',
});
}
위 코드처럼 구현하면 DOM은 다음과 같이 렌더링됩니다:
<ul style="display: flex; padding: 8px">
...
</ul>
<section style="border: 1px solid black; font-weight: bold">...</section>
style에 객체를 바인딩하면 Angular는 이전값과 현재값을 동등 연산자(===)로 비교합니다.
그래서 객체 내부의 값이 변경되었다면 새 객체를 사용해야 변경사항이 제대로 반영됩니다.
엘리먼트에 같은 스타일 프로퍼티가 여러 번 바인딩되면, 우선순위에 따라 충돌을 해결합니다.
ARIA 어트리뷰트
ARIA 어트리뷰트에 문자열을 바인딩할 수 있습니다.
<button type="button" [aria-label]="actionLabel()">
{{ actionLabel() }}
</button>
Angular는 바인딩된 값에 따라 해당 문자열을 엘리먼트의 aria-label 어트리뷰트에 지정하고, 바인딩된 값이 null이나 undefined이면 어트리뷰트를 제거합니다.
일부 ARIA 기능은 엘리먼트 참조와 같이 어떤 형식을 요구하는 경우가 있습니다. 이런 경우는 일반적인 프로퍼티 바인딩을 사용하세요. 자세한 내용은 접근성 가이드에서 예제와 함께 확인할 수 있습니다.