본문으로 바로가기
2222

COUNT(*) 대신 EXISTS 써보기

category Backend 2025. 9. 8. 23:23

안녕하세요, 백엔드 개발자입니다.

백엔드 로직을 구현하다 보면, 특정 조건에 맞는 데이터가 '존재하는지' 여부만 확인하면 되는 경우가 많습니다. 예를 들면 다음과 같은 상황입니다.

  • 사용자가 닉네임 중복 확인을 요청했을 때
  • 게시글에 좋아요를 눌렀는지 여부를 확인할 때

이런 경우, 가장 먼저 떠오르는 방법은 COUNT(*)를 사용하는 것입니다.

SELECT COUNT(*)
FROM users
WHERE nickname = 'supiz';

이 쿼리의 결과가 0보다 크면 '존재한다', 0이면 '존재하지 않는다'고 판단하는 방식이죠.

매우 직관적이고 저 또한 오랫동안 습관적으로 사용해왔습니다.

 

하지만 '존재 여부' 확인이 목적이라면, COUNT(*)는 성능상 비효율을 유발할 수 있습니다.

이번 글에서는 이런 상황에서 EXISTS를 사용하는 것이 왜 더 좋은 선택인지 알아보겠습니다.

1. COUNT(*)는 모든 것을 세어본다

COUNT(*)의 가장 큰 특징은 조건에 맞는 데이터를 모두 찾아서 그 개수를 센다는 점입니다.

위의 닉네임 중복 확인 쿼리를 예로 들어보겠습니다. nickname 컬럼에 인덱스가 걸려있다고 하더라도, 데이터베이스는 nickname = 'supiz'인 데이터를 끝까지 모두 스캔하여 총 몇 개인지 집계합니다.

우리가 원하는 것은 'supiz'라는 닉네임이 단 한 개라도 있는지, 즉 존재 여부인데도 말이죠. 데이터가 수백만 건이고 우연히 중복된 닉네임이 많다면, 이 단순한 확인 작업은 생각보다 많은 리소스를 사용하게 됩니다.

2. EXISTS는 하나만 찾으면 멈춘다

반면, EXISTS는 다른 방식으로 동작합니다. EXISTS는 서브쿼리(Subquery)와 함께 사용되며, 서브쿼리에서 단 한 개의ロウ(row)라도 반환되면 즉시 TRUE를 반환하고 쿼리 실행을 멈춥니다.

-- 'supiz' 닉네임이 존재하는지 확인
SELECT EXISTS (
    SELECT 1
    FROM users
    WHERE nickname = 'supiz'
);

* EXISTS 서브쿼리 안의 SELECT 절에는 1이나 * 등 어떤 것을 써도 성능에 차이가 없습니다. 중요한 것은 조건에 맞는 데이터가 있느냐 없느냐 뿐입니다.

 

EXISTS는 COUNT(*)처럼 모든 데이터를 스캔할 필요가 없습니다. 조건에 맞는 데이터를 딱 하나만 찾으면, 그 즉시 "데이터가 존재한다"고 판단하고 자신의 임무를 끝냅니다.

3. 언제 무엇을 써야 할까?

그렇다면 COUNT(*)는 항상 나쁜 것일까요? 물론 아닙니다. 두 키워드는 목적에 맞게 사용하는 것이 중요합니다.

  • COUNT(*)를 사용해야 할 때
    • 정확한 개수가 필요할 때
    • 예: "게시물의 총 댓글 수: 123개", "전체 회원 수: 5,432명"
    • 목록 조회 API에서 전체 아이템 개수를 알려주어 페이지네이션을 구현할 때
  • EXISTS를 사용해야 할 때
    • 데이터의 존재 여부(Boolean)만 확인하면 될 때
    • 예: "이 닉네임은 사용 가능한가요?", "이미 좋아요를 누른 게시물인가요?"
    • IF문이나 CASE문처럼 조건 분기에 활용할 때

4. 정리하며

단순히 데이터의 존재 유무를 확인하는 로직이라면, 습관적으로 사용하던 COUNT(*) > 0 대신 EXISTS를 사용해 보는 것을 추천합니다. 사소해 보일 수 있는 이 변화가 데이터가 많은 테이블에서는 의미 있는 성능 개선으로 이어질 수 있습니다.

목적에 맞는 SQL 키워드를 선택하는 것은 안정적인 백엔드 시스템을 만드는 중요한 첫걸음입니다.

긴 글 읽어주셔서 감사합니다.