pgms

[4주차] - [9기] 타입스크립트로 함께하는 웹 풀 사이클 개발(React, Node.js) 10회차 - 17

Ejah 2026. 1. 27. 23:22

Express에서

핸들러·라우트·if(긍정문)·JSON 배열 find()·예외처리까지

핸들러

Request로 인해 호출되는 “메소드”

route

route와 endpoint의 차이

Express로 서버를 만들다 보면 자주 마주치는 키워드

  • route(라우트): 어떤 URL로 들어오면 어떤 로직을 실행할지 연결해주는 “길”
  • handler(핸들러): 요청(req)과 응답(res)을 처리하는 “함수”
  • if(긍정문): 조건이 “참일 때만” 실행하도록 만드는 기본 제어문
  • JSON array + find(): 배열 데이터에서 원하는 요소를 찾는 대표 패턴
  • 예외처리: 에러가 나도 서버가 멈추지 않게 안전하게 처리하기

이번 글에서는 이 다섯 가지를 실제로 자주 쓰는 형태로 정리해볼게요.


1) Route(라우트)와 Handler(핸들러)는 이렇게 붙어 있어요

Express에서 가장 흔한 형태는 아래처럼 생겼어요.

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

app.get("/users", (req, res) => {
  res.send("유저 목록");
});
  • app.get("/users", ...) → 라우트(Route)
    /users로 GET 요청이 오면 아래 함수를 실행해요.
  • (req, res) => { ... } → 핸들러(Handler)
    들어온 요청을 읽고(req), 응답을 보내는(res) 역할을 해요.

즉, **라우트가 “길 안내”라면, 핸들러는 “실제 일 처리하는 담당자”**라고 이해하면 편해요.


2) if문은 “긍정문”으로 쓰면 더 읽기 쉬워요

처음엔 보통 이렇게 쓰죠.

 
if (user === undefined) {
  return res.status(404).json({ message: "user not found" });
}
// 정상 로직...

이 방식도 좋지만, 더 깔끔하게 읽히는 패턴이 있어요.

 early return(빠른 종료)로 “정상 흐름”을 아래로 몰기

 
if (!user) {
  return res.status(404).json({ message: "user not found" });
}

// 여기부터는 user가 '있다'가 확정!
return res.json(user);
  • if (!user) return ... 처럼 예외/실패 케이스를 먼저 처리하고
  • 아래는 정상 케이스만 집중하게 만드는 방식이에요.

이게 실무에서 많이 쓰이는 이유는:

  • 코드가 길어져도 “정상 흐름”을 찾기 쉽고
  • 중첩 if가 줄어들어서 덜 복잡해져요.

3) JSON array(배열)에서 원하는 것 찾기: find()

예를 들어, 서버 메모리에 유저 목록이 있다고 해볼게요.

 
const users = [
  { id: 1, name: "민수" },
  { id: 2, name: "지수" },
  { id: 3, name: "정현" },
];

요청으로 들어온 id와 같은 유저를 찾고 싶다면 find()가 가장 많이 쓰여요.

 
const user = users.find((u) => u.id === 2);
console.log(user); // { id: 2, name: "지수" }

find() 핵심 포인트

  • 조건을 만족하는 첫 번째 요소를 반환
  • 없으면 undefined 반환

즉, find()를 썼다면 보통 바로 이런 if가 따라와요:

 
if (!user) {
  return res.status(404).json({ message: "user not found" });
}

4) 라우트에서 find()까지 한 번에 연결해보기

/users/:id로 유저 1명을 조회하는 예시예요.

 
app.get("/users/:id", (req, res) => {
  const id = Number(req.params.id); // 문자열 -> 숫자

  const user = users.find((u) => u.id === id);

  if (!user) {
    return res.status(404).json({ message: "user not found" });
  }

  return res.json(user);
});

여기서 자주 나오는 실수 2가지:

(1) req.params.id는 문자열이다

그래서 비교할 때 숫자로 바꿔주는 게 안전해요.

  • Number(req.params.id)
  • 또는 parseInt(req.params.id, 10)

(2) find()가 못 찾으면 undefined

그래서 if (!user)로 처리하는 흐름이 자연스러워요.


5) 예외처리(에러 처리)는 “try/catch + next(err)”로 정리하면 편해요

라우트 핸들러에서 에러가 터질 수 있어요.

  • JSON 파싱 문제
  • DB 연결 실패
  • 코드 실수 등등

Express에서는 보통 아래처럼 정리해요.

 
app.get("/safe", (req, res, next) => {
  try {
    // 여기서 에러가 나면 catch로 이동
    throw new Error("문제 발생!");
  } catch (err) {
    next(err); // 에러 미들웨어로 넘기기
  }
});

그리고 맨 아래에 에러 처리 미들웨어를 둬요.

app.use((err, req, res, next) => { app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({ message: "서버 내부 오류", detail: err.message });
});

왜 next(err)를 쓰나요?

  • 핸들러마다 res.status(500)...를 반복하지 않아도 되고
  • 에러 응답을 “한 곳에서” 통일할 수 있어요.

6) (보너스) async/await 라우트에서 예외처리 더 깔끔하게

DB를 붙이면 보통 async가 되는데, 매번 try/catch가 귀찮을 수 있어요.
그럴 땐 asyncHandler 패턴을 많이 사용해요.

 
const asyncHandler = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
const asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);

사용:

 
app.get(
  "/users/:id",
  asyncHandler(async (req, res) => {
    const id = Number(req.params.id);

    const user = users.find((u) => u.id === id);
    if (!user) return res.status(404).json({ message: "user not found" });

    return res.json(user);
  })
);

이러면 async 함수에서 에러가 나도 자동으로 next(err)로 넘어가요.


회원 API 설계 및 결과 업로드

회원 API

설계

회원

  • 로그인 : POST/login
    • req : body (id, pwd) (body가 필요하면 post)
    • res : ${name}님 환영합니다. => 메인페이지
  • 회원가입 : POST/join
    • req : body (userId, pwd, name)
    • res : ${name}님 환영합니다. => 로그인페이지
  • 회원 개별 정보 조회 : GET/user/:id
    • req : URL (id)
    • res : userId, name
  • 회원 개별 탈퇴 : DELETE/users/:id
    • req : URL(id)
    • res : ${name}님 다음에 또 뵙겠습니다. or 메인페이지

채널

  • 채널생성 : POST /channels
    • req : body (channelTitle, userId) cf. userId는 body가 아니라 header에 숨겨서(개인정보를 header에 보이게 둘 수는 없기 때문에(개인정보유출)..Token
    • res 201 : ${channelTitle}님 채널을 응원합니다. =>다른페이지 ex. 채널 관리페이지
  • 채널 개별 수정 : PUT/channels:id
    • req : URL (id), body (channelTitle)
    • res 200 : ${channelTitle}이 성공적으로 수정되었습니다. / 기존 ${ } => 수정 ${ }
  • 채널 개별 삭제 : DELETE /channels/:id
    • req : URL(id)
    • res 200 : 삭제 되었습니다. => 메인페이지
  • 채널 전체 조회 : GET /channels
    • req : body (userId)
    • res 200 : 채널 전체 데이터 list, json array
  • 채널 개별 조회 : GET /channels/:id > 유니크한 키 값
    • req : URL (id)
    • res 200 : 채널 개별 데이터

 

import express from 'express'
const app = express();
app.listen(7777);
app.use(express.json()); //http 외 모듈 'json'

let db = new Map();
var id = 1; //하나의 객체를 유니크하게 구별하기 위함

//로그인
app.post('/login', (req, res) => {

})

//회원가입
app.post('/join', (req, res) => {
    console.log(req.body);

    if(req.body == {}){
        res.status(400).json({
            message: `입력값을 다시 확인해주세요.`
        })
    }else{
        db.set(id++, req.body)
        res.status(201).json({
            message: `${db.get(id - 1).name}님 환영합니다.`
        })
    }
})

app.route('/users/:id')

//회원 개별 조회
.get((req, res) => {
    let id = parseInt(req.params.id);
    const user = db.get(id);

    if( user == undefined){
        res.status(404).json({
            message : "회원 정보가 없습니다."
        })
    }else{
        res.status(200).json({
            userId : user.userId,
            name : user.name
        })
    }
})

//회원 개별 탈퇴
.delete((req, res) => {
    let id = parseInt(req.params.id);
    const user = db.get(id);

    if( user == undefined){
        res.status(404).json({
            message : "회원 정보가 없습니다."
        })
    }else{
        db.delete(id);
        res.status(200).json({
            message : `${user.name}님 다음에 또 뵙겠습니다.`
        })
    }
})

 

 

마무리 정리

  • Route: URL + HTTP 메서드로 요청을 받는 입구
  • Handler: req/res를 처리하는 함수(실제 로직)
  • if(긍정문): if (!조건) return으로 실패 케이스를 먼저 처리하면 깔끔
  • JSON array + find(): 원하는 요소 1개 찾기, 없으면 undefined
  • 예외처리: try/catch + next(err) + 에러 미들웨어로 통일