Angular/Tutorial

앵귤러 튜토리얼 - tour-of-heroes 만들기 (3)

반응형

앵귤러 튜토리얼 - tour-of-heroes 만들기 (3)


저번 시간에는 컴포넌트를 활용해 문자열 형태의 영웅 클래스를 만들어 출력하는 것까지 해보았다. 이제, 영웅의 수를 늘려볼 차례다. 영웅의 수가 늘어난다면 지금과 같은 형태로는 html에서 모두 나타낼 수가 없다. 이제 앵귤러의 중요한 기능 중 하나인 *ngFor를 이용해 볼 것이다.

임의로 영웅들을 미리 만들어놓은 배열을 만들어보자.

src/app에 ‘mock-heroes.ts’라는 이름으로 파일을 만든 후, 다음과 같이 코드를 작성해보자.

import { Hero } from './hero';

export 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' }
];

import부분을 보면, 우리가 처음 만들었던 hero로 부터 Hero 클래스를 가져오는 것을 알 수 있다. 다음은 상수화 시킨 HEROES라는 이름을 가진 Hero[] 배열이다.

이제 heroes.componet.ts로 돌아와서 이 HEROES를 import 해주도록 하자.

import { HEROES } from '../mock-heroes';

또한 HeroesComponent 클래스 안에 heroes 영웅에 바인딩을 제공하는 속성을 추가한다.

heroes = HEROES;

이제 *ngFor을 활용해서 저장된 모든 영웅들을 나열할 것이다.

기존의 heroes.component.html 코드를 모두 지우고, 아래와 같이 작성해보자

<h2>My Heroes</h2>
<ul class="heroes">
  <li>
    <span class="badge"></span> 
  </li>
</ul>

My Heroes라는 문구 아래, 배열에 저장된 영웅의 id번호와 name을 출력하는 코드다.

여기서 class=”heroes”와 “badge”는 지금 설명하는 코드와 상관이 없다. 이는 CSS에서 각 구역마다 만들 스타일에 사용하기 위한 클래스 이름이니 지금은 신경쓰지 않도록 하자.

이제 <li>코드 내에 *ngFor 코드를 다음과 같이 추가한다.

<li *ngFor="let hero of heroes">

*ngFor는 지시어로, 목록의 각 요소에 대해 반복하는 기능을 담당한다.

앞에 별표 (*)를 꼭 써야 기능이 활성화되니, 잊지않고 작성하도록 하자

이제 브라우저에서 배열안에 저장했던 영웅들이 잘 나오는 것을 확인 할 수 있다.

(기존에 활용했던 Windstorm 코드는 지우면 된다.)


스타일

영웅을 보여주는 목록에 사용자가 마우스를 가져가거나 클릭할 때, 시각적으로 반응하도록 만들어야 한다. 이러한 기능은 css를 통한 스타일에 기능을 추가해서 만들어낼 수 있다.

각 컴포넌트마다 존재하는 css파일을 이용하면, 기존의 코드에서 css파일로 변화를 주지 않고 따로 css 스타일만 관리가 가능하기 때문에 가독성이 좋아지고, 재사용 시에도 효율적이다.

우리 프로젝트에서 활용될 css에 대한 코드는 다음과 같으며, heroes.component.css에 저장하며 된다.

/* HeroesComponent's private CSS 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;
}

이제 어느정도 디자인이 완성된 영웅 목록을 브라우저를 통해 볼 수 있다. 영웅에 마우스를 가져가면 색깔이 변하면서 클릭하면 무언가 기능이 나올 것 처럼 보인다.

우린 이제 목록에서 영웅들을 클릭하면, 목록 아래에 그 해당 영웅의 세부사항을 출력하는 일을 해볼 것이다.

기존의 *ngFor를 작성했던 heroes 컴포넌트의 html에 클릭 이벤트 바인딩을 다음과 같이 추가해보자

<li *ngFor="let hero of heroes" (click)="onSelect(hero)">

사용자가 클릭하면, 그 해당 목록의 onSelect(hero) 표현식이 실행된다.

따라서 heroes.component.ts에 onSelect 메서드를 다음과 같이 추가시켜야 한다.

selectedHero: Hero;

onSelect(hero: Hero): void {
  this.selectedHero = hero;
}

이제 클릭 시 html에서 세부사항을 보여주기 위해서 세부사항을 추가로 입력시켜주어야 한다. 세부사항에 해당하는 hero의 이름은 selectedHero로 바꾸어 다음처럼 작성하도록 하자.

<h2> Details</h2>
<div><span>id: </span></div>
<div>
  <label>name:
    <input [(ngModel)]="selectedHero.name" placeholder="name">
  </label>
</div>

이 상태에서 브라우저를 보면, 제대로 출력이 안되는 모습이다. 오류를 검사해보면 다음과 같은 메시지가 출력된다.

HeroesComponent.html:3 ERROR TypeError: Cannot read property 'name' of undefined

이러한 이유는, 현재 세부사항에 해당하는 선택영웅(selectedHero)가 지정이 되어있지 않기 때문이다. 따라서 ‘만약 선택영웅이 지정되었을 때’에 해당하는 If와 같은 함수 기능을 써야 한다는 걸 생각해보자.

따라서 영웅 세부 사항만 따로 감싸기 위해서 <div>를 사용해 묶어주고, div안에 *ngIf를 활용해서 다음과 같이 세부사항 부분을 수정해서 나타내보도록 하자

<div *ngIf="selectedHero">

  <h2> Details</h2>
  <div><span>id: </span></div>
  <div>
    <label>name:
      <input [(ngModel)]="selectedHero.name" placeholder="name">
    </label>
  </div>

</div>

이제 브라우저에서 이름 목록이 다시 원래대로 잘 뜨는 것을 볼 수 있을 것이다.

이름을 클릭하면 세부사항이 아래에 출력이 잘 되고 있다.

사용자가 원하는 영웅을 선택하면, 그 영웅 값이 selectedHero의 값이 되면서 세부사항이 DOM에 출력되는 것이다.

선택 영웅 스타일링

위의 사진처럼 영웅을 선택하여 세부사항을 볼 때, 해당 영웅 목록을 스타일링 해보려고 한다.

기존의 모습에서는 내가 어떤 영웅을 클릭하여 세부사항을 보고 있을 때, 내가 어떤 영웅을 눌렀는지 표시가 나오지 않고 있다.

우리는 이 프로젝트의 css파일을 미리 카피해서 저장해두었다. 이 css파일에는 이미 이러한 기능을 할 수 있는 코드가 내장되어있는 상태다. 바로 .selected 부분이 이 역할을 담당한다.

.selected {
    background-color: #CFD8DC !important;
    color: white;
  }

선택했을 때, 뒷 배경의 색과 글씨 색을 설정해 준 코드다.

우리는 이걸 사용하기 위해 html상에서 불러오는 코드만 따로 추가해주면 된다.

.selected 클래스를 html에 가져와서, 사용자가 영웅을 클릭하여 selectedHero가 되었을 때만 이루어지도록 하면 된다. 따라서 다음과 같은 코드만 추가해주면 될 것이다.

[class.selected]="hero === selectedHero"

이를 앵귤러의 ‘클래스 바인딩’이라고 하는데, CSS 클래스를 조건부로 쉽게 추가하고 제거할 수 있는 기능이다. 위의 코드 한 줄로 해당 스타일을 지정할 수 있는 모습을 볼 수 있을 것이다.

이 코드가 추가된 heroes.component.html의 <li>는 다음과 같다.

<li *ngFor="let hero of heroes"
  [class.selected]="hero === selectedHero"
  (click)="onSelect(hero)">
  <span class="badge"></span> 
</li>

목록을 보여주기 위해 사용한 반복 지시자인 *ngFor과 클릭 이벤트에 추가로 <li> 내에 작성해주는 것이다.

이제 저장 후 브라우저를 통해, 세부사항을 누르면 해당 영웅 목록의 변경된 색이 유지되는 것을 볼 수 있다.


요약
  • 영웅의 목록과 세부사항을 작성해 나타낼 수 있다.
  • 사용자가 영웅을 선택하면, 해당 영웅의 세부사항을 출력하도록 만들 수 있다.
  • 목록을 표시하는데 사용하는 지시자는 *ngFor이다.
  • 표시하려면 조건이 필요할 때 사용하는 지시자는 *ngIf다.
  • class 바인딩을 사용해 CSS 스타일 클래스로 전환할 수 있다.


반응형