
NestJS와 Prisma를 사용하면서 복잡하거나 동적인 SQL 쿼리를 다룰 때, MyBatis의 XML 맵퍼처럼 별도의 파일에서 SQL 소스를 관리하고 싶을 수 있습니다. Prisma의 raw SQL 기능을 활용하여 이와 유사한 방식으로 SQL을 분리하고 호출하는 방법을 소개합니다.
💡 핵심 아이디어: SQL 파일 분리 및 로드
Prisma는 raw SQL 쿼리를 문자열로 받기 때문에, 이 문자열을 별도의 XML 또는 .sql 파일에서 읽어와 사용하면 됩니다. XML을 파싱하는 라이브러리를 사용하거나, 단순하게 .sql 파일에 쿼리를 저장하고 파일 시스템 모듈(fs)로 불러오는 방식을 고려할 수 있습니다. 여기서는 더 간단하고 일반적인 .sql 파일을 사용하는 방법을 예시로 들어 설명합니다.
1. 📂 프로젝트 구조 설정
SQL 쿼리를 저장할 디렉토리를 만듭니다.
src/
├── module/
│ └── example/
│ ├── example.service.ts
│ └── example.controller.ts
└── resources/ <- SQL 파일을 저장할 디렉토리
└── sql/
└── findUserByName.sql
resources/sql/findUserByName.sql 내용:
SELECT
id,
name,
email
FROM
"User"
WHERE
name = $1;
참고: Prisma raw SQL에서 매개변수는 보통 $1, $2 등으로 지정됩니다.
2. 📝 SQL 파일 로드 유틸리티 구현
SQL 파일을 읽어 문자열로 반환하는 유틸리티 함수를 만듭니다.
src/utils/sql-loader.util.ts:
import * as fs from 'fs';
import * as path from 'path';
/**
* resources/sql 디렉토리에서 SQL 쿼리 파일을 읽어옵니다.
* @param filename 쿼리 파일 이름 (예: 'findUserByName.sql')
* @returns SQL 쿼리 문자열
*/
export const loadSqlFile = (filename: string): string => {
// 프로젝트 루트 기준 경로
const filePath = path.join(process.cwd(), 'src', 'resources', 'sql', filename);
if (!fs.existsSync(filePath)) {
throw new Error(`SQL file not found: ${filePath}`);
}
// 파일 내용을 동기적으로 읽어와 문자열로 반환
return fs.readFileSync(filePath, 'utf8');
};
3. 🚀 NestJS 서비스에서 Raw SQL 실행
구현한 loadSqlFile 함수를 사용하여 SQL을 불러온 후, Prisma 클라이언트의 $queryRaw 또는 $executeRaw 메서드를 이용해 실행합니다.
src/module/example/example.service.ts:
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service'; // Prisma 모듈을 주입했다고 가정
import { loadSqlFile } from '../../utils/sql-loader.util';
import { Prisma } from '@prisma/client';
// 쿼리 결과 타입을 정의
interface UserQueryResult {
id: number;
name: string;
email: string;
}
@Injectable()
export class ExampleService {
constructor(private prisma: PrismaService) {}
async findUser(userName: string): Promise<UserQueryResult[]> {
// 1. 외부 파일에서 SQL 쿼리 로드
const sqlQuery = loadSqlFile('findUserByName.sql');
// 2. Prisma의 $queryRaw<T>() 메서드를 사용하여 쿼리 실행
// 매개변수는 배열 형태로 전달하며, SQL 파일의 $1에 매핑됨.
const users = await this.prisma.$queryRaw<UserQueryResult[]>(
Prisma.sql`${sqlQuery}`,
userName
);
// 이전 Prisma 버전 또는 다른 DB 드라이버에서는 아래처럼 사용할 수도 있습니다.
// const users = await this.prisma.$queryRaw<UserQueryResult[]>(
// sqlQuery,
// userName
// );
return users;
}
}
핵심 포인트: Prisma.sql 태그
Prisma의 템플릿 리터럴 태그인 Prisma.sql을 사용하면 SQL 인젝션 공격을 방지하고 매개변수를 안전하게 처리할 수 있습니다. 로드된 SQL 문자열을 이 태그 안에 넣고, 이어서 매개변수들을 전달하는 것이 가장 권장되는 방식입니다.
4. ✅ 장점 및 고려사항
| 구분 | 내용 |
| 장점 | SQL 로직을 서비스 로직에서 분리하여 가독성과 유지보수성 향상 |
| 복잡한 쿼리나 동적 쿼리 관리가 용이 | |
| 고려사항 | MyBatis의 강력한 동적 쿼리(if, choose 등) 기능은 직접 구현해야 함 |
| 파일을 로드하는 과정에서 I/O 비용 발생 (초기 로딩 후 캐싱을 고려할 수 있음) | |
| XML 대안 | XML 대신 .sql 파일을 사용하면 파싱 로직 없이 단순 문자열 로드만으로 충분 |
'Nest.js를 배워보자' 카테고리의 다른 글
| 🔄 MySQL CREATE TABLE 문으로 Prisma 스키마 만들기 (0) | 2025.12.09 |
|---|---|
| 🛠️ NestJS Prisma 6에서 자료가 없을 때 Seed 데이터 자동 추가하기 (0) | 2025.12.09 |
| ✨ Prisma 7과 NestJS, MySQL로 시작하기: 차세대 ORM 가이드 (1) | 2025.12.09 |
| 💡 NestJS에서 Gateway란? (0) | 2025.12.05 |
| 📢 NestJS: 멀티 WebSocket 서버 운용 전략 (4000번 & 4001번) (0) | 2025.12.05 |