본문 바로가기
💻 뚝딱뚝딱/북북클럽

[개발일지#005] 좋아요 도메인 개발 및 테스트

by 뚜루리 2025. 1. 20.
728x90
320x100

[사용기술]

Java, Spring Boot, Spring JPA, MySQL

 

[만들려는 것]

책을 위한 SNS.

 

[오늘 하려는 것]

좋아요(Likes) 도메인 개발 및 테스트

 


 

 

LikesRepository.java

package seulgi.bookbookclub.repository;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import seulgi.bookbookclub.domain.Likes;

import java.util.Optional;

@Repository
public class LikesRepository {
    @PersistenceContext
    private EntityManager em;

    //좋아요 추가
    public void save(Likes likes){
        em.persist(likes);
    }

    //좋아요 해제
    public void delete(Likes likes){
        em.remove(likes);
    }

    // 한 회원이 특정 타임라인에 좋아요 눌렀는지 조회
    public Optional<Likes> findLike(Integer memberSeq, Integer timelineSeq) {
        return em.createQuery("SELECT l FROM Likes l WHERE l.member.memberSeq = :memberSeq AND l.timeline.timelineSeq = :timelineSeq", Likes.class)
                .setParameter("memberSeq", memberSeq)
                .setParameter("timelineSeq", timelineSeq)
                .getResultStream()
                .findFirst();
    }

    // 특정 타임라인의 좋아요 개수 조회
    public Long countLikesByTimeline(Integer timelineSeq) {
        return em.createQuery("SELECT COUNT(l) FROM Likes l WHERE l.timeline.timelineSeq = :timelineSeq", Long.class)
                .setParameter("timelineSeq", timelineSeq)
                .getSingleResult();
    }
}
  • 좋아요를 추가하고 삭제하는 간단한 로직과 좋아요는 중복 좋아요가 불가능하므로 중복 좋아요를 확인할 수 있는 로직, 그리고 특정 타임라인의 좋아요 갯수를 조회 하는 로직을 만듦.

 

 

LikesService.java

package seulgi.bookbookclub.service;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import seulgi.bookbookclub.domain.Likes;
import seulgi.bookbookclub.domain.Member;
import seulgi.bookbookclub.domain.Timeline;
import seulgi.bookbookclub.repository.LikesRepository;
import seulgi.bookbookclub.repository.MemberRepository;
import seulgi.bookbookclub.repository.TimelineRepository;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class LikesService {

    private final LikesRepository likesRepository;
    private final MemberRepository memberRepository;
    private final TimelineRepository timelineRepository;

    // 좋아요 추가
    @Transactional
    public void addLike(Integer memberSeq, Integer timelineSeq) {
        Member member = memberRepository.findByMemberSeq(memberSeq);
        Timeline timeline = timelineRepository.findById(timelineSeq);

        likesRepository.findLike(memberSeq, timelineSeq).ifPresent(like -> {
            throw new IllegalStateException("이미 좋아요를 눌렀습니다.");
        });

        Likes like = new Likes(member, timeline);
        likesRepository.save(like);
    }

    // 좋아요 취소
    @Transactional
    public void removeLike(Integer memberSeq, Integer timelineSeq) {
        Likes like = likesRepository.findLike(memberSeq, timelineSeq)
                .orElseThrow(() -> new IllegalArgumentException("좋아요가 존재하지 않습니다."));
        likesRepository.delete(like);
    }

    // 특정 타임라인의 좋아요 개수 조회
    public Long countLikes(Integer timelineSeq) {
        return likesRepository.countLikesByTimeline(timelineSeq);
    }

}

 

  • 중복 좋아요가 불가능 함으로 ifPresent()을 사용해서 중복 좋아요 시에 예외 처리를 진행.
  • 좋아요 취소는 좋아요를 이미 누른 상태일 때만 가능하므로  orElseThrow()을 사용해서 좋아요를 누르지 않았는데 좋아요를 취소했을 때 예외 처리 진행.
    • ifPresent(): 값이 있으면 작업 실행, 없으면 무시.
    • orElseThrow(): 값이 있으면 반환, 없으면 예외 발생.

 

 

좋아요 도메인 테스트

package seulgi.bookbookclub.service;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;

import seulgi.bookbookclub.domain.Book;
import seulgi.bookbookclub.domain.Member;
import seulgi.bookbookclub.domain.Timeline;
import seulgi.bookbookclub.repository.BookRepository;
import seulgi.bookbookclub.repository.MemberRepository;
import seulgi.bookbookclub.repository.TimelineRepository;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
@Transactional
class LikesServiceTest {

    @Autowired private LikesService likesService;

    @Autowired private MemberRepository memberRepository;

    @Autowired private TimelineRepository timelineRepository;

    @Autowired private BookRepository bookRepository;

    @Test
    void 좋아요추가() {
        // given
        Member member = new Member("testMember", "1234", "닉네임");
        memberRepository.save(member);

        Book book = new Book("12345678910", "책제목", "글쓴이", "출판사");
        bookRepository.save(book);

        Timeline timeline = new Timeline(member, "테스트 글 내용", book);
        timelineRepository.save(timeline);

        // when
        likesService.addLike(member.getMemberSeq(), timeline.getTimelineSeq());

        // then
        Long likeCount = likesService.countLikes(timeline.getTimelineSeq());
        assertEquals(1, likeCount);
    }

    @Test
    void 좋아요중복방지() {
        // given
        Member member = new Member("testMember", "1234", "닉네임");
        memberRepository.save(member);

        Book book = new Book("12345678910", "책제목", "글쓴이", "출판사");
        bookRepository.save(book);

        Timeline timeline = new Timeline(member, "테스트 글 내용", book);
        timelineRepository.save(timeline);

        likesService.addLike(member.getMemberSeq(), timeline.getTimelineSeq());

        // when & then
        IllegalStateException exception = assertThrows(IllegalStateException.class, () -> {
            likesService.addLike(member.getMemberSeq(), timeline.getTimelineSeq());
        });
        assertEquals("이미 좋아요를 눌렀습니다.", exception.getMessage());
    }

    @Test
    void 좋아요취소() {
        // given
        Member member = new Member("testMember", "1234", "닉네임");
        memberRepository.save(member);

        Book book = new Book("12345678910", "책제목", "글쓴이", "출판사");
        bookRepository.save(book);

        Timeline timeline = new Timeline(member, "테스트 글 내용", book);
        timelineRepository.save(timeline);

        likesService.addLike(member.getMemberSeq(), timeline.getTimelineSeq());

        // when
        likesService.removeLike(member.getMemberSeq(), timeline.getTimelineSeq());

        // then
        Long likeCount = likesService.countLikes(timeline.getTimelineSeq());
        assertEquals(0, likeCount);
    }
}

 

 

테스트 성공

 

 

728x90
320x100