async addUser(request: AddUserRequest): Promise<UserEntity> {
const newUser = this.userRepository.create({
email: request.email,
password: request.password,
name: request.name,
});
await this.userRepository.save(newUser);
return newUser;
}
TypeORM 의 create()
메서드를 이용해서 튜플을 생성했다.
참고로 repository.create() 메서드는 단순히 repository entity 의 객체를 생성하는 것이므로 async await 으로 처리할 필요는 없다.
이렇게 만들어진 데이터를 저장하기 위해서 this.userRepository.save(newUser)
을 사용하는 것이다. 여기는 DB에 접근하는 것이므로 async await 으로 비동기를 제어해줘야 한다.
회원가입, 로그인, 로그아웃과 UseGuards(JwtAuthGuard)
를 이용해 accessToken
확인하는 로직까지 완성했고, 이제 User(1) : Board(N) 관계를 가지고 로그인한 유저가 게시글을 등록하는 로직을 만들어보자.
User Entity
@OneToMany(() => BoardEntity, (board) => board.writer)
boards: BoardEntity[];
Board Entity
@ManyToOne(() => UserEntity, (user) => user.boards)
writer: UserEntity;
로그인한 유저가 글을 등록하게 만들고 싶기 때문에 앞서 만든 @UseGuards(JwtAuthGuard)
를 사용했다.
Board Controller
@HttpCode(HttpStatus.CREATED)
@UseGuards(JwtAuthGuard)
@Post('/insertBoard')
async insertBoard(@Req() req: Request, @Body() request: InsertBoardRequest) {
const decodedUser = this.jwtService.decode(req.cookies['jwt']);
const user = await this.userService.fetchOneUser(decodedUser['id']);
await this.boardService.insertBoard(user, request);
}
Board Service
async insertBoard(user: UserEntity, request: InsertBoardRequest) {
const newBoard = this.boardRepository.create({
title: request.title,
content: request.content,
writer: user,
});
await this.boardRepository.save(newBoard);
}
Board Controller
에서 받은 쿠키를 decode 하는 작업을 포함시켰다. 이딴식으로 코드를 짜게 되면 매번 컨트롤러를 호출할 때마다 decode 하는 중복 코드가 발생한다. 이를 해결하기 위해서 Custom Decorator
를 만들었다.
export class Payload {
@ApiProperty()
readonly id: number;
@ApiProperty()
readonly email: string;
@ApiProperty()
readonly name: string;
}
import {
createParamDecorator,
ExecutionContext,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Payload } from 'src/modules/auth/dto/payload.dto';
export const UserPayLoad = createParamDecorator(
async (data: string, ctx: ExecutionContext) => {
try {
const jwtService = new JwtService({});
const request = ctx.switchToHttp().getRequest();
const cookie = data
? await request.cookies?.['jwt']
: await request.cookies;
console.log(cookie);
const jwt: Payload = await jwtService.verifyAsync(cookie.access_token);
console.log(jwt);
return jwt;
} catch (error) {
throw new HttpException('Unauthorized', HttpStatus.UNAUTHORIZED);
}
},
);
이랬더니, console.log(cookie) 값은 잘 나온다. 근데 뒤에서 자꾸 jwt를 내놓으라고 했고, console.log(cookie)는 jwt='액세스토큰값' 이 나오는데 왜 그럴까 생각했다.
그러다 내가 만든 cookie의 이름이 jwt 였다는게 기억났고,
@Post('/login')
async login(@Body() request: AuthenticateRequest, @Res() res: Response) {
const user = await this.userService.getByEmail(request.email);
this.authService.getAuthenticatedUser(request.email, request.password);
const accessToken = await this.authService.getAccessToken(user.email);
this.logger.debug(accessToken);
res.setHeader('Authorization', 'Bearer ' + accessToken);
// 이부분!!!
res.cookie('jwt', accessToken, {
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000,
});
return res.send({
message: 'success',
});
}
const const jwt: Payload = await jwtService.verifyAsync(cookie.jwt)
이렇게 수정했다.
그랬더니
[Nest] 28265 - 08/03/2023, 16:18:27 ERROR [ExceptionsHandler] secret or public key must be provided
JsonWebTokenError: secret or public key must be provided
at /Users/jin/Desktop/JIN/STUDY/solo-study/board-project/node_modules/jsonwebtoken/verify.js:113:19
at getSecret (/Users/jin/Desktop/JIN/STUDY/solo-study/board-project/node_modules/jsonwebtoken/verify.js:97:14)
at Object.module.exports [as verify] (/Users/jin/Desktop/JIN/STUDY/solo-study/board-project/node_modules/jsonwebtoken/verify.js:101:10)
at /Users/jin/Desktop/JIN/STUDY/solo-study/board-project/node_modules/@nestjs/jwt/dist/jwt.service.js:43:53
at new Promise (<anonymous>)
at JwtService.verifyAsync (/Users/jin/Desktop/JIN/STUDY/solo-study/board-project/node_modules/@nestjs/jwt/dist/jwt.service.js:43:16)
at /Users/jin/Desktop/JIN/STUDY/solo-study/board-project/src/decorators/userPayload.decorator.ts:15:43
at processTicksAndRejections (node:internal/process/task_queues:95:5)
앞서 설정해놓았던 secret 키를 추가함으로써 해결했다.
const jwt: Payload = await jwtService.verifyAsync(cookie.jwt, {
secret: process.env.JWT_SECRET,
});
이제 목표였던 로그인한 사용자가 게시글 올리기
를 위한 Board controller 를 수정했다.
@HttpCode(HttpStatus.CREATED)
@UseGuards(JwtAuthGuard)
@Post('/insertBoard')
async insertBoard(
@UserPayLoad() payload: Payload,
@Body() request: InsertBoardRequest,
) {
const user = await this.userService.fetchOneUser(payload.id);
await this.boardService.insertBoard(user, request);
}
}
Entity의 Column 개수보다 부족한 수의 인자를 save 할 때,
[Nest] 31357 - 15/03/2023, 03:53:40 ERROR [ExceptionsHandler] null value in column "STATUS" of relation "PRODUCT_REQ_LIST" violates not-null constraint
QueryFailedError: null value in column "STATUS" of relation "PRODUCT_REQ_LIST" violates not-null constraint
at PostgresQueryRunner.query (/Users/jin/Desktop/JIN/STUDY/solo-study/board-project/src/driver/postgres/PostgresQueryRunner.ts:299:19)
at processTicksAndRejections (node:internal/process/task_queues:95:5)
at InsertQueryBuilder.execute (/Users/jin/Desktop/JIN/STUDY/solo-study/board-project/src/query-builder/InsertQueryBuilder.ts:163:33)
at SubjectExecutor.executeInsertOperations (/Users/jin/Desktop/JIN/STUDY/solo-study/board-project/src/persistence/SubjectExecutor.ts:428:42)
at SubjectExecutor.execute (/Users/jin/Desktop/JIN/STUDY/solo-study/board-project/src/persistence/SubjectExecutor.ts:137:9)
at EntityPersistExecutor.execute (/Users/jin/Desktop/JIN/STUDY/solo-study/board-project/src/persistence/EntityPersistExecutor.ts:197:21)
at ProductReqListService.insertProductReqList (/Users/jin/Desktop/JIN/STUDY/solo-study/board-project/src/modules/productReqList/services/productReqList.service.ts:30:12)
at UserService.requestProduct (/Users/jin/Desktop/JIN/STUDY/solo-study/board-project/src/modules/user/services/user.service.ts:250:12)
at /Users/jin/Desktop/JIN/STUDY/solo-study/board-project/node_modules/@nestjs/core/router/router-execution-context.js:46:28
at /Users/jin/Desktop/JIN/STUDY/solo-study/board-project/node_modules/@nestjs/core/router/router-proxy.js:9:17
이런 에러메세지를 만났다. Status Column 이 null
값이라 PRODUCT_REQ_LIST 테이블이 not-null을 위반한다는 말이다.
productReqList.entity.ts
@Entity('PRODUCT_REQ_LIST')
export class ProductReqListEntity {
@PrimaryGeneratedColumn({ name: 'PRODUCT_REQ_LIST_ID' })
@Generated('increment')
id: number;
@Column({ name: 'PRODUCT_NAME' })
productName: string;
@Column({ name: 'PRICE' })
price: number;
@Column({ name: 'STATUS' })
status: string;
@Column({ name: 'REASON' })
reason: string;
@ManyToOne(() => UserEntity, (user) => user.productReqList)
@JoinColumn({ name: 'USER_ID' })
user: UserEntity;
@ManyToOne(() => EmployeeEntity, (employee) => employee.productReqList)
@JoinColumn({ name: 'EMPLOYEE_ID' })
employee: EmployeeEntity;
@ManyToOne(() => BrandEntity, (brand) => brand.productReqList)
@JoinColumn({ name: 'BRAND_ID' })
brand: BrandEntity;
}
찾아보니 default 값을 넣어줌으로써 null이 아니게 만들면 되더라.
@Column({ name: 'STATUS', default: '' })
status: string;
@Column({ name: 'REASON', default: '' })
reason: string;
해결!!