본문 바로가기
개발이야기/server

Express, Routing

by 효우너 2020. 5. 23.
728x90
반응형

※이 포스팅은 sopt 26기 server part seminar 자료 바탕으로 작성되었으며, 복습용입니다.

1-1. NodeJS

node.js

Express와 Routing을 설명하기 전, 알아둬야 할 부분이 있다.

바로 NodeJs인데, 이전까지 나는 프레임워크라고 잘못 알고 있었다...!_!,,,,

NodeJS란

-> JS 기반 서버 플랫폼

-> 이벤트 기반, 싱글 스레드 기반

-> non-blocking I/O

-> 비동기 방식

-> 프로그램 언어 (X), 프레임워크 (X)

-----> JS를 실행시키는 런타임 환경이다.

 


1-2. Express

express

그리고 Express가 바로 NodeJS기반의 웹 앱 프레임워크다.

서버를 구축하기 쉽게 틀을 제공한다.

HTTP 요청에 대해 라우팅 및 미들웨어 기능을 제공한다.


1-3. EVENT-DRIVEN

EVENT-DRIVEN은 이벤트가 발생할 때 미리 지정해둔 작업을 수행하는 방식이다.

"'어떤 일'이 발생하면 '이런 동작'수행"을 지정해두는 방식이다.

(EVENT-DRIVEN에 관한 포스팅은 따로 올려두도록 하겠습니당,,!)


1-4. 싱글 스레드

NodeJS를 설명하면서 언급했던 싱글 스레드는 하나의 스레드를 갖는 프로세스다.

싱글 스레드의 경우 첫번째 작업이 끝난 후, 두번째 작업을 시작한다.

싱글스레드의 반대되는 개념으로는 멀티스레드가 있다.

멀티스레드는  하나 이상의 스레드를 갖는 프로세스다.

싱글 스레드와의 다른 작업방식으로 진행하는데 바로

두 개의 스레드가 두개의 작업을 짧은 시간동안 번갈아가며 수행하기 때문에 두개의 작업이 동시에 처리되는 것으로 보인다.

실제로 싱글 스레드와 멀티스레드의 작업시간에는 큰 차이가 없지만 멀티스레드는 context-switching이 이루어져 시간이 더 걸린다.


1-5. blocking / non-blocking

blockingI/O 작업이 진행되는 동안 작업이 중단된 채 대기한다.

-> 여기서 동기(SYNCHRONOUS)라는 단어를 자주 볼 수 있는데

요청을 하고 완료할 때까지 기다리는 방식이며 백그라운드 작업 완료 여부를 계속 확인한다.

non-blockingI/O 작업이 진행되는 동안 작업이 중단되지 않고 다음 작업을 수행한다.

->여기서는 비동기(ASYNCHRONOUS)라는 단어를 자주 볼 수 있는데

요청을 하고 바로 제어권을 돌려받는 방식이다. 요청만 하고 다시 프로그램을 처리하다가 완료 이벤트가 발생하면 미리 지정한 처리를 진행한다.

NodeJS에서는 대부분 비동기로 실행하며 어떤 작업이 먼저 끝날지 모른다.

순차적인 작업을 위해 콜백 함수 내에 다시 콜백 함수를 부르면 결국 CALLBACK HELL이 된다.

다른 방법으로는 Promise와 Async가 있는데 지금 바로 알아볼 것이다..!


2-1. Promise

Promise는 ES6부터 공식적으로 포함된 흐름 제어 패턴이다.

그리고 앞으로 알게 될 async와 await도 Promise 기반이다.

내부적 예외처리 구조를 보여준다.

Promise에는 3가지의 상태가 있다.

1. pending -> 최초 생성된 시점의 상태

: new Promise() 메서드를 호출할 때, 콜백 함수를 선언할 수 있다. 동작에 대한 결과를 올바르게 줄 수 있으면 resolve, 동작을 실패하면 reject 함수를 호출한다.

2. fulfilled -> 작업이 성공적으로 완료된 상태

: 콜백 함수 인자 resolve를 실행하면 이행된 상태다.

3. rejected -> 작업이 실패한 상태

: reject를 호출하면 실패 상태다.

Promise 관련하여 Promise Chaining이라는 것이 있다.

여러 개의 프로미스를 연결하여 사용한다.

여기서는 then API와 catch API를 사용한다.

1. then API -> <Promise>.then() 형식이며 비동기 작업 완료 시 결과에 따라 함수를 호출한다.

2. catch API -> <Promise>.catch() 형식이며 chaining형태로 연결된 상태에서 비동기 작업이 중간에 에러가 났을 때 호출한다. 

((resolved, rejected) => {
        setTimeout (() => {
                console.log('func3 return resolved');
                resolved(`func 3 success: ${param}\n`);
            }, 500);
    });
}

const func4 = (param) => {
    return new Promise((resolved, rejected) => {
        setTimeout(() => {
                console.log('func4 return rejected');
                rejected(Error(`func 4 error: ${param}\n`));
            }, 500);
    });
}

const func5 = (param) => {
    return new Promise((resolved, rejected) => {
        setTimeout(() => {
                console.log('func5 return resolved');
                resolved(`func 5 success: ${param}\n`);
            }, 500);
    });
}

const promise = func1('sopt')

    .then(func2)
    .then(func3)
    .then(func4)
    .catch(console.error)
    .then(func5)
    .catch(console.error)
    .then(console.log);

이렇게 코드를 구성했다고 했을 때, 콘솔에 어떻게 출력되는지 보면

func1 return resolved
func2 return rejected
Error: func2 param: 'func 1 success: sopt'
    at Timeout.setTimeout [as _onTimeout] (c:~생략~\promise.js:14:26) at ontimeout (timers.js:436:11)
    at tryOnTimeout (timers.js:300:5)
    at listOnTimeout (timers.js:263:5)
    at Timer.processTimers (timers.js:223:10)
func5 return resolved
func 5 success: undefined

라고 나온다.

앞에서 설명했던 resolve와 reject가 실행된 걸 볼 수 있다.


2-2. Async

Async는 ES7부터 지원하는 자바스크립트 비동기 패턴이다.

장황한 Promise 코드를 한번 더 깔끔하게 줄여주고, 동기 코드와 매우 유사하다.

기존의 비동기 처리 방식인 콜백 함수와 Promise의 단점을 보완하여 읽기 좋은 코드로 만들어 준다고 한다.

AsyncPromise를 사용하지 않고도 callback hell을 해결할 수 있다. 그리고 Promise를 암묵적으로 반환한다.

AwaitPromise가 성공하는 실패 하는지 기다리며 Async로 정의된 내부에서만 사용 가능하다.

Promise와 Async/Await 중 사용하기 편한 걸로 선택해서 사용하면 된다..!

let asyncFunc1 = (msg) =>
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(`func1 : ${msg}`)
        }, 1000)
    })

let asyncFunc2 = (msg) => 
    new Promise((resolve) => {
        setTimeout(() => {
            resolve(`func2 : ${msg}`)
        }, 1000)
    })

function promiseMain() {
    asyncFunc1('Hello').then((result)=> {
        console.log(result)
        return asyncFunc2('world')
    }).then((result) => {
        console.log(result)
    })
}

async function asyncMain() {
    let result = await asyncFunc1('Hello')
    console.log(result)
    result = await asyncFunc2('world')
    console.log(result)
}
/*await을 빼면 pending 상태로 출력*/

promiseMain()
asyncMain()

/*출력은 promiseMain()와 asyncMain()가 똑같이 시간차를 두고 
func1 : Hello
func2 : world
로 출력됨 
만약 promiseMain()와 asyncMain()를 동시에 실행하면
func1 : Hello
func1 : Hello
func2 : world
func2 : world
로 출력됨*/

2-3. Module

모듈은

1. 독립된 기능을 하는 함수나 변수들의 집합이다.

2. 모듈 자체가 하나의 프로그램이면서 다른 프로그램의 부품으로 사용할 수 있다.

3. 재사용에 용이하다.

1 파일 = 1 모듈이라고 생각하면 된다.

Client-side의 Javascript에서 문제가 되는 전역 변수의 중복 문제가 해결된다고 한다.

모듈의 마지막에는 module.export()를 실행해주면 되는데 () 안에는 함수 또는 객체가 들어갈 수 있다.

//함수버전
// function sum(a,b){
//     return a+b;
// }

// module.exports = sum;

//객체버전
module.exports = {
    sum : (a,b) => {
        return a+b;
    },
}

2-4. 내장 모듈 소개

노드에서 기본적으로 제공해주는 모듈들이 있다.

그중 첫 번째는 crypto이다.

회원가입, 로그인 구현 시 자주 쓰이는 암호화 기법에 crypto가 쓰인다.

crypto는 문자열을 암호화, 복호화, 해싱하는 모듈이다.

단방향 암호화 방식으로 복호화할 수 없는 암호화 방식으로 비밀번호 암호화에 주로 사용하며 주로 *해시 기법을 이용한다.

*Hash : 어떤 문자열을 고정된 길이의 다른 문자열로 바꿔버리는 방식

salt암호화 중 해싱을 할 때 추가되는 임의의 문자열이며 데이터베이스에 salt와 password를 같이 저장해주어야 한다.

비밀번호를 DB에 저장할 때 Key stretching을 사용한다.

Key stretching이란 반복적으로 해싱하는 암호화 기법이다. 해싱된 암호를 다시 입력값으로 넣어 n번 동안 해싱한다.

똑같이 n번을 해싱해야만 *Digest를 찾을 수 있다. 1초에 5회 정도 적당하다고 한다.

* Digest: password + salt 

const crypto = require('crypto');

const encrypt = (salt, password) => {
    crypto.pbkdf2(password, salt.toString(), 1, 32, 'sha512' , (err, deriveKey) => {
        if (err) throw err;
        const hashed = deriveKey.toString('hex');
        console.log('salt : ', salt);
        console.log('hashed : ', hashed);
    });
}

const password = 'fl0wer';
const salt = crypto.randomBytes(32).toString('hex');
encrypt(salt, password);
출력 값은 
salt : f5e56200d8d649c22f5d7eeaf53330e3a53c85c8d0d693d320153418681ade3b hashed : bb529263f7fe5059280443edf51ed54b84a3af30112579c34ef96fda8d26a488
이며 여기서 
crypto.pbkdf2(password, salt.toString(), 1, 32, 'sha512' , (err, deriveKey) 은
pbkdf2 (비밀번호, 솔트 값, 반복 횟수, 출력 byte, 해시 알고리즘, callback)를 의미한다.

2-5. File system module

파일 시스템에 접근하는 모듈파일 생성, 삭제, 읽기, 쓰기 등을 수행 또는 폴더 생성, 삭제를 한다.

동기 방식과 비동기 방식으로 나뉘는데 실습한 코드로 바로 살펴보자..!

//동기방식
const fs = require('fs');

const numArr = [1,2,3,4,5];

numArr.forEach((num) => {
    const title = 'sync' + num;
    const data = `파일이 잘 만들어 졌어요!\n제 이름은 '${title}.txt'입니다 ~ `;
    fs.writeFileSync(`${title}.txt`, data);
    console.log(`${title} 동기라 순서에 맞게 ~.~`);
});

numArr.forEach((num) => {
    const title = 'sync' + num;
    const data = fs.readFileSync(`${title}.txt`);
    console.log(`${title}.txt 파일에는 아래의 데이터가 있습니다. \n"${data}"\n`);
})
//출력
sync1 동기라 순서에 맞게 ~.~
sync2 동기라 순서에 맞게 ~.~
sync3 동기라 순서에 맞게 ~.~
sync4 동기라 순서에 맞게 ~.~
sync5 동기라 순서에 맞게 ~.~
sync1.txt 파일에는 아래의 데이터가 있습니다.
"파일이 잘 만들어 졌어요!
제 이름은 'sync1.txt'입니다 ~ "

sync2.txt 파일에는 아래의 데이터가 있습니다.
"파일이 잘 만들어 졌어요!
제 이름은 'sync2.txt'입니다 ~ "

sync3.txt 파일에는 아래의 데이터가 있습니다.
"파일이 잘 만들어 졌어요!
제 이름은 'sync3.txt'입니다 ~ "

sync4.txt 파일에는 아래의 데이터가 있습니다.
"파일이 잘 만들어 졌어요!
제 이름은 'sync4.txt'입니다 ~ "

sync5.txt 파일에는 아래의 데이터가 있습니다.
"파일이 잘 만들어 졌어요!
제 이름은 'sync5.txt'입니다 ~ "

파일 만들어짐

파일이 만들어졌고 sync1.txt를 열어보면

'파일이 잘 만들어 졌어요!

제 이름은 'sync1.txt'입니다 ~ '

를 확인할 수 있다.

//비동기방식
const fs = require('fs');

const numArr = [1,2,3,4,5];

numArr.forEach((num) => {
    const title = 'async' + num;
    const data = `파일이 잘 만들어 졌어요!\n제 이름은 '${title}.txt'입니다 ~ `;
    fs.writeFile(`${title}.txt`, data, (err,data) => {
        if (err) return console.log(err.messae);
        console.log(`${title} 비동기라 순서가 뒤죽박죽 ~.~`);
    });
});

numArr.forEach((num) => {
    const title = 'async' + num;
    fs.readFile(`${title}.txt`, (err, data) => {
        if (err) return console.log (err.message);
        console.log(`${title}.txt 파일에는 아래의 데이터가 있습니다. \n"${data}"\n`);
    });
});
//출력
async2 비동기라 순서가 뒤죽박죽 ~.~
async1 비동기라 순서가 뒤죽박죽 ~.~
async4 비동기라 순서가 뒤죽박죽 ~.~
async3 비동기라 순서가 뒤죽박죽 ~.~
async5 비동기라 순서가 뒤죽박죽 ~.~
async1.txt 파일에는 아래의 데이터가 있습니다.
"파일이 잘 만들어 졌어요!
제 이름은 'async1.txt'입니다 ~ "

async3.txt 파일에는 아래의 데이터가 있습니다.
"파일이 잘 만들어 졌어요!
제 이름은 'async3.txt'입니다 ~ "

async2.txt 파일에는 아래의 데이터가 있습니다.
"파일이 잘 만들어 졌어요!
제 이름은 'async2.txt'입니다 ~ "

async4.txt 파일에는 아래의 데이터가 있습니다.
"파일이 잘 만들어 졌어요!
제 이름은 'async4.txt'입니다 ~ "

async5.txt 파일에는 아래의 데이터가 있습니다.
"파일이 잘 만들어 졌어요!
제 이름은 'async5.txt'입니다 ~ "

파일 만들어짐

파일이 만들어졌고 async2.txt를 열어보면

'파일이 잘 만들어 졌어요!

제 이름은 'async2.txt'입니다 ~ '

를 확인할 수 있다.


3. Routing

드디어 이번 포스팅의 마지막 개념이다.

RoutingURI(또는 경로) 및 특정한 HTTP 요청 메서드 (GET, POST 등)인

특정 엔드포인트에 대한 클라이언트 요청에 애플리케이션이 응답하는 방법을 결정한다.

Express에서는 app.js에서 모든 경로 요청을 처리하지 않고, routes/index.js를 기준으로 파일들을 추가하며 정리하는 것이 깔끔하다..!

이전까지 app.js에 중구난방 하게 경로 요청 처리했던 점 반성,,,,,,,,,,,,,,


여기까지 2주 차 세미나 복습을 마무리짓고, 이번 포스팅도 마무리 지을 것이다!!

복습하면서 개인적인 생각도 넣었는데 혹시나 고쳐야 할 개념이 있다면 언제든지 말씀해주세요..!

아직 잘 모르는 부분이 많아요,,,,,,,,(머쓱)

728x90
반응형

'개발이야기 > server' 카테고리의 다른 글

REST API, API 문서  (0) 2020.08.24
DB, JWT, 프로젝트 구조  (0) 2020.08.24
Database, RDS  (0) 2020.08.24
Javascript  (0) 2020.05.10
client와 server  (0) 2020.05.10

댓글