티스토리 뷰

분류없음

Angular v5.0.0

한장현 2017.11.02 12:50

안녕하세요. 한장현입니다.


드디어 Angular 5.0.0 버전이 나왔습니다!!

Angular 2가 나온지 13개월만에, 3 버전을 뛰어넘고 4 버전이 나온지 약 7개월만에 메이저 버전이 올라갔네요.


이 글에서는 5.0.0 버전에서 달라진 점을 간단하게 살펴봅니다.

글을 번역하면서 봤는데 AOT 빌드를 기본으로 적용한다던가, 라우터 이벤트가 늘었다는 내용같이 눈에 띄는 기능들이 많네요.


원문은 Angular 블로그의 게시글 Version 5.0.0 of Angular Now Available에서 확인할 수 있습니다.




Angular 5.0.0 버전 릴리즈


드디어 Angular 5.0.0버전, pentagonal-donut이 나왔다. 이 버전은 Angular의 새로운 메이저 버전이며, 몇 가지 기능을 추가하고, 알려진 버그도 해결되었다. Angular팀은 좀 더 작고, 좀 더 빠르고, 좀 더 사용하기 쉬운 프레임워크를 만드는 노력을 계속할 것이다.



이 글에서는 5.0.0 버전에서 바뀐 내용 중 중요한 내용을 다룬다. 변경된 내용 전체를 확인하려면 체인지로그를 확인하자.


빌드 최적화 툴(Build Optimizer)

빌드 최적화 툴은 CLI에 포함되어 있으며, 프로덕션 빌드에 CLI를 사용한다면 최적화 툴이 기본으로 적용된다.

빌드 최적화 툴은 크게 두 가지 작업을 수행한다. 

먼저, 애플리케이션 코드에 pure 마크를 지정할 수 있다. Webpack과 같이 트리 셰이킹을 수행하는 툴은 애플리케이션에서 사용하지 않는 코드를 제거하는데, 이 마크를 사용하면 트리 셰이킹 성능을 더 끌어올릴 수 있다.

그리고 런타임에서 동작하는 Angular 애플리케이션 코드에서 Angular 데코레이터를 제거한다. 데코레이터는 컴파일러를 위한 함수이며, 런타임에는 사용되지 않기 때문에 없어도 된다.

빌드 최적화 툴이 수행하는 작업 덕에 JavaScript로 번들링한 애플리케이션 크기는 더욱 작아지며, 사용자가 경험하는 애플리케이션 실행 속도는 더욱 빨라질 수 있다.


Angular Universal State Transfer API와 Dom 지원

Angular Universal을 사용하면서 서버 버전과 클라이언트 버전 두 개를 유지하고 있다면, 이들을 관리하기 좀 더 편해졌다.

Angular Universal은 Angular 애플리케이션을 서버에서 렌더링할 때 사용하는 프로젝트다. 애플리케이션을 서버에서 렌더링하면 전체를 클라이언트에서 렌더링하는 것보다는 좀 더 정적인 HTML이 생성되며, 이 파일을 이용해서 검색엔진을 최적화 할 수 있고 페이지 안에 있는 JavaScript를 실행하지 않는 검색 엔진에도 대응할 수 있다. 클라이언트에서 체감할 수 있는 애플리케이션 성능도 물론 빨라진다.

5.0.0버전에는 ServerTransferStateModule과 BrowserTransferStateModule이 추가되었다. 이 모듈을 사용하면 platform-server를 사용해서 애플리케이션을 렌더링할 때 사용한 정보를 클라이언트로 전달할 수 있으며, 클라이언트에서 애플리케이션을 실행했을 때 이 정보를 다시 만들어 내지 않아도 된다. 이 기능은 애플리케이션이 HTTP 통신으로 데이터를 가져오려고 할 때 유용한데, 서버의 상태를 클라이언트가 미리 알고 있다면 서버 상태를 확인하는 HTTP 콜을 생략하고 원하는 작업을 바로 할 수 있기 때문이다. State Transfer API에 대한 문서는 몇 주 후에 공개될 것 같다.

그리고 platform-server에 Domino가 추가되었다. Domino를 사용하면 서버 사이드 렌더링에서도 DOM을 조작할 수 있으며, 서드 파티 JS 라이브러리나 컴포넌트 라이브를 사용하는 것도 더 편해졌다.


컴파일러 개선

Angular 컴파일러는 이제 증분 컴파일(incremental compilation)을 지원한다. 컴파일 할 때마다 모든 코드를 컴파일하는 것이 아니라, 변경된 부분만 추적해서 컴파일하기 때문에 재빌드 속도가 빨라졌다. 이 차이는 프로덕션 빌드나 AOT 빌드에서 좀 더 확실하다. 이밖에도 공백문자를 제거해서 번들링 결과물의 크기를 줄일 수 있는 데코레이터들도 추가되었다.


TypeScript 변환

이제 Angular 컴파일러는 TypeScript 컴파일러처럼 사용한다. Angular의 TypeScript 컴파일은 증분 컴파일 기능 덕분에 재빌드 속도가 더욱 빨라졌고, TypeScript 컴파일러 2.3버전에 추가된 컴파일 파이프라인 기능을 적극 활용한다.

덕분에 다음과 같이 ng serve 명령을 실행하면서 AOT 빌드를 수행할 수 있다.

ng serve --aot


이 기능은 모두 한 번 써보기를 권한다. 이 동작은 앞으로 배포될 CLI에서는 기본 기능이 될 것이다. 다만, 컴포넌트가 천개를 넘어가는 프로젝트에서는 아직 성능 이슈가 있긴 하다. 이 이슈는 곧 해결될 것이며, 결국 모든 크기의 프로젝트에서 이 기능을 사용하는 것이 도움이 될 것이다.

https://angular.io 애플리케이션에 증분 AOT 빌드를 적용했을 때는 빌드 시간이 95% 단축되었다!! 원래 빌드 시간은 40초 정도 걸렸지만, 증분 AOT 빌드를 적용했을 때는 2초 정도 걸렸다.

우리가 생각하는 AOT 빌드의 최종 목표는 개발자가 AOT 빌드로 개발을 하면서 가끔 프로덕션 빌드를 할 때 차이를 느끼지 못하게 하는 것이다. angular.io 페이지를 AOT 증분 빌드하는 데에 2초밖에 걸리지 않았기 때문에, AOT 빌드는 곧 CLI 기본 기능이 될 것이다.

이런 변화와 함께, AOT 빌드에 사용하는 genDir 설정은 더이상 필요없다. 임시로 생성되는 파일은 모두 node_modules 안에서 처리할 것이다.


공백문자 보호 옵션

지금까지 템플릿에 있는 탭, 개행문자, 공백문자는 빌드 결과물에 그대로 포함되는 것이 일반적이었다. 이제는 이런 공백문자들을 그대로 포함할지, 포함시키지 않을지 선택할 수 있다.

이 기능은 true가 기본값이며, 컴포넌트 데코레이터에 따로 설정할 수도 있다.

@Component({
	templateUrl: 'about.component.html',
	preserveWhitespaces: false
}
export class AboutComponent {}

그리고 이 옵션을 tsconfig.json에 지정하면 애플리케이션 전체에 적용할 수 있다. 기본값은 true다.

{
	"extends": "../tsconfig.json",
	"compilerOptions": {
		"outDir": "../out-tsc/app",
		"baseUrl": "./",
		"module": "es2015",
		"types": []
	},
	"angularCompilerOptions": {
		"preserveWhitespaces": false
	},
	"exclude": [
		"test.ts",
		"**/*.spec.ts"
	]
}


일반적으로 컴포넌트 레벨에서 옵션값을 설정하면 애플리케이션 설정은 무시한다. 따라서 공백문자를 처리하지 않아야 하는 컴포넌트에서는 애플리케이션 설정에 true가 되어있다고 해도 컴포넌트 레벨에서 false를 지정할 수 있다.

<pre> 태그에 대해 걱정할 필요는 없다. 이 태그는 예외적으로 잘 처리하고 있다.

preserveWhiteSpaces 옵션에 대해서는 이 문서를 참고하자.


데코레이터 지원 강화

이제 데코레이터에서도 람다표현식( () => { } )을 지원한다. useValue, useFactory, data 를 사용할 때도 물론 람다표현식을 사용할 수 있다. 컴포넌트에서 사용하는 값이 런타임에 계산되는 경우라면 람다표현식을 사용하는 것이 더 적절할 것이다.

그래서 이제는 함수의 이름을 지정하거나 d.ts 파일에 선언된 API를 사용하지 않고도 더욱 간단한 코드를 작성할 수 있다.

@Component({
	provider: [{provide: SOME_TOKEN, useFactory: () => null}]
})
export class MyClass {}

useValue를 쓸 때도 마찬가지다

@Component({
	provider: [{provide: SOME_TOKEN, useValue: SomeEnum.OK}]
})
export class MyClass {}


i18n 숫자, 날짜, 통화 파이프

숫자, 날짜, 통화 파이프가 i18n에 더 적합하게 개선되었으며, 더이상 i18n을 지원하기 위해 폴리필을 사용할 필요가 없다.

원래 Angular는 숫자, 날짜, 통화 형식을 자체적으로 제공하지 않고 브라우저의 i18n API를 사용하도록 했다. 따라서 개발자가 원하는 i18n을 구현하려면 폴리필을 사용해야만 했고, 사용자가 보는 애플리케이션은 브라우저에 따라 다른 동작을 하기도 했다. 우리 팀은 이 이슈에 대해 수많은 댓글을 받았으며(특히 통화 파이프에 대해), 파이프를 한 번 정리하기로 했다.

5.0.0에서는 파이프들을 자체적으로 모두 구현했으며, 특수문자가 필요한 경우는 CLDR을 활용하기도 했다. Angular 5 버전의 파이프가 4 버전과 달라진 점은 이 문서에서 확인하자.

새로운 파이프를 아직 사용하지 않으려면 DeprecatedI18NPipesModule을 사용하면 된다.

i18n 파이프 체인지로그 보기


ReflectiveInjector를 StaticInjector로 대체

폴리필들을 좀 더 제거하기 위해 ReflectiveInjector는 StaticInjector로 대체했다. StaticInjector를 사용하면 Relect 폴리필을 사용할 필요가 없으며, 폴리필이 빠지기 때문에 애플리케이션 크기도 좀 더 줄어든다.

변경 전

ReflectiveInjector.resolveAndCreate(providers);

변경 후

Injector.create(providers);


Zone 활용 성능 개선

Zone 라이브러리를 생략할 수 있는 기능이 추가되었다. 이제 애플리케이션 성능을 더 끌어올리기 위해 Zone 라이브러리를 일부 생략할 수 있다.

Zone 라이브러리를 생략하려면 애플리케이션을 부트스트랩할 때 ngZone에 'noop' 옵션을 주면 된다.

latformBrowserDynamic().bootstrapModule(AppModule, {ngZone: 'noop'}).then( ref => {} );

예제를 확인하려면 ng-component-state 프로젝트를 확인하자.


exportAs 개선

컴포넌트나 디렉티브를 여러 이름으로 외부에 공개할 수 있는 기능이 추가되었다. 이 기능은 이전 이름을 유지하면서 새 이름을 지정하려고 할 때 유용하다. 새로 작성하는 코드는 새 이름을 사용하더라도, 이전 코드는 변경하지 않아도 된다. 이 기능은 사실 Angular Material 프로젝트에서 접두사를 변경하는 과정에 사용했었다. 컴포넌트를 유지하고 관리해야 하는 개발자라면 이 기능이 특히 반가울 것이다.


예제

@Component({
	moduleId: module.id,
	selector: 'a[mat-button], a[mat-raised-button], a[mat-icon-button], a[mat-fab], a[mat-mini-fab]',
	exportAs: 'matButton, matAnchor',
	.
	.
	.
}


HttpClient

HttpClient는 4.3 버전부터 @angular/common 패키지에 도입되었으며, 이전보다 간단하고 쉬우면서도 효율적인 방법으로 웹 요청을 처리할 수 있다. HttpClient는 도입된 이후로 개발자들의 큰 환호를 받았으며, 이제는 우리도 @angular/http 라이브러리에 있는 HttpModule을 사용하지 말고 HttpClient 모듈을 사용할 것을 권장한다.

이미 개발하고 있는 애플리케이션에 HttpClient 모듈을 도입하려면 HttpModule을 HttpClientModule로 바꾸고 HttpClient 서비스를 주입한 뒤에, map(res => res.json()) 코드를 제거하면 된다. 이 코드는 HttpClient에서는 더이상 필요 없다.


CLI v1.5

Angular CLI v1.5부터는 CLI로 생성하는 Angular 프로젝트에 v5.0.0을 기본으로 사용한다.

CLI v1.5부터는 위에서 설명한 빌드 최적화 툴이 기본으로 적용되며, 설정을 특별히 바꾸지 않아도 번들링 결과물이 더 작아지는 것을 확인할 수 있다.

그리고 .tsconfig 파일을 사용하는 방식을 개선했기 때문에 이제는 TypeScript 표준을 좀 더 엄격하게 적용한다. 이전에는 tsconfig.json 파일의 filesinclude에 파일 목록을 나열하고, 이 파일을 Angular가 추적해서 지연 로딩되는 라우터를 사용할 수 있었지만, 이제는 TypeScript 표준에 맞지 않기 때문에 이 방식은 사용할 수 없다. 이제 CLI가 지원하는 TypeScript 설정에는 filesinclude 항목이 포함되지 않는다. 이 내용이 그리 많은 개발자에게 영향을 주지 않기를 바란다.


Angular 폼 변경 감지 옵션

이제는 폼에 입력이 들어올때마다 유효성을 검사하지 않고 'blur' 시점이나 'submit' 시점을 활용할 수 있다.

폼은 애플리케이션에서 아주 중요한 부분이다. 서버에서 유효성 검사를 하지 않아서 클라이언트의 유효성 검사를 아주 엄격하게 한다면, 유효성 검사를 원할 때마다 수행하기 부담스러울 수도 있다. 이제는 값이 변경되는 시점을 선택해서 유효성 검사를 수행할 수 있다. 이 동작은 폼 컨트롤 레벨에서 할 수도 있고 전체 폼에 적용할 수도 있다.

그리고 이제 'asyncValidators'를 폼 컨트롤의 옵션으로 직접 적용할 수 있다. 이전에는 폼 컨트롤 생성자의 세번째 옵션으로만 지정할 수 있었다.


템플릿 기반 폼

변경 후

<input name="firstName" ngModel>

변경 후

<input name="firstName" ngModel [ngModelOptions]="{updateOn: 'blur'}">

또는

<form [ngFormOptions]="{updateOn: 'submit'}">


반응형 폼

변경 전

new FormGroup(value);
new FormControl(value, [], [myValidator])

변경 후

new FormGroup(value, {updateOn: 'blur'}));
new FormControl(value, {updateOn: 'blur', asyncValidators: [myValidator]})


RxJS 5.5

Angular에서 사용하는 RxJS가 5.5버전으로 업데이트 되었다. 이제 RxJS v5.5에서 도입한 lettable 연산자를 사용할 수 있으며, 개발자가 할 수 있는 일은 더 많아졌다. 이제 연산자를 불러올 때 사용하는 문법이 좀 더 깔끔해졌고, 코드 분할이나 트리 셰이킹에서 발생하던 사이드 이펙트도 해결되었다.

변경 전

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/filter';

names = allUserData
	.map(user => user.name)
	.filter(name => name);

변경 후

import { Observable } from 'rxjs/Observable';
import { map, filter } from 'rxjs/operators';

names = allUserData.pipe(
	map(user => user.name),
	filter(name => name),
);

게다가 RxJS는 이제 ECMAScript 모듈로 배포되며, Angular CLI v1.5는 이 버전을 기본으로 탑재할 것이다. Angular CLI를 사용하지 않는다면 물론 패키지를 따로 설치해야 한다. RxJS의 빌드와 트리 셰이킹에 대한 내용은 lettable 연산자 문서에서 찾을 수 있다.


새로운 라우터 라이프싸이클 이벤트

라우터에 새로운 라이프싸이클 이벤트가 추가되었다. 이제는 라우터 가드가 실행되는 시점부터 라우터가 활성화되는 시점을 감지할 수 있다. 자식 라우터가 변경될 때 라우터 영역에 로딩 UI를 표시해야 하거나, 라우터 가드의 성능을 확인하려고 할 때 활용하면 좋다.

새로 추가된 이벤트는 실행되는 순서대로 GuardsCheckStart, ChildActivationStart, ActivationStart, GuardsCheckEnd, ResolveStart, ResolveEnd, ActivationEnd, ChildActivationEnd다. 이 이벤트를 활용하면 로딩 UI를 표시하고 감추는 동작을 다음과 같이 구현할 수 있다.

class MyComponent {
	constructor(public router: Router, spinner: Spinner) {
		router.events.subscribe(e => {
			if (e instanceof ChildActivationStart) {
				spinner.start(e.route);
			} else if (e instanceof ChildActivationEnd) {
				spinner.end(e.route);
			}
		});
	}
}


새 버전은 어떻게 적용해야 하나?

우리는 Firebase 애플리케이션으로 Angular 업데이트 가이드를 제공하고 있다. 이 가이드를 활용하면 지금 어떤 버전을 사용하고 있는지에 관계없이, 원하는 버전을 적용할 때 코드의 어느 부분을 수정해야 하는지 적절한 가이드를 확인할 수 있다. 현재 사용하는 버전과 바꾸려고 하는 버전을 선택하고 가이드의 내용을 따라하면 된다.

Angular v5.0.0에서는 OpaqueToken과 같이 이제는 사용하지 않는 API를 많이 제거했으며, 앞으로 사용하지 않을 API를 새롭게 지정하기도 했다. Angular 애플리케이션을 만드는 데에 이 글이 도움이 되기를 바란다.


알려진 이슈

프로덕션 빌드에서 소스맵을 생성할 때 일부 소스맵이 정상적으로 생성되지 않는 이슈가 있긴 하다.

https://github.com/angular/angular/issues/19840

댓글
  • 프로필사진 최민영 항상 좋은글 감사합니다. 2017.11.08 13:48 신고
  • 프로필사진 BlogIcon 한장현 고맙습니다^^ 2017.11.27 06:42 신고
  • 프로필사진 초보개발자 앵귤러는 타입스크립트 사용을 권장하는건가요? 강제 하는건가요?
    ES6 문법으로 작성해도 되나요?
    2017.11.26 17:01 신고
  • 프로필사진 BlogIcon 한장현 Angular 초기에는 TypeScript 사용을 권장했고, Dart나 JS로도 Angular 애플리케이션을 작성할 수 있었습니다.
    가이드 문서도 언어별로 제공했었는데... 지금 찾아보니 Dart나 JS 문서는 더이상 지원하지 않는 것 같네요.

    TypeScript 대신 ES6를 쓴다면... Angular에서는 큰 의미가 없습니다 ㅎ 이미 Angular가 TypeScript로 작성되었기 때문에 더더욱요 ㅎㅎ
    예를 들면 @Component나 @NgModule과 같은 데코레이터가 TypeScript로 제공되는 함수입니다.

    TypeScript는 ES6의 문법을 모두 포함하고 있으니 천천히 TypeScript에 적응해보시는 것이 좋을 것 같습니다 ㅎ
    2017.11.27 06:42 신고
  • 프로필사진 초보개발자 아항. 넵 감사합니다!
    2017.11.28 13:46 신고
  • 프로필사진 리두 좋은글 잘봤습니다. ^^
    한가지 질문 드리겠습니다. Typescript에서 daum이나 naver 지도 api를 사용할때도 javascript와 동일한가요?
    typescript에서는 module을 import해서 사용해야할 것 같아서 여쭤봅니다.

    var options = { //지도를 생성할 때 필요한 기본 옵션
    center: new daum.maps.LatLng(33.450701, 126.570667), //지도의 중심좌표.
    level: 3 //지도의 레벨(확대, 축소 정도)
    };
    이렇게 작성할 경우 daum 객체에 대해서 cannot find 에러가 발생하네요.
    2017.12.05 17:33 신고
  • 프로필사진 BlogIcon 한장현 기본적으로 TypeScript는 JavaScript의 문법을 포함하기 때문에 일반적인 상황에서는 문제가 없습니다만, TypeScript에 해당하는 설정은 해줘야 합니다.

    말씀하신 것처럼 모듈을 찾을 수 없다는 에러가 난다면 import 부분을 보셔야 할 것 같네요.
    js로 만든 모듈을 TypeScript에서 참조할 때는 import * as 모듈명 from '패키지' 로 사용하기도 합니다.
    2017.12.06 13:33 신고
댓글쓰기 폼
공지사항
Total
230,633
Today
40
Yesterday
129
링크
«   2018/01   »
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
글 보관함