궁금한 점 2

kukudas·2022년 2월 18일
0

Angular

목록 보기
11/15

질문 1

<!--
  src/app/app.component.html
-->
<h1>{{title}}</h1>
<nav>
	<a routerLink="/dashboard">Dashboard</a>
	<a routerLink="/heroes">Heroes</a>
</nav>
<router-outlet></router-outlet>
<app-messages></app-messages>
<!--
  src/app/heroes/heroes.component.html
-->
<h2>My Heroes</h2>
<ul class="heroes">
  <li *ngFor="let hero of heroes">
    <a routerLink="/detail/{{hero.id}}">
      <span class="badge">{{hero.id}}</span> {{hero.name}}
    </a>
  </li>
</ul>

여기서 http://localhost:4200인 app.component.html에 {{title}}이 있는데 http://localhost:4200/heroes인 heroes.component.html에서도 title이 보임??

답 :

<!--
  src/app/app.component.html
-->
<h1>{{title}}</h1>
<nav>
    <a routerLink="/dashboard">Dashboard</a>
    <a routerLink="/heroes">Heroes</a>
</nav>
<router-outlet></router-outlet>
<app-messages></app-messages>
// src/app/app-routing.module.ts
const routes: Routes = [
  { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
  { path: 'dashboard', component: DashboardComponent },
  { path: 'detail/:id', component: HeroDetailComponent },
  { path: 'heroes', component: HeroesComponent },
];

router-outlet 부분만 route에 따라서 변경이 되는거임.
따라서 http://localhost:4200/heroes면 http://localhost:4200 페이지에서 <router-outlet> 부분에 HeroesComponent가 들어가기 때문에 title도 보이는 것임.
앵귤러가 SPA(single page application)이어서 그럼.

질문2

<router-outlet>이 정확히 뭐임??

답:

router-outlet은 라우터 라이브러리에 있는 디렉티브로 라우터가 브라우저의 URL에 해당하는 컴포넌트를 넣어주는 곳임.

질문3

// src/app/hero-search/hero-search.component.ts
private searchTerms = new Subject<string>();

// Push a search term into the observable stream.
search(term: string): void {
  this.searchTerms.next(term);
}

에서 next하면 publish가 되는거임??

맞음.

질문4

Subject가 뭐임??

Subject는 publish 가능한 객체고 Observable()은 subscribe 가능한 객체임.

질문 5

ng serve 어디서 해야함

package.json 있는곳에서 해야함.

질문 6

subscribe가 뭐임??

어플리케이션 내에서 특정 이벤트가 발생했다는걸 pub/sub 구조로 전파하는거임.

class MessageService {
  ...
  // subscribe 가능한 subject 객체
  messageSubject = new Subject<string>();
  ...
  // subcribe할 대상을 observable로 리턴
  messageSubscription(): Observable<string> {
    return this.messageSubject.asObservable();
  }
  ...
  onMessage(message: string): void {
    // 메세지가 도착했을때 자체적으로 할것들 앞에서 먼저 처리하고
    ...
    this.messageSubject.next(message);
  }
}

이런식으로 보통 3개의 property를 정의해서
subject 객체가 pub/sub 할 수 있는 객체임.
앵귤러에서 서비스가 root provide하면 싱글톤으로 동작하니 저 서비스가 불려오게 되면 messageSubject 객체도 하나 있는거임. 그러면 웹소켓 같은거로 메시지가 오면 그 웹소켓클라이언트가 이 서비스의 onMessage()를 호출하면 this.messageSubject.next(message)이 불려오 게됨. 이때 next가 subject 객체에 publish 하게 되는 함수임.
그러면 이제 이 메시지가 도착했다 하는 이벤트가 필요한 컴포넌트 들에서 이 객체(Subject)를 subscribe 하고 있으면 그 subscribe 하는 모든 컴포넌트에 메시지가 도착했다는 이벤트가 전파가 됨.
예를 들어서 카톡을 예로 들때 다른 채팅방에서 메시지가 오면 상단에 방이름이랑 새로온 메시지랑 메시지 새로 온거만큼 개수도 뜨고 메시지가 몇개왔는지도 띄워주는데 이 과정에서 과정의 책임을 각 컴포넌트가 가져가는거임. 그러면 이제 수정이 필요할때 어디서 해야하는지 파악하기가 편함.

class MessageComponent {
  constructor(private messageService: MessageService) {
  }
  ...
  ngOnInit() {
    this.messageSubscription = this.messageService.messageSubscription().subscribe(() => {
      // 메세지 왔을때 할 동작정의
      ...
    });

  ngOnDestroy() {
    this.messageSubscription.unsubscribe();
  }
}

그래서 컴포넌트에서 이제 위 처럼 .subscribe 뒤에 콜백으로 동작할 것을 정해주면됨. 이제 이 subject는 이벤트가 발생할때마다 이벤트 발생사실을 계속 publish 해주는데(data stream)이 생기는거임.

질문 7

// src/app/hero.service.ts
getHeroes(): Observable<Hero[]> {
    return this.http.get<Hero[]>(this.heroesUrl)
      .pipe(
        tap(_ => this.log('fetched heroes')),
        catchError(this.handleError<Hero[]>('getHeroes', []))
      );
  }

그러면 Observable을 subscribe 가능한거면 누가 subscribe 하는거임?

위 코드의 서비스가 하는거임. RxJS는 비동기처리에도 사용가능한데 httpclient는 response가 도착하면 observable로 publish 해주고 이 서비스가 해당 httpclient의 response가 도착했다는 걸 위 코드처럼 subscribe해서 처리를 하는거임.
데이터스트림 한개짜리 one off 형식임. 이거는 unsubscribe안해도 httpclient가 알아서 처리해줌.
정확히는 이 서비스는 직접적으로 subscribe한게 아니라 구독할 수 있는 Observable<Hero[]> 리턴을 주는걸 정의해둔거고 필요한 곳에서 아래처럼 subscribe 하면됨.

// src/app/heroes/heroes.component.ts
getHeroes(): void {
    this.heroService.getHeroes()
        .subscribe(heroes => this.heroes = heroes);
  }

이제 HeroesComponent에서 구독했으니까 this.heroService.getHeroes()이 호출된 시점에서 http request가 날아가고 response가 도착하면 .subscribe(heroes => this.heroes = heroes) 내부(heroes => this.heroes = heroes)의 콜백이 실행되는거임.

추가로 async처리해주려면 Observable에 toPromise()라는 메소드도 있으니 await해주면됨.
위는 async하게 되긴하는데 callback 형식으로 처리하는거라서 필요에 맞게 선택하면 됨.

질문 8

getHeroes(): Observable<Hero[]> {
  return this.http.get<Hero[]>(this.heroesUrl)
    .pipe(
      tap(_ => this.log('fetched heroes')),
      catchError(this.handleError<Hero[]>('getHeroes', []))
    );
}

여기서 pipe()가 뭐임??

RxJS에 있는건데
pipe() 함수는 인자로 조합할 함수를 받아서 이 함수들을 조합한 새로운 함수를 생성하고, 옵저버블에서 데이터가 전달될 때 순서대로 실행합니다. 라고 https://angular.kr/guide/rx-library에 나와있음.

추가로 위 코드에서 http.get<T> 함수가 Observable<T\>를 리턴해주는데 이 Observable은 subscribe가 가능함. 이 subscription에 pipe를 달아주는 함수가 pipe()임. 이제 pipe는 subscribe하는 행위 전에 데이터에 일괄적인 작업을 가하는건데 pipe안에 데이터 스트림을 배열 다루듯이 가공해주는 여러 함수들이 있음.
rx가 같은 형식의 데이터 스트림이 이벤트가 발생할때마다 publish되는데 거기다 특정동작을 가해주는데 pipe함수임.
https://rxjs.dev/guide/operators 보면 할 수 있는 동작들이 정리되어있음.

of(1, 2, 3)
  .pipe(map((x) => x * x))
  .subscribe((v) => console.log(`value: ${v}`));

of는 순서대로 1, 2, 3이 publish되는 mock observable을 만들어주는데 pipe의 map()으로 제곱해주면 subscribe하는 곳에서는 제곱된 결과물이 publish되는것처럼 보임.

질문 9

constructor(
    private http: HttpClient,
    private messageService: MessageService) { }

이거 해서 HttpClient 넣으면 객체가 생기는거임? 아니면 인스턴스를 가져오는거임??

답:

MessageService 인스턴스가 messageService property로 HeroService에 생김.

질문 10

서비스 인스턴스는 언제 생김?

provide 된 모듈이 불려오면 생김.
MessageService라는 서비스가 root provide면 이 서비스 인스턴스가 될때 그 싱글톤으로 생긴거를 가져오고 아니면 이제 provide 된 모듈에서 가져옴.

질문 11

// app-routing.module.ts
const routes: Routes = [
  { path: 'detail/:id', component: HeroDetailComponent },
];

// hero-detial.component.ts
ngOnInit(): void {
    this.getHero();
  }

getHero(): void {
    const id = Number(this.route.snapshot.paramMap.get('id'));
    this.heroService.getHero(id)
      .subscribe(hero => this.hero = hero);
  }
  
  // hero.service.ts
getHero(id: number): Observable<Hero> {
    // For now, assume that a hero with the specified `id` always exists.
    // Error handling will be added in the next step of the tutorial.
    const hero = HEROES.find(h => h.id === id)!;
    this.messageService.add(`HeroService: fetched hero id=${id}`);
    return of(hero);
  }

이렇게 있을때 http://localhost:4200/detail/14 간다음에 14를 추출함?

  1. http://localhost:4200/detail/14 에 들어감.
  2. HeroDetailComponent 불러옴(여기서 HeroDetailComponent이 display되는거임)
  3. ngOnInit 실행
  4. getHero 실행
  5. id == 14
  6. heroService.getHero 호출
  7. heroService.getHero 에서 14번 hero 리턴
  8. this.hero = hero callback 실행

const id = Number(this.route.snapshot.paramMap.get('id'));
에서 paramMap이
url에다가 :id이런식으로 패러미터로 정의해둔게 들어있음.

0개의 댓글