본문 바로가기

Uber Eats

Uber Eats # 20 Metadata로 사용자 분기 처리하기

@UseGuards(AuthGuard)를 사용해서 User가 있을 때에만 접근가능하도록 해주었다. 

 

이번에는 Metadata를 사용해보자. 여기서 Metadata를 사용하는 이유는 사용자의 role에 따라 접근하도록 하기 위함이다.

 

@SetMetadata를 사용하는데 여기에는 2개의 인자를 가질 수 있다. 

하나는 key이고 다른 하나는 value이다.

 

@SetMetadata("role"UserRole.Owner)

핵심은 모든 resolver에서 metadata를 사용하는데 있다.

 

그러기 위해서 auth 아래에 auth.decorator.ts를 만들어서 사용한다.

함수형을 만들고 여기에 전달하는 인자는 UserRole을 enum 형태로 export 하여 만든다.

 

type AllowedRoles = keyof typeof UserRole | "Any";

 

아래는 UserRole enum이다.

 

export enum UserRole {

  Client = 'CLIENT',

  Owner = 'OWNER',

  Delivery = 'DELIVERY',

}

 

코드는 아래와 같다. 

 

import { SetMetadata } from '@nestjs/common';
import { UserRole } from 'src/users/entities/user.entity';

type AllowedRoles = keyof typeof UserRole | "Any";

export const Role = (roles: AllowedRoles[]) => SetMetadata('roles', roles);

 

user.resolvers.ts에서 me를 보려면 Role이 어떤것이든지 상관 없다고 나오도록 한다. 

 

@Query(returns => User)
  @Role(['Any'])
  me(@AuthUser() authUser: User) {
    return authUser;
  }

 

auth.module.ts는 아래와 같다.

 

import { Global, Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './auth.guard';

@Module({
  providers: [
    {
      provide: APP_GUARD,
      useClass: AuthGuard,
    },
  ],
})
export class AuthModule {}

auth.guard.ts는 아래와 같다.

 

import { UseGuards } from '@nestjs/common';
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { AuthUser } from 'src/auth/auth-user.decorator';
import { Role } from 'src/auth/auth.decorator';
import { AuthGuard } from 'src/auth/auth.guard';
import {
  CreateAccountInput,
  CreateAccountOutput,
} from './dtos/create-account.dto';
import { EditProfileInput, EditProfileOutput } from './dtos/edit-profile.dto';
import { LoginInput, LoginOutput } from './dtos/login.dto';
import { UserProfileInput, UserProfileOutput } from './dtos/user-profile.dto';
import { VerifyEmailInput, VerifyEmailOutput } from './dtos/verify-email.dto';
import { User } from './entities/user.entity';
import { UserService } from './users.service';

@Resolver(of => User)
export class UserResolver {
  constructor(private readonly usersService: UserService) {}

  @Mutation(returns => CreateAccountOutput)
  async createAccount(
    @Args('input') createAccountInput: CreateAccountInput,
  ): Promise<CreateAccountOutput> {
    return this.usersService.createAccount(createAccountInput);
  }

  @Mutation(returns => LoginOutput)
  async login(@Args('input') loginInput: LoginInput): Promise<LoginOutput> {
    return this.usersService.login(loginInput);
  }

  @Query(returns => User)
  @Role(['Any'])
  me(@AuthUser() authUser: User) {
    return authUser;
  }

  @Query(returns => UserProfileOutput)
  @Role(['Any'])
  async userProfile(
    @Args() userProfileInput: UserProfileInput,
  ): Promise<UserProfileOutput> {
    return this.usersService.findById(userProfileInput.userId);
  }

  
  @Mutation(returns => EditProfileOutput)
  @Role(['Any'])
  async editProfile(
    @AuthUser() authUser: User,
    @Args('input') editProfileInput: EditProfileInput,
  ): Promise<EditProfileOutput> {
    return this.usersService.editProfile(authUser.id, editProfileInput);
  }

  @Mutation(returns => VerifyEmailOutput)
  verifyEmail(
    @Args('input') { code }: VerifyEmailInput,
  ): Promise<VerifyEmailOutput> {
    return this.usersService.verifyEmail(code);
  }
}

코드 대로 localhost:3000/graphql에서 me를 실행하면 X-JWT가 없을 때 resource를 찾을 수 없다는 것을 볼 수 있고 guard가 잘 작동됨을 알 수 있다.