본문 바로가기
캠프 개발일지

개발일지 - 9일차: 좋아요 기능 추가

by JHBang 2024. 3. 6.

코로나가 얼추 나은 줄 알았는데 목이 아직 아프다.. 

 

9일차에는 목표 페이지에 누를 수 있는 좋아요 기능을 추가했다.

 

이전 프로젝트에서 좋아요를 만들 기회가 있었지만, 직접 만들어 본 경험은 이번이 처음이다.

 

일단은 좋아요 정보를 담는 테이블이 필요하다는 생각이 들었다. 같은 사용자가 좋아요를 여러번 누를 때 좋아요 수가 계속 올라가면 안되기 때문이다.

 

테이블은 이런 식으로 구성했다.

 

@Entity
@Table(name = "likes")
class Like(
    @ManyToOne(fetch = FetchType.LAZY)
    @OnDelete(action = OnDeleteAction.CASCADE)
    @JoinColumn(name = "user_id")
    val user: User,

    @ManyToOne(fetch = FetchType.LAZY)
    @OnDelete(action = OnDeleteAction.CASCADE)
    @JoinColumn(name = "resolution_id")
    val resolution: Resolution
) {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "like_id")
    val id: Long? = null
}

 

user와 resolution을 n대1 관계로 가짐으로써 어떤 사용자가 어떤 resolution에 좋아요를 눌렀는지 확인할 수 있다.

 

 

 

컨트롤러는 아래와 같이 구성했다.

@RestController
@RequestMapping("api/likes")
class LikeController(
    private val likeService: LikeService
) {
    @PostMapping
    fun insertLike(
        @RequestBody likeRequest: LikeRequest
    ): ResponseEntity<Unit>{
        return ResponseEntity.ok(likeService.insertLike(likeRequest))
    }

    @DeleteMapping
    fun deleteLike(
        @RequestBody likeRequest: LikeRequest
    ): ResponseEntity<Unit>{
        likeService.deleteLike(likeRequest)
        return ResponseEntity.noContent().build()

    }
}

좋아요를 하는것과 취소하는 건 like 테이블에 데이터를 생성하거나 삭제하는 일이므로 @PostMapping과 @DeleteMapping을 사용했다.

 

여기서 사용자는 좋아요 버튼을 누르면 좋아요가 +1, 한번 더 누르면 좋아요를 취소하는 방식을 사용했는데, 한 메서드 내에서 처리할 수 있지만 이런 식으로 2개의 api로 분리한 이유가 있다.

일단 분리를 하지 않을 경우, PostMapping 요청 안에 좋아요 추가와 삭제 모두 구현해야 한다. 이는 Restful하지 못한 설계라고 할 수 있다.

 

아래는 로직이 구현되는 LikeService이다.

@Service
class LikeServiceImpl(
    private val userRepository: UserRepository,
    private val resolutionRepository: ResolutionRepository,
    private val likeRepository: LikeRepository
): LikeService {
    @Transactional
    override fun insertLike(request: LikeRequest) {
        val user = userRepository.findByIdOrNull(request.userId) ?: TODO("예외처리")
        val resolution = resolutionRepository.findByIdOrNull(request.resolutionId) ?: TODO("예외처리")
        if (!likeRepository.existsByUserAndResolution(user, resolution)){
            resolution.updateLikeCount(true)
            likeRepository.save(LikeResponse.from(user, resolution))
        }
        else{
            TODO("이미 좋아요를 눌렀을 때")
        }
    }

    @Transactional
    override fun deleteLike(request: LikeRequest) {
        val user = userRepository.findByIdOrNull(request.userId) ?: TODO("예외처리")
        val resolution = resolutionRepository.findByIdOrNull(request.resolutionId) ?: TODO("예외처리")
        likeRepository.findByUserAndResolution(user, resolution)
            ?.let {
                resolution.updateLikeCount(false)
                likeRepository.delete(it) }
            ?: TODO("예외처리")
    }
}

 

만약 사용자가 좋아요를 하지 않았을 경우에 (즉, like테이블에 존재하지 않을 경우) Resolution 엔티티에 있는 likeCount를 증가시키는 메서드 updateLikeCount를 실행한다. 이 메서드는 인자가 true일 때 likeCount를 +1, false일 때 likeCount를 -1 한다.

 

 

 

이런 식으로 좋아요 기능을 구현해 봤다. 요즘 컨디션이 안좋아 코드 치는게 죽을맛이다... 역시 건강해지는게 일단은 최우선인것 같다.