백엔드

Express app.param vs app.get

lgvv 2026. 7. 7. 00:56

Express app.param vs app.get

Express에서 app.get과 app.param에 대해서 간단하게 정리


둘 다 라우팅 과정에서 나오고, 둘 다 userId 같은 값을 다룰 수 있어서 처음에는 비슷해 보임.

간단히 보면 아래처럼 구분.

 

app.get(path, handler)
-> GET 요청을 실제로 처리하는 라우트 핸들러

app.param(name, handler)
-> 특정 path parameter가 있는 라우트 앞에서 먼저 실행되는 전처리 함수

 

여기서 말하는 app.get은 app.get("port")처럼 앱 설정값을 읽는 메서드가 아님.

HTTP GET 라우트를 등록하는 app.get(path, handler) 기준으로 정리.


기준

구분 역할
app.get 요청을 받아 응답을 보냄
app.param 라우트 핸들러 전에 path parameter를 미리 처리
req.params /users/:userId처럼 경로에 들어간 값을 읽음
req.query /users?role=admin처럼 ? 뒤에 붙은 값을 읽음

 

app.get은 실제 요청을 처리하는 곳.

app.param은 그 전에 공통 검증이나 조회를 끼워 넣는 방법.

 

app.get

app.get은 특정 경로로 GET 요청이 들어왔을 때 실행.

app.get("/users/:userId", (req, res) => {
  res.send(`userId: ${req.params.userId}`);
});

 

위 코드는 아래 요청을 처리.

GET /users/1

 

/users/:userId 라우트에 GET 요청이 들어오면 콜백 함수가 실행됨.

여기서 :userId 자리에 들어온 값은 req.params.userId로 꺼냄.

 

app.get("/users/:userId", (req, res) => {
  console.log(req.params.userId); // "1"
  res.send("OK");
});

 

라우트 핸들러 안에서는 query string도 읽을 수 있음.

GET /users/1?tab=posts
app.get("/users/:userId", (req, res) => {
  console.log(req.params.userId); // "1"
  console.log(req.query.tab); // "posts"

  res.send("OK");
});

 

따라서 app.get은 요청을 받고, 필요한 값을 읽고, 응답을 보내는 곳이라고 보면 됨.

app.param

app.param은 라우트 경로에 특정 path parameter가 있을 때 먼저 실행.

app.param("userId", (req, res, next, id) => {
  console.log("userId:", id);
  next();
});

 

Express는 param callback에 (req, res, next, value, name) 순서로 값을 넘김.

보통은 네 번째 값만 id처럼 이름 붙여 받아도 충분.

여기서 "userId"는 라우트의 :userId와 이름이 같아야 함.

 

app.get("/users/:userId", (req, res) => {
  res.send("User page");
});

 

요청이 이렇게 들어오면:

GET /users/1

 

실행 순서는 아래처럼 됨.

app.param("userId", ...)
-> app.get("/users/:userId", ...)

 

즉 app.get으로 바로 가는 게 아니라 app.param("userId")를 먼저 거침.

Express가 라우트를 매칭하는 과정에서 :userId를 발견하고, 그 이름에 등록된 param callback을 먼저 실행하는 흐름.

 

왜 app.param을 쓰는지

사용자 정보를 가져오는 라우트가 여러 개 있다고 가정.

app.get("/users/:userId", (req, res) => {
  const user = getUserById(req.params.userId);

  if (!user) {
    return res.status(404).send("User not found");
  }

  res.json(user);
});

app.get("/blogs/:userId", (req, res) => {
  const user = getUserById(req.params.userId);

  if (!user) {
    return res.status(404).send("User not found");
  }

  res.json({
    message: `${user.name}님의 블로그입니다.`,
    user,
  });
});

 

위 코드에서는 사용자 조회와 404 처리가 반복됨.

const user = getUserById(req.params.userId);

if (!user) {
  return res.status(404).send("User not found");
}

 

이런 코드를 app.param으로 분리 가능.

app.param("userId", (req, res, next, id) => {
  const user = getUserById(id);

  if (!user) {
    return res.status(404).send("User not found");
  }

  req.user = user;
  next();
});

 

이후 라우트 핸들러에서는 이미 조회된 req.user만 사용.

app.get("/users/:userId", (req, res) => {
  res.json(req.user);
});

app.get("/blogs/:userId", (req, res) => {
  res.json({
    message: `${req.user.name}님의 블로그입니다.`,
    user: req.user,
  });
});

 

이렇게 하면 사용자 조회와 검증을 한 곳에서 관리.

다만 모든 로직을 app.param으로 빼는 게 항상 좋은 건 아님.

 

한 라우트에서만 쓰는 코드라면 그냥 app.get 안에 두는 편이 더 읽기 쉬움.

여러 라우트에서 같은 path parameter를 기준으로 같은 검증이나 조회를 반복할 때 app.param을 사용하면 좋음.

 

전체 예제

const express = require("express");
const app = express();

app.param("userId", (req, res, next, id) => {
  const user = getUserById(id);

  if (!user) {
    return res.status(404).send("User not found");
  }

  req.user = user;
  next();
});

app.get("/users/:userId", (req, res) => {
  res.json(req.user);
});

app.get("/blogs/:userId", (req, res) => {
  res.json({
    message: `${req.user.name}님의 블로그입니다.`,
    user: req.user,
  });
});

function getUserById(id) {
  const users = {
    "1": { id: 1, name: "Alice", role: "Admin" },
    "2": { id: 2, name: "Bob", role: "User" },
  };

  return users[id];
}

app.listen(3000, () => {
  console.log("Server running on port 3000");
});

 

요청예제

GET http://localhost:3000/users/1

 

응답:

{
  "id": 1,
  "name": "Alice",
  "role": "Admin"
}
GET http://localhost:3000/blogs/1

 

응답:

{
  "message": "Alice님의 블로그입니다.",
  "user": {
    "id": 1,
    "name": "Alice",
    "role": "Admin"
  }
}

 

없는 사용자를 요청하면:

GET http://localhost:3000/users/999

 

응답:

User not found

 

실행 순서

GET /users/1 요청이 들어오면 흐름은 대략 아래와 같음.

1. Express가 /users/:userId 라우트 확인
2. :userId가 있으므로 app.param("userId") 실행
3. getUserById("1") 실행
4. req.user에 사용자 정보 저장
5. next() 호출
6. app.get("/users/:userId") 실행
7. res.json(req.user) 응답

 

GET /blogs/1도 동일.

라우트는 다르지만 둘 다 :userId를 가지고 있으므로 같은 app.param("userId")를 먼저 거침.

그래서 app.param은 라우트마다 반복되는 parameter 기반 전처리에 잘 맞음.

 

예를 들면 아래와 같은 작업이 잘 어울림.

id 형식 검증
사용자 조회
권한 확인
없는 리소스에 대한 404 처리


path params와 query params 차이

app.param을 이해하려면 path params와 query params를 먼저 구분해야 함.

  • path params
GET /users/1
app.get("/users/:userId", (req, res) => {
  console.log(req.params.userId); // "1"
});

 

/users/:userId에서 :userId 자리에 들어간 값이 req.params.userId에 들어감.

보통 특정 리소스를 식별할 때 사용.

/users/1
-> 1번 사용자

 

 

  • query params
GET /users?userId=1
app.get("/users", (req, res) => {
  console.log(req.query.userId); // "1"
});

 

? 뒤에 붙은 값들은 req.query에 들어감.

검색, 필터, 정렬, 페이지네이션 같은 조건에 자주 사용.

 

/users?role=admin
-> 사용자 목록 중 role이 admin인 것만 조회


그러면 query도 app.get에서 가져올 수 있을까? (O)

app.get 안에서는 req.params도 읽을 수 있고 req.query도 읽을 수 있음.

app.get("/users/:userId", (req, res) => {
  console.log(req.params.userId);
  console.log(req.query.tab);
});

 

다만 app.param은 query parameter에는 반응하지 않음.

app.param("userId", (req, res, next, id) => {
  console.log(id);
  next();
});

app.get("/users", (req, res) => {
  res.send(req.query.userId);
});

 

이 상태에서 아래 요청을 보내도:

GET /users?userId=1

 

app.param("userId")는 실행되지 않음.

라우트 경로에 :userId가 없기 때문.

app.param("userId")가 실행되려면 라우트에 아래처럼 :userId가 있어야 함.

 

app.get("/users/:userId", (req, res) => {
  res.send(req.params.userId);
});


아래 두 요청은 비슷해 보여도 Express 입장에서는 완전히 다르게 처리됨.

/users/1
-> req.params.userId
-> app.param("userId") 실행 가능

/users?userId=1
-> req.query.userId
-> app.param("userId") 실행 안 됨

 

 

userId라는 이름이 같아도 하나는 path parameter이고, 하나는 query parameter.

app.param은 이름만 보는 게 아니라 라우트 경로에 선언된 :paramName을 봄.

 

비교

구분 app.get app.param

역할 GET 요청 처리 path parameter 전처리
실행 시점 라우트가 매칭될 때 라우트 핸들러 실행 전
응답 보통 여기서 응답을 보냄 보통 검증 후 next() 호출
다루는 값 req.params, req.query 등 특정 :paramName
예시 /users/:userId 처리 :userId 값으로 사용자 미리 조회


헷갈리기 쉬운 부분

  • app.param은 라우트를 만드는 게 아님
app.param("userId", callback);

 

이 코드만 있다고 해서 /users/1 주소가 생기는 건 아님.

실제 라우트는 따로 있어야 함.

app.get("/users/:userId", callback);

 

  • 이름이 같아야 함
app.param("userId", callback);
app.get("/users/:id", callback);

 

위 코드는 "userId"와 :id 이름이 달라서 app.param("userId")가 실행되지 않음.

아래처럼 이름을 맞춰야 함.

 

app.param("userId", callback);
app.get("/users/:userId", callback);

 

또 app.param(":userId", callback)처럼 콜론을 붙이지 않음.

라우트에는 :userId라고 쓰지만 app.param에 넘기는 이름은 "userId".

  • next를 호출해야 넘어감
app.param("userId", (req, res, next, id) => {
  req.userId = id;
  next();
});

 

next()를 호출해야 뒤의 app.get으로 넘어감.

사용자를 찾지 못해서 여기서 응답을 끝낼 때는 return res.status(...).send(...)처럼 처리.

 

if (!user) {
  return res.status(404).send("User not found");
}

 

  • 같은 요청에서는 한 번만 실행됨

Express 공식 문서 기준으로 param callback은 같은 request-response cycle에서 한 번만 호출.

예를 들어 같은 요청에서 같은 :id가 여러 라우트 핸들러를 거치더라도 app.param("id", ...)는 매번 반복 실행되지 않음.

중복 조회를 줄여준다는 장점이 있음.

반대로 app.param 안에 라우트별로 달라져야 하는 로직을 넣으면 흐름이 꼬일 수 있음.

 

  • router.param과 범위가 다름

라우터를 분리해서 쓰기 시작하면 app.param과 router.param의 범위도 신경 써야 함.

const router = require("express").Router();

router.param("userId", (req, res, next, id) => {
  // 이 router 안의 :userId 라우트에 적용
  next();
});

router.get("/users/:userId", (req, res) => {
  res.send("OK");
});

 

처음 Express를 배울 때는 app.param만 알아도 충분하지만, 나중에 라우터를 파일별로 나누면 param callback도 그 라우터에 맞게 가까운 곳에 두는 편이 읽기 쉬움.

 

언제 app.param을 쓰면 좋을까

아직까지는 아래와 같은 기준을 가지고 사용

여러 라우트에서 같은 :userId를 사용
각 라우트마다 같은 사용자 조회 코드가 반복
없는 사용자를 같은 방식으로 404 처리
조회된 user 객체를 뒤 라우트에서 공통으로 사용


반대로 아래라면 굳이 쓰지 않아도 됨.

한 라우트에서만 쓰는 로직
parameter 검증보다 라우트 본문 로직이 중심
query string 조건을 처리하려는 목적

 

이 경우에는 app.get 안에서 직접 처리하는 편이 더 단순함.

 

정리

app.get
-> GET 요청의 본문 처리
-> 보통 여기서 응답을 보냄

app.param
-> path parameter 기반 전처리
-> 보통 검증하거나 미리 조회한 뒤 next()로 넘김

 

app.get 안에서는 req.params와 req.query를 모두 읽을 수 있음.

하지만 app.param은 req.query가 아니라 라우트 경로에 선언된 :paramName에만 반응.

 

그래서 /users/:userId처럼 특정 사용자를 가져오는 라우트에서는 app.param으로 사용자 조회나 검증을 먼저 처리.

반대로 /users?role=admin&page=2처럼 검색 조건이나 페이지 조건을 다룰 때는 app.get 안에서 req.query로 처리하는 편이 자연스러움.

 

 

(참고)
https://expressjs.com/en/5x/api/application/
https://expressjs.com/en/5x/guide/routing/