유저 이름에 유니크한 값 주기(중복 금지)
두가지 방법이 있다.
- 레포지토리에서 findOne 메소드를 이용해서 이미 같은 유저 이름을 가진 아이디가 있는지 확인하고, 없다면 데이터를 저장하는 방법. 이 방법은 DB처리를 두 번 해줘야 함.
- DB레벨에서 만약 같은 이름을 가진 유저가 있따면 에러를 던져줌
2번째 방법으로 구현.
user.entity.ts 파일로 가서
import {
BaseEntity,
Column,
Entity,
PrimaryGeneratedColumn,
Unique,
} from 'typeorm';
@Entity()
@Unique(['username'])
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
username: string;
@Column()
password: string;
}
@Unique([’username’]) 을 사용하여 유니크한 값을 갖게 한다.
위와 같이 처리 할 때 서버는 이미 가지고 있는 아이디를 또 가입하게 된다면 500에러를 띄우고 메세지로는 “Internal server error” 메세지를 날려준다.
하지만 좀 더 명확한 메세지를 보내는 것이 더 좋을 것 같다. 그렇다면 어떻게?
try catch 구문을 통해 에러를 확실하게 명시해준다.
import {
ConflictException,
InternalServerErrorException,
} from '@nestjs/common';
import { Injectable } from '@nestjs/common/decorators/core/injectable.decorator';
import { DataSource, Repository } from 'typeorm';
import { AuthCredentialDto } from './dto/auth-credential.dto';
import { User } from './user.entity';
@Injectable()
export class UserRepository extends Repository<User> {
constructor(private dataSource: DataSource) {
super(User, dataSource.createEntityManager());
}
async createUser(authCredentialDto: AuthCredentialDto): Promise<void> {
const { username, password } = authCredentialDto;
const user = this.create({ username, password });
try {
await this.save(user);
} catch (error) {
if (error.code === '23505') {
throw new ConflictException('Existing username');
} else {
throw new InternalServerErrorException();
}
}
}
}
위에 나오는 error.code 는 중복 된 아이디를 작성하였을 때 발생하는 코드로 조건문을 통해 구분해준다.
비밀번호 암호화 하기
bcryptjs 모듈 사용하기
npm install bcryptjs --save
import * as bcrypt from 'bcryptjs'
repository에서 작업
import {
ConflictException,
InternalServerErrorException,
} from '@nestjs/common';
import { Injectable } from '@nestjs/common/decorators/core/injectable.decorator';
import { DataSource, Repository } from 'typeorm';
import { AuthCredentialDto } from './dto/auth-credential.dto';
import { User } from './user.entity';
import * as bcrypt from 'bcryptjs';
@Injectable()
export class UserRepository extends Repository<User> {
constructor(private dataSource: DataSource) {
super(User, dataSource.createEntityManager());
}
async createUser(authCredentialDto: AuthCredentialDto): Promise<void> {
const { username, password } = authCredentialDto;
// 랜덤한 salt 값을 포함한 해쉬값을 통해 암호화를 진행한다.
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('Existing username');
} else {
throw new InternalServerErrorException();
}
}
}
}
salt값을 새로 생성하며 해쉬함수에 패스워드와 함께 넣어주면 유니크한 해쉬함수가 저장된다.
ex)
id : GHGHgh
password : $2a$10$JQYn2m5zYOaybzYUMw8BteEb3TWEkFYILU8sPyw2JtPhLS37jZnPm
로그인 기능 구현
auth.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { AuthCredentialDto } from './dto/auth-credential.dto';
import { User } from './user.entity';
import { UserRepository } from './user.repository';
import * as bcrypt from 'bcryptjs';
import { UnauthorizedException } from '@nestjs/common/exceptions';
@Injectable()
export class AuthService {
constructor(
@InjectRepository(UserRepository)
private userRepository: UserRepository,
) {}
async signUp(authCredentialDto: AuthCredentialDto): Promise<void> {
return this.userRepository.createUser(authCredentialDto);
}
async signIn(authCredentialDto: AuthCredentialDto): Promise<String> {
const { username, password } = authCredentialDto;
const user = await this.userRepository.findOneBy({ username });
if (user && (await bcrypt.compare(password, user.password))) {
return 'login success';
} else {
throw new UnauthorizedException('login failed');
}
}
}
조건문을 통해 유저가 존재 한다면 bcrypt의 compare함수를 사용하여 입력한 패스워드와 해쉬화 된 비밀번호를 비교하여 로그인 처리를 해준다.
auth.controller.ts
import { Controller, Post, Body, ValidationPipe } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthCredentialDto } from './dto/auth-credential.dto';
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
@Post('/signup')
signUp(
@Body(ValidationPipe) authCredentialDto: AuthCredentialDto,
): Promise<void> {
return this.authService.signUp(authCredentialDto);
}
@Post('/signin')
signIn(@Body(ValidationPipe) authCredentialDto: AuthCredentialDto) {
return this.authService.signIn(authCredentialDto);
}
}
'Nest.js' 카테고리의 다른 글
[Nest.js] 인증된 유저만 게시물 보고 쓸 수 있게 만들기 (0) | 2023.01.30 |
---|---|
[Nest.js] Passport, JWT 이용해서 토큰 인증 후 유저 정보 가져오기 (0) | 2023.01.30 |
[Nest.js] 회원가입과 로그인 구현 1 (0) | 2023.01.27 |
[Nest.js] Nest.js + PostgreSQL + typeORM CRUD 구현하기 3 (0) | 2023.01.26 |
[Nest.js] Nest.js + PostgreSQL + typeORM CRUD 구현하기 2 (0) | 2023.01.26 |