본문 바로가기

WINK-(Web & App)/Express.js (Node.js) 스터디

[2024 Node.js 스터디] 장민우 #4주차 "노드 입문 1~5"

반응형

3장 노드 기능 알아보기

3.1 REPL 사용하기

REPL이란?

  • READ (읽고)
  • Eval (해석하고)
  • Print (결과물 리턴)
  • Loop (종료할 때까지 반복)

 

vscode 기준으로 터미널 창에 (단축키 ctrl+`)

node 작성 -> 프롬프트가 > 모양으로 바뀌었다면 JS코드 입력 가능

> const str = 'Hello world, hello node';
undefined
> console.log(str);
Hello world, hello node
undefined
>

위와 같이 출력되었다면 성공입니다.

 

실습 따라하는데 node를 안쳐서 왜 실행이 안되지?라고 고민했던 건 비밀....

3.2 JS 파일 실행하기

function helloWorld() {
  console.log('Hello World');
  helloNode();
}

function helloNode() {
  console.log('Hello Node');
}

helloWorld();

콘솔에서 node로 실행합니다. (확장자 생략 가능)

REPL에서 입력하는 것이 아니라 콘솔에서 입력해야 합니다.

$ node helloWorld
Hello World
Hello Node

이렇게 작성된다면 성공..!

3.3 모듈로 만들기

모듈이란?

- 특정한 기능을 하는 함수나 변수들의 집합

ex) 수학에 관련된 코드들만 모아서 모듈을 하나 만들 수 있습니다.

모듈은 자체로도 하나의 프로그램이면서 다른 프로그램의 부품으로도 사용 가능합니다.

자료구조나 C++ 수강 하시면서 다 배우셨을 겁니다!

노드에서는 두 가지 형식의 모듈을 사용하는데,

하나는 CommonJS 모듈이고 다른 하나는 ECMAScript 모듈입니다. CommonJS 모듈부터 알아봅시다.

const odd = 'CJS 홀수입니다';
const even = 'CJS 짝수입니다';

module.exports = {
  odd,
  even,
};

 

var.js파일 하나 만들고

const { odd, even } = require('./var');

function checkOddOrEven(num) {
  if (num % 2) { // 홀수이면
    return odd;
  }
  return even;
}

func.js파일 하나 만듭니다.

 

var.js에 변수 두 개 (odd, even) 선언했고,

var.js를 참조하는 func.js 파일도 만들었습니다.

const { odd, even } = require('./var');
const checkNumber = require('./func');

function checkStringOddOrEven(str) {
  if (str.length % 2) { // 홀수이면
    return odd;
  }
  return even;
}

console.log(checkNumber(10));
console.log(checkStringOddOrEven('hello'));

마지막으로 index.js도 만들었는데, index.js는 var.js와 func.js를 모두 참조합니다.

모듈 하나가 여러 개의 모듈을 사용할 수 있는 것이죠. 또한,

var.js가 func.js와 index.js에 두 번 쓰이는 것처럼, 모듈 하나가 여러 개의 모듈에 사용될 수도 있습니다.

모듈로부터 값을 불러올 때 변수 이름을 다르게 지정할 수도 있습니다.

실행하면 ~

인코딩이 자꾸 이상해서 한동안 애먹었다~...

이렇게 출력이 되는데, 여러 파일에 걸쳐 재사용되는 함수나 변수를 모듈로 만들어두면 편리합니다.

 

순환 참조

만약 두 모듈 dep1과 dep2가 있고 이 둘이 서로를 require 한다면 어떻게 될까요?

const dep2 = require('./dep2');
console.log('require dep2', dep2);
module.exports = () => {
  console.log('dep2', dep2);
};
const dep1 = require('./dep1');
console.log('require dep1', dep1);
module.exports = () => {
  console.log('dep1', dep1);
};

dep1, dep2 파일이고

const dep1 = require('./dep1');
const dep2 = require('./dep2');

dep1();
dep2();
이 파일을 실행시키면, require('./dep1')이 가장 먼저 실행됩니다. 그런데 dep1.js에서는
제일 먼저 requrie('./dep2)가 실행되는데요, 다시 dep2.js에서는 require(./dep1)이 실행됩니다.
이 과정이 계속 반복됩니다. 실제로 실행을 시키면 놀랍게도 dep1의 module.exports가 함수가
아니라 빈 객체로 표시됩니다. 이러한 현상을 순환참조라고 합니다. 이렇게 순환 참조가 있을 경우에는
순환 참조되는 대상을 빈 객체로 만듭니다.

 

3.4.1 global ***
global 객체 내부에는 매우 많은 속성이 들어 있습니다.
전역 객체라는 점을 이용해 파일 간에 간단한 데이터를 공유할 때 사용합니다.
globalA.js 와 globalB.js를 생성하겠습니다.
module.exports = () => global.message;
const A = require('./globalA');

global.message = '안녕하세요';
console.log(A());

globalA 모듈의 함수는 global.message 값을 반환합니다. globalB.js에서는 global 객체에 속성명이

message인 값을 대입하고 globalA 모듈의 함수를 호출합니다.

콘솔 결과를 보면, globalB에서 넣은 global.message 값을 globalA에서도 접근할 수 있음을 알 수 있습니다.

3.4.2 console

console 객체는 보통 디버깅을 위해 사용합니다. 개발 중 변수에 값이 제대로 들어 있는지 확인하기 위해 

사용하기도 하고, 에러 발생 시 에러 내용을 콘솔에 표시하기 위해서도 사용하며, 코드 실행 시간을 알아보려고 할 때도

사용합니다. 대표적으로 console.log 메서드가 있습니다. 다른 로깅 함수들도 알아봅시다!

 

  • console.time(레이블): 같은 레이블을 가진 time과 timEnd 사이의 시간을 측정
  • console.error(에러 내용): 에러를 콘솔에 표시
  • console.table(배열): 배열의 요소로 객체 리터럴을 넣으면, 객체의 속성들이 테이블 형식으로 표현
  • console.dir(객체, 옵션): 객체를 콘솔에 표시할 때 사용. 첫 번째 인수로 표시할 객체를 넣고, 두 번째 인수로옵션을 넣습니다. 옵션의 colors를 true로 하면 콘솔에 색이 추가되어 보기가 한결 편해집니다. depth는 객체 안의 객체를 몇 단계까지 보여줄지를 결정합니다. 기본값은 2입니다.
  • console.trace(레이블): 에러가 어디서 발생했는지 추적할 수 있게 합니다. 보통은 에러 발생 시 에러 위치를 알려주므로 자주 사용하지는 않지만, 위치가 나오지 않는다면 사용할 만합니다.

3.5.3 url

인터넷 주소를 쉽게 조작하도록 도와주는 모듈입니다. url 처리에는 크게 두 가지 방식이 있습니다.

하나는 노드 버전 7에서 추가된 WHATWG 방식의 url이고, 다른 하나는 예전부터 노드에서 사용하던 방식의 url입니다. 요즘은 WHATWG 방식만 사용합니다.

const url = require('url'); ---- ➊

const { URL } = url;
const myURL = new URL('http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor');
console.log('new URL():', myURL);
console.log('url.format():', url.format(myURL));

 

 

new URL(): URL {
  href: 'http://www.gilbut.co.kr/book/bookList.aspx?sercate 1 = 001001000 #anchor',
  origin: 'http://www.gilbut.co.kr' ,
  protocol: 'http:',
  username: '',
  password: '',
  host: 'www.gilbut.co.kr',
  hostname: 'www.gilbut.co.kr',
  port: '',
  pathname: '/book/bookList.aspx',
  search: '?sercate 1 = 001001000 ',
  searchParams: URLSearchParams { 'sercate 1 ' => '001001000 ' },
  hash: '#anchor'
}
url.format(): http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor

 

주소가 host 부분 없이 pathname 부분만 오는 경우(예시: /book/bookList.apsx), WHATWG 방식은 이 주소를 처리할 수 없습니다. 4장에서 서버를 만들 때 host 부분 없이 pathname만 오는 주소를 보게 될 것입니다.

이럴 때는 new URL처럼 두 번째 인수로 host를 적어줘야 합니다.

3.5.5 crypto ***

단방향 암호화

비밀번호는 보통 단방향 암호화 알고리즘을 사용해서 암호화합니다. 단방향 암호화란 복호화 할 수 없는 암호화 방식을 뜻 합니다. 복호화는 암호화된 문자열을 원래 문자열로 되돌려 놓는 것을 의미합니다. 즉, 단방향 암호화는 한번 암호화하면 원래 문자열을 찾을 수 없습니다. 복호화할 수 없으므로 암호화라고 표현하는 대신 해시 함수라고 부르기도 합니다.

 

단방향 암호화 아록리즘은 주로 해시 기법을 사용합니다.

해시 기법이란 어떠한 문자열을 고정된 길이의 다른 문자열로 바꾸어 버리는 방식!

ex) abcedfgh라는 문자열을 qvew로 바꾸고, ijklm이라는 문자열을 zvsf로 바꾸는 겁니다.

 

hash.js

const crypto = require('crypto');

console.log('base64:', crypto.createHash('sha512').update('비밀번호').digest('base64'));
console.log('hex:', crypto.createHash('sha512').update('비밀번호').digest('hex'));
console.log('base64:', crypto.createHash('sha512').update('다른 비밀번호').digest('base64'));

콘솔

base64: dvfV6nyLRRt3NxKSlTHOkkEGgqW2HRtfu19Ou/psUXvwlebbXCboxIPmDYOFRIpqav2eUTBFuHaZri5x+usy1g==
hex: 76f7d5ea7c8b451b773712929531ce92410682a5b61d1b5fbb5f4ebbfa6c517bf095e6db5c26e8c483e60d8385448a6a6afd9e513045b87699ae2e71faeb32d6
base64: cx49cjC8ctKtMzwJGBY853itZeb6qxzXGvuUJkbWTGn5VXAFbAwXGEOxU2Qksoj+aM2GWPhc1O7mmkyohXMsQw==

 

비밀번호라는 문자열을 해시를 사용해 바꾸어 보았습니다.

4장. http 모듈로 서버 만들기

4.1 요청과 응답 이해하기

 

 

데이터베이스 수강 중이신 분들은 이 사진을 보고 한눈에 아실 겁니다.

서버는 클라이언트가 있기에 동작합니다. 클라이언트에서 서버로 요청을 보내고, 서버에서는 요청의 내용을 읽고 처리한 뒤 클라이언트에 응답을 보냅니다.

따라서 서버에는 요청을 받는 부분과 응답을 보내는 부분이 있어야 합니다. 요청과 응답은 이벤트 방식이라고 생각하면 됩니다. 클라이언트로부터 요청이 왔을 때 어떤 작업을 수행할지 이벤트 리스너를 미리 등록해둬야 합니다.

 

createServer.js

const http = require('http');

http.createServer((req, res) => {
  // 여기에 어떻게 응답할지 적어줍니다
});

server1.js

const http = require('http');

http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  res.write('<h1>Hello Node!</h1>');
  res.end('<p>Hello Server!</p>');
})
  .listen(8080, () => { // 서버 연결
    console.log('8080번 포트에서 서버 대기 중입니다!');
});

실행하면 8080번 포트에서 서버 대기 중입니다!라는 문장이 나올 것입니다.

이제 웹 브라우저를 열어 http://localhost:8080 또는 http://127.0.0.1:8080에 접속합니다.

Hello Node!

Hello Server!가 뜨면 성공~

 

NOTE * 소스 코드 변경

서버의 소스 코드를 변경할 때 서버는 자동으로 변경 사항을 반영하지 않습니다.

즉, 서버를 종료했다가 다시 실행해야만 변경 사항이 반영됩니다!

 

NOTE * HTTP 상태 코드

  • 2XX : 성공을 알리는 상태 코드 200(성공), 201(작성됨)이 많이 사용됩니다.
  • 3XX : 리디렉션을 알리는 코드입니다. 301(영구이동), 302(임시 이동)이 있습니다.
  • 4XX : 요청 오류를 나타냅니다. 400(잘못된 요청), 401(권한 없음), 403(금지됨), 404(찾을 수 없음)
  • 5XX :  서버 오류를 나타냅니다. 요청은 제대로 왔지만 서버에 오류가 생겼을 때 발생. 이 오류가 뜨지 않게 주의해서 프로그래밍해야 합니다. 500(내부 서버 오류), 502(불량 게이트웨이), 503(서비스를 사용할 수 없음)

4.3 쿠키와 세션 이해하기

로그인을 구현하려면 쿠키와 세션을 알고 있어야 합니다. 여러분이 웹 사이트에 방문해서 로그인할 때 내부적으로는 쿠키와 세션을 사용하고 있습니다. 여러분이 누구인지 기억하기 위해 서버는 요청에 대한 응답을 할 때 쿠키라는 것을 같이 보냅니다. 브라우저는 쿠키가 있다면 자동으로 동봉해서 보내주므로 따로 처리할 필요가 없습니다. 서버에서 브라우저로 쿠키를 보낼 때만 여러분이 코드를 작성해 처리하면 됩니다.

const http = require('http');

http.createServer((req, res) => {
  console.log(req.url, req.headers.cookie);
  res.writeHead(200, { 'Set-Cookie': 'mycookie=test' });
  res.end('Hello Cookie');
})
  .listen(8083, () => {
    console.log('8083번 포트에서 서버 대기 중입니다!');

 

$ node cookie
8083번 포트에서 서버 대기 중입니다!

쿠키 간에는 세미콜론을 넣어 각각을 구분합니다.

4.4 https와 http2

https 모듈은 웹 서버에 SSL 암호화를 추가합니다. GET이나 POST 요청을 할 때 오가는 데이터를 암호화해서 중간에 다른 사람이 요청을 가로채더라도 내용을 확인할 수 없게 합니다. 요즘은 로그인이나 결제가 필요한 창에서 https 적용이 필수가 되는 추세입니다.

const http2 = require('http2');
const fs = require('fs');

http2.createSecureServer({
  cert: fs.readFileSync('도메인 인증서 경로'),
  key: fs.readFileSync('도메인 비밀 키 경로'),
  ca: [
    fs.readFileSync('상위 인증서 경로'),
    fs.readFileSync('상위 인증서 경로'),
  ],
}, (req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  res.write('<h1>Hello Node!</h1>');
  res.end('<p>Hello Server!</p>');
})
  .listen(443, () => {
    console.log('443번 포트에서 서버 대기 중입니다!');
  });

http2를 적용한 파일입니다. https 모듈과 거의 유사한데, createServer 메서드를 createSecure Server 메서드로 바꾸면 된다.

클러스터는 이해가 잘 안돼서.. 다시 공부하고 블로깅 해보겠습니다!

5장. 패키지 매니저

5.1 npm 알아보기 **

npm은 Node Package Manager의 약어로, 이름 그대로 노드 패키지 매니저를 의미합니다.

서비스에 필요한 패키지를 하나씩 추가하다 보면 어느샌가 패키지 수가 100개를 훌쩍 넘어버리게 됩니다. 그리고 사용할 패키지는 저마다 고유한 버전이 있으므로 어딘가에 기록해둬야 합니다. 같은 패키지라도 버전별로 기능이 다를 수 있으므로 프로젝트를 설치할 때 패키지도 동일한 버전을 설치하지 않으면 문제가 생길 수 있습니다. 이때 설치한 패키지의 버전을 관리하는 파일이 바로 package.json입니다.

따라서 노드 프로젝트를 시작하기 전에는 폴더 내부에 무조건 package.json부터 만들고 시작해야 합니다.

npm은 package.json을 만드는 명령어를 제공합니다.

 

--save 옵션

패키지를 설치할 때, npm install 명령어에 --save옵션을 붙이는 책이나 블로그를 많이 볼 수 있습니다.

dependencies에 패키지 이름을 추가하는 옵션이지만 npm@5부터는 기본값으로 설정되어 있으므로 따로 설정할 필요 X

프로젝트 이름과 설치하는 패키지 이름은 달라야 합니다!!

npm에는 제가 아까 위에 블로깅한 전역(global)설치라는 옵션도 있습니다. 패키지를 현재 폴더의 node_modules에 설치하는 것이 아니라 npm이 설치되어 있는 폴더에 설치합니다. 이 폴더의 경로는 보통 시스템 환경 변수에 등록되어 있으므로 전역 설치한 패키지는 콘솔의 명령어로 사용할 수 있습니다. 전역 설치를 했다고 해서 패키지를 모든 곳에서 사용한다는 뜻은 아닙니다. 대부분 명령어로 사용하기 위해 전역 설치합니다.

5.3 패키지 버전 이해하기

는 깊게 안 읽어봐도 될 것 같아요!!

5.5 패키지 배포하기

패키지를 만들어 배포해 보겠습니다. 코딩에 앞서 npm 계정을 만들어주고,

  1. npm 웹사이트 회원가입
  2. 회원 가입 confirm 메일을 확인
  3. 콘솔에서 npm login 명령어를 입력해 생성한 계정으로 로그인합니다. 보안이 강화돼서 가입 시 입력했던 이메일로 OTP코드가 발송됩니다. OTP 코드도 같이 입력해야 로그인됩니다.
module.exports = () => {
  return 'hello package';
};

 

package.json의 main 부분의 파일명과 일치해야 합니다. 그래야 npm에서 이 파일이 패키지의 진입점임을 알 수 있습니다.

npm publish 명령어를 사용해 이 패키지를 배포해 보면 -> 에러가 발생할 것입니다.

$ npm publish
npm  notice
// notice 생략
npm ERR! code E403
npm ERR! 403 403 Forbidden - PUT https://registry.npmjs.org/npmtest - You do not have permission to publish "npmtest". Are you logged in as the correct user?
npm ERR! 403 In most cases, you or one of your dependencies are requesting
npm ERR! 403 a package version that is forbidden by your security policy, or
npm ERR! 403 on a server you do not have access to.

npm ERR! A complete log of this run can be found in:
npm ERR!      C:\Users\speak\AppData\Roaming
pm-cache\_logs\2022-04-24T17_52_25_852Z-debug.log

npmtestm라는 이름을 누군가가 사용하고 있어 즉 이름이 겹쳐서 그런 겁니다.

패키지의 이름을 바꿔서 배포하면 됩니다^^!

 

콘솔

$ npm publish
// notice 생략
+ npmtest-1234@0.0.1
$ npm info npmtest-1234
npmtest-1234@0.0.1 | ISC | deps: none | versions: 1
hello package.json
// 중략
maintainers:
- zerocho <zerohch0@gmail.com>

dist-tags:
latest: 0.0.1

published 51 seconds ago by zerocho <zerohch0@gmail.com>

이렇게 패키지에 대한 정보가 나오면 성공입니다!

이번엔 버전과 관련된 문제에 대해 설명드리겠습니다.

 

$ npm publish
// notice 생략
npm ERR! code E403
npm ERR! 403 403 Forbidden - PUT https://registry.npmjs.org/npmtest-1234 - You cannot publish over the previously published versions: 0.0.1.

이 에러 메세지가 보인다면 이미 출시한 버전이라는 뜻입니다. 따라서 이보다 더 높은 버전을 출시해야 합니다.

 

npm version patch 명령어를 사용해 버전을 올린 후 출시하면 성공적으로 될 것입니다

 

npm 배포 시 주의사항

npm에 배포할 때는 우리의 코드가 세상에 공개되는 것이므로 개인 정보가 코드에 들어있지 않은지 꼭 확인!!

반응형