JiSoo's Devlog

[Node.js] Express.js 작업 본문

Backend/Node.js

[Node.js] Express.js 작업

지숭숭숭 2024. 10. 22. 22:19

Express.js

Express는 파싱을 대신해 주기 위한 프로젝트 내부에 쉽게 연결하는 다른 패키지를 쉽게 설치하도록 도와준다

매우 유연하며 특별한 기능들을 과도하게 추가하지 않는다

애플리케이션을 구축하거나 유입되는 요청들을 처리함에 있어 특정 방식을 제공함으로써 높은 확장성을 지닌다

Express를 대상으로 구축된 제3자 패키지 수가 수천 가지에 달하고 크게 새로 구성할 필요 없이도 쉽게 Node Express 애플리케이션에 추가할 수 있다

 

Express.js 설정

npm install --save express

--save dev가 아닌 --save인 이유는 프로덕션 의존성이기 때문

프로덕션은 사용자에게 실제로 제공되는 환경

개발환경은 개발자들이 개발하고 테스트하는 환경

 

프로덕션 의존성: 실제 서비스가 돌아갈 때 꼭 필요한 라이브러리

개발 의존성: 앱을 개발하거나 테스트할 때만 필요한 라이브러리

 

개발 중에만 사용하는 툴뿐만 아니라 애플리케이션 배포 후 실행하게 될 모든 서버와 컴퓨터에 설치되어야 한다

 

const http = require("http");

const express = require('express');

const app = express();

const server = http.createServer(app);

server.listen(3000);

express 애플리케이션을 생성해 정해진 이름의 앱에 저장 가능

express 함수로 express 애플리케이션 만들고 app이라는 객체를 생성

 

미들웨어 추가

Express.js는 미들웨어와 깊게 연관되어 있는데 미들웨어는 들어오는 요청을 다양한 함수를 통해 자동으로 이동하는 것

모든 걸 하나의 함수로 처리하기보다 다수의 블록으로 분할할 수 있는 게 Express.js의 특징

 

use메서드를 사용하면 새로운 미들웨어 함수를 추가할 수 있다

app.use((req, res, next)=>{
  console.log('In the middleware!');
  next();
});
app.use((req, res, next)=>{
  console.log('In another middleware!');
});

인수로 3가지 (req, res, next)

next 인수인 함수는 다음 미들웨어로 요청이 이동할 수 있게 실행되어야 한다

위에 함수에서 next를 호출하지 않으면 그냥 저기서 멈추기 때문에 next를 호출해서 아래에 있는 미들웨어로 이동시켜야 한다

 

 

미들웨어 작동 방식

Express.js는 기본 응답 같은 것을 보내지 않는다

next를 호출하지 않을거면 응답을 보내야 하는데 send를 사용해 응답을 보낼 수 있고 any 유형의 본문을 첨부할 수 있다

app.use((req, res, next)=>{
  console.log('In another middleware!');
  res.send('<h1>Hello from Express!</h1>')
});

send 메서드는 기본적으로 html 콘텐츠 유형을 선택해 준다

 

 

Express.js 백그라운드

// const server = http.createServer(app);
// server.listen(3000);

app.listen(3000);

createServer 대신 app을 사용해 listen 호출도 가능

 

 

다른 라우트 방법

app.use("/", (req, res, next) => {
  console.log('This always runs!');
  next();
});

app.use("/add-product", (req, res, next) => {
  console.log("In another middleware!");
  res.send('<h1>"Add Product" Page</h1>');
});

app.use("/", (req, res, next) => {
  console.log("In another middleware!");
  res.send("<h1>Hello from Express!</h1>");
});

 

 

수신 요청 분석

app.use("/add-product", (req, res, next) => {
  res.send('<form action="/product" method="POST"><input type="text" name="title"><button type="submit">Add Product</button></form>');
});

app.use("/product", (req, res)=>{
  console.log(req.body);
  res.redirect('/');
})

redirect 안에 경로를 입력하면 리다이렉트 가능

요청 본문 저런식을 불러오면 undefined라고만 나오는데 req.가 요청 본문을 분석하려 하지 않기 때문이다

이를 위해 또 다른 미들웨어를 추가해 구현해야 하는데 일반적으로 경로 처리 미들웨어 이전에 둔다

npm install --save body-parser

이 제3자 패키지를 설치해야 한다

const body = require("body-parser");

const app = express();

app.use(bodyParser.urlencoded());

이렇게 미들웨어를 등록해 준다

본문 분석 패키지는 최종적으로 미들웨어 함수의 끝에서 next를 호출해 요청이 미들웨어에 도달하게 해 준다

app.use(bodyParser.urlencoded({extended: false}));

설정 옵션은 비표준 대상의 분석이 가능한지 나타낸다

 

설정까지 하면 요청 본문을 키값 쌍으로 자바스크립트 객체를 얻을 수 있다

 

※ 새로운 패키지를 설치하면 서버를 재시작하는 게 좋다

 

app.use("/product", (req, res) => {
  console.log(req.body);
  res.redirect("/");
});

이 상태로서는 미들웨어가 POST 요청뿐만 아니라 GET 요청에 대해서도 실행된다

use 대신 app.get을 사용하면 GET 요청에만 작동한다

app.post를 사용하면 POST 요청을 걸러준다

app.post("/product", (req, res) => {
  console.log(req.body);
  res.redirect("/");
});

 

 

Express 라우터 사용

Exress.js는 라우팅을 다른 파일에 위탁하는 방법을 제공한다

routes 폴더 안에 라우팅 관련 코드를 담아서 export 하면 된다

const expresss = require('express');

const router = expresss.Router();

router.get("/add-product", (req, res, next) => {
  res.send(
    '<form action="/product" method="POST"><input type="text" name="title"><button type="submit">Add Product</button></form>'
  );
});

router.post("/product", (req, res) => {
  console.log(req.body);
  res.redirect("/");
});

module.exports = router;

이런 식으로 작성하고 이 라우터를 등록하려면

const express = require("express");
const bodyParser = require("body-parser");

const app = express();

const adminRoutes = require('./routes/admin');
const shopRoutes = require('./routes/shop');

app.use(bodyParser.urlencoded({ extended: false }));

app.use(adminRoutes);
app.use(shopRoutes);


app.listen(3000);

지금은 adminRoutes랑 shopRoutes 순서를 바꿔도 제대로 작동하지만 순서를 신경 쓰는 게 중요하다

 

get, post 등은 경로와 정확히 일치하는 요청만을 처리한다

메소드 확인만이 아니라 정확한 경로인지 확인한다

 

 

404 오류 페이지 추가

app.use((req, res, next)=>{
  res.status(404).send('<h1>Page not found</h1>');
});

전송 전에 status나 setHeader 같은 모든 메서드 호출을 연계할 수 있고 마지막에 send이기만 하면 된다

status로 상태 코드 설정

 

 

경로 필터링

아웃소싱 라우트들은 시작 경로를 공유하는 경우가 있다

메서드가 다르면 같은 경로를 사용할 수 있다

router.get("/admin/add-product", (req, res, next) => {
  res.send(
    '<form action="/add-product" method="POST"><input type="text" name="title"><button type="submit">Add Product</button></form>'
  );
});

router.post("/admin/add-product", (req, res) => {
  console.log(req.body);
  res.redirect("/");
});

라우터 파일 경로가 같은 구간에서 시작하는 경우 라우트에서 해당 경로를 빼서 app.js에서 해당 구간을 필터로 추가할 수 있다

app.use('/admin', adminRoutes);

이런 식으로 필터에 의해 /admin으로 시작한 경우 해당 파일에서 /admin 부분은 다시 확인하지 않고 두 번째 부분만 확인해서 요청 라우트에 도달한다

 

HTML 페이지 서비스

html 파일을 라우트에서 서비스하려면

sendFile을 사용해 사용자에게 파일을 회신하면 된다

const path= require('path')

const expresss = require('express');

const router = expresss.Router();

router.get("/", (req, res, next) => {
  res.sendFile(path.join(__dirname, '../', 'views', 'shop.html'));
});

파일 경로를 구축하려면 Node.js가 제공하는 기능을 활용해야 한다

require('path')를 통해 코어 모듈 임포트하고 join 메서드를 호출해 생성한 경로로 파일을 보낸다

__dirname 은 운영체제의 절대 경로를 이 프로젝트 폴더로 고정해 주는 전역 변수

path.join을 사용하는 건 자동으로 리눅스와 윈도우 모두에서 작동하는 방식으로 경로를 생성해 주기 때문

path.join은 실행 중인 운영체제를 감지해 자동으로 올바른 경로를 생성한다

 

 

헬퍼함수로 내비게이션

헬퍼함수로 상위 디렉터리를 얻을 수 있다

const path = require('path');

module.exports = path.dirname(process.mainModule.filename);

dirname은 경로의 디렉터리 이름을 회신한다

mainModule 속성은 애플리케이션을 시작한 주요 모듈을 나타낸다 → app.js에서 만든 모듈

filename으로 이 모듈이 어떤 파일에서 시작되었는지 알아낼 수 있다

 

const rootDir = require("../util/path");

const router = expresss.Router();

router.get("/add-product", (req, res, next) => {
  res.sendFile(path.join(rootDir, "views", "add-product.html"));
});

이렇게 하면 깔끔하게 루트 디렉터리 경로 구축 가능

 

파일을 정적으로 서비스

정적이라는 의미는 express.Router나 다른 미들웨어 소프트에서 처리되지 않고 파일 시스템으로 직접 포워딩된다는 뜻

app.use(express.static(path.join(__dirname, 'public')));

express 객체 자체를 사용하고 static 메서드를 사용해 정적 파일을 서비스해 준다

위의 코드처럼 하면 사용자가 public 경로에 액세스 할 수 있다

 

 

정리

미들웨어 함수는 요청 객체나 응답 객체를 받아 응답 전송을 도와주는 함수

next 인수는 요청을 다음 순서의 미들웨어로 포워딩할 때 호출해야 하는 함수

순서는 루트 파일의 위에서부터 아래로 향하는 방향을 의미

응답을 전송하지 아닌 이상 항상 next를 호출해야 하고 응답 전송하려면 절대 next 호출해서는 안된다

 

app.use를 사용해 경로를 추가하면 쉽게 경로나 메서드로 요청을 필터링할 수 있고 app.get, app.post를 사용해도 된다

app.get과 같이 메서드에 따라 필터링하면 경로가 정확히 일치해야만 처리된다

app.user를 사용하면 url의 시작 부분 즉 localhost 다음 부분만이랑 경로 매칭

 

express.static을 사용하면 파일에 대한 정적 서비스 가능

 

728x90