21. [Web Server] 기초

문도연·2022년 6월 16일
0

Chapter1. CORS
Chapter1-1. SOP
Chapter1-2. CORS 동작 방식
Chapter1-2. CORS 설정 방법
과제 - Mini Node Server
Chapter2. Refactor Express
Chapter2-1. Express 시작하기
Chapter2-2. 자주 사용하는 Middleware
과제 - StatesAirline Server


Chapter1. CORS

  • SOP에 대해 이해할 수 있다.
  • CORS에 대해 이해할 수 있다.
  • CORS 동작 방식에 대해 이해할 수 있다.
  • CORS 설정 방법을 이해한다

Chapter1-1. SOP

SOP

Same-Origin Policy, 동일 출처 정책
‘같은 출처의 리소스만 공유가 가능하다’는 정책

출처(Origin)
프로토콜, 호스트, 포트의 조합으로 이 중 하나라도 다르면 동일한 출처로 보지 않음

  • https://codestates.com:443 vs https://codestates.com
    • https 프로토콜의 기본 포트는 443입니다. 따라서 https://codestates.comhttps://codestates.com:443 과 동일합니다.
    • ⇒ 두 URI는 프로토콜, 호스트, 포트가 모두 같은 동일 출처입니다.

SOP의 필요성

보안상 이점 때문임
예를 들어, SOP은 애초에 다른 사이트와의 리소스 공유를 제한하기 때문에 로그인 정보가 타 사이트의 코드에 의해서 새어나가는 것을 방지
->이래서 SOP은 모든 브라우저에서 기본적으로 사용하고 있는 정책임

그러나
다른 출처의 리소스를 사용하게 될 일은 너무나도 많습니다.
ex) 로컬 환경에서 개발을 할 때에도 클라이언트와 서버를 따로 개발하게 된다면 둘은 출처가 달라짐
ex) 개발중인 웹 사이트에서 네이버 지도 api를 사용하고 싶다면?

CORS의 필요성

SOP정책에서 비롯된 문제상황 해결
-> 즉, 다른 출처의 리소스를 받아올 수 있다!

CORS

Cross-Origin Resource Sharing의 줄임말로 교차 출처 리소스 공유를 뜻함

[MDN] 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제

즉, 브라우저는 SOP에 의해 기본적으로 다른 출처의 리소스 공유를 막지만, CORS를 사용하면 접근 권한을 얻을 수 있게 되는 것임

CORS 설정을 통해 서버의 응답 헤더에 ‘Access-Control-Allow-Origin’을 작성하면 접근 권한을 얻을 수 있습니다.

Chapter1-2. CORS 동작 방식

1. 프리플라이트 요청 (Preflight Request)

실제 요청을 보내기 전, OPTIONS 메서드로 사전 요청을 보내 해당 출처 리소스에 접근 권한이 있는지부터 확인하는 것

브라우저는 서버에 실제 요청을 보내기 전에 프리플라이트 요청을 보내고, 응답 헤더의 Access-Control-Allow-Origin으로 요청을 보낸 출처가 돌아오면 실제 요청을 보내게 됨
실제 요청시 서버는 요청을 수행하고 응답함

만약에 요청을 보낸 출처가 접근 권한이 없다면 브라우저에서 CORS 에러를 띄우게 되고, 실제 요청은 전달되지 않습니다.

프리플라이트 요청의 필요성

  • 실제 요청을 보내기 전에 미리 권한을 확인할 수 있기 때문에, 실제 요청을 처음부터 통째로 보내는 것보다 리소스 측면에서 효율적
  • CORS에 대비가 되어있지 않은 서버를 보호할 수 있습니다. CORS 이전에 만들어진 서버들은 SOP 요청만 들어오는 상황을 고려하고 만들어졌습니다. 따라서 다른 출처에서 들어오는 요청에 대한 대비가 되어있지 않았습니다.
    • 브라우저는 응답을 받은 후에야 CORS 권한이 없다는 것을 인지하지만, 브라우저가 에러를 띄운 후에는 이미 서버에서는 요청이 수행된 상태가 됩니다.
    • CORS에 대비가 되어있지 않은 서버라도 프리플라이트 요청에 대해CORS 에러를 띄우게 됨!

2. 단순 요청 (Simple Request)

특정 조건이 만족되면 프리플라이트 요청을 생략하고 요청을 보내는 것

  • 이 조건들을 모두 만족시키기는 어려우므로, 일단은 참고만
  • 조건
    • GET, HEAD, POST 요청 중 하나여야 합니다.
    • 자동으로 설정되는 헤더 외에, Accept, Accept-Language, Content-Language, Content-Type 헤더의 값만 수동으로 설정할 수 있습니다.
      • Content-Type 헤더에는 application/x-www-form-urlencoded, multipart/form-data, text/plain 값만 허용됩니다

3. 인증정보를 포함한 요청 (Credentialed Request)

요청 헤더에 인증 정보를 담아 보내는 요청

출처가 다를 경우에는 별도의 설정을 하지 않으면 쿠키를 보낼 수 없습니다. 민감한 정보이기 때문입니다.
이 경우에는 프론트, 서버 양측 모두 CORS 설정이 필요함

  • 프론트 측: 요청 헤더에 withCredentials : true 를 넣어줘야 함
  • 서버 측: 응답 헤더에 Access-Control-Allow-Credentials : true 를 넣어줘야 함
  • 서버 측에서 Access-Control-Allow-Origin 을 설정할 때, 모든 출처를 허용한다는 뜻의 와일드카드(*)로 설정하면 에러가 발생. 인증 정보를 다루는 만큼 출처를 정확하게 설정해주어야 합니다.

Chapter1-2. CORS 설정 방법

Node.js 서버

const http = require('http');

const server = http.createServer((request, response) => {
// 모든 도메인
  response.setHeader("Access-Control-Allow-Origin", "*");

// 특정 도메인
  response.setHeader("Access-Control-Allow-Origin", "https://codestates.com");

// 인증 정보를 포함한 요청을 받을 경우
  response.setHeader("Access-Control-Allow-Credentials", "true");
})

Express 서버

Express 프레임워크를 사용해서 서버를 만드는 경우에는, cors 미들웨어를 사용

const cors = require("cors");
const app = express();

//모든 도메인
app.use(cors());

//특정 도메인
const options = {
  origin: "https://codestates.com", // 접근 권한을 부여하는 도메인
  credentials: true, // 응답 헤더에 Access-Control-Allow-Credentials 추가
  optionsSuccessStatus: 200, // 응답 상태 200으로 설정
};

app.use(cors(options));

//특정 요청
app.get("/example/:id", cors(), function (req, res, next) {
  res.json({ msg: "example" });
});

과제 - Mini Node Server

Node.js는 HTTP 요청을 보내거나, 응답을 받을 수 있는 도구들을 제공합니다.
HTTP 요청을 처리하고 응답을 보내 주는 프로그램이 웹 서버(Web Server)임

  • Node.js의 http 모듈을 이용해 웹 서버를 만듭시다.
  • HTTP 요청과 응답을 다루기 위해 HTTP 모듈을 사용합니다.

서버 실행
nodemon:서버를 매번 실행시키지 않아도 되는 개발도구

  • npm install nodemon으로 설치합니다.

  • package.json의 "scripts"에 아래 코드를 추가합니다.
    "start": "nodemon server/basic-server.js"

  • 혹은 npx로 nodemon을 따로 설치하지 않고 실행합니다.
    npx nodemon server/basic-server.js

클라이언트 실행

  • client/index.html를 웹 브라우저에서 실행합니다.

  • 특정 포트로 클라이언트를 실행하고 싶다면, serve를 이용할 수 있습니다.
    npx serve -l 포트번호 client/

HTTP 트랜잭션 해부(Anatomy of an HTTP Transaction) 공식 가이드 문서

서버측 코드 : 요청에 반응하고 응답

const http = require('http');

http.createServer((request, response) => {
  // 서버가 요청을 받는 방식
  const { headers, method, url } = request;
  let body = [];
  request
    .on('error', (err) => {  //에러 이벤트 발생시
    console.error(err);
  }).on('data', (chunk) => { //데이터를 받는 이벤트 발생시, chunk는 데이터조각
    body.push(chunk);
  }).on('end', () => {  //데이터를 받는 행위가 끝날시 발생하는 이벤트
    body = Buffer.concat(body).toString(); // 요청body, Buffer는 데이터조각모음
   
    // 서버가 응답하는 방식
    response.on('error', (err) => {
      console.error(err);
    });
    
    response.statusCode = 200;
    response.setHeader('Content-Type', 'application/json'); //헤더
    // 주의: 위 두 줄은 다음 한 줄로 대체할 수도 있습니다.
    // response.writeHead(200, {'Content-Type': 'application/json'})

    const responseBody = { headers, method, url, body };

    response.write(JSON.stringify(responseBody));  //바디
    response.end(); 
    // 주의: 위 두 줄은 다음 한 줄로 대체할 수도 있습니다.
    // response.end(JSON.stringify(responseBody))

    // 새로운 부분이 끝났습니다.
  });
}).listen(8080); // 이 서버를 활성화하고 8080 포트로 받습니다.

Chapter2. Refactor Express

Express로 구현한 서버가 Node.js HTTP 모듈로 작성한 서버와 다른 점은 다음과 같습니다.

  • 미들웨어를 추가할 수 있다.
  • 라우터를 제공한다.

HTTP 모듈로 작성했던 서버를, 프레임워크 Express를 이용하는 방식으로 리팩토링하기

  • Express로 간단한 서버를 구현할 수 있다.
  • 미들웨어를 이해할 수 있다.
  • 미들웨어를 이용할 수 있다.
  • 미들웨어를 구현할 수 있다.
  • 과제 - Mini-Node Server를 Express로 리팩토링할 수 있다.

2-1. Express 시작하기

공식문서
https://expressjs.com/ko/starter/installing.html

라우팅(Routing)

메서드와 url(/lower, /upper 등)로 분기점을 만드는 것

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

즉, 라우팅: 메서드와 url에 따라 분기(Routing)하기

기본 라우팅
app.METHOD(PATH, HANDLER)

  • app은 express의 인스턴스입니다.
  • METHOD는 HTTP 요청 메소드입니다.
  • PATH는 서버에서의 경로입니다.
  • HANDLER는 라우트가 일치할 때 실행되는 함수입니다.
const express = require('express');
const app = express()
const port = 3000

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

app.put('/user', function (req, res) {
  res.send('Got a PUT request at /user');
});

app.listen(port, () => {
  console.log(`예시 포트 ${port}`)
})

Express는 프레임워크 자체에서 라우터 기능을 제공

const router = express.Router()

router.get('/lower', (req,res) => {
  res.send(data);
})

reouter.post('/lower', (req,res)=>{
  //do sth
})

각 라우트는 하나 이상의 핸들러 함수를 가질 수 있으며, 이러한 함수는 라우트가 일치할 때 실행됨

응답메소드

응답 오브젝트에 대한 메소드(res)는 응답을 클라이언트로 전송하고 요청-응답 주기를 종료할 수 있음
라우트 핸들러로부터 다음 메소드 중 어느 하나도 호출되지 않는 경우, 클라이언트 요청은 정지된 채로 방치됨

메소드설명
res.download()파일이 다운로드되도록 프롬프트합니다.
res.end()응답 프로세스를 종료합니다.
res.json()JSON 응답을 전송합니다.
res.jsonp()JSONP 지원을 통해 JSON 응답을 전송합니다.
res.redirect()요청의 경로를 재지정합니다.
res.render()보기 템플리트를 렌더링합니다.
res.send()다양한 유형의 응답을 전송합니다.
res.sendFile()파일을 옥텟 스트림의 형태로 전송합니다.
res.sendStatus()응답 상태 코드를 설정한 후 해당 코드를 문자열로 표현한 내용을 응답 본문으로서 전송합니다.

app.route()

라우트 경로에 대하여 체인 가능한 라우트 핸들러를 작성할 수 있음

app.route('/book')
  .get((req, res) => {
    res.send('Get a random book');
  })
  .post((req, res) => {
    res.send('Add a book');
  })
  .put((req, res) => {
    res.send('Update the book');
  });

express.Router

express.Router 클래스를 사용하면 모듈식 마운팅 가능한 핸들러를 작성할 수 있음Router - 인스턴스는 완전한 미들웨어이자 라우팅 시스템이며, 따라서 “미니 앱(mini-app)”이라고 불리는 경우가 많음

다음 예에서는 라우터를 모듈로서 작성하고, 라우터 모듈에서 미들웨어 함수를 로드하고, 몇몇 라우트를 정의하고, 기본 앱의 한 경로에 라우터 모듈을 마운트합니다.

//birds.js 라우터 파일이름

var express = require('express');
var router = express.Router();

// middleware that is specific to this router
router.use(function timeLog(req, res, next) {
  console.log('Time: ', Date.now());
  next();
});
// define the home page route
router.get('/', function(req, res) {
  res.send('Birds home page');
});
// define the about route
router.get('/about', function(req, res) {
  res.send('About birds');
});

module.exports = router;

위 과정 이후, 앱 내에서 다음과 같이 라우터 모듈(birds)을 로드하자

var birds = require('./birds');
...
app.use('/birds', birds);

앱은 이제 /birds/birds/about에 대한 요청을 처리할 수 있게 되었으며,
해당 라우트에 대한 특정한 미들웨어 함수인 timeLog를 호출할 것입니다.

Express 앱에서 사용하기 위한 미들웨어 작성

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

미들웨어 함수는 다음과 같은 태스크를 수행할 수 있음

  • 모든 코드를 실행.
  • 요청 및 응답 오브젝트에 대한 변경을 실행.
  • 요청-응답 주기를 종료.
  • 스택 내의 그 다음 미들웨어를 호출.

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

미들웨어 함수 호출의 요소

아래에는 “myLogger”라는 이름의 미들웨어 함수
이 함수는 앱에 대한 요청이 해당 함수를 거쳐 전달될 때 단순히 “LOGGED”를 인쇄함
이 미들웨어 함수는 myLogger라는 이름의 변수에 지정되어 있음

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

next() 이 함수를 호출하면 앱 내의 그 다음 미들웨어 함수가 호출됩니다.
next() 함수는 Node.js 또는 Express API의 일부가 아니지만 미들웨어 함수에 전달되는 세 번째 인수입니다.

미들웨어 함수를 로드하려면 미들웨어 함수를 지정하여 app.use()를 호출해야함
다음의 코드는 루트 경로(/)로 라우팅하기 전에 myLogger 미들웨어 함수를 로드함

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

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

app.use(myLogger);

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

app.listen(3000);

앱이 요청을 수신할 때마다, 앱은 “LOGGED”라는 메시지를 터미널에 인쇄합니다.
미들웨어의 로드 순서는 중요하며, 먼저 로드되는 미들웨어 함수가 먼저 실행됩니다.
루트 경로에 대한 라우팅 이후에 myLogger가 로드되면, 루트 경로의 라우트 핸들러가 요청-응답 주기를 종료하므로 요청은 절대로 myLogger에 도달하지 못하며 앱은 “LOGGED”를 인쇄하지 않습니다.
미들웨어 함수 myLogger는 단순히 메시지를 인쇄한 후 next() 함수를 호출하여 스택 내의 그 다음 미들웨어 함수에 요청을 전달합니다.

다음 예에서는 requestTime라는 특성을 요청 오브젝트에 추가합니다
미들웨어 함수는 “requestTime”

var requestTime = function (req, res, next) {
  req.requestTime = Date.now();
  next();
};

루트 경로 라우트의 콜백 함수는 미들웨어 함수가 req(요청 오브젝트)에 추가하는 특성을 사용합니다.

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

var requestTime = function (req, res, next) {
  req.requestTime = Date.now();
  next();
};

app.use(requestTime);

app.get('/', function (req, res) {
  var responseText = 'Hello World!';
  responseText += 'Requested at: ' + req.requestTime + '';
  res.send(responseText);
});

app.listen(3000);

앱의 루트에 대한 요청을 실행할 때, 앱은 이제 요청의 타임스탬프를 브라우저에 표시합니다.
사용자는 요청 오브젝트, 응답 오브젝트, 스택 내의 그 다음 미들웨어 함수, 그리고 모든 Node.js API에 대한 액세스 권한을 가지고 있으므로, 미들웨어 함수에 대한 가능성은 끝이 없음

미들웨어 사용

Express 애플리케이션은 기본적으로 일련의 미들웨어 함수 호출임
Express 애플리케이션은 다음과 같은 유형의 미들웨어를 사용할 수 있습니다.

  • 애플리케이션 레벨 미들웨어
  • 라우터 레벨 미들웨어
  • 오류 처리 미들웨어
  • 기본 제공 미들웨어
  • 써드파티 미들웨어

애플리케이션 레벨 및 라우터 레벨 미들웨어는 선택적인 마운트 경로를 통해 로드할 수 있음.
일련의 미들웨어 함수를 함께 로드할 수도 있으며, 이를 통해 하나의 마운트 위치에 미들웨어 시스템의 하위 스택을 작성할 수 있음

애플리케이션 레벨 미들웨어

app.use() 및 app.METHOD() 함수를 이용해 애플리케이션 미들웨어를 앱 오브젝트의 인스턴스에 바인드해야 함
이때 METHOD는 미들웨어 함수가 처리하는 요청(GET, PUT 또는 POST 등)의 소문자로 된 HTTP 메소드입니다.

마운트 경로가 없는 미들웨어 함수 예시
이 함수는 앱이 요청을 수신할 때마다 실행됨

var app = express();

app.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

/user/:id 경로에 마운트되는 미들웨어 함수
이 함수는 /user/:id 경로에 대한 모든 유형의 HTTP 요청에 대해 실행됨

app.use('/user/:id', function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

라우트 및 해당 라우트의 핸들러 함수(미들웨어 시스템)이 표시된 예시
이 함수는 /user/:id 경로에 대한 GET 요청을 처리

app.get('/user/:id', function (req, res, next) {
  res.send('USER');
});

하나의 마운트 경로를 통해 일련의 미들웨어 함수를 하나의 마운트 위치에 로드하는 예
/user/:id 경로에 대한 모든 유형의 HTTP 요청에 대한 요청 정보를 인쇄하는 미들웨어 하위 스택을 나타냄

app.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

라우트 핸들러를 이용하면 하나의 경로에 대해 여러 라우트를 정의할 수 있음
/user/:id 경로에 대한 GET 요청에 대해 2개의 라우트를 정의
두 번째 라우트는 어떠한 문제도 발생키지 않지만, 첫 번째 라우트가 요청-응답 주기를 종료시키므로 두 번째 라우트는 절대로 호출되지 않는다.

app.get('/user/:id', function (req, res, next) {
  console.log('ID:', req.params.id);
  next();
}, function (req, res, next) {
  res.send('User Info');
});

// handler for the /user/:id path, which prints the user ID
app.get('/user/:id', function (req, res, next) {
  res.end(req.params.id);
});

라우터 미들웨어 스택의 나머지 미들웨어 함수들을 건너뛰려면 next('route')를 호출하여 제어를 그 다음 라우트로 전달해야함
참고: next('route')는 app.METHOD() 또는 router.METHOD() 함수를 이용해 로드된 미들웨어 함수에서만 작동함

app.get('/user/:id', function (req, res, next) {
  // if the user ID is 0, skip to the next route
  if (req.params.id == 0) next('route');
  // otherwise pass the control to the next middleware function in this stack
  else next(); //
}, function (req, res, next) {
  // render a regular page
  res.render('regular');
});

// handler for the /user/:id path, which renders a special page
app.get('/user/:id', function (req, res, next) {
  res.render('special');
});

라우터 레벨 미들웨어

라우터 레벨 미들웨어는 express.Router() 인스턴스에 바인드된다는 점을 제외하면 애플리케이션 레벨 미들웨어와 동일한 방식으로 작동함

var router = express.Router();

router.use() 및 router.METHOD() 함수를 사용하여 라우터 레벨 미들웨어를 로드해야 함
위 애플리케이션 레벨 미들웨어에서의 예시에서 app 대신 router를 쓰면됨.

 // mount the router on the app
// router는 루트경로에 대한 모든 유형의 HTTP 요청에 대해 실행됨
app.use('/', router);

오류 처리 미들웨어

어떠한 함수를 오류 처리 미들웨어 함수로 식별하려면 오류 처리 미들웨어에는 항상 4개의 인수, (err, req, res, next) 시그니처를 갖는다.
next 오브젝트를 사용할 필요는 없지만, 시그니처를 유지하기 위해 해당 오브젝트를 지정해야 합니다. 그렇지 않으면 next 오브젝트는 일반적인 미들웨어로 해석되어 오류 처리에 실패한다

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

기본 제공 미들웨어

express.static(root, [options])
Express의 유일한 기본 제공 미들웨어 함수 express.static

이 함수는 serve-static을 기반으로 하며, Express 애플리케이션의 정적 자산을 제공하는 역할을 함

  • root 인수는 정적 자산의 제공을 시작하는 위치가 되는 루트 디렉토리를 지정
  • 선택사항인 options 오브젝트는 여러가지 같은 특성을 가질 수 있음
var options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now());
  }
}

app.use(express.static('public', options));

하나의 앱은 2개 이상의 정적 디렉토리를 가질 수 있습니다.

써드파티 미들웨어

Express 앱에 기능을 추가하려면 써드파티 미들웨어를 사용한다.
필요한 기능을 위한 Node.js 모듈을 설치한 후, 애플리케이션 레벨 또는 라우터 레벨에서 해당 모듈을 앱에 로드하십시오.

$ npm install cookie-parser

var express = require('express');
var app = express();
var cookieParser = require('cookie-parser');

// load the cookie-parsing middleware
app.use(cookieParser());

API 참조

express.json([옵션])

JSON request body 파싱하기

요청 body(payload)를 받는 작업을 간단하게 처리할 수 있습니다.

Express에 내장된 미들웨어 기능입니다.
JSON 페이로드(바디)로 들어오는 요청을 구문 분석하고 body-parser 를 기반으로 합니다.

Content-Type으로 JSON만 구문 분석하고 헤더가 type옵션 과 일치하는 요청만 보는 미들웨어를 반환

body파싱된 데이터를 포함하는 새 객체 request는 미들웨어 이후에 객체에 채워지며, 파싱할 본문 이 없거나 일치하지 않거나 오류가 발생한 경우 req.body빈 객체( )가 채워집니다.

app.use(express.json({strict:false})) //요청의 body를 가져옴

옵션 속성
strict
유형: boolean
기본값: true
배열과 객체만 허용하거나 사용하지 않도록 설정합니다. 비활성화되면 모든 것을 JSON.parse수락합니다.

type
유형: 혼합
기본값: "application/json"
이것은 미들웨어가 구문 분석할 미디어 유형을 결정하는 데 사용됩니다. 이 옵션은 문자열, 문자열 배열 또는 함수일 수 있습니다.

Request 객체는 API를 컨트롤하기 위한 3가지 메소드를 담고 있다.

3개 모두 request 프로퍼티에 해당하며
request 프로퍼티란 'HTTP request'로써 'req'로 표현되는 객체라고 함

req.params

주소에 포함된 변수(URL의 경로 부분에 있음)를 담는다.
예를 들어 https://okky.com/post/12345 라는 주소가 있다면 12345를 담는다.

기본값은 {}

// GET /user/tj
console.dir(req.params.name)
// => 'tj'
// => /user/:name에서 "name" 속성을 사용할 수 있어짐


// GET /api/detail/:id
console.dir(req.params)
// -> id값을 req.params로 불러옴

req.query


주소 바깥,
? 이후의 변수를 담는다.
예를 들어 https://okky.com/post?q=Node.js 일 경우 Node.js를 담는다

// GET /search?searchWord=구글검색
console.dir(req.query.name)
// => '구글검색'
// searchWord 매개변수(parameter)의 구글검색 이라는 값(argument)을 가져온다.

//GET /edit?postId=id
// id 값을 req.query로 불러올 수 있음. (req.query는 경로상 약속된 값을 가져옴)

req.params vs req.query

서버에서 req.params로 받을 수 있는 것은,
이미 예약된(?) 값이라고 생각 할 수 있다.

서버의 라우팅 코드가 다음과 같고,

post.get("/:id/:name", function1);

데이터를 요청하는 클라이언트 측의 axios가 아래와 같다면

await axios({
  method: "get",
  url: `www.example.com/post/1/jun`,
  params: { title: 'hello!' },
})

이경우 전송되는 url은 ‘www.example.com/post/1/jun?title=hello!’이다.

routing을 보면 id와 name이 예약되어 있음을 알고있다.
즉 서버에서 id라는 변수로 어떤 값이 들어올 것을 알고, name이라는 변수에 어떤 값이 들어올 것임을 알고 대기하고 있다. req.params는 url을 분석하여 id와 name자리에 있는 값을 낚아챈다.

console.log(req.params); // { id: '1', name: 'jun' }
console.log(req.query); // { title : 'hello!' }

axios에서 data를 보내기 위해 XML, JSON, Multi Form등을 사용해야 할 때는 post 요청을 보낼 때다.
delete, get을 사용할 때는, data로 넘기지말고 params로 간단히 id를 넘겨야 한다.

req.body

XML, JSON, Multi Form 등의 데이터를 담는다. 주소에선 확인할 수 없고 post method를 사용할 때 쓴다.

참고
req.params vs req.query (axios, express)
https://wooooooak.github.io/web/2018/11/10/req.params-vs-req.query/

res.send([body])

HTTP 응답을 보냄
send에 전해진 argument에 따라서 Content-type이 자동적으로 만들어진다.

기본적으로 서버에서 response 처리를 할 때 Content-Type을 지정해주어야 한다.
res.send는 우리가 어떤 데이터를 보내는지 파악을 해서 이에 알맞게 Contnet-Type을 지정해준다. 이는 Buffer,String, Object, Array 일 수 있다.

예를 들어서 Buffer 데이터를 반환해준다면 res.send는 자동으로 Content-Type을 application/octet-stream으로 지정한다.

res.send(Buffer.from('whoop'))
res.send({ some: 'json' })
res.send([1, 2, 3])
res.status(404).send('Sorry, we cannot find that!')
res.status(500).send({ error: 'something blew up' })

매개변수가 Buffer객체일 때 메소드는 Content-Type 이전에 아래와 같이 정의하지 않는 한 응답 헤더 필드를 "application/octet-stream"으로 설정

res.set('Content-Type', 'text/html')
res.send(Buffer.from('<p>some html</p>'))

매개변수가 a String일 때 메소드는 Content-Type: "text/html"로 설정합니다.

res.send('<p>some html</p>')

매개변수가 Array또는 Object인 경우 Express는 JSON 표현으로 응답합니다.

res.json([body])

JSON 응답을 보냅니다.
이 메서드는 JSON.stringify() 를 사용하여 JSON 문자열로 변환된 매개변수인 응답(올바른 콘텐츠 유형 포함)을 보냅니다.

매개변수는 객체, 배열, 문자열, 부울, 숫자 또는 null을 포함한 모든 JSON 유형이 될 수 있으며 다른 값을 JSON으로 변환하는 데 사용할 수도 있습니다.

즉,
json이 아닌 것도 json 형식으로 바꾸어서 보내준다. 즉 content-type 헤더를 application/JSON으로 고정한다. 그런데 결국 res.json()도 마지막에 res.send()를 호출한다.

res.json(null)
res.json({ user: 'tobi' })
res.status(500).json({ error: 'message' })

res.json vs res.send

둘은 사실상 기능은 동일하다.
하지만, json type을 전달하는 것이 확실하다면 res.json()을 활용하는 것이 좋은 선택이다.
이유1 data type이 json이라는 것을 쉽게 예측할 수 있음
이유2. res.send이 실행되는 순서는 res.send=>res.json=>res.send
res.json의 실행순서는 res.json=>res.send 이므로 한단계 덜 거침

Express 미들웨어

미들웨어 모듈설명내장 함수 (Express 3)
body-parserHTTP 요청 body를 파싱합니다.express.bodyParser
errorhandler개발 중에 발생하는 에러를 핸들링하고 디버깅합니다.express.errorHandler
serve-static정적 파일을 제공합니다.express.static

2-2. 자주 사용하는 Middleware

미들웨어는 말 그대로 프로세스 중간에 관여하여 특정 역할을 수행
미들웨어를 이용하면 Node.js 만으로 구현한 서버에서는 번거로울 수 있는 작업을 보다 쉽게 적용할 수 있음

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

case 1: POST 요청 등에 포함된 body(payload)를 구조화할 때

const jsonParser = express.json();

// 특정 경로에 대한 요청에 적용
app.post('/api/users', jsonParser, function (req, res) {
})

// 모든 요청에 적용
app.use(express.json({strict:false}))

case 2: 모든 요청/응답에 CORS 헤더를 붙일 때

npm install cors

const cors = require('cors');

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

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

case 3: 모든 요청에 대해 url이나 메서드를 확인할 때

모든 요청에 동일한 미들웨어를 적용할때는 app.use 메서드 사용

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

const myLogger = function (req, res, next) {
  console.log('LOGGED');
  const { method, url } = req;
  console.log('http request method is ' + method + 'url is ' + url);
  next();
};

app.use(myLogger);

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

app.listen(3000);

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

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

app.use((req, res, next) => {
  // 토큰이 있는지 확인, 없으면 받아줄 수 없음.
  if(req.headers.token){
    req.isLoggedIn = true;
    next();
  } else {
    res.status(400).send('invalid user')
  }
})

서버에서는 요청에 포함된 데이터를 통해 미들웨어가 요구하는 조건에 맞지 않으면, 불량품으로 판단하고 돌려보내도록 구현할 수 있습니다.


과제 StatesAirline Server 구현하기

  • Express 프레임워크를 사용하여 States Airline API Server를 개발합니다.
  • Flight API 와 Book API 에서 정의한 API 요청을 수행하는 코드를 작성합니다.
  • Flight Router, Book Router 에 있는 모든 테스트를 통과해야 합니다.
  • States Airline Server는 클라이언트의 요청에 따라 항공편과 예약 데이터를 조회, 생성, 수정, 그리고 삭제하는 기능을 수행할 수 있어야함
  • Express 프로젝트에서 사용하는 폴더 구조를 이해
  • Express를 활용하여 API 요청을 처리하는 방법 실습
  • 코드를 작성하면서 폴더 구조가 어떻게 구성이 되어 있고 router , controller가 어떠한 역할을 수행하는지 관찰해보기

실시간세션

  • app.use()로 라우터를 분기해줄수있음
  • controller MVC 패턴 쓸 때 씀
  • 라우터에 대한 어떤 행위를 해줄때 컨트롤러 씀
  • req.query 엄청많이씀
    • req.query = 쿼리와 값을 프로퍼티로 가지는 객체를 반환
    • req.query.쿼리명 = 쿼리 값을 반환
profile
중요한건 꺾이지 않는 마음이 맞는 것 같습니다

0개의 댓글