✅ 인증을 위한 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 |