일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- es6
- readonly
- 클론코딩
- 타입스크립트
- angular
- Props
- jwt
- npm
- 0.75px border
- TS
- 10px
- Strict
- 0.25px border
- entity
- ES5
- 1px border
- github
- font-size
- 데이터베이스 #try #이중
- literal
- TypeScript
- &연산
- Websocket
- 0.5px border
- ZOOM
- 서버리스 #
- 당근마켓
- 컴포넌튼
- 문서번호
- 전역변수
- Today
- Total
복잡한뇌구조마냥
Angular 공부 ( 개발환경 설정, 기본 구조 및 동작 ) 본문
1. 개발 환경 설정
1-0. VScode, Node.js 설치
- 구글 검색창에 검색 후 설치
- Node.js 는 최신 버전이 아닌 안정된 버전 설치 권장
1-1. Angular CLI 설치
npm install -g @angular/cli
- 터미널 명령어 실행
npm install -g
- npm 최신 업데이트 오류시 패치
- ng 명령어 오류시 컴퓨터 재구동
1-2. 기본 어플리케이션 생성
ng new [App-Name]
- ng new 명령어로 생성
1-3. 어플리케이션 실행
cd [App-Name] // 경로 이동
ng serve --open // 실행
- 어플리케이션 경로로 이동
- ng serve 명령어로 실행 ( --open 옵션으로 실행 후 접속 )
- 브라우저 실행이 되지 않을 경우 http://localhost:4200/ 주소로 접속
2. Agular 컴포넌트
파일 | 설명 |
app.component.ts | TypeScript로 작성된 컴포넌트 클래스 코드입니다. |
app.component.html | HTML로 작성된 컴포넌트 템플릿입니다. |
app.component.css | 이 컴포넌트에만 적용되는 CSS 스타일 파일입니다. |
2-1. app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Tour of Heroes';
}
- 클래스 내부에 값을 지정하여 사용
프로퍼티 | 설명 |
selector | 컴포넌트의 css 엘리먼트 셀렉터 |
templateUrl | 컴포넌트 템플릿 파일의 위치 |
styleUrls | 컴포넌트 css 스타일 파일의 위치 |
2-2. app.component.html
<h1>{{title}}</h1>
- ts파일의 title 값을 불러와서 사용, 함수도 동작
3. 컴포넌트 생성
3-1. 컴포넌트 생성 명령어
ng generate component [컴포넌트 이름]
- ng generate 명령을 실행하면 src/app/[컴포넌트] 폴더를 생성하고 해당 Component를 구성하는 파일을 생성합니다.
3-2. OnInit
// src/app/heroes/heroes.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
- OnInit을 import 하여 해당 클래스에 상속
- ngOnInit은 라이프사이클 후킹 함수
- Angular는 컴포넌트를 생성한 직후 ngOnInit을 호출
- 초기화 로직을 ngOnInit에 작성하는 것이 좋음
3-2. 생성된 컴포넌트 사용
<!-- app.component.html -->
<h1>{{title}}</h1>
<app-heroes></app-heroes>
- 상위 템플릿에 해당 방법으로 선언해주어 화면에 표시되도록 갱신
3-3. 인터페이스 생성 및 사용
// scr/app/hero.ts
export interface Hero {
id: number;
name: string;
}
- Angular는 TypeScript 기반 언어이므로 인터페이스를 선언하여 사용
// 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 {
hero: Hero = {
id: 1,
name: 'Windstorm'
};
constructor() { }
ngOnInit(): void {
}
}
- 다음과 같이 해당 타입을 import 해서 hero 변수에 할당
3-4. UppercasePipe로 표시형식 지정
<!-- src/app/heroes/heroes.component.html -->
<h2>{{hero.name | uppercase}} Details</h2>
- Uppercase는 Angular의 기본 파이프인 UppercasePipe를 가르킴
- 파이프는 문자열의 형식을 지정하거나, 통화 단위를 변경하고, 날짜나 데이터가 표시되는 형식을 변경할 때 사용
4. 양방향 바인딩
4-1. ngModel
<!-- html -->
<div>
<label for="name">Hero name: </label>
<input id="name" [(ngModel)]="hero.name" placeholder="name">
</div>
- [(ngModel)] 은 Angular의 양방향 바인딩 문법
- hero.name 프로퍼티의 값이 텍스트 박스에 작성되면 값이 전달되는 방식
- ngModel을 사용하기 위해서는 FormsModule을 로드해야함
4-2. FormsModule
// app.module.ts
import { FormsModule } from '@angular/forms'; // <-- NgModel은 이 패키지가 제공합니다.
@NgModule({
declarations: [
AppComponent,
HeroesComponent //새로 추가된 컴포넌트
]
imports: [
BrowserModule,
FormsModule // 해당 부분 추가
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
- 컴포넌트가 아닌 models.ts에 import해줌으로서 해당 모듈을 로드함
- ng genelate component로 추가한 컴포넌트도 해당 모듈에 등록해야함
5. 외부 데이터 값 사용하기 (데이터베이스 사용 연계?)
5-1. 데이터 파일 생성
// src/app/mock-heroes.ts
import { Hero } from './hero';
export const HEROES: Hero[] = [
{ id: 12, name: 'Dr. Nice' },
{ 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' }
];
- 다음과 같이 배열 값 데이터 작성
5-2. 데이터 로드
// src/app/heroes/heroes.component.ts
import { HEROES } from '../mock-heroes';
export class HeroesComponent implements OnInit {
heroes = HEROES;
}
- 해당 컴포넌트에 import 하여 변수로 할당
<!-- heroes.component.html -->
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="let hero of heroes">
<button type="button">
<span class="badge">{{hero.id}}</span>
<span class="name">{{hero.name}}</span>
</button>
</li>
</ul>
- 화면출력을 위해서는 *ngFor 을 이용하여 순회
- *ngFor은 항목을 반복하는 Angular 디렉티브 - 목록에 있는 항목마다 호스트 엘리먼트를 반복
- for ... of 문 사용하는 것과 비슷하게 사용 ( heroes의 값을 불러와서 hero 변수로 할당하여 사용 )
6. 클릭 이벤트
6-1. 클릭 이벤트 작성
<!-- heroes.component.html -->
<li *ngFor="let hero of heroes">
<button type="button" (click)="onSelect(hero)">
<!-- ... -->
- (click) = "함수(파라미터)" 를 작성해주어 해당 버튼 클릭시 동작하도록 지정
6-2. 클릭 함수 동작
// src/app/heroes/heroes.component.ts
import { HEROES } from '../mock-heroes';
export class HeroesComponent implements OnInit {
heroes = HEROES;
selectedHero?: Hero;
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
}
- selectedHero? 를 하여 해당 변수가 있을 수 있음을 선언
- onSelect 함수에 Hero형식의 파라미터를 보내주면 selectedHero 에 값을 넣어주는 함수 작성
- 버튼 클릭시 해당 함수가 동작하여 해당 변수에 값 할당하는 방식
6-3. ngIf
<!-- heroes.component.html -->
<div *ngIf="selectedHero">
<h2>{{selectedHero.name | uppercase}} Details</h2>
<div>id: {{selectedHero.id}}</div>
<div>
<label for="hero-name">Hero name: </label>
<input id="hero-name" [(ngModel)]="selectedHero.name" placeholder="name">
</div>
</div>
- class에서 옵션처리된 파라미터가 있을 때만 해당 div가 출력되도록 지정
- *ngIf 디렉티브는 해당 값이 존재할 때만 해당 엘리먼트를 표시하는 역할
6-4. Angular 제공 클래스 바인딩
<!-- heroes.component.html (히어로 목록) -->
<li *ngFor="let hero of heroes">
<!-- [class.some-css-class]="some-condition" 문법 -->
<button [class.selected]="hero === selectedHero" type="button" (click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span>
<span class="name">{{hero.name}}</span>
</button>
</li>
- [class.some-css-class] = "조건" 문법으로 스타일 지정
- [class.selected]="hero === selectedHero" [ selected ]라는 클래스가 hero와 selectedHero와 같을 때 추가됨
7. 서비스 (전역상태 저장소?)
7-1. 서비스 생성
ng generate service [서비스 이름]
- ng generate service 명령어로 해당 서비스 생성
7-2. 데이터 가져오기
//src/app/hero.service.ts
import { Injectable } from '@angular/core';
import { Hero } from './hero';
import { HEROES } from './mock-heroes';
@Injectable({
providedIn: 'root',
})
export class HeroService {
getHeroes(): Hero[] {
return HEROES;
}
constructor() { }
}
- @Injectable() 데코레이터는 서비스를 정의하는 메타데이터 객체를 인자로 받음
- @Component() 데코레이터에 메타데이터를 사용했던 것과 같은 방식
- providedIn 클래스가 주입되는 모든 곳에서 같은 인스턴스 공유
- 서비스에 데이터를 받아와서 해당 함수 호출 시 값을 반환하도록 메소드 추가
7-3. 서비스 주입
// src/app/heroes/heroes.component.ts
import { HEROES } from '../mock-heroes';
import { HeroService } from '../hero.service';
export class HeroesComponent implements OnInit {
heroes: Hero[] = [];
getHeroes(): void {
this.heroes = this.heroService.getHeroes();
}
constructor(private heroService: HeroService) {}
ngOnInit(): void {
this.getHeroes();
}
}
- 서비스를 import
- 프로퍼티 할당 값 지정
- constructor에 서비스타입 인자를 선언 (클래스 프로퍼티로 선언하면서 의존성 객체가 주입)
- 인스턴스를 찾아서 해당 인자로 전달
- OnInit에서 데이터를 받아오는 메소드를 동작하도록함
- 해당 메소드는 동기 방식으로 동작하므로 비동기로 동작하도록 처리 필요
7-4. 옵저버블 ( Observable )
- RxJS라이브러리가 제공하는 클래스 중 가장 중요한 클래스
//src/app/hero.service.ts
import { Injectable } from '@angular/core';
import { Hero } from './hero';
import { HEROES } from './mock-heroes';
import { Observable, of } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class HeroService {
getHeroes(): Observable<Hero[]> {
const heroes = of(HEROES);
return heroes;
}
constructor() { }
}
- of(HEROES)는 히어로 데이터를 Observable<Hero[]> 타입으로 반환
- of 함수는 인자들을 Observable 하게 변환하는 기능
7-5. 옵저버블을 사용하는 코드
// 기존 코드
getHeroes(): void {
this.heroes = this.heroService.getHeroes();
}
======================================================
// 옵저버블을 사용하는 코드
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes);
}
- 해당 값을 subscribe 를 사용하여 값을 반환하도록 해야 비동기 방식으로 동작
- subcribe 구을 통해 Observable이 동작하도록함
8. 네이게이션
8-1. AppRoutingModule 생성
ng generate module app-routing --flat --module=app
- ng generate module 명령어로 app-routing 모듈을 생성
- app-routing.module.ts 파일이 생성됨
인자 | 설명 |
--flat | 새로운 폴더를 만들지 않고 src/app폴더에 파일 생성 |
--module=app | ng generate 명령을 실행하면서 AppModule의 imports배열에 자동으로 추가 |
8-2. app-routing.module.ts
# 기존코드
//src/app/app-routing.module.ts
content_copy
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
imports: [
CommonModule
],
declarations: []
})
export class AppRoutingModule { }
#수정코드
//src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HeroesComponent } from './heroes/heroes.component';
const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: 'dashboard', component: DashboardComponent },
{ path: 'detail/:id', component: HeroDetailComponent },
{ path: 'heroes', component: HeroesComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
- RouterModule과 Routes 심볼을 로드
- 사용할 컴포넌트를 로드
- path: ' ' 은 root 경로를 의미하며, path값을 지정하여 각 해당되는 컴포넌트가 출력되도록 작성
- Routes의 프로퍼티
프로퍼티 | 설명 |
path | 브라우저 주소표시줄에 있는 URL과 매칭될 문자열을 지정 |
component | 라우터가 생성하고 화면에 표시할 컴포넌트를 지정 |
- @NgModul에 메타데이터를 지정하면 모듈이 생성될 때 라우터를 초기화 하면서 브라우저 주소 변화를 감지
- RouterModule.forRoot()에 routes인자를 넣어 실행한 결과를 지정하여 AppRoutingModule에 라우터 초기화
- exports로 RoterModule을 앱에서 사용할 수 있도록 지정
8-3. 라우팅 영역(RouterOutlet) 추가
<!-- src/app/app.component.html (router-outlet) -->
<h1>{{title}}</h1>
<router-outlet></router-outlet>
- <router-outlet> 은 라우팅 된 화면이 표시될 위치를 지정하는 엘리먼트
8-4. 네이게이션 링크 추가
<!-- src/app/app.component.html (router-outlet) -->
<h1>{{title}}</h1>
<nav>
<a routerLink="/heroes">Heroes</a>
</nav>
<router-outlet></router-outlet>
- routerLink는 RouterModule이 제공하는 RouterLink 디렉티브
- 엘리먼트를 클릭하면 네비게이션은 실행하여 해당 라우팅 경로로 이동
<button type="button" (click)="goBack()">go back</button>
goBack(): void {
this.location.back();
}
- 다음과 같이 함수를 만들어 뒤로가기 기능을 제공할 수 있
8-5. 라우팅 변수 추출하기
//src/app/app-routing.module.ts
const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: 'dashboard', component: DashboardComponent },
// id값을 url로 받는 부분
{ path: 'detail/:id', component: HeroDetailComponent },
{ path: 'heroes', component: HeroesComponent }
];
- { path: 'detail/:id', component: HeroDetailComponent },
- path값에 :id 를 통해 id라는 값을 할당
//ts
getHero(): void {
const id = Number(this.route.snapshot.paramMap.get('id'));
this.heroService.getHero(id)
.subscribe(hero => this.hero = hero);
}
- const id = Number(this.route.snapshot.paramMap.get('id'))
- route.snapshot은 컴포넌트가 생성된 직후에 존재하는 라우팅 규칙에 대한 정보를 담고 있는 객체
- paramMap을 사용하면 URL에 존재하는 라우팅 변수를 참조할 수 있음
- id 값에 해당하는 키를 참조 ( 라우팅 변수는 언제나 문자열 타입 )
9. 서버에서 데이터 받아오기 ( API )
9-1. HTTP 서비스 추가
// app.module.ts
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http'; // <-- HTTP통신 제공 서비스
@NgModule({
declarations: [
AppComponent,
HeroesComponent //새로 추가된 컴포넌트
]
imports: [
BrowserModule,
FormsModule,
HttpClientModule, // 해당 부분 추가
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
- HttpClient는 리모트 서버와 HTTP 통신을 하기 위해 Angular가 제공하는 서비스
9-2. HTTP API
//src/app/hero.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { catchError, map, tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class HeroService {
private heroesUrl = 'api/heroes'; // 웹 API 형식의 URL로 사용
// 옵션 예제, 토큰도 이렇게 명시하면 될듯
httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
// GET : 서버에 등록된 데이터 가져오기
getHeroes(): Observable<Hero[]> {
return this.http.get<Hero[]>(this.heroesUrl)
.pipe(
tap(_ => this.log('fetched heroes')),
catchError(this.handleError<Hero[]>('getHeroes', []))
);
}
// PUT : 수정하기
updateHero(hero: Hero): Observable<any> {
return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe(
tap(_ => this.log(`updated hero id=${hero.id}`)),
catchError(this.handleError<any>('updateHero'))
);
}
// POST: 서버에 새로운 데이터를 추가
addHero(hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.heroesUrl, hero, this.httpOptions).pipe(
tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)),
catchError(this.handleError<Hero>('addHero'))
);
}
// DELETE: 서버에서 데이터를 제거
deleteHero(id: number): Observable<Hero> {
const url = `${this.heroesUrl}/${id}`;
return this.http.delete<Hero>(url, this.httpOptions).pipe(
tap(_ => this.log(`deleted hero id=${id}`)),
catchError(this.handleError<Hero>('deleteHero'))
);
}
// 검색 : 해당되는 데이터 목록을 반환
searchHeroes(term: string): Observable<Hero[]> {
if (!term.trim()) {
// 입력된 내용이 없으면 빈 배열을 반환합니다.
return of([]);
}
return this.http.get<Hero[]>(`${this.heroesUrl}/?name=${term}`).pipe(
tap(x => x.length ?
this.log(`found heroes matching "${term}"`) :
this.log(`no heroes matching "${term}"`)),
catchError(this.handleError<Hero[]>('searchHeroes', []))
);
}
private handleError<T>(operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
// TODO: 리모트 서버로 에러 메시지 보내기
console.error(error); // 지금은 콘솔에 로그를 출력합니다.
// TODO: 사용자가 이해할 수 있는 형태로 변환하기
this.log(`${operation} failed: ${error.message}`);
// 애플리케이션 로직이 끊기지 않도록 기본값으로 받은 객체를 반환합니다.
return of(result as T);
};
}
private log(message) {
console.log(message)
}
constructor() {
private http: HttpClient,
}
}
- http.get 함수로 Observable 값을 반환
- http응답 body를 반환하는데 타입 지정이 없을 경우 json 객체로 처리
- catchError() 연산자는 Observable이 실패했을 때 실행되는 연산
- put 메소드는 [ URL, 수정할 데이터, 옵션 ] 3가지 인자를 받습니다.
- 옵션은 토큰, 타입 등 헤더에 담을 내용인듯?
- post 메소드도 [ URL, 수정할 데이터, 옵션 ] 3가지 인자를 받습니다.
- delete의 경우 제거할 데이터 값에 대한 정보 url 을 제공하며, 옵션적용하여 사용합니다.
9-3. AsyncPipe
<!-- heroes.component.html (히어로 목록) -->
//AsyncPipe
<li *ngFor="let hero of heroes$ | async" >
<!-- [class.some-css-class]="some-condition" 문법 -->
<button [class.selected]="hero === selectedHero" type="button" (click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span>
<span class="name">{{hero.name}}</span>
</button>
</li>
- heroes 배열 대신 heroes$를 사용
- $는 Observable을 뜻하는 관용적 표현
- ansync는 Observavle을 자동으로 구독하는 역할할
참조
- Angular Tutorial : https://angular.kr/tutorial
Angular 가이드
Angular 가이드
angular.kr
'웹 개발 공부' 카테고리의 다른 글
Angular 공부 ( RxJS ) (1) | 2023.01.11 |
---|---|
22년 탑10 Angular 개발자를 위한 VSCode 확장 기능 (0) | 2023.01.08 |
타입스크립트(TS, TypeScript) 공부 6 (0) | 2022.12.26 |
타입스크립트(TS, TypeScript) 공부 5 (0) | 2022.12.08 |
타입스크립트(TS, TypeScript) 공부 4 (0) | 2022.12.05 |