워크샵 상세 페이지에 찜 하기 기능을 추가했다.
디폴트(찜을 하지 않았을 때)는 빈 하트(♡) 상태로 있다가 찜 하기를 한 유저가 접속하면 채운 하트(♥)를 보여주려고 했다.
이와 같이 동적으로 찜 아이콘을 보여주려면 다음과 같은 로직이 필요하다.
현재 우리는 wishlist라는 테이블에 user_id와 workshop_id를 저장하고 있다.
해당 유저가 특정 워크샵 상세 페이지에 접속했을 때 해당 워크샵의 id와 user_id를 받아 wishlist 테이블에 일치한 데이터가 있는지 확인하는 것이다.
문제는 로그인을 하지 않은 유저가 상세 페이지를 확인했을 때의 로직이었다. 그래서 경우의 수를 3가지로 정리했다.
- 유저가 로그인 상태로 접속 시 → 해당 유저의 user_id와 workshop_id가 wishlist 테이블에 있는지 확인하여 빈 하트 or 채운 하트를 보여주기
- 유저가 비로그인 상태로 접속 시 → user_id가 없는 상태(=비로그인)를 인식하여 찜하기 버튼을 빈 하트로 보여주기
- 유저가 비로그인 상태로 접속한 후 찜하기 버튼을 눌렀을 때 → '로그인 후 이용 가능합니다' 경고창 노출
워크샵 상세 조회 API (controller)
@ApiResponse({
status: 200,
description: '성공',
})
@ApiOperation({ summary: '워크샵 상세 조회 api' })
@UseGuards(JwtUserPageGuard)
@Get('/:workshop_id')
async getWorkshopDetail(
@Param('workshop_id') workshop_id: number,
@CurrentUser() user: CurrentUserDto | boolean,
) {
// 워크샵 상세 정보 가져오기
const workshop = await this.workshopsService.getWorkshopDetail(workshop_id);
if (typeof user === 'boolean' && user === false) {
return { workshop, wish: false };
}
let isWish: boolean;
if (typeof user === 'object') {
isWish = await this.workshopsService.checkingWish(user.id, workshop_id);
}
if (isWish === false) {
return { workshop, wish: false };
}
return { workshop, wish: true };
}
user_id를 확인하기 위해 @UseGuards(JwtUserPageGuard)
데코레이터를 사용한다.
user가 false인 경우 비로그인 상태로 인식하고 wish 변수에 false를 할당한다. 만약 user가 객체 형태로 전달된다면 로그인한 유저라고 인식하고 찜하기 여부를 체크하는 함수로 전달한다.
JwtUserPageGuard 클래스
import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtUserPageGuard extends AuthGuard('refreshToken') {
canActivate(context: ExecutionContext) {
return super.canActivate(context);
}
// 만약 토큰이 불량 상태라면 user 는 false
handleRequest(err: any, user: any, info: any) {
if (!user) {
return false;
}
return user;
}
}
Service layer의 찜하기 여부를 판단하는 함수
// 워크샵 상세 정보를 가져올 때 로그인 한 유저가 있다면, 해당 유저가 워크샵을 찜 했는지 안했는지 여부를 확인
async checkingWish(user_id: number, workshop_id: number) {
const wishCheck = await this.wishRepository.findOne({
where: { user_id, workshop_id },
});
if (!wishCheck) {
return false;
}
return true;
}
axios
.get(`/api/workshops/${workshopId}`)
.then((res) => {
const workshop = res.data.data.workshop[0];
const wishCheck = res.data.data.wish; // false or true
프론트 통신은 axios를 이용하여 wish 데이터를 클라이언트로 보내준다.
<button type="button" class="btn btn-outline-primary wish" onclick="addToWish()"> ♡ </button>
하트 버튼을 클릭했을 때 해당 함수가 발동할 수 있도록 onclick 속성을 사용했다.
// 찜하기 유저에 있으면 하트 칠하기
if (wishCheck) {
document.querySelector('.wish').textContent = '♥';
} else {
document.querySelector('.wish').textContent = '♡';
}
wish 데이터가 true면 채운 하트, false면 빈 하트를 보여주도록 자바스크립트로 조작했다.
이커머스에서 워낙 기본적인 부분이라 간단하게 생각했는데, 은근히 고려해야 할 부분이 많았던 찜하기 기능이었다. user 로그인 여부와 찜하기 여부를 확인하기 위해 @UseGuards 데코레이터를 사용하는 부분이 조금 까다로웠던 것 같다.