Design-loving front-end engineer
Ryong
Design-loving front-end engineer
전체 방문자
오늘
어제
    • Framework
    • React
      • Concept
      • Library
      • Hook
      • Component
      • Test
    • NodeJS
    • Android
      • Concept
      • Code
      • Sunflower
      • Etc
    • Flutter
      • Concept
      • Package
    • Web
    • Web
    • CSS
    • Language
    • JavaScript
    • TypeScript
    • Kotlin
    • Dart
    • Algorithm
    • Data Structure
    • Programmers
    • Management
    • Git
    • Editor
    • VSCode
    • Knowledge
    • Voice
Design-loving front-end engineer

Ryong

[ Node.js ]  맛보기
NodeJS

[ Node.js ] 맛보기

2022. 5. 31. 10:10

⚪  app.js

 
import express from "express";
import cors from "cors";
import bodyParser from "body-parser";
import "dotenv/config";
import favicon from "serve-favicon";
import path from "path";

import defaultRouter from "./router/index.js";
import userRouter from "./router/user.js";
import tokenRouter from "./router/token.js";

const app = express();

// favicon
const __dirname = path.resolve();  // 현재 경로를 받아오기 위함(app.js가 있는 위치)
app.use(favicon(path.join(__dirname, "../home/build/favicon.ico")));

// public
// CSS 파일 및 JS 파일과 같은 정적 파일 경로를 전달하여 직접적인 파일을 제공하는데 사용된다.
app.use(express.static("../home/build"));

// cors
app.use(cors({
    // 특정 도메인에만 cors 허용
    origin : "http://localhost:3000", // default react server
    credentials : true,
}));

// body parser
// req.body의 json을 해석하기 위함
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended : false}));

// port
app.set("port", process.env.PORT || 8081);

// routing
// 3️⃣ 라우팅 객체를 app 객체에 등록
app.use("/", defaultRouter);
app.use("/user", userRouter);
app.use("/token", tokenRouter);

// server start
app.listen(app.get("port"), () => {
    console.log(`${app.get('port')} 포트에서 대기 중입니다.`);
})

 

🔴  express란?

  🔊  Node.js 웹 애플리케이션 프레임워크

  🔊  스크립트로 서버를 구축하고, 서버에서 스크립트가 작동되도록 해주는 런타임 플랫폼

 

🟠  path란?

  🔊  node의 내장모듈로써 스크립트에서 폴더나 파일의 경로를 쉽게 문자열로 만들어주는 유틸리티 모듈

path.join

  🔊  인자로 받은 경로들을 하나로 합쳐서 문자열 형태로 path를 리턴한다.

 
path.join('/a', 'b', 'c/d');  // '/a/b/c/d'

path.resolve

  🔊  맨 오른쪽부터 왼쪽으로 경로인자들을 합쳐나가며, 합쳐 나가는 중 /를 만나면 절대 경로로 인식해서 나머지 경로 인자들을 무시한다.

 
path.resolve('/foo/bar', './baz');  // 'foo/bar/baz'

path.resolve('foo/bar', '/tmp/file/');  // 'tmp/file'

 

🟡  app.use란?

  🔊  미들웨어 기능을 마운트하거나 지정된 경로에 마운트하는데 사용된다.

  🔊  app.get(), app.post() 등과 달리 요청 URL을 지정하지 않아도 사용할 수 있으며, 해당 경우에 URL에 상관없이 매번 실행된다.

 

🟢  cors란?

  🔊  자신이 속하지 않은 다른 도메인, 다른 프로토콜, 다른 포트에 있는 리소스를 요청하는 cross-origin HTTP 요청 방식이다.

  🔎  왼쪽의 웹사이트는 domina-a.com 서버로 구동되고 있다. 파란색 이미지는 구동 중인 도메인과 동일한 서버로부터 자료를 요청하지만, 빨간색 이미지는 다른 도메인에 자료를 요청하고 있는 상황이며, 이런 상황이 cors이다.

  🔎  서버는 기본적으로 cors 방식을 제한해둔다. 임의의 웹사이트들이 악의적으로 request를 보내어 특정 서버의 세션을 탈취하거나 무리가 가는 행위를 할 수도 있기 때문이다.

 

🔵  bodyParser.urlencoded이란?

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

  🔊  클라이언트에서 post로 보내준 데이터들을 자동으로 파싱해주는 body-parser 모듈을 사용하면 같이 설정해줘야 하는 설정이다.

  🔊  extended 옵션이 true일 경우, 객체 형태로 전달된 데이터 내에서 또 다른 중첩된 객체를 허용하며, false는 허용하지 않는다는 의미이다.

  🔊  false 값일 시 node.js에 기본으로 내장된 queryString, true 값일 시 따로 설치가 핑료한 npm qs 라이브러리를 사용한다.

 

⚪  router / index.js

 
import express from "express";
import path from "path";

const __dirname = path.resolve();
// 1️⃣ 라우터 객체 참조
const router = express.Router();
// 2️⃣ 라우팅 함수 등록
router.get("/", (req, res) => {
    // 파일을 옥텟 스트림의 형태로 전송
    res.sendFile(path.join(__dirname, "/index.html"));
})

router.post("/test", (req, res) => {
    console.log(req.body);
    // 다양한 유형의 응답을 전송
    res.send(req.body);
})

export default router;

🔴  미들웨어

  🔊  미들웨어는 요청에 대한 응답 과정 중간에 껴서 동작하며 작동하는 경로를 선택할 수 있으며, req, res, next를 인자로 가진 함수이다.

  💡  req : 클라이언트에서 오는 요청 정보가 담긴 객체 (클라이언트 -> 서버로 받는 내용)

  💡  res : 서버가 응답해주는 응답 정보가 담긴 객체 (서버 -> 클라이언트로 보낼 내용)

  💡  next : 현재 미들웨어에 이어서 다음 미들웨어를 실행한다. 요청-응답 주기를 종료하지 않은 상태에서 next()를 실행하지 않으면 해당 요청은 정지된 채로 방치된다.

애플리케이션 레벨 미들웨어

  🔊  미들웨어를 app 인스턴스에 바인딩하는 미들웨어이다.

 
const app = express();

  🔊  app.use()나 app.METHOD() 함수를 이용해 미들웨어를 사용할 수 있다.

라우터 레벨 미들웨어

  🔊  router 인스턴스에 미들웨어가 바인딩 되는 것이며, 이 외에는 애플리케이션 레벨 미들웨어와 차이가 없다.

 
const router = express.Router();

  🔊  router.use()나 router.METHOD() 함수를 이용해 미들웨어를 사용할 수 있다.

 
// 마운트 경로가 없는 미들웨어 함수는 앱이 요청을 수신할 때마다 실행된다.
router.use((req, res, next) => {
    console.log('Time: ', Date, now());
});
// /user/:id 경로에 대한 모든 유형의 HTTP 요청에 대해 실행된다.
router.use('/user/:id', (req, res, next) => {
    console.log('Request Type: ', req.method);
});
// /user/:id 경로에 대한 GET 유형을 처리한다.
router.get('/user/:id', (req, res, next) => {
    res.send('USER');
});
 
router.get('/pages/:id', (req, res, next) => {
    // pages id가 0이면 'regular'가 아닌 'special'로 넘어감
    if (req.params.id == 0) next('route');
    // pages id가 0이 아니라면 'regular'로 넘어감
    else next();
}, (req, res, next) => {
    res.send('regular');
});

// pages id가 0일 때 넘어올 미들웨어
router.get('/pages/:id', (req, res, next) => {
    res.send('special');
}

 

  🔎  next()는 라우터 미들웨어 스택의 다음 라우터로 이동하며, next('router')는 라우터 미들웨어 스택을 벗어난 다음 라우터로 제어가 넘어가게 한다.

  🔎  next('route')가 아닌 문자열이 next 함수의 인수로 입력될 경우, 모두 다 오류 처리 미들웨어로 처리된다.

 
router.use((err, req, res, next) => {
    console.error(err.stack);
});

 

⚪  data / data.js

 
const userData = [
    {
        id : "test",
        password : "1234",
    }, 
    {
        id : "mzrnd",
        password : "1234",
    }
]

export const has = (id, password) => {
    for(const user of userData) {
        if(user.id === id+"" && user.password === password+"") {
            return true;
        }
    }
    return false;
}
export const add = (id, password) => {
    userData.push({id, password});
}

 

🔴  forEach vs for ...in vs for ...of 차이점

  🔊  forEach : Array, Map, Set 등에 사용할 수 있으며, 배열의 요소들을 반복하여 작업을 수행할 수 있다.

 
var items = ['item1', 'item2', 'item3'];

items.forEach(item => {
    console.log(item);
};
// item1, item2, item3

  🔊  for ...of : 반복되는 객체의 value에 접근할 수 있다.

 
var iterable = [10, 20, 30];

for (let value of iterable) {
    console.log(value);
}
// 10, 20, 30

  🔊  for ...in : 반복되는 객체의 key에만 접근할 수 있으며, value에 접근하려면 인덱스로 접근해야 한다.

 
var obj = {
    a: 1,
    b: 2,
    c: 3
};

for (let prop in obj) {
    console.log(prop, obj[prop]);
}
// a 1, b 2, c 3

 

⚪  router / user.js

 
import express from "express";
import { has, add } from "../data/data.js";
import { verifyToken, signNewToken } from "../middlewares/jwt.js";

const router = express.Router();
router.post("/", async (req, res)=>{
    console.log(`${req.ip} 에서 보낸 request :`);
    console.log("/user", req.body);
    try {
        const result = await has(req.body.id, req.body.password);
        if(!result) throw new Error("등록되지 않은 사용자");        
        const token = await signNewToken(req.body.id);
        if (token) {
            res.json({
                id : req.body.id,
                status : true,
                token,
            })
        }
    } catch(err) {
        console.log(`${err.name} : ${err.message}`);
        res.status(419).json({
            code : 419,
            id : req.body.id,
            status : false,
            message : "등록되지 않은 사용자입니다.",
        })
    }
})

router.post("/test", verifyToken(cbFail), (req, res)=>{
    console.log("/user/test", req.body);
    res.json(req.decoded);
}) 

export default router;

🔴  res.send와 res.json 비교

  🔊  res.json 함수 내부에 res.send를 내포하고 있다.

  🔊  object에 json을 넣고 res.send(object)를 실행했을 때 함수의 호출 순서는 아래와 같다.

  1. res.send(object)
  2. res.json(object)
  3. res.send(string)

  🔊  반면, res.json(object)를 실행했을 때 함수의 호출 순서는 아래와 같다.

  1. res.json(object)
  2. res.send(string)

  🔑  따라서, JSON 응답을 한다면 res.send보다 res.json이 추가 비용이 덜 들기 때문에 효율적이다.

 

🟠  res.status().json() 해석

  🔊  status() 함수는 HTTP 상태 코드를 반환한다. 하지만, 상태 코드만 반환할 뿐, end()나 send() 같은 전송 메서드를 추가로 호출해야 전송할 수 있다.

  🔊  json() 함수는 위에서 보았듯이 내부에 send() 함수를 내포하고 있기 때문에 둘을 순서대로 선언하면 상태 코드와 상태 코드에 대한 설명을 클라이언트에 전송할 수 있다.

 

⚪  router / token.js

 
import express from "express";
import jwt from "jsonwebtoken";

const router = express.Router();
router.post("/", (req, res)=>{
    console.log(req.body);
    try {
        jwt.verify(req.body.userToken, process.env.JWT_SECRET);
    } catch(err) {
        if (err.name === "TokenExpiredError") {
            return res.status(499).json({
                code : 499,
                message : "토큰이 만료되었습니다.",
            })
        }
        return res.status(401).json({
            code : 401,
            message : "유효하지 않은 토큰입니다.",
        })
    }

    return res.status(201).json({
        code : 201,
        isValid : true,
    })
})

export default router;

 

⚪  middlewares / jwt.js

 
import jwt from "jsonwebtoken";

export const verifyToken = (req, res, next, cbFail) => {
    try {
        // 요청 헤더에 저장된 토큰과 비밀키를 사용하여 토큰을 req.decoded에 반환
        req.decoded =  jwt.verify(req.headers.authorization, process.env.JWT_SECRET);
        return next();
    } catch (err) {
        if (cbFail) cbFail();
        if (error.name === "TokenExpiredError") {
            return res.status(499).json({
                code : 499,
                message : "토큰이 만료되었습니다.",
            })
        }
        return res.status(401).json({
            code : 401,
            message : "유효하지 않은 토큰입니다.",
        })
    }
}

export const signNewToken = (id, cbFail) => {
    try {
        const token = jwt.sign({
            id : id,
        }, process.env.JWT_SECRET, {
            expiresIn : process.env.JWT_TOKEN_EXPIRE,
            issuer : "Mediazen",
        });
        return token;
    } catch(err) {
        console.error(err);
        if(cbFail) cbFail();
        return false;
    }
}

 

🔴  jwt란?

  🔊  JSON Web Token으로 전자 서명된 URL로 이용할 수 있는 문자만 구성된 JSON이다.

  🔊  JWT는 서버와 클라이언트 간 정보를 주고 받을 때 HTTP request header에 JSON 토큰을 넣은 후 서버는 별도의 인증 과정 없이 헤더에 포함되어 있는 JWT 정보를 통해 인증한다.

 

🟠  jwt.verify(token, scretKey)

🔊  토큰 인증(확인)

1️⃣  token : 클라이언트에게 받은 토큰. 디코딩하여 payload 부분을 추출할 수 있다.

2️⃣  secretKey : 토큰 발급 시 사용했던 secretKey. 보안을 위해 .env 파일에 상수로 정의해 놓는다.

3️⃣  return : 유효성 검증 통과 시 jwt payload 추출

 

🟡  jwt.sign(payload, secretKey, option)

🔊  토큰 발급

1️⃣  payload : 토큰의 payload 부분이다.

2️⃣  secretKey : 보안을 위해 .env 파일에 상수로 정의해 놓는다.

3️⃣  option : 어떠한 알고리즘을 사용할 것인지, 만료 기간은 언제인지, 발급자는 누구인지 등의 옵션을 넣어줄 수 있다.

4️⃣  return : 위의 인자들을 사용하여 만들어진 토큰을 리턴한다.

저작자표시 (새창열림)

'NodeJS' 카테고리의 다른 글

[ NodeJS ] MongoDB  (0) 2022.08.23
[ NodeJS ] express  (0) 2022.08.23
[ NodeJS ] 파일  (0) 2022.08.23
[ NodeJS ] 소개  (0) 2022.08.23
    'NodeJS' 카테고리의 다른 글
    • [ NodeJS ] MongoDB
    • [ NodeJS ] express
    • [ NodeJS ] 파일
    • [ NodeJS ] 소개
    Design-loving front-end engineer
    Design-loving front-end engineer
    디자인에 관심이 많은 모바일 앱 엔지니어 Ryong입니다.

    티스토리툴바