SQL 포맷팅 모범 사례: 깔끔하고 읽기 쉬운 쿼리 작성하기

· 12분 읽기

목차

작동하는 SQL을 작성하는 것과 다른 사람들이 읽고, 이해하고, 유지보수할 수 있는 SQL을 작성하는 것은 별개의 문제입니다. 협업 환경에서 잘못 포맷된 쿼리는 혼란을 야기하고, 버그를 숨기며, 코드 리뷰를 지연시킵니다. 이 가이드는 지저분한 SQL을 팀이 감사해할 깔끔하고 전문적인 코드로 변환하는 필수 포맷팅 관행을 다룹니다.

SQL 기초를 배우는 주니어 개발자든 팀 표준을 수립하는 시니어 엔지니어든, 이러한 관행은 디버그, 리뷰, 최적화가 더 쉬운 쿼리를 작성하는 데 도움이 될 것입니다. 아마추어 SQL과 프로덕션 준비 코드를 구분하는 구체적인 기법들을 살펴보겠습니다.

SQL 포맷팅이 중요한 이유

SQL은 종종 "한 번 작성하면 영원히 실행"으로 취급되지만, 현실은 훨씬 다릅니다. 쿼리는 처음 작성될 때보다 읽고, 수정하고, 디버그하는 경우가 훨씬 더 많습니다. 복잡한 리포트 쿼리는 한 번 작성될 수 있지만 수명 동안 수십 번 검토되고 디버그됩니다.

일관된 포맷팅은 인지 부하를 줄여 논리적 오류를 발견하고, 조인 관계를 이해하고, 성능 병목 현상을 식별하기 쉽게 만듭니다. 연구에 따르면 개발자는 코드를 작성하는 것보다 읽는 데 약 70%의 시간을 소비합니다. 잘 포맷된 SQL은 쿼리 이해 시간을 절반으로 줄여 팀 생산성에 직접적인 영향을 미칠 수 있습니다.

비즈니스 영향을 고려해보세요: 새벽 2시에 중요한 리포트가 중단되면, 당직 엔지니어는 쿼리를 즉시 이해해야 합니다. 잘못된 포맷팅은 5분 수정을 30분 디버깅 세션으로 바꿉니다. 이를 수십 개의 쿼리와 수백 건의 인시던트에 곱하면 비용이 상당해집니다.

프로 팁: SQL 포맷터를 사용하여 전체 코드베이스에 일관된 포맷팅을 자동으로 적용하세요. 이는 스타일에 대한 논쟁을 없애고 모든 쿼리가 동일한 규칙을 따르도록 보장합니다.

잘못된 포맷팅의 실제 비용

가독성을 넘어, 잘못된 SQL 포맷팅은 실질적인 결과를 초래합니다:

일관된 포맷팅 표준을 채택한 팀은 코드 리뷰 주기가 40% 빨라지고 SQL 논리 오류와 관련된 프로덕션 인시던트가 크게 감소했다고 보고합니다.

들여쓰기와 줄 바꿈

적절한 들여쓰기는 읽기 쉬운 SQL의 기초입니다. 각 주요 절은 기본 들여쓰기 수준에서 새 줄로 시작해야 하며, 중첩된 요소는 한 수준 더 깊게 들여쓰기해야 합니다. 이는 쿼리의 논리적 구조를 반영하는 시각적 계층을 만듭니다.

들여쓰기의 황금 규칙

코드베이스 전체에서 2개 또는 4개의 공백을 일관되게 사용하세요. 탭은 편집기, 터미널, 코드 리뷰 도구에서 다르게 렌더링되므로 절대 사용하지 마세요. 하나의 표준을 선택하고 자동화 도구로 강제하세요.

각 주요 절(SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY)은 기본 들여쓰기 수준에서 시작해야 합니다. 컬럼 목록, 조인 조건, 필터 조건은 한 수준 더 깊게 들여쓰기합니다.

SELECT
    u.user_id,
    u.first_name,
    u.last_name,
    u.email,
    u.created_at
FROM users u
WHERE u.status = 'active'
    AND u.email_verified = true
ORDER BY u.created_at DESC;

컬럼 목록 포맷팅

세 개 이상의 컬럼이 있는 쿼리의 경우 각 컬럼을 별도의 줄에 배치하세요. 이 접근 방식은 여러 가지 장점을 제공합니다:

2-3개의 컬럼이 있는 매우 짧은 쿼리의 경우 인라인 포맷팅이 허용됩니다:

SELECT user_id, email FROM users WHERE status = 'active';

하지만 세 개 이상의 컬럼을 초과하거나 복잡성이 추가되면 여러 줄 포맷팅으로 전환하세요.

WHERE 절 포맷팅

각 조건은 AND 또는 OR로 시작하는 별도의 줄을 차지해야 합니다. 이렇게 하면 논리적 흐름이 즉시 명확해지고 개별 조건을 쉽게 주석 처리할 수 있습니다:

SELECT
    o.order_id,
    o.order_date,
    o.total_amount
FROM orders o
WHERE o.order_date >= '2026-01-01'
    AND o.order_date < '2026-04-01'
    AND o.status = 'completed'
    AND o.total_amount > 100.00
    AND (o.payment_method = 'credit_card' OR o.payment_method = 'paypal');

괄호 조건이 짧고 논리적으로 그룹화되어 있을 때는 한 줄로 유지하는 방법에 주목하세요. 복잡한 중첩 조건의 경우 추가 들여쓰기를 추가하세요:

WHERE o.status = 'completed'
    AND (
        (o.payment_method = 'credit_card' AND o.card_type = 'visa')
        OR (o.payment_method = 'paypal' AND o.paypal_verified = true)
        OR (o.payment_method = 'bank_transfer' AND o.transfer_confirmed = true)
    );

빠른 팁: 선행 AND/OR 연산자는 디버깅 중 조건을 주석 처리하기 쉽게 만듭니다. 후행 연산자는 조건과 이전 줄의 연산자를 모두 주석 처리해야 합니다.

키워드 대소문자 규칙

SQL 커뮤니티는 키워드 대소문자에 대해 의견이 나뉘지만, 대문자 키워드가 가장 널리 채택된 규칙으로 남아 있습니다. 대문자 키워드는 SQL 구문과 데이터 요소(테이블 이름, 컬럼 이름, 별칭) 간에 명확한 시각적 구분을 만듭니다.

대문자 키워드가 우세한 이유

대문자 키워드는 여러 가지 실용적인 장점을 제공합니다:

SELECT
    p.product_id,
    p.product_name,
    c.category_name,
    COUNT(o.order_id) AS order_count,
    SUM(o.quantity) AS total_quantity
FROM products p
INNER JOIN categories c
    ON p.category_id = c.category_id
LEFT JOIN order_items o
    ON p.product_id = o.product_id
WHERE p.status = 'active'
GROUP BY p.product_id, p.product_name, c.category_name
HAVING COUNT(o.order_id) > 10
ORDER BY total_quantity DESC;

소문자 대안

일부 팀은 소문자 키워드를 선호하며, 입력하기 쉽고 현대적인 프로그래밍 규칙과 더 일관성이 있다고 주장합니다. 일관되게 적용된다면 이것도 완벽하게 유효합니다:

select
    u.user_id,
    u.username,
    count(p.post_id) as post_count
from users u
left join posts p
    on u.user_id = p.author_id
where u.created_at >= '2026-01-01'
group by u.user_id, u.username;

중요한 요소는 어떤 규칙을 선택하느냐가 아니라 전체 코드베이스에 일관되게 적용하는 것입니다. 혼합된 대소문자는 시각적 노이즈를 만들고 세부 사항에 대한 주의 부족을 시사합니다.

규칙 장점 단점
대문자 명확한 시각적 구분, 범용 표준, 구문 강조 없이도 작동 Shift 키 필요, "소리치는" 느낌
소문자 입력이 빠름, 현대적인 미학, 다른 언어와 일관성 시각적 구분이 적음, 일반 텍스트에서 읽기 어려움
혼합 대소문자 없음 일관성 없음, 비전문적, 혼란스러움

별칭 사용 모범 사례

테이블 별칭은 읽기 쉬운 SQL에 필수적이며, 특히 여러 조인이 있는 쿼리에서 그렇습니다. 좋은 별칭은 간결함과 명확성 사이의 균형을 맞춰 쿼리를 작성하고 이해하기 쉽게 만듭니다.

효과적인 별칭 선택하기

테이블 이름을 기반으로 짧고 의미 있는 약어를 사용하세요. 단일 문자 별칭은 간단한 쿼리에 적합하지만, 다중 문자 별칭은 복잡한 쿼리에서 명확성을 향상시킵니다:

-- 좋음: 명확하고 간결함
SELECT
    u.user_id,
    u.username,
    ord.order_date,
    ord.total_amount,
    prod.product_name
FROM users u
INNER JOIN orders ord
    ON u.user_id = ord.user_id
INNER JOIN order_items oi
    ON ord.order_id = oi.order_id
INNER JOIN products prod
    ON oi.product_id = prod.product_id;

정신적 번역이 필요한 암호 같은 약어는 피하세요. 별칭은 쿼리를 읽는 누구에게나 명확해야 합니다:

-- 나쁨: 불명확한 약어
SELECT
    x.id,
    y.dt,
    z.amt
FROM users x
INNER JOIN orders y ON x.id = y.uid
INNER JOIN payments z ON y.id = z.oid;

명확성을 위한 컬럼 별칭

컬럼 별칭에 AS 키워드를 명시적으로 사용하세요. 대부분의 SQL 방언에서 선택 사항이지만, 의도를 매우 명확하게 만듭니다:

SELECT
    u.first_name || ' ' || u.last_name AS full_name,
    COUNT(o.order_id) AS total_orders,
    SUM(o.total_amount) AS lifetime_value,
    AVG(o.total_amount) AS average_order_value
FROM users u
LEFT JOIN orders o
    ON u.user_id = o.user_id
GROUP BY u.user_id, u.first_name, u.last_name;

컬럼 별칭은 일반적인 데이터베이스 명명 규칙과 일치하도록 snake_case를 사용해야 합니다. 일부 데이터베이스에서 따옴표로 허용하더라도 별칭에 공백을 사용하지 마세요.

프로 팁: 모호함이 없더라도 항상 테이블 별칭으로 컬럼 이름을 한정하세요. 이렇게 하면 쿼리가 나중에 겹치는 컬럼 이름을 가진 추가 테이블을 포함하도록 수정될 때 오류를 방지할 수 있습니다.

별칭을 생략해야 하는 경우

단일 테이블 쿼리의 경우 별칭은 불필요한 복잡성을 추가합니다:

-- 불필요한 별칭
SELECT u.user_id, u.email FROM users u;

-- 더 나음
SELECT user_id, email FROM users;

하지만 조인을 추가하는 즉시 별칭은 명확성을 위해 필수가 됩니다.

명확성을 위한 조인 포맷팅

조인은 SQL 쿼리가 복잡해지는 부분이며, 적절한 포맷팅이 중요합니다. 각 조인은 시각적으로 구별되어야 하며, 조인 조건은 조인 유형과 명확하게 분리되어야 합니다.

조인 포맷팅 구조

각 조인을 기본 들여쓰기 수준에서 별도의 줄에 배치하고, ON 절은 한 수준 더 깊게 들여쓰기하세요. 다중 조건 조인의 경우 각 조건을 별도의 줄에 배치하세요:

SELECT
    u.user_id,
    u.username,
    o.order_id,
    o.order_date,
    p.product_name,
    oi.quantity,
    oi.unit_price
FROM users u
INNER JOIN orders o
    ON u.user_id = o.user_id
INNER JOIN order_items oi
    ON o.order_id = oi.order_id
    AND oi.quantity > 0
INNER JOIN products p
    ON oi.product_id = p.product_id
    AND p.status = 'active'
WHERE u.status = 'active'
    AND o.order_date >= '2026-01-01';

조인 유형 선택 및 명확성

WHERE 절의 암시적 조인보다 항상 명시적 조인 구문(INNER JOIN, LEFT JOIN 등)을 사용하세요. 명시적 조인은 쿼리의 의도를 즉시 명확하게 만듭니다:

-- 나쁨: 암시적 조인
SELECT u.username, o.order_date
FROM users u, orders o
WHERE u.user_id = o.user_id;

-- 좋음: 명시적 조인
SELECT u.username, o.order_date
FROM users u
INNER JOIN orders o
    ON u.user_id = o.user_id;

동등하더라도 명확성을 위해 단순히 JOIN보다 INNER JOIN을 사용하세요. 명시적인 것이 암시적인 것보다 낫습니다.

복잡한 조인 조건

여러 조건이나 복잡한 논리가 있는 조인의 경우 추가 들여쓰기와 그룹화를 사용하세요:

SELECT
    u.user_id,
    u.username,
    s.subscription_type,
    s.start_date
FROM users u
LEFT JOIN subscriptions s
    ON u.user_id = s.user_id
    AND s.status = 'active'
    AND (
        s.subscription_type = 'premium'
        OR (s.subscription_type = 'basic' AND s.trial_period = false)
    )
WHERE u.created_at >= '2026-01-01';
We use cookies for analytics. By continuing, you agree to our Privacy Policy.