Guard로 비로그인 접근제한 만들기

소스코드

이 게시물에는 코드작성이 포함되어 있습니다. 소스코드를 받으신 후 진행해 주세요. MEAN Stack/개발 환경 구축에서 설명된 프로그램들(git, npm, atom editor)이 있어야 아래의 명령어들을 실행할 수 있습니다.

이 게시물의 소스코드는 기본사이트 만들기 / 회원가입 만들기에서 이어집니다.

angular-site.git 을 clone 한 적이 있는 경우: 터미널에서 해당 폴더로 이동 후 아래 명령어들을 붙여넣기합니다. 폴더 내 모든 코드가 이 게시물의 코드로 교체됩니다. 이를 원치 않으시면 이 방법을 선택하지 마세요.

git reset --hard
git pull
git reset --hard 73fd793
git reset --soft 0620bc7
npm install
atom .

angular-site.git 을 clone 한 적이 없는 경우: 터미널에서 코드를 다운 받을 폴더로 이동한 후 아래 명령어들을 붙여넣기하여 angular-site.git 을 clone 합니다.

git clone https://github.com/a-mean-blogger/angular-site.git
cd angular-site
git reset --hard 73fd793
git reset --soft 0620bc7
npm install
atom .

- Github에서 소스코드 보기: https://github.com/a-mean-blogger/angular-site/tree/73fd793eba32db7f113a84e5d3fa8d5e98f1edaf


사이트의 회원 목록을 보여주는 page를 만들고 이 페이지에는 로그인된 사용자만 접근할 수 있게 해봅시다.

로그인되지 않은 사용자가 접근하려고 하면 아래 그림처럼 에러메세지를 내게 됩니다.

Angular 2에서는 Guard라는 것으로 route에 접근을 허가/불허가 하게 할 수 있습니다.

Guard

Angular 2에서 guard class는 CanActivate라는 interface class를 구현(implement)하여, canActivate라는 함수에 접근을 허가할지(true 리턴), 접근을 거부할지(false 리턴) 결정하는 코드를 넣게 됩니다. Routing module의 canActivate 항목에 guard를 넣을 수 있고, 해당 route에 접근하면 guard의 canActivate함수의 결과에 따라 접근이 허가 또는 불허됩니다.
참고로 CanDeactivate 인터페이스를 구현해서 CanDeactivate 함수를 가진 guard클라스를 만들어 route의 canDeactivate 항목에 넣으면 CanActivate와 반대로 현재 page를 떠날 수 있을 지 없을 지를 결정하게 됩니다.

이번 강의에서는 가입된 회원들의 리스트를 보여주는 페이지를 만들고 CanActivate를 구현한 auth guard를 만들어서 로그인된 유저만 해당 route에 접근할 수 있게 하겠습니다.

폴더 구조


주황색은 변경된 파일, 녹색은 새로 생성된 파일, 회색은 변화가 없는 파일입니다.

아래 명령어로 guard.ts를 생성합니다.

$ ng g guard auth

아래 명령어로 user-index component도 생성해 줍시다.

$ ng g component user-index --spec false

--spec false를 options으로 사용하면 .spec.ts를 생성하지 않습니다.

코드

 우선 user-index.component 파일들입니다.

// src/app/user-index/user-index.component.ts

import { Component, OnInit } from '@angular/core';

import { User } from '../user';

import { UserService } from '../user.service';

@Component({
  selector: 'app-user-index',
  templateUrl: './user-index.component.html',
  styleUrls: ['./user-index.component.css']
})
export class UserIndexComponent implements OnInit {
  users: User[];

  constructor(
    private userService: UserService,
  ) {
    this.userService.index()
    .then(users =>
      this.users = users
    )
    .catch(response => null);
  }

  ngOnInit() {
  }

}

userService의 index함수를 통해 가입된 모든 회원들의 목록을 가져옵니다.

<!-- src/app/user-index/user-index.component.html -->

<div class="page page-users">

  <div class="contentBox">
    <h3 class="contentBoxTop">Users</h3>
    <ul>
      <ng-container *ngIf="users == null || users.length == 0">
        <div class="noData" colspan=100> There is no user yet.</div>
      </ng-container>
        <li *ngFor="let user of users">
          <a [routerLink]="['/','users',user.username]">{{user.username}}</a>
        </li>
    </ul>
  </div>

</div>

ngFor를 사용하여 리스트를 보여줍니다.

/* src/app/user-index/user-index.component.css */

ul{
  margin: 0;
  padding: 3px 12px;
}
ul:after {
  content: "";
  display: block;
  clear: both;
}
ul li{
  display: inline-block;
  list-style-type: none;
  float:left;
}
ul li a{
  display: inline-block;
  text-decoration:none;
  margin: 3px;
  background-color: #eee;
  padding: 3px 10px;
  border-radius: 3px;
}
ul li a:hover{
  background-color: #ccc;
}

css 설명은 생략합니다.

다음으로 오늘 강의의 핵심은 auth.guard.ts를 살펴보죠. ng g guard auth명령어를 사용하면 CanActivate를 구현(implement)해서 canActivate 함수를 가지고 있는 @Injectable() 클라스가 생성됩니다. 

나머지 필요한 부분을 채우면 됩니다.

// src/app/auth.guard.ts

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { AuthService } from './auth.service';

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(
    private router: Router,
    private authService: AuthService,
  ) { }

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    if(this.authService.isLoggedIn()){
      return true;
    }
    else{
      alert("Please login first");
      this.router.navigate(['login'],{ queryParams: { redirectTo: state.url } });
      return false;
    }
  }
}

우리는 authService의 isLoggedIn함수를 이용하여 현재 로그인이 된 상태인지 아닌지를 알 수 있습니다.

로그인이 된 상태라면 true를 리턴하여 접근을 허가하고, 아니라면 에러메세지를 띄운 후 login 페이지로 보냅시다. 그리고 redirectTo라는 query에 현재 url를 저장해서 로그인이 되면 바로 다시 돌아올 수 있게 하였습니다.

다음으로 app-routing.module.ts, app.module.ts를 살펴봅시다.

// src/app/app-routing.module.ts

import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { AuthGuard } from './auth.guard';

import { WelcomeComponent } from './welcome/welcome.component';
import { Error404Component } from './error404/error404.component';
import { LoginComponent } from './login/login.component';
import { UserNewComponent } from './user-new/user-new.component';
import { UserIndexComponent } from './user-index/user-index.component';

const routes: Routes = [
  { path: '',  component: WelcomeComponent },
  { path: 'login', component: LoginComponent },
  { path: 'users/new',  component: UserNewComponent },
  { path: 'users', canActivate: [AuthGuard], //1
    children: [
      { path: '', component: UserIndexComponent },
    ]
  },
  { path: '**', component: Error404Component },
];

@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
})
export class AppRoutingModule {}

1. users route에 canActivate로 AuthGuard가 들어간 것을 볼 수 있습니다. 이 users route에는 children을 만들어서 ' ' path에 UserIndexComponent를 넣었는데요, 이렇게 하면 users를 포함한 하위 route 전체에 이 AuthGuard가 적용됩니다. users route에는 나중에 페이지들이 더 추가될 예정입니다.

// src/app/app.module.ts

//...생략

import { AppRoutingModule }    from './app-routing.module';
import { AuthGuard } from './auth.guard'; //1

//...생략

import { UserNewComponent } from './user-new/user-new.component';
import { UserIndexComponent } from './user-index/user-index.component'; //2

@NgModule({
  declarations: [
    //... 생략
    UserNewComponent,
    UserIndexComponent, //2
  ],
  //...생략
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: RequestInterceptor,
      multi: true,
    },
    AuthGuard, //1
    UtilService,
    AuthService,
    UserService,
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

1. AuthGuard는 @Injectable()이므로 providers에 넣습니다.
2. Component는 declarations에 넣습니다.

실행 결과

Angular-CLI 명령어 사용해서 서버를 실행합시다. 

ng serve --open

로그인이 되지 않은 상태에서 Users 메뉴를 클릭해봅시다.

이제 로그인을 하고 다시 Users 메뉴를 클릭해 봅시다.

아무 문제 없이 페이지가 로딩되었습니다.

마치며..

눈썰미가 좋으신 분들은 Users 메뉴를 눌렀을 때 아래와 같은 화면이 잠깐 보이는 것을 눈치 채셨을 겁니다.

다음 강의에서는 이 부분에 대해 알아보도록 하겠습니다.

댓글

댓글쓰기

이 글에 댓글을 다시려면 SNS 계정으로 로그인하세요. 자세히 알아보기

UP