Post

[Springboot] 포토그램 구독/구독취소 구현


아이티윌의 국비지원 [스프링부트 SNS 포토그램 프로젝트] 강의를 수강하며 정리한 내용입니다.


오늘의 실습

  • 구독 모델 및 API 제작


🔎그 전에 알아야할 것


연관관계 개념

여기 참고


실습

[Subscribe.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
@Table()
public class Subscribe {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;
	
	@JoinColumn(name = "fromUserId")
	@ManyToOne // User 1 : Sub N
	private User fromUser;
	
	@JoinColumn(name = "toUserId")
	@ManyToOne
	private User toUser;
	
	private LocalDateTime createDate;
	
	@PrePersist
	public void createDate() {
		this.createDate = LocalDateTime.now();
	}
}

@Table : 1번 유저가 2번 유저를 중복해서 팔로우 할 수 없으니 유니크 제약 조건을 추가한다.
@JoinColumn : 칼럼의 이름을 설정해주는 어노테이션으로 설정해주지 않으면 알아서 이름을 만들어 준다.
@ManyToOne : 다대일 관계에 사용

image


image
구독테이블과 구독유저테이블, 구독받는유저테이블 관계를 보면
subscribe N : fromUserId 1
subscribe N : toUserId 1
이므로 @ManyToOne 어노테이션을 붙여줘야 한다. (Subscribe 엔티티가 여러 User 엔티티와 다대일 관계를 갖는다.)


서버를 실행했을 때 subscribe 테이블이 자동 생성되어 있는 것을 확인!

[SubscribeRepository.java]

1
2
3
4
5
6
7
8
9
10
11
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)
	void mSubscribe(@Param("fromUserId") int fromUserId, @Param("toUserId") int toUserId);
	
	@Modifying
	@Query(value= "DELETE FROM subscribe WHERE fromUserId=:fromUserId AND toUserId=:toUserId", nativeQuery=true)
	void mUnSubscribe(@Param("fromUserId") int fromUserId, @Param("toUserId") int toUserId);
}

@Modifying : INSERT, DELETE, UPDATE 를 네이티브 쿼리로 작성하려면 해당 어노테이션 필요하다.


fromUserId와 toUserId의 타입이 테이블에선 User이고 Service에선 int이기에 nativeQuery를 만들어주었다.

[SubscribeService.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@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);
	}
}

구독취소의 경우 오류날 상황이 없다고 생각해 예외처리 안 함!

[SubscribeApiController.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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);
	}
}

포스트맨으로 동작 확인

image
로그인을 먼저 해준 후


image
구독 요청을 보냈을 때 잘 동작하는 것을 확인!


image
중복해서 구독을 요청할 경우 예외처리 핸들러가 잘 동작하는 것도 확인!


image
DB에도 구독정보 삽입되는지 확인!


This post is licensed under CC BY 4.0 by the author.