컴포넌트(component) 란
컴포넌트는 Angular의 핵심 구성요소로 Angular는 컴포넌트 중심으로 구성된다.
컴포넌트는 화면을 구성하는 뷰를 생성하고 관리한다.
웹 컴포넌트
컴포넌트는 독립적이고 완결된 뷰를 생성하기 위해 HTML,CSS,자바스크립트를 하나의 단위로 묶는 것이다.
웹 컴포넌트가 제공하여야 하는 기능은 다음과 같다. (웹 컴포넌트 4대 기술 스펙)
- 컴포넌트 뷰 생성(HTML Template)
- 외부의 간섭을 제어하기 위해 스코프(scope)를 분리하여 DOM을 캡슐화함(Shadow DOM)
- 외부로 부터 컴포넌트 호출(HTML Import)
- 컴포넌트를 명시적으로 호출하기 위한 명칭을 선언하여 네이티브 html 요소처럼 사용함(custom Element)
https://www.webcomponents.org/
위 사이트를 들어가면 브라우저별 컴포넌트 지원 현황을 알 수 있다.
컴포넌트 트리
하나의 컴포넌트로 화면을 구성하기 보다는 재사용이 용이한 구조로 분할하여 작성하며 분할된 컴포넌트를 조립하여 코드 중복 없는 ui를 생성해야한다.
Angular 애플리케이션은 분할된 컴포넌트로 구성되기 때문에 컴포넌트 간에는 컴포넌트 트리로 표현되는 부모-자식 관계가 형성된다.
컴포넌트 간의 부모-자식 관계는 데이터와 이벤트가 왕래하는 상태 정보 흐름의 통로가 되며
이를 통해 상태 공유가 이루어지기 때문에 컴포넌트 간의 부모-자식 관계는 Angular 애플리케이션에서 중요한 의미를 갖는다.
따라서 설계 시점부터 화면을 어떠한 컴포넌트 단위로 분할할 것인지에 대한 검토가 필요하다.
컴포넌트 기본 구조
ng new 명령어로 생성된 프로젝트는 루트 컴포넌트와 루트 모듈 1개씩을 갖는다.
1 | // 임포트 영역 |
컴포넌트는 임포트 영역, @Component 데코레이터 영역, 컴포넌트 클래스 영역으로 구분한다.
1 | .. |
템플릿은 컴포넌트의 뷰를 정의하기 위해 html과 Angular의 템플릿 문법으로 작성한다.
Angular chapter7. 컴포넌트은 템플릿 문법 중 하나인 **인터폴레이션** 으로 컴포넌트 클래스와 데이터를 템플릿에 바인딩 한다. 이를 **데이터 바인딩**이라고 한다.컴포넌트 클래스
1 | .. |
선언한 클래스를 모듈화하여 외부에 공개하기 위해 export 키워드를 사용함
@Component 데코레이터 영역
1 | .. |
Angular는 4가지 유형의 데코레이터를 제공한다.
- 클래스 데코레이터 : @Component, @NgModule, @Directive, @Injectable, @Pipe
- 프로퍼티 데코레이터 : @Input, @Output, @ViewChild, @ViewChildren, @ContentChild, @ContentChildren, @HostBinding
- 메소드 데코레이터 : @HostListener
- 파라미터 데코레이터 : @Inject
@Component 데코레이터는 Angular core 패키지에 정의되어있다. Angular 라이브러리 모듈의 경우 @가 붙고 경로를 따로 명시하지 않는다.
1 | import { Component } from '@Angular/core'; |
@Component 데코레이터에게 전달할 메타데이터 객체의 중요 프로퍼티는 다음과 같다.
- seletor 프로퍼티
selector는 컴포넌트의 뷰를 마크업으로 표현할때 사용하는 이름으로 html 요소 태그 이름처럼 사용한다.
기본 접두사는 app-이다.
- template/templateUrl 프로퍼티
컴포넌트는 화면을 구성하는 뷰를 생성하고 관리하므로 뷰(html)가 반드시 필요한데, 이 뷰를 template/templateUrl 프로퍼티에 선언한다.
1 | // templateUrl 외부 파일로 작성한 탬플릿 |
- styles/styleUrls 프로퍼티
탬플릿을 위한 스타일을 선언한다.
1 | // styleUrls 외부 파일로 정의된 스타일 , 여러 css 파일을 한번에 지정 가능 |
새로운 컴포넌트 생성해서 루트 컴포넌트에 등록하기.
- app-new-component 를 만든다.
1 | import { Component, OnInit } from '@Angular/core'; |
- app.compontnent.html에 selector를 선언한다.
1 | <app-new-component></app-new-component> |
- app.module.ts 에 컴포넌트를 등록한다.
1 | import { BrowserModule } from '@Angular/platform-browser'; |
component 명령어로 로 컴포넌트를 생성하면 자동으로 모듈에 등록된다.
@ngModule 데코레이터의 메타데이터 중요 프로퍼티
| 프로퍼티 | 내용 |
|---|---|
| providers | 주입 가능한 객체, 서비스의 리스트를 선언, 루트 모듈에 선언된 서비스는 전역에서 사용 가능 |
| declarations | 컴포넌트, 디렉티브, 파이프 선언 |
| imports | 의존 관계에 있는 Angular 라이브러리 모듈 , 라우팅 모듈 등을 선언 |
| bootstrap | 루트 컴포넌트를 선언 |
템플릿
템플릿은 html과 Angular 고유의 템플릿 문법을 사용하여 컴포넌트의 뷰를 정의한다.
템플릿은 뷰(view), 컴포넌트 클래스는 컨트롤러(controller)를 담당한다.
DOM의 상태가 변화하면 템플릿은 변화를 감지하고 변화된 상태를 컴포넌트 클래스로 전달한다. (데이터 바인딩)
템플릿 문법
Angular 가 제공하는 템플릿 문법은 아래와 같다.
- 데이터 바인딩
- 인터폴레이션
- 프로퍼티 바인딩
- 어트리뷰트 바인딩
- 클래스 바인딩
- 스타일 바인딩
- 이벤트 바인딩
- 양뱡향 데이터 바인딩
- 빌트인 디렉티브
- 빌트인 어트리뷰트 디렉티브 ngClass,ngStyle
- 빌트인 구조 디렉티브 ngIf, ngFor, ngSwitch
- 템플릿 참조 변수
- 템플릿 표현식 연산자
템플릿 문법에서는 script요소, 연산자, window, console, document 등이 금지된다.
또한 html, body 등의 요소의 경우 최상위 컴포넌트인 루트 컴포넌트가 html,body 의 자식요소이고, 모든 컴포넌트는 루트의 자식 컴포넌트이므로
컴포넌트 템플릿에서 ,
1 | <!doctype html> |
데이터 바인딩
데이터 바인딩은 뷰와 모델을 하나로 연결한다. 템플릿(view)와 컴포넌트 클래스의 데이터(Model)을 연결한다는 것이다.
- javascript
1 | function a(){ |
위 예제의 경우 만약 h1태그가 p 태그로 바뀌면 (DOM의 구조가 바뀌면) 로직도 변경되어야 한다.
- Angular
1
2
3
4
5
6
7//ts
export class AppComponent {
title='app';
}
// html
<h1>{{title}}</hl>
그러나 Angular는 DOM에 직접 접근하지 않고, 데이터 바인딩을 통해 템플릿과 컴포넌트 클래스를 연결한다.
따라서 h1태그가 p로 변경되어도 로직도 변경될 필요가 없다.
- Angular는 총 7개의 데이터 바인딩을 제공한다.
| 데이터 바인딩 | 문법 |
|---|---|
| 인터폴레이션 | |
| 프로퍼티 바인딩 | [property]=”expression” |
| 어트리뷰트 바인딩 | [attr.attribute-name]=”expression” |
| 클래스 바인딩 | [class.class-name]=”expression” |
| 스타일 바인딩 | [style.style-name]=”expression” |
| 이벤트 바인딩 | (event)=”statement” |
| 양방향 데이터 바인딩 | [(ngModel)]=”property” |
- 인터폴레이션
단방향 데이터 바인딩에 사용되는 템플릿 문법 표현식의 평가 결과를 문자열로 변환하여 템플릿에 바인딩함
1 | import { Component, OnInit } from '@angular/core'; |
1 | <p>{{name}}</p> |
- 프로퍼티(property) 바인딩 vs 어트리뷰트(attribute) 바인딩
프로퍼티는 DOM 노드 객체에 있는 것으로 동적으로 변한다.
어트리뷰트는 html 문서에 존재하는 것으로 어트리뷰트 값은 변하지 않는다.
1 | import { Component, OnInit } from '@angular/core'; |
1 | <!-- 프로퍼티 바인딩 --> |
- 클래스 바인딩
html 요소의 클래스를 추가 또는 삭제할 수 있다.
1) 단항 클래스 바인딩
html 요소의 class 어트리뷰트에 클래스를 추가 또는 삭제 가능
1 | <!-- 다른 클래스가 적용된 경우 true일 때 --> |
2) 다항 클래스 바인딩
클래스 바인딩 왼쪽에는 class를 , 오른쪽에는 class 이름의 리스트
단항 클래스 바인딩과 다르게 기존의 class 어트리뷰트를 삭제하고 바인딩 된 클래스 리스트를 새로 덮어쓰게 된다.
빌트인 디렉티브
빌트인 어트리뷰트 디렉티브
- ngStyle
여러 개의 인라인 스타일을 추가 또는 제거한다.
ngStyle 디렉티브는 바인딩된 객체를 html 요소의 style 어트리뷰트에 반영한다.
1 | <!-- 위 코드는 아래와 같다. --> |
빌트인 구조 디렉티브
Angular에서 제공하는 구조 디렉티브로 ngFor, ngIf, ngSwitch 등으로 뷰의 구조를 변경한다.
템플릿 참조 변수
템플릿 요소 내에서 해시(#)를 변수명 앞에 추가하여 참조 변수를 선언한다.
<element #myElement>
1 | import { Component } from '@angular/core'; |
세이프 내비게이션 연산자
세이프 내비게이션 연산자 ?는 컴포넌트 클래스의 프로퍼티 값이 null or undefined 일때 발생하는 에러를 회피하기 위해 사용
1 | import { Component } from '@angular/core'; |
컴포넌트 간의 상태 공유
분할된 컴포넌트를 조립하여서 구성된 애플리케이션은 컴포넌트 간의 부모-자식 관계 같은 계층적 트리 구조를 가진다.
이는 DOM트리와 유사한 형태를 갖게되는 컴포넌트 트리가 된다.
- Angular는 컴포넌트 간에 상태 정보를 공유할 수 있는 다양한 방법을 제공
- @Input, @Output 데코레이터
- @ViewChild, @viewChildren, @ContentChild, @ContentChildren 데코레이터
부모 컴포넌트에서 자식 컴포넌트로 데이터 전달
- @Input 데코레이터
form 요소를 갖고 있는 부모컴포넌트에서 상태가 변경되면 이를 자식 컴포넌트와 공유해야하는데,
부모 컴포넌트는 프로퍼티 바인딩을 통해 전달하고 자식컴포넌트는 전달받은 상태 정보를 @Input 데코레이터를 통해 전달 받을 수 있다.
- 부모 컴포넌트에서 userList라는 값을 전달하는 경우
1 | // app.component.ts는 부모 컴포넌트 입니다. |
- 부모 컴포넌트의 html에서 자식 selector를 선언하고 프로퍼티로 값을 전달한다.
1 | |
- 자식 컴포넌트에서 @Input 데코레이터를 통해 컴포넌트 프로퍼티 users에 바인딩한다.
1 | import {Component, Input, OnInit} from '@angular/core'; |
- 전달받은 값을 html 상에서 표현한다. 만약 부모 컴포넌트의 userList 값이 바뀌면 자동으로 자식도 바뀐 값으로 반영된다.
1 | <p>children works!</p> |
- @Input 데코레이터와 setter를 이용한 프로퍼티 조작
setter와 getter를 사용하여 부모 컴포넌트가 전달한 데이터를 추출 또는 변형하여 사용 할 수 있다.
1 | import {Component, Input, OnInit} from '@angular/core'; |
자식 컴포넌트에서 부모 컴포넌트로 상태 전달
- @Output 데코레이터와 EventEmitter
자식 컴포넌트는 @Output 데코레이터와 함께 선언된 프로퍼티를 EventEmitter 객체로 초기화한다.
부모 컴포넌트로 상태를 전달하기 위해 emit() 메소르를 사용하여 이벤트를 발생시키면서 상태를 전달한다.
부모 컴포넌트는 자식 컴포넌트가 전달한 상태를 이벤트 바인딩을 통해 접수한다.
- 자식 컴포넌트에서 부모로 값을 전달하기 위해 EventEmitter객체를 생성한다.
EventEmitter은 커스텀 이벤트를 발생시키는 emit 메소드를 가진다. emit에 전달할 값을 넣어 부모 컴포넌트로 상태를 전달한다.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
@Component({
selector: 'app-children',
templateUrl: './children.component.html',
styleUrls: ['./children.component.scss']
})
export class ChildrenComponent implements OnInit {
@Input() users;
// EventEmitter 객체 생성
@Output() deleteEvent = new EventEmitter();
constructor() {
}
ngOnInit(): void {
}
public deleteUser(user) {
// user라는 값을 전달한다.
this.deleteEvent.emit(user);
}
}
1 | <div *ngFor="let user of users"> |
2) 부모 컴포넌트의 html에 이벤트 바인딩을 통해 자식 컴포넌트가 발생시킨 이벤트를 접수한다.
1 | |
3) 이벤트 핸들러를 통해 전달된 user를 가지고 userList에서 삭제한다.
1 | import {Component} from '@angular/core'; |
부모 컴포넌트에서 자식 요소로의 접근
부모 컴포넌트에서 자식요소에 접근이 필요할 경우 템플릿 참조 변수(#)를 사용하여 자식 요소에 접근한다.
1 | import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; |
템플릿 참조변수를 사용하여 자식 컴포넌트에 접근함
1 | <app-child #counter></app-child> |
템플릿 참조 변수를 사용할 수 없는 상황에서는 아래와 같은 데코레이터를 사용한다.
- @ViewChild
- @ViewChildren
- @ContentChild
- @ContentChildren
@ViewChild 와 @ViewChildren
컴포넌트 템플릿에 배치된 자식 요소를 ViewChild 라고 한다.
@ViewChild 는 탐색 조건에 부합하는 1개의 요소를 취득할 때, @ViewChildren는 여러개를 한꺼번에 취득할 때 사용한다.
@ViewChild
app.component.ts 1
@ViewChild(ChildComponent) myChild: ChildComponent;@ViewChildren
@ViewChildren 데코레이터는 여러 개의 자식 요소를 한꺼번에 취득한다.
이 때 취득된 자식요소를 바인딩할 프로퍼티 타입은 QueryList이다.
1 | @ViewChildren(ChildComponent) myChildren : QueryList<ChildComponent>; |
@ContentChild와 @ContentChildren
부모 컴포넌트가 자신의 템플릿(html) 일부를 자식 컴 컴포넌트의 콘텐츠로 전달함
자식 컴포넌트는 부모 컴포넌트가 전달한 콘텐츠를 받아 표시할 위치를 ngContent 디렉티브를 사용하여 지정함.
1 | <children> |
자식 컴포넌트는 ngContent 디렉티브를 사용하여 부모 컴포넌트가 전달한 템플릿을 원하는 곳에 위치시킨다.
1 | <ng-content></ng-content> |
부모 컴포넌트
1
2
3
4
5
6
7
8
9
10
11
12
13
14import {Component} from '@angular/core';
@Component({
selector: 'app-root',
template: `
<!-- 자식 selector 안에 전달할 템플릿을 넣고 -->
<app-content>
<strong>여기가 부모에서 전달할 템플릿이다.</strong>
</app-content>
`,
styles: []
})
export class AppComponent {
}자식 컴포넌트
content.component.ts (자식) 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-content',
template: `
<h3>single</h3>
<div>
<!-- 이 곳에 부모에서 전달받은 템플릿이 표시된다. -->
<ng-content></ng-content>
</div>
`,
styles: [
]
})
export class ContentComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
- @ContentChild/@ContentChildren
@ContentChild : viewChild의 시작 태그와 종료 태그 사이에 있는 콘텐츠, 탐색 조건에 부합하는 한 개의 콘텐츠를 취득할 때
@ContentChildren : viewChilden의 시작 태그와 종료 태그 사이에 있는 콘텐츠, 탐색 조건에 부합하는 여러개의 콘텐츠를 취득할 때
컴포넌트와 스타일
스타일을 정의하는 방법은 @Component 데코레이터의 메타데이터 객체의 styles 프로퍼티에 정의하는 방법과,
styleUrls 프로퍼티에 외부 css 파일 경로를 정의하는 방법이 존재한다.
1 | import {Component} from '@angular/core'; |
AppComponent에서 정의한 스타일은 AppComponent에서만 유효하며 다른 컴포넌트에 영향을 미치지 않는다.
- angular에 글로벌 스타일 적용법
1 | .... |