데이터를 업데이트하려면 어떻게 해야할까 ?
service 에서 업데이트하려는 데이터를 모두 보내주면 될까?
update(id:number,new_email:string,new_password:string...etc) {
...
}
만약에 내가 업데이트 하려는 데이터 중 이메일이 없다면 ? 비밀번호가 없다면 어떻게 할 것인가 ? 그때마다 다르게 함수를 만들어 비슷한 동작을 하는 함수 여러개를 만들것인가?
이메일을 업데이트하는 함수, 비밀번호를 업데이트하는 함수, 두개를 동시에 업데이트 하는 함수..
이러한 방식은 너무 비효율적이다 !
그렇다면 이것은 어떤가? 객체로 묶어서 거기에 유효하는 데이터가 들어가 있다면 그것을 처리하는 것은 ?
그러면 코드가 이런식으로 정리될 것이다
update(id:number,attrs:Partial<User>) {
//Partial로 넣고싶은 속성만 골라서 넣을 수 있다
//동적으로 데이터를 넣을 수 있다!
}
Partial 이라는 데이터 타입을 이용하여 이런 코드로 해당 문제를 해결할 수 있다.
typescript 의 문법으로, 특정 타입의 부분집합을 만족하는 타입을 정의할 수 있는 데이터 타입이다 !
이제 다음으로 넘어가서 업데이트가 어떠한 과정을 거쳐 이루어져야하는지 알아보자.
업데이트가 어떠한 과정을 거치는지 알아보자 일단 데이터베이스에서 어떤 데이터를 업데이트 할 것인지 데이터베이스에서 업데이트 할 데이터를 찾아와야 한다. 다음으로 그 인스턴스를 받아서 업데이트를 한다. 그런 다음 해당 인스턴스를 이용하여 save를 호출한다. 저장 메서드는 업데이트에도 사용된다. typeorm은 업데이트가 맞는지 확인하고 업데이트 동작을 할 것이다 !
그러나 이러한 일련의 과정들은 너무나도 복잡하고 번거롭다. 왜냐고? 두번의 데이터베이스 이동이 있기 때문이다.업데이트 메서드를 사용하면 추가적인 왕복이 필요하지 않다!
일단 앞에서 작성한 동작을 코드로 옮겨보면 다음과 같다.
async update(id:number,attrs:Partial<User>) {
//Partial로 넣고싶은 속성만 골라서 넣을 수 있다
//동적으로 데이터를 넣을 수 있다!
const user = await this.findOne(id);
//service 메서드를 이용하여 유저데이터를 조회
if(!user){
throw new Error('User not found!');
}
//user데이터가 없을 경우를 예외처리함
Object.assign(user,attrs);
//이미 존재하는 오브젝트에 붙여넣기 함
return this.repo.save(user);
}
여기에서 우리는 update를 할때마다 처리할 데이터의 dto 또한 작성해주어야한다.
데이터의 유효성 검사를 하는 dto를 말이다. 그럼 이 dto는, update의 특성에 따라서 유동적으로 들어오는 데이터에 따라 검사를 안하는 항목이 있도록 코드를 작성해주어야한다. 그것을 알려주는 데코레이터가 바로 IsOptional 이다
코드는 다음과 같다.
import { IsEmail, IsString, IsOptional } from 'class-validator';
export class UpdateUserDto {
@IsEmail()
@IsOptional()
email: string;
@IsString()
@IsOptional()
password: string;
}
데이터를 지우는 과정에 있어서 우리는 앞에서 데이터를 업데이트와 할 때와 같은 고민을 할 수 있다.
remove vs delete 는 hook의 사용여부에 따라 다르다. delete 의 경우 해당 id를 가진 모든 사용자를 찾아 삭제한라는 메세지를 보낸다 그래서 매우 빠르다. 하지만, 이 메서드를 사용하면 엔티티와 연결된 모든 후크가 삭제된다. 우리가 remove와 관련된 hook을 사용하기 위해서는 remove를 사용해야한다.
대신, 데이터베이스에서 두번의 과정을 거쳐야 한다. 즉, 후크를 사용하기 위해서는 이런 번거로운 방식을 거쳐야한다는 것이다.
async remove(id:number) {
const user = await this.findOne(id);
if (!user){
throw new Error('user not found!')
}
return this.repo.remove(user);
}
우리는 지금까지 에러처리를 두곳에서 했다. 컨트롤러와 서비스이다.
서비스에서 전달받은 컨트롤러의 web socket 과 grpc는 service에서 전달받은 NotFoundException을 처리하는 방법을 알지못한다 이 일을 처리하는 것은 http 이기 때문이다.
그래서 서비스에서 에러를 발생시키는 것은 부적절하며, 해당 코드는 재사용하기에도 어려워진다.
그렇기 때문에 우리는 에러를 nest에서 제공하는 모듈을 사용하여 처리할 것이다.
users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
//사용자 저장소가 필요하다는 것을 종속성 주입 시스템에 알리는 것
import { User } from './user.entity';
@Injectable()
export class UsersService {
constructor(@InjectRepository(User) private repo: Repository<User>) {}
create(email: string, password: string) {
const user = this.repo.create({
email,
password,
});
return this.repo.save(user);
}
findOne(id: number) {
return this.repo.findOneBy({ id });
//해당하는 단 하나의 레코드
}
find(email: string) {
return this.repo.find({ where: { email } });
//해당하는 모든 레코드
}
async update(id: number, attrs: Partial<User>) {
//Partial로 넣고싶은 속성만 골라서 넣을 수 있다
//동적으로 데이터를 넣을 수 있다!
const user = await this.findOne(id);
//service 메서드를 이용하여 유저데이터를 조회
if (!user) {
throw new NotFoundException('User not found!');
}
//user데이터가 없을 경우를 예외처리함
Object.assign(user, attrs);
//이미 존재하는 오브젝트에 붙여넣기 함
return this.repo.save(user);
}
async remove(id: number) {
const user = await this.findOne(id);
if (!user) {
throw new NotFoundException('user not found!');
}
return this.repo.remove(user);
}
}
user.controller.ts
import {
Body,
Controller,
Post,
Param,
Query,
Patch,
Get,
Delete,
NotFoundException,
} from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { UsersService } from './users.service';
@Controller('auth')
export class UsersController {
constructor(private usersService: UsersService) {}
@Post('/signup')
createUser(@Body() body: CreateUserDto) {
this.usersService.create(body.email, body.password);
}
@Get('/:id')
async findUser(@Param('id') id: string) {
//service 의 id는 number이기 때문에 이것을 기억해야한다
//param으로 들어오는 데이터는 number가 될 수 있다.
const user = await this.usersService.findOne(parseInt(id));
if (!user) {
throw new NotFoundException('user not found');
}
return user;
}
@Get()
findAllUsers(@Query('email') email: string) {
return this.usersService.find(email);
}
@Delete('/:id')
removeUser(@Param('id') id: string) {
return this.usersService.remove(parseInt(id));
}
@Patch('/:id')
updateUser(@Param('id') id: string, @Body() body: UpdateUserDto) {
return this.usersService.update(parseInt(id), body);
}
}