오전에 카페가서 공부하고
오후에 인터넷 연걸이 되어서 집에서 편안하게 작업을 했다.
북마크보다 마이리스트라는 비슷한 기능이 우선적으로 진행되어야 한다해서
마이리스트를 새벽까지 코딩하여 구현해냈다.
튜터님의 조언도 듣고, 팀원들의 도움도 받으면서 배운점이 많았다.
@Entity()
export class CollectionItem {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne((type) => Collection, (collection) => collection.collectionItems)
@JoinColumn({ name: 'collection_id' })
collection: Collection;
@ManyToOne((type) => Restaurant, (restaurant) => restaurant.collectionItems)
@JoinColumn({ name: 'restaurant_id' })
restaurant: Restaurant;
@ManyToOne((type) => Post, (post) => post.collectionItems)
@JoinColumn({ name: 'post_id' })
post: Post;
}
컬렉션아이템이라는 엔티티에 콜렉션과 레스토랑 포스트를 연결하여 다대다 관계를 만들었다.
해당 정보를 꺼내써야 한다는게 매우 어려웠다. 이부분을 튜터님과 팀원들이 도와주었다.
우선 컨트롤러에만 dto를 사용할수 있다는걸 배웠고, (서비스에서도 사용할 수 있는지 헷갈렸다)
스웨거에 대해서 배웠다.
// @UseGuards(AuthGuard('local'))
@Get('/:userId')
@ApiOkResponse({
description: 'MyList 전체조회(해당 유저의 맛집리스트만 불러오기)',
})
@ApiUnauthorizedResponse({ description: '전체조회 실패' })
async getMyLists(@Param('userId') userId: number) {
const myLists = await this.myListService.getMyList(userId);
return await myLists;
}
위와 같이 작성하면 localhost:3000/api에서 자세한 명세를 볼 수 있어 프론트와의 협업이 편이했다.
swagger
https://bit.ly/3Jy1Sgg
Nestjs 프로젝트에 Swagger 도입하기
프로젝트의 사이즈가 커질수록 REST API의 수가 정말 어마 무시하게 많아지는데요, API가 문서화되지 않는다면 어떤 API들이 있었나 헷갈려서 소스코드 이곳저곳을 뒤적거리는 일이 발생할 수 있습
velog.io
위 주소를 참고하였고 쉽고 편리했다.
실제 데이터없고 프론트에서 넘겨줘야할 정보는 더미데이터를 만드는 법을 배웠다.
@Post('/:collectionId')
@ApiOkResponse({
description: 'MyList 포스팅 추가',
})
@ApiUnauthorizedResponse({ description: 'MyList 포스팅 추가 실패' })
async myListPlusPosting(@Param('collectionId') collectionId: number) {
const postId = 1; //<<=== 이부분을 배웠다.
return this.myListService.myListPlusPosting(postId, collectionId);
}
}
유저아이디를 넘겨줘야 하는데 프론트에서 할 수 있는 부분이거나 유즈가드를 사용해 할 수 있을거라 생각되어,
더미데이터를 넘겨 테스트 하였다.
실제 작동할때 필요한 로직을 담당하는 서비스에선 정말 많은 것을 배웠다.
async getMyList(userId: number) {
try {
const myLists = await this.collectionRepository.find({
where: { user_id: userId, deletedAt: null, type: 'myList' },
select: { name: true, description: true, image: true },
});
return myLists;
} catch (err) {
console.error(err);
throw new InternalServerErrorException(
'Something went wrong while processing your request. Please try again later.',
);
}
}
where, select의 사용법을 배웠고 앞으로 코딩할때 많은 도움이 될 것 같다.
async createMyList(userId, name, type) {
try {
return this.collectionRepository.insert({
user_id: userId,
name,
type: 'myList',
});
} catch (err) {
console.error(err);
throw new InternalServerErrorException(
'Something went wrong while processing your request. Please try again later.',
);
}
}
insert의 사용방법을 배웠다. user_id는 콜렉션 엔티티에 있는 데이터이고 해당 부분에 받아온 userId=1을 넘겼다.
async updateMyList(
userId: number,
collectionId: number,
name: string,
image: string,
description: string,
visibility: 'public' | 'private',
) {
try {
// id와 type이 모두 일치하는 Collection 엔티티를 찾는다.
const myList = await this.collectionRepository.find({
relations: {
user: true,
},
});
if (!myList) {
throw new NotFoundException('마이리스트가 없습니다.');
}
await this.collectionRepository.update(
{ id: collectionId },
{
name,
image,
description,
visibility,
},
);
} catch (err) {
if (err instanceof NotFoundException) {
throw err;
} else {
console.error(err);
throw new InternalServerErrorException(
'Something went wrong while processing your request. Please try again later.',
);
}
}
}
이부분에선 다대다 관계에서 relations를 사용하여 해당 user데이터를 가져오는 방법을 배웠다.
추가적으로 console.error(err); 를 사용하여 상세한 에러를 보내라는 조언을 들었다.
async deleteMyList(id: number) {
try {
const result = await this.collectionRepository.softDelete(id); // soft delete를 시켜주는 것이 핵심입니다!
if (result.affected === 0) {
throw new NotFoundException('마이리스트가 없습니다.');
}
} catch (err) {
if (err instanceof NotFoundException) {
throw err;
} else {
console.error(err);
throw new InternalServerErrorException(
'Something went wrong while processing your request. Please try again later.',
);
}
}
}
위 부분에선 affected에 대해 배웠다. 해당 코드에 대한 설명의 GPT 답변을 첨부하였다.
영향을 받지 않았을때 0을 반환한다는 것이다.
마지막으로 가장 고생했던 마이리스트 포스팅 추가부분이다.
async myListPlusPosting(postId: number, collectionId: number) {
try {
await this.collectionItemRepository.insert({
post: { id: postId },
collection: { id: collectionId },
});
} catch (err) {
if (err instanceof NotFoundException) {
throw err;
} else {
console.error(err);
throw new InternalServerErrorException(
'Something went wrong while processing your request. Please try again later.',
);
}
}
}
}
위 부분에서 고생한 점은 2가지다.
콜렉션아이디랑 포스트아이디를 넘겨줘야 하는 로직이다.
첫번째, 어떻게 포스팅정보를 넘겨줘야하지? 위 로직처럼 넘긴다면 너무 간단한거 아닌가?
이걸 넘겨받는 팀원이 한마디할 것 같았다. 그래서 아래로직처럼 생각했다.
관계를 맺어서 포스트와 유저의 실제정보를 가져와서 이러쿵 저렁쿵.....
튜터님의 조언을 받아 만든 코드였지만 실제로 넘길 데이터를 가져오는게 어려웠고
사실 정답은 다양하지만 넘겨받을 팀원과 얘기를 통해 insert로 기능을 구현했다.
너무 쉬운건 정답이 아닌거 아냐? 라는건 방탈출에서도 종종 겪었고 실제상황에서 자주 착각하는 것이다.
너무 어렵게 돌아가려고 했다. 알고리즘문제도 풀다보면 엄청쉽게 풀수도있고 어렵게 풀수도 있따.
const myLists = await this.collectionRepository.find({
relations: {
collectionItems: {
post: true,
},
user: true,
},
where: {
user: {
id: userId,
},
},
});
두번쨰, 중간테이블(콜렉션아이템)에 어떻게 값을 넣을 수 있지?
await this.collectionItemRepository.insert({
post: { id: postId },
collection: { id: collectionId },
});
@Entity()
export class CollectionItem {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne((type) => Collection, (collection) => collection.collectionItems)
@JoinColumn({ name: 'collection_id' })
collection: Collection;
@ManyToOne((type) => Restaurant, (restaurant) => restaurant.collectionItems)
@JoinColumn({ name: 'restaurant_id' })
restaurant: Restaurant;
@ManyToOne((type) => Post, (post) => post.collectionItems)
@JoinColumn({ name: 'post_id' })
post: Post;
}
시행착오는 다음과 같다
postId: post, post:postId 등 작동원리를 모르니 별별 것을 다 시도해봤다.
그런데 팀원중 한분이 해결해주었다.
정확한 이해는 아니겠지만 이렇게 생각된다.
우선 엔티티에는 post: Post로 post에 Post라는 데이터 테이블이 담겼다고 생각한다.
그래서 왼쪽엔 post만 적었고, 오른쪽의 { id: postId }에서
id는 Post라는 테이블의 id값이고,
해당 아이디 값에 받아온 postId값을 넣는다.
정확한 개념일지 모르겠지만 이렇게 생각하니 좀 쉽게 느껴졌다.
결론적으로 정상작동 하였고 새벽2시에 모두 완료하니 오랜만에 기능을 완성하여 뿌듯함을 느꼈다.