Wanna be Brilliant Full-Stack Developer

SpringBoot 구독, 구독취소 API 만들기 본문

Back-End/Spring Boot

SpringBoot 구독, 구독취소 API 만들기

Flashpacker 2022. 2. 14. 13:53


목표: 실제로 구독 하기와 구독 취소하기 API를 만들어 보려고한다!

왜 API에대 만드냐면 데이터만 리턴하는 컨트롤러를 만들것이기떄문에 

어떤 페이지나 파일을 리턴하는것이 아니라 데이터만 리턴하는 컨트롤러는 API컨트롤러라고불린다! 

 

구독하기 위해서는 누구를 구독한다는 정보뿐만 아니라 (ToUser) , FromUser(누가 구독하는지) 를 알아야한다.

지금은 누가누구를 구독하는지를 짤거냐면 현재로그인 하는 사람이 누구를 구독하는것을 만들것이다. \

package com.cos.photogramstart.web.api;

import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import com.cos.photogramstart.config.auth.PrincipalDetails;

@RestController
public class SubscribeApiController {
	
	@PostMapping("/api/subscribe/{toUserId}")
	public ResponseEntity<?> subscribe(@AuthenticationPrincipal PrincipalDetails principalDetails, @PathVariable int id) {
		return null;
	}
	
	@DeleteMapping("/api/subscribe/{toUserId}")
	public ResponseEntity<?> unsubscribe(@AuthenticationPrincipal PrincipalDetails principalDetails, @PathVariable int id) {
		return null;
	}
}

그러면 이제 실제로 정보를 받아서 데이터베이스에 인서트하거나 딜리트 하기 위해서는 서비스가 필요하다 

인서트하거나 딜리트하는것은 데이터베이스에 영향을 주기떄문에 @Transactional을 걸어준다.

package com.cos.photogramstart.service;

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

import com.cos.photogramstart.domain.subscribe.SubscribeRepository;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class SubscribeService {
	
	private final SubscribeRepository subscribeRepository;
	
	
	@Transactional
	public void 구독하기(int fromUserId, int toUserId) {
	
		
	}
	@Transactional
	public void 구독취소하기(int fromUserId, int toUserId) {
		
	}
}

레파지토리를 연결해서 실제로 인서트와 딜리티를 해볼것이다. 

또한 int 값으로는 Subscribe 객체를 만들수없다 이들은 int값이 아니라 오브젝트이기때문에 

이럴떄는 네이티브 쿼리를 사용하는것이 좋다.

 

네이티브 쿼리는 어렵지 않다. 그저 직접 쿼리를 짜는것이다. 

package com.cos.photogramstart.domain.subscribe;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

public interface SubscribeRepository extends JpaRepository<Subscribe, Integer> {
	
	@Modifying // INSERT, DELETE, UPDATE를 네이티브 쿼리로 작성하려면 해당 어노테이션 필요!!
	@Query(value = "INSERT INTO subscribe(fromUserId, toUserId, createDate) VALUES(:fromUserId, :toUSerId, now())", nativeQuery = true)
	int mSubscribe(int fromUserId, int toUserId);
	
	@Query(value = "DELETE FROM subscribe WHERE fromUserId = :fromUserId AND toUserId = :toUserId", nativeQuery = true)
	int mUnSubscribe(int fromUserId, int toUserId);
	
}

: 은 무엇이냐면 쿼리가 실행되기 위해서 여기 변수를 바인드해서 넣겠다는 문법이다. 

데이터베이스에 변경을주는 네이티브 쿼리에는 @Modifying이 필요하다 

 

애가 이제 리턴값을 int가 적혀져있는 이유는 머냐면 지금 우리가 JPA를 쓰고 있는데 

성공하면 1이 리턴되고  실패하면 -1이 리턴된다. 

인서트를 만약 10번하면? 변경된 행의 갯수만큼 숫자가 리턴이 되기떄문에 10이리턴된다.

만약에 0이 나오면 변경된것이 하나도 없다는것이다. 

 

만약에 삭제를 할때도 1번유저를 삭제를 했는데도 0이 리턴된다고 하면 1번유저가 없으니가 삭제가 안된것이다. 

만약에 1번유저가 있었으면 삭제가 됬을테니 1이 리턴이 되었을것이다 

만약에 쿼리가 무언가잘못되었으면 -1이 리턴될것이다! 

오류가 발생하면 Try and catch 로 컨트롤익셉션핸들러에서 전부 처리하면되기떄문에 안받아도된다.

 


만약에 중복된 데이터가 들어가면 당연히 오류가 나오고 

이걸 해결하기위해

package com.cos.photogramstart.handler.ex;

import java.util.Map;

public class CustomApiException extends RuntimeException{

	//시리얼 번호는 객체를 구분할때!! 
	private static final long serialVersionUID = 1L;
	
	public CustomApiException(String message ) {
		super(message); 
	}

}

서비스단도 수정을 해야한다

package com.cos.photogramstart.service;

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

import com.cos.photogramstart.domain.subscribe.SubscribeRepository;
import com.cos.photogramstart.handler.ex.CustomApiException;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class SubscribeService {
	
	private final SubscribeRepository subscribeRepository;
	
	
	@Transactional
	public void 구독하기(int fromUserId, int toUserId) {
		try {
			subscribeRepository.mSubscribe(fromUserId, toUserId);
		} catch (Exception e) {
			throw new CustomApiException("이미 구독을 하였습니다.");
		}
	
		
	}
	@Transactional
	public void 구독취소하기(int fromUserId, int toUserId) {
		subscribeRepository.mUnSubscribe(fromUserId, toUserId);
	
	}
}

마지막으로 컨트롤 익셉션 핸들러에서 

@ExceptionHandler(CustomApiException.class) 
public ResponseEntity<?> apiException(CustomValidationApiException e) {
return new ResponseEntity<>(new CMRespDto<>(-1, e.getMessage(),null), HttpStatus.BAD_REQUEST);
}

추가해서 만들어준다!