Angular는 화면을 구성하는 최소 단위가 컴포넌트입니다. 클라이언트에게 보이는 최종 화면은 하나의 html로 보이지만, 내부적으로는 여러 개의 컴포넌트들로 이루어져 있을 수 있습니다. 따라서 컴포넌트 간에 데이터를 교환해야 하는 이슈가 발생할 수 있습니다. 

이번 장에서는 컴포넌트간에 데이터를 교환하는 방법에 대해 알아보겠습니다.

부모 -> 자식 컴포넌트로의 데이터 전달

parent, child 컴포넌트 2개를 생성합니다.

$ ng g c parent
$ ng g c child

parent component를 다음과 같이 작성합니다. <app-child>태그를 추가하면 parent에 child component가 추가됩니다. parent->child component로 데이터를 보내기 위해 태그 내에 [received]=’parentMessage’ 와 같은 표현이 추가되는데, 의미는 다음과 같습니다. 

[child에서 값을 받을 변수명] = ‘parent에서 값을 보낼 변수명’

// ts
parentMessage = '';
// html
<app-child [received]='parentMessage'></app-child>
<input type="text" [(ngModel)]="parentMessage">

예제에서는 parentMessage에 세팅된 값이 child component의 received변수로 전달되도록 하였습니다. 그리고 input에 값을 입력하면 parentMessage가 변경되도록 바인딩 처리하였습니다. 따라서 input값을 변경하면 parentMessage가 변경되고 해당 값이 child component의 received 변수에 전달됩니다. 

이제 child component를 보겠습니다. 내용은 다음과 같이 작성합니다.

// ts
@Input()
received: string;
// html
<p>parentMessage : {{received}}</p>

parent html의 <app-child> 태그에 정의한 received 변수가 선언되어 있는 것을 확인할 수 있습니다. @Input()을 붙여 외부 컴포넌트로부터 입력받는 데이터임을 표시합니다. 그리고 전달된 receive의 값을 html 파일에서 출력하도록 단방향 바인딩( {{received}} ) 처리합니다. 

parent의 input에 데이터를 입력하면 child로 데이터가 전달되고 뷰단이 변경되는 것을 확인할 수 있습니다.

자식 -> 부모 컴포넌트로의 데이터 전달

부모 컴포넌트에 include 된 app-child 태그에 다음 내용을 추가합니다. 
(childSender)=’onMessage($event)’ 의미는 다음과 같습니다. 
(Child의 EventEmitter)=’Child 이벤트를 처리할 parent 측 메서드’ 
해석하면 ‘Child 컴포넌트에 childSender라는 EventEmitter를 설정할 것인데, EventEmitter에서 메시지를 발송하면 Parent 컴포넌트의 onMessage() 메서드에서 처리하도록 하겠다.’입니다. 아래에서는 Child에서 데이터가 전달되면 화면에 단방향 바인딩( {{childMessage}} )으로 데이터를 표시하도록 처리하고 있습니다. 

// html
<app-child (childSender)='onMessage($event)'></app-child>
received child message : {{childMessage}} 
// ts
export class ParentComponent {
  childMessage = '';

  constructor() { }

  onMessage(event) {
    this.childMessage = event;
  }
}

자식 컴포넌트 처리

EventEmitter를 하나 생성하고 @Output을 선언하여 다른 컴포넌트로 방출되는 데이터임을 표시합니다.

// ts
export class ChildComponent implements OnInit {

  @Input()
  received: string;

  @Output()
  childSender = new EventEmitter();

  constructor() { }

  ngOnInit() {
    this.childSender.emit('welcome to mom');
  }
}

ChildComponent가 생성되면 ngOnInit()을 통해 childSender.emit(‘보낼 메시지’)로 메시지가 방출됩니다. 여기서는 빠른 확인을 위해 ngOnInit()에 선언하였지만, 실제 사용 시엔 메서드를 만들어 사용하면 됩니다. childSender.emit을 하면 parent에서는 onMessage로 받아 childMessage변수에 설정합니다. 화면엔 다음과 같이 출력됩니다. 
received child message : welcome to mom

여러 컴포넌트에 메시지 전달

부모, 자식 관계를 가지지 않는 컴포넌트 간에 데이터를 전달하거나, 변수의 변화를 여러 개의 컴포넌트에서 동시에 전달받아야 하는 경우가 있습니다. 이러한 경우에 데이터를 전달하는 방법에 대해 살펴보겠습니다. 

앵귤러에서는 Subject 객체를 생성함으로써 Observable 변수를 만들 수 있습니다. Observable 변수는 해당 변수의 변화를 구독(subscribe) 자에게 전달할 수 있는 기능을 가지고 있는 변수를 말합니다. 즉 Global 하게 접근할 수 있는 Observable 변수를 만들어놓고, 해당 변수의 변화되는 값을 사용하고자 하는 컴포넌트에서 Observable 변수를 구독하여 사용하면 됩니다. 코드로 살펴보겠습니다. 

rxjs 라이브러리 추가

해당 기능을 사용하기 위해서는 프로젝트에 rxjs 관련 라이브러리를 설치해야 합니다. 다음 명령어로 설치합니다.

$ npm install --save rxjs
$ npm install --save rxjs-compat

Observable 변수를 선언하기 위한 Service 생성

$ ng g s communication

import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})

export class CommunicationService {
  private messageSource = new Subject<string>();
  messageTransfer = this.messageSource.asObservable();

  constructor() { }

  sendMessage(message: string) {
    this.messageSource.next(message);
  }
}

messageSource란 이름으로 Subject변수를 선언합니다. messageSource를 통해 string형태의 데이터( new Subject<string>() )를 주고받을 수 있도록 생성합니다. 위에서 생성한 messageSource로 Observable 변수를 선언합니다. 해당 변수를 구독하면 변수의 변화를 전달받을 수 있습니다. 구독자에게 메시지의 전달을 위해 sendMessage 메서드를 생성합니다. messageSource.next를 이용하면 구독자에게 메시지를 발송할 수 있습니다. 

테스트를 위해 아래와 같이 메시지 발송, 수신 컴포넌트 총 3개를 생성합니다.

$ ng g c sender
CREATE src/app/sender/sender.component.html (21 bytes)
CREATE src/app/sender/sender.component.spec.ts (628 bytes)
CREATE src/app/sender/sender.component.ts (269 bytes)
CREATE src/app/sender/sender.component.css (0 bytes)
UPDATE src/app/app.module.ts (939 bytes)

$ ng g c receiver-one
CREATE src/app/receiver-one/receiver-one.component.html (27 bytes)
CREATE src/app/receiver-one/receiver-one.component.spec.ts (664 bytes)
CREATE src/app/receiver-one/receiver-one.component.ts (292 bytes)
CREATE src/app/receiver-one/receiver-one.component.css (0 bytes)
UPDATE src/app/app.module.ts (1043 bytes)

$ ng g c receiver-two
CREATE src/app/receiver-two/receiver-two.component.html (27 bytes)
CREATE src/app/receiver-two/receiver-two.component.spec.ts (664 bytes)
CREATE src/app/receiver-two/receiver-two.component.ts (292 bytes)
CREATE src/app/receiver-two/receiver-two.component.css (0 bytes)
UPDATE src/app/app.module.ts (1147 bytes)

sender 컴포넌트에 receiver-one, receiver-two 컴포넌트를 include 하고 Observable 변수로 메시지를 발송하는 메서드를 구현합니다.

// html
<input type="text" [(ngModel)]="sendMessage" (keyup)="onChange()">
<app-receiver-one></app-receiver-one><br>
<app-receiver-two></app-receiver-two><br>
// ts
export class SenderComponent {
  sendMessage: string;
  constructor(private communicationService: CommunicationService) {
  }
  onChange() {
    this.communicationService.sendMessage(this.sendMessage);
  }
}

receiver-one, receiver-two 컴포넌트에서는 Observable 변수를 구독하고, 변수에 변화가 있으면 화면에 출력하도록 처리합니다.

// receiver-one
// html
<p>receiver-one {{receiveMessage}}</p>
// ts
export class ReceiverOneComponent {
  receiveMessage: string;
  constructor(private communicationService: CommunicationService) {
    communicationService.messageTransfer.subscribe(message => {
      this.receiveMessage = message;
    });
  }
}
// receiver-two
// html
<p>receiver-two {{receiveMessage}}</p>
// ts
export class ReceiverTwoComponent {
  receiveMessage: string;
  constructor(private communicationService: CommunicationService) {
    communicationService.messageTransfer.subscribe(message => {
      this.receiveMessage = message;
    });
  }
}

테스트

서버를 구동하고 sender 컴포넌트에 접속하여 메시지를 입력하면 다음과 같이 receiver-one, receiver-two에 메시지가 전달되어 출력 되는것을 확인 할 수 있습니다.

연재글 이동[이전글] Angular2 정리(2) – Hello Angular 및 기본개념설명
[다음글] Angular2 정리(4) – Routing, layout component