728x90
📍 27일차 12.1. 수(온라인 강의)
오늘은 2021년 마지막 해의 첫 날이다. 그날 배운 내용들을 복습하는 글도 27번째 쓰고 있는데, 22년 2월까지 하루도 빠짐없이 복습하는 글을 작성했으면 좋겠다. 현업에 뛰어들어서 볼 지금의 글들이 밤톨이나마 도움이 된다면 나의 목표는 달성한 것이다. 강의에서는 어제 실시간 강의로 간략하게 배웠던 node.js, express.js, module 등에 대해서 조금 자세하게 배웠다.
❏ node.js의 등장 배경
- 웹의 발전에 의해 등장함. 단방향 통신 위주였던
WEB1.0에서 사용자와 상호작용하는WEB2.0으로 발전하게 되면서 웹페이지의 동작은 더욱 복잡해졌고, 복잡한JS를 실행하기 위해 고성능의JS실행기가 필요해졌다. 크롬에서는 웹브라우저를 위한V8엔진을 만들게 됨V8엔진으로 인해JS속도가 상당히 빨라지게 됐고,V8엔진을 이용해서 웹 브라우저에서만 사용하는 것이 아닌 어느 환경에서나 동작시킬 수 있도록 만들어진 것이node.js이다. 즉,js를 어느 환경에서나 실행할 수 있게 해주는 실행기라고 할 수 있다. - 브라우저에서의
JS: 브라우저에서 실행, 웹 내부 제한된 동작, 웹 프런트 개발자의 언어 node.js: 크로스 플랫폼 실행, 제한 없는 동작, 다양한 어플리케이션 개발, 모든 개발자의 언어가 되었다.FE: React.js,BE: Express.js,(최근 가장 인기 있는 웹서비스 구성),Mobile: React-native,(한가지 코드로 IOS와 Android 개발),Desktop-app: Electron(discord, slack 등 앱 개발),Machine-Learning: Brain.js (JS로 구현하는 딥러닝)

❏ node.js의 특징
- 싱글 스레드 - 비동기 - 이벤트 기반
- 스레드: 명령을 실행하는 단위, 싱글스레드는 한 번에 한 가지 동작만 실행 가능, 멀티스레드는 동시에 여러 동작 수행 가능하다.
- 장점: 스레드가 늘어나지 않기 때문에 리소스 관리에 효율적
- 단점: 스레드 기반의 작업들의 효율이 떨어짐(CPU 연산 작업)
- 그래서
node.js는 비동기 동작으로 스레드 기반의 작업을 최소화한다. - 비동기: 동작을 실행한 후 완료가 되길 기다리지 않는 방식, 동작의 완료를 기다리지 않기 때문에 다른 동작을 바로 실행 가능,
node.js는 싱글 스레드이기 때문에 비동기 방식을 사용함. - 멀티스레드는 한번에 여러 가지 동작을 수행하기 때문에 동작을 수행하고 완료를 기다리는 동안
CPU리소스가 낭비되는 반면, 싱글 스레드는 한 번에 여러 가지 동작을 수행할 수 없지만 동작의 완료를 기다리지 않기 때문에CPU리소스를 효율적으로 사용할 수 있다. - 이벤트 기반: 비동기 동작의 완료를 처리하는 방법. 비동기 방식은 특정 동작을 실행한 후, 해당 동작을 전혀 신경 쓰지 않음. 대신 해당 동작이 완료될 경우 실행할 함수를 미리 등록함. 비동기 동작이 완료되면 미리 등록된 함수를 실행한다.
node.js는 싱글 스레드기 때문에 비동기 동작이 필요하고 비동기 동작을 구현하기 위해 이벤트 기반의 동작 방식을 사용한다.

❏ node.js 시작하기
node.js는 빠르게 개발 중이므로 보안 이슈 및 버그 수정, 최신기술을 빠르게 적용한다. 급변하는 기술은 가장 안정적인 최신 버전을 선택하는 것이 최선이다.LTS(Long-term support):node.js의 안정적이고, 오래 지원하는 버전 명- 가장 최신의 기술보다 안정적이고 최신의 버전을 선택하자.
❏ ES6
ECMA6버전 이후를 통틀어 일반적으로ES6라고 부름ECMAScript: 계속해서 발전해가는JS의 표준 문법, 2015년ES6이후 많은 현대적인 문법이 추가됨let,const: 상수와 변수 구분 가능Template String: 기존에는 줄 바꿈은\n를 사용했으나backtick사용시\n을 사용하지 않아도 된다. 변수는${}와 같이 사용할 수 있다.arrow-function: 기존 함수는function으로 선언했으나,()=>{}와 같이 간결하게 표현 가능하다.class: 기존에는 객체 지향을 구현하기 위해function과prototype을 사용했다.class는function으로 선언하고 클래스의 멤버나 메소드를 구현할 때는prototype을 사용했다. 이는 직관적이지 않고 가독성이 떨어졌다.ES6에서class형태로 객체 지향적인 코드를 작성할 수 있다.destructing:Object에서 값을 꺼낼 때 일일이 작성하지 않고{} = obj처럼 변수를 꺼낼 수 있다.
❏ 비동기 코딩
- 비동기: 이벤트 기반 동작을 코드로 구현하는 방법
callback: 전통적인JS의 이벤트 기반 코딩 방식
// callback
// getUsers로 넘기는 함수가 이벤트 함수이고, 이러한 형태를 콜백이라 부른다.
// 쿼리가 완료되면 오류가 있는지, 혹은 성공했는지 여부를 인자형태로 받는다.
// 에러와 결과를 같이 전달하는 것이 표준으로 자리잡혀 있음
db.getUsers((err, users) => {
console.log(users)
});
// callback-hell
// async1, async2, async3...을 동기적으로 실행해야 하는 경우, node.js는 기본적으로 비동기 동작을 callback으로 처리하기 때문에 콜백지옥 현상이 일어난다.
db.getUsers((err, users) => {
if (err) {
return;
}
async1(users, (r1) => {
async2(r1, (r2) => {
async3(r2, (r3) => {
...
});
});
});
});
Promise: callback 의 단점을 보완한 비동기 코딩 방식
// promise: 함수의 동작이 완료되면 then에 등록된 callback을 실행한다. 오류가 발생한 경우 catch문의 callback 실행
// promise.chaning으로 코드를 간결하게, short-hand 표현으로 더욱 간결하게 표현 가능
db.getUsersPromise()
.then((users) => {
return promise1(users);
})
.then(r1 => promise2(r1))
.catch(... );
// callback을 promise 함수로 변경하기
// async1 함수의 실행 결과에 따라 resolve, reject로 분리
// reject는 catch에 등록된 callback 실행, resolve는 then에 등록된 callback 실행
function getUsersPromise(params){
return new Promise((resolve, reject) => {
getUsers(params, (err, users) => {
if(err){
reject(arr);
return;
}
resolve(users);
});
});
}
Async-await:promise의 단점을 보완한 비동기 코딩 방식,promise의 다른 문법
// async-await: 리턴 값은 promise
// await 한 promise 함수가 완료될 때까지 다음 라인으로 넘어가지 않음
// 순차적 프로그래밍처럼 작성 가능하다.
async function doSomething() => {
const r1 = await promise1();
const r2 = await promise2(r1);
const r3 = await promise3(r1, r2);
...
return r3;
}
doSomething().then(r3 => {
console.log(r3);
});
// err처리
async function doSomething(msg) => {
try {
const r1 = await promise1();
console.log(r);
}catch(e){
console.log(e)
}
}
// parallel run: promise 함수를 동시에 실행시키고 등록된 모든 함수가 마무리되면 결과값을 한꺼번에 반환
// 총 2초의 시간 소요
async function parallel(){
const [r1, r2] = await Promise.all([
promise1(),
promise2(),
]);
console.log(r1, r2);
}
// 총 3초의 시간 소요
async function doSomething() => {
const r1 = await promise1();
const r2 = await promise2();
console.log(r1, r2)
}
callback-hell:promise chaining으로 해결promise-hell:async-await으로 해결- 대부분 가독성이 좋은
async-await을 사용하지만, 상황에 따라callback,promise를 구사할 줄 알아야 한다.
728x90
❏ 이벤트 루프
- 이벤트를 처리하는 반복되는 동작(loop)
node.js가 비동기 - 이벤트 동작을 처리하는 일련의 반복 동작이며, 비동기 코딩이 어떤 순서로 수행되는지에 대해 이해할 수 있다.- 브라우저와
node.js의 이벤트 루프는 기본적인 동작 방식에 큰 차이가 없음. 이벤트 루프의 기본적인 동작 원리를 이해하는 것이 중요하다. call stack:LIFO스택, 이벤트 루프는 콜스택이 비어있을 때까지 스택의 함수를 실행한다.message queue:setTimeout같은 지연실행 함수를 등록하는FIFO큐, 콜스택이 비어있을 경우 이벤트 루프에 의해 등록된 함수를 콜 스택에 추가한다.(setTimeout은 콜 스택이 비어있을 때 실행)job queue:Promise에 등록된 콜백을 등록하는FIFO큐, 상위 함수가 종료되기 전에 콜 스택이 비어있지 않더라도 잡큐에 등록된 콜백을 콜 스택에 추가한다.(promise는 상위 함수가 종료되기 전 실행)
❏ npm
npm(node package manager):node.js프로젝트를 관리하는 필수적인 도구(온라인 저장소 + 커맨드라인 도구)npm 저장소: 필요한 라이브러리나 도구를 손쉽게 검색 가능.node.js의 인기로, 거대한 생태계를 보유- 커맨드 라인 도구: 프로젝트 관리를 위한 다양한 명령어 제공(프로젝트 설정 / 관리, 프로젝트 의존성 관리)
npm init: 해당 디렉터리 안에서package.json이라는 파일을 만들고, 이 디렉터리는node.js프로젝트가 된다.package.json: 프로젝트 관련 정보들이 저장되는 파일. 이 파일을 직접 수정하거나npm명령어를 사용하여 프로젝트의 정보를 수정할 수 있음- 의존성: 프로젝트 내에서 라이브러리를 관리하는 방법, 프로젝트가 실행되기 위해 라이브러리에 의존하기 때문에 이러한 라이브러리들을
dependency(의존성)이라고 부른다. - 라이브러리: 특정 기능을 수행하는 코드의 묶음, 복잡한 기능을 직접 작성하지 않고, 다른 사람이 구현한 것을 사용하는 방법(
node.js에서는 패키지라고도 부른다.) npm install(약어npm i): 프로젝트 의존성 관리 가능(의존성 추가, 의존성 내려받기, 개발용 의존성 추가, 전역 패키지 추가)npm install [package-name]: 필요한 패키지를 프로젝트에 추가한다. 추가된 패키지는package.json의dependencies안에 추가되며,node_modules디렉터리에 저장된다.npm install [package-name] --save-dev: 개발용 의존성은 배포 전까지만 사용하느 의존성(유닛 테스트 라이브러리 등)인데,--save-dev옵션을 이용하면 개발용 의존성을 추가할 수 있다. 개발용 의존성은package.json-devDependencies에 추가된다. 기본적으로node_modules디렉터리는 코드 관리나 배포 시에 업로드하지 않는데, 의존성이 많아지면 용량이 너무 커지고 운영체제별로 실행되는 코드가 다른 경우가 존재하기 때문이다.npm install --production: 프로젝트 배포시 개발용 의존성을 포함하지 않고 내려받는다.package.json-dependencies만node_modules에 내려받는다.npm install [package-name]@[version]:~1.13.0: 1.13.x버전 설치,~^1.13.0: 1.x.x 버전 설치, 가장 왼쪽의 0이 아닌 버전을 고정,0.13.0: 0.13.0 버전만 설치(버전이 달라 오류가 날 때 주로 사용)- 프로젝트에 의존성을 추가하면
package-lock.json이라는 파일이 생성됨. 프로젝트에 의존성을 추가하면 자동으로^최신버전으로 추가가 되는데, 의존성 버전이 갑자기 변경되지 않도록, 설치된 버전을 고정하는 역할을 한다. npm install [package-name] —global: 패키지를 전역 패키지 디렉터리에 내려받는다. 커맨드 라인 도구들을 주로 전역 패키지로 추가해서 받음(express-generator,pm2)- 로컬 패키지:
package.json에 선언되어 있고,node_modules에 저장된 패키지 - 전역 패키지:
npm install -g를 통해 내려받아, 전역 패키지 저장소에 저장된 패키지 - 전역 패키지도 프로젝트에서 사용할 수 있으나, 프로젝트의 의존성이
package.json내에 명시적으로 선언되어 있는 것이 프로젝트 관리의 좋은 방향이다. npm remove [package-name]: 의존성 패키지를 삭제한다.dependencies,devDependencies에서 삭제하고node_modules에서도 삭제한다.npm run [script-name]: 스크립트(간단한 동작을 수행하는 코드)를 실행한다.npm script: 의존성 패키지를 사용할 수 있음npm test: 코드 유닛테스트 등에 사용npm start: 프로젝트 실행npm stop: 프로젝트 종료run을 제외하고 사용할 수 있을 뿐,npm내부적으로 코드를 제공해 주는 것은 아니다.
❏ npx
npm패키지를 설치하지 않고 사용할 수 있게 해주는 도구, 프로젝트에 추가하거나 전역 패키지로 추가하지 않고npx를 이용하여 바로 실행할 수 있음gist코드를 다운로드하지 않고 바로 실행 가능(코드를 잘 확인하고 실행해야 함)
// npm
npm i cowsay -g
cowsay hi
// npx
npx cowsay hi
npx node@12 index.js
❏ module
- 간단한 프로그램이라면 파일 하나로도 가능, 프로젝트가 커지면 기능에 맞게 코드를 분리하는 것이 중요하다. 모듈은 코드를 분리하기 위한 방법
- 반복되는 코드는 모듈로 분리하여 사용(재사용성 증가)
- 패키지는 모듈의 모음,
npm패키지들은 많은 모듈을 포함하고 있는 코드 모음 process: 현재 실행 프로세스 관련 기능 제공(arch,argv,env,abort,kill,exit)fs: 파일 입출력을 하기 위해 사용(readFile,writeFile,watch로 파일 / 디렉터리 변경 이벤트 감지)http:http서버, 클라이언트를 위해 사용,createServer함수로 서버 생성,request함수로http요청 생성- 기타 기본 제공 모듈 확인하기: node.js/doc
❏ 모듈의 작성과 사용
require함수를 통해 모듈을load할 수 있음- 의존성 패키지, 직접 작성한 모듈 사용 가능
node.js의 모듈은 첫require시cache, 두 번 실행하지 않음- 모듈 코드를 여러 번 실행하기 위해선 함수 모듈로 작성
- 의존성 패키지들은
require('package-name')로load가능하다. 패키지를 사용하려면node_modules에 내려받아져 있어야 함
const name = "elice";
const age = 5;
const nationality = "korea";
// 모듈의 기본적인 사용법
// 모듈이 load 될 때 사용될 값을 module.exports로 내보냄
module.exports = {
name,
age,
nationality
}
// 변수명으로 export 하는 모듈 작성하기
// 모듈을 object로 만들고, 각 key - value를 지정해서 내보냄
exports.name = name;
exports.age = age;
exports.nationality = nationality;
const student = require('./elice'); // { name: "alice", age: 5, nationality: "korea" }
// 함수를 export하는 모듈
module.exports = (name, age, nationality) => {
return {
name,
age,
nationality
}
}
// npm 패키지 모듈
const dayjs = require('dayjs');
console.log(dayjs());
// 직접 작성한 모듈
const myModule = require('./my-module');
console.log(myModule);
// 함수형 모듈: 함수형은 load한 즉시 실행되지 않음
const myFunctionModule = require('./my-function-module');
console.log(myFunctionModule(name, age, nationality));
// json 모듈: json파일도 load가능, object로 자동파싱
const myData = require('./my-data');
console.log(myData)
❏ ES Module
- ES6에서 등장한 JS의 공식적인 표준 모듈
- JS는 기본적으로 모듈을 제공하지 않았다.
node.js는 독자적인 방식을 통해 모듈을 지원하고 있었다(common js)- ES module의 등장으로
node.js에서는 2가지 모듈을 지원해야 한다. commonjs는module.exports,require로 모듈을 사용,ES module은export와import로 모듈을 만들고 사용- 현재
ES Module은node.js에서 기본적으로 사용하기에 제약이 많다.(프로젝트 타입을module로 변경,commonjs모듈import시 문제 발생 등)
❏ 웹의 이해
- 웹 서비스는 기본적으로
HTTP요청과 응답의 반복으로 이루어짐.HTTP요청은 사용자가 어떤 데이터가 필요한지를 서버에게 알리는 역할,HTTP응답은HTTP요청에 해당하는 적절한 데이터를 전달하는 역할

- 정적 웹(Web 1.0): 사용자와 상호작용하지 않는 페이지 - 단방향 통신,
Link를 통한 페이지 이동 정도만 가능, 일반적으로 변하지 않는html파일로 제공 - 동적 웹(Web 2.0): 사용자와 상호작용을 함 - 양방향 통신, 구글 맵, 웹, 채팅 등 사용자가 다양한 기능을 수행할 수 있음,
FE와BE가 유기적으로 통신하며 동작한다. 현대적인 웹은 대부분 동적 웹이다. CSR: 프론트엔드에서 사용자가 페이지에서 보는 동적인 부분을 대부분 처리하는 방식(사이트가 변하는 부분을 프론트엔드에서 처리, 페이지 리소스들이 미리 정의,API통신이용,API호출이 완료된 후에 보여준다. 복잡한 프로젝트 구성, 개발 사이즈가 커진다.)SSR: 백엔드에서 페이지 대부분의 영역을 처리해서 프론트엔드로 전달하는 방식(프론트엔드는HTTP응답을 받아 화면에 표시, 백엔드에서 필요한 데이터가 포함된 페이지를 만들어서HTTP응답에 전달, 백엔드에서HTML파일을 작성해서 프론트로 전달, 쉬운 구성, 작은 개발 사이즈, 로딩이 느려 보이고, 페이지가 이동하면 페이지가 깜빡인다.)- 웹서버는
HTTP요청과HTTP응답으로 이루어지는데 클라이언트는 서버로HTTP요청을 보내고, 서버는HTTP응답을 보낸다.
❏ 웹 프레임워크
- 웹 서비스에 필요한 기능들을 제공해주는 다양한 도구들의 모음, 필요한 부분만 집중해서 개발할 수 있음
HTTP 요청,HTTP 응답,라우팅,HTML Templating- 라우팅:
HTTP요청에 따라 알맞은 응답을 보내주는 경로를 설정하는 일 HTML Templating:SSR을 구현하기 위한 방법,SSR에서 응답으로 보낼HTML을 서버에서 작성하기 위해HTML Template를 통해 미리 페이지의 뼈대를 작성 가능node.js의 다양한 웹 프레임워크:Express.js,Koa.js,Nest.js,Hapi,Sails.js,Meteor.js등등
❏ Express.js 시작하기
node.js웹 프레임워크 중 가장 유명한 웹 프레임워크- 필요에 따라 유연하게 구조 설정 가능
- 다양한 미들웨어를 통해 필요한 기능을 간단하게 추가 기능
- 모든 동작이 명시적으로 구성되기 때문에, 웹 프레임워크의 동작 방식을 이해하기 가장 좋은 프레임워크
npm init expressexpress-generator: 프로젝트 생성기, 리액트의CRA와 비슷함
// npm 시작
npm i -g express-generator
express my-web
cd my-web
npm i
npm start
// npx 시작
npx express-generator
cd my-web
npm i
npm start
❏ Express.js 구조

❏ Express.js 동작 방식
express-generator로 만들어진 프로젝트 디렉토리에 접근하여,npm start로Express.js프로젝트를 실행할수 있고[localhost:3000](http://localhost:3000)에 접속하여 페이지를 확인할 수 있다.
1. localhost:3000 접속
2. app.js -> app.use('/', indexRouter);
3. routes/index.js -> router.get('/', ...)
4. routes/index.js -> res.render('index', ...)
5. views/index.jade
- app.js:
express()로 생성되는app객체를 확인할 수 있다.Express.js의 기능을 담은 객체 - 라우팅:
app라우팅,express 라우팅 path parameter: 주소의 일부를 변수처럼 사용할 수 있다.
1. /users/:id - /users/123, /users/456 등으로 접속했을 때 라우팅 적용
2. /messges/:from-:to /message/123-456/ 등으로 접속했을 때 라우팅 적용
request handler - request: 라우팅에 적용되는 함수,HTTP요청과 응답을 다룰 수 있는 함수로, 설정된 라우팅 경로에 해당하는 요청이 들어오면Request handler함수가 실행됨

// 설정된 라우팅 경로에 해당하는 요청이 들어오면 `request handler` 함수를 실행한다.
router.get('/:id', (req, res) => {
const id = req.params.id
res.send(`hello ${id}`);
});
resquest-handler - response: HTTP 응답을 처리하는 객체, HTTP 응답의 데이터를 전송하거나, 응답 상태 및 헤더를 설정할 수 있음

// path parameter
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send("hi");
});
// path parameter 사용하기
app.get('/say/:greeting', (req, res) => {
const { greeting } = req.params;
res.send(greeting);
});
app.listen(8080);
// router 연결
const express = require('express');
const userRouter = require('./routes/users');
const app = express();
app.get('/', (req, res) => {
res.send("OK");
});
/* 라우터를 '/users' 경로에 연결 */
app.use('/users', userRouter)
app.listen(8080);반응형
'Frontend > 엘리스 SW 엔지니어 트랙' 카테고리의 다른 글
| [ 엘리스 SW 엔지니어 트랙 ] 29일차 (0) | 2021.12.03 |
|---|---|
| [ 엘리스 SW 엔지니어 트랙 ] 28일차 (0) | 2021.12.02 |
| [ 엘리스 SW 엔지니어 트랙 ] 26일차(6주차: Node.js와 Express.js - npm, Middleware, MongoDB) (0) | 2021.11.30 |
| [ 엘리스 SW 엔지니어 트랙 ] 25일차 (0) | 2021.11.28 |
| [ 엘리스 SW 엔지니어 트랙 ] 24일차 (0) | 2021.11.26 |
댓글