Express 라우팅에서 고정 경로와 동적 경로의 선언 순서가 중요한 이유

강 진성·2024년 10월 23일
0

개인 프로젝트 중 Express로 api 주소 선언 중 다음과 같은 문제가 생겼다.

CastError: Cast to ObjectId failed for value "reset-today" (type string) at path "_id" for model "user"
at SchemaObjectId.cast

GPT에 물어본 결과 이 에러는 Mongoosereset-today 라는 문자열을 MongoDB의 _id 필드로 해석하려고 시도하는 과정에서 발생한 것이다. MongoDB에서 _id 필드는 기본적으로 ObjectId 타입이어야 하지만, 지금 전달된 값이 문자열 "reset-today" 여서 문제가 발생한 것이다.

따라서 해결 방법으로는

  1. 라우팅 경로를 명확하게 고정 경로로 설정
  2. 경로 우선 순위 확인

이 두 가지 방법을 추천하였다. 첫 번째 방법은 경로를 확실히 해줬으므로 문제가 되지않았다. 따라서, 두 번째 방법으로 해결을 하였다. 고정된 경로들을 동적 경로들보다 먼저 선언하도록 라우팅 파일을 수정해줬다.

// 고정된 경로
router.put("/reset-today", userController.resetUserTeam);

// 동적 경로
router.put("/:id", userController.updateUser);

이렇게 하니 잘 작동을 하였다. 하지만, 왜 이렇게 해야 작동이 되는지 궁금해서 좀 더 찾아보았다.



1. 고정 경로와 동적 경로의 차이

고정 경로

고정 경로는 특정 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 같은 경로에서 123id 로 받아 처리하는 등 유연하게 동작한다.



2. 왜 고정 경로를 먼저 선언해야 할까?

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 로 요청이 들어오면 먼저 고정된 경로가 처리되고, 이후에 동적 경로가 처리된다. 이 방식으로 동적 경로가 고정된 경로와 충돌하는 문제를 방지할 수 있다.



3. 결론

Express에서는 고정 경로와 동적 경로를 선언할 때, 고정 경로를 먼저 선언해야 한다. 그렇지 않으면 동적 경로가 고정된 경로와 일치하여 잘못된 값이 변수로 처리되거나, 예상치 못한 에러가 발생할 수 있다.

다음과 같은 규칙을 기억:

  • 고정 경로는 특정 URL에만 매칭되므로 최우선으로 처리해야 한다.
  • 동적 경로는 유연하게 다양한 값들을 처리할 수 있지만, 고정 경로를 덮어쓰지 않도록 주의해야 한다.

Express의 라우팅 순서를 잘 이해하고 사용하면, 이런 문제를 피하고 애플리케이션이 의도한 대로 작동하도록 만들 수 있다.

profile
완전완전완전초보초보초보
post-custom-banner

0개의 댓글