이번에는 연관된 배열 내에 데이터를 삭제가 되지 않았던 문제에 대해서 글을 작성한다.
아래 코드는 내가 구성한 Hotel 모델과 Room 모델이다.
import { Schema, model, Types } from "mongoose";
interface IHotel {
name: string;
type: string;
city: string;
address: string;
distance: string;
photos?: string[];
title: string;
desc: string;
rating?: number;
rooms?: string[];
cheapestPirce: number;
featured: boolean;
}
const hotelSchema = new Schema<IHotel>({
name: { type: String, required: true },
type: { type: String, required: true },
city: { type: String, required: true },
address: { type: String, required: true },
distance: { type: String, required: true },
photos: { type: [String] },
title: { type: String, required: true },
desc: { type: String, required: true },
rating: { type: Number, min: 0, max: 5 },
rooms: { type: [String] },
cheapestPirce: { type: Number, required: true },
featured: { type: Boolean, default: false },
});
const Hotel = model<IHotel>("Hotel", hotelSchema);
export default Hotel;
import { Schema, model } from "mongoose";
interface DocumentResult<T> {
_doc: T;
}
interface IRoom extends DocumentResult<IRoom> {
title: string;
price: string;
desc: string;
maxPeople: number;
roomNumbers: {
number: number;
unavailableDates: Date[];
}[];
}
const roomSchema = new Schema<IRoom>(
{
title: { type: String, required: true },
price: { type: String, required: true },
desc: { type: String, required: true },
maxPeople: { type: Number, required: true },
roomNumbers: [{ number: Number, unavailableDates: { type: [Date] } }],
},
{
timestamps: true,
}
);
const Room = model<IRoom>("Room", roomSchema);
export default Room;
나는 room을 하나 추가할 때마다 이것을 Hotel model에 room 속성에 roomId를 추가하고 나중에 room을 삭제해도 기존에 room 삭제는 물론 Hotel model에 room 속성에 추가되었었던 roomId도 삭제하려고 했다.
CreateRoom 코드
export const createRoom = async (
req: express.Request,
res: express.Response,
next: NextFunction
) => {
const hotelId = req.params.hotelId;
const newRoom = new Room(req.body);
try {
const savedRoom = await newRoom.save();
try {
await Hotel.findByIdAndUpdate(hotelId, {
$push: { rooms: savedRoom._id },
});
} catch (err) {
next(err);
}
res.status(200).json(savedRoom);
} catch (err) {
next(err);
}
};
room을 생성하면서 추가로 Hotel.findByIdAndUpdate를 사용하여 저장하는 것을 볼 수 있다.
실제로 생성해 보면 아래와 같은 결과가 나온다
room 컬렉션에 잘 저장되었다.

위에서 생성한 room의 id도 hotels 컬렉션에 잘 저장되었다.

문제는 데이터 삭제 부분이였다. room을 하나 삭제하면서 당연히 기존에 hotels 컬렉션에 rooms 배열에 추가되었던 room의 id도 같이 삭제가 되어야 하는데 되지 않는 것이었다. 실제 코드를 동작 시키면 에러가 발생하지 않고 Room has been deleted라는 응답을 받았으나 직접 확인해 본 결과 위에 사진처럼 그대로 roomId 값이 그대로 남아있었다.
export const deleteRoom = async (
req: express.Request,
res: express.Response,
next: NextFunction
) => {
try {
const hotelId = req.params.hotelId;
const deletedRoom = await Room.findByIdAndDelete(req.params.id);
try {
await Hotel.findByIdAndUpdate(hotelId, {
$pull: { rooms: req.params.id },
});
} catch (err) {
next(err);
}
res.status(200).json("Room has been deleted");
} catch (err) {
next(err);
}
};
기존에 모델들에 대해서 정의한 interface를 보면 _id 필드에 대해서 정의를 하지 않았는데 정의하지 않았는데 이것이 문제가 될까 하고 공식 문서를 다시 살펴봤다. 문서에는 TObjectId 유형의 속성을 정의하려면 TypeScript 문서 인터페이스에서 Types.ObjectId를 사용해야 한다고 나와있고 배열 타입도 정의할 때 2가지 방법을 제시하고 있다.
간단하게 정리하면 Types.Array는 문자열, 숫자, 불리언 값과 같은 기본 값의 단순 배열을 나타내고 Types.DocumentArra는 Mongoose 문서 (하위 문서)에 대한 참조를 보유하는 배열을 나타낸다고 말할 수 있다.

interface를 다시 작성한 결과 삭제 코드의 수정 없이 데이터가 삭제되는 걸로 보아 인터페이스가 문제였던 것 같다. 타입에 관한 mongoose 문서를 자세히는 아직 살펴보지는 않았지만 역시 공식 문서를 보는 게 중요하고 나중에 다시 한 번 더 살펴봐야 할 것 같다.