반응형

Follow.java
- Follow라는 모델을 만든다.
- @JoinColumn 어노테이션으로 DB의 컬럼명을 커스텀한다.
- @Table 컬럼을 통해 한 테이블에 동일한 관계 정보가 중복되지 않게 들어가도록 Uniqe 제약조건을 설정한다.
  (한 컬럼만 걸거면 그냥 @Column(unique = true)하면 되긴하다.)
- User 테이블이 1이고 Follow 테이블이 N 이니까 @ManytoOne 어노테이션을 적는다
- 데이터 생성 정보 컬럼도 넣어준다.

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
@Table(
        uniqueConstraints = {
                @UniqueConstraint(
                        name = "follow_uk",
                        columnNames = {"fromUserId", "toUserId"} //실제 db이 컬럼명이 들어가야한다.
                )
        }
)
public class Follow {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @JoinColumn(name = "fromUserId") // DB컬럼명 설정
    @ManyToOne
    private User fromUser; //팔로우 하는애

    @JoinColumn(name = "toUserId")
    @ManyToOne
    private User toUser; //팔로우 받는애

    private LocalDateTime createDate;

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

}

 

FollowApiController

- 팔로우 메소드, 언팔로우 메소드를 만든다.
- 팔로우(언팔라우) 주체를 알아야 하기 때문에 @AuthenticationPrincipal PrincipalDetails principalDetails 를 매개변수로 넣어준다.
- 또한 팔로우,언팔 대상(toUserId)를 매개변수로 넣어준다.

@RequiredArgsConstructor
@RestController
public class FollowApiController {

    private final FollowService followService;

    @PostMapping("/api/follow/{toUserId}")
    public ResponseEntity<?> follow(@AuthenticationPrincipal PrincipalDetails principalDetails,
    								@PathVariable int toUserId){

        followService.follow(principalDetails.getUser().getId(), toUserId);
        return new ResponseEntity<>(new CMRespDto<>(1, "팔로우성공", null), HttpStatus.OK);
    }

    @DeleteMapping("/api/follow/{toUserId}")
    public ResponseEntity<?> unfollow(@AuthenticationPrincipal PrincipalDetails principalDetails, 
    								  @PathVariable int toUserId){

        followService.unfollow(principalDetails.getUser().getId(), toUserId);

        return new ResponseEntity<>(new CMRespDto<>(1, "언팔로우 성공", null), HttpStatus.OK);
    }
}


FollowService

-실제 팔로우, 언팔로우 로직이 실행되는곳
- DB에 영향을 주는 메소드기 때문에 @Transactional 어노테이션 넣어준다.

@RequiredArgsConstructor
@Service
public class FollowService {

    private final FollowRepositoy followRepositoy;

    @Transactional
    public void follow(int fromUserId, int toUserId){
        try {
            followRepositoy.mFollow(fromUserId,toUserId);
        } catch (Exception e){
            throw new CustomApiException("이미 팔로우 했습니다.");
        }

    }

    @Transactional
    public void unfollow(int fromUserId, int toUserId){
        followRepositoy.mUnFollow(fromUserId,toUserId);
    }
}

 

CustomApiException

public class CustomApiException extends RuntimeException{

    private static final long serialVersionUID = 1L;

    public CustomApiException(String message){
        super(message);
    }
}

 

ControllerExceptionHandler

- apiException 메소드를 만든다. 여기에선 CMRespDto 매개변수에 에러메시지만 받고, errorMap은 null로만 받는다.

@RestController
@ControllerAdvice //모든 Exception들을 낚아챔
public class ControllerExceptionHandler {

  ...

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

 

FollowRepositoy
- 네이티브 쿼리로 작성
- ':' 은 매개변수에 들어온 값을 바인딩하겠다는 의미다. 

public interface FollowRepositoy extends JpaRepository<Follow,Integer>{

    @Modifying //INSERT, DELETE, UPDATE를 네이티브쿼리로 작성하려면 해당 어노테이션 필요
    @Query(value = "INSERT INTO follow(fromUserId, toUserId, createDate) Values(:fromUserId, :toUserId, now())", nativeQuery = true)
    void mFollow(int fromUserId, int toUserId);

    @Modifying
    @Query(value = "DELETE FROM follow WHERE fromUserId = :fromUserId AND toUserId = :toUserId", nativeQuery = true)
    void mUnFollow(int fromUserId, int toUserId);
}

 

+ Recent posts