이제 Angular의 기본 구조를 정리하려고 합니다.

읽기 전에 Angular Documentation에 있는 튜토리얼을 먼저 진행하시는 것을 추천합니다.

설명만 읽었을땐 개념이 와닫지 않았는데, 튜토리얼을 직접 해보면서 머리속에 정리가 되었었네요.

 

그럼 Angular CLI 를 설치하셨다는 가정하에 진행하겠습니다.

※ 튜토리얼 개발환경 설치 및 실행

git clone https://github.com/angular/quickstart.git quickstart
cd quickstart
npm install
npm start

다음 명령어를 차례대로 입력해주면 초기 프로젝트 환경의 설치 및 실행은 끝납니다.

첫번째 명령어는 git에서 해당 소스코드를 로컬에서 지정한 디렉토리로 복사하는 것이고,

두번째는  복사한 디렉토리로 이동, 세번째는 Angular 실행을 위해 프로젝트 폴더에 NPM 모듈 설치를 지시하고,

마지막 명령이 typescript 컴파일 및 프로젝트 실행 명령입니다.

보통 .ts 파일을 수정하면 자동으로 재 컴파일 및 실행이 되나,

컴파일 도중 에러가 발생하기 시작하면 이러한 작동이 멈춰버리는 경우가 있습니다.

그럴땐 작동중인 angular 및 node.js 모듈을 kill 명령으로 모두 종료하고 다시 npm start 명령을 입력해서 작동시켜야 합니다.

 

※ Bootstrapping

Angular에서 Application을 시작하면 Root Module의 @NgModule decorator에 입력된 metadata를 읽어

Component, Service 등 요소의 구조를 구성합니다. 이러한 일련의 과정을 Angular에서는 bootstrapping 으로 명칭합니다.

이를 통해 Root Module인 AppModule 클래스가 재구성되어 실행되며, 정의된 컴포넌트에 접근을 할수 있게 되죠.

이러한 @NgModule은 app.module.ts 파일에 정의합니다.

 

※ app.module.ts 파일 예시

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

import { AppComponent }        from './app.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroesComponent }     from './heroes.component';
import { HeroService }         from './hero.service';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot([
      {
        path: 'heroes',
        component: HeroesComponent
      }
    ])
  ],
  declarations: [
    AppComponent,
    HeroDetailComponent,
    HeroesComponent
  ],
  providers: [
    HeroService
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule {
}

 

보시면 AppModule 클래스를 직접 작성하지 않고, @NgModule 안에서 구성을 선언하도록 되어 있습니다.

프로젝트의 실질적인 초기화 및 환경설정이 이 안에서 이루어지는 걸로 보시면 됩니다.

imports - Angular 라이브러리를 불러와서 사용하는 경우, 해당하는 모듈 명을 기재합니다.

declarations - 프로젝트 안에서 작성하여 사용할 컴포넌트를 선언하는 부분입니다.

providers - 각 컴포넌트 안에 Injection을 걸어 사용할 서비스 클래스를 명시하는 부분입니다.  Spring을 공부하셨다면 이해하시기 쉬울겁니다.

bootstrap - 정확한 의미는 잘 모르겠습니다. 다만 여기에 지정된 Component는 메인 페이지의 역할을 하는거 같습니다. 보통 이 부분은 수정하지 않습니다.

 

여기서 잠깐 Spring, Struts 등의 자바 MVC 프레임워크를 생각해보면 DAO, Service 같은 객체를 통해서 Data 처리를 별도로 분리했었습니다.

또한 Spring 같은 경우는 DI라는 패턴을 통해 싱클톤으로 객체의 의존관계를 주입했었죠?

그 DI가 Angular에서도 비슷하게 사용됩니다.

 

위에서부터 보였던 @NgModule, @Component 같은 decorator가 바로 DI를 정의하는 역할을 합니다.

 

@NgModule decorator는 모듈안에서 Component와 Service 및 외부 라이브러리 모듈 등의 의존관계를 정리하며,

@Component decorator는 Component별로 Service, template, style 등의 의존 관계를 정의합니다.

 

Component라는 요소는 하나의 페이징 단위에 해당하며,

고유한 태그 명을 갖고 있기 때문에 다른 컴포넌트에서도 호출해서 쓸수 있으며,

 

각 페이지의 html tag, client action, css style을 모두 포함하는 요소입니다.

 

Component는 @Component decorator로 selector, template, style, providers를 지정할 수 있습니다.

select - 외부에서 해당 컴포넌트를 호출하기 위해 사용하는 명칭입니다.

template - 브라우저에 실제로 표시되는 HTML 마크업을 정의하는 요소입니다.

templateUrls - HTML 마크업을 별도의 파일로 분리한 경우 해당 파일의 path를 지정할때 씁니다.

styles - HTML 마크업의 CSS 스타일을 정의하는 요소입니다.

styleUrls - CSS를 별도의 파일로 분리한 경우 해당 파일의 path를 지정할때 씁니다.

providers - 컴포넌트 내에서 사용할 서비스 클래스를 명시합니다.

 

import { Component }   from '@angular/core';
 
import { Hero }        from './hero';
import { HeroService } from './hero.service';
 
@Component({
  selector: 'hero-list',
  template: `
  <div *ngFor="let hero of heroes">
    {{hero.id}} - {{hero.name}}
  </div>
  `
})
export class HeroListComponent {
  heroes: Hero[];
 
  constructor(heroService: HeroService) {
    this.heroes = heroService.getHeroes();
  }
}

※ app.component.ts 파일 예시

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

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

const HEROES: Hero[] = [
  { id: 11, name: 'Mr. Nice' },
  { id: 12, name: 'Narco' },
  { id: 13, name: 'Bombasto' },
  { id: 14, name: 'Celeritas' },
  { id: 15, name: 'Magneta' },
  { id: 16, name: 'RubberMan' },
  { id: 17, name: 'Dynama' },
  { id: 18, name: 'Dr IQ' },
  { id: 19, name: 'Magma' },
  { id: 20, name: 'Tornado' }
];
 
@Component({
  selector: 'my-app',
  template: `
    <h1>{{title}}</h1>
    <h2>My Heroes</h2>
    <ul class="heroes">
      <li *ngFor="let hero of heroes"
        [class.selected]="hero === selectedHero"
        (click)="onSelect(hero)">
        <span class="badge">{{hero.id}}</span> {{hero.name}}
      </li>
    </ul>
    <div *ngIf="selectedHero">
      <h2>{{selectedHero.name}} details!</h2>
      <div><label>id: </label>{{selectedHero.id}}</div>
      <div>
        <label>name: </label>
        <input [(ngModel)]="selectedHero.name" placeholder="name"/>
      </div>
    </div>
  `,
  styles: [`
    .selected {
      background-color: #CFD8DC !important;
      color: white;
    }
    .heroes {
      margin: 0 0 2em 0;
      list-style-type: none;
      padding: 0;
      width: 15em;
    }
    .heroes li {
      cursor: pointer;
      position: relative;
      left: 0;
      background-color: #EEE;
      margin: .5em;
      padding: .3em 0;
      height: 1.6em;
      border-radius: 4px;
    }
    .heroes li.selected:hover {
      background-color: #BBD8DC !important;
      color: white;
    }
    .heroes li:hover {
      color: #607D8B;
      background-color: #DDD;
      left: .1em;
    }
    .heroes .text {
      position: relative;
      top: -3px;
    }
    .heroes .badge {
      display: inline-block;
      font-size: small;
      color: white;
      padding: 0.8em 0.7em 0 0.7em;
      background-color: #607D8B;
      line-height: 1em;
      position: relative;
      left: -1px;
      top: -4px;
      height: 1.8em;
      margin-right: .8em;
      border-radius: 4px 0 0 4px;
    }
  `]
})
export class AppComponent {
  title = 'Tour of Heroes';
  heroes = HEROES;
  selectedHero: Hero;
 
  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }
}

 

 

예시 코드를 보시면 (click)으로 클릭 이벤트 발생시의 action을 정의하면서 해당 컴포넌트에 선언된 함수를 호출하도록 하고 있습니다.

컴포넌트에서 선언한 정의한 메소드를 통해 페이징 단위의 이벤트를 처리할 수 있는 방식이네요.

 

 

@Component decorator 내 providers 속성과 Component 클래스 내의 constructor를 통해

해당 서비스 클래스를 사용한다는 것을 명시하고 있습니다.

자바에서 Spring, Struts를 사용할때와는 달리 해당 클래스에 직접 명시를 하네요.

이 부분에서 java와 angular의 차이가 보이는 군요.

angular는 설정파일을 최소화하고 각각의 컴포넌트를 Single View Applcation으로 사용한다는 취지를 살린게 보입니다.

이런 방식의 차이에 대해서 여러분들의 호불호가 상당히 갈릴것으로 보이네요.

저같은 경우는 변경의 단위가 컴포넌트 파일 하나로 줄어들기 때문에 더 신호하지만, 의견 차이가 분명히 있을 거 같네요.

 

Service, Component, Module의 관계를 그림으로 설명하겠습니다.

 

Component와 브라우저에서 보이는 페이지는 아래 그림으로 설명이 되겠습니다.

Component에 정의된 template으로 view에 표현될 마크업이 정의되며,

Component와 실제 html 사이에는 위의 화살표 방향의 binding으로 연결됩니다.

이를 통해 html에서 실제로는 Component에 있는 데이터를 보여주고, Component에 정의된 action을 호출하죠.

 

Injector 및 Routing에 대해서는 다음 포스트로 이어서 설명하겠습니다.

참고한 문서는 다음과 같습니다.

1. https://angular.io/guide/architecture

2. https://angular.io/guide/bootstrapping

3. https://angular.io/guide/setup

Posted by kevin.jeong.
,