gengminy
갱미니의 코딩일지
gengminy
전체 방문자
오늘
어제
  • 분류 전체보기 (61)
    • 🚀 프로젝트 (16)
      • 🎸 고스락 티켓 (4)
      • 🌞 내친소 (5)
      • 🥁 두둥 (7)
    • 📡 백엔드 (31)
      • 🌱 Spring Boot (13)
      • 🐱 Nest.js (10)
      • ⭐ Node.js (8)
    • 🏭 Infra (11)
      • ⚙ 준비를 위한 준비 (2)
      • 🥑 AWS (3)
      • 🐳 Docker (3)
      • ⚡ Github Actions (3)
    • 🌊 프론트엔드 (1)
      • 🌌 React.js (1)
    • 😎 주저리 (2)

블로그 메뉴

  • 💻Github
  • 📸Instagram
  • ✨Blog

공지사항

인기 글

태그

  • JWT
  • oauth2
  • OAuth
  • nodejs
  • nest
  • Spring
  • AWS
  • github
  • docker
  • nestjs
  • 깃헙액션
  • SlackAPI
  • GithubActions
  • springboot
  • 슬랙알림
  • 네스트
  • JSON
  • 스프링부트
  • 도커
  • 스프링

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
gengminy

갱미니의 코딩일지

[NestJs] 따라하면서 배우는 NestJs - 6 (auth 모듈 구현)
📡 백엔드/🐱 Nest.js

[NestJs] 따라하면서 배우는 NestJs - 6 (auth 모듈 구현)

2022. 7. 7. 13:38

 

✅ 인증을 위한 auth 모듈 생성

nest g module auth
nest g controller auth --no-spec
nest g service auth --no-spec

 

 

✅ User Entity & Repository 구현 및 등록

📝 ./src/auth/user.entity.ts

import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User extends BaseEntity {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    username: string;

    @Column()
    password: string;
}

 

📝 ./src/auth/user.repository.ts

import { EntityRepository, Repository } from 'typeorm';
import { User } from './user.entity';

@EntityRepository(User)
export class UserRepository extends Repository<User> {}

 

📝 ./src/auth/auth.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { UserRepository } from './user.repository';

@Module({
    imports: [TypeOrmModule.forFeature([UserRepository])],
    controllers: [AuthController],
    providers: [AuthService],
})
export class AuthModule {}

 

📝 ./src/auth/auth.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { UserRepository } from './user.repository';

@Injectable()
export class AuthService {
    constructor(
        @InjectRepository(UserRepository)
        private userRepository: UserRepository,
    ) {}
}

 

📝 ./src/auth/auth.controller.ts

import { Controller } from '@nestjs/common';
import { AuthService } from './auth.service';

@Controller('auth')
export class AuthController {
    constructor(private authService: AuthService) {}
}

 

의존성 주입까지 끝내면 기본 셋팅이 끝난다

 

 

 

✅ 회원가입 기능 추가

 

📝 ./src/auth/dto/auth-credential.dto.ts

export class AuthCredentialDto {
    username: string;
    password: string;
}

User 정보를 넘기기 위한 DTO(Data Transfer Object)도 추가해준다

 

 

📝 ./src/auth/user.repository.ts

async createUser(authCredentialDto: AuthCredentialDto) {
    const { username, password } = authCredentialDto;
    const user = this.create({ username, password });

    await this.save(user);
}

 

📝 ./src/auth/auth.service.ts

async signUp(authCredentialDto: AuthCredentialDto): Promise<void> {
    return this.userRepository.createUser(authCredentialDto);
}

 

📝 ./src/auth/auth.controller.ts

@Post('/signup')
signUp(@Body() authCredentialDto: AuthCredentialDto): Promise<void> {
    return this.authService.signUp(authCredentialDto);
}

 

간단하게 구현 끝

 

 

✅ 유저 유효성 체크

📝 ./src/auth/dto/auth-credential.dto.ts

import { IsString, Matches, MaxLength, MinLength } from 'class-validator';

export class AuthCredentialDto {
    @IsString()
    @MinLength(4)
    @MaxLength(20)
    username: string;

    @IsString()
    @MinLength(4)
    @MaxLength(20)
    //영어랑 숫자만 가능하도록 하는 정규식
    @Matches(/^[a-zA-Z0-9]*$/, {
        message: 'password only accepts English and number',
    })
    password: string;
}

class-validator 에서 데코레이터 끌어와서 사용한다

비밀번호에서는 정규식을 사용할 수도 있다

골뱅이 몇개 찍는 거 만으로도 유효성 체크를 자동으로 해주는게 진짜 편리하다

 

 

📝 ./src/auth/auth.controller.ts

@Post('/signup')
signUp(
    @Body(ValidationPipe) authCredentialDto: AuthCredentialDto,
): Promise<void> {
    return this.authService.signUp(authCredentialDto);
}

authController에서 @Body 부분에 ValidationPipe 집어넣어주면 동작한다

 

 

✅ 유저에 유니크한 이름 부여하기

📝 ./src/auth/user.entity.ts

@Entity()
@Unique(['username'])
export class User extends BaseEntity {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    username: string;

    @Column()
    password: string;
}

@Unique 데코레이터로 특정 값이 DB에서 처리될 때 unique 한지 검사할 수 있다

여기서는 username 에 유니크한 값을 주었다

 

대신 이렇게만 처리하면 오류 발생 시 500 에러(Internal Server Error)를 내보내는데

어디선가 듣기로 500 에러는 절대 나와서는 안되는 것이라고 했다

그러니까 try catch로 따로 잡아서 처리를 해주어야 한다

 

 

📝 ./src/auth/auth.controller.ts

async createUser(authCredentialDto: AuthCredentialDto) {
    const { username, password } = authCredentialDto;
    const user = this.create({ username, password });

    try {
        await this.save(user);
    } catch (error) {
        if (error.code === '23505') {
            throw new ConflictException('Existring username');
        } else {
            throw new InternalServerErrorException();
        }
    }
}

23505(500)에 해당하는 에러를 잡아서 따로 처리

역시 예외 처리는 백엔드 기본 소양이다

 

 

✅ 비밀번호 암호와 (bcryptjs)

npm i bcryptjs --save
import * as bcrypt from 'bcryptjs';

bcrypt 모듈 추가

 

 

📝 ./src/auth/auth.controller.ts

async createUser(authCredentialDto: AuthCredentialDto) {
    const { username, password } = authCredentialDto;

    const salt = await bcrypt.genSalt();
    const hashedPassword = await bcrypt.hash(password, salt);

    const user = this.create({ username, password: hashedPassword });

    try {
        await this.save(user);
    } catch (error) {
        if (error.code === '23505') {
            throw new ConflictException('Existring username');
        } else {
            throw new InternalServerErrorException();
        }
    }
}

비밀번호를 sha256 hash 를 사용하여 변환해 저장하는 것은 동일하지만

salt 를 덧붙이게 되면

서로 다른 유저가 동일한 비밀번호를 사용할 때 암호가 뚫리는 문제를 방지

salt 라는 유니크한 값을 추가하여 각기 다르게 해싱된다

 

 

✅ 로그인 기능 구현

📝 ./src/auth/auth.service.ts

async signIn(authCredentialDto: AuthCredentialDto): Promise<string> {
    const { username, password } = authCredentialDto;
    const user = await this.userRepository.findOne({ username });

    if (user && (await bcrypt.compare(password, user.password))) {
        return 'Login Success';
    } else {
        throw new UnauthorizedException('Login Failed');
    }
}

 

📝 ./src/auth/auth.controller.ts

@Post('/signin')
signIn(@Body(ValidationPipe) authCredentialDto: AuthCredentialDto) {
    return this.authService.signIn(authCredentialDto);
}

bcrypt.compare(pw1, pw2)로 두 비밀번호를 비교함

간단하다

저작자표시 (새창열림)

'📡 백엔드 > 🐱 Nest.js' 카테고리의 다른 글

[NestJs] 따라하면서 배우는 NestJs - 8 (권한 부여, 유저와 게시글 관계 부여)  (0) 2022.07.09
[NestJs] 따라하면서 배우는 NestJs - 7 (JWT, passport 이용한 인증 구현)  (0) 2022.07.08
[NestJs] 따라하면서 배우는 NestJs - 5 (레포지토리 구현 및 DB 이용 CRUD)  (0) 2022.07.07
[NestJs] TypeORM 사용 시 RepositoryNotFoundError 해결하기  (0) 2022.07.06
[NestJs] 따라하면서 배우는 NestJs - 4 (Postgres, TypeORM 적용)  (0) 2022.07.05
    '📡 백엔드/🐱 Nest.js' 카테고리의 다른 글
    • [NestJs] 따라하면서 배우는 NestJs - 8 (권한 부여, 유저와 게시글 관계 부여)
    • [NestJs] 따라하면서 배우는 NestJs - 7 (JWT, passport 이용한 인증 구현)
    • [NestJs] 따라하면서 배우는 NestJs - 5 (레포지토리 구현 및 DB 이용 CRUD)
    • [NestJs] TypeORM 사용 시 RepositoryNotFoundError 해결하기
    gengminy
    gengminy
    코딩

    티스토리툴바