회원가입 및 로그인 구현
/* NestJS 백엔드 */
/* user.controller.ts */
@Controller('user')
export class UserController {
PASSWORD_SALT = 10;
constructor(private readonly userService: UserService) {}
@Get('/login')
login(@Req() loginUser: { query: LoginUserDto }) {
return this.userService.login(loginUser.query);
}
@Post('/create')
async createUser(@Body() input: CreateUserDto) {
return this.userService.createUser(input);
}
}
/* NestJS 백엔드 */
/* user.service.ts */
@Injectable()
export class UserService {
PASSWORD_SALT = 10;
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
async login(input: LoginUserDto) {
const query = `SELECT * FROM user WHERE email = '${input.email}'`;
const validUser = await this.userRepository.query(query);
const validPassword = await bcrypt.compare(input.password, validUser[0].password);
if (validUser.length === 0) {
throw new HttpException('존재하지 않는 이메일입니다.', HttpStatus.BAD_REQUEST);
}
if (!validPassword) {
throw new HttpException('비밀번호가 일치하지 않습니다.', HttpStatus.BAD_REQUEST);
}
return {
status: 200,
message: '로그인이 완료되었습니다.',
result: {
email: validUser[0].email,
name: validUser[0].name,
age: validUser[0].age,
phone: validUser[0].phone,
},
};
}
async createUser(input: CreateUserDto) {
const email = input.email;
const hashedPassword = await bcrypt.hash(input.password, this.PASSWORD_SALT);
// const user = await this.userRepository.findOne({ where: { email } });
const query = `SELECT * FROM user WHERE email = '${email}'`;
const user = await this.userRepository.query(query);
console.log('user', user);
if (user.length !== 0) {
throw new HttpException('이미 등록된 이메일입니다.', HttpStatus.BAD_REQUEST);
}
// const result = await this.userRepository.save({
// ...input,
// password: hashedPassword,
// });
const query2 = `INSERT INTO user (email, password, name, age, phone) VALUES (?, ?, ?, ?, ?)`;
const values = [email, hashedPassword, input.name, input.age, input.phone];
await this.userRepository.query(query2, values);
return {
status: 200,
message: '회원가입이 완료되었습니다.',
};
}
}
/* React 프론트엔드 */
export const loginJWT_api = async (data: FormInputs1) =>
await axios.post("http://localhost:8080/auth/login", data).then(res => res.data);
export const userCreate_api = async (data: FormInputs2) =>
await axios.post("http://localhost:8080/user/create", data).then(res => res.data);
const login = useMutation({
mutationKey: ["login"],
mutationFn: (data: FormInputs1) => loginJWT_api(data),
onSuccess: () => {
alert("로그인이 완료되었습니다.");
navigate("/board");
reset1();
},
onError: (err: any) => {
alert(err.response.data.message);
},
});
const create = useMutation({
mutationKey: ["userCreate"],
mutationFn: (data: FormInputs2) => userCreate_api(data),
onSuccess: () => {
alert("회원가입이 완료되었습니다.");
setIsRegister(false);
reset2();
},
onError: (err: any) => {
alert(err.response.data.message);
},
});
JWT 적용, accessToken, refreshToken 생성
/* NestJS 백엔드 */
/* auth.module.ts */
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { UserService } from './user.service';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
@Module({
imports: [JwtModule.register({}), TypeOrmModule.forFeature([User])],
controllers: [AuthController],
providers: [AuthService, UserService],
})
export class AuthModule {}
/* NestJS 백엔드 */
/* auth.controller.ts */
@Controller('auth')
export class AuthController {
constructor(
private readonly userService: UserService,
private readonly authService: AuthService,
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
@Post('login')
async login(@Body() input: LoginUserDto, @Res() res: Response) {
const { email, password } = input;
const query = `SELECT * FROM user WHERE email = '${email}'`;
const user = await this.userRepository.query(query);
if (user.length === 0) {
throw new HttpException('이메일 또는 비밀번호가 일치하지 않습니다.', HttpStatus.BAD_REQUEST);
}
const validPassword = await bcrypt.compare(password, user[0].password);
if (!validPassword) {
throw new HttpException('이메일 또는 비밀번호가 일치하지 않습니다.', HttpStatus.BAD_REQUEST);
}
this.authService.setRefreshToken({ user: user[0], res });
const jwt = this.authService.getAccessToken({ user: user[0] });
// return res.status(200).send(jwt);
return res.status(200).json({
status: 200,
message: '로그인이 완료되었습니다.',
result: {
email: user[0].email,
name: user[0].name,
age: user[0].age,
phone: user[0].phone,
jwt,
},
});
}
}
/* NestJS 백엔드 */
/* auth.service.ts */
@Injectable()
export class AuthService {
constructor(private readonly jwtService: JwtService) {}
getAccessToken({ user }): string {
return this.jwtService.sign(
{
email: user.email,
},
{
secret: process.env.ACCESS_TOKEN_SECRET_KEY,
expiresIn: '5m',
},
);
}
setRefreshToken({ user, res }) {
const refreshToken = this.jwtService.sign(
{
email: user.email,
},
{
secret: process.env.ACCESS_TOKEN_SECRET_KEY,
expiresIn: '2w',
},
);
res.setHeader('Set-Cookie', `refreshToken=${refreshToken}`);
// res.cookie('refreshToken', refreshToken, {
// httpOnly: true,
// path: '/',
// maxAge: 60 * 60 * 24 * 14 * 1000, // 2주
// // secure: true, // HTTPS 사용 시 주석 해제
// // domain: 'your-domain.com', // 필요한 경우 도메인 설정
// });
}
}
다음 일정