본문 바로가기

WINK-(Web & App)/Express.js (Node.js) 스터디

[2024-2 Node.js 스터디] 김민재 #3주차 - 익스프레스 웹 서버 만들기

반응형

익스프레스 프로젝트 시작하기

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

Express는 http 모듈의 요청과 응답 객체에 추가 기능을 부여했다. 또한, 코드를 분리하게 쉽게 만들어 관리하기 용이하다

  • 초기 설정을 위해 package.json 과 app.js를 작성했다

  • html 파일을 사용하기 전

  • index.html 파일을 만들고 난 후 이다

  • res.sendFile 이라는 명령어를 사용해 응답을 지정된 경로에 있는 파일로 보낼 수 있다

 

자주 사용하는 미들웨어


미들웨어(미들웨어가 익스프레스의 전부라고 해도 과언이 아니라는데 한번 느껴보겠다..)

  • 요청과 응답의 중간에 위치하며, 요청과 응답을 조작해 기능을 추가하기도 하고, 나쁜 요청을 거른다
  • app.use에 매게변수가 req, res, next인 함수를 넣으면 된다
  • 미들웨어는 위에서부터 아래로 순서대로 실행되고, next라는 매게변수는 다음 미들웨어로 넘어가는 함수다

morgan

  • morgan 연결 후 localhost:3000에 다시 접속해보면 기존 로그 외에 추가적인 로그를 볼 수 있다.
  • 인수로 dev 외에 combined, common, short, tiny 등을 사용할 수 있다.

static

  • static 미들웨어는 정적인 파일들을 제공하는 라우터 역할을 한다
  • 서버의 폴더 경로와 요청 경로가 다르므로 외부인이 서버의 구조를 쉽게 파악할 수 없다 → 보안에 큰 도움

body-parser

  • 요청의 본문에 있는 데이터를 해석해서 req.body 객체로 만들어주는 미들웨어
  • 보통 폼 데이터나 AJAX 요청의 데이터를 처리한다.
    • 폼 데이터: HTML 폼을 통해 수집된 데이터를 의미합니다.
    • AJAX: 페이지를 새로 고치지 않고 서버와 비동기적으로 데이터를 주고 받을 수 있게 해주는 기술

cookie-parser

  • 요청에 동봉된 쿠키를 해석해 req.cookies 객체로 만든다
  • 옵션은 domain, expires, httpOnly, maxAge, path, secure 등이 있다.

express-session

  • 로그인 등의 이유로 세션을 구현하거나 특정 사용자를 위한 데이터를 임시적으로 저장해둘 때 유용하다
  • 인수로는 세션에 대한 설정을 받는다
  • 일반적인 쿠키 옵션이 모두 제공되어 httpOnly를 true로 성정해 클라이언트에서 쿠키를 확인하지 못하게 하는 등 여러 설정을 할 수 있다.

미들웨어 특성 활용하기

  • 미들웨어는 req, res, next를 매개변수로 갖는 함수(에러 처리 미들웨어만 예외적으로 err, req, res, next를 가진다)로서 app.use나 app.get, app.post 등으로 장착한다
  • 동시에 여러 개의 미들웨어를 장착할 수도 있으며, 다음 미들웨어로 넘어가려면 next 함수를 호출해야 한다

  • next함수에 인수를 넣으면 특수한 동작을 하게 된다.
    • route라는 문자열을 넣으면 다음 라우터의 미들웨어로 바로 이동하고, 그 외의 인수를 넣으면 에러 처리 미들웨어로 이동한다.
  • 미들웨어 간에 데이터를 전달하는 방법 - res.locals 객체에 데이터를 넣어 요청이 끝날때까지만 데이터를 유지한다. 세션을 사용하면 세션유지하는 동안 데이터도 유지가 된다는 단점이 있기 때문
  • 미들웨어를 사용할때 유용한 패턴
  • 미들웨어 안에 미들웨어를 넣는 방식
미들웨어 안에 미들웨어를 넣는 방식

app.use(morgan('dev'));
// 또는
app.use((req, res, next) => {
  morgan('dev')(req, res, next);
});

app.use((req, res, next) => {
  if (process.env.NODE_ENV === 'production') {
    morgan('combined')(req, res, next);
  } else {
    morgan('dev')(req, res, next);
  }
});

multer

  • 이미지, 동영상 등을 비롯한 여러 가지 파일을 멀티파트 형식으로 업로드할 때 사용하는 미들웨어
    • 멀티파트 형식: enctype이 mulipart/form-data인 폼을 통해 업로드하는 데이터의 형식
  • multer의 기본적인 특성
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.originalname, ext) + Date.now() + ext);
    },
  }),
  limits: { fileSize: 5 * 1024 * 1024 },
});
- destination 함수: 어떤 이름으로 저장할지
- req: 요청에 대한 정보, file: 업로드한 파일의 정보, done: 함수

app.post('/upload', upload.single('image'), (req, res) => {
  console.log(req.file, req.body);
  res.send('ok');
});
- 파일을 하나만 업로드할 시 single 미들웨어를 사용 (이런식으로 업로드한다는 느낌)

app.post('/upload', upload.array('many'), (req, res) => {
  console.log(req.files, req.body);
  res.send('ok');
});
- 여러 파일을 업로드하는 경우 single -> array, image -> many
- html file 또한 input 태그에 multiple 사용

app.post('/upload',  upload.none(),  (req, res) => {
  console.log(req.body);
  res.send('ok');
});
- 파일을 업로드하지 않고 멀티파트 형식으로 업로드 간능
  • multer를 수정해 로컬 환경에서 실행시켜보았다

Router 객체로 라우팅 분리하기


  • 라우터를 만들때 요청 메소드와 주소별 분기 처리하느라 코드가 복잡함
    • 익스프레스를 사용하는 이유 중 하나가 라우팅을 깔끔하게 관리할 수 있다는 점
    • app.get 같은 메서드가 라우터 부분임
    • routes 폴더를 만들고 index.js, user.js 작성
const indexRouter = require('./routes');
const userRoutes = require('./routes/user');

app.use('/', indexRouter);
app.use('/user', userRoutes);

//index.js
const express = require('express');

const router = express.Router();

// GET / 라우터
router.get('/', (req, res) => {
    res.send('Hello, Express');
});

module.exports = router;

//user.js
const express = require('express');

const router = express.Router();

// GET /user 라우터
router.get('/', (req, res) => {
    res.send('Hello, User');
});

module.exports = router;

 

잘 작동되는 것을 볼 수 있다.

 

  • 라우터 주소에서 자주 쓰이는 패턴을 알아보자
// 라우트 매개변수
// 이 패턴을 사용할 시 일반 라우터보다 뒤에 위치해야한다.
router.get('/user/:id', (req, res) => {
  console.log(req.params, req.query);
});
  • 자주 사용하는 활용법 하나 더
router.get('/abc', (req, res) => {
  res.send('GET /abc');
});
router.post('/abc', (req, res) => {
  res.send('POST /abc');
});

// 관련있는 코드끼리 묶여 보기가 좋음

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

req, res 객체 살펴보기


  • 익스프레스의 req, res 객체는 http 모듈의 req, res 객체의 확장이다
  • 자주 쓰이는 속성과 메소드들을 알아보겠다

req 객체

  • req.app: req 객체를 통해 app 객체에 접근할 수 있습니다. req.app.get('port')와 같은 식으로 사용할 수 있습니다.
  • req.body: body-parser 미들웨어가 만드는 요청의 본문을 해석한 객체입니다.
  • req.cookies: cookie-parser 미들웨어가 만드는 요청의 쿠키를 해석한 객체입니다.
  • req.ip: 요청의 ip 주소가 담겨 있습니다.
  • req.params: 라우트 매개변수에 대한 정보가 담긴 객체입니다.
  • req.query: 쿼리스트링에 대한 정보가 담긴 객체입니다.
  • req.signedCookies: 서명된 쿠키들은 req.cookies 대신 여기에 담겨 있습니다.
  • req.get(헤더 이름): 헤더의 값을 가져오고 싶을 때 사용하는 메서드입니다. </aside>

res 객체

  • res.app: req.app처럼 res 객체를 통해 app 객체에 접근할 수 있습니다.
  • res.cookie(키, 값, 옵션): 쿠키를 설정하는 메서드입니다.
  • res.clearCookie(키, 값, 옵션): 쿠키를 제거하는 메서드입니다.
  • res.end(): 데이터 없이 응답을 보냅니다.
  • res.json(JSON): JSON 형식의 응답을 보냅니다.
  • res.locals: 하나의 요청 안에서 미들웨어 간에 데이터를 전달하고 싶을 때 사용하는 객체입니다.
  • res.redirect(주소): 리다이렉트할 주소와 함께 응답을 보냅니다.
  • res.render(뷰, 데이터): 다음 절에서 다룰 템플릿 엔진을 렌더링해서 응답할 때 사용하는 메서드입니다.
  • res.send(데이터): 데이터와 함께 응답을 보냅니다. 데이터는 문자열일 수도, HTML일 수도, 버퍼일 수도, 객체나 배열일 수도 있습니다.
  • res.sendFile(경로): 경로에 위치한 파일을 응답합니다.
  • res.set(헤더, 값): 응답의 헤더를 설정합니다.
  • res.status(코드): 응답 시의 HTTP 상태 코드를 지정합니다. 

 

  • req, res 객체의 메서드는 메서드 체이닝을 지원하는 경우가 많다
res
	.status(201)
	.cookie('test','test')
	.redirect('/admin')

템플릿 엔진 사용하기


  • HTML은 정적인 언어다. 주어진 기능만 사용할 수 있고, 사용자가 기능을 직접 추가할 수 없다.
    • 템플릿 엔진은 js를 사용해 html을 렌더링할 수 있게 함.
    • pug와 nunjucks에 대해 알아보자

 

템플릿 엔진 대신에 리액트, 뷰 쓰면 안되나요?

FE를 리액트나 뷰 등으로 제작한 뒤 노드 서버와 Restful한 방식으로 데이터를 주고받으면 된다.

이 책의 예제는 모두 넌적스로 되어 있지만, 리액트나 뷰를 별도로 공부해서 예제를 리액트나 뷰로 전환해보는 것도 좋은 경험이 될 것이다.

 

퍼그(제이드)

  • HTML 표현
  • 퍼그
doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/style.css')
  • HTML
<!DOCTYPE html>
<html>
  <head>
    <title>익스프레스</title>
    <link rel="stylesheet" href="/style.css" />
  </head>
</html>
  • 변수
    • HTMl과 다르게 자바스크립트 변수를 템플릿에 렌더링할 수 있다.
  • 반복문
    • for 대신에 each를 사용해 반복문
  • 조건문
    • 조건문을 이용해 편리하게 분기 처리 가능
  • include
    • 다른 퍼그나 html 파일을 넣을 수 있다.
  • extends와 block
    • 레이아웃을 정하고, 공통되는 레이아웃 부분을 따로 관리할 수 있다

넌적스

  • 퍼그의 HTML 문법 변화에 적응하기 힘든 사람에게 유용한 템플릿 엔진이다
  • 변수
    • res.render 호출 시 보내는 변수를 넌적스가 처리한다
  • 반복문
    • 넌적스에서는 특수한 문을 {%%}안에 쓴다. 반복문도 이 안에 for in문과 endfor사이에 위치한다.
    • index를 사용하고싶다면 loop.index라는 변수를 사용하자
  • 조건문
    • {% if 변수 %}, {% elif %}, {% else %}, {% endif %}로 이뤄져있다.
  • Include
    • 다른 html 파일을 넣을 수 있다.
  • extends와 block
    • 레이아웃을 정하며, 공통되는 레이아웃을 따로 관리할 수 있다.
    • 페이지마다 달라지는 부분을 block으로 비워 사용한다.

에러 처리 미들웨어

  • 404응답 미들웨어와 에러 처리 미들웨어를 수정해 error.html에 에러 내용을 적어 사용한다
  • 에러 처리 미들웨어는 error라는 템플릿 파일을 렌더링한다.
//error.html
app.use((req, res, next) => {
  const error =  new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
  error.status = 404;
  next(error);
});

app.use((err, req, res, next) => {
  res.locals.message = err.message;
  res.locals.error = process.env.NODE_ENV !== 'production' ? err : {};
  res.status(err.status || 500);
  res.render('error');

함께 보면 좋은 자료


반응형