저장을 습관화

과제 기록 - Node.js, express, MongoDB, mongoose, AWS EC2 본문

공부/node.js

과제 기록 - Node.js, express, MongoDB, mongoose, AWS EC2

ctrs 2023. 6. 18. 19:38

이번 과제에서 한 내용 요약

- Node.js, express를 통한 웹 서비스와 CRUD 기능이 담긴 REST API 구축

- MongoDB/mongoose를 통한 DB 구축

- 구축한 서비스를 AWS EC2를 통하여 배포

 

 

1. 과제 요구 조건에 맞춘 Directory Stucture 구성

- 요구 조건

.
├── app.js
├── routes
│   ├── index.js
│   ├── comments.js
│   └── posts.js
└── schemas
    ├── index.js
    ├── comment.js
    └── post.js

- 생성

 

2. package.json 생성

$ npm init

별도 입력사항 없음

$ npm init -y를 입력하여 입력사항을 전부 생략하고 바로 설치도 가능하다

모든 질문사항에 yes를 입력한 것과 같다

"main" app.js를 실행시킴으로 이 프로젝트를 시작한다는 의미이다.

 

3. express 설치

$ npm install express

별도 입력사항 없음

 

node_modules 폴더와 package-lock.json 파일 생성 확인

 

4. express를 통한 웹 서버 가동 확인

 

- app.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, "포트로 서버가 열렸어요!");
});

 

- 터미널

$ node app.js

 

로컬호스트 3000 포트로 웹 접속 확인

※ 만약 웹 접속이 안된다면 express의 실행을 중지하고

기존에 3000 포트를 사용하고 있던 프로그램이 있는지 확인한다

 

 

VSC 확장 프로그램 Thunder Client를 사용하여 실행 결과와 더불어 상태코드도 확인할 수 있다.

HTTP 메소드는 GET을 선택, 상단 입력창에 localhost:3000을 입력하고 SEND를 클릭하면

상태코드 200과 함께 어떤 결과가 출력되는지 확인 가능하다

 

[참조]

HTTP의 상태 코드

https://developer.mozilla.org/ko/docs/Web/HTTP/Status

 

HTTP 상태 코드 - HTTP | MDN

HTTP 응답 상태 코드는 특정 HTTP 요청이 성공적으로 완료되었는지 알려줍니다. 응답은 5개의 그룹으로 나누어집니다: 정보를 제공하는 응답, 성공적인 응답, 리다이렉트, 클라이언트 에러, 그리고

developer.mozilla.org

 

5. 웹 서버 내용 추가

- posts.js

const express = require("express"); // express를 할당받는다
const router = express.Router(); // 객체 express 안에 있는 함수 Router()를 실행시켜 그 값을 변수 router에 할당한다

// localhost:3000/api/ 접근 시 출력되는 내용
router.get("/", (req, res) => {
  res.send("default url for posts.js GET Method");
});

// localhost:3000/api/about으로 접근했을때 출력되는 내용
router.get("/about", (req, res) => {
  res.send("posts.js about PATH");
});

module.exports = router;
// 위 내용을 app.js로 보내기 위한 export

 

- app.js

const express = require("express");
const app = express();
const port = 3000;
const postsRouter = require("./routes/posts.js"); // /routes/posts.js의 내용을 받아와 변수 postsRouter에 할당한다

app.get("/", (req, res) => {
  res.send(`Hello World! <br>
  안녕하세요 정호중입니다. <br>
  제 블로그는 https://ctrs.tistory.com/ 입니다.`);
});

app.use("/api", postsRouter); // app.use는 express에게 모든 미들웨어가 이 곳을 통과할것이다라는 것을 알려주기 위함
// 기본 URL 뒤 /api 가 경로로서 추가될 경우 사용자를 postsRouter 라우터로 보낸다.
// localhost:3000/api -> postsRouter

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

 

localhost:3000/api
localhost:3000/api/about

 

 

6. 웹 서버 조회 기능 확인

- posts.js

---(중략)---
// posts(게시글)의 카테고리
const posts = [
  {
    postsId: 1,
    postsAuthor: "jhj",
    postPw: "1234", //number로 사용할 예정
    postTitle: "test",
    postCnt: "content",
    postDate: Date,
  },
];

// posts 모두 조회
router.get("/posts", (req, res) => {
  res.status(200).json({ posts: posts });
});

module.exports = router;
// 위 내용을 app.js로 보내기 위한 export

express 재실행 후

http://localhost:3000/api/posts 접속

JSON 형태로 출력되는것이 확인

하지만 위와 같은 형태도 내용이 많으면 읽기가 힘드니 

크롬 브라우저 확장 프로그램 JSONVue 설치

깔끔

 

게시글 목록 posts에서 postId를 기준으로 하나만 검색하려면

- posts.js

---(중략)---
// posts 하나만 검색
router.get("/posts/:postsId", (req, res) => {
  const { postsId } = req.params;

  let result = null;

  for (const post of posts) {
    if (Number(postsId) === post.postsId) {
      result = post;
    }
  }
  
  // 위 for문은 아래와 같이 한줄로 축약할 수도 있음
  // const [result] = goods.filter((good) => Number(postsId) === this.post.postsId);

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

module.exports = router;
// 위 내용을 app.js로 보내기 위한 export

http://localhost:3000/api/posts/1

postsId 1번을 검색하였을때

http://localhost:3000/api/posts/99

존재하지않는 ostsId 99를 검색하였을때, null 출력

 

7. DBMS - MongoDB와 mongoose, studio 3T 사용

 

1) MongoDB 설치 - Windows 10 기준

C드라이브에 data\db 폴더를 생성한다

 

2) MongoDB 설치 파일 다운로드, 실행

https://www.mongodb.com/try/download/community

 

Try MongoDB Community Edition

Try MongoDB Community Edition on premise non-relational database including the Community Server and Community Kubernetes Operator for your next big project!

www.mongodb.com

 

※ 설치 중 주의할 점

설치 경로를 사전에 만들었던 c:\data\db로 지정한다.

 

MongoDB Compass는 설치하지 않는다.

 

3) 설치 완료 후

제어판 -> 시스템 및 보안 -> 시스템 -> 고급 시스템 설정  -> 고급 -> 환경 변수 -> 시스템 변수 에서

Path에 MongoDB의 경로 C:\data\db\bin를 추가한다.

 

4) 설치 확인

윈도우의 cmd 창에서 아래 명령어를 입력한다.

mongod --install --serviceName MongoDB --serviceDisplayName MongoDB --dbpath C:\data\db --logpath C:\data\db\log\mongoservice.log --logappend

MongoDB가 제대로 설치되지 않았다면 내부 또는 외부 명령.....이 아닙니다. 라는 메세지가 나오지만

제대로 설치되었다면 아무런 메세지도 나오지 않고 명령이 완료된 듯한 표시가 나오게 된다.

 

이전 버전의 MongoDB에서는  cmd에 mongo 를 입력하여 현재 버전을 확인할 수 있었으나

6.0 버전 부터는 mongo를 입력하여도 내부 또는 외부 명령.. 이 아닙니다.라는 메세지가 나온다.

 

대신 웹 브라우저에서 localhost:27017에 접속하는 방법으로 정상적으로 설치되었는지 확인할 수 있다.

MongoDB 설치 완료

 

5) Studio 3T 설치

https://robomongo.org/

 

Robo 3T | Free, open-source MongoDB GUI (formerly Robomongo)

Read by Dmitry Schetnikovich

robomongo.org

Studio 3T의 설치 중 주의 사항은 없다.

구글 계정으로 로그인하고, 디폴트 설정으로 설치한다.

 

 

8. DB 연결

1) studio 3T 실행

좌측 상단 Connect -> New Connection

Manually configure my connection settings 체크, Next

Connection Name 지정, 서버와 port는 디폴트 사용, Save

Connection Manager 서버 목록으로 돌아와서

새로 지정한 서버 선택, Connect

 

2) 서버가 연결 되었으면 연결된 서버를 우클릭, Add Database... 클릭

DB명 지정 후 OK 선택

 

3) Studio 3T GUI를 이용한 기본 사용법, DB 우클릭, Open IntelliShell 클릭

db.Gaein_Gwaje.insertOne({ key: "value", key2: "값" }) // 내용 추가

db.Gaein_Gwaje.find({}) // 내용 조회

db.Gaein_Gwaje.deleteOne({ _id: ObjectId("...")}) // 내용 삭제, "..."에 _id의 내용이 들어가야한다.

- 내용 추가 및 조회

 

- 내용 삭제 및 조회

 

 

 

9. express 내부에서 MongoDB 사용하기

1) mongoose 설치

$ npm install mongoose

 

2) /schemas/index.js 파일 생성

const mongoose = require("mongoose"); // mongoose 라이브러리를 가져와 변수 mongoose에 할당한다.

const connect = () => {
  mongoose
    .connect("mongodb://127.0.0.1:27017/Gaein_Gwaje") // mongoose로 연결할 mongoDB 서버와 DB를 입력한다.
    // 주소는 localhost도 가능하지만 127.0.0.1로 적는 편이 좋다.
    .catch((err) => console.log(err));
  // MongoDB에 연결시 실패하였을 경우 err 로그를 남긴다.
};

mongoose.connection.on("error", (err) => {
  // MongoDB에 연결이 실패하였을 경우 에러 로그 발생
  console.error("몽고디비 연결 에러", err);
});

module.exports = connect;
// 위 내용을 app.js에서 쓰기위한 exports

mongoose를 이용하여 mongoDB 서버에 접근할때 localhost 대신 127.0.0.1을 쓴 이유

https://ctrs.tistory.com/71

 

에러 기록 - MongooseServerSelectionError: connect ECONNREFUSED ::1:27017

순서 1. 에러 발생 상황과 증상 2. 해결 방법 1. 에러 발생 상황과 증상 node.js 공부 중 app.js 파일에서 express를 이용해 웹서버를 가동시키고, ./schemas/index.js에 등록되어있는 내용에 따라 mongoDB에 연

ctrs.tistory.com

 

3) routes/posts.js

const express = require("express"); // express를 할당받는다

const router = express.Router(); // 객체 express 안에 있는 함수 Router()를 실행시켜 그 값을 변수 router에 할당한다

const posts = [
  {
    postsId: 1,
    user: "test",
    password: "1234",
    title: "test title",
    content: "test content",
    createdAt: "",
  },
  {
    postsId: 2,
    user: "test2",
    password: "1234",
    title: "test title2",
    content: "test content",
    createdAt: "",
  },
];

// localhost:3000/api/ 접근 시 출력되는 내용
router.get("/", (req, res) => {
  // res.send("default url for posts.js GET Method");
  res.send("/posts와 /comments를 확인해주세요");
});

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

// posts 상세 조회 API
router.get("/posts/:postsId", (req, res) => {
  const { postsId } = req.params;

  let result = null;

  for (const post of posts) {
    if (Number(postsId) === post.postsId) {
      result = post;
    }
  }

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

// 게시글 작성
const Posts = require("../schemas/post.js");
router.post("/posts/", async (req, res) => {
  const { postsId, user, password, title, content, createdAt } = req.body;
  const posts = await Posts.find({ postsId });

  if (posts.length) {
    // posts의 길이가 0이 아니라는 것은 내용이 있다는 의미이다.
    return res.status(400).json({
      success: false,
      errorMessage: "이미 존재하는 postsId입니다.",
    });
  }
  const createdPosts = await Posts.create({
    postsId,
    user,
    password,
    title,
    content,
    createdAt,
  });

  res.json({ posts: createdPosts });
});

module.exports = router;
// 위 내용을 app.js로 보내기 위한 export

 

4) /schemas/post.js

const mongoose = require("mongoose");

const postsSchema = new mongoose.Schema({
  postsId: {
    type: Number,
    required: true, // 이 값은 필수적이다.
    unique: true, // 이 값은 유일하다.
  },
  user: {
    type: String,
    required: true,
  },
  password: {
    type: String, // 문자와 숫자를 혼합한 패스워드를 사용할 수도 있으므로 데이터 타입은 문자열을 사용한다.
    required: true,
  },
  title: {
    type: String,
    required: true,
  },
  content: {
    type: String,
    required: true,
  },
  createdAt: {
    type: Date,
  },
});

module.exports = mongoose.model("posts", postsSchema);

 

5) 게시글 작성 테스트 (CRUD 중 Create)

http 메소드: post

경로: localhost:3000/api/posts

Body JSON Content:

{
   "postsId": 3,
   "user": "POST Test",
   "password": "1234",
   "title": "post test",
   "content": "is it work?",
   "createdAt": "2023-06-15T14:29:07.654Z"
}

SEND를 클릭하였고, 정상적으로 DB에 추가된 것 확인

Thnder Client
Studio 3T

 

 

10. 게시글의 수정과 삭제

// 게시글 수정, put
router.put("/posts/:postsId", async (req, res) => {
  const { postsId } = req.params;
  const { content } = req.body;
  const existPosts = await Posts.find({ postsId });

  if (existPosts.length) {
    await Posts.updateOne({ postsId: postsId }, { $set: { content: content } });
    // postsId가 일치할때 content의 내용을 새로 입력받은 content로 교체하겠다는 의미
    // $set은 MongoDB의 업데이트 연산자이다.
  }
  res.status(200).json({ success: true });
});

// 게시글 삭제, delete
router.delete("/posts/:postsId", async (req, res) => {
  const { postsId } = req.params;
  const existPosts = await Posts.find({ postsId });

  if (existPosts.length) {
    await Posts.deleteOne({ postsId });
  }

  res.json({ result: "삭제 완료" });
});

 

스크린샷 생략