당신의 AI는 '오늘'이 무엇을 의미하는지 모릅니다
요약
AI가 시간 기반 데이터를 처리할 때 발생할 수 있는 논리적 오류와 이를 방지하기 위한 데이터 엔지니어링 가이드를 제공합니다. 타임존, 보고 경계, 지연 도착 정책 등을 명확히 정의하여 신뢰할 수 있는 데이터 답변을 도출하는 방법을 설명합니다.
핵심 포인트
- 단순 CURRENT_DATE 사용 대신 IANA 식별자를 통한 로컬 경계 계산 권장
- 일광 절약 시간제를 고려하여 UTC 변환 및 반개방 구간(Half-open interval) 쿼리 사용
- 비즈니스 시간대, 보고 차단 시점, 지연 도착 정책 등 명확한 데이터 계약 필요
- 데이터의 신선도와 비교 기준을 답변에 명시하여 투명성 확보
“오늘의 매출은 얼마인가요?”라는 질문은 쉬운 데이터베이스 질문처럼 들립니다.
하지만 그렇지 않습니다.
어떤 시간대 (Timezone)가 오늘을 정의할까요?
보고일 (Reporting day)은 자정에 종료되나요, 아니면 결제 배치 (Settlement batch) 이후에 종료되나요?
환불은 원래 판매 날짜에 속하나요, 아니면 환불 게시 날짜에 속하나요?
어제의 이벤트가 내일 도착하면 어떻게 될까요?
SQL은 이러한 질문 중 어느 것에도 답하지 않고도 성공적으로 실행될 수 있습니다.
그것이 바로 AI가 잘못된 의미를 가진 정확한 숫자를 반환하는 방식입니다.
CURRENT_DATE는 비즈니스 정의가 아닙니다
UTC 타임스탬프 (Timestamps)가 포함된 결제 테이블이 있다고 가정해 봅시다. 코펜하겐의 한 사용자가 현지 시간으로 00:15에 오늘의 매출을 묻습니다.
단순한 쿼리는 다음과 같이 사용합니다:
WHERE occurred_at::date = CURRENT_DATE
결과는 데이터베이스 세션 시간대 (Database session timezone)에 따라 달라집니다. 개발자의 노트북, UTC 연결 풀 (Connection pool), 그리고 지역 보고 서비스는 동일한 문장에 대해 서로 다른 합계를 반환할 수 있습니다.
모델이 환각 (Hallucinate)을 일으킨 것이 아닙니다.
시스템이 보고 경계 (Reporting boundary)를 정의하지 않았을 뿐입니다.
로컬 경계를 정의한 다음 UTC로 쿼리하세요
안전한 패턴은 다음과 같습니다:
- 비즈니스 시간대를
Europe/Copenhagen과 같은 IANA 식별자로 선택합니다. - 로컬 시작 및 종료 경계를 계산합니다.
- 두 경계를 모두 UTC로 변환합니다.
- 반개방 구간 (Half-open interval)으로 쿼리합니다.
WHERE occurred_at >= :start_utc
AND occurred_at < :end_utc
UTC 시작 시간에 24시간을 더해서 종료 시간을 계산하지 마세요. 로컬의 하루는 일광 절약 시간제 (Daylight-saving) 전환 기간 동안 23시간 또는 25시간이 될 수 있습니다.
모든 시간 기반 지표에는 계약이 필요합니다
최소한 다음 사항들을 정의해야 합니다:
- 비즈니스 시간대 (Business timezone)
- 로컬 보고 차단 시점 (Local reporting cutoff)
- 이벤트를 특정 기간에 할당하는 타임스탬프 (Timestamp)
- 반개방 경계 규칙 (Half-open boundary rule)
[start, end) - 지연 도착 정책 (Late-arrival policy)
- 신선도 워터마크 (Freshness watermark)
- 비교 창 규칙 (Comparison-window rule)
“오늘 대 어제” 역시 대칭성이 필요합니다.
오전 10:00에, '오늘-현재'를 '어제의 전체 시간'과 비교하시겠습니까? 아니면 어제의 동일한 경과 로컬 시간과 비교하시겠습니까? 아니면 마지막으로 완료된 두 비즈니스 영업일과 비교하시겠습니까?
각각은 논리적으로 방어 가능합니다. 하지만 서로 대체될 수 없습니다.
신뢰할 수 있는 답변은 그 선택 사항을 드러내야 합니다:
Europe/Copenhagen 시간대 기준 00:00–10:00 사이의 캡처된 매출은 X이며, 이는 어제의 동일한 경과 시간대와 비교한 수치입니다. 소스 데이터는 09:56까지 완료되었습니다. 해당 기간은 잠정적(provisional)입니다.
시간대(timezone), 경계(boundary), 비교 형태(comparison shape), 그리고 최신성(freshness)은 답변의 일부여야 하며, 숨겨진 구현 세부 사항(implementation details)이 되어서는 안 됩니다.
지연된 데이터는 진실을 변화시킵니다
00:05에 실행된 보고서와 08:00에 실행된 동일한 보고서의 결과가 정당하게 다를 수 있습니다. 결제 처리기(Payment processors)는 웹훅(webhooks)을 재시도합니다. 창고는 배치(batches) 작업을 늦게 마칩니다. 모바일 기기는 다시 연결됩니다.
정직한 정책을 선택하십시오:
- 과거 기간은 기준 시점(as-of timestamp)과 수정 사항을 포함하여 재진술될 수 있습니다.
- 마감 시간(closing window)이 만료될 때까지 기간은 잠정적(provisional) 상태로 유지됩니다.
- 변경 사항을 원장 기입 기간(ledger posting period)에 할당하여 이미 마감된 보고서가 안정적으로 유지되도록 합니다.
AI는 이들 중 하나를 조용히 선택해서는 안 됩니다.
SQL뿐만 아니라 달력을 테스트하십시오
다음 상황에 대한 시드 픽스처(Seed fixtures)를 준비하십시오:
- 정확히 자정
- 컷오프(cutoff) 1마이크로초 전
- 일광 절약 시간제(daylight-saving) 전환 시점 양쪽 모두
- 서로 다른 시간대를 가진 두 개의 테넌트(tenants)
- 지연 도착하는 이벤트(late-arriving events)
- 이후 기간에 기입된 환불(refunds)
- 월말 및 윤년(leap day)
- 혼합된 통화(mixed currencies)
시계를 고정하고 정확한 UTC 경계와 포함된 행 세트(row set)를 단언(assert)하십시오.
최종 합계만 테스트하면, 가장 중요한 드문 날이 올 때까지 경계값 오류(off-by-one boundary)를 숨길 수 있습니다.
자연어는 유용한 인터페이스입니다. 하지만 “오늘”, “이번 주”, “지난달”과 같은 단어는 비즈니스 규칙(business rules)입니다. 모델은 프롬프트에서 이를 스스로 만들어내는 대신, 관리되는 데이터베이스 도구로부터 해당 규칙을 전달받아야 합니다.
전체 계약(contract), PostgreSQL 예시, 결과 형태, 그리고 테스트 매트릭스는 여기에서 확인할 수 있습니다:
ChatGPT database query: define timezone and cutoff contracts before reporting
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기