티스토리 뷰
개요
- postgreSQL 데이터베이스에 실제로 데이터를 저장하고 꺼내오는 등 데이터 조작을 해볼 예정입니다.
- 요청이 들어오면 컨트롤러가 받아서 서비스에게 넘겨주고, 서비스는 해당 로직을 처리합니다. 이 때 데이터가 필요하다면 레포지토리를 통해 불러올 것이고 데이터를 저장해야 한다면 이 또한 레포지토리를 통할 것입니다.
- 레포지토리는 데이터를 알맞는 테이블과 매핑하여 데이터베이스를 조작합니다.
- 각 단계마다 데이터들은 DTO 객체로 감싸서 통신이 될 것이고 typeORM이 DTO 객체를 클래스 객체로 변환하여 데이터베이스와 통신합니다. 반대로 데이터베이스에서 받아온 데이터를 DTO로 변환해서 응답합니다.
DTO 생성
- DTO는 Data Transfer Object 의 약자입니다. 말 그대로 데이터를 담고 이동하는 객체입니다.
- 클래스 객체를 그대로 이용하는 것은 변조의 위험이나 데이터의 무결성을 헤칠 수 있고 또한 클래스에서 정의한 속성들 중 필요한 부분만을 사용할 수 있다는 점에 의의가 있습니다. 또한 클래스 속성이 변동되었을 때도 DTO는 영향을 덜 받을 수 있겠죠!
- 이러한 이유로 데이터는 DTO로 감쌀 것이고 최종적으로 레포지토리와 통신할 때 적절한 클래스와 매핑할 것입니다.
// user/dto/request.dto.ts
export class CreateUserDto {
username: string;
age: number;
}
- 우선 유저 생성에 필요한 DTO를 만들어볼까요?
- DTO를 만들 때 고민해야 하는 점은 실제로 요청을 받을 때 입력받아야 하는 것이 무엇일까? 하는 것입니다.
- User 클래스에는 속성으로 id, username, age, role 이 있는데 이 중 id는 데이터베이스에서 자동으로 부여되는 것이고 role 또한 default 값이 있으므로 따로 입력받을 필요가 없습니다. 그렇기 때문에 request DTO에는 username과 age만 있어도 되는 것이죠!
- 참고로 DTO는 class와 interface로 정의할 수 있는데 user는 인스턴스 객체를 만들 수 있어야 하기 때문에 class로 정의하였습니다.
// user/dto/response.dto.ts
import { UserRole } from '../user.entity';
export class ResponseDto {
username: string;
age: number;
role: UserRole;
}
- 위와 같이 요청시 필요한 데이터와 응답시 필요한 데이터는 다를 수 있습니다.
- 이것들을 각각 DTO로 만들어서 필요한 데이터만 감싸서 보내줄 수 있겠죠?!
Repository 의존성 주입
- Repository를 Service가 사용하려면 우선 repository를 주입받아야 합니다.
// user.service.ts
@Injectable()
export class UserService {
constructor(
@InjectRepository(UserRepository)
private userRepository: UserRepository,
) {}
getAllUsers(): string {
return '옛다! users info';
}
}
- 다른 프로바이더와 달리 repository는 주입할 때 @InjectRepository라는 데코레이터와 함께 사용합니다.
Create
// user.controller.ts
import { Body, Controller, Post } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/request.dto';
import { ResponseDto } from './dto/reponse.dto';
@Controller('user')
export class UserController {
constructor(private userService: UserService) {}
@Post()
createUser(@Body() requestDto: CreateUserDto): Promise<ResponseDto> {
return this.userService.createUser(requestDto);
}
}
- 요청 body에 데이터를 보낼 것이므로 @Body() 데코레이터를 사용하고 CreateUserDto 타입의 dto 데이터를 받아옵니다.
- 이 데이터는 사용자에게 직접 입력받는 것이고 지금은 Postman을 이용해서 json 형식으로 보낼 것입니다.
// user.service.ts
import { Body, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { UserRepository } from './user.repository';
import { CreateUserDto } from './dto/request.dto';
import { ResponseDto } from './dto/reponse.dto';
@Injectable()
export class UserService {
constructor(
@InjectRepository(UserRepository)
private userRepository: UserRepository,
) {}
async createUser(@Body() requestDto: CreateUserDto): Promise<ResponseDto> {
const user = this.userRepository.create(requestDto);
await this.userRepository.save(user);
const response = { ...user, role: UserRole.GHOST };
return response;
}
}
- 서비스에서는 controller에서 보내온 dto 객체를 이용해서 user 객체를 만든 후 repository를 통해 데이터베이스에 저장을 합니다.
- postman으로 유저를 생성해보겠습니다.
- json으로 requestDto 형식에 맞게 body에 입력을 하면 responseDto 형식으로 응답이 오는 것을 볼 수 있습니다.
- 참고로 nestjs는 요청 성공시 https status를 POST가 201, 그 외는 200을 기본으로 하고 있습니다. 이것을 요청별로 제어하려면 @HttpCode() 데코레이터를 이용하면 됩니다.
- insert 쿼리가 잘 생성이 되었구요!
- 데이터베이스에도 잘 반영이 되었네요!
Read
- 우선 데이터베이스에 유저를 몇 개 더 만들어두었습니다.
// user.controller.ts
@Get('/all')
getUsers(): Promise<ResponseDto[]> {
return this.userService.getUsers();
}
@Get('/:id')
getUser(@Param('id') id: string): Promise<ResponseDto> {
return this.userService.getUser(Number(id));
}
- getUsers() 는 모든 유저의 목록을 불러오는 API입니다. 따라서 반환 타입도 ResponseDto[] 죠!
- getUser()는 파라미터로 id를 받아서 해당 id인 유저 하나만 받아옵니다.
async getUsers(): Promise<ResponseDto[]> {
const user = await this.userRepository.find();
return user;
}
async getUser(id: number): Promise<ResponseDto> {
const user = await this.userRepository.findOneBy({ id: id });
if (!user) {
throw new NotFoundException(`${id} 회원은 존재하지 않습니다.`);
}
return user;
}
- 서비스단도 크게 다를 게 없습니다. 다만 단일 유저를 불러오는 API는 user가 존재하지 않을 경우를 대비해서 예외처리도 해보았습니다.
- /user/all 로 요청을 보내면 모든 유저 목록을 볼 수 있고
- 파라미터와 일치하는 유저도 잘 불러와지네요!
- 존재하지 않는 4번 회원을 보내달라고 요청하면 작성한 예외처리 문구와 함께 에러 코드가 자동으로 오는 것도 편리한 점인 것 같습니다!
Update
- 유저 리스트에서 1번 유저의 role을 admin으로 바꾸는 API를 구현해보겠습니다.
// user.controller.ts
@Patch('/:id')
updateUserRole(
@Param('id') id: string,
@Body('role') role: UserRole,
): Promise<ResponseDto> {
return this.userService.updateUser(Number(id), role);
}
- HTTP 메소드는 PATCH 를 사용합니다. PUT과 PATCH 는 둘 다 update 작업을 위한 메소드인데 PUT은 해당 컬럼의 값을 전부 바꿀 때 사용하고 PATCH는 특정 값만을 바꿀 때 사용할 수 있습니다. role 한 부분만 수정할 것이므로 PATCH를 사용합니다.
- 그리고 어떤 유저의 정보를 바꿀 것인지 알아야 하므로 식별자인 id를 파라미터로 받을 것이고 바꿀 데이터는 body로 받을 것입니다.
/// user.service.ts
async updateUser(id: number, role: UserRole): Promise<ResponseDto> {
const user = await this.userRepository.findOneBy({ id: id });
user.role = role;
await this.userRepository.save(user);
return user;
}
- 데이터베이스에서 유저 정보를 꺼내온 뒤 업데이트를 하고 다시 save를 하는 작업을 합니다.
- postman으로 테스트를 해보면 데이터베이스에 정상적으로 반영이 된 것을 볼 수 있습니다!
Delete
- 데이터베이스에서 데이터를 삭제하는 메소드에는 remove() 와 delete() 가 있습니다.
remove
- Removes a given entity or array of entities. It removes all given entities in a single transaction (in the case of entity, manager is not transactional). Returns the removed entity/entities.
await repository.remove(user)
await repository.remove([category1, category2, category3])
delete
- Deletes entities by entity id, ids or given conditions:
await repository.delete(1)
await repository.delete([1, 2, 3])
await repository.delete({ firstName: "Timber" })
- 공식문서에 따르면, remove()는 엔티티나 엔티티 배열을 인자로 받아서 해당 엔티티를 삭제합니다. 엔티티를 가지고 있을 때 사용할 수 있겠죠!
- delete()는 id 또는 특정 조건을 인자로 받아서 일치하는 엔티티를 삭제합니다.
- 저는 유저의 id를 받아서 해당 id인 유저를 삭제할 것이므로 delete() 메소드를 사용합니다.
// user.controller.ts
@Delete('/:id')
deleteUser(@Param('id') id: string): Promise<void> {
return this.userService.deleteUser(Number(id));
}
// user.service.ts
async deleteUser(id: number): Promise<void> {
await this.userRepository.delete(id);
}
- 반환값을 void로 했기 때문에 응답으로 오는 것은 없지만 http status code 가 200인 것으로 보아 정상적으로 요청과 응답이 오고갔음을 알 수 있습니다.
- 데이터베이스에서도 해당 유저가 잘 삭제 되었네요!
반응형
'개발냥이 > Nest.js' 카테고리의 다른 글
[Nest.js] 라이프사이클(LifeCycle), 유틸리티 클래스(Utility class) (0) | 2024.01.15 |
---|---|
[Nest.js] postgreSQL, typeORM 적용시켜보기 (2) | 2024.01.11 |
[Nest.js] 기본 구조 파악, Controller, Service 구현해보기 (1) | 2024.01.10 |
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- BFS
- 해시맵
- SQL
- 알고리즘
- 리액트
- JavaScript
- Algorithm
- Nest
- 자바bfs
- 자바dp
- java
- 자바트리
- 이분탐색
- 백준
- 스프링
- 형변환
- Queue
- Spring
- SQLD
- 프로그래머스
- 자바스크립트
- CS
- 자바
- 정렬
- JPA
- DP
- dfs
- 스프링부트
- Comparator
- 타입스크립트
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
글 보관함