TIL 19일차 - node.js 입문주차 강의이론

박찬웅·2023년 2월 24일
0

항해99

목록 보기
24/105

23년 2월 24일

  • 대부분 스파르타에서 제공된 강의노트를 위주를 참고해서 정리했으며, 보다 더 자세한 코드는 강의 노트를 참고

배운 것

드디어 오늘 부터 3주동안 본격적으로 javascript의 언어이자 백앤드 개발자의 주특기로 Node.js 공부를 시작하였다. 오늘은 전반적으로 이론과 기본적인 강의 실습 위주로 공부하였다. 새로운 요소들도 많이 배웠지만 저번주에 배웠던 복습도 일부 있었다.

1. Javascript 복습 이론

  • 이 부분은 에러 핸들링과 class 빼고는 나머지는 심화 개념에서 이미 한번 다룬 개념들이기에, 자세한 내용은 이전 TIL 16~18일차를 보면 된다.

(1) 동기(Sync) & 비동기(Async)

  • 동기 - 먼저 실행된 코드의 결과가 나올때까지 대기 하는 것
  • 비동기 - 실행된 순서와 관계 없이 결과가 나오는 것

이거랑 유사한 것으로는 Blocking Model & Non-Blocking Model도 있는데 이것도 간단하게 요약하면 이렇다.

  • Blocking Model - 코드의 실행이 끝나기 전까지 실행 제어권을 다른곳에 넘기지 않아 다른 작업을 하지 못하고 대기하는 것
  • Non-Blocking Model - 코드의 실행이 끝나지 않아도 실행 제어권을 다른곳에 넘겨 다음 코드가 실행될 수 있는 것

동기랑 비동기에 차이점이라면 제어권을 넘기면(Non-blocking) 다른 코드도 실행될 수 있으므로 비동기 처리가 가능하지만, 제어권을 넘기지 않으면(Blocking) 비동기 처리가 가능한 환경이어도 비동기 처리가 불가능하다.

그 외로 중요한 개념 단어라면 다음과 같이 있다. 자세한 내용은 이전 TIL 16~18일차를 보면 된다.

  • 프로미스
    자바스크립트에서 비동기 처리동기로 처리할 수 있게 돕는 Built-in(미리 내부적으로 정의된)객체 유형이며, 이 객체를 이용하면 여러분은 비동기 처리를 아주 손쉽게 할 수 있다.
  • 비동기 함수
    비동기 함수는 일반 함수나 화살표 함수와 아주 비슷하지만 딱 두가지만 다르다.
    1. 비동기 함수결과 값항상 Promise 객체로 resolve된다.
    2. 비동기 함수 안에서만 await 연산자를 사용할 수 있다.

(2) 객체 리터럴

  • 객체(Object) - Javascript의 데이터 타입은 크게 원시 타입객체 타입으로 분류됩니다.

    • 원시 타입은 단 하나의 값만을 나타내고, 원시 타입의 값은 변경이 불가능 한 값
    • 객체 타입은 다양한 타입의 값을 하나의 단위로 구성한 복합적인 자료 구조이고, 객체 타입의 값을 변경 가능한 값
  • 객체 리터럴(literal) : 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용해 값을 생성하는 표기법이다.
    객체 리터럴은 객체를 생성하기 위해 Class를 먼저 선언하고 new 연산자와 함께 생성자를 호출할 필요가 없이 일반적인 숫자, 문자열을 만드는것과 유사하게 객체를 생성할 수 있습니다.

  • 프로퍼티(Property) : 객체의 상태를 나타내는 (Data)이며, 키(Key)값(Value)으로 구성

  • 메서드(Method) : 프로퍼티를 참조하고 조작할 수 있는 동작(behavior), 객체의 프로퍼티 값이 함수로 구성되어 있을 경우

(3) Error handling

에러 핸들링은 에러를 관리하는 방법이고, 예상치 못한 상황에 대처하는 방식이다.

에러는 예상할 수 있는 에러예상치 못한 에러로 구분할 수 있는데, 일반적인 어플리케이션을 설계할 때에는 예상치 못한 에러 상황이 더욱 많이 일어날 것으로 가정해야 한다.

프로그래머가 작성한 코드에서 예상치 못한 에러가 일어날 가능성은 언제나 존재하고, 이러한 에러 상황을 대비해 언제든지 처리할 수 있어야 한다.

이러한 대비로는 try...catch, throw, finally를 사용 가능한데 다음과 같이 예외 처리를 할 수 있다.

  • try...catch : 일반적으로 예외처리 하는 방법이며, 에러가 발생하더라도 프로그램이 멈추지 않고 에러를 기록할 수 있다.
const users = ["Lee", "Kim", "Park", 2];

try {
  for (const user of users) {
    console.log(user.toUpperCase());
  }
} catch (err) {
  console.error(`Error: ${err.message}`);
}

// LEE
// KIM
// PARK
// Error: user.toUpperCase is not a function
  • throw : 호출하면 그 즉시 현재 실행되고 있는 함수는 실행을 멈추게 한다.
function withdraw(amount, account) {
  if (amount > account.balance)
    throw new Error("잔고가 부족합니다.");
  account.balance -= amount;
	console.log(`현재 잔고가 ${account.balance}남았습니다.`); // 출력되지 않음
}

const account = { balance: 1000 };
withdraw(2000, account);

// Error: 잔고가 부족합니다.
  • finally : 에러가 발생했는지 여부와 상관없이 언제든지 실행된다.
function errorException(isThrow) {
  try {
    console.log('자원을 할당하였습니다.');
    if (isThrow) throw new Error();
  } catch (error) {
    console.log('에러가 발생했습니다.');
  } finally {
    console.log('자원을 제거하였습니다.');
  }
}

errorException(false);
// 자원을 할당하였습니다.
// 자원을 제거하였습니다.
errorException(true);
// 자원을 할당하였습니다.
// 에러가 발생했습니다.
// 자원을 제거하였습니다.

(4) 클래스(Class)

👉 클래스는 정보를 묶는 것입니다!

  • 현실과 비슷한 개념(객체)을 나타내기 위한 도구클래스(Class)라고 부른다.
  • 클래스는 미리 정의해놓으면 필요할 때마다 해당 클래스로 동일한 틀을 가진 객체를 만들 수 있고, 여기서 동일한 클래스를 이용해 생성한 객체를 인스턴스(Instance)라고 부른다.
class User { // User 부모 클래스
  constructor(name, age, tech) { // 부모 클래스 생성자
    this.name = name;
    this.age = age;
    this.tech = tech;
  }
  getTech(){ return this.tech; } // 부모 클래스 getTech 메서드
}

class Employee extends User{ // Employee 자식 클래스
  constructor(name, age, tech) { // 자식 클래스 생성자
    super(name, age, tech); 
  }
}

const employee = new Employee("이용우", "28", "Node.js");
console.log(employee.name); // 이용우
console.log(employee.age); // 28
console.log(employee.getTech()); // 부모 클래스의 getTech 메서드 호출: Node.js

위 코드는 클래스 예시 코드를 한번에 정리한 코드이다.
간단하게 용어들 더 설명하면 자기 자신을 나타내는 this, 객체(Object) 에 묶여 있는 함수라고 말하는 메서드, 부모클래스와 자식클래스를 연결하는 상속이나, 그걸 해당하는
class (자식 클래스) extend (부모클래스)와 부모클래스의 생성자와 메서드를 호출 가능한 super 키워드도 있으나, javascript와 node.js에서는 자주 쓰는 건 아니기에 이정도만 대략 이렇다고 이해하고 넘어가도 무방하다.

2. HTTP / Web Server

(1) HTTP

  • HTTP는 데이터를 주고 받는 양식을 정의한 "통신 규약"중 하나가 HTTP입니다!
    (통신 규약: Protocol)
  • 매우 범용적인 양식을 가지고 있어 전 세계에서 제일 널리 쓰이는 통신 규약 (거의 만능!)
  • 여기서 말하는 통신 규약이란, 컴퓨터끼리 데이터를 주고 받을때 정해둔 약속을 의미

개발자 도구를 통해서 네트워크가 어떻게 돌아가는지 볼 수 있다.

(2) Web Server

  • 웹서버는 인터넷을 통해 HTTP를 이용하여 웹상에서 클라이언트의 요청을 응답해주는 통신을 하는 컴퓨터 또는 프로그램이라고 생각하시면 된다.
  • 오늘날 우리가 자주 사용하는 이메일이나 웹사이트 등 대부분의 인터넷 사용은 웹서버를 통해 우리가 사용할 수 있게 되었다.
  • 웹 서버의 기본 동작 원리
    • 브라우저를 통해 HTTP request로 웹사이트를 웹서버에 요청한다. 이후 웹서버는 요청을 승인하고 HTTP response를 통해 웹사이트 데이터를 브라우저에 전송한다. 마지막으로 브라우저는 서버에서 받아온 데이터를 이용해 웹사이트를 브라우저에 그려내는 일을 한다.
    • 기본적으로 브라우저가 웹서버에 요청을 할때는 항상 GET method로 요청하게 된다.

3. Package Manager

  • 패키지 매니저는 패키지를 손쉽게 다루는 작업을 안전하고 편리하게 사용하기 위한 툴이다.
  • 다른 사람들이 만들어준 코드를 다운로드 받거나, 자신의 코드를 배포하여 다른 사람이 쓸 수 있도록 할 수 있다. 웹종반에서 배웠던 파이참에서 패키지 설치랑 했던거를 생각하면 된다.

Node.js에서 대표적으로 사용하는 패키지 매니저는 npmyarn이 존재한다.

  • npm은 자바스크립트에서 사용할 수 있는 패키지(모듈) 관리자이다.
  • yarn은 npm의 대체제로 FaceBook이 출시한 패키지 매니저이다.

다만 저 두개를 같이 쓰는거는 좋지 않다. 여러가지로 충돌이 일어날 수 있기 때문이다.
이제 우리가 앞으로 실습 할 것은 npm을 이용해서 패키지 설치하는 실습을 하였다.

그리고 이제 패키지를 설치하다보면 다음과 같은 두개의 json이 받아지게 되는데 다음 두개의 json을 요약하면 다음과 같다.

  • Package.json 이란?
    • 설치한 패키지들의 버전을 관리할 때 사용하는 파일
    • 동일한 패키지를 사용하더라도 버전별로 기능을 다르게 사용할 수 있으므로 특정한 버전을 설치할 때 필요
    • 패키지 관리 외에도 프로젝트명, 작성자, 라이센스 정보등 다양한 메타 데이터들을 기록 가능
    • npmyarn 모두 동일한 package.json 파일을 참조
  • Pacakge-lock.json 이란?
    • package.json파일에서 정의한 패키지 외에도 node_modules에 들어있는 패키지들의 버전과 의존 관계가 상세하게 기록되어 있음
    • npm으로 패키지를 설치, 수정, 삭제할 때마다 패키지들의 의존 관계package-lock.json파일에 저장
    • 저장된 패키지들은 정확히 일치하는 버전만 기록되므로, 프로젝트에서 의존하는 패키지 버전을 정확하게 관리할 때 사용할 수 있음

이제 우리가 앞으로 실습 할 것은 npm을 이용해서 패키지 설치하는 실습을 하였다. VScode안에 있는 터미널에 가서 실습을 하였고, 다음과 같은 명령어로 설치하면 된다.

  • 먼저 터미널에 'npm init'를 입력을 한다. 이것은 package.json 파일을 만들 때 사용되며, package.json은 npm 으로 설치된 모듈에 대한 정보가 들어있다.
  • 다음으로 'npm install (패키지명)' 를 입력을 한다. 그러면 이제부터 필요한 해당 패키지를 자유롭게 사용이 가능하다. 패키지 여러개 적으면 여러개의 패키지도 설치 가능하고 install 대신에 그냥 i 만 입력해도 설치가 가능하다.

앞으로의 실습을 위해서 'npm install express'를 이용을 했고, express 패키지는 바로 하단에서 설명 할 예정이다.

4. express.js

(1) 준비하기

  • Express.jsNode.js로 서버를 빠르고 간편하게 만들 수 있게 도와주는 웹 프레임워크
  • Express.js 이외에 다양한 웹 프레임워크가 존재하지만 오늘날 가장 많은 Node.js 웹서버가 Express.js 프레임워크를 통해 개발되었습니다.
  • 최근 각광받고 있는 Node.js의 웹 프레임워크로 Nest.js도 있습니다.
  • Express.js와 웹서버는 동일하지 않다는 점을 명심

이제부터 본격적으로 실습을 하기 시작하였다. 실습하기 위한 아무 폴더을 만들었다.
폴더의 제목은 spa_mall으로 하였다. 그리고 나서 VScode에 들어가서 해당 폴더를 열었다. 이 다음에는 만들어진 폴더에 app.py 파일을 만들어줬다. 그리고 VScode에서 제공하는 터미널에 들어가서 'npm init -y' 명령어를 입력해서 package.json을 생성해주고 'npm install express' 명령어를 입력해 express.js 패키지를 설치를 하였다.
모두 완료되면 아래와 같은 사진처럼 기본 세팅이 마무리 된다.

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

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

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

app.py에 다음과 같이 입력해서 기본적인 서버 코드를 작성해서 실습을 하였다, 포트번호는 3000으로 지정했다. 그리고 localhost:3000 입력하면 서버가 연결 된 것을 확인 할 수 있다.

그리고 이후에 VScode에 있는 확장을 눌러서 Thunder Client를 추가적으로 설치하였다. 이 기능은 API Client의 확장 프로그램중 하나인데, API Client란, 개발단계에서 우리가 작성한 API의 요청을 확인하거나 테스팅 할 때 도움을 주는 툴이다. API Client를 사용함으로 개발 속도를 높이거나 치명적인 에러를 예방하는데 도움을 받을 수 있다.

이렇게 설치하면 다음과 같이 이용을 할 수 있다. 좌상단에 있는 New Request를 사용해서 위에 테스트 해볼 주소를 적고 요청하는 방식을 정해서 send를 누르면 다양한 결과를 보여 줄 수 있다.
이 후부터는 터미널에 들어가서 node app.js를 치면서 계속 실습을 하였다.

(2) 시작하기

먼저 시작하기 전에 추가적인 개념을 배웠다.

1) Router

  • Routing은 클라이언트의 요청 조건(메서드, 주소 등)에 대응해 응답하는 방식
  • Router는 클라이언트의 요청을 쉽게 처리 할 수 있게 도와주는 Express.js 기본 기능 중 하나

일반적으로 Router는 아래와 같은 구조를 가집니다.

router.**METHOD**(**PATH**, **HANDLER**);
  • router: express의 라우터를 정의하기 위해 사용합니다.
  • METHOD: HTTP Method를 나타냅니다. (ex: get, post, put, delete …)
  • PATH: 실제 서버에서 API를 사용하기 위한 경로를 나타냅니다.
  • HANDLER: 라우트가 일치할 때 실행되는 함수힙니다.

이제 goods.js 파일을 만들어서 다음과 같이 실습을 하였다.

// routes/goods.js

// localhost:3000/api/ GET
router.get("/", (req, res) => {
  res.send("default url for goods.js GET Method");
});

// localhost:3000/api/about GET
router.get("/about", (req, res) => {
  res.send("goods.js about PATH");
});
  • 작성한 Router를 app.js에서 사용하기 위해 하단에 내보내주는 코드를 추가합니다.
    // routes/goods.js
    
    module.exports = router;
  • Router 미들웨어를 사용하겠다고 작성합니다.
// app.js

const goodsRouter = require("./routes/goods");

// localhost:3000/api -> goodsRouter
app.use("/api", [goodsRouter]);

이러면 localhost:3000에서 /api 로 시작되는 주소는 routes/goods.js 에 있는 Router 미들웨어를 통해 처리된다.

2) Module

모듈(Module)Javascript 파일 단위로 분리된 코드를 일컫습니다.

여기서 Javascript 파일은 특정한 기능을 가진 여러 개의 함수변수들의 집합이다.

  1. 모듈(Module)은 하나의 모듈에서 다른 모듈호출하여 사용할 수 있습니다.
  2. 모듈(Module)은 그 자체로도 하나의 프로그램이면서 다른 프로그램의 부품으로도 사용할 수 있습니다.
  3. 보통 1개의 파일1개의 모듈이 됩니다.

실습은 다음과 같이 했으며 modules 폴더를 생성해 math.js, run.js 라는 파일을 생성했다.
그리고 모듈을 사용하는 예시는 총 4가지가 있으며 다음과 같이 사용하면 된다. 아래 코드들은 제가 직접 실습한 코드이며, 두 수를 더하면 모두 값은 40으로 출력된다.

run.js 파일

const {add} = require("./math.js");

console.log(add(10,30));

math.js 파일

// Module을 export하는 다양한 방법 4가지
// 모듈을 호출했을 때, add 키 값에는 add 변수 함수가 가지고 있는 값이 할당된다.
 const add = (a, b) => {
     return a + b;
 }
 exports.add = add;


// 모듈을 호출했을 때, add 키 값에는 (a,b){return a + b:} 익명함수가 할당되는 방법이다.
 exports.add = function(a,b) {
     return a + b;
 }

// 모듈을 호출했을 때, add 키 값에는 add 함수가 들어가는 방법이다.
 function add(a, b) {
     return a + b;
 }
 module.exports = { add : add };

// 모듈 그 자체를 바로 add 함수를 할당한다.
 function add(a, b) {
     return a + b;
 }
 module.exports = add;

3) Request와 Response

  • Request클라이언트가 서버에게 전달하려는 정보나 메시지를 담는 객체를 의미
  • Response서버에서 클라이언트로 응답 메시지를 전송시켜주는 객체

여기서 자주 사용하는 모듈은 Express 모듈이며 req, res 객체의미는 다음과 같이 요약 가능하다. 종류는 이외에도 많지만 대표적인거만 적었다.

req 객체

  • req.body: Request를 호출할 때 body로 전달된 정보가 담긴 객체입니다.
    • express.json() Middleware를 이용하여야 해당 객체를 사용할 수 있습니다.
  • req.params: 라우터 매개 변수에 대한 정보가 담긴 객체입니다.
  • req.query: Request를 호출할 때 쿼리 스트링으로 전달된 정보가 담긴 객체입니다.

res 객체

  • res.status(코드) : Response에 HTTP 상태 코드를 지정합니다.
  • res.send(데이터) : 데이터를 포함하여 Response를 전달합니다.
  • res.json(JSON) : JSON 형식으로 Response를 전달합니다.

(3) REST API

  • 먼저 API는 애플리케이션끼리 연결해주는 매개체이자 약속이라고 볼 수 있다.
  • REST API는 “REST 아키텍쳐”라는 규칙을 따르는 API라고 생각하시면 된다.

REST API의 구성은 세 가지
1. 자원(Resource) - URL
2. 행위 - HTTP method
3. 표현

REST API는 다음과 같이 예시로 사용 가능하다.

router.get('/books', (req, res) => {
	res.json({ success: true, data: getAllBooks() });
});

이제 최종적으로 내가 만든 REST API 코드이다. (app.py는 오늘 공부했던 파트들도 것도 같이 들어 있다.)
app.py 파일

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

app.use(express.json());

app.post("/", (req,res) => {
  console.log(req.body);

  res.send("URI에 POST 메소드가 정상적으로 실행되었습니다.");
});

app.get("/", (req,res) => {
  console.log(req.query);
  // 첫번째 방법
  // const obj = {
  //   "KeyKey" : "value 입니다.",
  //   "이름입니다.":"이름일까요?",
  // }
  
  // 두번째 방법
  // res.json({
  //   "KeyKey" : "value 입니다.",
  //   "이름입니다.":"이름일까요?",
  // });

  // 요청 실패 했을 때
  res.status(400).json({
    "KeyKey" : "value 입니다.",
    "이름입니다.":"이름일까요?",
  });
});

app.get("/:id", (req,res) => {
  console.log(req.params);

  res.send(": id URI에 정상적으로 반환되었습니다.");
});

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


// localhost:3000/api -> goodsRouter
app.use("/api", [goodsRouter]);


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

good.js 파일

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

// /routes/goods.js
const goods = [
    {
      goodsId: 4,
      name: "상품 4",
      thumbnailUrl:
        "https://cdn.pixabay.com/photo/2016/09/07/02/11/frogs-1650657_1280.jpg",
      category: "drink",
      price: 0.1,
    },
    {
      goodsId: 3,
      name: "상품 3",
      thumbnailUrl:
        "https://cdn.pixabay.com/photo/2016/09/07/02/12/frogs-1650658_1280.jpg",
      category: "drink",
      price: 2.2,
    },
    {
      goodsId: 2,
      name: "상품 2",
      thumbnailUrl:
        "https://cdn.pixabay.com/photo/2014/08/26/19/19/wine-428316_1280.jpg",
      category: "drink",
      price: 0.11,
    },
    {
      goodsId: 1,
      name: "상품 1",
      thumbnailUrl:
        "https://cdn.pixabay.com/photo/2016/09/07/19/54/wines-1652455_1280.jpg",
      category: "drink",
      price: 6.2,
    },
  ];

    //상품 목록 조회 API
    router.get("/goods", (req, res) => {
        res.json({goods});
    });

    router.get("/goods/:goodsId", (req, res) => {
        const {goodsId} = req.params;
        
        // 첫번째 방법
        // let result = null;
        // for(const good of goods) {
        //     if(Number(goodsId) === good.goodsId) {
        //         result = good;
        //     }
        // }

        // 두번째 방법
        const [result] = goods.filter((good) => Number(goodsId) === good.goodsId)

        res.status(200).json({ detail: result });
    });

module.exports = router;

이렇게 설정 마무리 되면 다음과 같이 성공적으로 REST API 작성 된것을 볼 수 있다. 아래 사진은 Thunder Client에 가서 localhost:3000/api/goods/2를 지정했을때 나오는 사진이다. 사진을 보면 goodId의 2번이 제대로 출력 된 것을 알 수 있다.

이렇게 오늘의 실습은 express 패키지를 통해서 다양한 실습을 마무리 하였다.

알게 된 점

오늘은 하루종일 실습만 하다가 부랴부랴 자정이 되서 끝나게 되었다. 강의 영상만 보고 실습하고 복습할겸 TIL을 작성했는데 벌써 15시간이 지나가버렸다. 특히나 제일 나한테 애를 먹었던거는 REST API 작성하는 것이였다. 계속 서버를 키고 실행을 시키는데 자꾸 뭔가 안되서 기술매니져님한테 도움을 받아서 겨우 해결을 했었다. 다른 실험을 해보려고 주석 처리를 한것을 풀어야 했던 것인데 이걸 잘 몰라서 막막하였다. 코드 애러가 났을때는 한번 제대로 살펴보고 수정을 해야 한다는 것을 알았다.
실습한 부분은 아직 완전히 이해 된건 아니지만, 이것은 나중에 기본적인 강의가 다 마무리 되고 나서 개인 과제로 다시 복습 할 시간을 가질 수 있기에 그때 가서 다시 복습하고 공부하면 된 다는 것을 알게 되었다. 그리고 다행인 것은 ES6 빼고는 다른 개념은 지난 3일동안 공부 했던것 덕분에 이해는 빨리 할 수 있었다.

앞으로 할 일

내일은 어제보다는 그나마 수월할 것이다. 웹종합반과 토이프로젝트에서 미리 공부했던 몽고디비와 Git 커밋하는 방법, ec2 도메인을 만드는 것이기에 저번에 했던 기억을 살펴보면서 공부해야겠다. 물론 기존 개념에서 더 추가되는 것이니 방심은 금물이다.

profile
향해 13기 node.js 백앤드

0개의 댓글