개인 프로젝트 중 Express로 api 주소 선언 중 다음과 같은 문제가 생겼다.
CastError: Cast to ObjectId failed for value "reset-today" (type string) at path "_id" for model "user"
at SchemaObjectId.cast
GPT에 물어본 결과 이 에러는 Mongoose
가 reset-today
라는 문자열을 MongoDB의 _id
필드로 해석하려고 시도하는 과정에서 발생한 것이다. MongoDB에서 _id
필드는 기본적으로 ObjectId
타입이어야 하지만, 지금 전달된 값이 문자열 "reset-today"
여서 문제가 발생한 것이다.
따라서 해결 방법으로는
이 두 가지 방법을 추천하였다. 첫 번째 방법은 경로를 확실히 해줬으므로 문제가 되지않았다. 따라서, 두 번째 방법으로 해결을 하였다. 고정된 경로들을 동적 경로들보다 먼저 선언하도록 라우팅 파일을 수정해줬다.
// 고정된 경로
router.put("/reset-today", userController.resetUserTeam);
// 동적 경로
router.put("/:id", userController.updateUser);
이렇게 하니 잘 작동을 하였다. 하지만, 왜 이렇게 해야 작동이 되는지 궁금해서 좀 더 찾아보았다.
고정 경로는 특정 URL에 대해서만 매칭된다. 예를 들어, 다음과 같은 경로는 URL이 정확히 /reset-today
일 때만 매칭된다.
router.put("/reset-today", (req, res) => {
res.send("Resetting today's data");
});
이 경로는 URL이 정확하게 /reset-today
일 때만 실행된다. 다른 URL이 들어오면 이 경로는 매칭되지 않는다.
동적 경로는 경로에 들어오는 다양한 값들을 변수로 처리할 수 있다. 예를 들어, /:id
는 다양한 값이 들어올 수 있는 유연한 경로이다.
router.put("/:id", (req, res) => {
res.send(`Updating user with ID: ${req.params.id}`);
});
이 경로는 /user/123
같은 경로에서 123
을 id
로 받아 처리하는 등 유연하게 동작한다.
Express는 라우터를 위에서 아래로 순차적으로 검사합니다. 즉, 요청이 들어왔을 때 가장 먼저 일치하는 경로가 실행됩니다. 고정 경로와 동적 경로가 함께 있을 때 고정 경로가 먼저 선언되지 않으면, 동적 경로가 고정 경로보다 먼저 매칭될 수 있습니다.
// 동적 경로가 먼저 선언된 경우
router.put("/:id", (req, res) => {
res.send(`Updating user with ID: ${req.params.id}`);
});
router.put("/reset-today", (req, res) => {
res.send("Resetting today's data");
});
이 경우, 만약 클라이언트가 /reset-today
로 요청을 보낸다면 Express는 첫 번째로 선언된 /:id
경로와 먼저 매칭된다. 따라서 "reset-today"
라는 문자열이 id
로 해석되어, 예상치 못한 동작이 발생하게 된다.
만약 이 id
값이 MongoDB의 _id
와 같이 특정 형식(예: ObjectId
)으로 사용된다면, 다음과 같은 오류가 발생할 수 있다.
CastError: Cast to ObjectId failed for value "reset-today" at path "_id"
이 오류는 MongoDB가 reset-today
라는 문자열을 ObjectId
로 변환하려고 시도했지만 실패했기 때문에 발생한다.
이 문제를 해결하려면 고정 경로를 먼저 선언하여 동적 경로가 이를 덮어쓰지 않도록 해야 한다.
// 고정 경로를 먼저 선언
router.put("/reset-today", (req, res) => {
res.send("Resetting today's data");
});
// 동적 경로는 그 이후에 선언
router.put("/:id", (req, res) => {
res.send(`Updating user with ID: ${req.params.id}`);
});
이제 /reset-today
로 요청이 들어오면 먼저 고정된 경로가 처리되고, 이후에 동적 경로가 처리된다. 이 방식으로 동적 경로가 고정된 경로와 충돌하는 문제를 방지할 수 있다.
Express에서는 고정 경로와 동적 경로를 선언할 때, 고정 경로를 먼저 선언해야 한다. 그렇지 않으면 동적 경로가 고정된 경로와 일치하여 잘못된 값이 변수로 처리되거나, 예상치 못한 에러가 발생할 수 있다.
다음과 같은 규칙을 기억:
Express의 라우팅 순서를 잘 이해하고 사용하면, 이런 문제를 피하고 애플리케이션이 의도한 대로 작동하도록 만들 수 있다.