서버 기본 세팅

 

> 서버 스크립트 파일 생성 및 스크립트 작성

 

root에 서버를 세팅할 app.js 파일을 생성합니다.

생성 후 폴더구조는 아래와 같습니다.

// app.js

const express = require('express');
const http = require('http');
const app = express();
const server = http.createServer(app);
server.listen(8080, function(){  
	console.log('서버 실행중...');
});

 

코드 설명

 

> 세팅에 필요한 모듈들은 require로 가져와 변수에 담습니다.

http 모듈은 node.js의 기본내장 모듈이기 때문에 따로 다운로드하지않아도 사용할 수 있습니다.

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

 

 

> http 모듈은 서버 생성 메소드(createServer)를 제공하며 파라미터로 express를 넘겨줍니다.

이것을 통해 express로 서버를 생성합니다.

const server = http.createServer(app);

파라미터로 전달하여 실행하면 그만일텐데, 

server 변수에 담은 이유는 http.createServer() 메소드는 서버를 생성하는 작업을 하고 난 후 생성한 서버 객체를 리턴해줍니다.

그래서 생성된 서버를 제어하기 위해 server 변수에 담는 것입니다.

 

 

> 생성된 서버를 사용자가 웹에서 확인할 수 있게 합니다.

listen 메소드를 사용하여 port 설정을 합니다. (8080)

server.listen(8080, function(){
    console.log('서버 실행중...');
});

8080이 port 번호이고 listen메소드를 통해 8080에 서버가 실행되면 function이 실행됩니다.

이렇게 하면 localhost:8080로 웹에서 접속하여 당장 확인할 수 있을 것 같습니다.

 

 

> 서버 실행은 아래의 명령어로 하며 vscode 에디터의 터미널에서 서버 종료는 ctrl + c 입니다.

node app.js

 

 

But...

localhost:8080으로 접속하보면 'cannot Get/' 문구만 노출됩니다.

이유는 간단하게 말해서 localhost:8080/로 접근했을때의 행동을 정해줘야하기 때문입니다.

 

 

 

서버 라우터 설정해서 사용자에게 index.html 보여주기

 

라우터란?
쉽게 설명하면 클라이언트 요청에 따른 응답 을 해주는 장치와 같은 개념이라고 생각하면 됩니다. 

예시
사용자가 네이버에서 나의정보페이지 버튼을 눌렀다. (클라이언트 요청) 
나의정보페이지로 이동되었다.(서버 응답)

위와 같이 사용자가 이동하고자하는 페이지에 대한 요청을 서버가 보여주는, 응답을 도와주는 장치와 같은 개념을 말합니다.
라우팅은 라우터가 하는 일을 라우팅이라고 합니다.

 

localhost:8080/로 사용자가 접속했을때, express의 라우터 메소드 get을 통해 index.html을 보여주도록 설정하겠습니다.

 

 

 

> index.html 생성하기

 

src 폴더를 생성하여 하위로 index.html 파일을 생성합니다.

현재 폴더구조는 아래와 같습니다.

// index.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>chat-app</title>
</head>
<body>
    <div>welcome</div>
</body>
</html>

정상적으로 세팅이 된다면 'welcome'이라는 텍스트가 보여질 것입니다.

 

 

 

> 추가적인 서버세팅

 

index.html을 웹에 노출시키도록 하겠습니다.

// app.js

const express = require('express');
const http = require('http');
const app = express();
const server = http.createServer(app);
const fs = require('fs');

app.use(express.static('src'));

app.get('/', function(req, res){
    fs.readFile('./src/index.html', (err, data) => {
        if(err) throw err;
 
        res.writeHead(200, {
            'Content-Type' : 'text/html'
        })
        .write(data)
        .end();
    });
});

server.listen(8080, function(){
    console.log('서버 실행중...');
});

 

코드설명

 

> 사용자가 사이트에 접근할 때는 get과 post방식으로 접근합니다. (라우팅 메소드는 많습니다.)

get(URL로 접근) 방식으로 제어할 것이기 때문에 express의 get메소드를 사용하였습니다.

app.get('/', function(req, res){
    fs.readFile('./src/index.html', (err, data) => {
        if(err) throw err;

        res.writeHead(200, {
            'Content-Type' : 'text/html'
        })
        .write(data)
        .end();
    });
});

 

 

> app.get에 첫번째 파라미터 '/'는 사용자가 접근할 url의 경로값을 나타냅니다.

'/'은 localhost:8080/ 경로가 됩니다.

app.get('/',

 

 

> 두번째에는 '/'로 접속이 성공했을 경우 실행될 function입니다.

, function(req, res){
    fs.readFile('./src/index.html', (err, data) => {
        if(err) throw err;

        res.writeHead(200, {
            'Content-Type' : 'text/html'
        })
        .write(data)
        .end();
    });
});

 

 

> function에는 req, res의 인자값이 있는데,

req(request) 요청 오브젝트(클라이언트가 요청한 사항의 정보)가 전달되어오고, 

res(response)는 응답 오브젝트(서버에게 클라이언트로 전송하는 정보)가 전달되어 옵니다.

, function(req, res){

 

 

> 응답 오브젝트를 통해 여러 메소드들을 통해 다양한 작업을 할 수 있지만,

index.html의 파일을 읽어서 사용자가 볼 수 있게 하기 위해서

node.js의 기본 내장 모듈 fs(File System)을 사용하도록 하겠습니다.

기본 내장 모듈이기 때문에 따로 install할 필요 없이 바로 require합니다.

const fs = require('fs');

 

 

> fs모듈도 여러 메소드들을 제공합니다.

그 중에 파일 전체를 비동기로 읽어오는 readFile 메소드를 사용하겠습니다.

fs.readFile('./src/index.html', (err, data) => {
	if(err) throw err;

	res.writeHead(200, {
    	'Content-Type' : 'text/html'
    })
    .write(data)
    .end();
});

readFile메소드에 전달하는 파라미터로는 순서대로 '파일명, 옵션(인코딩 문자열), 콜백함수'입니다.

(옵션없이 바로 콜백함수를 작성해도 됩니다.)

예제에서는 src/index.html을 읽어오도록 파일명을 전달했습니다.

 

파일을 읽어오게되면 function이 실행됩니다.

function에는 err, data 두개의 인자가 있습니다.

err은 에러 시 전달되는 값, data는 파일내용이 읽어졌을 경우 전달되는 값입니다.

아래 부분은 에러처리 구간입니다. 

 

 

> 정상적으로 응답 데이터가 있을 경우 

http의 메서드인 writeHead와 write를 사용해 콘텐츠를 노출할것입니다.

writeHead 메소드는 응답 스트림에 헤더와 상태코드를 작성하는 것이고 

res.writeHead(200, {
    'Content-Type' : 'text/html'
})

 

 

이것이 완료되면

> 메소드체이닝에 의해 write가 실행되는데, write메서드는 응답바디를 작성하는 부분입니다. 

data를 파라미터로 넘겨서 작성합니다.

.write(data)

 

 

> write를 통해 응답을 전송하고나면 필수로 end 메서드를 통해 요청전송을 종료해줘야합니다.

.end();

 

 

> src 폴더 접근 권한 설정

 

마지막으로

src 폴더 하위로 index.html을 생성했기때문에,

express객체가 src 폴더에 접근할 수 있도록 설정해줘야합니다.

 

만약 이 작업을 하지 않으면 http://주소/src/index.html로 접근하려고 했을때 액세스가 거부됩니다.

정적 파일 설정은 선택이 아니라 필수입니다.

express의 use 메소드를 통해 정적파일 설정을 해줍니다.

파라미터에는 express.static 미들웨어 함수를 사용하여 경로를 설정하여 전달해줍니다.

app.use(express.static('src'));

 

 

현재 작성한 코드는 src 폴더 자체에 접근을 허용해주었는데,

이렇게하면 src하위에 있는 모든 파일, 폴더에 접근이 가능해집니다.

만약 src 폴더 자체에 보안에 민감한 내용이 있다면 세분화해서 정적파일을 사용하도록 하는 것이 좋다고합니다.

 

세분화를 예로 들자면 src 폴더 하위에 css 폴더를 만들어 css폴더를 정적파일로 설정하는 것을 말합니다.`

 

서버를 실행시켜 localhost:8080/에 접속하여 welcome 텍스트를 확인합니다.

node app.js

 

 

 

(3)회차는 socket.io를 통한 통신 스크립트 설정을 해보겠습니다.

 

 

 

 

들어가기에 앞서...

 

전체적인 가이드는 근둥이님 블로그를 참고했습니다.

https://codevkr.tistory.com/58?category=719250

 

쪼랩이라 예전에 채팅구현을 도전했다가 허접하게 마무리되었었는데,

근둥이님 블로그보니까 설명이 잘되어있어서 무리없이 진행했습니다.

 

제 글이 이해가 안된다면 근둥이님 블로그 글을 찾아가는것이 큰 도움이 될 것입니다.

 

(굽신-감사합니다.)

 

예전에 작업했을때 이해못했던 부분이, 역할 담당의 부분인데, 결론은 이거였습니다.

서버는 express 담당, 통신은 socket.io 담당한다.

 

express와 socket.io에 대해 간단히 설명하겠습니다. 

(자세한 사항은 정리를 어마무시하게 잘해두신 블로거님들 링크를 달아둘데니, 들어가서보셔도 되고, 검색해봐도 됩니다.)

 

Express.js

node.js에서 동작하는 프레임워크.

http와 connect 컴포넌트 기반으로 하며 서버를 쉽게 만들기위해 사용한다.

https://velog.io/@ground4ekd/nodejs-express

socket.io

실시간 웹 애플리케이션을 위한 라이브러리.

클라이언트 - 서버간의 양방향 통신이 가능하게 한다.

http://www.secmem.org/blog/2019/08/17/websocket-socketio/

 

 

express와 socket.io를 구글에 검색해보면 예제로 채팅만들기가 많이나옵니다.

 

 

사전작업

 

node.js 프로젝트 설정

 

> 프로젝트폴더 생성

프로젝트 폴더명을 'chat-app'으로 생성했고 vscode로 열어둔 상태입니다.

 

 

> 프로젝트폴더(chat-app)를 node 프로젝트로 설정

명령어로는 npm init 이며 옵션으로 -y (--yes)를 붙일 수 있습니다.

npm init -y / npm init --yes

-y, --yes는 npm init만 했을 시 나오는 질문들에 대해 기본 옵션을 사용하겠다는 뜻입니다.

// cmd
npm init -y

 

 

> express, socket.io 모듈 다운로드

express는 서버를 socket.io는 통신을 담당할 모듈입니다.

// cdm
npm install --save express socket.io

 

 

폴더구조는 아래와 같습니다.

 

 

(2)회차는 서버 셋팅을 진행해보도록 하겠습니다.

 

 

 

이전 포스팅 바로가기

 

이전에 포스팅에서 minify, merge를 진행해봤었는데요, 이번엔 자동화를 시켜보겠습니다.

 

 

gulp 실행후 minify, merge가 끝나고나서,

다시 원본파일을 수정하면 cmd에 gulp를 입력해서 실행해야하기때문에,

이 번거로움을 없애고자 자동화를 시켜보도록하겠습니다.

자동화는 gulp의 메소드 watch를 이용해서 실행합니다.

 

watch는 gulp의 메서드로 제공되고 있으며 내가 지정한 파일을 지켜보고 있다가 그 파일에 변화가 생기면

자동으로 설정된 행동을 진행해줍니다.

지난 코드에서 이어서 진행하겠습니다.

 

gulpfile.js

const gulp = require('gulp');
const concat = require('gulp-concat');
const uglify = require('gulp-uglify');

gulp.task('concat', function () {
    return gulp.src('src/js/*.js')
        .pipe(uglify())
        .pipe(concat('main.min.js'))
        .pipe(gulp.dest('dist'));
});

// watch 설정
gulp.task('watch', function(){
    gulp.watch('src/js/*.js', gulp.series('concat'));
});

gulp.task('default', gulp.series('concat', 'watch'));

 

1.

사용자가 gulp를 실행하면 default가 실행되고 gulp.series에 의해 concat과 watch가 순서대로 실행됩니다.

 

2.

concat이 먼저 실행되므로 gulp.task('concat')함수가 실행되고 minify와 merge가 실행됩니다.

 

3.

그 후 watch가 실행됩니다.

gulp.task('watch')함수가 실행됩니다.

gulp.watch() 메서드에서 첫번째 파라미터는 내가 지켜볼 파일의 경로이고

두번째 파라미터는 내가 지켜보는 파일에 변화가 생겼을 경우 실행하게될 함수입니다.

즉, src/js/*.js (src > js 폴더의 .js 확장자를 가진 모든 파일)에 변화가 생겼을 경우 gulp.task('concat')이 실행됩니다.

 

 

 

gulp 실행을 해보겠습니다.

cmd

gulp

 

Starting 'watch'라고 되어있고 Finished 'watch'가 없으면 watch가 '실행 중'인 상태입니다.

 

 

 

watch가 정상적으로 동작하는지 확인하기 위해, js파일을 수정해보도록 하겠습니다.

예제를 위해 name 값을 수정해보겠지만 어떠한 부분이 수정되더라도 상관없습니다.

src > js > index.js

var sayHelloFunc = require('./sayHello');
console.log(sayHelloFunc('J.K?????'));

 

 

수정을 하고 파일 저장을 마치면 cmd 창에 변화가 생깁니다.

cmd 상태

 

 

병합 및 압축이 완료되어 아웃풋으로 나온 main.min.js도 수정된 것을 확인할 수 있습니다.

dist > main.min.js

 

 

 

*가 적혀있는 단어에 대한 뜻은 최하단에 있습니다.

 

 

TypeScript를 공부해볼까하면서 찾아본 튜토리얼 사이트에서 gulp를 마주했습니다. 

 https://typescript-kr.github.io/pages/tutorials/Gulp.html 

 

그리고 튜토리얼 따라하는도중 에러가 뜨게되었는데,

gulp에 대한 지식이 1도 없었기 때문에 찾고 공부한 내용을 작성해둡니다.

 

에러사항

AssertionError [ERR_ASSERTION]: Task function must be specified 

검색하면 첫번째로 나오는 감사한 블로그에서 정보를 얻었습니다.

gulp의 버전이 4가 되면서 작성 규칙이 조금 바뀌었다고 합니다. 

https://forgiveall.tistory.com/521

 

task룰에 series | parallel 옵션이 추가되었다고 합니다.

series는 직렬방식*이고 parallel는 병렬방식*이라고 하네요.

 

작성할 때 아래와 같이 씁니다.

gulp.task('default', gulp.series('uglify', 'watch'));

gulp.task('default', gulp.parallel('uglify', 'watch'));

 

 


저는 gulp에 대해 1도 모르는 쪼랩이라서 예제를 따로 진행해보고나서야 이해가되었습니다.

 

기존 방식으로 작성

gulpfile.js 

const gulp = require('gulp');
const uglify = require('gulp-uglify'); 
const concat = require('gulp-concat');

const paths = {
    js : ['src/js/*.js']
}

// minify
gulp.task('uglify', function () {
    return gulp.src(paths.js)
          .pipe(concat('main.min.js')) // marge
          .pipe(uglify()) // minify
          .pipe(gulp.dest('dist')); 
});

// watch
gulp.task('watch', function(){
    gulp.watch(paths.js, ['uglify']);
});

gulp.task('default', ['uglify', 'watch']);


수정된 방식으로 작성

gulpfile.js

const gulp = require('gulp');
const uglify = require('gulp-uglify');
const concat = require('gulp-concat');

const paths = {
    js : ['src/js/*.js']
}

// minify
gulp.task('uglify', function () {
    return gulp.src(paths.js)
       	  .pipe(concat('main.min.js')) // merge
          .pipe(uglify()) // minify
          .pipe(gulp.dest('dist'));
});

// watch
gulp.task('watch', function(){
    gulp.watch(paths.js, gulp.series('uglify'));
});

//gulp를 실행하면 default 로 uglify task를 실행
gulp.task('default', gulp.parallel('uglify', 'watch'));

 

gulp 실행 시 gulp.task('default',)가 실행되고

gulp.parallel에 의해 병렬로 uglify와 watch가 실행됩니다.

 

watch가 실행될 때 gulp.watch(경로) 메서드에 의해서 (경로)의 파일에 변화가 있을 때

gulp.series('uglify')를 통해 직렬로 uglify가 실행됩니다.

 

 

 

직렬과 병렬로 실행하면 뭐가 달라? 라고 생각할 수 있기 때문에

실행 후 차이가 나는 부분을 보여드리겠습니다.

 

parallel 병렬로 실행

startExpress와 watch가 둘다 실행되는 것을 확인할 수 있습니다.

(startExpress 함수는 done을 시켜주지 않고 있습니다.)

 

series 직렬로 실행

startExpress만 실행되고 watch는 실행되지 않습니다.

series와 parallel에 따라서 원하던 조건대로 되지않는 경우가 생길수도 있으니 유의하고 써야합니다.

 

 

 

 


*

직렬방식 : 순차적으로 실행

병렬방식 : 나란히 실행

 

참고

 

+ Recent posts