자연어에서 타입 안전 SQL을 Java로 구현하는 방법: Querier AI와 함께
사용자가 복잡한 필터를 탐색하거나 SQL을 작성하는 대신, 평범한 영어(plain English)로 애플리케이션 데이터에 대한 질문을 할 수 있다면 어떨까요?
예를 들어:
지난 30일 동안 총 매출 기준으로 상위 10개 고객을 보여주세요.
최근 저는 오픈 소스 Java SQL 쿼리 빌더인 Querier의 선택적 모듈인 Querier AI를 출시했습니다. 이는 자연어 프롬프트를 검증된 Querier Select 객체로 변환하여, 데이터베이스 접근 권한을 AI 모델에 직접 넘기지 않고도 AI 기반 쿼리를 사용할 수 있게 해줍니다.
이 튜토리얼에서는 이를 Spring Boot 애플리케이션에 통합하고 Spring JDBC를 사용하여 결과 쿼리를 실행하는 방법을 다룹니다.
무엇을 만들 것인가
요청 흐름은 간단합니다:
- 사용자가 자연어 질문을 제출합니다.
- Querier AI는 노출된 스키마를 기반으로 SQL을 생성합니다.
- 이 SQL은 해당 스키마와 검증되고 Querier
Select 객체로 파싱됩니다.
- 애플리케이션은 기존 데이터베이스 계층을 통해 결과 쿼리를 실행합니다.
- 애플리케이션이 결과를 표시합니다.
중요한 차이점은 AI 작업의 결과가 단순히 눈가림으로 실행할 SQL 문자열이 아니라, Java 애플리케이션이 검사하고 사용할 수 있는 Querier 객체라는 점입니다.
1. 의존성 추가하기
Querier AI는 선택적 모듈이므로, 핵심 쿼리 빌더만 사용하려면 AI 관련 의존성을 추가할 필요가 없습니다.
Maven의 경우, pom.xml에 querier-ai를 추가하세요:
<dependency>
<groupId>com.aytmatech</groupId>
<artifactId>querier-ai</artifactId>
...
이 예제는 Querier 2.0.0을 사용합니다. 최신 버전은 GitHub releases를 확인하세요.
Java 17 이상, 데이터베이스 접근 기능이 있는 Spring 애플리케이션, 그리고 쿼리하려는 테이블을 설명하는 엔티티 클래스가 필요합니다.
2. AI 엔드포인트 구성하기
Querier AI는 bring-your-own-key 접근 방식을 따릅니다. OpenAI와 Azure OpenAI, Mistral, Ollama와 호환되는 엔드포인트를 포함하여 OpenAI와 호환되는 환경에서 작동합니다.
자신의 소스 코드에 자격 증명을 하드코딩하는 대신 환경 변수를 통해 제공업체를 구성하세요:
export QUERIER_AI_API_KEY="your-api-key"
export QUERIER_AI_BASE_URL="https://api.openai.com/v1"
export QUERIER_AI_MODEL="gpt-4o-mini"
사용하는 제공업체에 맞는 적절한 기본 URL, 모델 또는 배포 식별자 및 자격 증명을 사용하세요.
다음으로 AI 클라이언트와 QuerierAi 서비스를 Spring 빈(bean)으로 등록합니다:
import com.aytmatech.querier.ai.AiClient;
import com.aytmatech.querier.ai.QuerierAi;
import com.aytmatech.querier.ai.retry.RetryPolicy;
...
재시도 정책(retry policy)은 생성된 SQL이 유효하지 않을 때 Querier AI가 수정 시도를 할 수 있도록 합니다. 애플리케이션에 맞게 이 정책을 조정할 수 있습니다.
API 키는 로그, 소스 제어 및 클라이언트 측 코드에서 제외하세요. 프로덕션 환경에서는 배포 플랫폼의 비밀 관리 시설(secret-management facilities)을 사용하세요.
3. AI가 볼 수 있는 엔티티 선택하기
노출하는 스키마는 이 통합의 중요한 부분입니다.
애플리케이션에 Order, Customer, Product라는 엔티티 클래스가 있다고 가정해 봅시다. AI의 스키마 컨텍스트를 이러한 클래스로 제한할 수 있습니다:
private static final Class<?>[] EXPOSED_ENTITIES = {
Order.class,
Customer.class,
...
첫 번째 클래스는 인스펙터(inspector)에 의해 기본 엔티티로 처리되므로, 주요 엔티티를 먼저 배치하세요.
Querier AI는 노출된 클래스와 해당 매핑을 검사하여 스키마 컨텍스트를 구축할 수 있습니다:
import com.aytmatech.querier.ai.model.AiSchemaContext;
import com.aytmatech.querier.ai.model.AiSchemaInspector;
...
엔티티 매핑이 데이터베이스 스키마를 올바르게 설명하는지 확인하세요. Querier는 자체 테이블 및 컬럼 어노테이션뿐만 아니라 지원되는 JPA 어노테이션도 지원합니다.
AI가 'sales', 'revenue', 또는 'transactions'와 같은 도메인별 용어를 이해하도록 돕기 위해 의미론적 설명(semantic descriptions)과 동의어(synonyms)를 추가할 수도 있습니다. 자세한 내용은 Querier 문서를 참조하세요.
4. 프롬프트를 검증된 쿼리로 변환하기
Spring MVC 컨트롤러의 핵심을 구현해 보겠습니다.
이 컨트롤러는 프롬프트(prompt)를 받아 QueryRequest를 생성하고, 이를 QuerierAi.query()로 전송합니다.
import com.aytmatech.querier.Select;
import com.aytmatech.querier.ai.QuerierAi;
import com.aytmatech.querier.ai.model.AiSchemaContext;
...
이것이 쿼리 엔드포인트(query endpoint)이며, 초기 페이지를 표시하기 위한 GET 핸들러나 다른 경로도 필요합니다. 예제는 애플리케이션에 엔티티 클래스(entity classes)와 ai/index.html의 Thymeleaf 템플릿이 있다고 가정합니다.
주목할 만한 세부 사항들이 몇 가지 있습니다:
temperature(0.0)은 이 쿼리 생성 흐름에 대해 낮은 변동성 응답을 요청합니다.
result.isValid()를 사용하면 실행 전에 검증 실패를 처리할 수 있습니다.
result.query()는 검증된 Select 객체를 제공합니다.
toSqlAndParams()는 NamedParameterJdbcTemplate과 함께 작동하는 형태로 SQL 및 이름 지정 파라미터(named parameters)를 반환합니다.
컨트롤러는 또한 Querier AI가 해석 주석(interpretation note)을 제공할 때 이를 뷰(view)에 사용할 수 있도록 합니다.
5. 간단한 폼과 결과 테이블 추가하기
이 기능을 시도하는 데 특별한 프론트엔드 프레임워크가 필요하지 않습니다. 기본적인 Thymeleaf 폼만으로 충분합니다:
<form method="post" th:action="@{/ai/query}">
<textarea
name="prompt"
...
Spring Security가 활성화된 경우, 폼에 적절한 CSRF 보호(CSRF protection)를 포함했는지 확인하세요.
검증 오류를 표시하려면:
<div th:if="${queryError}" class="alert alert-error">
<strong>유효한 쿼리를 생성할 수 없습니다:</strong>
<pre th:text="${queryError}"></pre>
...
결과 행을 동적으로 렌더링하려면:
<table th:if="${queryColumns != null and !queryColumns.isEmpty()}">
<thead>
<tr>
...
열 이름과 행 값은 쿼리 결과에서 파생되므로, 동일한 템플릿으로 다양한 쿼리 형태를 표시할 수 있습니다. 완전한 애플리케이션에서는 결과가 0개인 성공적인 쿼리에 대한 별도의 빈 상태 메시지를 추가해야 합니다.
6. 보안은 어떻습니까?
생성된 쿼리를 노출된 스키마와 검증하는 것은 중요한 안전장치이지만, 애플리케이션의 일반적인 보안 제어를 대체해서는 안 됩니다.
프로덕션 환경에서 사용하기 전에:
- 기능이 쿼리할 수 있어야 하는 엔티티와 열만 노출하십시오.
- 최소한의 권한을 가진 데이터베이스 계정을 사용하십시오.
- 애플리케이션에 사용자 인증(user authorization), 테넌트 격리(tenant isolation), 쿼리 시간 초과(query timeouts), 그리고 합리적인 결과 크기 제한을 적용하십시오.
- 유효한 쿼리가 모든 사용자에게 자동으로 승인된다고 가정하지 마십시오.
- 생성된 SQL이나 데이터베이스 세부 정보를 최종 사용자에게 노출하는 것에 주의하십시오.
사용자가 어떤 데이터에 접근할 수 있는지 결정하고 쿼리를 안전하게 실행하는 책임은 여전히 애플리케이션에 있습니다.
Querier AI 사용해 보기
이제 자연어 질문을 받아 검증된 Querier Select로 변환한 다음, 이미 사용 중인 데이터베이스 계층을 통해 실행할 수 있습니다.
AI 모듈은 선택 사항이며, 나머지 Querier는 Java 쿼리 빌딩 라이브러리로 남아 있습니다.
프로젝트 및 소스 코어를 살펴보세요:
다음 튜토리얼에서는 동일한 AI 통합을 사용하여 단일 자연어 설명으로부터 전체 대시보드를 생성할 것입니다.