문제1

public void deleteFriend(Long userId) {
    friendRepository.findByIdAndSetStatusDeleted(userId);
}

 

친구를 삭제하고 싶을 때 전달받은 유저의 id로 해당 테이블에서 데이터를 조회하고, 그 데이터의 Status 값을 DELETED로 변경하고자 했다.

Spring Data JPA는 메서드명을 주어진 형식으로 사용해야 자동으로 JPA가 DB에 데이터를 처리해줄 수 있다.

위 코드처럼 findByIdAndSetStatusDeleted 같이 명확하지 않으면서 형식으로 주어지지 않은 메서드명은 사용할 수 없었다.

 

해결

먼저 테이블에서 내가 원하는 데이터를 가져오려면 로그인한 유저와 상대 유저의 id가 모두 있어야 했다.

그러므로 findByReceiverAndRequester를 사용해서 두 유저 자체를 레포지토리에 전달해서 해당하는 객체를 반환받았다.

그리고 반환받은 객체의 Status 필드를 변경 감지를 통해 변경하도록 코드를 수정했다.

User receiver = userRepository.findById(loginedUserId).orElseThrow(NoSuchElementException::new);
User requester = userRepository.findById(userId).orElseThrow(NoSuchElementException::new);

// 상대 유저가 requester, 로그인 유저가 receiver인 friendRequest 반환
FriendRequest foundFriendRequest = friendRepository.findByReceiverAndRequester(receiver, requester).orElseThrow(NoSuchElementException::new);

// Status가 PENDING 상태인 경우 ACCEPT로 변경
if (foundFriendRequest.getStatus() == PENDING) {
    foundFriendRequest.setStatus(ACCEPT);
}else {
    // Status가 PENDING이 아닌 경우 예외 발생(이미 요청을 거절, 수락하거나 친구 삭제한 경우)
    throw new AlreadyProcessedException();
}

 


문제2

user1과 user2를 사용해서 레포지토리에서 데이터를 반환하려고 할 때, 두 유저 모두 receiver 또는 requester가 될 수 있는 상황이라 양방향으로 체크해야 했다.

이 경우는 user1이 receiver일 때와, user2가 receiver일 때를 각각 조회해야 하는지 고민을 했던 것 같다.

 

해결

이런 상황은 직접 쿼리를 작성하는데 더 효율적인 방식인 것 같아서 검색해보니 JPQL을 사용하면 쉽게 해결할 수 있었다.

JPQL은 엔티티 객체를 기준으로 쿼리를 작성하는 방법이다.

테이블 이름, 컬럼 이름이 아니라 엔티티 이름과 필드 이름을 사용한다.

@Query("select f from FriendRequest f where (f.receiver = :loginedUser and f.requester = :friend)"
+ "or (f.receiver = :friend and f.requester = :loginedUser)")
Optional<FriendRequest> findInteractiveRequest(@Param("loginedUser") User loginedUser, @Param("friend")User friend);

 

@Query 어노테이션을 통해 내 의도대로 쿼리를 작성한다.

그리고 기존에 JDBC template을 사용할 때 ?로 prepared 방식으로 sql문을 사용했던 것과 유사하게, @Param과 :을 사용해서 파라미터를 바인딩한다.

 

만약 엔티티, 필드명이 아니라 실제 데이터베이스의 테이블 이름과 컬럼 이름을 사용하고 싶다면 @Query 어노테이션의 속성으로 nativeQuery=true로 설정하면 된다.