티스토리 뷰

728x90

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

  1-1 여기서는 잠시 쉬어가는 의미에서 로그인 성공 시 로그인 정보를 저장하고 공유하는 데이터 클래스를 작성한다.

  1-2 특별한 내용은 없고 로그인하면 LoginInfo 클래스에 유저 이름만 담을 예정이다. 

  1-3 프로그램에 따라서 토큰 정보가 들어갈 수도 있고, 비밀번호가 들어갈 수도 있는데 여기서는 유저이름만 저장한다.

 

2. LoginInfo 클래스를 하나 생성한다.

 

  2-1 필요한 속성들로 채운다

    2-1-1 여기에서는 로그인 여부와 유저이름을 저장하고 있다.

 

export class LoginInfo {
  isLogin: boolean = false
  username: string = ''
}

 

3. 서비스에서 로그인 때 예전에 username을 받아 왔었는데 대신에 LoginInfo 클래스를 사용하여 채운다.

  3-1 loginInfo 속성을 지정하여 공유데이터를 로그인, 로그아웃마다 적절하게 채운다.

  3-2 interceptor에서도 구현가능하지만 여기서는 그냥 service에서 작성했다.

 

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, BehaviorSubject } from 'rxjs';
import { tap } from 'rxjs/operators'
import { AuthCredential, LoginCredential } from './auth-responses';
import { LoginInfo } from './login-info';

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

  signedIn$ = new BehaviorSubject<boolean>(null)
  loginInfo: LoginInfo = new LoginInfo()

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

  private setLoginInfo(login: boolean, username: string) {
    this.loginInfo.isLogin= login
    this.loginInfo.username = username
  }


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

  signUp(crediential: AuthCredential): Observable<SignUpResponse> {
    return this.http.post<SignUpResponse>(`${this.url}/signup`, crediential).pipe(
      tap(({username})=> {
        this.setLoginInfo(true, username)
        this.signedIn$.next(true)
      })
    )
  }

  signIn(crediential: LoginCredential): Observable<any> {
    return this.http.post(`${this.url}/signin`, crediential).pipe(
      tap(({username}) => {
        this.setLoginInfo(true, username)
        this.signedIn$.next(true)
      })
    )
  }

  signOut() {
    return this.http.post(`${this.url}/signout`, {}).pipe(
      tap(() => {
        this.setLoginInfo(false, '')
        this.signedIn$.next(false)
      })
    )
  }

  checkDuplication(username: string) {
    return this.http.post(`${this.url}/username`, { username })
  }
}

interface SignedInResponse {
  authenticated: boolean,
  username: string
}

interface SignUpResponse {
  username: string
}

 

4. app 컴포넌트의 메뉴바에 로그인 한 경우 이름을 표기하는 로직을 작성한다.

  4-1 auth 서비스의 loginInfo를 app 컴포넌트에서 연결해 놓으면 상호 값의 변화가 연동이 된다.

  4-2 subject로 구현이 가능한 부분을 이렇게 call by reference로도 구현이 가능하다.

    4-2-1 다만 컴포넌트의 파괴와 생성이 잦은 경우는 별로 좋은 방법은 아니다.

  4-3 app 클래스에서 ngOnInit()에서 loginInfo를 연결한다.

 

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

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

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

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

 

  4-4 template에서 로그인 여부에 따라서 적절하게 유저 이름을 보여 주도록 하였다.

 

<nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-3">
  <a class="navbar-brand" [routerLink]="'.'">
    EM<i class="fas fa-paper-plane bg-light text-dark 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]="'/inbox'" (click)="isMenuCollapsed = true"
          [routerLinkActive]="'active'">Inbox</a>
      </li>
      <li class="nav-link active" *ngIf="loginInfo.isLogin">Hello! {{ loginInfo.username }}</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]="'/auth/signOut'" (click)="isMenuCollapsed = true"
          [routerLinkActive]="'active'" *ngIf="signedIn$ | async">Sign Out</a>
      </li>
      <ng-container *ngIf="!(signedIn$ | async)">
        <li class="nav-item">
          <a class="nav-link" [routerLink]="'/auth/signUp'" (click)="isMenuCollapsed = true"
            [routerLinkActive]="'active'">Sign Up</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" [routerLink]="'/auth/signIn'" (click)="isMenuCollapsed = true"
            [routerLinkActive]="'active'">Sign In</a>
        </li>
      </ng-container>
    </ul>
  </div>
</nav>

<div class="container">
  <router-outlet></router-outlet>
</div>

 

5. 결과화면이다.

 

728x90
댓글