시작하기전에....

 

백엔드 공부를 하면서, 백엔드 개발자로써 구현할 수 있는 기능과 다뤄야하는 패키지가 무엇이 있는가에 대해

정리를 해보고자 한다.

여기는 어디까지나 이러이러한 기능이 있고 이것들을 익혀야 한다.

라는 뉘양스로 작성한 포스트에 가깝다.

 

때문에 이곳에서는 아주 짧고 간단하게 패키지를 소개하고자 한다.

참고로 아래서 소개할 패키지중엔 같은 기능을 하는 다른 패키지도 있을 수 있는 점도 유의하자

 

0. 서버, DB 등을 위한 패키지

Node.js를 할 때 거의 필수적으로 설치하게 되는 패키지에 대한 내용

데이터 베이스에 관한 부분도 포함한다.

express

웹서버를 만들 때 거의 필수적으로 사용하게 되는 패키지

API서버를 구축하는데 주로 사용

사실상 필수 패키지다.

 

helmet

express사용할 때 헤더의 설정을 통해 웹 취약점으로부터 서버를 보호해주는 보안 모듈이다.

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

app.use(helmet());

서버실행에 이렇게 추가해주면 된다.

 

cors

CORS를 사용하기 위한 것으로

다른 사이트에서 나의 서버에 요청하는 것을 허가할 때 사용한다.

꽤나 어려운 개념이므로 따로 공부가 필요하다.

 

mongoose

몽고DB를 사용한다면 설치해야 하는 패키지

다른 DB를 사용한다면 필요 없다.

 

mysql2

mysql DB를 사용한다면 설치해야 하는 패키지

mysql을 사용하려면 아래 두 개도 설치해야 한다.

npm i sequelize mysql2 -S 
npm i sequelize-cli -D

 

1. 로그인 기능

주로 로그인이나 회원가입에 관한 부분

비밀번호의 암호화, 회원가입시 아이디나 비번에 대소문자 포함 등의 내용을 다룬다.

jsonwebtoken

로그인시 토큰을 발행할 용도로 사용한다.

로그인에 성공하면 토큰을 발행하고 로그인한 사용자가 요청을 할 때마다

이 토큰을 검증하여 요청을 허가 또는 차단한다.

 

bcrypt

암호화 모듈로 패스워드를 암호화하여 저장하기 위해 사용한다.

암호화 모듈엔 여러가지가 있지만 현재는 이게 가장 강력하고 또 유명하다.

 

joi

아이디나 비밀번호를 입력할 때 제한을 두기 위해 사용한다.

아이디나 비밀번호를 입력할때 최소 몇자리, 최대 몇자리, 영문자 대소문자 포함 등의

조건이 주렁주렁 달리는데 이것을 구현할 때 사용한다.

더보기
const { signupSchema, loginSchema, emailDupSchema, nicknameDupSchema } = require('../util/validation');

class UserController {
  signup = async (req, res, next) => {
    try {
      const { locationId, nickname, password, confirm, email } =
        await signupSchema.validateAsync(req.body);
    } catch (error) {
      next(error);
    }
}}
이하 생략
const joi = require('joi');

module.exports = {
  signupSchema: joi.object({
    locationId: joi.number().required(),
    email: joi
      .string()
      .pattern(
        /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/
      )
      .required(),
    nickname: joi.string().min(2).max(10).required(),
    password: joi
      .string()
      .pattern(/^(?=.*[a-zA-Z])[0-9a-zA-Z!@#$%^&*]{8,16}$/)
      .required(),
    confirm: joi
      .string()
      .pattern(/^(?=.*[a-zA-Z])[0-9a-zA-Z!@#$%^&*]{8,16}$/)
      .required(),
  }),
};
이하 생략

 위 코드는 실제로 작동하는 코드는 아니고 일종의 참조용으로 적어놓은 것이다.

위는 로그인 컨트롤러 파일이고

아래는 joi관련 파일이다.

회원가입을 할때 바디로부터 필요한 내용을 받아서 validation스키마로 보내면

그 스키마에서 검증을 거쳐준다고 생각하면 된다.

 

 

cookie-parser 

쿠키를 사용하기 위해 설치한다.

쿠키는 사용자의 컴퓨터에 저장되는 정보를 뜻하며 주로 로그인정보나 장바구니 정보등을 저장할 때 쓴다.

이것 또한 매우 중요하니 따로 공부를 해볼 것을 권장한다.

 

2. 포스트 기능

포스팅을 작성할 때 필요한 패키지를 위주로 다룬다

multer

파일 업로드를 위해서 사용한다.

 

multer-s3

사진을 업로드하기 위해서 사용하는 듯하다.

이 때, aws-sdk 패키지랑 같이 사용하는 듯 하다.

 

sharp

이미지 크기를 변경하기 위해서 사용한다.

프로필 이미지, 썸네일 등을 사용하기 위해서 사용한다.

 

aws-sdk

AWS서비스와 연동해서 사용하기 위해서 설치한다.

즉 배포용이라고 생각하면 편하다.

 

 

3. 실시간 데이터 교환 기능

socket.io

알람, 채팅 등과 같은 실시간 서비스를 구현해야 할 때 사용한다.

따로 공부가 필요할 정도로 어려운 부분임을 참고

 

※ 그 외 개발 편의용 패키지

prettier

그냥 문서양식을 통일해주는 패키지

내부 코드를 조작하면 어떻게 통일해줄지도 조정할 수 있다.

 

nodemon

자동으로 서버를 실행시켜주는 패키지

없어도 되지만 편하다보니 다들 사용하는 모양이다.

 

morgan

서버 로그 관리를 위해 사용한다.

예를 들어 서버와 클라이언트간의 통신이 발생하면 그 로그가 터미널에 발생하는 것을 알 수 있다.

 

 


좋은 개발자가 되기 위해서, 위의 패키지의 사용법은 익히도록 하자

 

 

시작하기전에......

 

https://tarelbase.tistory.com/24?category=1003239

 

지난번에 쿠키와 세션에 대해 다룬적이 있다.

 

그러나 개인적으로 이 부분은 몇 번 더 공부해갈 필요가 있다고 판단되어 다시한번 짚어넘어가보겠다

 

1. 쿠키는 무엇이고 왜 필요한가??

초창기 인터넷에는 개인화라는 것이 없었다.

 

개인화란?? 다시말해 사람마다 보는 정보가 다르다는 것을 말한다.

 

내 통장에 100만원이 있다고 해서 다른사람들도 통장에 100만원이 있지는 않을 것이다.

또한 내 통장은 나만 볼 수 있어야 하고 다른사람들은 보게해선 안 된다.

 

초창기 인터넷은 이런 기능이 없었던 것이다.

 

누구나 특정 홈페이지에서 같은 정보를 볼 수 밖에 없었고

이는 마이페이지, 즉 , 나만의 정보에 대한 필요성을 만들었다.

이것이 지금의 쿠키를 낳은 것이다.

 

쿠키를 통해 지금 접속한 사람이 누구인지 판별하고 그 사람만 접근할 수 있는 정보를 제공할 수 있게 되면서

개인화라는 것을 실현할 수 있게 된 것이다.

 

2. 쿠키를 구워보자

일단 기본적인 세팅을 위해 간단한 웹코드를 작성하자

const http = require('http');
http.createServer(function(req,res){
  res.end('cookie')
}).listen(3000)

여기 보면 응답에 cookie라고 적어놨는데, 그냥 웹페이지에서 cookie라는 글자가 출력될 것이다.

한글을 적어도 되긴하는데 글자가 깨진다... 그냥 영어로 적자

 

위 코드를 실행하고 로컬 호스트 3000번으로 들어간 모습, cookie라는 글자가 출력된다.

 

const http = require('http');
http.createServer(function(req,res){

  res.writeHead(200, {
    'Set-Cookie':['smallCookie = choco','cuteCookie = vanilla']		//추가된 부분
  })

  res.end('cookie')
}).listen(3000)

위 코드를 보자, 추가된 부분의 바로 위아래줄이 새로 추가된 부분이다.

res(응답)의 writeHead를 통해 해드를 정의한다. 200은 성공을 나타내는 상태코드(404NotFound같은거)고

바로 아래 Set-Cookie를 통해 쿠키를 발급하고 있다.

여기서 발급된 쿠키가 바로 smallCookie와 cuteCookie다.

즉, 쿠키를 두 개를 구운 것이다.

 

 

구워진 쿠키는 개발자 도구에서 Network -> localhost 에서 확인할 수 있다.

 

 

쿠키를 없애버릴 수도 있다.

Application에서 localhost를 찾고 쿠키를 선택해 없애면 된다.

 

3. 구워진 쿠키를 함 봐보자

기껏 쿠키를 구웠는데 이걸 써먹지 못하면 무슨 소용일까?

자, 쿠키를 읽어보자

console.log(req.headers.cookie)

이걸 쓰면 터미널에서 쿠키를 확인할 수 있다.

아마 웹페이지를 새로고침하면 뜰 것이다.

npm install -s cookie

터미널에 위를 입력해 cookie를 사용하기위한 모듈을 설치해 주고

const cookie = require('cookie');

코드 최상단에 위 코드를 추가해 주자

var cookies ={}

cookies = cookie.parse(req.headers.cookie);

위 코드는 쿠키 데이터를 객체로 가져오는 역할을 한다.

이게 있으면 쿠키를 가져오기 편해진다.

단, cookie값이 undefined라면 에러가 발생할 수 있으니 조건문을 추가해줘야한다.

 

const http = require('http');
const cookie = require('cookie');

http.createServer(function(req,res){
  var cookies = {};
  if(request.headers.cookie !== undefined){
    cookies = cookie.parse(req.headers.cookie);
  }

  console.log(cookies);
  console.log(cookies.smallCookie);
  res.writeHead(200, {
    'Set-Cookie':['smallCookie = choco','cuteCookie = vanilla']
  });

  res.end('cookie')
}).listen(3000)

이쯤되서 재확인하는 완성코드

터미널에는 모든 쿠키의 정보와 smallCookie에 뭘 담고 있는지를 알 수 있을 것이다.

 

웹페이지를 새로고침 할 때마다 터미널에 쿠키의 값이 나타날 것이다.

 

4. 쿠키의 종류? 일회용 쿠키와 쿠키의 유통기한?

쿠키는 Session Cookies와 Permanent Cookies로 나뉜다.

Session Cookies는 위에 다룬 내용이니 넘어가고

Permanent Cookies를 다루어 보자

  res.writeHead(200, {
    'Set-Cookie':['smallCookie = choco','cuteCookie = vanilla', 
    'permanent=cookies; Max-Age=${60*60*24*30}']
  });

res.wirteHead부분을 수정했다.

작은 쿠키와 귀여운 쿠키는 무시하고 아래 permanet라는 것이 또 정의되어 있다.

 

여기에는 세미콜론 뒤에 Max-Age가 온 것을 볼 수 있는데

이는 이 쿠키의 유통기한이라고 할 수 있다.

60은 60초, 60*60은 1시간, 60*60*24는 하루, 60*60*24*30은 한 달로 볼 수 있다

(식만 봐도 얼추 이해가 될 것이다.)

 

이렇게 쿠키의 유통기한을 설정해주면 다른 쿠키들과 다르게 인터넷을 껐다가 다시 켜도 쿠키가 유통기한동안은 남아있다.

물론 유통기한이 경과하면 사라진다.

 

여기서Permanent Cookies의 특징이 드러나는데, 보시다시피 브라우저를 없애든 말든 유통기한 동안은 사라지지 않고

계속 남아있는 쿠키를 말한다.

 

 

5. 쿠키의 보안

내 쿠키는 나만 써야하므로 아무에게나 줄 수 없다.

res.writeHead부분을 수정해보자

  res.writeHead(200, {
    'Set-Cookie':['smallCookie = choco','cuteCookie = vanilla', 
    'permanent=cookies; Max-Age=${60*60*24*30}',
    'lovelySecureCookie= starCookie; Secure',
    'httpOnlyCookie=cookielove; HttpOnly']
  });

4번째 줄을 보면 아까 없던 새로운 쿠키를 정의했다.

쿠키의 이름이나 startCookie는 중요한게 아니고 중요한건 세미콜론 다음의 Secure 문자다.

5번째 줄에도 세미콜론 뒤에 HttpOnly가 있다.

 

이 두가지는 각자의 특징을 통해 쿠키의 보안을 담당하는데 이는 다음과 같다.

 

Secure의 경우

클라이언트에서 자바스크립트를 통한 쿠키 도둑질을 예방할 수 있다.

상술한 것처럼 그저 짧은 문장하나 넣어주는 것 만으로도 예방할 수 있다.

이것은 브라우저와 서버가 https로 통신하는 경우에만 브라우저가 서버에게 쿠키를 전송한다는 의미를 가지고 있다.

 

HTTP Only의 경우

브라우저에서 해당 쿠키로 접근할 수 없게 되지만 쿠키에 포함된 정보의 대부분이 브라우저에서 접근할

필요가 없기 때문에 HTTP only 는 기본적으로 적용하는 것이 좋다.

이것은 JS의 document.cookie를 이용해 쿠키에 접근하는 것을 막는 옵션이다.

 

6. 쿠키의 인증, 경로와 도메인

res.writeHead부분을 수정해보자

  res.writeHead(200, {
    'Set-Cookie':['smallCookie = choco','cuteCookie = vanilla', 
    'permanent=cookies; Max-Age=${60*60*24*30}',
    'lovelySecureCookie= starCookie; Secure',
    'httpOnlyCookie=cookielove; HttpOnly',
    'pathCookie = cocoa; Path=/cookie']
  });

마지막 줄에 페스쿠키가 추가되었다.

 

세미콜론 뒤에 페스라고 되어 있는 것을 볼 수 있는데

이것은 해당 경로(또는 그 경로의 하위)에서만 작동하게 하는 옵션이다.

즉 저 패스쿠키는

http://localhost:3000/cookie 경로나

http://localhost:3000/cookie/sub와 같은 하위 경로에서만 사용할 수 있다.

 

비슷한 기능으로 도메인이 있다.

res.writeHead부분을 수정해 보자

 res.writeHead(200, {
    'Set-Cookie':['smallCookie = choco','cuteCookie = vanilla', 
    'permanent=cookies; Max-Age=${60*60*24*30}',
    'lovelySecureCookie= starCookie; Secure',
    'httpOnlyCookie=cookielove; HttpOnly',
    'pathCookie = cocoa; Path=/cookie',
    'domainCookie = tea; Domain=o2.org']
  });

최하단에 추가된 쿠키를 보자

아마 눈치빠른 사람들은 눈치 챘겠지만

저건 특정 도메인에서만 작동하도록 하는 것이다.

 

우선은 여기까지 하고 나머지 부분은 다음편에 업로드

1. TCP와 UDP

 

소켓에 대해 알기 전에 먼저 짚고 넘어가자

이 두가지는 데이터 송수신을 위한 프로토콜이다.

 

TCP란 내가 데이터를 전달하고 상대방이 잘 전달 받았는지 확인하는 과정을 거친다.

때문에 신뢰성이 높다.

 

UDP는 내가 데이터를 전달하기만 하면 그대로 끝이다.

때문에 데이터가 옳게 전달 되었는지 알 수 없기에 신뢰성이 낮다.

이러한 특징 덕분에 전송속도가 빠르다는 장점이 있다.

 

2. 소켓

2-1. 소켓이란?

네트워크에서 데이터를 송수신하기 위해 반드시 거쳐야하는 연결부에 해당한다.

소켓은 상술한 TCP와 UDP 소켓이 있다.

주로 TCP를 쓰겠지만 가끔 빠른 전송이 필요할 때는 UDP를 쓰기도 한다.

 

2-2. 패킷이란?

소켓을 통해 송수신하는 데이터 덩어리 하나하나를 말한다.

 

2-3. 웹 소켓이란?

실시간 웹 서비스를 위해 만들어진 소켓이다.

주로 메신저 같은 곳에서 주로 사용한다.

노션과 같은 실시간 공동 편집기능에서도 사용한다.

 

2-4. socket.io?

자바스크립트를 통해 웹소켓을 사용하고자 한다면 가장 많이 쓰이는 라이브러리다.

모든 브라우저가 이걸 지원하지 않지만 그에 대비하기 위해 polling 기능을 지원한다.

 

2-5. socket.io는 웹소켓과 다른가?

그렇다.

socket.io는 웹소켓을 포함하며, 웹소켓을 지원하지 않는 곳에서도 비슷한 기능을 지원하는 라이브러리다.

 

 

3.socket.io를 써보자

 

 

우선 HTML 파일에도 이것과 관련된 코드를 작성해 주어야 한다.

 

head 영역에 다음을 추가하자

<script src="https://cdn.socket.io/socket.io-3.0.1.min.js"></script>

body 영역에는 다음을 추가하자

 

<script>
  const socket = io("ws://localhost:3000");
  socket.on("connect", () => {
    socket.send("Hello!");
  });

  socket.on("message", (data) => {
    console.log(data);
  });
</script>

자바스크립트의 app.js에는 다음을 추가하자

const io = require("socket.io")(3000, {
  cors: {
    origin: "*",
    methods: ["GET", "POST"],
  },
});

io.on("connection", (socket) => {
  console.log("새로운 소켓이 연결됐어요!");

  socket.on("message", (data) => {
    console.log(data);
  });
});

 

가장 기본적인 세팅이다.

app.js 의 가장 위 코드가 socket.io를 쓰겠다는 뜻이며

뒤의 3000은 3000번 포트를 쓰겠다는 선언이다.

 

cors는 프론트엔드 담당자와 협업하면서 에러가 발생할 수 있는 부분이다.

cors의 origin은 어떤 사용자만 접속이 가능하게 할 것인가를 정하고(별표는 모두에게 열어둔다는 의미다.)

methods는 말그대로, 어떤 메소드만 열어둘 것인가를 정한다.

 

io.on은 어떤식으로 연결할 것인가를 말한다.

socket.on은 오른쪽의 "message"라는 소켓에 들어왔을 때의 반응을 정한다.

 

 

정리하자면 맨 위부터 소켓의 포트넘버를 정의하고

어떤 사람들을 받고 어떤 요청을 받을 것인지를 정의하고

어떤식으로 연결할 것인가를 정한다고 볼 수 있다.

 

 

express모듈과 함께 쓰고 싶다면

일단 express 모듈을 설치 하고 app.js를 다음과 같이 편집하자

const express = require("express");
const { createServer } = require("http");

const app = express();
const http = createServer(app);
const io = require('socket.io')(http, {
  cors: {
    origin: "*",
    methods: ["GET", "POST"],
  },
});

http.listen(3000, () => {
  console.log("서버가 요청을 받을 준비가 됐어요");
});

3000번 포트가 들어가던 곳에 http가 들어갔고

http.listen으로 서버를 여는 것을 알 수 있다.

 

 

 

기본적인 부분은 여기까지

 

1. 도메인

1-1. 도메인

개발의 목적은 사업 등을 하면서 필요한 과정을 자동화하거나 개선하기 위한 것이다.

온라인 서점으로 예를 들자면

서점의 일을 온라인으로 해결하고자 하니 온라인 서점은 도메인, 즉, 문제의 영역이 된다.

그리고 그 도메인은 다시 하위 도메인으로 나누어 진다.

 

온라인서점이 기능을 하기 위해선

주문, 회원, 혜택, 결제, 배송, 정산, 리뷰 등의 기능이 필요하다.

이것들이 바로 하위 도메인이 된다.

 

1-2. 도메인 모델

특정 도메인을 개념적으로 정리한 것을 도메인 모델이라고 한다.

이런식으로 모델화를 하면 도메인의 작동을 예상하고 발생하는 문제 등을 빠르게 파악할 수 있다.

 

1-3. 엔티티

실제 DB테이블과 연관되어 있는 핵심 클래스로

엔티티를 기준으로 테이블이 생성되고 DB스키마가 변경된다.

 

엔티티는 요청이나 응답으로 전달하는 클래스로 쓰면 안 된다.

엔티티 내부의 속성이변경되더라도 여전히 동일한 엔티티로 남아있다.

 

 

 

2. 아키텍처 패턴

2-1. 아키텍처 패턴

소프트웨어 구조를 구성하기위한 가장 기본적인 토대라고 할 수 있다.

저장소 패턴, 서비스 계층 패턴, 작업단위 패턴, 애그리게이트 패턴 등이 있으며 이 외에도 다양하게 있다.

 

아키텍처 패턴을 도입하기 전에 고민해야할 것이 있는데 이는 다음과 같다

1. 아키텍처 패턴을 사용하면서 발생하는 이익과 비용에 대해 확실한 이유가 있어야 한다.

2. 해당하는 아키텍처 패턴을 채택했을 때 어떤 장단점이 있는지 명확하게 인지해야 한다.

3. 여러계층을 추가하기 위해 들이는 노력과 시간을 투자할 만한 가치가 있을 정도로 어플리케이션과 도메인이 복잡한 경우에만 도입하는 것이 좋다.

 

2-2. 계층형 아키텍처 패턴

가장 흔하게 사용되고 있는 패턴 중 하나로 단순하고 대중적이면서 비용도 적어서 사실상 표준 아키텍처이다.

어떤 아키텍처 패턴을 도입할까 결정장애가 올 때는 이걸 고르는게 현명할 수 있다.


계층형 아키텍처는 각 계층별로 의존성이 낮아서 모듈을 교체하더라고 코드수정이 용이하다.

 

계층이야 여러개가 있을 수 있지만 이번에 알아 볼 것은 3계층 아키텍처이다

3계층 아키텍쳐는 프레젠테이션, 비지니스, 데이터 엑세스 계층으로 나뉜다

3계층 아키텍처는 다음 3가지의 처리과정을 이용하여 구현한다.

 

컨트롤러: 어플의 가장 바깥으로 요청과 응답을 담당한다. 일종의 고객 대응 직원같은 느낌

서비스: 어플의 중간부분으로 비지니스 로직이 수행되는 부분임과 동시에 실제 중요한 작동이 많이 일어난다.

레퍼스토리: 어플의 가장 안쪽 부분으로 DB와 맞닿아 있어서 실제 DB의 데이터를 사용하는 계층이다.

 

이것은 다음과 같은 순서대로 로직이 수행된다.

1. 사용자가 어떤 것을 요구를 한다. (클라이언트가 요청을 보낸다.)

2. 요구사항에 적합한 담당자를 부른다. (요청URL을 알맞는 컨트롤러가 받는다.)

3. 담당자는 알맞는 서비스 담당자를 부른다. (컨트롤러가 서비스를 호출한다.)

4. 서비스 담당자는 저장소에서 가져온 물건을 가공하여 담당자에게 준다. (서비스는 저장소에서 가져온 데이터를 가공해 컨트롤러에게 넘긴다.)

5. 담당자가 사용자에게 요구한 물건을 넘긴다. (컨트롤러가 서비스의 결과물을 클라이언트에게 전달한다.)

 

위의 특성을 보면 컨트롤러는 말 그대로 고객대응 직원이고 서비스가 중간에서 핵심적인 것을 한다는 것을 알 수 있다.

실제로 서비스코드는 현업에서 엄청나게 비대해진다고 한다.

레퍼스토리는 DB를 지키는 저장소 관리자 같은 느낌이라서 서비스가 DB의 정보가 필요할 때 레퍼스토리에게

요청을 보낸다고 한다.

 

대략적인 관계도

 

 

일단은 여기까지

생각보다 공부해야할 내용이 많다

 

'Framework > Node.js' 카테고리의 다른 글

Node.js 공부정리 - 쿠키 - 복습 - 001  (0) 2022.10.26
Node.js 공부정리 - Socket.io  (0) 2022.10.14
Node.js 공부정리 - 미들웨어  (0) 2022.10.09
Node.js 공부정리 - JWT  (1) 2022.10.08
Node.js 공부정리 - 쿠키와 세션  (1) 2022.10.08

1. 미들웨어란?

모든 웹서버엔 미들웨어가 있다

 

본인이 만든 app.js 파일에 각종 부가적인 기능을 붙인다고 생각하면 된다.

 

 

팩토리가 서버라면 기계실은 미들웨어다.

다만, 차이점이 있다면 팩토리는 기계실을 하나만 달지만

서버는 미들웨어를 주구장창 달아버릴 수 있다는 것이다.

 

물론 여러가지 기능을 달아줄 순 있지만

미들웨어는 주로 요청과 응답에 대한 처리만을 담당하는 경우가 많다.

app.use(express.urlencoded({ extended: false }));
app.use(express.json());

이 두가지는 form -urlencoded라는 규격의 body데이터를 손쉽게 코드에서 사용할수 있게 도와준다.

json은 JSON이라는 규격의 데이터를 손쉽게 코드에서 사용할 수 있게 도와주는 미들웨어다.

 

이외에도 많지만 일단 두가지를 사용해보자

 

 

2. 미들웨어 작성

 

app.use((req, res, next) => {
  // 필요한 코드
});

요런식으로 작성한다.

req와 res는 요청과 응답에 대한 정보가 담긴 객체이고 next가 새로보이는데 이것은 다음 스택으로 정의된 미들웨어를 호출하는 것이다.

 

 

대략 요런식이다.

요청이 오면 미들웨어에서 처리하고 그 결과를 리스폰으로 보내고

다음 미들웨어를 실행하고.... 이걸 계속 반복한다.

 

app.use((req, res, next) => {
    console.log('첫번째 미들웨어');
    next();
});

app.use((req, res, next) => {
    console.log('두번째 미들웨어');
    next();
});

app.use((req, res, next) => {
    console.log('세번째 미들웨어');
    next();
});

// print: 첫번째 미들웨어
// print: 두번째 미들웨어
// print: 세번째 미들웨어

이렇게 미들웨어가 여러개 있으면 순차적으로 실행한다.

그러나 next()가 있어야 진행하며 없으면 진행하지 않는다.

 

라우터는 미들웨어 기반으로 구현된 객체이므로 미들웨어와 동일한 방식으로 작동한다.

 

app.use(Middleware)
app.use('/api', Middleware)
app.post('/api', Middleware)

위에서부터 순서대로

모든 요청에서 미들웨어가 실행

api로 시작하는 요청에서 미들웨어를 실행

api로 시작하는 POST 요청에서 미들웨어를 실행

 

 

1. JWT란?

로그인을 구현하기 위해서 쓰는 기술이다.

변조가 불가능하고 어디서나 복호화는 아무대사나 가능하여 어디서나 열어볼 수 있다.

때문에 JWT안에는 중요하지 않은 정보를 위주로 담는다

header, payload, signature 총 3가지 데이터를 포함한다.

때문에 JWT형식으로 변환 된 데이터는 항상 2개의 .이 포함된 데이터여야 한다.

 

 

API를 요청할 때마다 이 JWT를 같이 보내게 된다.

그리고 이 JWT가 정상적이여야지 서버가 응답을 해준다.

 

이걸 이용하면 쿠키랑 세션이 필요 없어지는데

쿠키랑 세션은 서버쪽이든 클라이언트쪽이든 뭔가 데이터를 가지고 해야하는데

JWT에선 토큰만 항상 보내주면 된다.

 

 

2. JWT를 써보자!

우선 터미널을 통해 설치하자

npm i jsonwebtoken -S를 입력 (대문자 확인)

const jwt = require("jsonwebtoken")

const token = jwt.sign({test: true}, 'my-secret-key')

console.log(token)

이제 이걸 입력하고 출력한다.

여기서 my-secret-key는 말그대로 비밀키이기 때문에 절대로 누출되선 안 된다.

 

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0Ijp0cnVlLCJpYXQiOjE2NjUyMjUzOTB9.kP9gg8gFpoSqYcorNTw4elEyKWuf4M-ipa86mal0Ma4

실행하면 대충 요런 값이 찍힌다.

이것을 JWT 복호화 사이트에서 복호를 하면 다음과 같이 나온다.

//HEADER
{
	"alg": "HS256"
	"typ": "JWT"
}

//PAYLOAD
{
	"test": true,
	"iat": 1665225390
}

보시다시피 복호화는 어디에서나 할 수 있다.

단, SIGNATURE은 유효하지 않다고 뜬다.

 

가장 아래에 이것도 같이 뜨는데, 저기 입력칸에다가 my-secret-key를 입력하면 유효하다고 뜨니 주의해야 한다.

 

이번엔 VSCODE에서 복호화를 해보자

 

const jwt = require("jsonwebtoken")
const token = jwt.sign({test: true}, 'my-secret-key')
const decoded = jwt.verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0Ijp0cnVlLCJpYXQiOjE2NjUyMjUzOTB9.kP9gg8gFpoSqYcorNTw4elEyKWuf4M-ipa86mal0Ma4", "my-secret-key")

console.log(decoded)

//결과
//{ test: true, iat: 1665225390 }

 

보시다시피 내용이 아주 잘 나온다.

특히 마지막에 키값까지 넣었기 때문에 아무런 문제 없이 잘나온다.

 

때문에 키값을 정할 때는 아주 복잡하게 하는 것이 좋다.

 

정리하자면 발급과 검증은 서버만이 할 수 있지만

그 외 내용을 보는 것은 쉽게 복호화를 할 수 있으니

비중요 정보만을 담는것을 추천한다고 할 수 있다.

 

이것을 잘 사용하기 위해선 아래를 터미널로 입력해

해당 패키지를 설치하도록 하자

npm init -y
npm install express jsonwebtoken cookie-parser -S

 

 

 

 

 

1. 쿠키

인터넷을 이용할때, 로그인 정보, 장바구니 정보등을 저장할 필요가 있는데

그 정보를 서버가 아닌 사용자 컴퓨터에 저장한다.(이게 쿠키다)

때문에 보안에 약하지만 서버입장에선 본인 일을 떠넘긴거니 편하다.

userId=user-1321;userName=sparta

=를 기준으로 왼쪽은 name, 오른쪽은 value 의 형태로 관리 된다.

각 쿠키마다 관리는 ;을 기준으로 관리한다.

 

2. 세션

쿠키를 기반으로 구성된 기술

사용자의 인증여부를 서버가 가지기 때문에 보안성은 좋지만 사용자들이 많아지게 되면 서버입장에선 빡세진다.

 

3. 쿠키 만들기

/set-cookie라는 API를 호출했을 때, 이름은 name, 값은 sparta인 쿠키를 만들어보자

app.get("/set-cookie", (req, res) => {
  const expire = new Date();
  expire.setMinutes(expire.getMinutes() + 60); // 만료 시간을 60분으로 설정합니다.

  res.writeHead(200, {
    'Set-Cookie': `name=sparta; Expires=${expire.toGMTString()}; HttpOnly; Path=/`,
  });
  return res.status(200).end();
});

 

1번째 줄: 메서드는 get, API는 set-cookie다.

2번째 줄: 시간을 하나 만들었다. 이 시간은 쿠키의 유지시간이다.

3번째 줄: setMinutes를 통해 시간을 60분으로 조정했다

 

5번째 줄: writeHead라는 메서드로 쿠키를 만든다.

      200이라는 숫자는 HTTP status code로 200은 제대로 잘 작동했음을 의미한다.

6번째 줄: 키 값은 Set-Cookie, 이름은 name, 값은 sparta으로 한다.

      Expires부터는 쿠키에 속성을 지정해주는 문이다.

      Expires는 만료기간이 언제까지인가 정한다. 뒤에 보면 2번째 줄에 선언한 expire가 있다. toGMTString은 그냥 쿠키 선언을 하기 위한 형식으로 변환해 주는 것이라고 생각하면 된다.

      HttpOnly는 Http에서만 사용할 수 있다는 뜻이다.

      Path는 어디서부터(경로) 사용할 수 있는가 를 정해준다.

마지막 줄: 상태가 제대로 돌아갔음을 응답하고 end를 통해 API를 종료한다.

 

app.get("/set-cookie", (req, res) => {
  const expires = new Date();
  expires.setMinutes(expires.getMinutes() + 60); // 만료 시간을 60분으로 설정합니다.

  res.cookie('name', 'sparta', {
    expires: expires
  });
  return res.status(200).end();
});

이건 바로 위와 동일하나 좀더 간결해서 보기 좋다.

 

 

4. 쿠키 접근하기

 

쿠키는 일반적으로 req.headers.cookie에 들어 있다.

req.headers는 클라이언트가 요청한 Request의 헤더를 의미한다.

app.get("/get-cookie", (req, res) => {
  const cookie = req.headers.cookie;
  console.log(cookie);                                 // name=sparta
  return res.status(200).json({ cookie });
});

2번째 줄: 쿠키를 가져와 cookie라는 변수에 넣었다.

 

자 이걸 console.log에 찍으면 오른쪽 주석처럼 쿠키가 나온다.

실질적으로 쿠키를 사용하기 위해서는 =도 없애주고, 쿠키가 다수라면 ;으로 또 분리를 해줘야 한다.

이런 불편함을 없애기 위해 미들웨어를 써보자

 

5. cookie-parser 미들웨어 적용하기

cookie-parser를 쓰면 req.cookies라는 객체로 만들어 준다

이렇게 객체가 되면 왼쪽은 이름, 오른쪽은 값으로 이루어진 객체가 된다.

 

app.use(cookieParser());

우선, cookieParser를 쓰기 위해 이 코드로 전역 미들웨어를 설정하자

 

const cookieParser = require('cookie-parser');
app.use(cookieParser());

app.get("/get-cookie", (req, res) => {
  const cookie = req.cookies;
  console.log(cookie); // { name: 'sparta' }
  return res.status(200).json({ cookie });
});

쿠키 접근하기에서 다루었던거랑 동일한 코드이나 위에 뭔가 더 늘었다

cookie-parser를 씀으로써 몇가지 불편한 사항이 없어진 것이다.

당장은 이해가 잘 안되지만 편의성 개선 기능이니 쓰다보면 이해가 될 것이다.

 

6. 세션 만들기

쿠키는 서버를 재시작하거나 새로고침해도 로그인이 유지가 된다.

그 계산을 모두 사용자 컴퓨터가 하기 때문이다.

물론 이건 서버입장에선 엄청난 위험부담 행위다.

서버가 뻗은 사이, 쿠키는 얼마든지 조작되거나 노출될 수 있기 때문이다.

이를 막기 위해 우리는 세션을 사용한다.

세션을 만들어보자

 

let session = {};
app.get('/set-session', function (req, res, next) {
  const name = 'sparta';
  const uniqueInt = Date.now();
  session[uniqueInt] = { name };

  res.cookie('sessionKey', uniqueInt);
  return res.status(200).end();
});

1번째 줄: 우선 세션을 쓰겠다고 선언했다

2번째 줄:

3번째 줄: 일단 이름은 sparta로 한다.

4번째 줄: 현재 시간을 나타내는 변수를 사용하며 이름은 uniqueInt이다.

5번째 줄: 1번째 줄에서 선언한 그 변수에 4번째 줄에서 선언한 시간을 "키"로 넣는다. 3번째 줄에서 선언한 이름도 넣는다.

           이로써 이름과 시간이 들어감에 따라 유니크한 세션이 되었다.

7번째 줄: 1번째 줄의 session에 할당된 객체를 사용자에게 전달할 때는 sessionKey라는 키로 전달된다.

 

이로써 전달받은 쿠키에는 아무런 정보가 없고 그저 서버가 사용자를 구분하는 용도로만 사용된다.

 

좀더 알기쉽게 설명하자면

사용자에겐 sessionKey라는 문자열과 uniqueInt의 변수값이 전달된다.

그리고 이것을 다시 사용자가 서버에게 전달하면

서버는 uniqueInt 값이 무엇인지 확인하고 그것을 바탕으로 사용자를 구분한다.

일종의 주민번호 같은 개념인듯 하다

 

get-session API

app.get('/get-session', function (req, res, next) {
  const { sessionKey } = req.cookies;
  const name = session[sessionKey];
  return res.status(200).json({ name });
});

req.cookies를 통해서 sessionKey에 해당하는 쿠키를 가지고 오고

가지고 온 sessionKey를 session에서 찾는다.

만약 존재한다면 그것을 name변수에 할당한다.

그리고 return을 통해서 실제 사용자에게 전달해 준다.

만약 없다면 에러나 null값을 전달하던지 등을 지정해주어야 한다.

 

 

 

목차

 

1. 시작하기

   1-1. 시작하기

   1-2. 기본코드

   1-3. API Client

2. 라우팅

   2-1 라우트 사용

3. Module

4. Request와 Response

   4-1. Express의 req객체와 res 객체

5. API의 REST API

6. Package Manager

   6-1. npm

      6-1-1. npm의 명령어

      6-1-2. node_modules

   6-2 yarn

   6-3 package.json

   6-4 package-lock.json

7. MongoDB -> mongoose

   7-1. mongoose 용어


1. 시작하기

1-1. 시작하기

 

설치과정은 전부 생략

시작할 폴더에 app.js를 만들고 다음을 입력하여 json을 생성해준다.

npm init -y

 

참고로 맥은 앞에 $를 붙이는 듯 하다

 

json이 만들어지면 다음을 입력하여 Express.js 프레임워크를 설치하자

npm i express

 

1-2. 기본코드

app.js에 다음을 입력하면 localhost:3000 을 사용할 수 있게 된다.(단, 실행을 해주어야 한다)

이때, 3000번 이 사용중인지 아닌지 확인하고 사용중이면 3000을 끄던가 번호를 바꾸던가 하자

const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(port, () => {
  console.log(port, '포트로 서버가 열렸어요!');
});

 

이제 터미널에 node app 이라고 치면 실행되고 이제 localhost:3000 을 사용할 수 있다.(실행시 시간이 좀 걸린다.)

 

끄는 법은 터미널에 Ctrl + C를 입력하는 것이다.

 

1-3. API Client

이것은 우리가 작성한 API의 요청을 확인하거나 테스트할때 필요하다

postman, insomnia 등 여러 API가 있지면 여기서는 Thunder Client를 사용한다.

 

아무튼 이걸 설치하자

 

 

VS Code 에서 왼쪽 아래 버튼을 눌러주자

 

이후 검색창에다 Thunder Client를 검색하고

 

보라색 번개모양 아이콘이 있는 가장 위의 것을 설치하자

 

실치가 완료되면 VS Code에 번개모양 아이콘이 나타날 것이다.

 

 

 

 

 

 

 

서버가 켜져있는 상태에서 번개모양을 클릭하고 최상단에 New Request를 클릭

이런 화면이 나오는데 맨 위의 URL 입력란의 좌측 리스트를 GET으로 해놓고 URL에 localhost:3000 를 입력하고

우측의 send 버튼을 누르면 hello world가 출력된다.

 

주의사항

맥의 경우 http://localhost:3000/ 이렇게 입력해야 하는 모양이다.

하지만 윈도우의 경우 localhost:3000 라고 입력해야 정상 작동한다.

 

이 창에서 Collections에는 API 목록을 정리해서 사용할 수 있다.

여러가지 API를 그룹화 시킬 수 있다.

 

 

Env는 여러번 사용되는 값들을 환경변수로 설정할 때 사용하는데

Token, URL, 개인 키 등 다양한 자격증명을 저장 및 사용할 수 있다.

 

 

 

 

 

 


2. 라우팅

라우팅이란 클라이언트의 요청에 대응해 응답하는 방식을 말한다

라우터란 클라이언트의 요청을 쉽게 처리할 수 있게 도와주는 Express.js의 기본 기능중 하나다.

 

일반적으로 라우터의 구조는 아래와 같다

router.METHOD(PATH, HANDLER);

router: 라우터를 정의한다.

METHOD: HTTP의 메소드를 나타낸다. (get, post, put, delete 등등)

PATH: API를 사용하기 위한 경로

HANDLER: 라우트가 일치할 때 실행되는 함수

 

2-1 라우트 사용

 

routes라는 폴더를 만들고 goods.js 라는 파일을 만든다.

const express = require('express');
const router = express.Router();

기본코드를 입력하고

router.get('/', (req, res) => {
	res.send('this is home page');
});

router.get('/about', (req, res) => {
	res.send('this is about page');
});

예시 코드를 입력하고

module.exports = router;

작성한 Router를 app.js에서 쓰기 위해 하단에 내보내주는 코드를 추가한다.

 

그리고 app.js로 돌아와 하단에 코드를 추가한다.

const goodsRouter = require("./routes/goods.js");
app.use("/api", [goodsRouter]);

 

이제부터 localhost:3000/ 뒤에 /api로 시작되는 주소는 routes폴더에 있는 goods.js에 있는 router미들웨어를 통해 처리된다.

 

이제 아래 주소로 가보면 this is about page라는 문구가 뜰 것이다.

http://localhost:3000/api/about

 

완성코드는 다음과 같다.

app.js의 경우

const express = require('express');
const app = express();
const port = 3000;
const goodsRouter = require("./routes/goods.js");

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(port, () => {
  console.log(port, '포트로 서버가 열렸어요!');
});

app.use("/api", [goodsRouter]);

goods.js 의 경우

const express = require('express');
const router = express.Router();

router.get('/', (req, res) => {
	res.send('this is home page');
});

router.get('/about', (req, res) => {
	res.send('this is about page');
});

module.exports = router;

3. Module

모듈이란 분리된 JS 파일이고 특정한 기능을 가진 여러개의 함수와 변수의 집합

모듈로 만들면 다른 프로그램에서 해당 모듈을 재사용할 수 있다.

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

 

expor 명령어를 변수나 함수 앞에 붙이면 외부 모듈에서 접근할 수 있게 된다.

import, require를 사용하면 외부 모듈의 기능을 가져올 수 있다.

이 둘은 기능은 같지만 모듈 관리 방식이 조금 차이가 난다.

 

modules 폴더를 만들고 math.js 파일을 만든 뒤 다음을 적어보자

function add(a, b) {
 return a + b;
}

module.exports = add;

맨 위는 math.js의 기능이다.

 

그 아래는 작성한 값을 다른 모듈로 보내주기 위한 코드다

 

모듈 폴더 안에 run.js를 만들고 math.js의 add 함수를 사용하도록 지시한다.

const add = require("./math");
console.log(add(3, 4));

 


4. Request와 Response

리퀘스트는 클라이언트가 서버에게 전달하는 것을 뜻하고

리스폰 그 반대라고 생각하면 된다.

 

Node의 서버 모듈에는 대표적으로 http 모듈과 Express모듈이 있다.

Express모듈은 http를 확장한 것이다.

http가 레이스만 뽑는다면 Express는 드랍쉽도 뽑게 해준다고 보면 된다.

때문에 다들 Express만 쓴다.

 

4-1. Express의 req객체와 res 객체

칠한 것은 중요한 거다.

req 객체
- req.app : req 객체를 통해 app 객체에 접근할 수 있습니다.
- req.ip: 요청한 Client의 ip 주소가 담겨 있습니다.
- req.body: Request를 호출할 때 body로 전달된 정보가 담긴 객체입니다.
    - body-parser Middleware를 이용하여야 해당 객체를 사용할 수 있습니다.
- req.params: 라우터 매개 변수에 대한 정보가 담긴 객체입니다.
- req.query: Request를 호출할 때 쿼리 스트링으로 전달된 정보가 담긴 객체입니다.
- req.cookies: Request를 호출할 때 Cookie 정보가 담긴 객체입니다.
    - cookie-parser Middleware를 이용하여야 해당 객체를 사용할 수 있습니다.
- req.get(*Header*): 헤더에 저장된 값을 가져오고 싶을 때 사용합니다.

res 객체
- res.app : res 객체를 통해 app 객체에 접근할 수 있습니다.
- res.status(코드) : Response에 HTTP 상태 코드를 지정합니다.
- res.send(데이터) : 데이터를 포함하여 Response를 전달합니다.
- res.json(JSON) : JSON 형식으로 Response를 전달합니다.
- res.end() : 데이터 없이 Response를 전달합니다.
- res.direct(*주소*) : 리다이렉트할 주소와 함께 Response를 전달합니다.
- res.cookie(*Key, Value, Option*) : 쿠키를 설정할 때 사용합니다.
- res.clearCookie(*Key, Value, Option*) : 쿠키를 제거할 때 사용합니다.

 

 


5. API의 REST API

선술한 내용과는 달리 이번엔 자세히 알아보자

API는 애플리케이션 끼리 연결해주는 매개체이자 약속이라고 볼 수 있다.

모스신호는 점신호와 선신호로 약속을 잡아서 사용했는데 이러한 약속이 API라고 할 수 있다.

 

REST API란 사람이 봐도 이해하기 쉬운 표현이라고 할 수 있다.

그러니까 API를 그먼씹이 아닌 사람도 쉽게 알아볼 수 있게 번역(?) 한 것이라 봐야겠다.

 

 

 


6. Package Manager

 

패키지 매니저란 패키지를 안전하고 쉽게 다루기 위한 툴이다.

이것은 타인의 코드 가져오거나 자신의 코드를 배포하게 할 수 있다.

Node.js에서 대표적으로 사용하는 패키지 매니저는 npm과 yarn이 있다.

 

6-1. npm

npm이란 JS에서 사용하는 패키지(모듈) 관리자다.

npm을 통해 라이브러리를 쉽게 설치하고 버전을 관리하고, 제거할 수 있다.

npmjs.com에서 검색을 통해 패키지 설치가 가능하다

누구나 새로운 패키지를 등록할 수 있다.

Node Package Manager의 약자이지만 Node.js와 관계 없이 프론트엔드에서만 사용 가능한 JS 패키지도 모두 있다.

 

6-1-1. npm의 명령어

npm init: package.json 파일을 만들 때 사용, 새로운 프로젝트나 패키지를 만들 때 사용, 다양한 정보 설정 가능

npm install express: install을 i로 줄여 쓸 수 있음, express는 설치하고자 하는 모듈의 이름이다.

      띄어쓰기를 쓰면 동시에 여러개를 설치할 수 있다. ex: npm install mongoose express jest 등등

      npm install 이라고만 입력하면 package.json 기반으로 node_modules에 명시된 모듈들을 설치해 준다.

 

6-1-2. node_modules

package.json 파일 내용을 기반으로 npm install 명령어를 통해 설치된 모듈 파일들이 모여 있는 곳

 

6-2 yarn

npm의 대체제로 페이스북이 출시했다.

npm에서 부족한 부분을 보완하여 편리한 기능이 추가되었고, 더욱 빠른 속도로 패키지를 관리할 수 있다.

 

6-3 package.json

아마 이 파일은 위의 절차를 마쳤으면 vs code에도 있을 것이다.

설치한 패키지들의 버전을 관리할 때 사용하는 파일이다.

패키지 버전관리 외에도 프로젝트명, 작성자, 라이센스 정보 등 다양한 메타데이터들을 기록할 수 있다.

npm과 yarn 모두 동일한 package.json 파일을 참조한다.

 

6-4 package-lock.json

마찬가지로 이 파일도 vs code에 있을 탠데

node_modules에 들어있는 패키지들의 버전과 의존 관계가 기록되어 있다.

npm으로 패키지를 설치, 수정, 삭제할 때마다 패키지들의 의존관계를 파일에 저장한다.

 

 

7. MongoDB -> mongoose

 

MongoDB는 가장 인기있는 NoSQL형 DB로 모든 데이터가 JSON형태로 저장된다.

 

VS-Code에서 MongoDB를 연결해 사용하려면 다음과 같은 명령어를 입력해 mongoose를 설치해야 한다.

npm install mongoose

시간이 꽤나 걸린다.

 

7-1. mongoose 용어

문서: MongoDB가 가지고 있는 데이터 하나하나를 문서라고 한다. id값을 가지고 있으며 Key-Value쌍으로 이루어 져있다.

컬렉션: JSON형식의 여러가지 문서를 보유한 것, 관계형 데이터베이스의 Table과 동일한 역할을 한다.

스키마: 컬렉션에 있는 문서에 들어간 값의 종류를 정의, null, String, Nunber, Date, Buffer, Boolean, Array 등이 있다

모델: 데이터베이스에 데이터를 저장해줄때 데이터의 구조를 담당한다. 문서를 생성할 때 사용

 

 

 

 

 

 

 

 

 

 

 

 

 

+ Recent posts