이전글 : NestJS TypeORM의 Many to Many 구현 2- 관계형 구성 및 User, Book의 CRUD구현
이제 Join Table을 이용하여 관계를 만들도록 하겠습니다.
// user_book_join.entity.ts : src/book/entities
import { User } from 'src/users/entities/user.entity';
import {
Column,
CreateDateColumn,
Entity,
ManyToOne,
PrimaryColumn,
} from 'typeorm';
import { Book } from './book.entity';
@Entity()
export class User_Book_Join {
// 여기서 PrimaryColumn은 userId와 bookId의 조합이다.
@PrimaryColumn({
nullable: false,
})
userId: number;
@PrimaryColumn({
nullable: false,
})
bookId: number;
@Column({
nullable: false,
})
book_review: string;
@CreateDateColumn({ readonly: true })
created_at: Date;
@ManyToOne(() => User, (user) => user.id)
user: User;
@ManyToOne(() => Book, (book) => book.id)
book: Book;
}
위 Entity는 user와 book을 연결해 주는 JoinTable 입니다. 다만 그대로 연결하는 것이 아닌 리뷰도 작성하여 저장할수 있습니다.
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateBookDto } from './dto/create-book.dto';
import { UpdateBookDto } from './dto/update-book.dto';
import { Book } from './entities/book.entity';
import { User_Book_Join } from './entities/user_book_join.entity';
@Injectable()
export class BookService {
constructor(
@InjectRepository(Book)
private readonly bookRepository: Repository<Book>,
@InjectRepository(User_Book_Join)
private readonly user_Book_JoinRepository: Repository<User_Book_Join>,
) {}
async create(createBookDto: CreateBookDto) {
const createData = this.bookRepository.create(createBookDto);
// 생성한 users레코드 삽입
await this.bookRepository.save(createData);
// 생성한 레코드를 Controller로 return
return createData;
}
async findAll() {
return await this.bookRepository.find();
}
async findOne(id: number) {
return await this.bookRepository.findOne(id);
}
async update(id: number, updateBookDto: UpdateBookDto) {
return await this.bookRepository.save({
id,
title: updateBookDto.title,
});
}
async remove(id: number) {
return await this.bookRepository.delete(id);
}
// 아래부터는 JoinTable 서비스 async getJoin() {
console.log('run getJoin');
try {
return await this.user_Book_JoinRepository.find({
where: {},
});
} catch (err) {
console.log('message : ', err);
}
}
async postJoin(userId: number, bookId: number, book_review: string) {
const createData = this.user_Book_JoinRepository.create({
userId,
bookId,
book_review,
});
await this.user_Book_JoinRepository.save(createData);
return createData;
}
async patchJoin(userId: number, bookId: number, book_review: string) {
const find = await this.user_Book_JoinRepository.find({
userId,
bookId,
});
const createData = this.user_Book_JoinRepository.create({
...find,
book_review,
});
await this.user_Book_JoinRepository.save(createData);
return createData;
}
async deleteJoin(userId: number, bookId: number) {
const find = await this.user_Book_JoinRepository.find({
userId,
bookId,
});
await this.user_Book_JoinRepository.delete({
userId,
bookId,
});
return find;
}
}
위 코드를 보면 JoinTable에 대한 CRUD 컨트롤러를 확인할수 있습니다. JoinTable의 고유키는 userId, bookId의 조합이기 때문에 해당 레코드를 찾기 위해서는 2개의 요소가 필요합니다.
// book.controller.ts : src/book/
import {
Body,
Controller,
Delete,
Get,
Param,
Patch,
Post,
Query,
} from '@nestjs/common';
import { BookService } from './book.service';
import { CreateBookDto } from './dto/create-book.dto';
import { UpdateBookDto } from './dto/update-book.dto';
@Controller('book')
export class BookController {
constructor(private readonly bookService: BookService) {}
@Post()
create(@Body() createBookDto: CreateBookDto) {
return this.bookService.create(createBookDto);
}
@Get()
findAll() {
return this.bookService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.bookService.findOne(+id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateBookDto: UpdateBookDto) {
return this.bookService.update(+id, updateBookDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.bookService.remove(+id);
}
// 아래부터는 JoiinTable 컨트롤 부분
@Get('/join/all')
async getJoin() {
console.log('active');
// return 'done';
return await this.bookService.getJoin();
}
@Post('/join')
async postJoin(
@Body('userId') userId: number,
@Body('bookId') bookId: number,
@Body('book_review') book_review: string,
) {
return await this.bookService.postJoin(userId, bookId, book_review);
}
@Patch('/join')
async patchJoin(
@Query('userId') userId: number,
@Query('bookId') bookId: number,
@Body('book_review') book_review: string,
) {
return await this.bookService.patchJoin(userId, bookId, book_review);
}
@Delete('/join')
async deleteJoin(
@Query('userId') userId: number,
@Query('bookId') bookId: number,
) {
return await this.bookService.deleteJoin(userId, bookId);
}
}
이제 JoinTable의 컨트롤러를 만들도록 하겠습니다. 물론 book폴더가 아닌 독립적인 폴더에 JoinTable컨트롤러와 서비스, entity를 만들수 있습니다. 하지만 book의 리뷰를 작성하는 경우는 book과 관련 되어있을때이기 때문에 같은 컨트롤러에 넣었습니다.
 |
사진1) book, user Record |
현재 존재하고 있는 책("Hello World")과 유저("Alex")입니다.
 |
사진2) Post결과 |
이미 존재하는 유저와 책에 대해서 리뷰를 작성하게 되면 정상적으로 DB에 저장됩니다.
 |
사진3) 존재하지 않는 user로 Post결과 |
 |
사진4) 에러 원인 |
사진3 처럼 존재하지 않는 유저(ID: 3)로 책 리뷰를 작성할려고 하면 MySQL에서 거부를 합니다. user테이블에 존재하지 않는 ID를 넣었다는 이유 때문 입니다.
이렇듯 관계형으로 만들면 DB에서 조회를 알아서 합니다. 다만 삭제할때 관계형으로 연결되어 있어서 JoinTable도 따로 지워야 한다는 번거로움이 있습니다. 이 문제는 CASCAD로 해결 할수 있습니다. 이는 다음 글을 작성할때 알려드리도록 하겠습니다.
위 예제 GitHub 링크 및 Branch : ManyToMany3
댓글
댓글 쓰기