Node.js란
Node.js는 자바스크립트를 스크립트 언어 이상으로 프로그래밍 언어 역할을 할 수 있도록 지원하는 플랫폼입니다. 다시말해 언어가 아니고 Node.js를 이용해서 자바스크립트로 백엔드를 구현할 수 있습니다.
Node.js특징
음식점에 요리사가 한명일때로 비유
- 싱글 스레드 : 주문이 계속 밀려들어와도, 한명이 어떻게든 해야한다.
- 이벤트 기반 : 주문이 들어와야만 일을 한다.(주문이 없으면 일을 안함)
- 논블로킹 I/O : 한명이 일을 하는데, 요리를 순차적으로 x, 중간중간 비는 시간이 있으면, 다른 요리를 한다.
HTTP 프로토콜
- HTTP(HyperText Transfer Protocol) : 직역하면 파이퍼텍스트 전달 프로토콜이다. 하이퍼텍스트(Hyper Text)는 인터넷 사용자가 필요한 정보의 자유로운 검색을 가능하도록 해주는 텍스트의 전개방식이다. HTTP는 이러한 하이퍼텍스트 방식의 정보를 교환하기 위한 하나의 규칙이다.
HTTP통신을 사용하면 클라이언트와 서버간에 두가지 유형의 메시지(HTTP요청 및 HTTP응답)을 전송할 수 있다. 클라이언트/브라우저가 요청을 서버로 보내고 서버가 브라우저로 응답을 보낸다. 두 메시지는 공통 포맷을 가지고 있고, HTTP헤더와 HTTP본문을 포함한다.
HTTP 메시지
HTTP 메시지는 클라이언트와 서버 사이에서 데이터가 교환되는 방식이다.
HTTP메시지는 텍스트 정보를 구성하며 구성파일, API, 기타 인터페이스에서 HTTP 메시지를 자동으로 완성한다.
HTTP메시지에는 요청(Request)와 응답(Response) 두 가지 유형이 있다.
- Start line : 요청의 상태를 나타내며, 항상 첫번째 줄에 위치(수행할작업 get, put, post등의 http메서드)
- Status line : 응답의 상태를 나타내며, 항상 첫번째 줄에 위치(요청의결과, 200, 400, 500 등)
- Http headers : 요청을 지정하거나, 메시지에 포함된 본문을 설명하는 헤더의 집합
- empty line : 헤더와 본문을 구분하는 빈줄
- body : 요청과 관련된 데이터나 응답과 관련된 데이터 또는 문서를 포함, 요청과 응답의 유형에 따라 선택적으로 사용
start line과 HTTP headers를 묶어 요청의 헤드(head)
status line과 HTTP headers를 묶어 응답의 헤드(head)라 한다.
요청과 응답 서버
server.js파일을 만들고, 그 안에 아래와 같이 써줍니다.
let http = require('http');
function onRequest(request,response){
response.writeHead(200,{'Content-Type':'text/html'});
response.write('Hello Node.js');
response.end();
}
http.createServer(onRequest).listen(8888);
Node.js는 모듈 시스템을 구축하고 있습니다.
모듈이란, 필요한 것만 불러오는 것이라고 생각하면 됩니다. http 서버가 필요하니 http모듈을 require메소드를 통해서 불러와 http변수에 저장하였습니다. 서버가 실행된 후의 동작을 콜백함수로 등록합니다. http패키지의 createServer 메소드의 인자로 넣어주면 됩니다. 그리고 8080포트에 연결(listen)합니다.
그리고 node server.js를 터미널에 입력해 8080포트로 접속해보면 아래와 같이 정상적으로 작동됩니다.
위에서 createServer 메소드 콜백의 매개변수인 request와 response에 대해 알아보겠습니다.
간단하게 말해서 request는 요청을 담당하고, response는 클라이언트(브라우저)로 돌려줄 응답을 담당합니다.
- request : 서버로 보내는 요청에 대한 정보가 들어있습니다. 방금 우리가 주소창에 localhost:8080이라고 친 행위도 서버에 그 주소에 해당하는 정보를 달라고 요청한 겁니다. 서버는 항상 대기중이다가 request가 들어올 때 반응하면 됩니다. 그리고 request에 대한 처리를 한 후 response 객체로 돌려줍니다.
- resonse : 어떤 정보를 보내고 싶다면 response객체를 활용하면 됩니다.
request -> 서버처리 -> response로 흐름이 이어집니다.
이제 요청,응답을 알았으니 위의 코드를 좀 자세히 살펴보겠습니다. 먼저 헤더를 설정해보면
- response.writeHead(상태코드, 헤더정보) : 응답 헤더에 대한 정보를 기록합니다. 상태코드는 http상태코드를 말하는데 200,404,500 등이 있습니다. 'text/html'은 응답의 콘텐츠 형식이 HTML이라는 의미입니다.
헤더 정보에는 아래와 같은 정보가 들어갈 수 있습니다.
Content-Type : response body의 MIME을 특정합니다.
Content-Length : response body의 길이를 특정합니다.(bytes)
Cache-Control : 클라이언트가 response를 어떻게 캐싱할지 특정합니다.
Location : 클라이언트가 리디릭션하기 위해 지정해야하는 url을 특정하기 위해 사용됩니다.
Set-Cookie : 클라이언트의 브라우저에서 쿠키를 설정하기 위해 사용됩니다.
Access-Control-Allow-Origin : This header specifies the allowed origins for cross-origin resource sharing (CORS)
헤더를 설정하면 다음과 같은 이점을 얻을 수 있다고 합니다.
사용자경험개선, 성능향상, SET 개선, 보안성향상
head를 설정했으니 이제 body를 설정해보겠습니다.
- response.write() : 본문(body)에 보여지는 부분을 쓰는 메서드입니다.
- response.end() : 응답을 종료하는 메서드입니다.
- .listen(포트번호,콜백함수) : 서버와 연결할 포트번호를 지정하고, 포트연결 완료 후 실행된 콜백함수를 입력합니다.
Node.js가 미리 만들어둔 모듈을 require('모듈 이름'); 이렇게 사용한 것 처럼, 내가 만든 server도 모듈처럼 다른 javascript파일에서 사용할 수 있습니다. 위에서 만든 server.js를 모듈화 시켜 다른 js에서도 접근할 수 있게 해보겠습니다.
모듈이란?
Node.js으로 개발하기 위해서는 모듈 시스템을 사용합니다. 이때, 모듈이란 ' 미리 만들어둔 함수들의 집합'이라고 할 수 있습니다. Node.js에서의 모듈은 2가지(코어모듈, 파일모듈)로 분류할 수 있습니다.
- 코어모듈(Core Module) : 코어 모듈은 Node.js에서 기본적으로 제공하는 모듈입니다. http, fs등의 모듈이 기본적으로 제공되는 코어 모듈에 속합니다.
- 파일모듈(File Module, Local Module) : 코어 모듈을 제외한 모듈을 파일모듈이라고 부릅니다. 직접 모듈을 생성하는 경우도 당연히 파일 모듈에 속합니다. 모듈을 직접 생성하기 위해서는 exports객체를 사용하고, 모듈을 불러오기 위해서는 require()함수를 사용합니다.
위의 server.js를 파일모듈로 만들어보겠습니다. 단순히 코드를 하나의 함수로 묶고 exports해주면 끝입니다.
let http = require('http');
function start(){
function onRequest(request,response){
response.writeHead(200,{'Content-Type':'text/html'});
response.write('Hello Node.js');
response.end();
}
http.createServer(onRequest).listen(8888);
}
exports.start=start;
그리고 모듈을 불러오는 index.js파일을 하나 만들어 아래와 같이 코드를 작성하고
터미널에서 node index.js를 입력하면
let server = require('./server');
server.start();
정상적으로 결과가 나옵니다.
라우터
라우터는 한마디로 설명하면 '연결 장치' 입니다.
즉, server.js 한 파일에서만 런칭할 수 있는 것이 아닌 router라는 것을 이용해서 여러가지 .js파일에다가 원하는 코드를 짤 수 있다는 겁니다.
이 router를 사용하는 가장 큰 이뉴는 코드의 간략화 + 가독성 때문입니다. server.js 하나에 다 코드를 넣으면 길어지고 가독성이 떨어지게 됩니다.
- Server : Request를 받습니다.
- Router : Request의 URL에 따라 루트(Route)를 정해줍니다. 즉 어디로 갈지 길만 정해줍니다.
router.js파일을 하나 만들고 아래의 코드를 작성해줍니다.
route함수는 pathname을 인수로 받아 터미널에 출력해줍니다. pathname은 어디서 줘야할까요? server.js입니다.
function route(pathname){
console.log('pathname : '+pathname);
}
exports.route = route;
server.js에서 pathname이라는 변수를 하나 선언하고 parse함수를 사용해 url을 저장하고 route에 전달합니다.
근데 server.js에서는 route가 어디서 오는지 알 수가 없습니다. 따라서 index.js에서 설정해줍니다.
let http = require('http');
let url = require('url'); //우리가 접속하는 url주소( localhost:8888/여기문자열 )
function start(route){
function onRequest(request,response){
let pathname = url.parse(request.url).pathname; //변수에담아
route(pathname); // route에 전달
response.writeHead(200,{'Content-Type':'text/html'});
response.write('Hello Node.js');
response.end();
}
http.createServer(onRequest).listen(8888);
}
exports.start=start;
index.js에서 아래와같이 작성해줍니다.
require메소드를 이용해 router파일을 가져오고 server를 start해줄때 인수로 router의 route함수를 전달해주면 됩니다.
let server = require('./server');
let router = require('./router');
server.start(router.route);
이제 서버를 실행시키고 http://localhost:8888/hello 로 접속해보면
vscode의 터미널에서 아래와 같이 정상적으로 출력됩니다.
Request Handler
Request Handler를 이용해 path에 따라 핸들러 내 다른 함수를 호출할 수 있습니다.
requestHandler.js파일을 만들어 아래와 같이 작성합니다.
handle이라는 Object(key:value)를 만들어 경로마다 실행되는 함수를 처리해줍니다.
function main(){
console.log('main');
}
function login(){
console.log('login');
}
//라우터가 경로를 분배해서 알려주면 이제 뭘 해야할지 알려주는
let handle = {}; // key : value로 이루어진 변수
handle['/'] = main; // '/'로 가면 main()함수 를 실행해야하고
handle['/login'] = login; // '/login'로 가면 login() 함수를 실행해야한다. 즉, 경로에 따라서 다른 함수 실행
exports.handle = handle;
그리고 index.js에서 server.js를 호출할때 파라미터로 handle도 같이 전달해줍니다.
let server = require('./server');
let router = require('./router');
let requestHandler = require('./requestHandler');
server.start(router.route, requestHandler.handle);
서버에서도 방금 전달받은 handle을 이용해 마찬가지로 route를 호출할때 handle을 전달해줍니다.
let http = require('http');
let url = require('url');
function start(route, handle) {
function onRequest(request, response) {
if (!request.url.includes("favicon.ico")) {
let pathname = url.parse(request.url).pathname;
route(pathname, handle);
response.writeHead(200, { 'Content-Type': 'text/html' });
response.write('Hello Node.js');
response.end();
}
}
http.createServer(onRequest).listen(8888);
}
exports.start = start;
마지막으로 router에서도 handle을 받아 pathname을 이용해 전달해줍니다.
function route(pathname, handle){
console.log('pathname : '+pathname);
if(typeof handle[pathname]==='function'){
handle[pathname]();
}else{
console.log('NO');
}
}
exports.route = route;
접속해보면 main페이지 '/'일때입니다.
'login'일때 입니다.
Url에 따라 다른 response보내기
이제 url마다 다른 response를 보내주겠습니다.
기존에는 server.js에서 아래의 response.writeHead, write등을 사용해서 메인에 response를 보내줬습니다.
response.writeHead(200, { 'Content-Type': 'text/html' });
response.write('Login Page');
response.end();
위의 코드를 requestHandler.js의 각 url이 실행되었을때의 함수에 넣고 원하는 response를 작성해주면됩니다.
그전에 requestHandler에 response를 인수로 전달해줘야합니다.
server.js에서 route를 호출할때 response를 같이 넘겨줍니다.
route(pathname, handle, response);
그럼 router.js에서는 handle을 받고 handle을 호출할때 같이 response를 넘겨줍니다.
function route(pathname, handle, response){
console.log('pathname : '+pathname);
if(typeof handle[pathname]==='function'){
handle[pathname](response);
}else{
console.log('NO');
}
}
exports.route = route;
이제 requestHandler.js의 각함수에서 response를 받아 작성해주면 끝입니다.
function main(response){
console.log('main');
response.writeHead(200, { 'Content-Type': 'text/html' });
response.write('Main Page');
response.end();
}
function login(response){
console.log('login');
response.writeHead(200, { 'Content-Type': 'text/html' });
response.write('Login Page');
response.end();
}
결과입니다.
메인페이지입니다.
로그인페이지입니다.
즉, 유저가 각 url마다 다른 정보를 요청할 수 있습니다.
그래서 프론트에서는 해당 url을 가지고 해당 페이제에서만 원하는 정보를 요청해 데이터를 활용할 수 있습니다.
예외처리
현재는 main, login 2개의 주소를 설정해주었는데 유저가 설정하지 않은 url(주소)로 접속할 경우의 페이지를 만들어주겠습니다.
router.js에서 아래와 같이 작성해주면 됩니다.
handle[pathname]이 작성한 function에 없으면 Not Found를 출력합니다.
function route(pathname, handle, response){
console.log('pathname : '+pathname);
if(typeof handle[pathname]==='function'){
handle[pathname](response);
}else{
response.writeHead(404, { 'Content-Type': 'text/html'});
response.write('Not Found');
response.end();
}
}
exports.route = route;
결과입니다.
'백엔드 > node.js(express)' 카테고리의 다른 글
Expess Post(Postman, app.use()함수) (0) | 2023.12.11 |
---|---|
Express 구조 이해하기(express,generator) (0) | 2023.12.11 |
req.params연습(쿼리스트링, 객체, Map객체) (1) | 2023.12.07 |
Express 설치 및 REST API 실습(GET,동적url) (0) | 2023.12.06 |
node.js에 db연동 (0) | 2023.11.24 |