Web Server – 기초

node.js를 이용한 백엔드 구축
→ API 서버를 구현하여 Express, 라우팅, Server-side 디버깅 학습

 

CommonJS모듈의 개념 학습

학습 목표

*HTTP
- HTTP 요청/응답
- HTTP 의 요청 방식과 응답 코드

* node.js modules의 사용
- node.js의 내장 http 모듈 사용법
- http 모듈 사용시에 서버에 CORS 설정하기
- CommonJS를 이용한 모듈 내보내기/불러오기

* 라우팅과 API
- 라우팅(조건에 따른 분기)을 이해하고, 이를 서버 코드에서 구현
- 클라이언트가 사용할 수 있도록, 서버 API 문서를 직접 작성

* Express 라이브러리
- express 라이브러리에 대한 이해
- 미들웨어의 개념을 이해

* 서버 개발과 디버깅
- CRUD 를 수행하는 웹 서버 개발 방법
- 서버 개발을 돕는 다양한 툴

CORS 리뷰 및 적용

SPA가 등장하고, 웹 앱이 고도화되면서 서버-클라이언트 통신 간에 여러 곳(다른 origin)에 있는 리소스를 활용한 필요가 생김
클라이언트(브라우저?) 에서는 same origin이 아니라 cross origin 요청을 해야 한다

CORS(Cross Origin Resource Sharing)

cross origin에서 리소스(서버 자원)을 요청하여 사용한다

 

보안 상의 이유로 브라우저에서 크로스 도메인 요청은 기본적으로는 제한되어 있음
→ 웹 애플리케이션 고도화를 위해 개선 요청(개발자들)
→ 서버가 허용한 범위 내에서 cross origin 요청이 허용됨

defaultCorsHeaders 의 내용 예시

* 모든 도메인(*)을 허용한다
* 메소드는 GET POST PUT DELETE OPTIONS 만 허용
* 헤더에는 content-type과 accept만 쓸 수 있다
* preflight request는 10초까지 허용됨

// 이를 코드화 하면 다음과 같다

const defaultCorsHeader = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Accept',
  'Access-Control-Max-Age': 10  //응답 헤더는 결과를 브라우저 캐시에 캐시 할 수있는 기간을 나타냅니다.
};

OPTIONS 메소드

간단하게, preflight 요청이다

 

서버에서 Allow 하는 조건들을 다 맞추고 있는가?
→ 사전에 서버에 확인하는 요청(preflight request)

defaultCorsHeaders{
...
access-control-allow-origin: 허용주소.com,

...

}

//이 `화이트 리스트`만 서버에 OPTIONS 요청이 허용 된다

이 OPTIONS 메소드로 CORS 설정과 관련된 부분을 체크한다고 볼 수 있는 듯 싶다?

Node 앱 디버깅 하는 법

postman 앱과 함께 써서 직접 해보자

 

--inspect 옵션을 통해 디버깅이 가능하다

ex)"scripts": {
"start": "node --inspect server/basic-server.js",

그런데 나는 시작하자마자 디버깅을 걸고싶다?
→ 딱 브레이킹 포인트가 시작지점(처음)부터 걸려있다고 보면된다
→ 옵션: --inspect-brk

"scripts": {
"start": "node --inspect-brk server/basic-server.js",
"test": "echo no test",

노드몬이 다행히 이 옵션들을 다 지원하므로, node→ nodemon으로 바꿔서 쓰면 된다!

HTTP 통신(클라이언트-서버) 관련 보충 내용

<http 요청 메시지의 예>

GET /kimcoding/message    http/1.1    // 메소드    /uri    http버전
// 요청의 첫줄에는  메소드 uri http 버전이 들어간다
Content-type: application/json    //헤더키: 헤더값
            // 반드시 한칸 띄우기
payload(body부분)


----------

<http 응답 메시지의 예>

http/1.1 200 ok        // http 버전    http 상태 코드
헤더키: 헤더값
        // 반드시 한칸 띄우기
body 부분

node.js 공식문서 읽기

node.js 의 공식문서를 참고하여 미니 노드 서버를 만들어 보자

 

https://nodejs.org/ko/docs/guides/anatomy-of-an-http-transaction/

http 트랜잭션 해부

공식 문서 내용 중 요청과 응답에 대한 부분만 추려보면,

request.on('data', (chunk)=> { //요청에 data가 왔을때, callback함수를 실행해라~ //이벤트리스너 생각
body.push(chunk);
}).on('end', ()=>{
body=Buffer.concat(body).toString();//body.toString() 과 똑같음

// 여기에 요청바디가 문자열로 담겨있다.
response.writeHead(201, defaultCorsHeader);
console.log(body);
response.end(body.toUpperCase());
})

// .on 은 이벤트라고 생각하면 편하다

chunk란?
buffer를 이름지어준 것. buffer가 데이터 흐름 속에서 하나의 덩어리라서 그냥 인자명을 이름지은 것

크로스 오리진의 기준

크로스 오리진(Cross Origin)을 판별하는 기준에는 다음 아래의 조건이 있다
이 조건이 다르다면 같은 오리진이 아닌 다른 오리진이라는 뜻!

1. 스킴(http, https 같은거)
2. 호스트(localhost, ...)
3. 포트번호(4000,5000, ...)

serve 유틸리티

클라이언트를 서버처럼 띄워주는 유틸리티 이다
npm으로 설치하면 클라이언트를 서버처럼 동작시킬 수 있다
→ 로컬 주소가 아닌 localhost:port 처럼 동작하게 하는 것!

 

vscode에서 Live Sever 확장 앱을 설치하여 live sever로 동작시키면 같은 원리로 작동하는 듯 싶다

 

서버 모드로 동작시키면 기존에는 프리플라이트 요청이 건by건으로 동작하던 것이(로컬 모드)
CORS에서 설정한 값('Access-Control-Max-Age': 10)에 따라 해당 age가 지나면 다시 프리플라이트 요청을 날리는 식으로 동작한다


Express를 통한 리팩토링

express 공식 문서 를 보면서 시작하기부터 내용을 실습해 보는 것을 추천한다!

https://expressjs.com/ko/

Node.js Express

Express.js는 Node.js 환경에서 웹 서버, 또는 API 서버를 제작하기 위해 사용되는 인기 있는 프레임워크

Express의 장점

1. 미들웨어 추가가 편리
2. 자체 라우터 기능을 제공

Express 설치

다운로드 원하는 폴더 내에서
npm install express --save // 의존성(dependency) 모듈로 설치되도록 설정

라우팅 처리하기

메소드와 URL에 따라 분기(라우팅)하면 된다

//기존 node.js 코드

const requestHandler = (req, res) => {
  if(req.url === '/lower') {
    if (req.method === 'GET') {
      res.end(data)
    } else if (req.method === 'POST') {
      req.on('data', (req, res) => {
        // do something ...
      })
    }
  }
}

 

이를 express의 라우팅을 활용하면 다음과 같이 직관적 코드가 작성 가능하다

const router = express.Router()

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

router.post('/lower', (req, res) =>{
  // do something
})

미들웨어(Middleware)

express의 가장 큰 장점인 미들웨어
→ 일종의 완성된 함수?메소드? 같은 느낌이다

자주 사용하는 미들웨어

미들웨어를 주로 사용하는 상황은 다음과 같다


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

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

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

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

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

xxx.use() 메소드를 사용한다

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

const myLogger = function (req, res, next) {
  console.log('LOGGED'); // 이 부분을 req, res 객체를 이용해서 고쳐도 된다
  next();
};

app.use(myLogger);

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

app.listen(3000);

 

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

body-parser 미들웨어를 사용하면 간단히 처리 가능하다
→ express 최신버전에는 이 기능이 내장되어 있으므로 안 받아도 된다

 

내장된 express.json() 메서드를 이용
→ json화시킬때 원시 데이터 타입들도 내용이 나오게 하려면 strict 옵션을 false로 지정해야 한다

 

만약 일반 text를 요청/응답한다면 express.text() 메서드 사용

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

server.use(express.json({strict: false}));    //반드시 strict: false 해줘야 된다!

…(생략)

server.post('/upper', (req, res)=> {
console.log('upper 테스트');
console.log(req.body);
res.json((req.body).toUpperCase());
})

 

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

간단하게 cors 미들웨어를 설치해서 쓰면 간편하다

const cors = require('cors')

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

 

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

로그인 및 권한과 관련된 부분에서 사용한다

// HTTP 요청에서 토큰이 있는지 여부를 판단하여, 있으면, next()하고 없으면 에러(400)를 보냄

app.use((req, res, next) => {
  // 토큰의 유무 판단?
  if(req.headers.token){
    req.isLoggedIn = true;
    next()
  } else {
    res.status(400).send('invalid user')
  }
})

 

express에 대한 보충 내용

  • 라우팅: 메소드와 URL(엔드포인트)를 이용해서 분기(routing)점을 만드는 것
  • express의 장점
* 라우팅할 때 if 안써도 된다
* body 받기가 편함(미들웨어 사용시)
→ buffer 조합으로 할 필요 x
* cors 적용이 간편(미들웨어 사용시)

→ 즉, 미들웨어 잘 사용하는 게 중요!

 

  • res.send() vs. res.end()

둘 다 비슷해보이나, 데이터 없이 응답할 때는 res.end()를 사용하고,
데이터를 담아서 응답할때는 res.send()를 사용하는 것을 추천함(공식문서)

복사했습니다!