첫 화면 만들어보기
2.3. 첫 화면 만들어보기
이제 피자가게를 오픈합니다. 오픈하려니 준비할 것이 많군요. 우선은 사람들에게 알리려면 가게이름이 있어야 할 것 같습니다. 화면에 우리 가게 이름을 띄워봅시다.
Angular2로 Hello World 해보는 것은 요즘의 트렌디한 다른 언어나 웹플랫폼들에 비해서 살짝 단계가 있어 보입니다. 워낙 심플함이 유행인 요즘에 비하면 걱정처럼 비칠 수도 있지만, 사실 이런 점은 Angular2가 많은 표현들을 일반적인 패턴으로 함축하려는 고민의 결과입니다. Hello World 만 따라해 보더라도 벌써 Angular2를 어떻게 사용하는 것인지 어느새 알게 되는 것이죠. 그럼 일단 따라서 진행해 봅시다.
Angular2는 모듈을 통해 편리한 코드 관리를 지원합니다. Angular2의 핵심 기능도 모듈로써 제공하므로 이를 우리 어플리케이션에서 사용하게 될 것입니다. 따라서, 우리도 먼저 피자 가게 어플리케이션의 모듈을 하나 만들어 사용합시다. 모듈은 코드 관리를 위한 여러 기능들을 수행하는 단위로 이는 6장 Module 에서 자세히 다룰 예정입니다.
지금은 가장 기초적인 모듈구조를 살펴보겠습니다. src/app/app.module.ts
파일 내용을 보면 NgModue 을 '@angluer/core' 에서, BrowserModule 를 '@angular/platform-browser' 에서 각각 가져와 사용하고 있습니다. 벌써 눈치빠른 분들은 이 두 가지가 모듈이라는 이름으로 쓰이고 있다는 점을 보면서 미소를 띄우고 있겠죠. 아직 자세히 모듈이 무엇이다 라는 것을 느끼기 어렵더라도 Angular2 를 쭉 사용해 보는 것만으로 모듈에 점차 익숙해질 것입니다.
<예제> src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
/* ... */
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
/* ... */
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
NgModule 은 Angular 모듈을 정의할 때 사용하는 데코레이터 입니다. 지금은 비록 하나의 모듈을 정의하여 사용하지만 어플리케이션이 발전하고 복잡해질 수록 모듈은 점차 많아질 것입니다.
먼저, 위 예제의 마지막 줄에서 모듈은 class 로 선언되었습니다. @NgModule()
데코레이터는 이 class 를 Angular 모듈로 만들어 줍니다. imports 규칙에 BrowserModule 을 포함합니다. 이것은 지금 정의하는 모듈이 쓸 수있는 다른 모듈들을 나열하는 것입니다. 이 곳에 모듈을 포함하는 것 만으로 그 모듈을 재사용할 수 있게 됩니다. 예를 들어, BrowserModule 은 웹 브라우져에서 Angular2 어플리케이션을 수행하기 위해 필요한 상호작용에 관한 기법들을 제공해 줍니다. 우리는 이 부분을 다시 구현할 필요 없이 모듈을 재사용하여 구현하고 싶은 어플리케이션만 생각하면 됩니다. 참고로 앞으로는 BrowserModule 보다 CommonModule 을 더 많이 접하게 될 것입니다. BrowserModule 은 특별히 웹 브라우져에서 어플리케이션을 시작하려는 모듈에서 사용됩니다.
이제 좀 더 진행해보겠습니다.
<예제> src/app/app.module.ts
/* ... */
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [ /* ... */ ],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule { }
이번에는 Component 가 등장하였습니다. 컴포넌트는 Angular2의 기본 단위이고 모듈은 이런 컴포넌트의 집합을 포함합니다. 이제부터 우리는 필요한 컴포넌트를 만들고 적절한 모듈에 포함시켜 다른 컴포넌트와 함께 사용할 것입니다. 이 작업을 계속하다가 보면 어플리케이션이 예쁜 모양으로 만들어지게 되는 것이죠. 바로 다음에 첫 컴포넌트를 만들게 될테니 지금은 모듈의 내용을 좀 더 보겠습니다.
NgModule 에 declarations 와 bootstrap 항목이 추가되었습니다. 먼저 declarations 은 이 모듈이 포함하는 컴포넌트 등을 선언하는 곳 입니다. 곧 짚어볼 AppComponent 가 포함되어 있군요. AppModule 을 사용하는 다른 모듈이 AppComponent 를 사용할 수 있다는 뜻입니다.
bootstrap 은 우리가 이전에 짚어본 bootstrapModule()
의 의미를 이제 설명할 수 있습니다.
<예제> src/main.ts
platformBrowserDynamic().bootstrapModule(AppModule);
어플리케이션을 시작하려면 C/C++의 main() 함수나 Java의 main() 메서드와 같은 진입점이 필요합니다. Angular2 에서는 bootstrapModule( module )
으로 진입할 모듈을 선택하고 그 모듈의 bootstrap: [ component ]
에 표현된 컴포넌트가 어플리케이션의 진입점이 됩니다. Angular2 가 얼마나 많은 부분을 모듈과 컴포넌트로 표현하고 싶어하는지 느껴지지요.
모듈의 전체 소스는 다음과 같습니다.
<예제> src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
자, 이제 피자가게 어플리케이션의 진입점이 될 컴포넌트를 살펴봅시다.
<예제> src/app/app.component.ts
export class AppComponent {
title = 'pz works!';
}
컴포넌트는 모듈처럼 class 로 선언되며 첫 컴포넌트의 이름은 AppComponent 입니다. 이 컴포넌트는 title 속성을 가지고 있는 간단한 구성입니다. 이 객체가 갖는 속성은 뷰에 공개되어 원하는 화면을 구성하는 역할을 합니다. 사실 컴포넌트는 Angular2에서 참으로 중요한 개념입니다. 기존의 웹 어플리케이션이 결과적으로 가지고 있는 한계점 중에 한 가지는 웹 환경이 '컴포넌트 기반 개발' 같은 코드 자산을 재사용하기 쉬운 구성을 하기 어려웠다는 점입니다. HTML5 에서 이러한 고민들이 포함되어 Web Component를 구현할 수 있는 환경이 마련되었다는 점에서 환영받고 있습니다.
웹 컴포넌트는 기존 웹 구성 요소인 "Document", "Styles", "Behaviors"를 모두 혹은 일부분으로 표현합니다. 일반적인 웹에서는 Document는 HTML, Styles는 CSS, Behaviors는 Javascript 로 표현합니다. 컴포넌트의 표현이라고 크게 다른 것은 없습니다. 단지 컴포넌트 자신만의 Scope 안에서만 작용할 수 있도록 경계를 잡아주는 것이 다릅니다. 자세한 내용은 3. Components와 Directives에서 다룰 것입니다. 지금은 우리의 첫 컴포넌트를 어서 만들어보죠.
<예제> src/app/app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'pz-root',
templateUrl: './app.component.html'
})
export class AppComponent {
/* ... */
}
Angular2 에서 컴포넌트는 Component 데코레이터로 정의하며 NgModule 과 같은 '@angular/core'에 포함되어 있습니다. AppComponent의 위에 @Component()
를 선언하면 이 class 는 Angular 컴포넌트가 됩니다. 참 쉽죠? Typescript 문법인 데코레이터를 사용하여 어떤 일들을 해내고 있는지를 보면 감탄스럽습니다.
이제 컴포넌트를 구성하는 설정을 살펴봅시다. selector: 'pz-root'
이 보입니다. 웹 컴포넌트는 Custom Element 표현을 통해 좀 더 쉽게 사용할 수 있습니다. Angular는 이전 버젼부터 이 부분을 적극 활용하여 컴포넌트 간 결합이나 화면구성 등을 표현하는 방법으로 사용하고 있습니다. Angular2 에서도 직관적인 표현을 위해 Custom Element 표현을 사용하며 selector 에 엘리먼트 이름을 적으면 끝입니다. 정말 간단하죠? 이제 우리는 다음과 같은 HTML 표현을 쓸 수 있습니다.
<pz-root>Loading...</pz-root>
AppComponent 를 사용하고 싶은 위치에 이 HTML 표현을 적어두기만 하면 됩니다. 실제로 우리는 이번 장의 마지막에 이 엘리먼트 를 원하는 위치에 적는 것으로 첫 페이지 작성을 완성할 것입니다.
Component 의 다음 속성은 templateUrl 입니다. src/app/app.component.html
파일을 열어봅시다. 간단한 HTML이 준비되어 있습니다. 이 부분을 우리 입맛대로 변경하여 봅시다.
<예제> src/app/app.component.html
<h1>
Welcome, {{title}} home <!-- 이곳을 변경합니다. -->
</h1>
템플릿은 selector 로 표현된 Custom Element 안에 표현될 실제구성입니다. 짐작이 가겠지만 컴포넌트의 구성요소인 도큐먼트, 스타일, 행위 중에 도큐먼트는 템플릿이 맡고 행위는 구현된 타입스크립트 코드가 맡아서 구성하게 됩니다. 스타일에 대한 표현은 차차 알아보도록 하죠. 간단한 템플릿 표현은 templateUrl 표현 대신 template 표현으로 대체하기도 합니다. 예제를 잠시 소개하면 다음과 같습니다.
<예제> src/app/app.component.ts
@Component({
/* ... */
template: `
<h1>
Welcome, {{title}} home
</h1>
`
})
export class AppComponent {
/* ... */
}
Backquote 문법으로 여러줄의 문자열을 쉽게 표현하고 있는 것은 참고할만 합니다. 이렇게 코드 안에 HTML 문법이나 HTML 문자열을 포함하는 것에 대해서는 호불호가 있습니다. 단지 이 이유만으로 리액트의 JSX 문법을 비판하는 의견도 있을 정도입니다. 필자는 HTML 문법 혼용에 대하여 가독성을 기준으로 짧은 표현이라면 혼용되는 것이 별 문제 없지만 결과적으로는 리팩토링을 고려할 때 별도의 파일로 분리 가능한 부분으로 생각합니다. 살펴본 첫 컴포넌트의 전체 소스는 다음과 같습니다.
<예제> src/app/app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'pz-root',
templateUrl: './app.component.html',
styles: []
})
export class AppComponent {
title = 'pz works!';
}
이제 마지막 작업만 남았습니다. 피자가게의 오픈을 축하하며, index.html
을 엽시다.
<예제> src/index.html
<!-- ... -->
<body>
<pz-root></pz-root> <!-- 축하합니다! -->
</body>
<!-- ... -->
body 태그 안에 루트 컴포넌트를 놓아두면 이제 우리 첫 스텝은 완성되었습니다. 이제 콘솔에서 우리 프로젝트를 구동해봅시다.
> ng serve
Webpack-dev-server 가 정상적으로 구동이 되면 웹브라우져를 열고 http://localhost:4200/
에 접속하여 피자가게 첫 화면을 볼 수 있습니다.
<그림> Webpack-dev-server이 구동된 모습
<그림> localhost 접속 화면
드디어 피자가게의 이름을 볼 수 있군요. 장황하긴 했지만 실제 우리는 20줄 내의 코드 2개로 피자가게 이름을 화면에 표시하였습니다. 그리고 앞으로 지금까지의 과정을 그대로 밟으면서 다음 작업을 하게 될 것입니다. Angular의 장점은 이 부분에 있는 것이죠.
그런데 가만히 살펴보니 내용이 조금 바뀌었습니다. 작성했던 예제 app.component.ts
의 일부를 다시 확인해 봅시다.
/* ... */
export class AppComponent {
title = 'pz works!';
}
화면에 표시된 Your's Pizza House
가 어떻게 구성된 것인지 쉽게 눈에 보이네요. 한 가지 독특한 표현만 이해한다면 말이죠. 설명에 앞서 한 가지 수정을 해 보세요. this.name = 'Your';
에 여러분의 피자가게 이름을 직접 적어보세요.
/* ... */
export class AppComponent {
title = 'Kim'; /* 여러분의 피자가게 이름으로 적어보세요 */
}
수정을 감지한 Webpack이 리빌드를 수행하고 나면 웹 페이지가 바뀌어 있을 것입니다.
<그림> 바뀐 localhost 접속 화면
여러분의 피자가게 이름으로 바뀐 것을 확인하셨나요? template 를 수정하지 않고도 멋진 결과를 확인하게 되었네요. 이제 template 에서 사용된 {{name}}
문법을 잠깐 설명하겠습니다. 컴포넌트 class 를 만들 때 객체의 속성이 뷰에 공개된다는 설명을 기억하시나요? {{propertyName}}
은 이렇게 공개된 객체 속성을 뷰인 template 에서 사용하는 문법입니다. 앞으로 좀 더 복잡한 Behavior가 이런 속성들을 수정하면 그 결과가 즉시 template 에 반영되는 마법을 경험하게 될 것입니다. 3.1. Template 기초 문법과 Model에 연결하기에서 이와 관련한 내용을 다루고 있습니다.
<예제> src/app/app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'pz-root',
templateUrl: './app.component.html',
styles: []
})
export class AppComponent {
title = 'Kim';
}
<예제> src/app/app.component.html
<h1>
Welcome, {{title}} home
</h1>