소개 및 Hero Editor(@Component, @NgModule, [(ngModel)])

소스코드

이 게시물에는 코드작성이 포함되어 있습니다. 소스코드를 받으신 후 진행해 주세요. MEAN Stack/개발 환경 구축에서 설명된 프로그램들(git, npm, atom editor)이 있어야 아래의 명령어들을 실행할 수 있습니다.

git clone https://github.com/a-mean-blogger/tour-of-heroes.git
cd tour-of-heroes
git reset --hard b86cb77
npm install
atom .

- Github에서 소스코드 보기: https://github.com/a-mean-blogger/tour-of-heroes/tree/b86cb770c6619dd74900a80b03225ef70c043021


Tour of Heroes

Tour of Heroes는 Angular 공식 사이트(https://angular.io)의 tutorial(https://angular.io/docs/ts/latest/tutorial)입니다. Step by step으로 쉽고 상세하게 설명되어 있지만 영어가 부담스러운 분들을 위해 진행하려 합니다. 이 포스트는 https://angular.io/docs/ts/latest/tutorial/toh-pt1.html 를 기준으로 하고 있습니다.

완성된 사이트는 Hero의 목록을 보여주며, Hero를 등록, 삭제, 수정, 열람 할 수 있는 일반적인 CRUD(create, read, update, delete) 기능을 가지고 있습니다. 완성된 예제는 https://angular.io/generated/live-examples/toh-pt1/eplnkr.html 에서 확인할 수 있습니다. 링크를 눌러 살펴봅시다.


링크를 누르면 http://plnkr.co에서 예제가 작동되는데, 왼쪽에는 파일과 코드를 볼 수 있고, 오른쪽에는 preview로 완성된 사이트를 볼 수 있습니다. 왼쪽에서 코드를 수정하면 실시간으로 preview에 반영됩니다. 우선 preview에서 hero를 등록, 삭제, 수정해 봅시다.

이 예제는 완성품이고, 이 포스트에서 우리가 할 부분은  https://angular.io/resources/live-examples/toh-1/ts/eplnkr.html 여기에 있습니다. 마찬가지로 링크를 눌러 어떤 기능이 있는지 살펴봅시다. 


name에 있는 input에 값을 변경하면 타이틀(Tour of Heroes) 바로 밑의 text  값 또한 실시간으로 바뀌게 됩니다. input에 값이 변경되면 component class의 hero 항목값이 변경되고, html template에서 이 값이 따라서 변경되는 형태입니다.

프로젝트 생성

$ ng new tour-of-heroes

Angular-CLI를 사용하여 새로운 프로젝트를 생성합시다. 프로젝트 이름은 tour-of-heroes입니다.

$ cd tour-of-heroes

프로젝트 폴더로 이동합니다.

$ atom .

Atom text editor로 프로젝트를 엽니다. Atom editor를 쓰지 않으시면 다른 원하시는 editor프로그램을 사용하시면 됩니다.

$ ng serve --open

프로젝트를 실행합니다. 프로젝트 실행중에 코드를 변경하면 자동으로 브라우저에 적용이 되므로 프로젝트를 실행한 상태에서 코딩하면 편합니다.

예제 코드 가져오기

1. https://angular.io/resources/live-examples/toh-1/ts/eplnkr.html 에서 Project/app/app.component.ts에 있는 코드를 복사해서 프로젝트 폴더의 src/app/component.ts에 붙여넣습니다.

2. Project/style.css도 복사해서 src/style.css에 붙여넣습니다.

3. 프로젝트 폴더의 src/index.html에서 <app-root>Loading...</app-root>를  <my-app>Loading...</my-app>으로 바꿉니다.(이 이유는 아래 코드 설명에서 설명합니다.)

4. 이번 포스팅에서 쓰이지 않는 app.component.spec.ts, app.component.html, app.component.css 를 프로젝터 폴더의 src/app에서 지워줍니다.

모든 파일을 저장하면 내 브라우져에 예제와 같은 사이트가 나타납니다.(ng serve가 실행되고 있어야 합니다.)


코드

이 강의는 새로운 용어, 개념들을 최대한 상세하게 풀어서 설명하고 있습니다. TypeScript, Angular를 처음 접하는 분들은 이러한 설명들을 모두 이해하려고 하지 말고 일단 "어떻게"에 중점을 맞추고 강의를 따라하시고 나중에 어느정도 "방법"이 익숙해 지면 그때 다시 돌아와서 개념들을 익히셔도 좋습니다.

이 강의에서는 angular사이트의 기본 구조가 어떻게 생겼는지, 어떻게 AppComponent class의 항목값들이 사이트에 표시가 되는지만 알고 넘어가시면 됩니다.

살펴볼 파일들과 기본적인 역할은 다음과 같습니다.

  • 프로젝트 폴더/src/app/app.component.ts : 사이트의 main template으로 사용
  • 프로젝트 폴더/src/app/app.module.ts : 사이트에 사용될 ts파일들을 묶어주는 역할
  • 프로젝트 폴더/src/index.html : front-end library(예를 들어 bootstrap) 및 resource(fonts, css 등등) 호출 및 app.component의 template 호출
  • 프로젝트 폴더/src/styles.css : 사이트 전체에 적용될 css를 설정하는 부분 - css는 따로 설명하지 않습니다.

app.component.ts를 살펴봅시다.

// src/app.component.ts

import { Component } from '@angular/core'; //1


export class Hero { //2
  id: number;
  name: string;
}


@Component({ //3
  selector: 'my-app',
  template: ` //5
    <h1>{{title}}</h1>
    <h2>{{hero.name}} details!</h2>
    <div><label>id: </label>{{hero.id}}</div>
    <div>
      <label>name: </label>
      <input [(ngModel)]="hero.name" placeholder="name">
    </div>
    `
})
export class AppComponent { //4
  title = 'Tour of Heroes';
  hero: Hero = {
    id: 1,
    name: 'Windstorm'
  };
}

1. @angular/core package에서 Component객체를 가져옵니다(import). 

2. Hero 객체를 class로 선언하고 있습니다. Hero 객체는 id와 name 항목을 가지고 있으며 id의 타입은 number, name의 타입은 string인 것을 알 수 있습니다. 이처럼 TypeScript에서 class 항목 바로 옆에 :(콜론)이 사용되는 경우 그 항목의 타입을 나타내는 역학을 합니다(항목명type 의 형태).
class 왼쪽에 export 는 이 class가 다른 파일에서 가져갈(import) 수 있음을 나타냅니다. (사실 이번 코드에서는 Hero class가 다른파일에서 사용되지 않으므로 굳이 필요하지는 않습니다.)

3. 1번에서 가져온 Component 객체에@을 붙여 @Component (Component decorator)가 되었습니다. decorator는 class에 역할을 주고 각종 필요한 값들을 설정하게 해주는 역할을 합니다. 아무 객체에나 @을 붙여서 decorator를 만들 수 있는 건 아니고 @angular/core package의Component 객체가 decorator의 역할을 할 수 있게 만들어져 있습니다. 4번의 AppComponent class를 Component로 만들어 주는 역할을 하고 있습니다.
@componentselector 항목은 html 코드에서 해당 component를 호출할때 사용되는 이름을 나타냅니다. my-app 으로 값이 주어졌으니 html 코드에 <my-app></my-app>을 넣으면 이 component가 해당위치에 나타나게 됩니다.
@component의 template 항목은 해당 component의 html 코드를 문자열로 가집니다. 자바스크립트 ES2015버전부터 ` (키보드에서 1번 왼쪽에 있는 작은따옴표)를 사용해 문자열을 감싸는 경우 줄바꿈(엔터)까지 포함하게 되었습니다. 일반 따옴표(", ')를 사용하고 해당 html 코드를 한줄로 작성하는 것과 같습니다. 이 template html코드는 5번설명으로 다시 상세하게 살펴보겠습니다.

4. 3번의 component decorator가 붙어있는 AppComponent class입니다. Angular에서 Component들은 class이름끝에 Component를 붙이도록 약속(convention)이 되어 있으니 반드시 따르도록 합시다. AppComponent는 title과 hero 항목이 있는데 title에는 type이 없이 바로 문자열 값을 대입(=)하고 있고, hero는 2번에서 만든 Hero class를 type으로 정한 후(: Hero가 hero에 붙어있는 모습) Hero class에서 설정한 항목들에 각각 타입의 값을 넣어주고 있습니다.
TypeScript는 이처럼 Type을 정할 수 있지만 반드시 타입을 써야하는 것은 아닙니다. 4번 부분을

export class AppComponent {
  title = 'Tour of Heroes';
  hero = {
    id: 1,
    name: 'Windstorm'
  };
}

이처럼 hero 항목에 type을 빼고 만드는 경우 hero 값의 항목이름에 오타가 있는 경우(예를들어 id를 Id로 쓴 경우), 혹은 해당 항목에 원치 않는 타입의 값이 들어간 경우(id에 문자열의 값을 준 경우) TypeScript가 이를 오류로 보지 않게 됩니다.

export class AppComponent {
  title: string = 'Tour of Heroes';
  hero: Hero = {
    id: 1,
    name: 'Windstorm'
  };
}

위 처럼 title에도 string type을 지정할 수도 있으며, 이 경우 title에 string 외의 값이 대입되는 경우 TypeScript가 오류를 내게 됩니다.

5. AppComponent의 html Template 코드를 살펴봅시다. html부분만 따로 가져와서 상세히 파헤쳐봅시다.

    <h1>{{title}}</h1> <!-- 5-1 -->
    <h2>{{hero.name}} details!</h2> <!-- 5-2 -->
    <div><label>id: </label>{{hero.id}}</div> <!-- 5-3 -->
    <div>
      <label>name: </label>
      <input [(ngModel)]="hero.name" placeholder="name"> <!-- 5-4 -->
    </div>

5-1,2,3,. Angular component template에서 {{ 항목 }}은 해당 component class의 항목값을 그대로 표시하는 역할을 합니다.

5-4. Angular component template에서 input에 [(ngModel)]="항목"input과 component class의 항목값을 양방향으로 연결하는 역할을 합니다. 즉 웹사이트 이용자가 input의 값을 바꾸는 경우 hero.name의 값이 변경되고 이로인해 5-2번의 hero.name의 값 역시 바뀌게 됩니다. 물론 프로그램내에서 hero.name의 값이 변경되는 경우 5-2번과 5-4번의 값 역시 실시간으로 바뀌게 됩니다.

다음으로 app.module.ts 파일을 살펴봅시다.

// src/app/app.module.ts

import { NgModule }      from '@angular/core'; //1
import { BrowserModule } from '@angular/platform-browser'; //1
import { FormsModule }   from '@angular/forms'; // <-- NgModel lives here //1

import { AppComponent }  from './app.component'; //2

@NgModule({ //3
  imports: [
    BrowserModule,
    FormsModule // <-- import the FormsModule before binding with [(ngModel)]
  ],
  declarations: [
    AppComponent
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { } //4

1. NgModule, BrowserModule, FormsModule 객체를 각각의 package에서 가져오고 있습니다. AppComponent의 template에서 사용했던 NgModel이 FormsModule에서 왔다고 설명하고 있네요. 

2. 위에서 살펴봤던 AppComponent를 가져오고 있습니다. 1번 객체들은 package에서 불러왔기 때문에 package 이름을 그대로 from으로 사용했지만(1번의 @angular/...들의 @는 특별한 의미가 있는 것은 아니고 @angular가 package이름입니다) AppComponent는 다른 파일에서 객체를 불러오기 때문에 상대주소로 파일 위치를 나타내고 있습니다.

3. NgModule은 AppModule class에 decorator로 사용되고 있음을 알 수 있습니다. NgModule decorator는 상세하게 설명하면 꽤 복잡하기 때문에 설명은 생략하고, Module들은 imports 항목에, Component들은 declarations 항목에, bootstrap 항목은 메인이 되는 component 하나가 들어간다고 생각하고 일단 지나가기 바랍니다. 해당 사이트에서 사용되는 코드들은 NgModule에 등록되어야 정상적으로 작동하게 됩니다. 

마지막으로 index.html을 살펴봅시다.

<!DOCTYPE html>
<html>
  <head>
    <title>Angular Tour of Heroes</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="styles.css">

    <!-- Polyfills -->
    <script src="https://unpkg.com/core-js/client/shim.min.js"></script>

    <script src="https://unpkg.com/[email protected]?main=browser"></script>
    <script src="https://unpkg.com/[email protected]/dist/system.src.js"></script>

    <script src="systemjs.config.js"></script>
    <script>
      System.import('main.js').catch(function(err){ console.error(err); });
    </script>
  </head>

  <body>
    <my-app>Loading...</my-app> <!-- 1 -->
  </body>
</html>

1. AppComponent에서 selector로  설정했던 my-app이 여기에 사용되었습니다. 해당 component가 로딩이 완료되면 my-app tag안의 내용은 component으로 교체됩니다. 

index.html은 외부 라이브러리(bootstrap, fonts 등)를 넣는 용도 외에는 만질 일이 없습니다.

마치며..

AppComponent class의 항목명을 바꾸거나 Hero class의 구조를 바꿔가며 어떻게 데이터가 template으로 전달되는지 충분히 연습해 보세요. 수고하셨습니다.

댓글

하영아빠 2017.07.12
이번 글도 잘 보았습니다. 감사합니다.
I
Ian H 2017.07.12
@하영아빠,
감사합니다^^
이도원 2017.11.23
저번 강의에서는 잘 작동되었는데 ng new tour-of-heroes 실행시 You cannot use the new command inside an Angular CLI project라는 오류가 뜨는데 어떻게 해결해야 할까요 
I
Ian H 2017.11.24
@이도원,
에러메세지를 해석하면 "Angular CLI 프로젝트 안에서 new 명령어를 사용할 수 없습니다." 즉 현재 ng new를 사용해서 생성된 폴더안에서 다시 ng new로 프로젝트를 만드시려는 것 같은데, 다른 폴더로 이동한 후에 입력해 보세요
이서영 2019.02.06
덕분에 노드js를 잘 흉내내어보고 있습니다!!  댓글을 처음 달아보네요ㅎㅎ; 앵귤러를 이제 시작한는데 위 링크들중에 몇몇개가 page not found가 뜨네요 ㅠㅠ 수정 부탁드릴 수 있을까요?
그리고 갑자기 ng라는 명령어가 나왔는데 이부분을 알 수 있을까요?
복 많이받으세요!!
이서영 2019.02.06
헉 자문자답입니다... tour of heroes 전에 단계가 하나 더 있었군요 어쩐지... 죄송해용 ><
I
Ian H 2019.02.06
@이서영,
안녕하세요~ 새해 복 많이 받으세요^^ ng 명령어들은 angluar-CLI를 설치하면 사용할 수 있어요^^ 말씀듣고 이전 문서에도 해당 내용을 추가했습니다. 감사해요.
링크가 깨진 것은.. angular 새버전이 나오면서 공식 사이트의 튜토리얼이 변경되었기 때문인데요.. 이글을 작성한 2017년에는 angular 4였고, 지금은 angular 7까지 나온 상태입니다.
angular 7의 tour of heroes 예제를 살펴보니.. 단순히 링크를 수정하는 정도가 아니라 강의 글을 새로 작성해야 할 판이네요ㅠㅠ 나중에 시간적 여유가 된다면 다시 작성해 보겠습니다.
참고로 angular는 하위 호환이 되기 때문에 위 강의로 공부하셔도 크게 문제가 되진 않습니다!
댓글쓰기

이 글에 댓글을 다시려면 SNS 계정으로 로그인하세요. 자세히 알아보기

UP