서버 코드는 대화문처럼 읽을 수 있습니다
이전 장에서 우리는 브라우저로 http://localhost:3000에 접속했고, Express 서버가 짧은 문장을 돌려주는 것을 확인했습니다. 그때 코드 안에는 이런 모양이 있었습니다.
app.get('/', (req, res) => {
res.send('안녕하세요. 첫 Express 서버입니다.');
});처음 보면 req, res가 약속된 암호처럼 느껴질 수 있습니다. 하지만 뜻은 단순합니다.
req는 request의 줄임말입니다. 손님이 가져온 주문서라고 생각하면 됩니다. 손님이 어떤 주소로 왔는지, 어떤 방식으로 요청했는지 같은 정보가 들어 있습니다.
res는 response의 줄임말입니다. 서버가 손님에게 돌려보낼 답장 봉투입니다. 이 봉투에 글자를 넣을 수도 있고, HTML 조각을 넣을 수도 있고, JSON 데이터를 넣을 수도 있습니다.
Express에서 res는 한 번 들어온 요청에 대해 서버가 응답을 보내는 객체입니다. 공식 문서에서도 res.send(), res.json(), res.status() 같은 메서드를 이 응답 객체에 붙여 사용한다고 설명합니다.1
정리하면 라우트 코드는 이렇게 읽으면 됩니다.
app.get('/', (req, res) => {
res.send('안녕하세요. 첫 Express 서버입니다.');
});app.get('/'): 기본 주소/로 GET 요청이 오면(req, res) => { ... }: 요청서와 답장 봉투를 받아서res.send(...): 답장을 보낸다
서버 코드는 마법 주문이 아니라 "요청을 받고, 응답을 보낸다"는 짧은 대화입니다.
AI 생성 이미지
req 안에는 손님의 요청 정보가 들어 있습니다
이번 실습에서는 브라우저가 서버에 어떤 요청을 보내는지 터미널에 찍어 보겠습니다.
무엇을 만들고 있나요?
브라우저가 서버에 접속할 때, 서버가 받은 요청 방식과 주소를 터미널에 기록하는 작은 확인 코드를 만듭니다.
어떤 파일을 여나요?
이전 장에서 만든 express-practice 폴더의 app.js 파일을 엽니다.
/ 주소를 처리하는 코드 안에 다음 두 줄을 넣어 봅니다.
app.get('/', (req, res) => {
console.log('요청 방식:', req.method);
console.log('요청 주소:', req.url);
res.send('안녕하세요. 첫 Express 서버입니다.');
});여기서 req.method는 요청 방식입니다. 브라우저 주소창에 주소를 입력해서 들어오면 보통 GET 요청이 들어옵니다. GET은 "정보를 보여 주세요"에 가까운 요청 방식입니다.
req.url은 손님이 찾아온 주소를 보여 줍니다. 기본 주소로 접속하면 /가 찍힙니다. 나중에 ?name=kim처럼 물음표가 붙은 주소를 사용하면 그 부분까지 함께 보일 수 있지만, 지금은 "브라우저가 어느 주소로 들어왔는지 확인하는 값" 정도로 이해하면 충분합니다.
어떤 명령을 입력하나요?
터미널에서 서버가 이미 켜져 있다면 Ctrl + C로 끈 뒤 다시 실행합니다.
node app.js성공하면 무엇이 보이나요?
브라우저에서 http://localhost:3000에 접속하면 화면에는 원래처럼 문장이 보입니다. 그리고 터미널에는 다음과 비슷한 내용이 찍힙니다.
요청 방식: GET
요청 주소: /가끔 /favicon.ico 같은 주소가 추가로 보일 수 있습니다. 브라우저가 탭 아이콘을 찾느라 보내는 요청입니다. 오류가 아니라 브라우저의 자연스러운 행동입니다.
res.send는 글자나 HTML 조각을 보내는 답장입니다
이제 서버가 보내는 답장 쪽을 보겠습니다. 가장 먼저 사용할 도구는 res.send()입니다.
res.send()는 편지 봉투에 짧은 글이나 HTML 조각을 넣어 보내는 느낌입니다. 간단한 확인 문장, 안내 문구, 작은 HTML을 보낼 때 좋습니다.
app.js에서 기본 주소 아래에 /about 주소를 하나 추가해 봅니다.
app.get('/about', (req, res) => {
res.send('<h2>예약 안내</h2><p>전화 없이 온라인으로 예약할 수 있습니다.</p>');
});이 코드는 이렇게 읽습니다.
/about주소로 GET 요청이 오면서버는 HTML 조각을 응답으로 보낸다
브라우저는
<h2>,<p>를 해석해서 제목과 문단처럼 보여 준다
서버를 다시 실행한 뒤 브라우저에서 다음 주소로 접속합니다.
http://localhost:3000/about성공하면 화면에 "예약 안내"라는 제목과 안내 문장이 보입니다.
지금은 HTML 파일을 따로 만들지 않고 문자열로 보냈습니다. 나중에는 HTML, CSS, 이미지 파일을 따로 두고 서버에서 보여 주는 방법도 배웁니다. 이번 장에서는 res.send()가 "브라우저에 바로 보낼 내용을 담는 방법"이라는 점만 잡으면 충분합니다.
res.json은 데이터를 보내는 답장입니다
웹페이지가 아니라 데이터를 보내야 할 때도 있습니다. 예를 들어 예약 가능한 시간을 앱이나 프런트엔드 화면에 알려 주고 싶다고 해 봅시다. 이때는 res.json()을 사용합니다.
JSON은 데이터를 주고받기 좋은 글자 형식입니다. 사람도 읽을 수 있고, 프로그램도 해석하기 좋습니다.
app.js에 다음 주소를 추가합니다.
app.get('/api/reservation', (req, res) => {
res.json({
shop: '작은 미용실',
available: true,
times: ['10:00', '14:00', '16:00']
});
});이 코드는 예약 정보를 데이터로 보냅니다.
shop: 가게 이름available: 예약 가능 여부times: 예약 가능한 시간 목록
서버를 다시 실행하고 브라우저에서 접속합니다.
http://localhost:3000/api/reservation성공하면 브라우저에 다음과 비슷한 데이터가 보입니다.
{
"shop": "작은 미용실",
"available": true,
"times": ["10:00", "14:00", "16:00"]
}res.send()와 res.json()의 차이는 목적에 있습니다.
res.send(): 사람에게 바로 보여 줄 글자나 HTML 조각을 보낼 때 자주 사용res.json(): 다른 프로그램이 읽기 좋은 데이터를 보낼 때 자주 사용
예약 앱, 상품 목록, 문의 내역처럼 "화면보다 데이터가 중심"인 응답은 보통 JSON으로 보냅니다.
상태 코드는 답장 봉투에 붙이는 짧은 표식입니다
서버는 내용만 보내지 않습니다. 이 답장이 어떤 상태인지도 함께 알려 줍니다. 이것을 HTTP 상태 코드라고 부릅니다.
상태 코드는 택배 상자에 붙은 스티커와 비슷합니다.
200: 잘 처리했습니다201: 새로 만들었습니다404: 찾는 주소나 자료가 없습니다500: 서버 안에서 문제가 생겼습니다
Express에서는 res.status()로 상태 코드를 붙입니다. 단, res.status()만으로는 답장이 출발하지 않습니다. 상태 스티커만 붙인 것입니다. 실제로 보내려면 뒤에 send()나 json()을 이어야 합니다.
res.status(201).json({ message: '예약이 접수되었습니다.' });이 코드는 이렇게 읽습니다.
상태 코드는
201로 붙이고JSON 데이터로
{ message: '예약이 접수되었습니다.' }를 보낸다
app.js에 연습용 주소를 하나 추가해 봅니다.
app.get('/created', (req, res) => {
res.status(201).json({
message: '예약이 접수되었습니다.'
});
});브라우저에서 http://localhost:3000/created에 접속하면 JSON 메시지가 보입니다. 브라우저 화면에서는 200과 201의 차이가 크게 보이지 않을 수 있습니다. 하지만 서버와 통신하는 프로그램은 이 숫자를 보고 "성공", "생성됨", "찾을 수 없음" 같은 상태를 판단합니다.
예를 들어 예약 접수 서버라면 상황에 따라 이렇게 응답할 수 있습니다.
res.status(200).json({ message: '예약 목록입니다.' });
res.status(201).json({ message: '새 예약을 만들었습니다.' });
res.status(404).json({ message: '예약을 찾을 수 없습니다.' });
res.status(500).json({ message: '서버에서 문제가 생겼습니다.' });상태 코드는 사용자를 위한 문장이라기보다, 브라우저와 프로그램을 위한 짧은 신호입니다.
없는 주소에는 404 답장을 보냅니다
지금 서버에 없는 주소로 접속해 봅시다.
http://localhost:3000/no-pageExpress가 기본 404 응답을 보여 줄 수 있습니다. 하지만 실제 서버에서는 "이 주소는 없습니다"라고 우리가 직접 알려 주는 편이 좋습니다.
무엇을 만들고 있나요?
서버에 등록되지 않은 주소로 요청이 왔을 때 404 메시지를 보내는 마지막 창구를 만듭니다.
어떤 파일을 여나요?
계속 app.js를 사용합니다.
중요한 점은 위치입니다. 404 처리는 모든 정상 주소 아래쪽, app.listen(...) 위에 둡니다. 정상 라우트들이 먼저 요청을 확인할 기회를 가져야 하기 때문입니다. 일반적으로 등록된 라우트가 모두 지나간 뒤 마지막 app.use(...)에서 404 응답을 보내는 방식이 사용됩니다.2
app.use((req, res) => {
res.status(404).send('찾는 주소가 없습니다: ' + req.url);
});이 코드는 이렇게 읽습니다.
위에 있는 주소들과 맞지 않은 요청이 여기까지 내려오면
상태 코드를
404로 붙이고어떤 주소를 찾지 못했는지 문장으로 보낸다
어떤 명령을 입력하나요?
서버가 이미 실행 중이라면 Ctrl + C로 끈 뒤 다시 켭니다.
node app.js성공하면 무엇이 보이나요?
브라우저에서 http://localhost:3000/no-page에 접속했을 때 다음과 비슷한 문장이 보입니다.
찾는 주소가 없습니다: /no-page404는 서버가 고장 났다는 뜻이 아닙니다. "서버는 살아 있지만, 요청한 주소는 준비되어 있지 않다"는 뜻입니다. 그래서 404를 직접 보내는 것은 친절한 서버를 만드는 첫 단계입니다.
한 요청에는 한 번만 답장해야 합니다
마지막으로 매우 중요한 규칙이 있습니다.
한 요청에는 응답을 한 번만 보내야 합니다.
손님 한 명이 주문서를 냈는데, 서버가 답장 봉투를 이미 보냈다고 생각해 봅시다. 봉투가 떠난 뒤에는 같은 봉투에 내용을 다시 넣을 수 없습니다. Express도 같습니다.
다음 코드는 잘못된 예입니다.
app.get('/double', (req, res) => {
res.send('첫 번째 응답입니다.');
res.send('두 번째 응답입니다.');
});첫 번째 res.send()에서 이미 응답이 나갔습니다. 그다음 줄에서 다시 보내려고 하면 터미널에 Cannot set headers after they are sent to the client와 비슷한 오류가 날 수 있습니다. 말뜻은 "이미 응답을 보냈는데, 또 응답 정보를 바꾸려고 했다"에 가깝습니다.
특히 조건문에서 이런 실수가 자주 나옵니다.
app.get('/check-seat', (req, res) => {
const hasSeat = false;
if (!hasSeat) {
res.status(404).json({ message: '빈 시간이 없습니다.' });
}
res.json({ message: '예약 가능합니다.' });
});hasSeat가 false이면 404 응답을 보낸 뒤에도 코드가 아래로 계속 내려갑니다. 그래서 마지막 res.json()을 또 실행하려고 합니다.
이럴 때는 return을 붙여 응답을 보낸 뒤 함수를 끝냅니다.
app.get('/check-seat', (req, res) => {
const hasSeat = false;
if (!hasSeat) {
return res.status(404).json({ message: '빈 시간이 없습니다.' });
}
res.json({ message: '예약 가능합니다.' });
});return은 "여기서 답장을 보냈으니 이 함수는 끝"이라는 표시입니다. 초보 단계에서는 if 안에서 응답을 보낼 때 return을 함께 쓰는 습관이 많은 혼란을 줄여 줍니다.
이번 장에서 잡아야 할 감각
req와 res를 이해하면 Express 코드가 훨씬 덜 낯설어집니다.
req는 손님이 가져온 주문서입니다. 어떤 주소로 왔는지, 어떤 방식으로 요청했는지 같은 정보가 들어 있습니다.
res는 서버가 돌려보내는 답장 봉투입니다. res.send()로 글자나 HTML 조각을 보내고, res.json()으로 데이터를 보냅니다. res.status()는 그 답장에 상태 코드를 붙입니다.
그리고 가장 중요한 규칙은 하나입니다.
한 요청에는 한 번만 응답합니다.
이 규칙만 기억해도 앞으로 라우팅, 폼 처리, JSON API를 배울 때 서버 코드의 흐름을 훨씬 안정적으로 읽을 수 있습니다.
참고
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.