티스토리 뷰

 

6장 익스프레스 웹 서버 만들기

 

📌 익스프레스

→ 서버를 제작하는 과정에서의 불편함을 해소하고 편의 기능을 추가한 웹 서버 프레임워크

→ http 모듈의 요청과 응답 객체에 추가 기능들을 부여

→ 코드를 분리하기 쉽게 만들어 관리하기 용의

const express = require('express');
const app = express(); // Express 모듈을 실행해 변수에 할당 (익스프레스 내부에 http 모듈이 내장되어 있으므로 서버 역할Ok)

app.set(키, 값) // 데이터 저장 (app.get(키)로 데이터를 가져올 수 있음)!
app.set('port', 포트) // 서버가 실행될 포트 설정

app.get(주소, 라우터) // 주소에 대한 GET 요청이 올 때 라우터 설정
app.post(주소, 라우터)
app.put(주소, 라우터)
app.patch(주소, 라우터)
app.delete(주소, 라우터)
app.options(주소, 라우터)

app.listen(app.get('port'), 콜백 함수) // listen 하는 부분
res.send() // 응답
res.sendFile() // 파일로 응답 (path 모듈로 파일 경로 지정)
res.state(상태코드) // HTTP 상태 코드 지정

 

📌 미들웨어

→ 미들웨어는 요청과 응답의 중간에 위치함 (익스프레스의 핵심)

→ 미들웨어는 요청과 응답을 조작하여 기능을 추가하기도 하고, 나쁜 요청을 걸러내기도 함

→ 라우터와 에러 핸들러 또한 미들웨어의 일종

app.use(미들웨어) // 모든 요청에서 미들웨어 실행
app.use('/user', 미들웨어) // user로 시작하는 요청에서 미들웨어 실행
app.post('/user', 미들웨어) // user로 시작하는 POST 요청에서 미들웨어 실행

→ 미들웨어는 매개 변수가 req, res, next인 함수 (next는 다음 미들웨어로 넘어가는 함수)

→ 미들웨어는 위에서부터 아래로 순서대로 실행

→ app.use나 app.get 같은 라우터에 미들웨어를 여러 개 장착할 수 있음 (next를 호출해야 다음 미들웨어로 넘어감)

 

→ 에러 처리 미들웨어는 매개변수가 err, req, res, next인 함수

→ 에러 처리 미들웨어는 특별한 경우가 아니면 가장 아래에 위치

 

app.use(
  morgan('dev'),
  express.static('/', path.join(__dirname, 'public')), //public 폴더에 정적 파일들이 있음
  express.json(),
  express.urlencoded({ extended: false }),
  cookieParser(process.env.COOKIE_SECRET),
); // 동시에 여러 개의 미들웨어 장착 가능 (다음 함수로 넘어가려면 next호출)

→ next를 호출하지 않는 미들웨어는 res.send나 res.sendFile 등의 메서드로 응답을 보내야 함

→ 미들웨어 장착 순서에 따라 어떤 미들웨어는 실행되지 않을 수도 있음

→ next함수에 route라는 문자열을 인수로 넣으면 다음 라우터의 미들웨어로 바로 이동하고, 그 외의 인수를 넣으면 에러 처리 미들웨어의 err 매개변수가 됨

* 미들웨어 간의 데이터 전달 방법
1) req.session 객체에 데이터 넣기 → 세션이 유지되는 동안 데이터도 계속 유지
2) req.data에 데이터 넣기 → 요청이 끝날 때까지 데이터 유지
3) app.set → 익스프레스에 전역적으로 데이터 저장 (앱 전체의 설정 공유에 사용)

 

→ 미들웨어 안에 미들웨어를 넣을 수 있음

app.use(morgan('dev'));
app.use((req, res, next) => {
  morgan('dev')(req, res, next);
}); // 두 코드가 같음

 

6.2 자주 사용하는 미들웨어 (패키지)

→ npm으로 설치하는 미들웨어는 보통 req, res, next를 내부에 포함하고 있음 (next도 내부적으로 호출) 

npm i [패키지] [패키지] ...

 

📌 dotenv

→ process.env를 관리하기 위한 패키지 (.env 파일을 읽어서 process.env로 만듬)

→ 앞으로 소개할 패키지 중 얘만 미들웨어 X

const dotenv = require('dotenv');

dotenv.config();

 

→ 이 패키지를 설치하면 .env 파일을 생성 (파일명이 .env이고 확장자 X)

→ .env 파일에 비밀키들을 적어둠

// .env
COOKIE_SECRET=cookiesecret

→ 보안과 설정의 편의성을 위해 별도의 파일을 생성하는 것

 

📌 morgan

→ 요청과 응답에 대한 정보를 콘솔에 기록 (ex. GET / 500 7.409 ms - 50)

const morgan = require('morgan');

app.use(morgan('dev')); // 인수로 dev, common, short, tiny 등 가능!

* 개발 환경에서는 dev, 배포 환경에서는 combined 추천!

 

📌 static

→ 정적인 파일들을 제공하는 라우터 역할 (기본적으로 제공되기에 따로 설치X)

app.use('요청 경로', express.static('실제 경로')); // 인수로 정적 파일들이 담겨 있는 폴더 지정
app.use('/', express.static(path.join(__dirname, 'public')));

→ 해당하는 파일이 없으면 알아서 내부적으로 next 호출

→ 해당하는 파일이 있으면 다음 미들웨어는 실행 X

 

📌 body-parser

→ 요청의 본문에 있는 데이터를 해석해서 req.body 객체로 만들어주는 미들웨어 (보통 폼 데이터나 AJAX 요청의 데이터를 처리)

→ 멀티 파트(이미지, 동영상, 파일) 데이터는 처리하지 못함 (multer 모듈 사용)

→ 익스프레스 4.16.0 버전부터 body-parser 미들웨어의 일부 기능이 익스프레스에 내장

→ 만약, JSON과 URL-encoded 형식의 데이터 외에 Raw, Text 형식의 데이터를 해석하려면 직접 설치!

app.use(express.json());
app.use(express.urlencoded({ extended: false })); // 폼 전송 데이터에 주로 사용
// extended 옵션이 true면 노드의 querystring 모듈 사용, false면 qs모듈 사용

→ 내부적으로 body를 받으면 스트림 처리를 해서 res.body에 추가 (req.on('data')를 할 필요가 없어짐)

 

📌 cookie-parser

→ 요청에 동봉된 쿠키를 해석해 req.cookies 객체로 만듬 (parseCookies와 유사)

const cookieParser = require('cookie-parser');
app.use(cookieParser(비밀키));

→ 유효 기간이 지난 쿠키는 알아서 걸러냄

→ 쿠키를 생성/제거하기 위해서는 res.cookie(키, 값, 옵션), res.clearCookie 메서드 사용

res.cookie('name', 'zerocho', {
    expires: new Date(Date.now() + 900000),
    httpOnly: true,
    secure: true,
}); // 옵션에는 domain, expires, httpOnly, maxAge, path, secure ...
res.clearCookie('name', 'zerocho', { httpOnly: true, secure: true }); // 쿠키를 지울려면 키와 값 외의 옵션도 일치해야 됨

 

→ 서명된 쿠키(req.signedCookies)의 경우, 제공한 비밀 키를 통해 해당 쿠키가 내 서버가 만든 쿠키임을 검증할 수 있음

→ 쿠키는 클라이언트에서 위조하기 쉬우므로 비밀 키를 통해 만들어낸 서명을 쿠키 값 뒤에 붙임(ex. name=zerocho.sign)

→ 옵션 중에 signed: true로 설정하면 쿠키 뒤에 서명이 붙음 (서명을 위한 비밀 키는 cookieParser 미들웨어에 인수로 넣은 process.env.COOKIE_SECRET)

 

📌 express-session

→ 세션 관리용 미들웨어 (로그인 또는 특정 사용자를 위한 데이터를 임시적으로 저장해둘 때 유용!)

→ 세션은 사용자별로 req.session 객체 안에 유지

const session = require('express-session');

app.use(session({
  resave: false, // 요청이 올 때 세션에 수정 사항이 생기지 않으면 다시 저장 X
  saveUninitialised: false, // 세션에 저장할 내역이 없으면 처음부터 세션을 생성 X
  secret: process.env.COOKIE_SECRET, // cookie-parser의 secret과 같게 설정하는 것이 좋음
  cookie: {
    httpOnly: true, // 클라이언트에서 쿠키 확인 X
    secure: false, // https가 아닌 환경에서도 사용 O
  },
  name: 'session-cookie', // 세션 쿠키의 이름
}));

* 배포 시에는  store라는 옵션을 통해 데이터베이스를 연결하여 세션을 유지 (레디스가 자주 사용됨)

req.session.name='' // 세션 등록
req.sessionID // 세션 아이디 확인
req.session.destroy() // 세션 모두 제거

→ 일반적으로 요청이 끝날 때 세션을 저장하는 메서드가 자동 호출!

 

📌 multer

→ 이미지, 동영상 등을 비롯한 여러 가지 파일들을 멀티파트(enctype="multipart/form-data") 형식으로 업로드할 때 사용하는 미들웨어

// 기본적인 설정
const multer = require('multer');

const upload = multer({
  storage: multer.diskStorage({
    destination(req, file, done) {
      done(null, 'uploads/'); // 첫 번째 인수는 에러, 두 번째 인수는 실제 경로 또는 파일 이름
    }, // 어디에
    filename(req, file, done) {
      const ext = path.extname(file.originalname);
      done(null, path.basename(file.originame, ext) + Date.now() + ext); // 파일명+현재시간.확장자
    }, // 어떤 이름으로
  }),
  limits: { fileSize: 5 * 1024 * 1024 }, // 파일 사이즈를 5MB로 제한
});

→ 서버에 uploads 폴더가 꼭 존재해야 됨 (없다면 직접 만들어주거나 fs모듈을 사용해서 서버를 시작할 때 생성) (p.246-247)

→ 미들웨어를 라우터 미들웨어 앞에 넣어두면, multer 설정에 따라 파일 업로드 후 req.file 또는 req.files 객체가 생성

// 파일을 하나만 업로드
app.post('/upload', upload.single('image'), (req, res) => { // single()의 인수는 input 태그의 name이나 폼 데이터의 키와 일치
  console.log(req.file, req.body);
  res.send('ok')
})

// 여러 파일 업로드
app.post('/upload', upload.array('many'), (req, res) => {
  console.log(req.files, req.body);
  res.send('ok')
})

// 여러 파일 업로드 (input 태그나 폼 데이터의 키가 다른 경우)
app.post('/upload', 
  upload.fields([{ name: 'image1' }, { name: 'image2' }]), 
  (req, res) => {
    console.log(req.files, req.body);
    res.send('ok')
}) // 업로드 결과는 req.files.image1, req.files.image2에 존재

// 파일 업로드 X
app.post('/upload', upload.none(), (req, res) => {
  console.log(req.body);
  res.send('ok')
})

 

6.3 Router 객체로 라우팅 분리하기

 

→ app.js에서 app.get()같은메서드가 라우터

→ 라우터를 분리하려면 라우터를 모아놓을 routes 폴더를 먼저 생성!

// app.js
// ...
const indexRouter = require('./routes'); // './routes/index'와 같음. index.js는 생략 가능!
const userRouter = require('./routes/user');
// ...
app.use('/', indexRouter);
app.use('/user', userRouter);

app.user((req, res, next) => {
  res.status(404).send('Not Found');
}); // 미들웨어에 일치하는 라우터가 없을 때
// ...
// routes/index.js
const express = require('express');
const router = express.Router();

router.route('/')
  .get((req, res) => {
    res.send('GET /');
  })
  .post((req, res) => {
    res.send('POST /');
  })

module.exports = router;
// routes/user.js
const express = require('express');
const router = express.Router();

router.route('/')
  .get((req, res) => {
    res.send('GET /user/');
  })
  .post((req, res) => {
    res.send('POST /user/');
  })

module.exports = router;

→ 라우터 주소에는 정규표현식을 비롯한 특수 패턴을 사용할 수 있음

router.get('/user/:id', (req, res) => {
  console.log(req.params, req.query); // :id는 req.params.id로 조회 가능
})

* 이 패턴을 쓸 때는 일반 라우터보다 뒤에 위치해야 함!

/users/123?limit=5&skip=10이라는 주소의 요청이 들어왔을 때
req.params = { id: '123' }
req.query = { limit: '5', skip: '10'}

 

📌 req, res 객체

// req
req.app // req 객체를 통해 app 객체에 접근
req.body // body-parser 미들웨어가 만드는 요청의 본문을 해석한 객체
req.cookies // cookie-parser 미들웨어가 만드는 요청의 쿠키를 해석한 객체
req.ip // 요청의 ip주소
req.params // 라우트 매개변수에 대한 정보가 담긴 객체
req.query // 쿼리스트링에 대한 정보가 담긴 객체
req.signedCookies // 서명된 쿠키들은 req.cookies 대신 여기에
req.get(헤더 이름) // 헤더의 값을 가져오고 싶을 때 사용
// res
res.app // res 객체를 통해 app 객체에 접근
res.cookie(키, 값, 옵션) // 쿠키를 설정하는 메서드
res.clearCookie(키, 값, 옵션) // 쿠키를 제거하는 메서드
res.end() // 데이터 없이 응답
res.json(JSON) // JSON 형식의 응답을 보냄
res.render(뷰, 데이터) // 템플릿 엔진을 렌더링해서 응답할 떄 사용
res.send(데이터) // 데이터와 함께 응답을 보냄 (데이터는 문자열, HTML, 버퍼, 객체, 배열...)
res.sendFile(경로) // 경로에 위치한 파일 응답
res.set(헤더, 값) // 응답의 헤더를 설정
res.status(코드) // 응갑 시의 HTTP 상태 코드 지정
// 메서드 체이닝
res
  .status(201)
  .cookie('test', 'test')
  .redirect('/admin');

 

6.5 템플릿 엔진 사용하기

 

→ 템플릿 엔진은 자바스크립트를 사용해서 HTML을 렌더링할 수 있게 해줌

 

📌 퍼그(제이드)

📌 넌적스

 

'백엔드 > Nodejs' 카테고리의 다른 글

[Nodejs] 패키지 매니저  (0) 2021.11.15
[Nodejs] http 모듈로 서버 만들기  (0) 2021.11.15
[Nodejs] 노드 기능 알아보기  (0) 2021.11.13
[Nodejs] 알아두어야 할 자바스크립트  (0) 2021.11.11
[Nodejs] 노드 시작하기  (0) 2021.11.10
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함