모든 특전, 번거로움 없음: Angular 9 튜토리얼

게시 됨: 2022-03-11

"매년 인터넷이 중단됩니다."라는 속담이 있으며 일반적으로 개발자는 가서 수리해야 합니다. 오랫동안 기다려온 Angular 버전 9에서는 이것이 적용될 것이라고 생각할 수 있으며 이전 버전에서 개발된 앱은 주요 마이그레이션 프로세스를 거쳐야 합니다.

하지만 그렇지 않습니다! Angular 팀은 컴파일러를 완전히 재설계하여 더 빠른 빌드, 더 빠른 테스트 실행, 더 작은 번들 크기, 그리고 가장 중요한 것은 이전 버전과의 이전 버전과의 호환성을 제공합니다. Angular 9를 사용하면 개발자는 기본적으로 번거로움 없이 모든 혜택을 누릴 수 있습니다.

이 Angular 9 자습서에서는 Angular 애플리케이션을 처음부터 빌드합니다. 우리는 최신 Angular 9 기능 중 일부를 사용하고 그 과정에서 다른 개선 사항을 살펴볼 것입니다.

Angular 9 튜토리얼: 새로운 Angular 애플리케이션으로 시작하기

Angular 프로젝트 예제를 시작하겠습니다. 먼저 Angular의 CLI 최신 버전을 설치해 보겠습니다.

 npm install -g @angular/cli

ng version 을 실행하여 Angular CLI 버전을 확인할 수 있습니다.

다음으로 Angular 애플리케이션을 만들어 보겠습니다.

 ng new ng9-app --create-application=false --strict

ng new 명령에서 두 개의 인수를 사용하고 있습니다.

  • --create-application=false 는 CLI에 작업 공간 파일만 생성하도록 지시합니다. 이것은 하나 이상의 앱과 여러 라이브러리가 필요할 때 코드를 더 잘 구성하는 데 도움이 됩니다.
  • --strict 는 TypeScript 타이핑과 코드 정리를 강화하기 위해 더 엄격한 규칙을 추가합니다.

그 결과 기본 작업 공간 폴더와 파일이 생겼습니다.

node_modules, .editorconfig, .gitignore, angular.json, package-lock.json, package.json, README.md, tsconfig.json 및 tslint.json이 포함된 ng9-app 폴더를 보여주는 IDE의 스크린샷.

이제 새 앱을 추가해 보겠습니다. 이를 위해 다음을 실행합니다.

 ng generate application tv-show-rating

메시지가 표시됩니다.

 ? Would you like to share anonymous usage data about this project with the Angular Team at Google under Google's Privacy Policy at https://policies.google.com/privacy? For more details and how to change this setting, see http://angular.io/analytics. No ? Would you like to add Angular routing? Yes ? Which stylesheet format would you like to use? SCSS

이제 ng serve 를 실행하면 앱이 초기 스캐폴딩으로 실행되는 것을 볼 수 있습니다.

"tv-show-rating app이 실행 중입니다!"라는 알림과 함께 Angular 9의 스캐폴딩 스크린샷 리소스 및 다음 단계에 대한 링크도 있습니다.

ng build --prod 를 실행하면 생성된 파일 목록을 볼 수 있습니다.

Angular 9의 "ng build --prod" 출력 스크린샷. "차등 로드를 위한 ES5 번들 생성 중..."으로 시작합니다. 이 작업이 완료되면 런타임, 폴리필 및 기본과 같은 여러 JavaScript 파일 청크(각각 -es2015 및 -es5 버전이 있음)와 하나의 CSS 파일이 나열됩니다. 마지막 줄은 타임스탬프, 해시 및 23,881밀리초의 런타임을 제공합니다.

각 파일에는 두 가지 버전이 있습니다. 하나는 레거시 브라우저와 호환되고 다른 하나는 최신 API를 사용하고 브라우저에서 실행하는 데 더 적은 폴리필이 필요한 ES2015를 대상으로 컴파일됩니다.

Angular 9의 한 가지 큰 개선 사항은 번들 크기입니다. Angular 팀에 따르면 대형 앱의 경우 최대 40% 감소를 볼 수 있습니다.

새로 생성된 앱의 경우 번들 크기는 Angular 8과 거의 유사하지만 앱이 성장함에 따라 이전 버전에 비해 번들 크기가 작아지는 것을 볼 수 있습니다.

Angular 9에 도입된 또 다른 기능은 구성 요소 스타일 CSS 파일이 정의된 임계값보다 큰 경우 경고하는 기능입니다.

배열에 두 개의 객체가 있는 Angular 9 JSON 구성 파일의 "예산" 섹션 스크린샷. 첫 번째 개체의 "유형"은 "초기"로, "maximumWarning"은 "2mb"로, "maximumError"는 "5mb"로 설정되어 있습니다. 두 번째 개체에는 "유형"이 "anyComponentStyle"로, "maximumWarning"이 "6kb"로, "maximumError"가 "10kb"로 설정되어 있습니다.

이것은 나쁜 스타일 가져오기 또는 거대한 구성 요소 스타일 파일을 잡는 데 도움이 됩니다.

TV 프로그램 평가 양식 추가

다음으로 TV 프로그램을 평가하는 양식을 추가합니다. 이를 위해 먼저 bootstrapng-bootstrap 을 설치합니다.

 npm install bootstrap @ng-bootstrap/ng-bootstrap

Angular 9의 또 다른 개선 사항은 i18n(국제화)입니다. 이전에는 개발자가 앱의 모든 로캘에 대해 전체 빌드를 실행해야 했습니다. 대신 Angular 9를 사용하면 앱을 한 번 빌드하고 빌드 후 프로세스에서 모든 i18n 파일을 생성하여 빌드 시간을 크게 줄일 수 있습니다. ng-bootstrap 에는 i18n에 대한 종속성이 있으므로 프로젝트에 새 패키지를 추가합니다.

 ng add @angular/localize

다음으로 앱의 styles.scss 에 Bootstrap 테마를 추가합니다.

 @import "~bootstrap/scss/bootstrap";

그리고 app.module.tsAppModuleNgbModuleReactiveFormsModule 을 포함합니다.

 // ... import { ReactiveFormsModule } from '@angular/forms'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; @NgModule({ imports: [ // ... ReactiveFormsModule, NgbModule ], })

다음으로, 우리는 우리 양식의 기본 그리드로 app.component.html 을 업데이트할 것입니다:

 <div class="container"> <div class="row"> <div class="col-6"> </div> </div> </div>

그리고 양식 구성 요소를 생성합니다.

 ng gc TvRatingForm

tv-rating-form.component.html 을 업데이트하고 TV 프로그램을 평가하는 양식을 추가해 보겠습니다.

 <form [formGroup]="form" (ngSubmit)="submit()" class="mt-3"> <div class="form-group"> <label>TV SHOW</label> <select class="custom-select" formControlName="tvShow"> <option *ngFor="let tvShow of tvShows" [value]="tvShow.name">{{tvShow.name}}</option> </select> </div> <div class="form-group"> <ngb-rating [max]="5" formControlName="rating"></ngb-rating> </div> <button [disabled]="form.invalid || form.disabled" class="btn btn-primary">OK</button> </form>

그리고 tv-rating-form.component.ts 는 다음과 같이 보일 것입니다:

 // ... export class TvRatingFormComponent implements OnInit { tvShows = [ { name: 'Better call Saul!' }, { name: 'Breaking Bad' }, { name: 'Lost' }, { name: 'Mad men' } ]; form = new FormGroup({ tvShow: new FormControl('', Validators.required), rating: new FormControl('', Validators.required), }); submit() { alert(JSON.stringify(this.form.value)); this.form.reset(); } }

마지막으로 app.component.html 에 양식을 추가해 보겠습니다.

 <!-- ... --> <div class="col-6"> <app-tv-rating-form></app-tv-rating-form> </div>

이 시점에서 몇 가지 기본 UI 기능이 있습니다. 이제 ng serve 를 다시 실행하면 실제로 작동하는 것을 볼 수 있습니다.

"TV SHOW"라는 제목의 양식을 보여주는 Angular 9 튜토리얼 앱의 스크린캡처에는 몇 가지 쇼 제목, 별점 측정기 및 확인 버튼이 나열된 드롭다운이 있습니다. 애니메이션에서 사용자는 쇼를 선택하고 등급을 선택한 다음 확인 버튼을 클릭합니다.

계속 진행하기 전에 디버깅을 돕기 위해 추가된 몇 가지 흥미로운 새로운 Angular 9 기능을 간단히 살펴보겠습니다. 이것은 일상 업무에서 매우 일반적인 작업이므로 우리 삶을 조금 더 쉽게 만들기 위해 무엇이 변경되었는지 아는 것은 가치가 있습니다.

Angular 9 Ivy로 디버깅

Angular 9 및 Angular Ivy에 도입된 또 다른 큰 개선 사항은 디버깅 경험입니다. 컴파일러는 이제 더 많은 오류를 감지하고 더 "가독성 있는" 방식으로 오류를 던질 수 있습니다.

행동으로 봅시다. 먼저 tsconfig.json 에서 템플릿 검사를 활성화합니다.

 { // ... "angularCompilerOptions": { "fullTemplateTypeCheck": true, "strictInjectionParameters": true, "strictTemplates": true } }

이제 tvShows 배열을 업데이트하고 nametitle 로 바꾸면:

 tvShows = [ { title: 'Better call Saul!' }, { title: 'Breaking Bad' }, { title: 'Lost' }, { title: 'Mad men' } ];

... 컴파일러에서 오류가 발생합니다.

파일 이름과 위치가 포함된 Angular 9/Angular Ivy 컴파일러 출력의 스크린샷으로 "오류 TS2339: '이름' 속성이 '{ 제목: 문자열; }' 유형에 존재하지 않습니다."라고 표시됩니다. 또한 문제의 코드 행을 표시하고 참조에 밑줄을 긋습니다(이 경우 tvShow.name이 언급된 tv-rating-form.component.html 파일). 그런 다음 이 HTML 파일에 대한 참조가 해당 TypeScript 파일로 추적되고 유사하게 강조 표시됩니다.

이 유형 검사를 통해 오타와 TypeScript 유형의 잘못된 사용을 방지할 수 있습니다.

@Input() 에 대한 Angular Ivy 검증

우리가 얻는 또 다른 좋은 유효성 검사는 @Input() 입니다. 예를 들어 다음을 tv-rating-form.component.ts 에 추가할 수 있습니다.

 @Input() title: string;

... 그리고 app.component.html 에 바인딩합니다.

 <app-tv-rating-form [title]="title"></app-tv-rating-form>

... 그런 다음 app.component.ts 를 다음과 같이 변경합니다.

 // ... export class AppComponent { title = null; }

이 세 가지를 변경하면 컴파일러에서 다른 유형의 오류가 발생합니다.

"오류 TS 2322: 'null' 유형은 '문자열' 유형에 할당할 수 없습니다."라는 메시지와 함께 app.component.html을 강조 표시한 이전 형식과 유사한 형식의 Angular 9/Angular Ivy 컴파일러 출력 스크린샷.

이를 우회하려는 경우 템플릿에서 $any() 사용하여 값을 any 로 캐스팅하고 오류를 수정할 수 있습니다.

 <app-tv-rating-form [title]="$any(title)"></app-tv-rating-form>

그러나 이것을 수정하는 올바른 방법은 형식의 title 을 nullable로 만드는 것입니다.

 @Input() title: string | null ;

Angular 9 Ivy의 ExpressionChangedAfterItHasBeenCheckedError

Angular 개발에서 가장 두려운 오류 중 하나는 ExpressionChangedAfterItHasBeenCheckedError 입니다. 고맙게도 Ivy는 오류를 더 명확하게 출력하므로 문제가 어디에서 오는지 쉽게 찾을 수 있습니다.

그럼 ExpressionChangedAfterItHasBeenCheckedError 오류를 소개하겠습니다. 이를 위해 먼저 서비스를 생성합니다.

 ng gs Title

다음으로 BehaviorSubjectObservable 에 액세스하고 새 값을 내보내는 메서드를 추가합니다.

 export class TitleService { private bs = new BehaviorSubject < string > (''); constructor() {} get title$() { return this.bs.asObservable(); } update(title: string) { this.bs.next(title); } }

그 후, 우리는 이것을 app.component.html 에 추가할 것입니다:

 <!-- ... --> <div class="col-6"> <h2> {{title$ | async}} </h2> <app-tv-rating-form [title]="title"></app-tv-rating-form> </div>

그리고 TitleService 에서 app.component.ts 를 주입합니다.

 export class AppComponent implements OnInit { // ... title$: Observable < string > ; constructor( private titleSvc: TitleService ) {} ngOnInit() { this.title$ = this.titleSvc.title$; } // ... }

마지막으로 tv-rating-form.component.ts 에서 AppComponent 를 주입하고 TitleService 의 제목을 업데이트하면 ExpressionChangedAfterItHasBeenCheckedError 오류가 발생합니다.

 // ... constructor( private titleSvc: TitleService ) { } ngOnInit() { this.titleSvc.update('new title!'); }

이제 브라우저의 개발 콘솔에서 자세한 오류를 볼 수 있으며 app.component.html 을 클릭하면 오류가 있는 위치를 알려줍니다.

ExpressionChangedAfterItHasBeenCheckedError 오류에 대한 Angular Ivy의 보고를 보여주는 브라우저 개발 콘솔의 스크린캡. 빨간색 텍스트의 스택 추적은 이전 및 현재 값 및 힌트와 함께 오류를 제공합니다. 스택 트레이스의 중간에 core.js를 참조하지 않는 유일한 라인이 있습니다. 사용자가 그것을 클릭하면 오류를 일으키는 app.component.html 행으로 이동합니다.

서비스 호출을 setTimeout 으로 래핑하여 이 오류를 수정할 수 있습니다.

 setTimeout(() => { this.titleSvc.update('new title!'); });

ExpressionChangedAfterItHasBeenCheckedError 오류가 발생하는 이유를 이해하고 다른 가능성을 알아보려면 해당 주제에 대한 Maxim Koretskyi의 게시물을 읽을 가치가 있습니다.

Angular Ivy를 사용하면 오류를 보다 명확하게 표시할 수 있으며 코드에서 TypeScript 입력을 강제 적용할 수 있습니다. 다음 섹션에서는 Ivy와 디버깅을 활용하는 몇 가지 일반적인 시나리오를 다룰 것입니다.

컴포넌트 하네스로 Angular 9 앱 테스트 작성하기

Angular 9에서는 구성 요소 하네스 라는 새로운 테스트 API가 도입되었습니다. 그 배후의 아이디어는 DOM과 상호 작용하는 데 필요한 모든 잡다한 일을 제거하여 작업하기 훨씬 쉽고 안정적으로 실행하는 것입니다.

구성 요소 하네스 API는 @angular/cdk 라이브러리에 포함되어 있으므로 먼저 프로젝트에 설치해 보겠습니다.

 npm install @angular/cdk

이제 테스트를 작성하고 구성 요소 하네스를 활용할 수 있습니다. tv-rating-form.component.spec.ts 에서 테스트를 설정해 보겠습니다.

 import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { ReactiveFormsModule } from '@angular/forms'; describe('TvRatingFormComponent', () => { let component: TvRatingFormComponent; let fixture: ComponentFixture < TvRatingFormComponent > ; beforeEach(async (() => { TestBed.configureTestingModule({ imports: [ NgbModule, ReactiveFormsModule ], declarations: [TvRatingFormComponent] }).compileComponents(); })); // ... });

다음으로 구성 요소에 대한 ComponentHarness 를 구현해 보겠습니다. 우리는 두 개의 하네스를 만들 것입니다. 하나는 TvRatingForm 용이고 다른 하나는 NgbRating 용입니다. ComponentHarness 에는 구성 요소 선택기의 값을 취해야 하는 static 필드인 hostSelector 가 필요합니다.

 // ... import { ComponentHarness, HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; class TvRatingFormHarness extends ComponentHarness { static hostSelector = 'app-tv-rating-form'; } class NgbRatingHarness extends ComponentHarness { static hostSelector = 'ngb-rating'; } // ...

TvRatingFormHarness 의 경우 제출 버튼에 대한 선택기와 click 을 트리거하는 함수를 만듭니다. 이것을 구현하는 것이 얼마나 쉬운지 알 수 있습니다.

 class TvRatingFormHarness extends ComponentHarness { // ... protected getButton = this.locatorFor('button'); async submit() { const button = await this.getButton(); await button.click(); } }

다음으로 등급을 설정하는 메서드를 추가합니다. 여기서 locatorForAll 을 사용하여 사용자가 클릭할 수 있는 별을 나타내는 모든 <span> 요소를 찾습니다. rate 함수는 가능한 모든 등급의 별표를 가져오고 전송된 값에 해당하는 별표를 클릭합니다.

 class NgbRatingHarness extends ComponentHarness { // ... protected getRatings = this.locatorForAll('span:not(.sr-only)'); async rate(value: number) { const ratings = await this.getRatings(); return ratings[value - 1].click(); } }

누락된 마지막 부분은 TvRatingFormHarnessNgbRatingHarness 에 연결하는 것입니다. 그렇게 하려면 TvRatingFormHarness 클래스에 로케이터를 추가하기만 하면 됩니다.

 class TvRatingFormHarness extends ComponentHarness { // ... getRating = this.locatorFor(NgbRatingHarness); // ... }

이제 테스트를 작성해 보겠습니다.

 describe('TvRatingFormComponent', () => { // ... it('should pop an alert on submit', async () => { spyOn(window, 'alert'); const select = fixture.debugElement.query(By.css('select')).nativeElement; select.value = 'Lost'; select.dispatchEvent(new Event('change')); fixture.detectChanges(); const harness = await TestbedHarnessEnvironment.harnessForFixture(fixture, TvRatingFormHarness); const rating = await harness.getRating(); await rating.rate(1); await harness.submit(); expect(window.alert).toHaveBeenCalledWith('{"tvShow":"Lost","rating":1}'); }); });

양식 내 select 항목의 경우 하네스를 통해 값 설정을 구현하지 않았습니다. API가 여전히 옵션 선택을 지원하지 않기 때문입니다. 그러나 이것은 구성 요소 하네스 이전에 요소와 상호 작용하는 모습을 여기서 비교할 수 있는 기회를 제공합니다.

테스트를 실행하기 전에 마지막으로 한 가지. titlenull 로 업데이트했기 때문에 app.component.spec.ts 를 수정해야 합니다.

 describe('AppComponent', () => { // ... it(`should have as title 'tv-show-rating'`, () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app.title).toEqual(null); }); });

이제 ng test 를 실행하면 테스트가 통과합니다.

Angular 9 앱에서 테스트를 실행하는 Karma의 스크린샷. "Incomplete: fit() or fdescribe() was found, 2 specs, 0 failures, randomized with seed 69573" 메시지와 함께 "Ran 2 of 6 specs"가 표시됩니다. TvRatingFormComponent의 두 가지 테스트가 강조 표시됩니다. AppComponent의 3가지 테스트와 TitleService의 1가지 테스트는 모두 회색입니다.

Angular 9 예제 앱으로 돌아가기: 데이터베이스에 데이터 저장

Firestore에 대한 연결을 추가하고 데이터베이스에 등급을 저장하여 Angular 9 자습서를 마무리하겠습니다.

그러려면 Firebase 프로젝트를 만들어야 합니다. 그런 다음 필요한 종속성을 설치합니다.

 npm install @angular/fire firebase

Firebase 콘솔의 프로젝트 설정에서 구성을 가져와서 environment.tsenvironment.prod.ts 에 추가합니다.

 export const environment = { // ... firebase: { apiKey: '{your-api-key}', authDomain: '{your-project-id}.firebaseapp.com', databaseURL: 'https://{your-project-id}.firebaseio.com', projectId: '{your-project-id}', storageBucket: '{your-project-id}.appspot.com', messagingSenderId: '{your-messaging-id}', appId: '{your-app-id}' } };

그런 다음 app.module.ts 에서 필요한 모듈을 가져옵니다.

 import { AngularFireModule } from '@angular/fire'; import { AngularFirestoreModule } from '@angular/fire/firestore'; import { environment } from '../environments/environment'; @NgModule({ // ... imports: [ // ... AngularFireModule.initializeApp(environment.firebase), AngularFirestoreModule, ], // ... })

다음으로 tv-rating-form.component.ts 에서 AngularFirestore 서비스를 삽입하고 양식 제출 시 새 등급을 저장합니다.

 import { AngularFirestore } from '@angular/fire/firestore'; export class TvRatingFormComponent implements OnInit { constructor( // ... private af: AngularFirestore, ) { } async submit(event: any) { this.form.disable(); await this.af.collection('ratings').add(this.form.value); this.form.enable(); this.form.reset(); } } 

더 큰 페이지 제목 "new title!" 아래에 "TV SHOW"라는 제목의 양식을 표시하는 Angular 9 튜토리얼 앱의 스크린캡 다시 말하지만, 여기에는 소수의 쇼 제목, 별 측정기 및 확인 버튼이 나열된 드롭다운이 있으며 다시 사용자가 쇼를 선택하고 등급을 선택한 다음 확인 버튼을 클릭합니다.

이제 Firebase 콘솔로 이동하면 새로 생성된 항목이 표시됩니다.

Firebase 콘솔의 스크린샷입니다. 왼쪽 열에는 참석자, 인종, 등급, 테스트 및 사용자와 같은 일부 컬렉션이 있는 joaq-lab이 있습니다. 평가 항목이 선택되고 ID가 선택된 가운데 열에 표시됩니다. 이는 유일한 문서입니다. 오른쪽 열에는 "rating"이 4로 설정되고 "tvShow"가 "Mad men"으로 설정된 두 개의 필드가 표시됩니다.

마지막으로 모든 등급을 AppComponent 에 나열해 보겠습니다. 이를 위해 app.component.ts 에서 컬렉션에서 데이터를 가져옵니다.

 import { AngularFirestore } from '@angular/fire/firestore'; export class AppComponent implements OnInit { // ... ratings$: Observable<any>; constructor( // ... private af: AngularFirestore ) { } ngOnInit() { // ... this.ratings$ = this.af.collection('ratings').valueChanges(); } }

… 그리고 app.component.html 에서 평가 목록을 추가합니다.

 <div class="container"> <div class="row"> // ... <div class="col-6"> <div> <p *ngFor="let rating of ratings$ | async"> {{rating.tvShow}} ({{rating.rating}}) </p> </div> </div> </div> </div>

이것이 Angular 9 튜토리얼 앱이 모두 합쳐졌을 때의 모습입니다.

더 큰 페이지 제목 "new title!" 아래에 "TV SHOW"라는 제목의 양식을 표시하는 Angular 9 튜토리얼 앱의 스크린캡 다시 말하지만, 소수의 쇼 제목, 별 측정기 및 확인 버튼을 나열하는 드롭다운이 있습니다. 이번에는 오른쪽 열에 이미 "Mad men(4)"이 나열되어 있으며 사용자는 Lost(별 3개)로 평가하고 "Mad men"(별 4개)을 다시 평가합니다. 오른쪽 열은 두 가지 새로운 평가 후에 알파벳순으로 정렬된 상태로 유지됩니다.

Angular 9 및 Angular Ivy: 더 나은 개발, 더 나은 앱, 더 나은 호환성

이 Angular 9 자습서에서는 기본 양식을 작성하고 Firebase에 데이터를 저장하고 여기에서 항목을 검색하는 방법을 다뤘습니다.

그 과정에서 Angular 9 및 Angular Ivy에 어떤 개선 사항과 새로운 기능이 포함되었는지 확인했습니다. 전체 목록은 공식 Angular 블로그의 최신 릴리스 게시물에서 확인할 수 있습니다.


Google Cloud 파트너 배지.

Google Cloud 파트너로서 Toptal의 Google 인증 전문가는 회사의 가장 중요한 프로젝트에 대한 수요가 있을 때 사용할 수 있습니다.