feat: Phase 3.1 MultiLangService Raw Query 전환 완료

25개 Prisma 호출을 모두 Raw Query로 전환
- 언어 관리 (getLanguages, createLanguage, updateLanguage, toggleLanguage, deleteLanguage)
- 다국어 키 관리 (getLangKeys, createLangKey, updateLangKey, deleteLangKey, toggleLangKey)
- 다국어 텍스트 관리 (getLangTexts, saveLangTexts, getUserText, getLangText)
- 배치 번역 조회 (getBatchTranslations)

주요 기술적 해결:
- 동적 WHERE 조건 생성 (ILIKE 검색 지원)
- 동적 UPDATE 쿼리 (변경된 필드만 업데이트)
- 트랜잭션 처리 (transaction 함수 사용)
- JOIN 쿼리 (multi_lang_text + multi_lang_key_master)
- IN 절 동적 파라미터 바인딩 (배치 번역)

TypeScript 컴파일 성공 (linter 에러 0개)
Prisma import 완전 제거

Phase 3 진행률: 25/162 (15.4%)
전체 진행률: 276/444 (62.2%)
This commit is contained in:
kjs 2025-10-01 10:25:38 +09:00
parent 244c47db35
commit 284c67193d
1 changed files with 343 additions and 273 deletions

View File

@ -1,4 +1,4 @@
import { PrismaClient } from "@prisma/client"; import { query, queryOne, transaction } from "../database/db";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { import {
Language, Language,
@ -15,8 +15,6 @@ import {
ApiResponse, ApiResponse,
} from "../types/multilang"; } from "../types/multilang";
const prisma = new PrismaClient();
export class MultiLangService { export class MultiLangService {
constructor() {} constructor() {}
@ -27,25 +25,27 @@ export class MultiLangService {
try { try {
logger.info("언어 목록 조회 시작"); logger.info("언어 목록 조회 시작");
const languages = await prisma.language_master.findMany({ const languages = await query<{
orderBy: [{ sort_order: "asc" }, { lang_code: "asc" }], lang_code: string;
select: { lang_name: string;
lang_code: true, lang_native: string | null;
lang_name: true, is_active: string | null;
lang_native: true, sort_order: number | null;
is_active: true, created_date: Date | null;
sort_order: true, created_by: string | null;
created_date: true, updated_date: Date | null;
created_by: true, updated_by: string | null;
updated_date: true, }>(
updated_by: true, `SELECT lang_code, lang_name, lang_native, is_active, sort_order,
}, created_date, created_by, updated_date, updated_by
}); FROM language_master
ORDER BY sort_order ASC, lang_code ASC`
);
const mappedLanguages: Language[] = languages.map((lang) => ({ const mappedLanguages: Language[] = languages.map((lang) => ({
langCode: lang.lang_code, langCode: lang.lang_code,
langName: lang.lang_name, langName: lang.lang_name,
langNative: lang.lang_native, langNative: lang.lang_native || "",
isActive: lang.is_active || "N", isActive: lang.is_active || "N",
sortOrder: lang.sort_order ?? undefined, sortOrder: lang.sort_order ?? undefined,
createdDate: lang.created_date || undefined, createdDate: lang.created_date || undefined,
@ -72,9 +72,10 @@ export class MultiLangService {
logger.info("언어 생성 시작", { languageData }); logger.info("언어 생성 시작", { languageData });
// 중복 체크 // 중복 체크
const existingLanguage = await prisma.language_master.findUnique({ const existingLanguage = await queryOne<{ lang_code: string }>(
where: { lang_code: languageData.langCode }, `SELECT lang_code FROM language_master WHERE lang_code = $1`,
}); [languageData.langCode]
);
if (existingLanguage) { if (existingLanguage) {
throw new Error( throw new Error(
@ -83,30 +84,44 @@ export class MultiLangService {
} }
// 언어 생성 // 언어 생성
const createdLanguage = await prisma.language_master.create({ const createdLanguage = await queryOne<{
data: { lang_code: string;
lang_code: languageData.langCode, lang_name: string;
lang_name: languageData.langName, lang_native: string | null;
lang_native: languageData.langNative, is_active: string | null;
is_active: languageData.isActive || "Y", sort_order: number | null;
sort_order: languageData.sortOrder || 0, created_date: Date | null;
created_by: languageData.createdBy || "system", created_by: string | null;
updated_by: languageData.updatedBy || "system", updated_date: Date | null;
}, updated_by: string | null;
}); }>(
`INSERT INTO language_master
(lang_code, lang_name, lang_native, is_active, sort_order, created_by, updated_by)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING *`,
[
languageData.langCode,
languageData.langName,
languageData.langNative,
languageData.isActive || "Y",
languageData.sortOrder || 0,
languageData.createdBy || "system",
languageData.updatedBy || "system",
]
);
logger.info("언어 생성 완료", { langCode: createdLanguage.lang_code }); logger.info("언어 생성 완료", { langCode: createdLanguage!.lang_code });
return { return {
langCode: createdLanguage.lang_code, langCode: createdLanguage!.lang_code,
langName: createdLanguage.lang_name, langName: createdLanguage!.lang_name,
langNative: createdLanguage.lang_native, langNative: createdLanguage!.lang_native || "",
isActive: createdLanguage.is_active || "N", isActive: createdLanguage!.is_active || "N",
sortOrder: createdLanguage.sort_order ?? undefined, sortOrder: createdLanguage!.sort_order ?? undefined,
createdDate: createdLanguage.created_date || undefined, createdDate: createdLanguage!.created_date || undefined,
createdBy: createdLanguage.created_by || undefined, createdBy: createdLanguage!.created_by || undefined,
updatedDate: createdLanguage.updated_date || undefined, updatedDate: createdLanguage!.updated_date || undefined,
updatedBy: createdLanguage.updated_by || undefined, updatedBy: createdLanguage!.updated_by || undefined,
}; };
} catch (error) { } catch (error) {
logger.error("언어 생성 중 오류 발생:", error); logger.error("언어 생성 중 오류 발생:", error);
@ -127,42 +142,72 @@ export class MultiLangService {
logger.info("언어 수정 시작", { langCode, languageData }); logger.info("언어 수정 시작", { langCode, languageData });
// 기존 언어 확인 // 기존 언어 확인
const existingLanguage = await prisma.language_master.findUnique({ const existingLanguage = await queryOne<{ lang_code: string }>(
where: { lang_code: langCode }, `SELECT lang_code FROM language_master WHERE lang_code = $1`,
}); [langCode]
);
if (!existingLanguage) { if (!existingLanguage) {
throw new Error(`언어를 찾을 수 없습니다: ${langCode}`); throw new Error(`언어를 찾을 수 없습니다: ${langCode}`);
} }
// 동적 UPDATE 쿼리 생성
const updates: string[] = [];
const values: any[] = [];
let paramIndex = 1;
if (languageData.langName) {
updates.push(`lang_name = $${paramIndex++}`);
values.push(languageData.langName);
}
if (languageData.langNative) {
updates.push(`lang_native = $${paramIndex++}`);
values.push(languageData.langNative);
}
if (languageData.isActive) {
updates.push(`is_active = $${paramIndex++}`);
values.push(languageData.isActive);
}
if (languageData.sortOrder !== undefined) {
updates.push(`sort_order = $${paramIndex++}`);
values.push(languageData.sortOrder);
}
updates.push(`updated_by = $${paramIndex++}`);
values.push(languageData.updatedBy || "system");
values.push(langCode); // WHERE 조건용
// 언어 수정 // 언어 수정
const updatedLanguage = await prisma.language_master.update({ const updatedLanguage = await queryOne<{
where: { lang_code: langCode }, lang_code: string;
data: { lang_name: string;
...(languageData.langName && { lang_name: languageData.langName }), lang_native: string | null;
...(languageData.langNative && { is_active: string | null;
lang_native: languageData.langNative, sort_order: number | null;
}), created_date: Date | null;
...(languageData.isActive && { is_active: languageData.isActive }), created_by: string | null;
...(languageData.sortOrder !== undefined && { updated_date: Date | null;
sort_order: languageData.sortOrder, updated_by: string | null;
}), }>(
updated_by: languageData.updatedBy || "system", `UPDATE language_master SET ${updates.join(", ")}
}, WHERE lang_code = $${paramIndex}
}); RETURNING *`,
values
);
logger.info("언어 수정 완료", { langCode }); logger.info("언어 수정 완료", { langCode });
return { return {
langCode: updatedLanguage.lang_code, langCode: updatedLanguage!.lang_code,
langName: updatedLanguage.lang_name, langName: updatedLanguage!.lang_name,
langNative: updatedLanguage.lang_native, langNative: updatedLanguage!.lang_native || "",
isActive: updatedLanguage.is_active || "N", isActive: updatedLanguage!.is_active || "N",
sortOrder: updatedLanguage.sort_order ?? undefined, sortOrder: updatedLanguage!.sort_order ?? undefined,
createdDate: updatedLanguage.created_date || undefined, createdDate: updatedLanguage!.created_date || undefined,
createdBy: updatedLanguage.created_by || undefined, createdBy: updatedLanguage!.created_by || undefined,
updatedDate: updatedLanguage.updated_date || undefined, updatedDate: updatedLanguage!.updated_date || undefined,
updatedBy: updatedLanguage.updated_by || undefined, updatedBy: updatedLanguage!.updated_by || undefined,
}; };
} catch (error) { } catch (error) {
logger.error("언어 수정 중 오류 발생:", error); logger.error("언어 수정 중 오류 발생:", error);
@ -180,10 +225,10 @@ export class MultiLangService {
logger.info("언어 상태 토글 시작", { langCode }); logger.info("언어 상태 토글 시작", { langCode });
// 현재 언어 조회 // 현재 언어 조회
const currentLanguage = await prisma.language_master.findUnique({ const currentLanguage = await queryOne<{ is_active: string | null }>(
where: { lang_code: langCode }, `SELECT is_active FROM language_master WHERE lang_code = $1`,
select: { is_active: true }, [langCode]
}); );
if (!currentLanguage) { if (!currentLanguage) {
throw new Error(`언어를 찾을 수 없습니다: ${langCode}`); throw new Error(`언어를 찾을 수 없습니다: ${langCode}`);
@ -192,13 +237,12 @@ export class MultiLangService {
const newStatus = currentLanguage.is_active === "Y" ? "N" : "Y"; const newStatus = currentLanguage.is_active === "Y" ? "N" : "Y";
// 상태 업데이트 // 상태 업데이트
await prisma.language_master.update({ await query(
where: { lang_code: langCode }, `UPDATE language_master
data: { SET is_active = $1, updated_by = $2
is_active: newStatus, WHERE lang_code = $3`,
updated_by: "system", [newStatus, "system", langCode]
}, );
});
const result = newStatus === "Y" ? "활성화" : "비활성화"; const result = newStatus === "Y" ? "활성화" : "비활성화";
logger.info("언어 상태 토글 완료", { langCode, result }); logger.info("언어 상태 토글 완료", { langCode, result });
@ -219,47 +263,54 @@ export class MultiLangService {
try { try {
logger.info("다국어 키 목록 조회 시작", { params }); logger.info("다국어 키 목록 조회 시작", { params });
const whereConditions: any = {}; const whereConditions: string[] = [];
const values: any[] = [];
let paramIndex = 1;
// 회사 코드 필터 // 회사 코드 필터
if (params.companyCode) { if (params.companyCode) {
whereConditions.company_code = params.companyCode; whereConditions.push(`company_code = $${paramIndex++}`);
values.push(params.companyCode);
} }
// 메뉴 코드 필터 // 메뉴 코드 필터
if (params.menuCode) { if (params.menuCode) {
whereConditions.menu_name = params.menuCode; whereConditions.push(`menu_name = $${paramIndex++}`);
values.push(params.menuCode);
} }
// 검색 조건 // 검색 조건 (OR)
if (params.searchText) { if (params.searchText) {
whereConditions.OR = [ whereConditions.push(
{ lang_key: { contains: params.searchText, mode: "insensitive" } }, `(lang_key ILIKE $${paramIndex} OR description ILIKE $${paramIndex} OR menu_name ILIKE $${paramIndex})`
{ description: { contains: params.searchText, mode: "insensitive" } }, );
{ menu_name: { contains: params.searchText, mode: "insensitive" } }, values.push(`%${params.searchText}%`);
]; paramIndex++;
} }
const langKeys = await prisma.multi_lang_key_master.findMany({ const whereClause = whereConditions.length > 0
where: whereConditions, ? `WHERE ${whereConditions.join(" AND ")}`
orderBy: [ : "";
{ company_code: "asc" },
{ menu_name: "asc" }, const langKeys = await query<{
{ lang_key: "asc" }, key_id: number;
], company_code: string;
select: { menu_name: string | null;
key_id: true, lang_key: string;
company_code: true, description: string | null;
menu_name: true, is_active: string | null;
lang_key: true, created_date: Date | null;
description: true, created_by: string | null;
is_active: true, updated_date: Date | null;
created_date: true, updated_by: string | null;
created_by: true, }>(
updated_date: true, `SELECT key_id, company_code, menu_name, lang_key, description, is_active,
updated_by: true, created_date, created_by, updated_date, updated_by
}, FROM multi_lang_key_master
}); ${whereClause}
ORDER BY company_code ASC, menu_name ASC, lang_key ASC`,
values
);
const mappedKeys: LangKey[] = langKeys.map((key) => ({ const mappedKeys: LangKey[] = langKeys.map((key) => ({
keyId: key.key_id, keyId: key.key_id,
@ -291,24 +342,24 @@ export class MultiLangService {
try { try {
logger.info("다국어 텍스트 조회 시작", { keyId }); logger.info("다국어 텍스트 조회 시작", { keyId });
const langTexts = await prisma.multi_lang_text.findMany({ const langTexts = await query<{
where: { text_id: number;
key_id: keyId, key_id: number;
is_active: "Y", lang_code: string;
}, lang_text: string;
orderBy: { lang_code: "asc" }, is_active: string | null;
select: { created_date: Date | null;
text_id: true, created_by: string | null;
key_id: true, updated_date: Date | null;
lang_code: true, updated_by: string | null;
lang_text: true, }>(
is_active: true, `SELECT text_id, key_id, lang_code, lang_text, is_active,
created_date: true, created_date, created_by, updated_date, updated_by
created_by: true, FROM multi_lang_text
updated_date: true, WHERE key_id = $1 AND is_active = $2
updated_by: true, ORDER BY lang_code ASC`,
}, [keyId, "Y"]
}); );
const mappedTexts: LangText[] = langTexts.map((text) => ({ const mappedTexts: LangText[] = langTexts.map((text) => ({
textId: text.text_id, textId: text.text_id,
@ -340,12 +391,11 @@ export class MultiLangService {
logger.info("다국어 키 생성 시작", { keyData }); logger.info("다국어 키 생성 시작", { keyData });
// 중복 체크 // 중복 체크
const existingKey = await prisma.multi_lang_key_master.findFirst({ const existingKey = await queryOne<{ key_id: number }>(
where: { `SELECT key_id FROM multi_lang_key_master
company_code: keyData.companyCode, WHERE company_code = $1 AND lang_key = $2`,
lang_key: keyData.langKey, [keyData.companyCode, keyData.langKey]
}, );
});
if (existingKey) { if (existingKey) {
throw new Error( throw new Error(
@ -354,24 +404,28 @@ export class MultiLangService {
} }
// 다국어 키 생성 // 다국어 키 생성
const createdKey = await prisma.multi_lang_key_master.create({ const createdKey = await queryOne<{ key_id: number }>(
data: { `INSERT INTO multi_lang_key_master
company_code: keyData.companyCode, (company_code, menu_name, lang_key, description, is_active, created_by, updated_by)
menu_name: keyData.menuName || null, VALUES ($1, $2, $3, $4, $5, $6, $7)
lang_key: keyData.langKey, RETURNING key_id`,
description: keyData.description || null, [
is_active: keyData.isActive || "Y", keyData.companyCode,
created_by: keyData.createdBy || "system", keyData.menuName || null,
updated_by: keyData.updatedBy || "system", keyData.langKey,
}, keyData.description || null,
}); keyData.isActive || "Y",
keyData.createdBy || "system",
keyData.updatedBy || "system",
]
);
logger.info("다국어 키 생성 완료", { logger.info("다국어 키 생성 완료", {
keyId: createdKey.key_id, keyId: createdKey!.key_id,
langKey: keyData.langKey, langKey: keyData.langKey,
}); });
return createdKey.key_id; return createdKey!.key_id;
} catch (error) { } catch (error) {
logger.error("다국어 키 생성 중 오류 발생:", error); logger.error("다국어 키 생성 중 오류 발생:", error);
throw new Error( throw new Error(
@ -391,9 +445,10 @@ export class MultiLangService {
logger.info("다국어 키 수정 시작", { keyId, keyData }); logger.info("다국어 키 수정 시작", { keyId, keyData });
// 기존 키 확인 // 기존 키 확인
const existingKey = await prisma.multi_lang_key_master.findUnique({ const existingKey = await queryOne<{ key_id: number }>(
where: { key_id: keyId }, `SELECT key_id FROM multi_lang_key_master WHERE key_id = $1`,
}); [keyId]
);
if (!existingKey) { if (!existingKey) {
throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`); throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`);
@ -401,13 +456,11 @@ export class MultiLangService {
// 중복 체크 (자신을 제외하고) // 중복 체크 (자신을 제외하고)
if (keyData.companyCode && keyData.langKey) { if (keyData.companyCode && keyData.langKey) {
const duplicateKey = await prisma.multi_lang_key_master.findFirst({ const duplicateKey = await queryOne<{ key_id: number }>(
where: { `SELECT key_id FROM multi_lang_key_master
company_code: keyData.companyCode, WHERE company_code = $1 AND lang_key = $2 AND key_id != $3`,
lang_key: keyData.langKey, [keyData.companyCode, keyData.langKey, keyId]
key_id: { not: keyId }, );
},
});
if (duplicateKey) { if (duplicateKey) {
throw new Error( throw new Error(
@ -416,21 +469,39 @@ export class MultiLangService {
} }
} }
// 동적 UPDATE 쿼리 생성
const updates: string[] = [];
const values: any[] = [];
let paramIndex = 1;
if (keyData.companyCode) {
updates.push(`company_code = $${paramIndex++}`);
values.push(keyData.companyCode);
}
if (keyData.menuName !== undefined) {
updates.push(`menu_name = $${paramIndex++}`);
values.push(keyData.menuName);
}
if (keyData.langKey) {
updates.push(`lang_key = $${paramIndex++}`);
values.push(keyData.langKey);
}
if (keyData.description !== undefined) {
updates.push(`description = $${paramIndex++}`);
values.push(keyData.description);
}
updates.push(`updated_by = $${paramIndex++}`);
values.push(keyData.updatedBy || "system");
values.push(keyId); // WHERE 조건용
// 다국어 키 수정 // 다국어 키 수정
await prisma.multi_lang_key_master.update({ await query(
where: { key_id: keyId }, `UPDATE multi_lang_key_master SET ${updates.join(", ")}
data: { WHERE key_id = $${paramIndex}`,
...(keyData.companyCode && { company_code: keyData.companyCode }), values
...(keyData.menuName !== undefined && { );
menu_name: keyData.menuName,
}),
...(keyData.langKey && { lang_key: keyData.langKey }),
...(keyData.description !== undefined && {
description: keyData.description,
}),
updated_by: keyData.updatedBy || "system",
},
});
logger.info("다국어 키 수정 완료", { keyId }); logger.info("다국어 키 수정 완료", { keyId });
} catch (error) { } catch (error) {
@ -449,25 +520,28 @@ export class MultiLangService {
logger.info("다국어 키 삭제 시작", { keyId }); logger.info("다국어 키 삭제 시작", { keyId });
// 기존 키 확인 // 기존 키 확인
const existingKey = await prisma.multi_lang_key_master.findUnique({ const existingKey = await queryOne<{ key_id: number }>(
where: { key_id: keyId }, `SELECT key_id FROM multi_lang_key_master WHERE key_id = $1`,
}); [keyId]
);
if (!existingKey) { if (!existingKey) {
throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`); throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`);
} }
// 트랜잭션으로 키와 연관된 텍스트 모두 삭제 // 트랜잭션으로 키와 연관된 텍스트 모두 삭제
await prisma.$transaction(async (tx) => { await transaction(async (client) => {
// 관련된 다국어 텍스트 삭제 // 관련된 다국어 텍스트 삭제
await tx.multi_lang_text.deleteMany({ await client.query(
where: { key_id: keyId }, `DELETE FROM multi_lang_text WHERE key_id = $1`,
}); [keyId]
);
// 다국어 키 삭제 // 다국어 키 삭제
await tx.multi_lang_key_master.delete({ await client.query(
where: { key_id: keyId }, `DELETE FROM multi_lang_key_master WHERE key_id = $1`,
}); [keyId]
);
}); });
logger.info("다국어 키 삭제 완료", { keyId }); logger.info("다국어 키 삭제 완료", { keyId });
@ -487,10 +561,10 @@ export class MultiLangService {
logger.info("다국어 키 상태 토글 시작", { keyId }); logger.info("다국어 키 상태 토글 시작", { keyId });
// 현재 키 조회 // 현재 키 조회
const currentKey = await prisma.multi_lang_key_master.findUnique({ const currentKey = await queryOne<{ is_active: string | null }>(
where: { key_id: keyId }, `SELECT is_active FROM multi_lang_key_master WHERE key_id = $1`,
select: { is_active: true }, [keyId]
}); );
if (!currentKey) { if (!currentKey) {
throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`); throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`);
@ -499,13 +573,12 @@ export class MultiLangService {
const newStatus = currentKey.is_active === "Y" ? "N" : "Y"; const newStatus = currentKey.is_active === "Y" ? "N" : "Y";
// 상태 업데이트 // 상태 업데이트
await prisma.multi_lang_key_master.update({ await query(
where: { key_id: keyId }, `UPDATE multi_lang_key_master
data: { SET is_active = $1, updated_by = $2
is_active: newStatus, WHERE key_id = $3`,
updated_by: "system", [newStatus, "system", keyId]
}, );
});
const result = newStatus === "Y" ? "활성화" : "비활성화"; const result = newStatus === "Y" ? "활성화" : "비활성화";
logger.info("다국어 키 상태 토글 완료", { keyId, result }); logger.info("다국어 키 상태 토글 완료", { keyId, result });
@ -533,33 +606,40 @@ export class MultiLangService {
}); });
// 기존 키 확인 // 기존 키 확인
const existingKey = await prisma.multi_lang_key_master.findUnique({ const existingKey = await queryOne<{ key_id: number }>(
where: { key_id: keyId }, `SELECT key_id FROM multi_lang_key_master WHERE key_id = $1`,
}); [keyId]
);
if (!existingKey) { if (!existingKey) {
throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`); throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`);
} }
// 트랜잭션으로 기존 텍스트 삭제 후 새로 생성 // 트랜잭션으로 기존 텍스트 삭제 후 새로 생성
await prisma.$transaction(async (tx) => { await transaction(async (client) => {
// 기존 텍스트 삭제 // 기존 텍스트 삭제
await tx.multi_lang_text.deleteMany({ await client.query(
where: { key_id: keyId }, `DELETE FROM multi_lang_text WHERE key_id = $1`,
}); [keyId]
);
// 새로운 텍스트 삽입 // 새로운 텍스트 삽입
if (textData.texts.length > 0) { if (textData.texts.length > 0) {
await tx.multi_lang_text.createMany({ for (const text of textData.texts) {
data: textData.texts.map((text) => ({ await client.query(
key_id: keyId, `INSERT INTO multi_lang_text
lang_code: text.langCode, (key_id, lang_code, lang_text, is_active, created_by, updated_by)
lang_text: text.langText, VALUES ($1, $2, $3, $4, $5, $6)`,
is_active: text.isActive || "Y", [
created_by: text.createdBy || "system", keyId,
updated_by: text.updatedBy || "system", text.langCode,
})), text.langText,
}); text.isActive || "Y",
text.createdBy || "system",
text.updatedBy || "system",
]
);
}
} }
}); });
@ -582,21 +662,18 @@ export class MultiLangService {
try { try {
logger.info("사용자별 다국어 텍스트 조회 시작", { params }); logger.info("사용자별 다국어 텍스트 조회 시작", { params });
const result = await prisma.multi_lang_text.findFirst({ const result = await queryOne<{ lang_text: string }>(
where: { `SELECT mlt.lang_text
lang_code: params.userLang, FROM multi_lang_text mlt
is_active: "Y", INNER JOIN multi_lang_key_master mlkm ON mlt.key_id = mlkm.key_id
multi_lang_key_master: { WHERE mlt.lang_code = $1
company_code: params.companyCode, AND mlt.is_active = $2
menu_name: params.menuCode, AND mlkm.company_code = $3
lang_key: params.langKey, AND mlkm.menu_name = $4
is_active: "Y", AND mlkm.lang_key = $5
}, AND mlkm.is_active = $6`,
}, [params.userLang, "Y", params.companyCode, params.menuCode, params.langKey, "Y"]
select: { );
lang_text: true,
},
});
if (!result) { if (!result) {
logger.warn("사용자별 다국어 텍스트를 찾을 수 없음", { params }); logger.warn("사용자별 다국어 텍스트를 찾을 수 없음", { params });
@ -632,20 +709,17 @@ export class MultiLangService {
langCode, langCode,
}); });
const result = await prisma.multi_lang_text.findFirst({ const result = await queryOne<{ lang_text: string }>(
where: { `SELECT mlt.lang_text
lang_code: langCode, FROM multi_lang_text mlt
is_active: "Y", INNER JOIN multi_lang_key_master mlkm ON mlt.key_id = mlkm.key_id
multi_lang_key_master: { WHERE mlt.lang_code = $1
company_code: companyCode, AND mlt.is_active = $2
lang_key: langKey, AND mlkm.company_code = $3
is_active: "Y", AND mlkm.lang_key = $4
}, AND mlkm.is_active = $5`,
}, [langCode, "Y", companyCode, langKey, "Y"]
select: { );
lang_text: true,
},
});
if (!result) { if (!result) {
logger.warn("특정 키의 다국어 텍스트를 찾을 수 없음", { logger.warn("특정 키의 다국어 텍스트를 찾을 수 없음", {
@ -691,31 +765,24 @@ export class MultiLangService {
} }
// 모든 키에 대한 번역 조회 // 모든 키에 대한 번역 조회
const translations = await prisma.multi_lang_text.findMany({ const placeholders = params.langKeys.map((_, i) => `$${i + 4}`).join(", ");
where: {
lang_code: params.userLang, const translations = await query<{
is_active: "Y", lang_text: string;
multi_lang_key_master: { lang_key: string;
lang_key: { in: params.langKeys }, company_code: string;
company_code: { in: [params.companyCode, "*"] }, }>(
is_active: "Y", `SELECT mlt.lang_text, mlkm.lang_key, mlkm.company_code
}, FROM multi_lang_text mlt
}, INNER JOIN multi_lang_key_master mlkm ON mlt.key_id = mlkm.key_id
select: { WHERE mlt.lang_code = $1
lang_text: true, AND mlt.is_active = $2
multi_lang_key_master: { AND mlkm.lang_key IN (${placeholders})
select: { AND mlkm.company_code IN ($3, '*')
lang_key: true, AND mlkm.is_active = $2
company_code: true, ORDER BY mlkm.company_code ASC`,
}, [params.userLang, "Y", params.companyCode, ...params.langKeys]
}, );
},
orderBy: {
multi_lang_key_master: {
company_code: "asc", // 회사별 우선, '*' 는 기본값
},
},
});
const result: Record<string, string> = {}; const result: Record<string, string> = {};
@ -726,7 +793,7 @@ export class MultiLangService {
// 실제 번역으로 덮어쓰기 (회사별 우선) // 실제 번역으로 덮어쓰기 (회사별 우선)
translations.forEach((translation) => { translations.forEach((translation) => {
const langKey = translation.multi_lang_key_master.lang_key; const langKey = translation.lang_key;
if (params.langKeys.includes(langKey)) { if (params.langKeys.includes(langKey)) {
result[langKey] = translation.lang_text; result[langKey] = translation.lang_text;
} }
@ -755,29 +822,32 @@ export class MultiLangService {
logger.info("언어 삭제 시작", { langCode }); logger.info("언어 삭제 시작", { langCode });
// 기존 언어 확인 // 기존 언어 확인
const existingLanguage = await prisma.language_master.findUnique({ const existingLanguage = await queryOne<{ lang_code: string }>(
where: { lang_code: langCode }, `SELECT lang_code FROM language_master WHERE lang_code = $1`,
}); [langCode]
);
if (!existingLanguage) { if (!existingLanguage) {
throw new Error(`언어를 찾을 수 없습니다: ${langCode}`); throw new Error(`언어를 찾을 수 없습니다: ${langCode}`);
} }
// 트랜잭션으로 언어와 관련 텍스트 삭제 // 트랜잭션으로 언어와 관련 텍스트 삭제
await prisma.$transaction(async (tx) => { await transaction(async (client) => {
// 해당 언어의 다국어 텍스트 삭제 // 해당 언어의 다국어 텍스트 삭제
const deleteResult = await tx.multi_lang_text.deleteMany({ const deleteResult = await client.query(
where: { lang_code: langCode }, `DELETE FROM multi_lang_text WHERE lang_code = $1`,
}); [langCode]
);
logger.info(`삭제된 다국어 텍스트 수: ${deleteResult.count}`, { logger.info(`삭제된 다국어 텍스트 수: ${deleteResult.rowCount}`, {
langCode, langCode,
}); });
// 언어 마스터 삭제 // 언어 마스터 삭제
await tx.language_master.delete({ await client.query(
where: { lang_code: langCode }, `DELETE FROM language_master WHERE lang_code = $1`,
}); [langCode]
);
}); });
logger.info("언어 삭제 완료", { langCode }); logger.info("언어 삭제 완료", { langCode });