7.0 KiB
7.0 KiB
📋 Phase 3.13: EntityJoinService Raw Query 전환 계획
📋 개요
EntityJoinService는 5개의 Prisma 호출이 있으며, 엔티티 간 조인 관계 관리를 담당하는 서비스입니다.
📊 기본 정보
| 항목 | 내용 |
|---|---|
| 파일 위치 | backend-node/src/services/entityJoinService.ts |
| 파일 크기 | 574 라인 |
| Prisma 호출 | 5개 |
| 현재 진행률 | 0/5 (0%) 🔄 진행 예정 |
| 복잡도 | 중간 (조인 쿼리, 관계 설정) |
| 우선순위 | 🟡 중간 (Phase 3.13) |
| 상태 | ⏳ 대기 중 |
🎯 전환 목표
- ⏳ 5개 모든 Prisma 호출을
db.ts의query(),queryOne()함수로 교체 - ⏳ 엔티티 조인 설정 CRUD 기능 정상 동작
- ⏳ 복잡한 조인 쿼리 전환 (LEFT JOIN, INNER JOIN)
- ⏳ 조인 유효성 검증
- ⏳ TypeScript 컴파일 성공
- ⏳ Prisma import 완전 제거
🔍 예상 Prisma 사용 패턴
주요 기능 (5개 예상)
1. 엔티티 조인 목록 조회
- findMany with filters
- 동적 WHERE 조건
- 페이징, 정렬
2. 엔티티 조인 단건 조회
- findUnique or findFirst
- join_id 기준
3. 엔티티 조인 생성
- create
- 조인 유효성 검증
4. 엔티티 조인 수정
- update
- 동적 UPDATE 쿼리
5. 엔티티 조인 삭제
- delete
💡 전환 전략
1단계: 기본 CRUD 전환 (5개)
- getEntityJoins() - 목록 조회
- getEntityJoin() - 단건 조회
- createEntityJoin() - 생성
- updateEntityJoin() - 수정
- deleteEntityJoin() - 삭제
💻 전환 예시
예시 1: 조인 설정 조회 (LEFT JOIN으로 테이블 정보 포함)
변경 전:
const joins = await prisma.entity_joins.findMany({
where: {
company_code: companyCode,
is_active: true,
},
include: {
source_table: true,
target_table: true,
},
orderBy: { created_at: "desc" },
});
변경 후:
const joins = await query<any>(
`SELECT
ej.*,
st.table_name as source_table_name,
st.table_label as source_table_label,
tt.table_name as target_table_name,
tt.table_label as target_table_label
FROM entity_joins ej
LEFT JOIN tables st ON ej.source_table_id = st.table_id
LEFT JOIN tables tt ON ej.target_table_id = tt.table_id
WHERE ej.company_code = $1 AND ej.is_active = $2
ORDER BY ej.created_at DESC`,
[companyCode, true]
);
예시 2: 조인 생성 (유효성 검증 포함)
변경 전:
// 조인 유효성 검증
const sourceTable = await prisma.tables.findUnique({
where: { table_id: sourceTableId },
});
const targetTable = await prisma.tables.findUnique({
where: { table_id: targetTableId },
});
if (!sourceTable || !targetTable) {
throw new Error("Invalid table references");
}
// 조인 생성
const join = await prisma.entity_joins.create({
data: {
source_table_id: sourceTableId,
target_table_id: targetTableId,
join_type: joinType,
join_condition: joinCondition,
company_code: companyCode,
},
});
변경 후:
// 조인 유효성 검증 (Promise.all로 병렬 실행)
const [sourceTable, targetTable] = await Promise.all([
queryOne<any>(
`SELECT * FROM tables WHERE table_id = $1`,
[sourceTableId]
),
queryOne<any>(
`SELECT * FROM tables WHERE table_id = $1`,
[targetTableId]
),
]);
if (!sourceTable || !targetTable) {
throw new Error("Invalid table references");
}
// 조인 생성
const join = await queryOne<any>(
`INSERT INTO entity_joins
(source_table_id, target_table_id, join_type, join_condition,
company_code, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, NOW(), NOW())
RETURNING *`,
[sourceTableId, targetTableId, joinType, joinCondition, companyCode]
);
예시 3: 조인 수정
변경 전:
const join = await prisma.entity_joins.update({
where: { join_id: joinId },
data: {
join_type: joinType,
join_condition: joinCondition,
is_active: isActive,
},
});
변경 후:
const updateFields: string[] = ["updated_at = NOW()"];
const values: any[] = [];
let paramIndex = 1;
if (joinType !== undefined) {
updateFields.push(`join_type = $${paramIndex++}`);
values.push(joinType);
}
if (joinCondition !== undefined) {
updateFields.push(`join_condition = $${paramIndex++}`);
values.push(joinCondition);
}
if (isActive !== undefined) {
updateFields.push(`is_active = $${paramIndex++}`);
values.push(isActive);
}
const join = await queryOne<any>(
`UPDATE entity_joins
SET ${updateFields.join(", ")}
WHERE join_id = $${paramIndex}
RETURNING *`,
[...values, joinId]
);
🔧 기술적 고려사항
1. 조인 타입 검증
const VALID_JOIN_TYPES = ["INNER", "LEFT", "RIGHT", "FULL"];
if (!VALID_JOIN_TYPES.includes(joinType)) {
throw new Error("Invalid join type");
}
2. 조인 조건 검증
// 조인 조건은 SQL 조건식 형태 (예: "source.id = target.parent_id")
// SQL 인젝션 방지를 위한 검증 필요
const isValidJoinCondition = /^[\w\s.=<>]+$/.test(joinCondition);
if (!isValidJoinCondition) {
throw new Error("Invalid join condition");
}
3. 순환 참조 방지
// 조인이 순환 참조를 만들지 않는지 검증
async function checkCircularReference(
sourceTableId: number,
targetTableId: number
): Promise<boolean> {
// 재귀적으로 조인 관계 확인
// ...
}
4. LEFT JOIN으로 관련 테이블 정보 조회
조인 설정 조회 시 source/target 테이블 정보를 함께 가져오기 위해 LEFT JOIN 사용
📝 전환 체크리스트
1단계: Prisma 호출 전환
getEntityJoins()- 목록 조회 (findMany with include)getEntityJoin()- 단건 조회 (findUnique)createEntityJoin()- 생성 (create with validation)updateEntityJoin()- 수정 (update)deleteEntityJoin()- 삭제 (delete)
2단계: 코드 정리
- import 문 수정 (
prisma→query, queryOne) - 조인 유효성 검증 로직 유지
- Prisma import 완전 제거
3단계: 테스트
- 단위 테스트 작성 (5개)
- 조인 유효성 검증 테스트
- 순환 참조 방지 테스트
- 통합 테스트 작성 (2개)
4단계: 문서화
- 전환 완료 문서 업데이트
🎯 예상 난이도 및 소요 시간
-
난이도: ⭐⭐⭐ (중간)
- LEFT JOIN 쿼리
- 조인 유효성 검증
- 순환 참조 방지
-
예상 소요 시간: 1시간
상태: ⏳ 대기 중
특이사항: LEFT JOIN, 조인 유효성 검증, 순환 참조 방지 포함