1. 소개
데이터베이스를 다루다 보면 정확하게 일치하는 데이터가 아니라 특정 패턴을 가진 데이터를 찾아야 할 때가 많습니다. 예를 들어, '김'씨 성을 가진 모든 사용자를 찾거나, 이메일 주소가 '@gmail.com'으로 끝나는 회원을 조회하는 경우입니다. 이때 가장 흔하게 사용되는 것이 바로 MySQL의 LIKE 연산자입니다.
LIKE는 SQL 표준 연산자로, 초보 개발자에게는 직관적이고 편리한 도구이지만, 데이터 양이 많아질수록 성능에 치명적인 영향을 줄 수 있는 양날의 검이기도 합니다. 이 글에서는 LIKE의 기본적인 사용법부터 인덱스를 태우기 위한 조건, 그리고 실무에서 주의해야 할 성능 이슈까지 깊이 있게 다루어 보겠습니다.
2. LIKE 연산자의 기본 문법과 와일드카드
LIKE 연산자는 특정 문자열이 지정된 패턴과 일치하는지를 검사합니다. 이때 패턴을 정의하기 위해 두 가지 핵심 와일드카드를 사용합니다.
퍼센트(%) 기호
퍼센트 기호(%)는 0개 이상의 임의의 문자와 일치함을 의미합니다. 길이에 상관없이 어떤 문자열이든 올 수 있다는 뜻입니다.
예시: SELECT * FROM users WHERE name LIKE '김%'; 위 쿼리는 '김'으로 시작하는 모든 이름을 찾습니다. '김철수', '김영희', 그리고 외자 이름인 '김'도 모두 포함됩니다.
반대로 '%수'라고 작성하면 '철수', '영수'처럼 '수'로 끝나는 모든 이름을 찾게 됩니다. '%길동%'과 같이 양쪽에 사용하면 '홍길동', '고길동' 등 위치에 상관없이 '길동'이 포함된 모든 데이터를 조회합니다.
언더스코어(_) 기호
언더스코어(_)는 정확히 1개의 문자와 일치함을 의미합니다. 글자 수를 지정해서 검색하고 싶을 때 유용합니다.
예시: SELECT * FROM products WHERE code LIKE 'A_'; 이 쿼리는 'A'로 시작하고 뒤에 딱 한 글자가 더 오는 코드를 찾습니다. 'A1', 'AB'는 검색되지만, 'A12'나 'A'는 검색되지 않습니다.
3. LIKE 쿼리와 성능: 인덱스는 언제 사용될까?
많은 개발자가 LIKE를 사용할 때 가장 고민하는 부분은 바로 성능입니다. "LIKE를 쓰면 느리다"라는 말을 들어보셨을 텐데, 이는 반은 맞고 반은 틀린 이야기입니다. LIKE 쿼리도 상황에 따라 인덱스를 효율적으로 사용할 수 있습니다.
인덱스를 사용할 수 있는 경우 (Leftmost Prefix)
MySQL의 B-Tree 인덱스는 데이터를 왼쪽에서 오른쪽으로 정렬하여 저장합니다. 따라서 패턴이 상수 문자로 시작하는 경우에는 인덱스를 활용할 수 있습니다. 이를 'Leftmost Prefix(가장 왼쪽 접두사)' 규칙이라고 합니다.
예를 들어, LIKE 'Apple%'과 같은 검색은 인덱스 범위 스캔(Range Scan)이 가능합니다. 인덱스 트리에서 'A'로 시작하는 구간만 찾으면 되기 때문입니다. 이 경우 검색 속도가 매우 빠릅니다.
인덱스를 사용할 수 없는 경우
반면, 와일드카드가 패턴의 가장 앞에 오는 경우(예: LIKE '%Apple')에는 인덱스를 사용할 수 없습니다. 데이터가 'A'로 시작할지 'B'로 시작할지 알 수 없기 때문에, 데이터베이스는 결국 테이블의 모든 행을 하나씩 검사하는 풀 테이블 스캔(Full Table Scan)을 수행해야 합니다.
데이터가 수백만 건 이상일 때 '%단어%' 형태의 검색을 자주 수행하면 DB 서버의 CPU 사용량이 급증하고 응답 속도가 현저히 느려질 수 있으므로 주의해야 합니다.
4. 특수 문자와 데이터 정렬(Collation) 이슈
실무에서는 조금 더 복잡한 상황을 마주하게 됩니다. 검색하려는 데이터 자체에 와일드카드 문자(%, _)가 포함된 경우나 대소문자 구분이 필요한 경우입니다.
이스케이프(Escape) 처리
만약 데이터베이스에 저장된 '100%'라는 문자열을 찾고 싶다면 어떻게 해야 할까요? LIKE '100%'라고 쓰면 100으로 시작하는 모든 문자열을 찾게 됩니다. 이때는 역슬래시()를 사용하여 해당 문자가 와일드카드가 아닌 일반 문자임을 알려주어야 합니다.
예시: WHERE content LIKE '100%' ESCAPE ''; 이렇게 작성하면 % 기호 자체를 문자로 인식하여 정확히 '100%'를 찾을 수 있습니다.
대소문자 구분과 Collation
MySQL의 기본 설정에서 LIKE 연산은 대소문자를 구분하지 않는 경우가 많습니다. 이는 테이블의 Collation(정렬 규칙) 설정에 따르는데, 보통 utf8mb4_general_ci 또는 utf8mb4_0900_ai_ci 등으로 설정되어 있기 때문입니다. 여기서 'ci'는 Case Insensitive(대소문자 구분 안 함)를 의미합니다.
정확하게 대소문자를 구분하여 검색해야 한다면, BINARY 연산자를 사용하거나 Collation을 _bin(Binary) 또는 _cs(Case Sensitive) 계열로 변경해야 합니다.
예시: WHERE name LIKE BINARY 'Admin%'; 이렇게 하면 'admin'은 검색되지 않고 'Admin'만 검색됩니다. 단, BINARY 변환 시 인덱스 사용에 제약이 생길 수 있으므로 실행 계획을 확인하는 것이 좋습니다.
5. MySQL 버전별 고려사항
MySQL 5.7에서 8.0으로 넘어오면서 기본 캐릭터 셋이 latin1에서 utf8mb4로 변경되었습니다. 또한 기본 Collation도 utf8mb4_0900_ai_ci로 바뀌었습니다.
기능적으로 LIKE의 동작 방식이 크게 바뀐 것은 아니지만, utf8mb4는 가변 길이 문자열을 저장하므로 LIKE 검색 시 메모리 사용량이나 성능 패턴이 미세하게 다를 수 있습니다. 특히 이모지 등을 포함한 검색이 필요하다면 반드시 utf8mb4 설정을 확인해야 합니다.
6. 결론 및 실무 팁
LIKE 연산자는 강력하고 편리하지만, 대용량 데이터 환경에서는 성능 저하의 주범이 될 수 있습니다. 쿼리를 작성할 때 다음의 사항들을 꼭 기억해 주세요.
핵심 요약 및 실무 팁
- 접두사 검색을 지향하세요: 가능하다면 '검색어%' 형태를 사용하여 인덱스를 태우는 것이 좋습니다. 사용자가 입력한 검색어 뒤에만 와일드카드를 붙이는 방식을 고려해 보세요.
- 풀 텍스트 검색(Full-Text Search) 고려: 만약 게시판 본문 검색처럼 앞뒤로 와일드카드가 들어가는 '%검색어%' 쿼리가 필수적이라면, LIKE 대신 MySQL의 전문 검색(Full-Text Search) 인덱스나 Elasticsearch 같은 별도의 검색 엔진 도입을 고려해야 합니다.
- 커버링 인덱스 활용: 쿼리가 인덱스에 포함된 컬럼만 조회하도록(SELECT id FROM ... WHERE code LIKE 'A%') 설계하면, 테이블 데이터에 접근하지 않고 인덱스만 읽어서 처리할 수 있어 성능이 크게 향상됩니다.
공식 문서
더 자세한 정보와 스펙은 아래 MySQL 공식 문서를 참고하시기 바랍니다.
MySQL 8.0 Reference Manual - String Comparison Functions (LIKE) https://dev.mysql.com/doc/refman/8.0/en/string-comparison-functions.html#operator_like
'SQL > MYSQL' 카테고리의 다른 글
| [MySQL] 논리 연산자 완벽 가이드: AND, OR, NOT 제대로 사용하기 (0) | 2026.02.03 |
|---|---|
| [MySQL] WHERE 절 완전 정복: 기본 문법부터 8.0 최적화 팁까지 (0) | 2026.01.30 |
| [MySQL] DISTINCT: 중복된 데이터를 세련되게 처리하는 기술 (기초부터 성능 최적화까지) (0) | 2026.01.29 |
| [MySQL] 백틱(`)의 역할과 올바른 사용법: 언제 써야 하고, 언제 피해야 할까? (0) | 2026.01.28 |
| [MySQL] 습관적으로 쓴 SELECT * 가 당신의 DB를 느리게 만드는 이유 (feat. 실행 계획 분석) (0) | 2026.01.27 |
| [MySQL] 에러 핸들링의 숨은 조력자: SHOW ERRORS 문 완벽 가이드 (0) | 2026.01.26 |
| [MySQL] 스토리지 엔진 확인의 첫걸음: SHOW ENGINES 문법 완벽 가이드 (0) | 2026.01.22 |
| [MySQL] SHOW ENGINE 문법 정복: SHOW ENGINE INNODB STATUS로 잠금·데드락·버퍼 이슈 읽어내기 (1) | 2026.01.20 |