백엔드 데이터 인프라 11편 — SELECT 데이터 조회 표준 패턴

2026-05-17백엔드 데이터 인프라

백엔드 데이터 인프라 11편. SELECT 데이터 조회의 표준 패턴 — WHERE·ORDER BY·LIMIT·LIKE·IN·BETWEEN·CASE까지 풀어쓴 학습 노트.

📚 백엔드 데이터 인프라 · 11편 — SELECT 데이터 조회 표준 패턴

이 글은 백엔드 데이터 인프라 시리즈 70편 중 11편이에요. 10편 INSERT 까지 데이터를 박았으니, 이번 11편은 박힌 데이터를 어떻게 꺼내는가 — SQL 가장 자주 쓰이는 동사 SELECT.

SELECT 표준 형태

SELECT  컬럼1, 컬럼2, ...
FROM    테이블
WHERE   조건
GROUP BY 컬럼
HAVING  그룹 조건
ORDER BY 컬럼 [ASC|DESC]
LIMIT   개수
OFFSET  시작 위치;

8개 절(clause) 의 표준 순서. 실행 순서는 약간 다른데 — FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT. 익히면 복잡한 쿼리도 한 흐름으로 읽혀요.

기본 — 전체 / 컬럼 선택

-- 모든 컬럼
SELECT * FROM users;

-- 특정 컬럼
SELECT id, name, email FROM users;

-- 계산
SELECT
    name,
    EXTRACT(YEAR FROM AGE(birth)) AS age
FROM users;

-- 별칭
SELECT
    name        AS user_name,
    email       AS user_email
FROM users;

AS 별칭은 생략 가능한데 명시하는 게 권장이에요.

WHERE — 필터링

비교 연산자

WHERE id = 1
WHERE id <> 1                     -- 같지 않음 (또는 !=)
WHERE age >= 18
WHERE created < NOW() - INTERVAL '7 days'

논리 연산자

WHERE age >= 18 AND status = 'ACTIVE'
WHERE city = 'Seoul' OR city = 'Busan'
WHERE NOT (age < 18)

표준은 AND·OR·NOT 세 개예요.

IN — 여러 값 중 하나

WHERE city IN ('Seoul', 'Busan', 'Daegu')
WHERE id NOT IN (1, 2, 3)
WHERE user_id IN (SELECT id FROM premium_users)

OR를 줄여주는 깔끔한 표현이고, 서브쿼리도 그대로 넣을 수 있어요.

BETWEEN — 범위

WHERE age BETWEEN 18 AND 65
WHERE created BETWEEN '2026-01-01' AND '2026-12-31'

>= + <=를 단축한 형태로, 양 끝을 포함해요.

LIKE — 패턴 매칭

WHERE name LIKE 'A%'              -- A로 시작
WHERE email LIKE '%@example.com'  -- 끝
WHERE name LIKE '_lice'           -- 5글자 + 끝 'lice' (단일 문자)
WHERE name ILIKE 'a%'             -- 대소문자 무시 (PG 특별)

%는 임의 문자열, _는 단일 문자를 뜻해요. ILIKE는 PG(PostgreSQL)에서 제공하는 대소문자 무시 버전이고요.

IS NULL

WHERE email IS NULL
WHERE deleted_at IS NOT NULL

7편 의 NULL 룰 — = NULL 안 됨.

정규식 — ~·~*

WHERE name ~ '^[A-Z]'             -- 정규식
WHERE name ~* '^a'                -- 대소문자 무시 정규식
WHERE name !~ '\d'                -- 매칭 X

PG가 따로 제공하는 기능이고 LIKE보다 강력해요.

ORDER BY — 정렬

-- 기본 = 오름차순
SELECT * FROM users ORDER BY created_at;

-- 명시
SELECT * FROM users ORDER BY created_at DESC;

-- 여러 컬럼
SELECT * FROM users ORDER BY age DESC, name ASC;

-- NULL 처리
SELECT * FROM users ORDER BY last_login NULLS LAST;

NULLS FIRST·NULLS LAST는 NULL을 어디에 둘지 정해요. ASC면 기본이 NULLS LAST, DESC면 NULLS FIRST.

LIMIT·OFFSET — 페이지네이션

-- 처음 10건
SELECT * FROM users ORDER BY id LIMIT 10;

-- 다음 10건
SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 10;

-- 페이지 3 (10건씩)
SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 20;

페이지네이션 표준이긴 한데 — OFFSET이 깊어지면 느려져요 (1000번째 페이지면 9990건을 건너뛰는 비용). 큰 데이터는 "커서 페이지네이션" (다음 ID > N) 을 권장.

-- 커서 페이지네이션
SELECT * FROM users WHERE id > 100 ORDER BY id LIMIT 10;

DISTINCT — 중복 제거

SELECT DISTINCT city FROM users;
SELECT DISTINCT city, country FROM users;     -- 조합 unique

DISTINCT ON — PG 특별

-- 각 사용자별 최근 주문 1건
SELECT DISTINCT ON (user_id) *
FROM orders
ORDER BY user_id, created_at DESC;

표준 SQL엔 없는 PG의 강력한 기능이에요. "그룹별 첫 행" 을 가져올 때 씁니다.

CASE — 조건 표현식

SELECT
    name,
    CASE
        WHEN age < 18 THEN '미성년'
        WHEN age < 65 THEN '성인'
        ELSE '시니어'
    END AS age_group
FROM users;

SQL 안의 if-else라고 보면 돼요. WHERE·ORDER BY 안에도 넣을 수 있고요.

짧은 형태

CASE status
    WHEN 'PENDING'  THEN '대기'
    WHEN 'PAID'     THEN '결제완료'
    ELSE            '기타'
END

NULL 처리 함수

COALESCE(email, 'no-email@example.com')   -- 첫 NULL 아닌 값
NULLIF(value, 0)                           -- value = 0이면 NULL

COALESCE는 백엔드에서 가장 자주 쓰는 함수예요. 디폴트 값 대체할 때 단골.

함수 — 자주 쓰는 30개 중 10개

함수 의미
LENGTH(s) 문자열 길이 LENGTH('hello') = 5
LOWER(s)·UPPER(s) 대소문자
TRIM(s) 양 끝 공백 제거
SUBSTRING(s FROM 1 FOR 3) 부분 문자열
CONCAT(s1, s2) 또는 s1 \|\| s2 연결
NOW() 현재 시각
CURRENT_DATE 오늘 날짜
DATE_TRUNC('day', ts) 단위 절삭
EXTRACT(YEAR FROM ts) 부분 추출
AGE(birth) 나이 (interval)

집계 함수

SELECT COUNT(*) FROM users;
SELECT COUNT(DISTINCT city) FROM users;
SELECT AVG(amount) FROM orders;
SELECT SUM(amount), MIN(amount), MAX(amount) FROM orders;

다음 14편 GROUP BY 에서 더 깊이 들어가요.

페이지네이션 실전 예

-- 사용자 목록, 최근 가입 순, 페이지당 20명, 3페이지
SELECT id, name, email, created_at
FROM users
WHERE deleted_at IS NULL
ORDER BY created_at DESC
LIMIT 20 OFFSET 40;

이게 한국 백엔드 "목록 조회 API" 의 99% 패턴이에요.

Spring 백엔드 흐름

자바 백엔드 입문 49편 쿼리 메서드 의 JPA(자바 ORM 표준)가 생성하는 SELECT:

// 메서드 이름 쿼리
List<User> findByCityAndAgeGreaterThan(String city, int age);
// → SELECT * FROM users WHERE city = ? AND age > ?

JPA가 자동 생성하는 SQL은 단순한 편이라 — "복잡한 SELECT는 직접 짜야" 해요. 자바 백엔드 입문 50편 QueryDSL(타입 안전 쿼리 빌더) 또는 native SQL.

함정 5가지

(1) SELECT * 운영 남용

SELECT * FROM users;   -- ❌ 운영 코드에 X

새 컬럼 추가되면 코드가 깨져요. 운영에선 컬럼을 명시하는 게 원칙.

(2) ORDER BY 없는 LIMIT

SELECT * FROM users LIMIT 10;   -- ❌ 어떤 10건일지 모름

LIMIT을 박을 때는 ORDER BY가 필수예요.

(3) LIKE 와일드카드 앞

WHERE name LIKE '%alice'   -- ❌ 인덱스 사용 X — 풀스캔
WHERE name LIKE 'alice%'   -- ✅ 인덱스 사용

앞에 %가 박힌 LIKE는 인덱스를 못 써요. 36편 인덱스 참고.

(4) OFFSET 깊음

LIMIT 10 OFFSET 1000000   -- 백만 건 스킵 — 느림

큰 데이터에선 커서 페이지네이션으로 가요.

(5) WHERE 안 NULL = NULL

WHERE col = NULL    ❌
WHERE col IS NULL   ✅

7편 참고.

🎯 SELECT 실전 5가지

(1) 컬럼 명시. (2) WHERE에 인덱스 가능한 조건. (3) ORDER BY + LIMIT 페이지네이션. (4) CASE·COALESCE로 표현식. (5) 깊은 OFFSET은 커서로.

한 줄 정리 — SELECT 8개 절 = SELECT·FROM·WHERE·GROUP BY·HAVING·ORDER BY·LIMIT·OFFSET. WHERE의 비교·LIKE·IN·BETWEEN·IS NULL 5종. ORDER BY는 LIMIT의 짝. DISTINCT ON·ILIKE·::·CASE 가 PG 강점.

시험 직전 한 번 더 — SELECT 입문자가 매번 헷갈리는 것

  • 절 순서 = SELECT·FROM·WHERE·GROUP BY·HAVING·ORDER BY·LIMIT·OFFSET
  • 실행 순서 = FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
  • SELECT * = 개발만, 운영은 명시
  • 비교 = =·<>·>=·<=·>·<
  • IN = 여러 값 중 하나 (서브쿼리 가능)
  • BETWEEN = >= + <= 단축 (양 끝 포함)
  • LIKE = 와일드카드 %·_
  • ILIKE = PG 대소문자 무시 LIKE
  • 정규식 = ~·~* (PG 특별)
  • IS NULL = NULL 비교 (= NULL 안 됨)
  • ORDER BY = ASC (기본) · DESC
  • NULL 위치 = NULLS FIRST·NULLS LAST
  • LIMIT 박을 때 = ORDER BY 필수
  • OFFSET 깊으면 느림 — 커서 페이지네이션
  • DISTINCT = 중복 제거
  • DISTINCT ON = 그룹별 첫 행 (PG 특별)
  • CASE WHEN ... THEN ... ELSE ... END
  • COALESCE = 첫 NULL 아닌 값
  • NULLIF(a, b) = a == b면 NULL
  • 자주 쓰는 함수 = LENGTH·LOWER·SUBSTRING·NOW·DATE_TRUNC·COALESCE
  • 페이지네이션 실전 = WHERE deleted_at IS NULL ORDER BY created_at DESC LIMIT N OFFSET M
  • LIKE 앞 % = 인덱스 X
  • 백엔드 99% = SELECT 가 가장 자주

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글:

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!