express 라이브러리로 Web Server 구현하기

🐶·2021년 7월 12일
1

개념 정리

목록 보기
24/41
post-custom-banner

MERN stack은 JavaScript 생태계에서 인기 있는 프레임워크인 MongoDB, Express, React, Node를 지칭하는 말이다. 이 중에서 Express.js는 Node.js 환경에서 웹 서버, 또는 API 서버를 제작하기 위해 사용되는 인기 있는 프레임워크이다.

지난 스프린트 때 Mini Node Server에서 http 모듈로 작성했던 서버를(몇일 전에 관련 포스팅 한 적 있음), Express 프레임워크를 이용하는 방식으로 리팩토링해보았다.

Express로 구현한 서버가 http 모듈로 작성한 서버와 다른 점은 아래와 같다.

  • 미들웨어 추가가 편리하다.
  • 자체 라우터를 제공한다.(조건문을 줄줄이 달지 않아도 된다~)

Node.js에서 (+express라는 라이브러리를 써서) 간단한 서버를 만들어보기

express 설치

  • 라이브러리? 코드를 쉽게 짜기 위해 빌려쓰는 코드 모음집.
  1. 작업 진행하려는 폴더에서 npm init 입력하여 셋팅하기

  2. npm --> 라이브러리 설치를 도와주는 도구

  3. 어떤 라이브러리를 설치했는지 기록해주는 것--> package.json이라는 파일이다. 이 파일은 npm init 명령어에 자동으로 생성된다.

  4. entry point에 내가 원하는 파일명을 적어준다.

  5. npm install express라고 입력한다. --> package.json에 기록이 된다.

  6. 라이브러리를 설치하는 순간 node_modules라는 폴더가 생성된다. 이 폴더는 라이브러리와 관련된 파일들을 담고 있다.

  • yarn을 설치할 수 있다.(npm과 똑같은 것.. 다만 명령어가 조금 다름: yarn add express -->조금 더 빠르고 안정적이다)

Node.js+ express로 간단한 웹 서버 만들기

공식문서 참조

1. 서버를 띄우기 위한 기본 셋팅을 진행한다. 아래는 서버를 열기 위한 기본 문법이라고 생각하면 된다.

const express = require('express'); //라이브러리 첨부
const app = express(); // 첨부한 라이브러리를 이용하여 객체를 하나 만든다.

app.listen(8080, ()=>{
	console.log('listening on 8080')
}); //listen(서버 띄울 포트 번호, 띄운 후 실행할 코드)

--> 코드 작성 후 해당 파일명(ex. server.js)을 터미널에 적고 실행시켜보면 node server.js --> 콘솔 메세지가 잘 뜬다.
--> 웹브라우저에서 localhost:8080 이라고 주소창 입력을 하면 서버에 접속이 된다.

2. GET요청을 처리하는 코드 작성

  • client: 주소창에 URL을 입력해서 서버에 GET 요청을 할 수 있음
  • server: 누군가 /pet으로 들어오면 XX를 보내주세요~ 라고 코드를 작성
app.get('/pet', (req, res)=>{
	res.send('펫용품을 쇼핑할 수 있는 페이지'); //send는 응답메소드
})

3. 요청이 들어왔을 때 HTML 파일을 띄우기

app.get('/', (req, res)=>{
	res.sendFile(__dirname + '/index.html')
})

서버 코드를 수정하였을 때 서버를 계속 재실행해야 한다.
이 과정을 자동화시키는 자바스크립트 라이브러리... 바로 nodemon!
터미널에 npm install -g nodemon 입력하면 라이브러리 설치 됨.
그리고 nodemon server.js로 서버 실행하기.
코드를 수정하고 저장하면 자동으로 서버가 재실행된다!


express

라우팅: 메소드와 URL에 따라 분기(Routing)하기

클라이언트는 특정한 HTTP 요청 메소드(GET, POST 등)나 서버의 특정 URI(또는 경로)로 HTTP 요청을 보낸다. 라우팅은 클라이언트의 요청에 해당하는 메소드와 Endpoint에 따라 서버가 응답하는 방법을 결정하는 것이다.

라우트 메소드(.get, .post ...)는 HTTP 메소드 중 하나로부터 파생되며, express 클래스의 인스턴스에 연결된다.

const router = express.Router()

router.get('/lower', (req, res) =>{ //GET요청 처리
  res.send(data) 
})

router.post('/lower', (req, res) =>{ //POST요청 처리
  // do something
})

미들웨어

요청 오브젝트(req), 응답 오브젝트 (res), 그리고 애플리케이션의 요청-응답 주기 중 그 다음의 미들웨어 함수 대한 액세스 권한을 갖는 함수이다. 그 다음의 미들웨어 함수는 일반적으로 next라는 이름의 변수로 표시된다.

미들웨어를 사용하는 상황은 아래와 같다.

  1. 모든 요청에 대해 url이나 메소드를 확인할 때
  2. POST 요청 등에 포함된 body(payload)를 구조화할 때(쉽게 얻어내고자 할 때)
  3. 모든 요청/응답에 CORS 헤더를 붙여야 할 때
  4. 요청 헤더에 사용자 인증 정보가 담겨있는지 확인할 때

현재의 미들웨어 함수가 요청-응답 주기를 종료하지 않는 경우에는 next()를 호출하여 그 다음 미들웨어 함수에 제어를 전달해야 한다. 그렇지 않으면 해당 요청은 정지된 채로 방치된다.(timeout에 걸린다)

다음 예시에 미들웨어 함수 호출의 요소가 표시되어 있다.
(출처: 공식문서)

1. 모든 요청에 대해 url이나 메소드를 확인할 때

특정 enpoint가 아니라 모든 요청에 동일한 미들웨어를 적용하려면 메소드 app.use 를 사용합니다.

const express = require('express');
const app = express();

const myLogger = function (req, res, next) {
  console.log('LOGGED'); 
  next();
};

app.use(myLogger); //모든 요청에 LOGGED가 출력된다

app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.listen(3000);

2. POST 요청 등에 포함된 body(payload)를 구조화할 때(쉽게 얻어내고자 할 때)

순수 node.js로 HTTP body(payload)를 받을 때에는 Buffer를 조합해서 다소 복잡한 방식으로 body를 얻었다.

클라이언트 측에서 API post 와 put 메소드로 요청시 (get delete 는 불가하다. 링크 참고) body 를 포함하여 보낼 수 있는데 이 값을 서버 측에서 받는다고 그대로 사용할 수 있는 것이 아니고 서버 내에서 해석 가능한 형태로 변형해야 사용할 수 있게 되는 것이다.

이때 API 요청에서 받은 body 값을 파싱하는 역할을 수행하는 것이 bodyParser 라는 미들웨어이다. parsing에는 아래 2가지 방법이 있다.

  1. body-parser 사용(4.16 이전 버전)
    body-parser 미들웨어를 사용하면 이 과정을 간단하게 처리할 수 있다.(외부 모듈을 사용해야 한다)
const bodyParser = require('body-parser')
const jsonParser = bodyParser.json()

// 생략
app.post('/api/users', jsonParser, function (req, res) {
  // req.body에는 JSON의 형태로 payload가 담겨져 있습니다.
})
  1. express.json() 사용(4.16 이후 버전)
const express = require('express');
const app = express();

app.use(express.json());
app.post('/', function(req, res){
  console.log(req.body); 
  res.send(req.body); 
});

app.listen(3000);

3. 모든 요청/응답에 CORS 헤더를 붙여야 할 때

순수 node.js 코드에 CORS 헤더를 붙이려면, 응답 객체의 writeHead 메소드 등을 이용한다. 이런 메소드를 이용하더라도 Access-Control-Allow-* 헤더를 매번 재정의해야 한다. 그뿐만 아니라, OPTIONS 메소드에 대한 라우팅도 따로 구현해야 합니다.

CORS 미들웨어를 사용하면 이 과정을 간단하게 처리할 수 있다.

const cors = require('cors')

// 생략
app.use(cors()) // 모든 요청에 대해 CORS 허용

///////특정요청에만 cors를 적용할 수도 있다/////////

const cors = require('cors')

// 생략
// 특정 요청에 대해 CORS 허용
app.get('/products/:id', cors(), function (req, res, next) {
  res.json({msg: 'This is CORS-enabled for a Single Route'})
})

4. 요청 헤더에 사용자 인증 정보가 담겨있는지 확인할 때

다음은 HTTP 요청에서 토큰이 있는지 여부를 판단하여, 이미 로그인한 사용자일 경우 성공, 아닐 경우 에러를 보내는 미들웨어 예제이다.

app.use((req, res, next) => {
  if(req.headers.token){ //request의 headers에 토큰이 있는지 확인
    req.isLoggedIn = true;
    next()
  } else {
    res.status(400).send('invalid user') //토큰 없을 때의 응답
  }
})

로그인 없이 웹사이트에 접근을 시도했을 때, 로그인 창 등으로 되돌려 보내는 경우가 해당 상황이다. 서버에서는 요청에 포함된 데이터를 통해 미들웨어가 요구하는 조건에 맞지 않으면, 불량품으로 판단하고 돌려보내도록 구현할 수 있습니다.

오류처리?

다른 미들웨어 함수와 동일한 방법으로 오류 처리 미들웨어 함수를 정의할 수 있지만, 오류 처리 함수는 3개가 아닌 4개의 인수, 즉 (err, req, res, next)를 갖는다는 점이 다르다.

app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

더 공부해볼 자료

https://velog.io/@yhe228/Express-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-send%EB%A9%94%EC%86%8C%EB%93%9C%EC%99%80-end%EB%A9%94%EC%86%8C%EB%93%9C%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9E%91%EB%8F%99%EB%B0%A9%EC%8B%9D
https://haeguri.github.io/2018/12/30/compare-response-json-send-func/

send메소드와 end메소드의 차이점 & 작동방식

profile
우당탕탕 개발일기📝🤖
post-custom-banner

0개의 댓글