티스토리 뷰

728x90

1. 이 포스트는 Email Client를 작성하는 시리즈의 일부이다.

 

2. 이 포스트는 angular-email.com이라는 REST email 서비스를 사용한다.

  2-1 기능이 제대로 구현이 안된 서비이지만 angular 클라이언트 작성용으로는 충분하고 넘친다.

  2-2 https://api.angular-email.com/auth/signedin 주소를 사용하여 현재 로그인 상태를 확인할 수 있다.

  2-3 세션을 쿠키로 관리하며 쿠키의 정보를 통해 서버의 세션을 확인할 수 있다.

 

3. 로그인 확인을 위한 주소가 있으니 이제 로그인 확인을 구현해야 한다.

  3-0 인증을 구현하려면 HttpClient가 필요하다. 따라서 HttpClientModule을 추가해야 한다.

    3-0-1 한 가지 혼동되는 부분이 있는데

    3-0-2 auth service는 auth 모듈에 들어있어 HttpClientModule을 auth 모듈에 추가하면 동작하지 않는다.

    3-0-3 서비스를 사용하는 모듈에 추가해야 한다. 이 경우는 app 컴포넌트에서 사용하므로 app.module.ts에 추가한다.

 

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

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    NgbModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

 

  3-1 인증 서비스에 이 url을 가지고 인증정보를 받아오는 부분을 작성한다.

    3-1-1 auth service에 signedIn이라는 인증상태를 확인하는 메소드를 추가한다.

    3-1-2 결과 값은 아래 SignedInResponse 인터페이스 형식으로 반환되므로 별도로 지정하였다.

    3-1-3 특이 한점은 signedIn$ 라는 BehaviorSubject 속성을 지정하였는데,

      3-1-3-1 어디에서든지 이 속성을 사용하여 상태가 바뀔 때마다 처리가 가능하도록 하였다. 

      3-1-3-2 signedIn 메소드를 보면 수신 정보가 있을 경우 사용자 이름과 로그인 상황을 업데이트 하도록 되어 있다.

    3-1-4 BehaviorSubject와 Subject의 차이점은 초기값을 어떤 것으로 할당하는가이다.

      3-1-4-1 BehaviorSubject는 처음 구독할 경우 초기값이 날아오게 된다. Subject는 데이터가 없으면 동작도 없다.

 

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, BehaviorSubject } from 'rxjs';
import { tap } from 'rxjs/operators'

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  signedIn$ = new BehaviorSubject<boolean>(null)
  username: string = ''

  url = 'https://api.angular-email.com/auth'
  constructor(private http: HttpClient) { }

  signedIn(): Observable<SignedInResponse> {
    return this.http.get<SignedInResponse>(`${this.url}/signedIn`).pipe(
      tap((response: SignedInResponse)=> {
        this.username = response.username
        this.signedIn$.next(response.authenticated)
      })
    )
  }
}

interface SignedInResponse {
  authenticated: boolean,
  username: string
}

 

4. 이제 처음 프로그램이 실행될 때 로그인 상태를 받아오는 부분인 app 컴포넌트를 업데이트한다.

  4-1 app 클래스이다.

    4-1-1 생성자에서 authService를 주입받고 있고 생성자 내에서 signedIn$ 속성을 참조하여 가지고 있다.

    4-1-2 객체 생성이 완료되면 ngOninit에서 서버로 로그인 확인을 요청한다. 반환된 값은 사용할 필요가 없다.

      4-1-2-1 서비스의 signedIn이 호출되면 내부적으로 이미 signedIn$의 next로 값이 보내졌기 때문이다.

 

import { Component, OnInit } from '@angular/core';
import { AuthService } from './auth/auth.service';
import { BehaviorSubject } from 'rxjs';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  public isMenuCollapsed = true;
  signedIn$: BehaviorSubject<boolean>

  constructor(private authService: AuthService) {
    this.signedIn$ = this.authService.signedIn$
  }

  ngOnInit(): void {
    this.authService.signedIn().subscribe(()=>{})
  }
}

 

  4-2 app template 이다.

    4-2-1 클래스의 signedIn$을 통해 데이터를 받아오는 부분을 async 파이프로 구현하였다.

      4-2-1-1 아주 많이 사용되는 방식이다.

    4-2-2 로그인 상태에 따라 보여질 메뉴를 처리하기 위해 각 메뉴에 ngIf를 사용하여 처리하였다.

 

<nav class="navbar navbar-expand-lg navbar-light bg-light mb-3">
  <a class="navbar-brand" [routerLink]="'.'">
    EM<i class="fas fa-paper-plane bg-dark text-white m-0"></i>IL
  </a>

  <!-- Step 3: Toggle the value of the property when the toggler button is clicked. -->
  <button class="navbar-toggler" type="button" (click)="isMenuCollapsed = !isMenuCollapsed">
    &#9776;
  </button>

  <!-- Step 2: Add the ngbCollapse directive to the element below. -->
  <div [ngbCollapse]="isMenuCollapsed" class="collapse navbar-collapse">
    <ul class="navbar-nav">
      <li class="nav-item">
        <!-- Step 4: Close the menu when a link is clicked. -->
        <a class="nav-link" [routerLink]="'.'" (click)="isMenuCollapsed = true">Inbox</a>
      </li>
    </ul>
    <ul class="navbar-nav ml-auto">
      <li class="nav-item">
        <!-- Step 4: Close the menu when a link is clicked. -->
        <a class="nav-link" [routerLink]="'.'" (click)="isMenuCollapsed = true" *ngIf="signedIn$ | async">Sign Out</a>
      </li>
      <ng-container *ngIf="!(signedIn$ | async)">
        <li class="nav-item">
          <a class="nav-link" [routerLink]="'.'" (click)="isMenuCollapsed = true">Sign Up</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" [routerLink]="'.'" (click)="isMenuCollapsed = true">Sign In</a>
        </li>
      </ng-container>
    </ul>
  </div>
</nav>

<div class="container">
  ...
  

 

5. 결과화면

  5-1 결과를 보면 당연히 로그인이 안되어 있으니 Sign Out이 보이지 않는다.

 

728x90
댓글