분할레이아웃

This commit is contained in:
kjs
2025-10-15 17:25:38 +09:00
parent 3d242c1c8e
commit 7686158a01
22 changed files with 2119 additions and 218 deletions

View File

@@ -313,6 +313,283 @@ class DataService {
return null;
}
}
/**
* 레코드 상세 조회
*/
async getRecordDetail(
tableName: string,
id: string | number
): Promise<ServiceResponse<any>> {
try {
// 테이블명 화이트리스트 검증
if (!ALLOWED_TABLES.includes(tableName)) {
return {
success: false,
message: `접근이 허용되지 않은 테이블입니다: ${tableName}`,
error: "TABLE_NOT_ALLOWED",
};
}
// Primary Key 컬럼 찾기
const pkResult = await query<{ attname: string }>(
`SELECT a.attname
FROM pg_index i
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
WHERE i.indrelid = $1::regclass AND i.indisprimary`,
[tableName]
);
let pkColumn = "id"; // 기본값
if (pkResult.length > 0) {
pkColumn = pkResult[0].attname;
}
const queryText = `SELECT * FROM "${tableName}" WHERE "${pkColumn}" = $1`;
const result = await query<any>(queryText, [id]);
if (result.length === 0) {
return {
success: false,
message: "레코드를 찾을 수 없습니다.",
error: "RECORD_NOT_FOUND",
};
}
return {
success: true,
data: result[0],
};
} catch (error) {
console.error(`레코드 상세 조회 오류 (${tableName}/${id}):`, error);
return {
success: false,
message: "레코드 조회 중 오류가 발생했습니다.",
error: error instanceof Error ? error.message : "Unknown error",
};
}
}
/**
* 조인된 데이터 조회
*/
async getJoinedData(
leftTable: string,
rightTable: string,
leftColumn: string,
rightColumn: string,
leftValue?: string | number
): Promise<ServiceResponse<any[]>> {
try {
// 테이블명 화이트리스트 검증
if (!ALLOWED_TABLES.includes(leftTable)) {
return {
success: false,
message: `접근이 허용되지 않은 테이블입니다: ${leftTable}`,
error: "TABLE_NOT_ALLOWED",
};
}
if (!ALLOWED_TABLES.includes(rightTable)) {
return {
success: false,
message: `접근이 허용되지 않은 테이블입니다: ${rightTable}`,
error: "TABLE_NOT_ALLOWED",
};
}
let queryText = `
SELECT r.*
FROM "${rightTable}" r
INNER JOIN "${leftTable}" l
ON l."${leftColumn}" = r."${rightColumn}"
`;
const values: any[] = [];
if (leftValue !== undefined && leftValue !== null) {
queryText += ` WHERE l."${leftColumn}" = $1`;
values.push(leftValue);
}
const result = await query<any>(queryText, values);
return {
success: true,
data: result,
};
} catch (error) {
console.error(
`조인 데이터 조회 오류 (${leftTable}${rightTable}):`,
error
);
return {
success: false,
message: "조인 데이터 조회 중 오류가 발생했습니다.",
error: error instanceof Error ? error.message : "Unknown error",
};
}
}
/**
* 레코드 생성
*/
async createRecord(
tableName: string,
data: Record<string, any>
): Promise<ServiceResponse<any>> {
try {
// 테이블명 화이트리스트 검증
if (!ALLOWED_TABLES.includes(tableName)) {
return {
success: false,
message: `접근이 허용되지 않은 테이블입니다: ${tableName}`,
error: "TABLE_NOT_ALLOWED",
};
}
const columns = Object.keys(data);
const values = Object.values(data);
const placeholders = values.map((_, index) => `$${index + 1}`).join(", ");
const columnNames = columns.map((col) => `"${col}"`).join(", ");
const queryText = `
INSERT INTO "${tableName}" (${columnNames})
VALUES (${placeholders})
RETURNING *
`;
const result = await query<any>(queryText, values);
return {
success: true,
data: result[0],
};
} catch (error) {
console.error(`레코드 생성 오류 (${tableName}):`, error);
return {
success: false,
message: "레코드 생성 중 오류가 발생했습니다.",
error: error instanceof Error ? error.message : "Unknown error",
};
}
}
/**
* 레코드 수정
*/
async updateRecord(
tableName: string,
id: string | number,
data: Record<string, any>
): Promise<ServiceResponse<any>> {
try {
// 테이블명 화이트리스트 검증
if (!ALLOWED_TABLES.includes(tableName)) {
return {
success: false,
message: `접근이 허용되지 않은 테이블입니다: ${tableName}`,
error: "TABLE_NOT_ALLOWED",
};
}
// Primary Key 컬럼 찾기
const pkResult = await query<{ attname: string }>(
`SELECT a.attname
FROM pg_index i
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
WHERE i.indrelid = $1::regclass AND i.indisprimary`,
[tableName]
);
let pkColumn = "id";
if (pkResult.length > 0) {
pkColumn = pkResult[0].attname;
}
const columns = Object.keys(data);
const values = Object.values(data);
const setClause = columns
.map((col, index) => `"${col}" = $${index + 1}`)
.join(", ");
const queryText = `
UPDATE "${tableName}"
SET ${setClause}
WHERE "${pkColumn}" = $${values.length + 1}
RETURNING *
`;
values.push(id);
const result = await query<any>(queryText, values);
if (result.length === 0) {
return {
success: false,
message: "레코드를 찾을 수 없습니다.",
error: "RECORD_NOT_FOUND",
};
}
return {
success: true,
data: result[0],
};
} catch (error) {
console.error(`레코드 수정 오류 (${tableName}/${id}):`, error);
return {
success: false,
message: "레코드 수정 중 오류가 발생했습니다.",
error: error instanceof Error ? error.message : "Unknown error",
};
}
}
/**
* 레코드 삭제
*/
async deleteRecord(
tableName: string,
id: string | number
): Promise<ServiceResponse<void>> {
try {
// 테이블명 화이트리스트 검증
if (!ALLOWED_TABLES.includes(tableName)) {
return {
success: false,
message: `접근이 허용되지 않은 테이블입니다: ${tableName}`,
error: "TABLE_NOT_ALLOWED",
};
}
// Primary Key 컬럼 찾기
const pkResult = await query<{ attname: string }>(
`SELECT a.attname
FROM pg_index i
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
WHERE i.indrelid = $1::regclass AND i.indisprimary`,
[tableName]
);
let pkColumn = "id";
if (pkResult.length > 0) {
pkColumn = pkResult[0].attname;
}
const queryText = `DELETE FROM "${tableName}" WHERE "${pkColumn}" = $1`;
await query<any>(queryText, [id]);
return {
success: true,
};
} catch (error) {
console.error(`레코드 삭제 오류 (${tableName}/${id}):`, error);
return {
success: false,
message: "레코드 삭제 중 오류가 발생했습니다.",
error: error instanceof Error ? error.message : "Unknown error",
};
}
}
}
export const dataService = new DataService();