메타코딩 SNS프로젝트

46. chapter 8. 좋아요 구현 - 좋아요, 좋아요 취소 API 생성하기

정현3 2022. 7. 8. 00:22

모델을 만들었으니 Repository, Service, Controller도 만들어주자

LikesRepository

package com.cos.photogramstart.domain.likes;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

public interface LikesRepository extends JpaRepository<Likes, Integer> {


    @Modifying
    @Query(value = "INSERT INTO likes(imageId, userId, createDate) VALUES(:imageId, :principalId, now())", nativeQuery = true)
    int mLikes(int imageId, int principalId);

    @Modifying
    @Query(value = "DELETE FROM likes WHERE imageId = :imageId AND userId = :principalId", nativeQuery = true)
    int mUnLikes(int imageId, int principalId);
}

LikesService

package com.cos.photogramstart.domain.likes;


import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@RequiredArgsConstructor
@Service
public class LikesService {

    private final LikesRepository likesRepository;

    @Transactional
    public void 좋아요(int imageId, int principalId) {

        likesRepository.mLikes(imageId, principalId);

    }

    @Transactional
    public void 좋아요취소(int imageId, int principalId) {

        likesRepository.mUnLikes(imageId, principalId);

    }
}

ImageApiController

package com.cos.photogramstart.web.api;

import com.cos.photogramstart.config.PrincipalDetails;
import com.cos.photogramstart.domain.Image.Image;
import com.cos.photogramstart.domain.likes.LikesService;
import com.cos.photogramstart.service.ImageService;
import com.cos.photogramstart.web.dto.CMRespDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
public class ImageApiController {

    private final ImageService imageService;
    private final LikesService likesService;

    @GetMapping("/api/image")
    public ResponseEntity<?> imageStory(@AuthenticationPrincipal PrincipalDetails principalDetails,
                                        @PageableDefault(size = 3)
                                        Pageable pageable) {
        Page<Image> images = imageService.이미지스토리(principalDetails.getUser().getId(), pageable);

        return new ResponseEntity<>(new CMRespDTO<>(1, "성공", images), HttpStatus.OK);
    }

    @PostMapping("/api/image/{imageId}/likes")
    public ResponseEntity<?> likes(@PathVariable int imageId,
                                   @AuthenticationPrincipal PrincipalDetails principalDetails) {
        likesService.좋아요(imageId, principalDetails.getUser().getId());
        
        return new ResponseEntity<>(new CMRespDTO<>(1, "좋아요성공", null), HttpStatus.CREATED);
    }

    @DeleteMapping("/api/image/{imageId}/likes")
    public ResponseEntity<?> unLikes(@PathVariable int imageId,
                                     @AuthenticationPrincipal PrincipalDetails principalDetails) {
        likesService.좋아요취소(imageId, principalDetails.getUser().getId());
        
        return new ResponseEntity<>(new CMRespDTO<>(1, "좋아요취소성공", null), HttpStatus.OK);
    }
}

-> 해당 컨트롤러는 Image에 딸려갈 것이기 때문에 주소경로도 image이고, Controller도 ImageApiController에 만들어주었다. 

-> LikesService를 DI하고, '좋아요'와 '좋아요취소' 메서드를 호출한다. 이때, Image의 Id와 User의 Id를 받아주어야 한다.

package com.cos.photogramstart.domain.likes;


import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@RequiredArgsConstructor
@Service
public class LikesService {

    private final LikesRepository likesRepository;

    @Transactional
    public void 좋아요(int imageId, int principalId) {

        likesRepository.mLikes(imageId, principalId);

    }

    @Transactional
    public void 좋아요취소(int imageId, int principalId) {

        likesRepository.mUnLikes(imageId, principalId);

    }
}

-> Service에서도 똑같이 받아준다.

package com.cos.photogramstart.domain.likes;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

public interface LikesRepository extends JpaRepository<Likes, Integer> {


    @Modifying
    @Query(value = "INSERT INTO likes(imageId, userId, createDate) VALUES(:imageId, :principalId, now())", nativeQuery = true)
    int mLikes(int imageId, int principalId);

    @Modifying
    @Query(value = "DELETE FROM likes WHERE imageId = :imageId AND userId = :principalId", nativeQuery = true)
    int mUnLikes(int imageId, int principalId);
}

-> Repository에 '네이티브 쿼리'를 작성한다.

package com.cos.photogramstart.web.api;

import com.cos.photogramstart.config.PrincipalDetails;
import com.cos.photogramstart.domain.Image.Image;
import com.cos.photogramstart.domain.likes.LikesService;
import com.cos.photogramstart.service.ImageService;
import com.cos.photogramstart.web.dto.CMRespDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
public class ImageApiController {

    private final ImageService imageService;
    private final LikesService likesService;

    @GetMapping("/api/image")
    public ResponseEntity<?> imageStory(@AuthenticationPrincipal PrincipalDetails principalDetails,
                                        @PageableDefault(size = 3)
                                        Pageable pageable) {
        Page<Image> images = imageService.이미지스토리(principalDetails.getUser().getId(), pageable);

        return new ResponseEntity<>(new CMRespDTO<>(1, "성공", images), HttpStatus.OK);
    }

    @PostMapping("/api/image/{imageId}/likes")
    public ResponseEntity<?> likes(@PathVariable int imageId,
                                   @AuthenticationPrincipal PrincipalDetails principalDetails) {
        likesService.좋아요(imageId, principalDetails.getUser().getId());
        
        return new ResponseEntity<>(new CMRespDTO<>(1, "좋아요성공", null), HttpStatus.CREATED);
    }

    @DeleteMapping("/api/image/{imageId}/likes")
    public ResponseEntity<?> unLikes(@PathVariable int imageId,
                                     @AuthenticationPrincipal PrincipalDetails principalDetails) {
        likesService.좋아요취소(imageId, principalDetails.getUser().getId());
        
        return new ResponseEntity<>(new CMRespDTO<>(1, "좋아요취소성공", null), HttpStatus.OK);
    }
}

-> 좋아요를 Controller로 돌아가서 응답성공시에 리턴할 값을 넣어주면 된다.

 

다 만들었으니 POSTMAN으로 테스트해보자

package com.cos.photogramstart.domain.Image;


import com.cos.photogramstart.domain.user.User;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.time.LocalDateTime;

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
public class Image { //N:1

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String caption; //해당 Image를 설명하는 영역
    private String postImageUrl; //Image을 전송받아서 그 사진을 서버의 특정 폴더에 저장 - DB에 그 저장된 경로를 INSERT

    @JsonIgnoreProperties({"images"})
    @JoinColumn(name = "userId")  // 1:1
    @ManyToOne(fetch = FetchType.EAGER)
    private User user; //1명의 User는 여러개의 Image를 만들어낼수 있다. - Image를 누가 올렸는지 알기위해 받은 USerObject

    //이미지 좋아요 기능 (추후에 업데이트)

    //이미지 좋아요 카운팅 (추후에 업데이트)

    //댓글 기능 (추후에 업데이트)
    private LocalDateTime createDate; //모든 DB에는 시간정보가 필요하다 - 데이터가 입력된 시간

    @PrePersist
    public void createDate() {
        this.createDate = LocalDateTime.now();
    }

    /**오브젝트를 콘솔에 출력할 때 문제가 될 수 있어서 User부분을 출력되지 않게 함
     * @Override
    public String toString() {
        return "Image{" +
                "id=" + id +
                ", caption='" + caption + '\'' +
                ", postImageUrl='" + postImageUrl + '\'' +
                ", createDate=" + createDate +
                '}';
    }
    **/
}
package com.cos.photogramstart.domain.likes;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

public interface LikesRepository extends JpaRepository<Likes, Integer> {


    @Modifying
    @Query(value = "INSERT INTO likes(imageId, userId, createDate) VALUES(:imageId, :principalId, now())", nativeQuery = true)
    int mLikes(int imageId, int principalId);

    @Modifying
    @Query(value = "DELETE FROM likes WHERE imageId = :imageId AND userId = :principalId", nativeQuery = true)
    int mUnLikes(int imageId, int principalId);
}

-> DELETE에는 당연히 넣을 필요가 없다. 다시한번 '좋아요' 테스트를 진행해보자.