프로젝트명: Dear Carmate – 중고차 계약 관리 서비스
팀명: 1팀
작성자: 권나현
작성일: 2025.08.12
| 이슈 | 원인 | 해결 방법 |
|---|---|---|
| 차량 등록 시 사고횟수 0 입력 시 필수값 누락 처리됨 | 0이 falsy 값으로 인식됨 | data.accidentCount === undefined 조건으로 수정 |
프론트에서 /cars/models 호출 시 데이터 미노출 | DB 기반 조회만 구현됨 | 백엔드에서 Manufacturer·Model 매핑 객체 기반 응답 생성 |
| 차량 삭제 시 계약 데이터 남음 | 차량 삭제 로직과 계약 삭제 로직이 분리됨 | Prisma 트랜잭션으로 차량+계약 Soft Delete 동시 처리 |
| 차량 검색 시 잘못된 결과 반환 | ID 조회만 지원 | 차량번호·차종·차량 ID 모두 지원하는 조건문 추가 |
계약 상태별 조회 누락 (contractDraft) | 상태 배열에 해당 값 없음 | 상태 배열에 contractDraft 추가 |
차량 이미지 수정 후 반영 안됨 (imageUrl 업데이트 누락) | 서비스 레이어에서 imageUrl을 업데이트 패치에 포함하지 않음 | 허용 필드에 imageUrl 추가, '' → null 처리 후 서비스로 전달 |
export const deleteCarCascade = async (carId: bigint, companyId: bigint) => {
return prisma.$transaction(async (tx) => {
// 1) 차량 소유/존재 확인
const car = await tx.car.findFirst({
where: { id: carId, companyId, isDeleted: false },
select: { id: true },
});
if (!car) throw new NotFoundError('존재하지 않거나 이미 삭제된 차량입니다.');
// 계약 문서 삭제
await tx.contractDocument.deleteMany({
where: { contract: { carId } },
});
// 미팅 알람 삭제
await tx.alarm.deleteMany({
where: { meeting: { contract: { carId } } },
});
// 미팅 삭제
await tx.meeting.deleteMany({
where: { contract: { carId } },
});
// 계약 소프트 삭제
await tx.contract.updateMany({
where: { carId },
data: { isDeleted: true, deletedAt: new Date() },
});
// 차량 이미지 실제 삭제
await tx.carImage.deleteMany({
where: { carId },
});
// 차량 소프트 삭제
await tx.car.update({
where: { id: carId },
data: { isDeleted: true, deletedAt: new Date() },
});
return { id: Number(carId) };
});
};
if (
!data.carNumber ||
!data.manufacturer ||
!data.model ||
!data.type ||
data.price === undefined || data.price === null ||
data.accidentCount === undefined || data.accidentCount === null
) {
return res.status(400).json({ message: '필수 값이 누락되었습니다.' });
}
export const getCarModels = (_req: Request, res: Response) => {
const manufacturers: Manufacturer[] = Object.keys(manufacturerModels) as Manufacturer[];
const result = manufacturers.map((m) => ({
manufacturer: m,
models: manufacturerModels[m],
}));
res.status(200).json({ data: result });
};
car.controller.ts
// imageUrl: 빈문자면 null, 값 있으면 그대로
if ('imageUrl' in data) {
if (typeof data.imageUrl === 'string') {
const t = data.imageUrl.trim();
data.imageUrl = t === '' ? null : t;
} else if (data.imageUrl == null) {
data.imageUrl = null;
}
}
car.service.ts
// ★ 핵심: imageUrl 반영 (빈 문자열이면 null, 값 있으면 그대로)
if ('imageUrl' in (data || {})) {
patch.imageUrl = toTrimmedOrNull(data.imageUrl);
}
끝나지 않을 것만 같던 중급 프로젝트가 드디어 마무리됐다.
비록 공식 기간은 짧았지만, 체감상 한 달 넘게 온 힘을 쏟아부은 듯하다.
이번 프로젝트를 하면서 가장 크게 느낀 건 팀워크의 중요성과 자기 객관화였다.
내가 가진 역량이 어느 정도인지 명확히 알아야 하고, 부족한 부분이 있다면 어떻게든 채우고 맞춰가는 노력이 필요하다는 걸 다시금 깨달았다. 처음엔 금방 끝낼 수 있을 거라 생각했지만, 진행 속도가 늦춰지고 의견이 엇갈리는 상황을 겪으며, 모든 조건을 완벽히 맞춘다는 게 얼마나 어려운 일인지 실감했다.
그럼에도 불구하고, 끝까지 잘 따라오지 못하는 팀원도 놓치지 않고 함께 이끌어가며 프로젝트를 마무리할 수 있었다는 점이 가장 뿌듯하다. 결과적으로, 여러 어려움 속에서도 팀 전체가 함께 완주했고, 성공적으로 프로젝트를 마쳤다는 사실이 큰 보람으로 남는다.