Tour of Heroes - Hero 에디터 폼(form) (NgModel, Angular Pipe)

소스코드

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

이 게시물의 소스코드는 Tour of Heroes / Tour of Heroes - 예제 소개 및 프로젝트 생성에서 이어집니다.

tour-of-heroes.git 을 clone 한 적이 있는 경우: 터미널에서 해당 폴더로 이동 후 아래 명령어들을 붙여넣기합니다. 폴더 내 모든 코드가 이 게시물의 코드로 교체됩니다. 이를 원치 않으시면 이 방법을 선택하지 마세요.

git reset --hard
git pull
git reset --hard 7eccad1
git reset --soft 1166f77
npm install
atom .

tour-of-heroes.git 을 clone 한 적이 없는 경우: 터미널에서 코드를 다운 받을 폴더로 이동한 후 아래 명령어들을 붙여넣기하여 tour-of-heroes.git 을 clone 합니다.

git clone https://github.com/a-mean-blogger/tour-of-heroes.git
cd tour-of-heroes
git reset --hard 7eccad1
git reset --soft 1166f77
npm install
atom .

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


이번 강의에서는 hero 모델을 생성하고 hero 모델을 수정할 수 있는 form을 만들어 봅니다. 또한 form을 통해서 component의 항목의 값을 바꾸는 방법을 알아 봅시다.

폴더 구조


hero.ts는

$ ng generate class hero --skipTests

명령어로 생성하고, heroes 폴더와 그 안의 파일들은

$ ng generate component heroes --skipTests

명령어로 생성합니다.

ng generate 만들것 이름 명령어는 Angular 프로젝트에서 사용되는 기본 코드를 생성해 주는 명령어입니다. Hero class와 Heroes component를 만들었는데요, class는 단순히 하나의 ts파일을 생성하지만, component는 관련 파일들을 생성하고 app.module.ts에 해당 component를 등록하도록 코드들을 변경합니다.

어떤 것들을 만들 수 있는지는 ng generate --help 명령어로 알 수 있고, ng generate 만들것 --help 명령어로 더욱 상세한 내용을 알 수 있습니다.

--skipTests은 .spec.ts 파일을 생성하지 않은 옵션입니다. .spec.ts 파일은 automated test에 관련된 파일로 Tour of Heroes 강의에서는 다루지 않습니다.

코드 - Hero Class

// src/app/hero.ts

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

Hero class는 Hero의 스키마(형식)를 담고 있는 class이며 number 타입인 id와 string 타입인 name을 가지고 있습니다.

자바스크립트는 C나 Java와 다르게 타입이 지정되지 않습니다. 변수를 선언할때도 타입 없이 var로만 변수를 선언하고, 이 변수에는 숫자를 넣든 문자열을 넣든 상관이 없습니다.

TypeScript에서는 위 코드처럼 :타입_or_클라스 형태로 데이터에 타입을 지정해 줄 수 있습니다. 타입의 지정은 선택사항으로, 타입이 지정이 되면 지정된 타입 이외의 값을 넣으려 하는 경우 TypeScript 컴파일러가 에러를 내게 됩니다.

코드 - Heroes Component

// src/app/heroes/heroes.component.ts

import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit { // 1
  hero: Hero = { // 2
    id: 1,
    name: 'Windstorm'
  };

  constructor() { }

  ngOnInit() { // 1
  }

}

1. implements 키워드는 interface(인터페이스)를 사용하기 위해 쓰입니다. 프로그래밍에서 interface가 뭔지 모르시는 분들은 지금은 그냥 넘어가셔도 괜찮습니다.

반드시 알고 넘어가야 할 부분은 OnInit를 implement하고 ngOnInit함수를 작성하면, class가 생성된 후 ngOnInit함수 안의 코드가 바로 실행되는 역할을 한다는 것입니다.

Implements OnInitngOnInit함수는 ng generate component 명령어에 의해 자동으로 작성되었습니다. 현재는 ngOnInit함수가 비어있으므로 아무런 일도 하지 않습니다.

2. heroes component는 hero라는 항목(property)를 가지며, 이 항목의 타입은 Hero입니다. hero class에서 본 것과 동일하게 콜론(:)을 써서 타입을 주고 있습니다. 위에서 우리가 만든 hero class를 타입으로 사용하는 코드입니다. 이처럼 string, number 등 자바스크립트에서 제공하는 기본적인 타입외에 class를 타입으로 사용할 수도 있습니다.

hero class가 id와 name 항목을 가지고 있으므로 heroes component의 hero도 마찬가지로 id와 name을 가져야 합니다. 만약 id나 name이 없거나, 이 둘 이외의 항목이 들어가면 terminal에 에러가 표시됩니다. 이게 TypeScript에서 타입이 하는 역할입니다. 데이터에 스키마를 지정하고, 지정된 스키마 이외의 값이 들어오면 에러를 냅니다.

<!-- src/app/heroes/heroes.component.html -->

<h2>{{hero.name | uppercase}} Details</h2> <!-- 1 -->
<div><span>id: </span>{{hero.id}}</div>
<div>
  <label>name:
    <input [(ngModel)]="hero.name" placeholder="name"/> <!-- 2 -->
  </label>
</div>

1. {{ hero.name }}에서 hero는 HerosComponent class의 hero 항목을 뜻하고, hero.name이므로 해당 항목의 name, 즉 'Windstorm'를 해당 위치에 출력하게 됩니다.

여기에 | uppercase가 더해졌는데요, 이처럼 Angular의 html파일에서 | 뒤에 오는 것들을 pipe라고 합니다. upppercase는 이름으로 부터 출력되는 값의 소문자를 대문자로 바꾸어주는 역할을 할 것을 예상할 수 있습니다.  이처럼 pipe는 앞의 데이터를 변경하는 역할을 합니다.

uppercase pipe는 @angular/common NPM package에 저장이 되어 있고, @angular/common는 package.json에 dependencies로 되어 있기 때문에 현재 프로젝트에서 사용할 수 있습니다.

https://angular.io/api/common#pipes 에서 @angular/common에 들어있는 다른 pipe들을 모두 볼 수 있으니 참고하세요. 물론 여기에 나열된 pipe가 전부는 아니고요, pipe는 다른 package에 있을 수도 있고, 직접 pipe를 만들 수도 있습니다. 여기서는 pipe가 정확히 뭔지, 뭘 위해서 사용하는 것인지, 어떻게 사용하는 것인지만 확실히 이해하고 넘어갑시다.

2. input에 [(ngModel)]="클라스_항목"은 input의 value와 component class의 항목값을 양방향으로 연결하는 역할을 합니다. 즉 웹사이트 이용자가 input의 값을 바꾸는 경우 HerosComponent class의 hero.name의 값이 변경되고 이로인해 위 1번의 hero.name의 값 역시 바뀌게 됩니다. 물론 만약 코드내에서 HerosComponent class의 hero.name의 값이 변경되는 경우 html 상의 1번과 2번의 값 역시 실시간으로 바뀌게 됩니다.

코드 - App Component

<!-- src/app/app.component.html -->

<h1>{{title}}</h1>
<app-heroes></app-heroes> <!-- 1 -->

1. HerosComponent class selector가 'app-heroes'로 설정되었기 때문에 <app-heroes></app-heroes>를 써서 해당 component를 배치하였습니다.

코드 - App Module

// src/app/app.module.ts

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

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

@NgModule({
  declarations: [
    AppComponent,
    HeroesComponent, // 2
  ],
  imports: [
    BrowserModule,
    FormsModule, // 1
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

1. NgModel을 사용하기 위해 FormsModuleAppModuleimports에 넣었습니다.

NgModel을 쓰려면 FormsModule을 가져와야하는 것을 어떻게 알 수 있느냐, 첫째로 저도 그렇게 배웠으니까 알 수 있는 부분이고, 정확히는 https://angular.io/api/forms/NgModel 에 설명되어 있습니다. 프로그래밍을 배울 때는 항상 공식 문서를 가까이 하는 습관을 들입시다.

2. component들은 AppModuledeclarations에 들어가야 합니다. ng generate component 명령어로 component를 생성하면 자동으로 AppModule의 코드가 수정됩니다.

반드시 기억합시다

  • module들은 AppModuleimports에 들어가야 하고,
  • component들은 AppModuledeclarations에 들어가야 합니다.
  • hero.ts와 같이 타입으로 사용되는 class들은 AppModule에 넣지 않아도 사용할 수 있습니다.

실행 결과

heroes component에 의해 heroes component의 hero 항목이 화면에 표시되고, 이름을 변경할 수 있는 input 이 표시됩니다.

input의 값을 변경하면 '~ Details'의 텍스트도 동시에 변경이 됩니다. NgModel에 의해 input과 heroes component class의 hero.name 항목을 서로 양방향으로 연결되었기 때문입니다.

마치며..

이번 강의에서는

  • ng generate 명령어
  • :타입_or_클라스 구문
  • ngOnInit함수
  • pipe의 개념과 uppercase pipe
  • ngModel의 기본 사용법

을 알아봤습니다. 반드시 모든 내용을 이해하신 후 계속해서 강의를 진행해 주세요. 만약 이해가 되지 않는다면 댓글로 질문을 해주세요.

댓글

댓글쓰기

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

UP