게스트 작성자: 아유시 아로라
Angular 요소는 맞춤 요소로 패키징된 Angular 구성요소입니다. 현재 Chrome, Opera 및 Safari에서 지원되며 다른 브라우저에서는 폴리필을 통해 사용할 수 있습니다. Angular 요소에서는 공통 Angular 인터페이스 및 Change Detection Strategy와 함께 전체 Angular 인프라를 활용할 수 있습니다. 이러한 요소는 한 번 등록되면 브라우저 내에서 사용할 수 있습니다.
이 Codelab에서는 자체 image-slider Angular 구성요소를 만드는 과정을 안내한 다음, 이를 Angular 프레임워크 외부에서 작동할 수 있도록 Angular 요소로 변환하는 방법을 설명합니다.
빌드할 항목
  | 
이 Codelab에서는 Angular를 사용하여 image-slider 요소를 빌드해 보겠습니다. 이 요소의 특징은 다음과 같습니다. 
  | 
과정 내용
- image-slider 맞춤 구성요소를 만드는 방법
 - image-slider 맞춤 구성요소를 맞춤 요소로 변환하는 방법
 - 브라우저 내에서 작동하도록 구성요소를 패키징하는 방법
 
필요한 사항
- 최신 버전의 angular-cli
 - 샘플 코드
 - 텍스트 편집기
 - Angular 구성요소에 관한 기본 지식
 
이 Codelab에서는 Angular 요소를 중점적으로 설명합니다. 관련 없는 개념과 코드 블록은 자세히 언급되지 않으며 복사하여 붙여넣기만 하도록 제공됩니다.
코드 다운로드하기
다음 링크를 클릭하면 이 Codelab의 모든 코드를 다운로드할 수 있습니다.
다운로드한 ZIP 파일의 압축을 해제합니다. 그러면 루트 폴더 (angular-element-codelab-master)의 압축이 해제됩니다.
루트 폴더에는 2개의 폴더 (image-slider) 및 (image-slider-finished)가 들어 있습니다. 모든 코딩 작업은 image-slider라는 디렉터리에서 하게 됩니다.
프로젝트 실행
프로젝트를 실행하려면 루트 디렉터리(image-slider)에서 명령어(ng-serve)를 실행해야 합니다.
앱이 부트스트랩되면 다음이 표시됩니다.

이미지 슬라이더를 만드는 방법
이 이미지 슬라이더에서는 Angular 클릭 결합을 사용하여 버튼을 결합합니다. 이미지, 대체 태그, 링크 등을 포함하는 객체 배열을 만들겠습니다. 이러한 이미지를 컨테이너에서 아래로 하나씩 쌓고 클릭 시 컨테이너를 변환하게 됩니다.
image-slider 구성요소를 만든 다음 이 구성요소를 angular-element로 변환해 보겠습니다.
- 이미지 및 제목을 위한 컨테이너
 - 데이터를 포함하는 배열
 - 데이터를 결합하기 위한 템플릿
 
프로젝트를 시작하는 방법에는 여러 가지가 있습니다. 이 경우 프로젝트를 최대한 단순하게 유지하고 Angular 요소에 집중하기 위해 CSS와 함께 기본 코드를 제공했습니다.
배열 및 데이터 서비스 만들기
sliderArray에는 다음이 포함된다는 사실을 기억하세요.
- 슬라이더에서 이미지 URL의 img 키
 - 이미지 대체 항목을 제공하는 alt 태그
 - 이미지에 관한 설명을 제공하는 텍스트
 
이미 src/assets 디렉터리에 있는 data.json 파일은 다음과 같습니다.
sliderArray = [
 {img: 'http://bloquo.cc/img/works/1.jpg', alt: '', text: '365 Days Of weddings a year'},
 {img: 'http://bloquo.cc/img/works/2.jpg', alt: '',  text: '365 Days Of weddings a year'},
 {img: 'http://bloquo.cc/img/works/3.jpg', alt: '', text: '365 Days Of weddings a year'},
 {img: 'http://bloquo.cc/img/works/4.jpg', alt: '',  text: '365 Days Of weddings a year'},
 {img: 'http://bloquo.cc/img/works/5.jpg', alt: '', text: '365 Days Of weddings a year'}
];
서비스를 사용하여 구성요소에서 이 데이터를 가져와야 합니다. data.service.ts 파일에서 @angular/common/http의 httpClient 모듈을 사용하여 getData() 메서드를 작성합니다. 그러면 이 메서드가 위에서 만든 배열에서 데이터를 가져옵니다.
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'
const URL = '../assets/data.json';
@Injectable({
 providedIn: 'root'
})
export class DataService {
 constructor(private http: HttpClient) {}
 getData() {
   return this.http.get(URL)
 }
}
데이터 서비스에서 데이터 가져오기
구성요소 내에서 서비스를 가져와야 합니다. 그러면 observable을 구독하여 data.json에서 객체를 가져올 수 있습니다.
이를 위해 다음 3단계를 진행해야 합니다.
- 구성요소 배열 초기화
 getData()함수에서 반환된 Observable 구독- observable을 구독한 후 데이터 유형 확인을 위한 인터페이스 Result 생성
 - 데이터를 구성요소 배열에 할당
 
구성요소 배열 초기화하기
객체의 배열인 slider.component.ts 내에서 구성요소 배열을 선언하고 초기화하겠습니다.
선언하려면 다음과 같이 합니다.
sliderArray: object[];
초기화하려면 다음과 같이 합니다.
constructor(private data: DataService) {
 this.sliderArray = [];
}
다음으로 생성자 내에서 서비스를 가져와 초기화해야 합니다.
constructor(private data: DataService) {}
이제 서비스를 사용하고 서비스 메서드를 호출할 준비가 되었습니다.
데이터 서비스에서 데이터 가져오기
서비스에서 데이터를 가져오기 위해 getData() 메서드를 호출하고 그 메서드에서 반환하는 observable을 구독하겠습니다. 또한 인터페이스 Result,를 생성하여 올바른 코드 유형을 가져왔는지 확인할 수 있도록 합니다.
이 작업은 ngOnInit 메서드 내에서 진행합니다.
this.data.getData().subscribe((result: Result)=>{
})
구성요소 배열에 데이터 할당하기
마지막으로 구성요소 배열에 데이터를 할당하겠습니다.
this.data.getData().subscribe((result: Result)=>{
  this.sliderArray = result.sliderArray;
})
구성요소 배열 내에서 데이터를 가져오면 이 데이터에 템플릿을 결합할 수 있습니다.
slider.component.html,에는 이미 HTML 템플릿이 있습니다. 다음 단계는 이 템플릿을 sliderArray에 결합하는 것입니다.
*ngFor 지시어를 사용하여 데이터를 템플릿에 결합하고, 마지막으로 템플릿에 변환을 추가하여 슬라이더가 작동하도록 합니다.
여기는 다음 3단계로 이루어집니다.
- 템플릿에 
sliderArray결합 - 슬라이더 버튼에 이벤트 결합 추가
 ngStyle및ngClass을 사용하여 CSS 변환 추가
구성요소에 slideArray 결합
img-container, a text-container 및 a slider.를 포함하는 컨테이너가 있습니다.
*ngFor 지시어를 사용하여 세 컨테이너 모두의 데이터를 결합합니다.
<div class="container">
 <div class="img-container" *ngFor="let i of sliderArray; let select = index;">
   <img src="{{i.img}}" alt="{{i.alt}}" >
 </div>
 <div>
   <div class="text-container">
     <div class="page-text" *ngFor="let i of sliderArray;let select = index;">
       <h3>{{i.text}}</h3>
     </div>
   </div>
 </div>
</div>
<div class="slider">
 <div class="slide-button-parent-container" *ngFor="let i of sliderArray; let x =index">
    <div class="select-box">
     <div class="slide-button">
     </div>
    </div>
 </div>
</div>
slideArray에 이벤트 결합하기
데이터가 결합되면 Angular click binding을 사용하여 클릭 이벤트를 모든 slide-button에 결합합니다. selected(x)라는 함수를 만듭니다. 여기서 x는 배열의 색인입니다.
selected(x) {
 this.downSelected(x);
 this.selectedIndex = x;
}
downSelected(i) {
  this.transform =  100 - (i) * 50;
  this.selectedIndex = this.selectedIndex + 1;
  if(this.selectedIndex > 4) {
    this.selectedIndex = 0;
  }
}
여기서 유념할 사항은 다음과 같습니다.
- 세부 선택된 함수는 
selected함수 클릭 시 색인에서 전달된 변환 속성의 값을 1/50로 줄입니다. - 이 로직은 텍스트 컨테이너를 100%, 50%, -50%, -100%로 변환하고 그 결과 네 가지 서로 다른 상태가 생성됩니다.
 
ngStyle 및 ngClass를 사용하여 CSS 변환 추가하기
처음에는 모든 이미지를 불투명도 0으로 설정하고 선택한 색인이 이미지 색인과 같아지면 ngClass directive를 사용하여 selected 클래스를 추가합니다. 이 selected 클래스는 이미지가 사용자에게 보이도록 이미지에 불투명도 1을 추가합니다.
<div class="img-container"  *ngFor="let i of sliderArray; let select = index;"
      [ngClass]="{'selected': select == selectedIndex}">
</div>
그런 다음 select() 함수를 사용하여 계산된 transform 값에 따라 text-container를 변환합니다.
<div [ngStyle]="{'transform': 'translateY('+ transform + '%' +')', 'transition': '.8s'}">
</div>
이 모든 단계를 진행하면 아래와 같은 최종 코드가 표시됩니다.
<div class="container">
 <div class="img-container"  *ngFor="let i of sliderArray; let select = index;"
      [ngClass]="{'selected': select == selectedIndex}">
   <img src="{{i.img}}" alt="{{i.alt}}" >
 </div>
 <!--</div>-->
 <div [ngStyle]="{'transform': 'translateY('+ transform + '%' +')', 'transition': '.8s'}">
   <div class="text-container">
     <div class="page-text" *ngFor="let i of sliderArray;let select = index;" [ngClass]="{'selected': select == selectedIndex}">
       <h3>{{i.text}}</h3>
     </div>
   </div>
 </div>
</div>
<div class="slider">
 <div class="slide-button-parent-container"  *ngFor="let i of sliderArray; let x =index" (click)="selected(x)" >
    <div class="select-box">
     <div   class="slide-button" [ngClass]="{'slide-button-select': x == selectedIndex}" >
     </div>
    </div>
 </div>
</div>
이 절차는 다음 5단계로 구성됩니다.
- Angular 요소에 
Shadow DOM사용 entryComponents활용@angular/elements에서CreateCustomElement모듈 가져오기 및 사용custom-element정의ngDoBootstrap메서드 실행
Angular 요소에 Shadow DOM 사용하기
이제 image-slider가 실행되고 있으므로 이를 Angular Element로 만들기만 하면 됩니다.
흥미로운 사실은 구성요소 DOM 즉, Shadow DOM을 만드는 데 약간의 변경만 하면 된다는 점입니다.
ViewEncapsulation 모듈을 가져와서 ShadowDom 메서드를 사용해야 합니다.
@Component({
 selector: 'app-slider',
 templateUrl: './slider.component.html',
 styleUrls: ['./slider.component.css'],
 encapsulation: ViewEncapsulation.ShadowDom
})
entryComponent 활용
entry 구성요소는 Angular에서 필수적으로 로드되는 구성요소입니다. entry 구성요소는 NgModule에서 부트스트랩하여 지정합니다.
여기서는 @NgModule 내의 entryComponents 배열에 SliderComponent를 지정합니다.
@NgModule({
 declarations: [
   SliderComponent
 ],
 imports: [
   BrowserModule,
   HttpClientModule
 ],
})
createCustomElement 모듈 가져오기 및 사용
여기에서는 @angular/elements.의 createCustomElement 모듈을 사용해야 합니다. SliderComponent,를 createCustomElement 함수의 매개변수로 사용해야 합니다. 그런 다음 DOM에 slider를 등록해야 합니다.
import { createCustomElement } from '@angular/elements';
export class AppModule {
 constructor(private injector: Injector) {
   const slider = createCustomElement(SliderComponent, { injector });
    }
}
슬라이더를 DOM 요소로 등록하려면 customElements.define 메서드를 사용하여 슬라이더를 정의합니다.
customElements.define('motley-slider', slider);
마지막으로 ngDoBootstrap() 메서드를 사용하여 이 custom-element를 부트스트랩해야 합니다. 전체 코드는 다음과 같습니다.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { SliderComponent } from './slider/slider.component';
import { HttpClientModule} from "@angular/common/http";
@NgModule({
 declarations: [
   SliderComponent
 ],
 imports: [
   BrowserModule,
   HttpClientModule
 ],
})
export class AppModule {
 constructor(private injector: Injector) {
   const slider = createCustomElement(SliderComponent, { injector });
   customElements.define('motley-slider', slider);
 }
 ngDoBootstrap() {}
}
Angular 요소 패키징
새 명령어로 package.json을 수정해야 합니다. package.json 파일 내의 스크립트 객체를 수정합니다.
수정된 스크립트 객체를 확인해 보겠습니다.
"scripts": {
 "ng": "ng",
 "start": "ng serve",
 "build": "ng build --prod --output-hashing=none",
 "package": "cat dist/my-app/{runtime,polyfills,scripts,main}.js | gzip > elements.js.gz",
 "serve": "http-server",
 "test": "ng test",
 "lint": "ng lint",
 "e2e": "ng e2e"
}
이제 ng build & ng package 명령어를 실행할 수 있습니다. 마지막으로 ng serve를 실행하여 build 명령을 사용하여 생성된 dist/ 폴더를 제공합니다. 또한 ng package 명령어에서 얻은 gzip을 사용하고 압축을 푼 다음 npm module로 게시할 수 있습니다.
