본문 바로가기

내일배움캠프_개발일지/Node.js 기초

Node.js 숙련 - 1

스파르타 Node.js 숙련

 

 

종합 폴더 : kimminsoo -> sparta -> node_js -> learning -> second_step

 

 

 

**** mongoose 스키마 타입 속성 메소드 참고용 블로그 *****

https://blog.naver.com/rwans0397/220696586520

 

 

 

<숙련> 시작

 

 

 

________________________________________________________________________________________________________________

 

1주차

=> 숙련 주차 학습 목표.

1. 자바스크립틔 숙련 문법을 활용할 수 있다.

2. JWT의 개념에 대해 이해하고, cookie 및 session 을 구현하거나 설명할 수 있다.

3. Mysql 에서 각 테이블 간의 관계가 무엇인지 이해하고, ERD 를 작성할 수 있다.

4. Sequelize를 이용하여 Migration, Model 파일을 작성할 수 있고, 각 파일들이 어떤 역할을 가지는지 설명할 수 있다.

5. Sequelize (ORM)의 필요성을 이해하고 있다.

 

 

 

 

 

 

 

__________________________________________________

 

1-1  지식 수준 맞추기

 

=>  < 2.1. 할 일 메모사이트 00 >

 

 

우선, 입문 주차때 배운 내용들을 간단하게 복습하고 넘어가자.

=>

1. Node.js는 JavaScript를 브라우저 없이 단독으로 실행할 수 있는 하나의 플랫폼

2. npm은 우리가 Node.js에서 오픈소스 라이브러리를 쉽게 사용하기 위해 개발된 "패키지 관리자"라는 형식의 프로그램

3. express라는 라이브러리는 우리가 쉽게 서버 프로그램을 구성할 수 있게 만들어진 "오픈소스 라이브러리”. 즉 웹 프레임워크.

 

 

— 그 외 —

 

HTTP란? 우리가 일반적으로 데이터를 주고 받을 때 사용되는 통신 규약

웹 브라우저는? HTML로 이루어진 데이터를 읽어서 화면에 그려주는 역할

=> 단순히 웹 문서를 가져와 보여주는것 뿐만 아니라, 

여러가지 프로토콜(http, ftp, file 등)을 지원하며 다른 웹 서버에 데이터를 보낼수 있기도

 

쿠키란? 웹 브라우저에 구현된 기술 중 하나. 보통 사용자의 상태를 저장하기 위해서 사용.

=> 서버에서 쿠키를 노릇노릇 구워서 Response에 담아 보내면 웹 브라우저는 받은 데이터를 그대로 저장.

** 쿠키는 보통 response 의 header 라는 영역에 담아 보낸다고.

브라우저는 가지고 있는 쿠키가 있다면 서버에 Request를 할 때 항상 가지고 있는 쿠키 데이터를 포함해서 보내. 

단, 쿠키는 별도의 암호화 없이 데이터를 그대로 주고받기 때문에 클라이언트에서 마음대로 조작하기 쉬워 보안에 취약.

 

세션이란? 웹 브라우저에 구현된 기술중 하나. 세션은 쿠키의 특성을 이용한 기술.

=> 세션 데이터는 서버에 저장되고 데이터마다 고유한 세션 ID가 만들어 짐.

이 ID를 쿠키를 이용해 주고 받기 때문에 세션 데이터에 접근이 가능한것은 오직 서버뿐이기 때문에 쿠키가 가지고 있던 보안 취약점을 해결.

허나, 모든 인증을 서버에서 처리하기 때문에 사용자가 많아질 수록 서버에서 처리해야하는 부하가 증가하여 문제가 발생할 수 있어.

*** 즉, 세션은 서버에 부담을 많이 준다.

*** 쿠키는 현재 상태에 대해 주고 받는다면,

세션은 데이터 자체를 주고 받는 게 아니라 ‘열쇠’ 를 가지고 있는거야.

만약에, 서버에서 해당 사용자가 인증이 되었다 라고 한다면, 인증 되었다 라는 사용자에 대한 좌물쇠는 서버가 가지고 있는 거고,

사용자, 즉 클라이언트에게는 세션이라는 열쇠만 주는 거야.

즉 사용자는 열쇠라는 세션을 가지고 있다면, 서버에 있는 좌물쇠를 열 수 있게 되는거야.

열쇠로 좌물쇠를 열면, 서버에서는 좌물쇠에서 나온 데이터를 사용하여 해당 사용자가 누구인지 특정하고, 

어떻게 사용자의 정보는 수정하는가 등으로 활용할 수 있게 되는 것.

=> 결국 특정 개인에 대한, 즉 보안성이 유지되어야 할 데이터를 서버에서만 안전하게 관리할 수 있다는 것.

***

session = false 에 대해서 알아보자…

 

 

서버(Server) 프로그램이란? 일반적으로 클라이언트에게 요청을 받아 응답을 주는 프로그램의 유형

=> express 웹 프레임워크르 기반으로 app.js 에서 서버를 실행시켰다? 그것 자체가 서버 프로그램.

 

서버(Server) 컴퓨터란? 위에서 설명된 "서버 프로그램"을 실행하고 있는 컴퓨터

 

 

 

 

 

 

 

 

 

__________________________________________________

 

1-2  [할 일 메모 사이트] - 준비 단계

 

=>  < 2.1. 할 일 메모사이트 01 >

 

 

— 이번 주차에서 만들어 볼 기능들 —

 

  • 로그인
  • 회원가입
  • 할 일 추가
  • 할 일 수정
  • 할 일 삭제

 

 

좀 더 세부적으로 보자면?

 

  1. 할 일 추가하기
  2. 할 일 목록 보기
  3. 할 일 순서 변경하기
  4. 할 일 완료하기
  5. 할 일 완료 해제하기
  6. 로그인
  7. 회원가입

 

 

 

 

 

 

 

 

 

__________________________________________________

 

1-3  [할 일 메모 사이트] - API 서버 준비하기(1)

 

=>  < 2.1. 할 일 메모사이트 02 >

 

 

 

폴더 : kimminsoo -> sparta -> node_js -> learning -> second_step -> todo_memo

 

 

 

  1. npm init -y
  2. npm i express
  3. npm i mongoose
  4. app.js 코드 스니펫 가져오기

 

——

< app.js >

 

const express = require("express");

const app = express();

const port = 8000;

 

// 미들웨어 및 route 설정

const router = express.Router();    // app.js 에 선언되어 있는 주소들 또한 자기 자신의 router 가 되는거야.

 

// routes use. router 는 작은 api 라고 생각하면 편해.

app.use("/api", express.json(), router);  // /api 로 들어왔을 때만 app.js 의 route 를 제공한다.

 

// 경로 : localhost:8000/api/     어떻게 보면 redirect 같은 느낌인거지. 근데 강제로.

router.get("/", (req, res) => {

  res.send("Hi! this is localhost:8000/api/");

});

 

 

app.listen(port, () => {

  console.log("서버가 켜졌어요!");

});

——

=> 이번 프로젝트에서는 이렇게 하지는 않지만,

애당초 루트 파일인 app.js 지역의 라우트 주소로 접근하긴 위해선 /api 로 접근해야 하도록 app.js 가 스스로를 라우트로 만드는 거지.

 

 

 

 

— 할 일 메모 사이트 프론트엔드 파일을 서버에 추가 —

 

코드스니펫이 제공하는 리액트 압축파일 링크

=> https://s3.ap-northeast-2.amazonaws.com/materials.spartacodingclub.kr/nodejs_advanced/week01/todo-web-week1.zip

 

위 링크엣에서 다운로드 받고, 나온 파일들을 todo_memo > assets 폴더 안에 넣어줘.

 

——

<현재 폴더 구조>

 

todo_memo

├── app.js

├── assets

  ├── asset-manifest.json

  ├── favicon.ico

  ├── index.html

  ├── logo192.png

  ├── logo512.png

  ├── manifest.json

  ├── robots.txt

  └── static

      ├── css

        ├── main.6842b365.chunk.css

        └── main.6842b365.chunk.css.map

      └── js

          ├── 2.96129310.chunk.js

          ├── 2.96129310.chunk.js.LICENSE.txt

          ├── 2.96129310.chunk.js.map

          ├── 3.0962e34f.chunk.js

          ├── 3.0962e34f.chunk.js.map

          ├── main.c5ceafa0.chunk.js

          ├── main.c5ceafa0.chunk.js.map

          ├── runtime-main.2ec3457b.js

          └── runtime-main.2ec3457b.js.map

├── package-lock.json

└── package.json

——

=> 여기에 node_modules 까지 더해서.

 

 

 

 

 

 

— assets 파일을 서빙할 수 있도록 static 미들웨어를 추가 —

=> 이게 무슨 의미인가.

flask 프로젝트 때처럼 ‘정적 파일’, 그니까 html 파일을 render_template 해줬을 때 처럼.

정적 파일들을 사용자에게 제공해 줄 수 있어야.

 

——

< app.js >

 

const express = require("express");

const app = express();

const port = 8000;

 

// 미들웨어 및 route 가져오기

const todosRouter = require("./routes/todos.router.js");

 

// routes use.    router 는 "작은 api" 라고 생각하면 편해.

app.use("/api", express.json(), todosRouter);  // /api 로 들어왔을 때만 todosRouter 의 route 를 제공한다.

app.use(express.static("./assets"));  

        // express.static() 는 static file(정적 파일)들을 연결해주는 미들웨어. 

                  // 좀 더 다르게 말하자면, assets 라는 폴더에 미들웨어가 연결이 되어 있는거야.

        // 이 미들웨어를 연결해 주기 위해서 다음과 같이 정의했어.

        //      => 특정 주소로 들어왔을 때, 즉 해당 주소가 assets 라는 폴더 안에 있다면, 그 파일을 전송해줘라

        // 이 경우, 특정 주소란 app.js(루트 폴더에 있는 최상위 파일)가 실행되면 바로 들어오게 되는 곳, 즉 localhost:8000

            /*

            localhost:8000 으로 접속하면 ./assets 폴더 안의 파일들을 바로 연결해주는데, 

            우리가 mongoose 를 모듈로 가져와서 연결할 때 사용했던 

 

            const connect = require("./schemas")  

            connect();

 

            위 선언에서 알 수 있다시피 index 라는 이름의 파일이 있으면 굳이 명시하지 않아도 최우선으로 읽어와.

            따라서 localhost:8000 으로 접속하면서 

            app.use(express.static("./assets")) 로 assets 폴더 안으로 미들웨어가 연결되는데, 

            이때 assets 폴더 안에 있는 index.html 이라는 파일을 localhost:8000 접속과 동시에 바로 읽어올 수 있어.

                        ** 물론 localhost:8000/index.html 이렇게 읽어올 수도 있고. 이게 fm이지.

            또한 index.html 은 static 폴더 안에 있는 .js 파일들과 함께 서버에 제공되고 있어. html 이 전달해 주고 있는거지.

 

            app.js 는 루트 폴더, 즉 최상위 폴더에 위치한 루트 파일이야. 

            최상위 폴더 바로 아래에는 app.js 와 함께 assets 폴더가 있지. 

            최상위 폴더 아래에 있는, assets 폴더 아래에 있는 모든 파일들을 express.static 을 통해서 연결하는 것.

 

    *******

            참고로 해당 미들웨어는 전역 미들웨어.

            */

 

 

app.listen(port, () => {

  console.log("서버가 켜졌어요!");

});

——

=> express.static 함수는app.js 파일 기준으로, 입력 값(지금은 "./assets") 경로에 있는 파일 

아무런 가공 없이 그대로 전달해주는 미들웨어.

 

이 코드 덕분에 미리 준비 되어진 프론트엔드 파일을 서빙할 수 있게 되는 것.

 

 

 

 

 

 

 

 

 

__________________________________________________

 

1-4  [할 일 메모 사이트] - API 서버 준비하기(2)

 

=>  < 2.1. 할 일 메모사이트 03 >

 

 

MongoDB 연결 준비하기.

 

 

*****

여태까지 mongoDB 관련 js 파일들은 < schemas > 라는 폴더 안에서 작업을 했지만, 이번엔 < models > 라는 이름의 폴더로.

 

 

 

폴더 : kimminsoo -> sparta -> node_js -> learning -> second_step -> todo_memo -> models -> index.js

 

 

 

 

 

 

— 몽구스 라이브러리를 가져와서 실제로 DB와 연결하는 코드가 담긴 index.js —

=> 이번에는 앞선 입문 주차때 했던 양식과 좀 달라.

 

——

< models -> index.js >

 

const mongoose = require("mongoose");

 

// localhost의 27017 포트 번호로 MongoDB와 연결.

// Database Name은 todo-demo

mongoose.connect("mongodb://localhost:27017/todo-demo", {

  useNewUrlParser: true,

  useUnifiedTopology: true,

})

  .then(value => console.log("MongoDB 연결에 성공하였습니다."))     

  .catch(reason => console.log("MongoDB 연결에 실패하였습니다."))

    // mongoose.connect() 메소드는 기본적으로 promise 속성을 가지고 있어. 성공 시 then(), 실패 시 catch() 를 실행.

 

/*

connect()메소드는 MongoClientOptions인스턴스를 두 번째 인수로 허용한다.

    useNewUrlParser: true => 몽고DB 연결 url 을 몽고DB 최신 버전에 맞게 파싱해준다.

    useUnifiedTopology: true => 최신 몽고DB 버전에 맞는 '서버 검색 및 모니터링 엔진' 을 사용하겠다.

    즉 npm 프로젝트에서 최신 버전의 몽고DB 서버에 알맞게 접근하여 데이터 정보 접근 및 검색을 할 수 있도록 해주는 옵션.

*/

 

const db = mongoose.connection;

db.on("error", console.error.bind(console, "connection error:"));

    /*

    기본적으로 mongoose.connection 는 connect() 가 성공적으로 작동하여 DB와 연결된 이후부터를 관리하는 메소드.

    mongoose.connection 이 on 상태(연결된 상태)에서 error 가 뜬다면 console.error 로 에러를 출력한다.

    */

 

module.exports = db;

        // 이번에는 db.connection; 으로 "커넥션 상태에 돌입하는 mongoose" 를 모듈화 시켰어.

——

 

 

 

 

 

 

그 다음 최상위 루트 파일인 app.js 에서 서버가 올라감과 동시에 몽고DB 를 연결.

——

< app.js >

 

// 미들웨어 및 route, 모듈 가져오기

 

const db = require("./models/index.js");

        /*

        이미 index.js 에서 전역에서 mongoose.connet() 를 선언했기 때문에

        이렇게 require 로 모듈을 가져 오는 과정에서 index.js 가 실행이 되며 바로 DB와 연결.

        따라서 connect() 이런 식으로 모듈을 호출해서 DB 를 connect 하거나 할 필요가 없어.

        */ 

 

——

=> 현재 models 의 index.js 에서는 

mongoose.connect() 이 전역에 바로 선언 되었어.

여태까지는 익명 함수에 저장헤서 함수를 호출하는 식으로 connet 를 발동시켰었지만,

이번에는 함수 안에 넣지 않고 전역으로 넣었기에 app.js 에서 index.js 를 불러오기만 하면 바로 연결이 실행.

 

즉,

const db = require("./models/index.js");

=> 이렇게 app.js 에서 선언을 해주기만 해도 index.js 의 모듈을 불러오는데,

모듈을 불러오는 과정에서 mongoose.connect() 이게 실행이 된다는 것.

 

따라서, 이전처럼 connet() 이런 식으로 모듈=함수 를 호출하지 않아도 

app.js 를 실행시키기만 해도 바로 몽고DB 와 커넥트 된다는 것.

 

 

 

 

 

 

— API를 구현하기 전에 짚고 넘어갈것 —

 

REST API란 무엇인가?

=> REST 아키텍쳐를 따라 구현된 API.

 

REST는 “Representational State Transfer”의 줄임 말.

REST(Representational State Transfer)는 월드 와이드 웹과 같은 분산 하이퍼미디어 시스템을 위한 소프트웨어 아키텍처의 한 형식

 

최대한 간단하게 설명하자면 URL, Headers, Method 등 네트워크 표현 수단을 사람이 봐도 이해하기 쉬운 표현으로 정의한다고 이해하면 돼.

또한 이 “REST 아키텍쳐”는 사람이 봐도 쉽게 이해할 수 있도록 “자원”을 정의하고 이 “자원”을 중심으로 표현을 구성하는 원칙을 제시해.

 

즉, url, 헤더, 특정 메소드만 가지고 현재 api가 어떤 식으로 구현되어 있는지를 바로 이해할 수 있도록 하는 게 REST API.

 

REST API는 “REST 아키텍쳐”라는 규칙을 따르는 API라고 생각해봐.

간단히 말하면 원래 있던 방법보다 더 쉽고 사람이 읽기 편한 방식으로 원칙을 세워놨고, 

개발자들의 생산성상호작용을 증진시키는것에 목적이 있어.

 

 

 

 

 

— Validation이란 무엇인가? —

=> 즉, 어떠한 로직을 통해서 값이 도출 되었을 때 그 값이 어떤 값인지에 따라서 후속 처리를 달리 하는 ‘검증’ 의 개념.

예외 처리 같은 것도 포함되지.

 

***

이런 데이터를 검증하는 것을 더 쉽고 간결하게 작성하도록 도와주는 < joi >라는 라이브러리를 다음 시간에 사용해 볼 예정

 

 

 

 

— api 서버 준비하면서 새롭게 배운 점들 —

  1. express.static("./assets")
  2. Mongoose 연결 및 모듈화. 이전과는 다른 방식.

 

 

 

 

 

 

 

 

 

 

 

__________________________________________________

 

1-5  [할 일 메모 사이트] - API 구현하기(1)

 

=>  < 2.1. 할 일 메모사이트 04 >

 

 

  1. Mongoose - todo 모델 작성하기
  2. 할 일 추가 API 만들기

 

 

폴더 : kimminsoo -> sparta -> node_js -> learning -> second_step -> todo_memo -> models -> todo.js

 

 

 

 

— < todo.js >  에서 schema 모델 만들기 —

 

——

const mongoose = require("mongoose");

 

const TodoSchema = new mongoose.Schema({

    value: String,          // 할 일이 어떤 것인지 확인하는 컬럼.

    doneAt: Date,           // 할 일이 언제 완료되었는지

    order: Number           // 몇 번째 할 일 인지.

})

 

/*

프론트엔드를 사용하기 위해서 추가를 해야 하는 부분.

virtual()은, 예를 들자면 우리가 데이터를 조회했을 때 자동적으로 생성되는 가상의 컬럼이야.

나는 todoId 라는 가상의 컬럼을 만들거야. get 타입으로.

아래의 컬럼 같은 경우, 

todoId 라는 가상의 컬럼을 가지고 오려고 했을 때, 해당하는 값을 ()=>{} 의 내용물의 방식대로 전달할 거다, 라는 뜻.

*/

TodoSchema.virtual("todoId").get(function () {

    return this._id.toHexString();    // _id는 ObjectId 라는 고유의 타입을 지니고 있어. 이 타입을 바꿔서 사용할 거야.

})

 

/*

virtual() 을 실질적으로 사용하기 위해서는 TodoSchema.set() 이라는,

TodoSchema를 사용하기 위해서 '어떤 타입' 으로 변경을 했을 때 보여줄 것인가, 에 대한 설정을 해줄 수 있어.

*/

TodoSchema.set("toJSON", { virtuals: true })

            // JSON 타입으로 스키마를 변경할 때에는 해당하는 가상 타입을 보내줄 수 있도록 설정.

            /*

            좀 더 달리 말하자면.

 

            나중에 Todo 라는 스키마 모델을 모듈로서 밖으로 내보낼 건데,

            해당 모델을 바탕으로 데이터를 조회하거나 생성했을때, 

            "todoId" 라는 걸 JSON 타입으로 변경했을 때 보여준다.

            */

 

 

module.exports = mongoose.model("Todo", TodoSchema)

——

=> virtual() 이란 걸 사용했어. 해당 스키마 모델을 사용할 때 생성되는 가상의 칼럼.. 이라는 듯?

 

 

 

— virtual() 교재 설명 —

mongoose 라이브러리에서는 MongoDB 내부에는 특정 Column이 존재하지 않지만, 

데이터를 조회할 때 가상의 Column을 추가하여 좀 더 편리하게 데이터를 가공하거나 사용할 수 있도록 도와줍니다.

 

Schema.set(”toJSON”, {virtuals: true})은 JSON 타입으로 해당 Schema를 변환할 때 가상값인 todoId를 반환하도록 설정하는 것 입니다.

 

Studio 3Tmongoose의 데이터를 비교해보았을 때, virtual로 등록된 todoId 값이 MongoDB에는 존재하지 않지만, 

mongoose를 이용해 조회하였을 때는 값이 존재하는것을 확인할 수 있습니다.

 

여기서 vitual로 설정한 todoId는 return this._id; 를 이용해 _id 즉, Todo Schema의 **ObjectId**를 반환하는것으로 확인할 수 있습니다.

 

 

 

**************

즉, mongoDB 의 컬렉션에는 입력되어 있지 않은 필드이지만,

해당 레코드(도큐먼트)를 조회하거나 생성하면서 레코드 값을 가져오려 할 때, 가상의 필드(칼럼) 가 추가되어 있는 형태로 {} 결과값을 가져오겠다.

 

Schema.set(”toJSON”, {virtuals: true})는 {} 결과값을 JSON 타입으로 변환할 때 가상값인 “todoId” 를 반환하도록 설정하는 것.

* 실제로 mongoose 를 통해서 레코드를 조회해 보면 Studio 3T 에서 조회했을 때랑은 필드 구성이 다른 것을 확인할 수 있을거야.

 

여기서 vitual로 설정한 “todoId” 는 return this._id; 를 이용해 _id 즉, 

Todo SchemaObjectId를 반환하는것으로 확인할 수 있어.

 

 

 

** 내 방식대로 말을 변경해서 이해해보자 **

 

즉 mongoose 를 통해 조회한 레코드의 필드 구성 중에 “todoId” 라는 필드를 하나 더 덧붙여서 최종적으로 결과값을 리턴 받을 건데,

이 “todoId” 라는 필드에는 mongoDB 에 등록된 레코드의 “_id” 필드의 값인 ObjectIs 를 toHexString() 해서 담을 거다

*** 레코드를 toJSON 할 때 virtual 도 함께 호출. 

 

 

 

 

________________

 

다음으로 ‘할 일을 추가하는 API’ 를 만들어보자.

 

 

——

< todos.router.js >

 

// 경로 : localhost:8000/api/todos              할 일 추가.

router.post('/todos', async (req, res) => {

    const {value} = req.body;

    const maxOrderByUserId = await Todo.findOne().sort("-order").exec();

        // 하나를 select 할 건데, 조회 방식이 order 를 기준으로 해당값이 가장 높은 하나를 조회할거다.

        // find() 는 하나라도 있으면 [{}] 인데, findOne() 은 {} 로 반환.

        /*

        공식 문서를 보면 find, findOne, findById, findOneAndUpdate 등의 메서드의 리턴값은 Query라고 되어 있다. 

        Mongoose Query는 프로미스가 아니고, then을 사용할 수 있는 일종의 유사 프로미스라고 할 수 있다.

        find, findOne 등의 메서드 뒤에 exec()을 붙이든 안 붙이든 기능은 동일하다.

        대신 exec()을 사용하면 유사 프로미스가 아닌 온전한 프로미스를 반환값으로 얻을 수 있으며, 

        에러가 났을 때 stack trace에 오류가 발생한 코드의 위치가 포함되기 때문에 

        공식 문서에서도 exec()을 사용할 것을 권장하고 있다.

        */

    const order = maxOrderByUserId ? maxOrderByUserId.order + 1 : 1;

            // 논리 삼항식. order 가 가장 높은 수치에서 1을 더할 것이고, 만약 레코드 자체가 없다면 첫 todo 이니 1로 설정.

 

    // order 값을 바탕으로 '할 일' 을 레코드로서 Insert 를 해볼 거야.

    const todo = new Todo({value, order});      // Todo schema 모델을 바탕으로 데이터 레코드(도큐먼트) 를 생성.

    await todo.save();      // 실제로 컬렉션(테이블) 에 insert

        /*

        정리를 좀 해보자.

        1. Todo 는 mongoose.schema 를 기반으로 만들어진 도큐먼트 모델이야.

                    => const TodoSchema = new mongoose.Schema({})

        2. 따라서 Todo 는 mongoose 라이브러리 속성을 가지고 있기 때문에, Todo.create, Todo.save 등

            mongoose 라이브러리가 가지고 있는 여러 기능과 속성들을 호출할 수 있어.

        3. 이렇게 mongoose 가 가진 기능을 호출할 경우, 현재 connection 상태인 mongoDB에 접근해서

            실제로 mongoose 문법을 반영하여 DB의 데이터들을 조회하거나 변화를 줄 수 있어.   

    아마 mongoose 가 내부적으로 현재 mongoDB 와 connection 이 원활한 상태인지 확인하고,

            문제가 없다 판단되면 DB에 직접 접근하는 느낌인듯?

        */

    

    res.send({todo});

})

——

 

 

 

 

 

 

 

 

 

__________________________________________________

 

1-6  [할 일 메모 사이트] - API 구현하기(2)

 

=>  < 2.1. 할 일 메모사이트 05 >

 

 

 

 

 

 

 

— todos.router.js —

——

// 경로 : localhost:8000/api/todos              할 일 목록 조회

router.get('/todos', async (req, res) => {

    const todos = await Todo.find().sort("-order").exec();

        // order 기준으로, 내림차순 정렬.

    

    res.json({todos});  // 이렇게 하면 키와 벨류를 자동으로 만들어 주더라.

})

——

=> 참고로, http://localhost:8000/ 으로 접속하면

assets 리액트 양식이 반영된 상태에서 해당 todo 리스트를 확인할 수 있어.

이건 뭐냐면, 

app.js 에서 app.use(express.static("./assets")); 

이걸로, 전역 미들웨어에서 정적 파일인 asstes > index.html 을 조회할 수 있도록 해 놨기 때문.

 

추가로, 8000/ 으로만 접근했는데 어떻게 /api/todos 까지 호출할 수 있는 건가.

아마 리액트에서 내부적으로 get 으로 api/todos 를 호출하고 있나봐.

 

 

 

 

 

 

— 다음으로, 할 일 순서 변경하는 API 만들기. —

 

——

// 경로 : localhost:8000/api/todos/:todoId      할 일 순서 변경

router.patch('/todos/:todoId', async (req, res) => {

    const {todoId} = req.params;  // todoId 는 프론트에서 받는다고 가정. 조회할 때 virtual 로 todoId 받았잖아.

    const {order} = req.body;     // 해당 '할 일' 의 order 도 프론트에서 받는다고 가정.

                                // up을 누르면 바로 위 order, down 을 누르면 바로 아래 order 가 들어오나?

 

    const currentTodo = await Todo.findById(todoId)

                // findById() 로 조회를 실행하면, mongoose 가 알아서 todoId를 _id 랑 매칭시켜줘.

    if (!currentTodo) {

        return res.status(200).json({"errorMessage": "해당 할 일이 존재하지 않습니다."})

    }

    if (order) {

        const targetTodo = await Todo.findOne({order}).exec();  // findOne 이라서 {} 형태. 배열이 아냐.

        if (targetTodo) {

            targetTodo.order = currentTodo.order;

            await targetTodo.save();    

                // findOne 으로 조회한 targetTodo 를 수정한 후 sava()를 호출하면, DB 안에 값이 변경된 상태로 저장됨.

                // mongoDB 내부적으로는 update 가 돌아갈텐데... _id 값으로 매칭해서 하는건가?

// 아마 sava() 라는 게, 뭔가를 기준으로 매칭을 해서 있으면 Update 하고 없으면 insert 하는 듯?

        }

        currentTodo.order = order;

        await currentTodo.save();

    }

    

    res.send({});

})

——

=> save() 가 좀 재밌네.

리액트에서 이전, 이후 order 가져올 때는 인덱스로 +1, -1 해서 있으면 버튼 활성화, 없으면 버튼 비활성화 시키나 본데?

 

 

'내일배움캠프_개발일지 > Node.js 기초' 카테고리의 다른 글

Node.js 숙련 - 3  (0) 2022.12.19
Node.js 숙련 - 2  (0) 2022.12.16
Node.js 입문 - 3  (0) 2022.12.14
Node.js 입문-2  (1) 2022.12.13
Node.js 입문 - 1  (0) 2022.12.13