Spring Boot/구독하기

구독하기 2

have a good time 2021. 12. 14. 10:50

<Subscribe.java>

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
@Table(
		uniqueConstraints = {
				@UniqueConstraint(
						name="subscribe_uk",
						columnNames = {"fromUserId", "toUserId"}
				)
		}
)
public class Subscribe {
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;
	
	@JoinColumn(name = "fromUserId") 
	@ManyToOne
	private User fromUser;
	
	@JoinColumn(name = "toUserId")
	@ManyToOne
	private User toUser;
	
	private LocalDateTime createDate;
	
	@PrePersist
	public void createDate() {
		this.createDate = LocalDateTime.now();
	}
}

 

 

<SubscribeApiController.java>

@RequiredArgsConstructor
@RestController
public class SubscribeApiController {

	
	private final SubscribeService subscribeService;
	
	@PostMapping("/api/subscribe/{toUserId}")
	public ResponseEntity<?> subscribe(@AuthenticationPrincipal PrincipalDetails principalDetails, @PathVariable int toUserId){
		subscribeService.구독하기(principalDetails.getUser().getId(), toUserId);
		return new ResponseEntity<>(new CMRespDto<>(1, "구독하기 성공", null), HttpStatus.OK);
	}
	
	@DeleteMapping("/api/subscribe/{toUserId}")
	public ResponseEntity<?> unSubscribe(@AuthenticationPrincipal PrincipalDetails principalDetails, @PathVariable int toUserId){
		subscribeService.구독취소하기(principalDetails.getUser().getId(), toUserId);
		return new ResponseEntity<>(new CMRespDto<>(1, "구독취소하기 성공", null), HttpStatus.OK);
	}
}

<SubscribeService.java>

 

@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);
	}
}

 

 

<SubscribeRepository.java>

 

public interface SubscribeRepository extends JpaRepository<Subscribe, Integer>{

	
	@Modifying 
	@Query(value = "INSERT INTO subscribe(fromUserId, toUserId, createDate) VALUES(:fromUserId, :toUserId, now())", nativeQuery = true)
	void mSubscribe(int fromUserId, int toUserId);
	
	@Modifying
	@Query(value = "DELETE FROM subscribe WHERE fromUserId = :fromUserId AND toUserId = :toUserId", nativeQuery = true)
	void mUnSubscribe(int fromUserId, int toUserId);
}

 

postman 에서 아래와 같이 로그인 한다.

검은부분에 post 요청 주소, username, password 값 넣고 로그인

 

성공적인 로그인이 되면 아래와 같이 200번 

 

 

그 후, 아래와 같이 2번 유저를 구독한다.

 

더보기

SubscribeApiController.java 에서 

 

구독하기 주소가 아래와 같으므로

	@PostMapping("/api/subscribe/{toUserId}")
	public ResponseEntity<?> subscribe(@AuthenticationPrincipal PrincipalDetails principalDetails, @PathVariable int toUserId){
		subscribeService.구독하기(principalDetails.getUser().getId(), toUserId);
		return new ResponseEntity<>(new CMRespDto<>(1, "구독하기 성공", null), HttpStatus.OK);
	}

 

성공하면

 

위와 같은 메세지가 postman에 나오게 된다.

위에 더보기에 보여준 듯이, SubscribeApiController.java 에서 구독하기 메서드의 리턴값이 아래와 같은 형태이므로

 

	return new ResponseEntity<>(new CMRespDto<>(1, "구독하기 성공", null), HttpStatus.OK);

 

데이터베이스에서도 아래와 같은 결과를 확인할 수 있다.

 

 

 

 

<예외처리>

 

Subscribe.java 에서

 

@Table(
		uniqueConstraints = {
				@UniqueConstraint(
						name="subscribe_uk",
						columnNames = {"fromUserId", "toUserId"}
				)
		}
)

이와 같은 처리를 했기 때문에 동일한 유저를 또 구독하면 에러가 생기게 된다.

 

따라서 

만약 1번 유저가 2번 유저를 한 번 더 구독하면, 아래와 같은 메세지가 나오도록 예외처리

 

 

 

 

<SubscribeService.java> - 이 부분은 위에서 SubscribeService.java 파일 첨부할 때 이미 이렇게 되어있었음

	@Transactional
	public void 구독하기(int fromUserId, int toUserId) {
		try {
			subscribeRepository.mSubscribe(fromUserId, toUserId);
		} catch (Exception e) {
			throw new CustomApiException("이미 구독을 하였습니다.");
		}
	}

 

<CustomApiException.java>

public class CustomApiException extends RuntimeException{

	
	private static final long serialVersionUID = 1L;
	
	public CustomApiException(String message) {
		super(message);
	}
}

 

<ControllerExceptionHandler.java>

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

 

 

추가적으로, 구독취소하기도 deletemapping 으로 하면 아래와 같이 잘됨

 

 

 

 

마지막으로, SecurityConfig.java 에서,

.antMatchers("/", "/user/**", "/image/**", "/subscribe/**", "/comment/**", "/api/**").authenticated()

이 중에 추가 안 된 부분 있으면 추가 시켜놓기.

 

 

참고 자료 : 이지업 사이트 강의 "스프링부트 SNS프로젝트 - 포토그램 만들기"