protocol: http 또는 https(강화된 보안, 별도 인증문서 필요)
domain: www.google.com / www.naver.com 등이 해당하며 hostname 혹은 ip + port 와 동치이다. : localhost:2200 / 127.0.0.1:2200. 도메인은 네트워크 상 일종의 약속이다. 선점방식이기 때문에 이미 존재하는 도메인은 사용할 수 없다.
path: 도메인의 특정 위치를 가리킵니다. (points specific location of domain)
query: ?key=value를 이용해 특정 페이지를 탐색한다.
노드는 모듈방식을 채택하고 있어 필요한 기능을 다양하게 정의된 모듈로 부터 끌어다 쓰는 것이 가능하다. 이 중 'http' 라는 내장모듈을 사용하면 서버를 직접 구축할 수 있다.
// http 모듈 가져오기
const http = require('http')
// === import http from 'http'
// const app = http.createServer([options], [request listener])
// return type = http.Server
// options = null, listenr = function(req,res)
const app = http.createServer((req,res) => {
const a = req. ...
res. ...
}
);
→ 반환 타입(return type)이 http.Server임을 주목합시다.
http.Server는 listen 매서드를 갖는데, 이를 통해 특정 포트와 서버를 연결할 수 있다.
// 앞선 코드의 연장
// 사용법 : app.listen([port],[hostname],[listener]);
app.listen(3000, 'localhost', () => console.log(port number 3000 is running on));
→ 이제 url : localhost:3000 은 상기 http.createServer 내 콜백함수 본문에 담긴 내용을 보여주는(로드해주는) 웹페이지의 주소가 된다. 본문에 직접 소스를 작성해 줄이 길어지거나 지저분하다면 별도 파일을 만들어서 연결해주면 됩니다. html, css, txt, javascript 등등 다양한 확장자가 대상이될 수 있다.
추가적으로 localhost라는 hostname은 본인의 컴퓨터 로컬에서만 접근 가능한 url을 만들며 타 컴퓨터에서 액세스가 불가능 하다. ip로는 127.0.0.1로 표현할 수 있다.
보통 하나의 파일에 150 ~ 200 줄이 넘어가는 코드가 들어가면 보기 지저분하다고들 한다. 긴 소스를 억지로 집어넣을 필요 없이 별도로 파일에 저장한 후 가져와 사용할 수 있다. 다양한 데이터(html, css, txt, json 등)을 담아 연결해보는 것은 웹페이지 구성의 기본이 될것이다.
http/test.html (출처 : https://www.w3schools.com/html/html_examples.asp)
<!DOCTYPE html>
<html>
<body>
<h2>An Unordered HTML List</h2>
<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ul>
<h2>An Ordered HTML List</h2>
<ol>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ol>
</body>
</html>
http/test.js
const fs = require('fs');
// readFile 사용법 : fs.readFile([file_Dir],[options],[callback])
// readDir 사용법 : fs.readDir([file_Dir],[options],[callback])
fs.readFile('./test.html', (err, data) => {
if (err) throw err;
data...
});
fs.readDir('./test.html', (err, data) => {
if (err) throw err;
data...
});
파일을 가져왔으면 그 파일의 데이터를 이용하는 것이 저희의 목적입니다. readFile, readDir매서드를 통해 인자 data를 자유롭게 사용할 수 있다.
node의 모듈사용법은 웬만하면 검색해서 나오는 도큐먼트를 보는게 낫다. (이 뿐 아니라 공식문서는 언제나 큰 도움이 됩니다.)
readFile, readDir은 모두 fs모듈의 매서드임으로 node js모듈 공식문서 를 참조하는 방식으로 말이다.
express 모듈은 기존 http 모듈 서버구축 방식에 라우터, 미들웨어, http req,res의 객체화 등의 개념을 추가한 것으로 http 모듈과 마찬가지 REST-API의 get / post / put / delete 매서드를 모두 사용할 수 있다.
const express = require('express');
const app = express();
app.use();
app.get();
app.post();
app.put();
app.delete();
미들웨어란 중간(middle)에 거쳐가는 함수라는 뜻으로 http req, res 객체와 next 미들웨어에 대한 접근권한을 갖는 함수이다. (이벤트 리스너 혹은 콜백의 느낌이 듭니다.)
app.use((req, res, next) => {
req. ...
res. ...
next(...);
});
app.get((req, res, next) => {
req. ...
res. ...
next(...);
});
app.post('__dir', (req, res, next) => {
req. ...
res. ...
next(...);
}, function(req, res, next) {
req. ...
res. ...
next(...);
});
app.use는 호출 매서드(get, post, put, delete) 에 상관 없이 호출되어 미들웨어 함수가 항상 호출된다. 이를 이용하면,
const logger = require('morgan');
const express = require('express');
const app = express();
app.use(morgan([options]))
// options : 'dev', 'short', 'tiny', 'combined' ...
npm i morgan
로그를 찍어주는, 써드파티 미들웨어인 morgan을 설치해 주고 app.use를 통해 등록해 주면
다음과 같이 클라이언트에게 요청이 올 때마다 항상 로그가 찍히게 된다.
라우터 미들웨어 스택의 다른 미들웨어 함수(동일 라우터에 속한 다른 미들웨어 함수)를 건너뛰고 싶다면 next('route')를 호출해주면 된다.
app.use( middlewareFunc1 = (req, res, next) => {
...
next('route'); // 호출로 인해 하단 middlewareFunc2는 호출되지 않고 건너뛰어진다.
}, middlewareFunc2(req, res, next) => {
...
});
라우팅은 클라이언트의 다양한 요청의 처리를 위해 서버를 분기하는 작업으로 express 모듈을 도입하기 전엔 if ~ else if ~ else 등으로 처리하였으나, express 모듈의 적용으로
app.get('direction', (callbacks or middlewares));
다음과 같이 클라이언트의 요청 주소를 받을 수 있게 되었다.
아래와 같이 분기(라우팅)문을 보았을 때 어떤 기분이 드는가? 중복되는 부분이 불편하시지 않는가.
대게 코딩에서 같은내용의 코드가 반복해서 나오면 따로 정의를 내리기 마련인데, 그와 같이 중복되는 경로에 대해서도 정의를 내릴 수 있다.
app.get('./jyp/twice/TT', (callback1));
app.get('./jyp/rain/rainism', (callback2));
app.get('./sm/aespa/blackmamba', (callback3));
app.get('./sm/aespa/next-level', (callback4));
이는 express모듈의 Router 인스턴스의 생성으로 해결할 수 있다. router라는 일종의 미들웨어 파일을 만들고 이를 exports 해줄거다.
router.js
const express =require('epxress');
// Router 클래스 인스턴스 생성
const routerJyp = express.Router();
const routerSm = express.Router();
routerJyp.get('/twice/TT', (callback1));
routerJyp.get('/rain/rainism', (callback2));
routerSm.get('/blackmamba', (callback3));
routerSm.get('/next-level', (callback4));
module.exports = { routerJyp, routerSm };
app.js
const express = require('express');
const { routerJyp, routerSm } = require('./router');
const app = express();
// { routerJyp, routerSm }미들웨어의 적용
app.use('/jyp', routerJyp);
app.use('/sm/aespa', routerSm);
"routerJyp를 사용한 주소는 앞에 '/jyp'가 기본으로 붙는다" 라고 이해하면 된다.
express 모듈을 사용하게 되며 가장 크게 변한 것 중 하나는 역시 req, res의 객체화로 사용할 수 있는 매서드가 다양해졌다는 것이다. 살펴보면
const express = require('express');
const app = express();
app.get('/', (req, res, next) => {
// req객체의 매서드를 이용하여 클라이언트 요청(바디, 헤더, 파라미터, 쿼리 등등)에 접근할 수 있다.
const body = req.**body**;
const field1 = req.**params**.field1;
const id = req.**query**.id;
// res객체의 매서드를 이용하여 클라이언트에게 여러가지 프라퍼티를(헤더, 쿠키, 세션, 타입, 상태코드, 바디 등등등)응답을 할 수 있다.
res.status(200);
res.redirect([dir]);
res.send(anything);
res.sendFile(file dir);
res.json(json file);
res.end(anything);
res.cookie(..);
res.set(..);
});
결국 이를 통해 클라이언트 - 서버 간에 원활한 소통이 가능해졌다는 장점이 생겼다.
다음번엔 node js를 이용한 tdd를 살펴볼 예정~!