이번 장의 내용을 이해하기 위해 [첫번째 프로젝트] 3. 람다와 [첫번째 프로젝트] 11. Predicate, Function을 먼저 읽는 것을 권장합니다. 이번 장의 핵심 내용은 QuerydslBinderCustomizer의 customize 메서드를 프로젝트에 적용하는 것을 목표로 정리된 글입니다.
QuerydslPredicateExecutor
는 Spring Data JPA에서 제공하는 인터페이스 중 하나입니다. 이 인터페이스는 Querydsl을 사용하여 동적 쿼리를 작성하고 실행할 수 있는 메서드를 제공합니다. 주로 Spring Data JPA와 Querydsl을 함께 사용할 때 유용하게 활용됩니다.
Querydsl은 Java 기반의 DSL(Domain Specific Language)로써, 데이터베이스 쿼리를 타입 안전하게 작성할 수 있도록 도와줍니다. QuerydslPredicateExecutor
를 사용하면, 동적인 검색 조건에 따라 쿼리를 생성하고 실행할 수 있습니다.
QuerydslPredicateExecutor
를 사용하면 동적인 검색 조건에 따라 쿼리를 작성할 수 있습니다. 이는 필요에 따라 조건을 프로그래밍적으로 결정하고 적용할 수 있어서 더 유연한 쿼리 작성이 가능합니다.QuerydslPredicateExecutor
를 상속하면 이러한 기능을 사용할 수 있습니다. 예를 들어, 메서드 이름에 findBy
를 사용하면 해당 필드를 기반으로 동적인 쿼리가 생성됩니다.QuerydslPredicateExecutor
는 다양한 메서드를 제공하여 개발자가 필요한 대부분의 쿼리를 쉽게 작성하고 실행할 수 있도록 도와줍니다. 몇 가지 예시는 findOne
, findAll
, findAll(Predicate, Sort)
, count
, 등이 있습니다.findOne(Predicate predicate)
:Optional<T>
이며, 찾지 못할 경우 빈 Optional
을 반환합니다.findAll(Predicate predicate)
:Iterable<T>
이며, 결과가 없는 경우 빈 Iterable을 반환합니다.findAll(Predicate predicate, Sort sort)
:Iterable<T>
이며, 결과가 없는 경우 빈 Iterable을 반환합니다.findAll(Predicate predicate, Pageable pageable)
:Page<T>
이며, 페이징 정보를 포함하고 있습니다.count(Predicate predicate)
:exists(Predicate predicate)
:boolean
입니다.다음은 Spring Data JPA와 Querydsl을 사용하여 동적인 검색 조건을 처리하는 예제입니다. 이 예제에서는 QuerydslPredicateExecutor
를 사용하여 Repository에 선언된 메서드들을 통해 동적인 쿼리를 작성하고 실행합니다.
먼저, 엔터티 클래스를 정의합니다. 여기서는 간단한 Person
엔터티를 사용하겠습니다.
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Integer age;
// getters, setters, constructors 등 생략
}
다음으로, 이 엔터티에 대한 Spring Data JPA Repository를 생성하고 QuerydslPredicateExecutor
를 상속합니다.
javaCopy codepublic interface PersonRepository extends
JpaRepository<Person, Long>,
QuerydslPredicateExecutor<Person> {
}
이제 Controller에서 Repository를 주입받아 다양한 동적 쿼리를 사용하는 예제를 살펴보겠습니다.
@RestController
@RequestMapping("/persons")
public class PersonController {
@Autowired
private PersonRepository personRepository;
@GetMapping
public Iterable<Person> getPersons(@QuerydslPredicate(root = Person.class) Predicate predicate) {
// findAll 메서드를 사용하여 동적인 쿼리를 실행
return personRepository.findAll(predicate);
}
@GetMapping("/age/{age}")
public Iterable<Person> getPersonsByAge(@PathVariable Integer age) {
// 동적인 쿼리를 작성하는 예제: 나이(age)가 주어진 값과 일치하는 경우
BooleanBuilder predicate = new BooleanBuilder().and(QPerson.person.age.eq(age));
// findAll 메서드를 사용하여 동적인 쿼리를 실행
return personRepository.findAll(predicate);
}
@GetMapping("/name/{name}")
public Iterable<Person> getPersonsByName(@PathVariable String name) {
// 동적인 쿼리를 작성하는 예제: 이름(name)이 주어진 값과 일치하는 경우 (대소문자 무시)
BooleanBuilder predicate = new BooleanBuilder().and(QPerson.person.name.equalsIgnoreCase(name));
// findAll 메서드를 사용하여 동적인 쿼리를 실행
return personRepository.findAll(predicate);
}
}
위의 예제에서는 /persons
엔드포인트로 GET 요청이 들어올 때, 동적인 검색 조건을 받아 해당하는 엔터티들을 조회하고 반환합니다. /age/{age}
와 /name/{name}
엔드포인트에서는 각각 나이와 이름에 대한 동적인 쿼리를 작성하는 예제를 보여줍니다.
이러한 방식으로 QuerydslPredicateExecutor
를 활용하면 동적인 검색 조건을 간편하게 처리할 수 있습니다.
바인딩(Binding)은 일반적으로 프로그래밍에서 두 가지 요소 간의 연결이나 결속을 의미합니다. Spring Data Querydsl에서의 바인딩은 쿼리 매개변수와 엔터티 필드 간의 연결을 말합니다.
특히, Querydsl을 사용하여 동적인 쿼리를 작성할 때, 사용자로부터 입력된 조건이나 파라미터를 어떻게 처리할지에 대한 규칙을 지정하는 과정을 바인딩이라고 할 수 있습니다. 이것은 쿼리 메서드의 파라미터와 Querydsl 엔터티의 필드 간의 매핑 및 검색 조건의 처리 방법에 관련이 있습니다.
예를 들어, 사용자가 이름에 대한 검색을 수행하려고 할 때, 대소문자를 무시하고 부분 일치를 하도록 바인딩을 설정할 수 있습니다. 이러한 바인딩 설정을 통해 사용자가 제공한 검색어를 데이터베이스에 적용할 때 어떤 규칙으로 적용할지를 결정할 수 있습니다.
Spring Data Querydsl에서 QuerydslBinderCustomizer
를 사용하면 이러한 바인딩 규칙을 정의할 수 있습니다. 바인딩을 사용하면 사용자 입력을 받아와 실제 쿼리에 어떻게 적용할지를 개발자가 제어할 수 있게 됩니다. 이는 검색 조건에 대한 유연성을 제공하고, 사용자가 원하는대로 쿼리를 작성할 수 있도록 도와줍니다.
QuerydslBinderCustomizer
는 Querydsl을 사용하여 동적인 쿼리를 작성할 때, 바인딩(binding)을 커스터마이징하는 데 사용되는 인터페이스입니다. 이를 통해 쿼리 매개변수를 어떻게 바인딩할지를 개발자가 제어할 수 있습니다. 주로 검색 조건의 매개변수를 어떻게 처리할지에 대한 로직을 제공하는 데 사용됩니다.
QuerydslBinderCustomizer
인터페이스를 구현하는 클래스는 customize
메서드를 오버라이드하여 원하는 바인딩 로직을 구현합니다.
간단한 예제를 통해 설명하겠습니다. 예를 들어, 다음은 Person
엔터티를 사용하는 도메인에서 이름(name
)이 포함된 경우에 대한 동적인 쿼리를 처리하는 예제입니다.
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Integer age;
// getters, setters, constructors 등 생략
}
PersonRepository
에서 QuerydslBinderCustomizer
를 구현하여 customize
메서드를 오버라이드합니다.
import com.querydsl.core.types.dsl.StringPath;
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
import org.springframework.data.querydsl.binding.QuerydslBindings;
public interface PersonRepository extends
JpaRepository<Person, Long>,
QuerydslPredicateExecutor<Person>,
QuerydslBinderCustomizer<QPerson> {
@Override
default void customize(QuerydslBindings bindings, QPerson root) {
// 이름(name)에 대한 바인딩을 커스터마이즈
bindings.bind(root.name).first(StringPath::containsIgnoreCase);
}
}
위 예제에서 customize
메서드는 QuerydslBindings
객체를 사용하여 쿼리의 바인딩을 조절합니다. 여기서는 name
필드에 대한 바인딩을 커스터마이즈하고, containsIgnoreCase
를 사용하여 대소문자를 무시하고 부분 일치하는 검색을 수행하도록 설정했습니다.
이제 PersonRepository
를 사용하는 곳에서 동적인 쿼리를 작성할 때, 이름에 대한 검색이 대소문자를 무시하고 부분 일치하는 방식으로 수행됩니다.
QuerydslBinderCustomizer
를 구현하면 Spring Data Querydsl이 동적인 쿼리를 처리할 때 어떻게 바인딩을 수행할지에 대한 로직을 개발자가 제어할 수 있습니다. 즉, 검색 조건의 매핑이나 처리 방식을 세밀하게 조절할 수 있습니다.QuerydslBinderCustomizer
를 구현하여 바인딩을 커스터마이징하는 과정에서도 컴파일 시점에서 타입을 체크할 수 있습니다.QuerydslBinderCustomizer
를 통해 다양한 바인딩 설정을 할 수 있습니다. 예를 들어, 대소문자를 무시하거나 부분 일치를 수행하는 등의 설정을 통해 동적인 검색에 대한 유연성을 확보할 수 있습니다.QuerydslBinderCustomizer
의 customize
메서드 내에서 QuerydslBindings
객체를 사용하여 엔터티 필드에 대한 바인딩을 조절할 수 있습니다. QuerydslBindings
는 바인딩 설정을 담당하는데 사용되며, 여러 메서드를 통해 각 필드에 대한 다양한 설정을 지정할 수 있습니다.QuerydslBinderCustomizer
인터페이스는 하나의 메서드를 정의하고 있습니다. 이 메서드는 바로 customize
메서드입니다. 아래는 QuerydslBinderCustomizer
의 주요 메서드에 대한 설명입니다:
customize(QuerydslBindings bindings, QType root)
이 메서드는 Querydsl이 동적인 쿼리를 처리할 때 어떻게 바인딩을 커스터마이징할지를 정의하는데 사용됩니다. 주로 QuerydslBindings
객체를 통해 엔터티 필드에 대한 바인딩 설정을 수행합니다.
customize(QuerydslBindings bindings, QType root)
매개변수:
bindings
: QuerydslBindings
객체는 엔터티 필드에 대한 바인딩 설정을 담당하는데 사용됩니다. customize
메서드 내에서 이 객체를 활용하여 필드별로 바인딩 설정을 수행합니다.root
: 엔터티를 나타내는 Querydsl 타입입니다. 여기서 QType
은 엔터티 클래스에 대한 Querydsl 타입으로, 예를 들면 QPerson
과 같은 형태입니다.customize(QuerydslBindings bindings, QType root)
사용 예제:
public interface PersonRepository extends
JpaRepository<Person, Long>,
QuerydslPredicateExecutor<Person>,
QuerydslBinderCustomizer<QPerson> {
@Override
default void customize(QuerydslBindings bindings, QPerson root) {
// 예제: 이름(name)에 대한 바인딩을 커스터마이즈
bindings.bind(root.name).first(StringPath::containsIgnoreCase);
// 여러 필드에 대한 바인딩 설정 예제
bindings.bind(root.age).as("ageAlias").first((path, value) -> path.eq(value));
}
}
1. bindings.bind(root.name).first(StringPath::containsIgnoreCase);
이 코드는 name
필드에 대한 바인딩 설정을 커스터마이징하는 부분입니다.
bindings
: QuerydslBindings
객체는 엔터티 필드에 대한 바인딩 설정을 담당하는데 사용됩니다. bind
메서드는 어떤 필드에 대한 바인딩을 설정할 것인지를 지정합니다.root.name
: root
는 Querydsl에서 사용되는 엔터티를 나타내는 타입입니다. 여기서는 QPerson
이 Person
엔터티에 대한 Querydsl 타입을 나타냅니다. name
은 QPerson
의 필드 중 하나인 name
필드를 나타냅니다.first(StringPath::containsIgnoreCase)
: first
메서드는 해당 필드에 대한 첫 번째 조건을 설정하는데 사용됩니다. StringPath::containsIgnoreCase
는 해당 필드(name
)에 대해 대소문자를 무시하고 부분 일치하는 검색을 수행하도록 설정합니다.2. bindings.bind(root.age).as("ageAlias").first((path, value) -> path.eq(value));
이 코드는 age
필드에 대한 바인딩 설정을 커스터마이징하는 부분입니다.
bindings
: 이전과 마찬가지로 QuerydslBindings
객체를 사용하여 엔터티 필드에 대한 바인딩 설정을 담당합니다.root.age
: QPerson
의 age
필드를 나타냅니다.as("ageAlias")
: age
필드에 대한 바인딩 설정을 할 때, 별칭("ageAlias")을 부여합니다. 이는 나중에 해당 필드에 대한 검색 조건을 설정할 때 사용됩니다.first((path, value) -> path.eq(value))
: first
메서드를 사용하여 첫 번째 조건을 설정합니다. 이 경우에는 해당 필드(age
)에 대해 값을 정확하게 일치시키도록 설정했습니다.종합하면, customize
메서드 내에서 bindings.bind(root.field)
를 통해 엔터티 필드에 대한 바인딩 설정을 하고, 그 뒤에 first
등의 메서드를 통해 해당 필드에 대한 검색 조건을 설정합니다. 이를 통해 동적인 검색에 대한 규칙을 세밀하게 조절할 수 있습니다.
QuerydslBindings
는 Spring Data Querydsl에서 사용되는 클래스로, Querydsl의 바인딩 규칙을 지정하는 데에 활용됩니다. QuerydslBinderCustomizer
를 구현한 인터페이스나 클래스에서 customize
메서드의 매개변수로 전달되어, 검색 조건의 바인딩을 개별적으로 설정할 수 있게 해줍니다.
여기서 간단한 설명을 제공하겠습니다.
QuerydslBindings
클래스의 bind
메서드를 사용하여 엔터티의 특정 필드에 대한 바인딩을 설정할 수 있습니다. 이 메서드는 바인딩할 필드(Path
)를 지정하고, 해당 필드에 대한 조건을 설정할 수 있는 SingleValueBinding
, MultiValueBinding
등의 메서드를 제공합니다.
간단한 예제를 통해 설명하겠습니다. 아래는 Person
엔터티에서 name
필드에 대한 바인딩을 설정하는 예제입니다.
public interface PersonRepository extends JpaRepository<Person, Long>, QuerydslPredicateExecutor<Person>, QuerydslBinderCustomizer<QPerson> {
@Override
default void customize(QuerydslBindings bindings, QPerson root) {
// 이름(name)에 대한 바인딩을 커스터마이즈
bindings.bind(root.name).first(StringPath::containsIgnoreCase);
}
}
위 예제에서 customize
메서드 내에서 bindings.bind(root.name)
을 사용하여 name
필드에 대한 바인딩을 설정했습니다. first(StringPath::containsIgnoreCase)
는 해당 필드에 대한 조건을 설정하는 부분으로, 여기서는 대소문자를 무시하고 부분 일치하는 검색을 지정했습니다.
즉, QuerydslBindings
를 이용하면 엔터티의 각 필드에 대한 검색 조건을 세밀하게 설정할 수 있습니다.
bind(Path<?> path)
: 지정된 경로(Path<?>
)에 대한 바인딩 설정을 시작합니다.as(String alias)
: 현재 바인딩 설정에 대한 별칭을 지정합니다.first(SingleValueBinding<?, ?> binding)
: 첫 번째 검색 조건을 설정합니다. SingleValueBinding
은 단일 값에 대한 검색 조건을 정의하는 함수형 인터페이스입니다.all(MultiValueBinding<?, ?> binding)
: 다중 값에 대한 검색 조건을 설정합니다. MultiValueBinding
은 다중 값에 대한 검색 조건을 정의하는 함수형 인터페이스입니다.in(SingleValueBinding<?, ?> binding)
: IN 연산자에 대한 검색 조건을 설정합니다.여기에 더불어, QuerydslBindings
는 다양한 메서드를 제공하므로 필요에 따라 특정한 바인딩 설정을 적용할 수 있습니다. 아래는 간단한 예제를 통해 몇 가지 메서드를 보여줍니다:
public interface PersonRepository extends
JpaRepository<Person, Long>,
QuerydslPredicateExecutor<Person>,
QuerydslBinderCustomizer<QPerson> {
@Override
default void customize(QuerydslBindings bindings, QPerson root) {
// 예제 1: 이름(name)에 대한 바인딩을 커스터마이즈
bindings.bind(root.name).first(StringPath::containsIgnoreCase);
// 예제 2: 나이(age)에 대한 바인딩을 커스터마이즈
bindings.bind(root.age).as("ageAlias")
.first((path, value) -> path.eq(value))
.all((path, values) -> path.between(values.get(0), values.get(1)));
// 예제 3: 여러 필드에 대한 바인딩 설정 예제
bindings.bind(root.email).as("emailAlias").first((path, value) -> path.eq(value));
}
}
위 예제에서 bindings.bind(root.age)
를 통해 age
필드에 대한 바인딩 설정을 시작하고, 이어지는 .first
와 .all
메서드를 통해 첫 번째 조건과 다중 값에 대한 조건을 설정하는 예시를 보여주고 있습니다. 마지막으로 bindings.bind(root.email)
를 통해 email
필드에 대한 바인딩 설정을 보여주고 있습니다.
예제 1: 이름(name)에 대한 바인딩을 커스터마이즈
// JAVA - 예제 1: 이름(name)에 대한 바인딩을 커스터마이즈
bindings.bind(root.name).first(StringPath::containsIgnoreCase);
-- SQL - JPA에서는 대소문자를 무시하고 부분 일치하는 검색을 위해 LIKE와 LOWER 함수를 사용
SELECT * FROM Person WHERE LOWER(name) LIKE LOWER('%검색값%');
예제 2: 나이(age)에 대한 바인딩을 커스터마이즈
// 예제 2: 나이(age)에 대한 바인딩을 커스터마이즈
bindings.bind(root.age).as("ageAlias")
.first((path, value) -> path.eq(value))
.all((path, values) -> path.between(values.get(0), values.get(1)));
-- 나이가 주어진 값과 정확히 일치하는 검색 조건
SELECT * FROM Person WHERE age = 검색값;
-- 또는 다중 값이 주어진 경우 범위로 검색
SELECT * FROM Person WHERE age BETWEEN 검색값1 AND 검색값2;
예제 3: 여러 필드에 대한 바인딩 설정 예제
// 예제 3: 여러 필드에 대한 바인딩 설정 예제
bindings.bind(root.email).as("emailAlias").first((path, value) -> path.eq(value));
-- 이메일이 주어진 값과 정확히 일치하는 검색 조건
SELECT * FROM Person WHERE email = '검색값';
상기 SQL 예제는 Querydsl이 어떤 식으로 동작할 수 있는지를 간략하게 보여주기 위한 것이며, 실제 생성되는 SQL은 JPA 구현체(예: Hibernate)에 따라 달라질 수 있습니다.
package limdae.diligence.repository;
import com.querydsl.core.types.dsl.DateTimeExpression;
import com.querydsl.core.types.dsl.StringExpression;
import limdae.diligence.domain.ArticleComment;
import limdae.diligence.domain.QArticleComment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
import org.springframework.data.querydsl.binding.QuerydslBindings;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface ArticleCommentRepository extends
JpaRepository<ArticleComment, Long>,
QuerydslPredicateExecutor<ArticleComment>,
QuerydslBinderCustomizer<QArticleComment> {
@Override
default void customize(QuerydslBindings bindings, QArticleComment root) {
bindings.excludeUnlistedProperties(true);
bindings.including(root.content, root.createdAt, root.createdBy);
bindings.bind(root.content).first(StringExpression::containsIgnoreCase);
bindings.bind(root.createdAt).first(DateTimeExpression::eq);
bindings.bind(root.createdBy).first(StringExpression::containsIgnoreCase);
}
}
예제1: 바인딩 설정하지 않은 필드는 바인딩을 제외하기
// 바인딩 설정을 지정하지 않은 나머지 모든 엔터티의 필드에 대해서는 바인딩을 제외하도록 하는 옵션
bindings.excludeUnlistedProperties(true);
-- 이 쿼리는 제외된 모든 필드를 기본적인 바인딩 규칙에 따라 처리
-- 따라서 특별한 바인딩 설정이 없는 필드에는 기본적인 처리 방식이 적용됨
excludeUnlistedProperties
는 바인딩 설정을 지정하지 않은 나머지 모든 엔터티의 필드에 대해서는 바인딩을 제외하도록 하는 옵션입니다. true
로 설정하면 바인딩을 지정하지 않은 필드에 대해서는 기본 바인딩 규칙이 적용되지 않습니다.
예제2: 바인딩을 수행할 필드 명시하기
// `including` 메서드는 바인딩을 수행할 엔터티 필드를 명시적으로 지정하는데 사용
bindings.including(root.content, root.createdAt, root.createdBy)
-- 이 쿼리는 content, createdAt, createdBy 필드에 대해서만 바인딩을 수행
-- 따라서 이후의 설정은 이들 필드에 대해서만 영향을 미침
including
메서드는 바인딩을 수행할 엔터티 필드를 명시적으로 지정하는데 사용됩니다. 여기서는 root.content
, root.createdAt
, root.createdBy
에 대해서만 바인딩을 수행하도록 설정되었습니다.
예제3: content 필드의 대소문자를 무시하고 검색하기
// 첫 번쩨 조건 설정으로 해당 필드에 대한 대소문자를 무시하고 부분 일치하는 검색을 지정
bindings.bind(root.content).first(StringExpression::containsIgnoreCase)
-- 이 쿼리는 content 필드에 대해 부분 일치 검색을 수행하도록 설정
SELECT * FROM article_comment WHERE LOWER(content) LIKE LOWER('%검색값%');
bind
메서드는 특정 필드에 대한 바인딩 설정을 시작합니다. 이 경우에는 root.content
필드에 대한 설정을 시작합니다. first
메서드는 해당 필드에 대한 첫 번째 조건을 설정하며, StringExpression::containsIgnoreCase
는 해당 필드에 대한 대소문자를 무시하고 부분 일치하는 검색을 지정합니다.
예제4: createdAt 필드와 동일한 지 검색하기
// `root.createdAt` 필드에 대해 동일한 DateTime 을 검색 조건을 설정
bindings.bind(root.createdAt).first(DateTimeExpression::eq)
-- 이 쿼리는 createdAt 필드에 대해 정확한 일치 검색을 수행하도록 설정
SELECT * FROM article_comment WHERE createdAt = '검색값';
bindings.bind(root.createdAt)
: 이 부분은 QuerydslBindings에게 root.createdAt
필드에 대한 바인딩 설정을 시작하도록 지시합니다. root.createdAt
은 Querydsl에서 사용되는 엔터티 필드에 대한 경로(Path
)입니다. bind
메서드를 사용하여 특정 필드에 대한 바인딩을 정의할 수 있습니다..first(DateTimeExpression::eq)
: first
메서드는 해당 필드에 대한 첫 번째 검색 조건을 설정합니다. 여기서 DateTimeExpression::eq
는 DateTime
타입의 필드인 root.createdAt
에 대해 정확한 일치(=
)를 지정합니다.이제 이 코드를 다시 살펴보면, root.createdAt
필드에 대해서는 정확한 일치를 하는 검색 조건을 설정하도록 바인딩이 커스터마이즈되었다는 것입니다. 이는 SQL에서 createdAt = '검색값'
와 유사한 조건을 만들어줄 것입니다.
예를 들어, 만약 root.createdAt
이 2024-01-24 12:34:56
이라는 값이라면, 위의 설정을 기반으로 한 동적인 쿼리에서는 해당 시간과 정확하게 일치하는 데이터를 검색하게 됩니다.
예제4: createdBy 필드의 대소문자를 무시하고 검색하기
// `root.createdBy` 필드에 대해 대소문자를 무시하고 부분 일치하는 검색을 지정
bindings.bind(root.createdBy).first(StringExpression::containsIgnoreCase)
-- 이 쿼리는 createdBy 필드에 대해 부분 일치 검색을 수행하도록 설정
SELECT * FROM article_comment WHERE LOWER(createdBy) LIKE LOWER('%검색값%');
root.createdBy
필드에 대해 대소문자를 무시하고 부분 일치하는 검색을 지정합니다.