카테고리 없음

TypeORM - Brackets 이란?

JohnnyDeveloper 2023. 12. 31. 17:39

Brackets 이란?

TypeORM에서 Brackets 클래스는 SQL 쿼리에서 중첩 조건을 만드는 데 사용됩니다. 이는 함께 그룹화해야 하는 여러 논리적 조건이 포함된 복잡한 쿼리를 작성하려는 경우 유용합니다. '괄호' 안에 조건을 래핑하면 TypeORM은 이를 생성된 SQL 쿼리에서 괄호로 묶어 단일 단위로 처리합니다.그룹화는 'AND' 및 'OR' 조건을 결합할 때 올바른 논리적 우선순위를 보장하는 데 중요합니다.


Brackets 왜?

보통 brackets는 다중 필터 옵션 개발에 많이 이용됩니다. 예를 들어 보자면, 검색에서 특정 옵션을 추가하는데 사용자의 나이는 30세 이상이고, 거주지는 신도림 이거나 강남에 사는 사용자를 검색한다고 했을 때, 만약 괄호 없이 SELECT * FROM USER WHERE AGE > 30 AND RESIDENCE = ‘신도림’ OR RESIDENCE = ‘강남’; 쿼리를 실행하게 된다면 30세 이상이 아니지만 거주지가 강남인 사람들이 결과에 포함 될 것입니다. 이와 같은 상황을 방지하려면, 괄호로 우선순위를 지정해 주면 됩니다. SELECT * FROM USER WHERE AGE > 30 AND (RESIDENCE = ‘신도림’ OR RESIDENCE = ‘강남’); 이렇게 쿼리를 작성하게 되면, 본래 의도대로 나이가 30 이상인 사용자가 조건에 꼭 포함되게 됩니다. TypeORM에서는 이를 brackets를 사용해서 구현하고 있습니다.


Brackets 특징

  1. 조건 그룹화: '괄호'를 사용하면 조건을 그룹화하여 함께 평가할 수 있습니다. 그룹화하지 않으면 조건이 왼쪽에서 오른쪽으로 평가되므로 특히 ANDOR결합할 때 의도한 결과가 나오지 않을 수 있습니다.
  2. 구문: 새로운 '괄호' 개체를 생성하고 해당 생성자에 함수를 전달합니다. 이 함수는 쿼리 빌더(qb)를 인수로 사용하며, 여기에 whereandWhere 또는 orWhere 메소드를 연결하여 조건을 추가할 수 있습니다.

Brackets 예시

1. 단순한 'AND' 및 'OR' 조합

'User' 엔터티가 있고(AND), 25세 이상이거나(OR) 특정 사용자 이름을 가진 사용자를 포함합니다. 앞의 두 경우 모두 ‘User’ 엔터티가 있어야 한다고 가정.

const users = await userRepository.createQueryBuilder("user")
    .where("user.isActive = :isActive", { isActive: true })
    .andWhere(new Brackets(qb => {
        qb.where("user.age > :age", { age: 25 })
          .orWhere("user.username = :username", { username: "john_doe" });
    }))
    .getMany();

 

SQL 쿼리

SELECT * FROM user WHERE user.isActive = true AND (user.age > 25 OR user.username = 'john_doe')

 

2. 여러 중첩 조건

25세 이상이거나(OR) 이름이 "John Doe"이고(AND) "뉴욕"에 거주하거나(OR) 특정 날짜 이후 상태인 사용자가 필요하다고 가정

const users = await userRepository.createQueryBuilder("user")
    .where(new Brackets(qb => {
        qb.where("user.age > :age", { age: 25 })
          .orWhere("user.username = :username", { username: "john_doe" });
    }))
    .andWhere(new Brackets(qb => {
        qb.where("user.city = :city", { city: "New York" })
          .orWhere("user.activeSince > :activeSince", { activeSince: new Date(2020, 0, 1) });
    }))
    .getMany();

 

SQL 쿼리

SELECT * FROM user WHERE (user.age > 25 OR user.username = 'john_doe') AND (user.city = 'New York' OR user.activeSince > '2020-01-01')

 

3. 복잡한 조건

2000년 이후에 출판되었거나(OR) 300페이지를 초과하는 도서들 중에(AND) "저자 A" 또는(OR) "저자 B"가 쓴 책이고(AND), 평점이 4점 이상이거나(OR) '소설' 또는(OR) '공상과학' 장르에 속해야 한다고 가정

const books = await bookRepository.createQueryBuilder("book")
    .where(new Brackets(qb => {
        qb.where("book.publishedYear > :year", { year: 2000 })
          .orWhere("book.pageCount > :pages", { pages: 300 });
    }))
    .andWhere(new Brackets(qb => {
        qb.where("book.author = :authorA", { authorA: "Author A" })
          .orWhere("book.author = :authorB", { authorB: "Author B" });
    }))
    .andWhere(new Brackets(qb => {
        qb.where("book.rating > :rating", { rating: 4 })
          .orWhere(new Brackets(qbInner => {
              qbInner.where("book.genre = :genre1", { genre1: "Fiction" })
                     .orWhere("book.genre = :genre2", { genre2: "Science Fiction" });
          }));
    }))
    .getMany();

 

SQL 쿼리

  1. 괄호의 첫 번째:
    • 2000년 이후에 출판되었거나 300페이지를 초과하는 도서의 조건을 그룹화한 것입니다.
    • 해당 SQL: (book.publishedYear > 2000 OR book.pageCount > 300)
  2. 괄호의 두 번째:
    • 이는 "A 저자" 또는 "B 저자"가 쓴 책에 적용됩니다.
    • 해당 SQL: (book.author = '저자 A' OR book.author = '저자 B')
  3. 괄호의 세 번째:
    • 여기에는 평점이 4점 이상인 도서도 포함됩니다.
    • 그 안에는 또 다른 'Brackets' 인스턴스가(qbInner) 장르별로 중첩되어 있습니다.
    • 해당 SQL: (book.rated > 4 OR (book.genre = 'Fiction' OR book.genre = 'Science Fiction'))

느낀점

만약 brackets가 없으면 SQL 연산자의 기본 우선 순위로 인해 의도하지 않은 다른 결과 집합이 발생할 수 있다. '괄호'(brackets)를 사용하면 각 괄호 안의 조건이 다른 조건과 결합되기 전에 하나의 단위로 평가되어 쿼리의 의도된 논리적 구조가 유지되서, 다중 필터 옵션 등에 꼭 필요한 기능이다.