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) + 에러 미들웨어로 통일
'pgms' 카테고리의 다른 글
| [4주차] - [9기] 타입스크립트로 함께하는 웹 풀 사이클 개발(React, Node.js) 10회차 - 19 (0) | 2026.01.29 |
|---|---|
| [4주차] - [9기] 타입스크립트로 함께하는 웹 풀 사이클 개발(React, Node.js) 10회차 - 18 (0) | 2026.01.28 |
| [4주차] - [9기] 타입스크립트로 함께하는 웹 풀 사이클 개발(React, Node.js) 10회차 - 16 (0) | 2026.01.26 |
| [3주차] - [9기] 타입스크립트로 함께하는 웹 풀 사이클 개발(React, Node.js) 10회차 - 15 (0) | 2026.01.25 |
| [3주차] - [9기] 타입스크립트로 함께하는 웹 풀 사이클 개발(React, Node.js) 10회차 - 14 (0) | 2026.01.22 |