본문 바로가기

Study

[내일배움캠프 TIL] 14일차 - 1. JPA 쿼리 메서드 주의할 점.

728x90
반응형

jpa 쿼리 메서드를 쓰는데, 의도한 바와 다르게 결과가 나와서 

알아보았더니 Or이 들어갈 경우 주의해서 써야한다는 것을 알았습니다.

그 내용을 정리해보았습니다.

 

JPA 쿼리 메서드의 AND/OR 우선순위 함정

1. 문제의 발단 (우리가 쓴 코드)

작성했던 쿼리메서드는 아래와 같습니다.

의도는 DeletedAt이 Null 아니면서
Role이 일치하면서,
username이 일치하는 것. 

 findAllByDeletedAtIsNullAndRoleAndUsernameContainingOrNicknameContaining

즉 예를들어. Role을 Master, nickname을 "사장"으로 주었을때,
Master 이면서 nickname에 사장이 들어간 친구들을 찾아오기를 원한 것이었습니다.

그런데 위 메서드로 실행을하니 
Role이 Master가 아니면서 닉네임이 "사장" 친구들도 불러오는 것이었습니다.

원인은 아래와 같았습니다.

2. 컴퓨터(SQL)의 해석 방식 (충격 주의)

SQL과 프로그래밍 언어에서 AND는 OR보다 우선순위가 훨씬 높습니다. (마치 수학에서 곱셈이 덧셈보다 먼저인 것과 같습니다.)

그래서 위 메서드는 우리가 의도한 것과 다르게 다음과 같이 쪼개집니다.

  • 조건 A (빡빡함): deleted_at IS NULL AND role = 'MASTER' AND username LIKE '%사장%'
  • 조건 B (완전 자유): nickname LIKE '%사장%'

이 두 조건이 **OR**로 묶여버립니다. 즉, "조건 A에 맞거나, 아니면 그냥 닉네임에 '사장'이 들어간 아무나" 다 가져오게 된 거죠. 그래서 MANAGER나 CUSTOMER 권한을 가진 사람들도 닉네임에 '사장'만 있으면 똔! 하고 튀어나온 겁니다.


3. 해결책: @Query와 괄호 ()의 힘

이 문제를 해결하려면 **"이름이나 닉네임 둘 중 하나에 키워드가 있는 사람"**이라는 범위를 괄호로 묶어줘야 합니다. JPA 메서드 이름으로는 괄호를 칠 수 없으니 **@Query**가 등판해야 합니다.

수정된 예시코드

@Query("SELECT u FROM UserEntity u " +
       "WHERE u.deletedAt IS NULL " +                // 1. 일단 삭제 안 된 사람 중에서
       "AND u.role = :role " +                        // 2. 역할이 반드시 일치해야 하고
       "AND (u.username LIKE %:keyword% OR u.nickname LIKE %:keyword%)") // 3. 그중 이름이나 닉네임에 키워드가 있는 사람!
Page<UserEntity> searchUsers(
    @Param("role") UserRoleEnum role, 
    @Param("keyword") String keyword, 
    Pageable pageable
);

 

 

 

반응형