Node.js 백엔드 스터디(4) - Node.js와 익스프레스로 웹 애플리케이션 서버 구현하기
Node.js 기본 라이브러리로 웹 서버를 만들었지만…
지난번 까지는 Node.js 기본 라이브러리(http, url 등)를 이용하여 구현했지만, 일반적으로 아래와 같이 웹 서버가 제공하는 기능을 제공하기에는 부족하다. 널리 사용되는 Express로 웹 서버를 구축해보자.
항목 | 설명 |
---|---|
라우팅 | URL 요청을 함수와 매핑시켜주는 기능 |
정적 파일 서비스 | CSS, JS, 이미지 등의 정적인 파일을 다루는 기능 |
템플릿 엔진 | 동적인 웹페이지를 HTML과 인스턴스를 사용해 생성하는 기능 |
요청(request) 데이터 다루기 | HTTP 요청을 추상화해 편리하게 다룰 수 있게 하는 기능 |
응답(response) 데이터 다루기 | HTTP 응답을 커스터마이징할 수 있는 기능. 파일 내려받기, 이미지 출력 등 |
파일 업로드 | HTTP로 전송된 파일을 읽고 다룰 수 있는 기능 |
쿠키 및 세션 지원 | 클라이언트 측 혹은 서버 측의 메모리에 일정 기간 동안 저장해야 하는 데이터를 다루는 기능 |
리다이렉트 | 서버의 응답 시 다른 페이지로 전달(redirect)시키는 기능 |
에러 페이지 | 요청이 잘못되었거나, 서버 에러 시 특정 에러 페이지를 보여주기 |
미들웨어 | 요청 혹은 응답 사이에 공통된 기능을 추가하는 기능 |
1. 예제 연습 1 - “헬로 Express”를 반환하는 서버
const express = require("express"); // 1. express 모듈 불러오기
const app = express(); // 2. express를 초기화 후 app에 할당
const port = 3000;
app.get("/", (req, res) => { // 3. '/'로 요청이 오는 경우 실행
res.set({ "Content-Type": "text/html; charset=utf-8" }); // 4. 헤더값 설정
res.end("헬로 Express");
});
app.listen(port, () => { // 5. 서버를 기동해 클라이언트 요청을 기다림
console.log(`START SERVER : use ${port}`);
})
2. 예제 연습 2 - Express로 라우팅다루기
지난 번에 http로 라우팅을 다루었던 코드(urlMap)를 Express로 아래와 같이 손쉽게 라우팅할 수 있다. 이 때, 라우팅을 담당했떤 urlMap 코드가 제거된 걸 확인할 수 있다.
const express = require("express");
const url = require("url");
const app = express();
const port = 3000;
app.listen(port, () => {
console.log("익스프레스로 라우터 리펙토링하기")
})
// 1. GET 메서드의 라우팅 설정
// 이전 코드에서 urlMap으로 라우팅 하던 코드가 제거
app.get("/", (_, res) => res.end("HOME"));
app.get("/user", user);
app.get("/feed", feed);
function user(req, res) {
const userInfo = url.parse(req.url, true).query;
// 2. 응답결과로 json 으로 내려줌
res.json(`[user] name : ${userInfo.name}, age : ${userInfo.age}`);
}
function feed(_, res) {
res.json(`<ul>
<li>picture1</li>
<li>picture2</li>
<li>picture3</li>
</ul>`);
}
3. 예제 연습 3 - 게시판 API 코드 작성하기
아래 게시판 API 스펙에 따라 Express로 게시판 API를 만들어보자.
경로 | HTTP 메서드 | 설명 |
---|---|---|
/ | get | 게시판 목록을 가져온다. |
/posts | post | 게시판에 글을 씁니다. 글은 아이디(id), 제목(title), 작성자(name), 내용(text), 생성일시(createdDt)로 구성된다. |
/posts/:id | delete | 게시글 아이디가 id인 글을 삭제한다. |
const express = require("express");
const app = express();
const port = 3001;
let posts = [];
// 1. req.body를 사용하려면 JSON 미들웨어를 사용
// 사용하지 않으면 undefined로 반환
app.use(express.json()); // json 미들웨어 활성화
// POST 요청 시 컨텐트 타입이 application/x-www-form-urlencoded인 경우 파싱
app.use(express.urlencoded({ extended: true })); // 2. json 미들웨어와 함께 사용
app.get("/", (_, res) => {
res.json(posts); // "/"로 요청이 오면 실행
// res.end(posts); // 에러 발생
})
app.post("/posts", (req, res) => { // "/posts"로 요청이 오면 실행
const { title, name, text } = req.body; // HTTP 요청의 body 데이터를 변수에 할당
// 게시글 리스트에 글 추가
posts.push({
id: posts.length + 1,
title: title,
name: name,
text: text,
createdDt: Date()
})
res.json({
title, name, text
})
})
app.delete("/posts/:id", (req, res) => {
const id = req.params.id; // 3. app.delete에 설정한 path 정보에서 id값을 가져옴
const filteredPosts = posts.filter((post) => post.id !== +id); // 글 삭제 로직
const isLengthChanged = posts.length !== filteredPosts.length;
posts = filteredPosts;
if (isLengthChanged) { // posts의 데이터 개수가 변경되었으면 삭제 성공
res.json(posts);
return
}
res.json("NOT CHANGED"); // 변경되지 않음 알림
});
app.listen(port, () => {
console.log("게시판 API 오픈");
})
- app.use()는 미들웨어를 사용할 때 사용하는 함수이다. 익스프레스에서 미들웨어는 요청과 응답 사이에 로직을 추가할 수 있는 함수를 제공한다. app.use(express.json())을 실행하지 않으면, req.body값이 undefined로 나오게 된다.
- express.urlencoded({ extended: true })는 컨텐트 타입이 application/x-www-form-urlencoded인 경우 파싱한다. POST 요청 대부분이 application/x-www-form-urlencoded 타입이라서 express.json()과 함께 사용한다. application/x-www-form-urlencoded 타입이란 body에 키=값&키2=값2 같은 조합형태의 데이터를 말한다.
- id 변수에 요청(req)의 path에 할당된 변수 id를 할당한다. 라우팅 규칙에 따라 :id로 표기한 부분은 데이터가 들어오면 문자열 타입으로 params.id에 할당된다.
예제 테스트하기
- 아래 화면은 3번의 포스트 후, get으로 posts를 받아본 결과화면이다.
- id 1번을 delete 요청하면, 남아있는 배열의 요소가 응답된다.
연습문제 오답노트
- 다국어를 표현하는 데 가장 많이 사용되는 가변 길이 문자열 인코딩 방식은?
- UTF-8
- HTTP 메서드로 API 작성 시 URL 자원을 표현하고 가져오는 행위를 표현하는 규칙은?
- REST
참고
다음에 다루게 될 것
- npm으로 패키지 관리하기
댓글남기기