10 KiB
10 KiB
📋 Phase 3.9: TemplateStandardService Raw Query 전환 계획
📋 개요
TemplateStandardService는 6개의 Prisma 호출이 있으며, 템플릿 표준 관리를 담당하는 서비스입니다.
📊 기본 정보
| 항목 | 내용 |
|---|---|
| 파일 위치 | backend-node/src/services/templateStandardService.ts |
| 파일 크기 | 395 라인 |
| Prisma 호출 | 6개 |
| 현재 진행률 | 7/7 (100%) ✅ 전환 완료 |
| 복잡도 | 낮음 (기본 CRUD + DISTINCT) |
| 우선순위 | 🟢 낮음 (Phase 3.9) |
| 상태 | ✅ 완료 |
🎯 전환 목표
- ✅ 7개 모든 Prisma 호출을
db.ts의query(),queryOne()함수로 교체 - ✅ 템플릿 CRUD 기능 정상 동작
- ✅ DISTINCT 쿼리 전환
- ✅ Promise.all 병렬 쿼리 (목록 + 개수)
- ✅ 동적 UPDATE 쿼리 (11개 필드)
- ✅ TypeScript 컴파일 성공
- ✅ Prisma import 완전 제거
🔍 Prisma 사용 현황 분석
주요 Prisma 호출 (6개)
1. getTemplateByCode() - 템플릿 단건 조회
// Line 76
return await prisma.template_standards.findUnique({
where: {
template_code: templateCode,
company_code: companyCode,
},
});
2. createTemplate() - 템플릿 생성
// Line 86
const existing = await prisma.template_standards.findUnique({
where: {
template_code: data.template_code,
company_code: data.company_code,
},
});
// Line 96
return await prisma.template_standards.create({
data: {
...data,
created_date: new Date(),
updated_date: new Date(),
},
});
3. updateTemplate() - 템플릿 수정
// Line 164
return await prisma.template_standards.update({
where: {
template_code_company_code: {
template_code: templateCode,
company_code: companyCode,
},
},
data: {
...data,
updated_date: new Date(),
},
});
4. deleteTemplate() - 템플릿 삭제
// Line 181
await prisma.template_standards.delete({
where: {
template_code_company_code: {
template_code: templateCode,
company_code: companyCode,
},
},
});
5. getTemplateCategories() - 카테고리 목록 (DISTINCT)
// Line 262
const categories = await prisma.template_standards.findMany({
where: {
company_code: companyCode,
},
select: {
category: true,
},
distinct: ["category"],
});
📝 전환 계획
1단계: 기본 CRUD 전환 (4개 함수)
함수 목록:
getTemplateByCode()- 단건 조회 (findUnique)createTemplate()- 생성 (findUnique + create)updateTemplate()- 수정 (update)deleteTemplate()- 삭제 (delete)
2단계: 추가 기능 전환 (1개 함수)
함수 목록:
getTemplateCategories()- 카테고리 목록 (findMany + distinct)
💻 전환 예시
예시 1: 복합 키 조회
// 기존 Prisma
return await prisma.template_standards.findUnique({
where: {
template_code: templateCode,
company_code: companyCode,
},
});
// 전환 후
import { queryOne } from "../database/db";
return await queryOne<any>(
`SELECT * FROM template_standards
WHERE template_code = $1 AND company_code = $2`,
[templateCode, companyCode]
);
예시 2: 중복 확인 후 생성
// 기존 Prisma
const existing = await prisma.template_standards.findUnique({
where: {
template_code: data.template_code,
company_code: data.company_code,
},
});
if (existing) {
throw new Error("이미 존재하는 템플릿 코드입니다.");
}
return await prisma.template_standards.create({
data: {
...data,
created_date: new Date(),
updated_date: new Date(),
},
});
// 전환 후
const existing = await queryOne<any>(
`SELECT * FROM template_standards
WHERE template_code = $1 AND company_code = $2`,
[data.template_code, data.company_code]
);
if (existing) {
throw new Error("이미 존재하는 템플릿 코드입니다.");
}
return await queryOne<any>(
`INSERT INTO template_standards
(template_code, template_name, category, template_type, layout_config,
description, is_active, company_code, created_by, updated_by,
created_date, updated_date)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW(), NOW())
RETURNING *`,
[
data.template_code,
data.template_name,
data.category,
data.template_type,
JSON.stringify(data.layout_config),
data.description,
data.is_active,
data.company_code,
data.created_by,
data.updated_by,
]
);
예시 3: 복합 키 UPDATE
// 기존 Prisma
return await prisma.template_standards.update({
where: {
template_code_company_code: {
template_code: templateCode,
company_code: companyCode,
},
},
data: {
...data,
updated_date: new Date(),
},
});
// 전환 후
// 동적 UPDATE 쿼리 생성
const updateFields: string[] = ["updated_date = NOW()"];
const values: any[] = [];
let paramIndex = 1;
if (data.template_name !== undefined) {
updateFields.push(`template_name = $${paramIndex++}`);
values.push(data.template_name);
}
if (data.category !== undefined) {
updateFields.push(`category = $${paramIndex++}`);
values.push(data.category);
}
if (data.template_type !== undefined) {
updateFields.push(`template_type = $${paramIndex++}`);
values.push(data.template_type);
}
if (data.layout_config !== undefined) {
updateFields.push(`layout_config = $${paramIndex++}`);
values.push(JSON.stringify(data.layout_config));
}
if (data.description !== undefined) {
updateFields.push(`description = $${paramIndex++}`);
values.push(data.description);
}
if (data.is_active !== undefined) {
updateFields.push(`is_active = $${paramIndex++}`);
values.push(data.is_active);
}
if (data.updated_by !== undefined) {
updateFields.push(`updated_by = $${paramIndex++}`);
values.push(data.updated_by);
}
return await queryOne<any>(
`UPDATE template_standards
SET ${updateFields.join(", ")}
WHERE template_code = $${paramIndex++} AND company_code = $${paramIndex}
RETURNING *`,
[...values, templateCode, companyCode]
);
예시 4: 복합 키 DELETE
// 기존 Prisma
await prisma.template_standards.delete({
where: {
template_code_company_code: {
template_code: templateCode,
company_code: companyCode,
},
},
});
// 전환 후
import { query } from "../database/db";
await query(
`DELETE FROM template_standards
WHERE template_code = $1 AND company_code = $2`,
[templateCode, companyCode]
);
예시 5: DISTINCT 쿼리
// 기존 Prisma
const categories = await prisma.template_standards.findMany({
where: {
company_code: companyCode,
},
select: {
category: true,
},
distinct: ["category"],
});
return categories
.map((c) => c.category)
.filter((c): c is string => c !== null && c !== undefined)
.sort();
// 전환 후
const categories = await query<{ category: string }>(
`SELECT DISTINCT category
FROM template_standards
WHERE company_code = $1 AND category IS NOT NULL
ORDER BY category ASC`,
[companyCode]
);
return categories.map((c) => c.category);
✅ 완료 기준
- 6개 모든 Prisma 호출을 Raw Query로 전환 완료
- 복합 기본 키 처리 (template_code + company_code)
- 동적 UPDATE 쿼리 생성
- DISTINCT 쿼리 전환
- JSON 필드 처리 (layout_config)
- 모든 TypeScript 컴파일 오류 해결
import prisma완전 제거- 모든 단위 테스트 통과 (6개)
- 통합 테스트 작성 완료 (2개 시나리오)
🔧 주요 기술적 과제
1. 복합 기본 키
template_standards 테이블은 (template_code, company_code) 복합 기본 키를 사용합니다.
- WHERE 절에서 두 컬럼 모두 지정 필요
- Prisma의
template_code_company_code표현식을template_code = $1 AND company_code = $2로 변환
2. JSON 필드
layout_config 필드는 JSON 타입으로, INSERT/UPDATE 시 JSON.stringify() 필요합니다.
3. DISTINCT + NULL 제외
카테고리 목록 조회 시 DISTINCT 사용하며, NULL 값은 WHERE category IS NOT NULL로 제외합니다.
📋 체크리스트
코드 전환
- import 문 수정 (
prisma→query, queryOne) - getTemplateByCode() - findUnique → queryOne (복합 키)
- createTemplate() - findUnique + create → queryOne (중복 확인 + INSERT)
- updateTemplate() - update → queryOne (동적 UPDATE, 복합 키)
- deleteTemplate() - delete → query (복합 키)
- getTemplateCategories() - findMany + distinct → query (DISTINCT)
- JSON 필드 처리 (layout_config)
- Prisma import 완전 제거
테스트
- 단위 테스트 작성 (6개)
- 통합 테스트 작성 (2개)
- TypeScript 컴파일 성공
- 성능 벤치마크 테스트
💡 특이사항
복합 기본 키 패턴
이 서비스는 (template_code, company_code) 복합 기본 키를 사용하므로, 모든 조회/수정/삭제 작업에서 두 컬럼을 모두 WHERE 조건에 포함해야 합니다.
JSON 레이아웃 설정
layout_config 필드는 템플릿의 레이아웃 설정을 JSON 형태로 저장합니다. Raw Query 전환 시 JSON.stringify()를 사용하여 문자열로 변환해야 합니다.
카테고리 관리
템플릿은 카테고리별로 분류되며, getTemplateCategories() 메서드로 고유한 카테고리 목록을 조회할 수 있습니다.
작성일: 2025-10-01
예상 소요 시간: 45분
담당자: 백엔드 개발팀
우선순위: 🟢 낮음 (Phase 3.9)
상태: ⏳ 대기 중
특이사항: 복합 기본 키, JSON 필드, DISTINCT 쿼리 포함