Node.js CRUD 서버 구축해보기 (1)

Taro·2023년 12월 13일
0

Node.js

목록 보기
9/9

Node.js 를 사용하여 기본적인 CRUD 서버를 구축해보려고 한다

Node.js 는 JavaScript 런타임 으로써
Single Thread , Non-blocking I/O 이다

node init 설치

$ npm init

npm -> 라이브러리 설치를 쉽게 도와주는 도구
npm init -> package.json 파일 생성용 명령어

express 설치

$ npm install express

express : Node.js 를 서버를 구현하게 해주는 라이브러리 집합체

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(`Example app listening on port ${port}`)
})

/news 로 접속하면 들어가면 Hello news 화면이 나오게 된다

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

html 파일 보내기

html 보내서 좀더 가시성을 높히고 싶다면

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  hello
</body>
</html> 

파일 전송시에는

app.get('/', function(req, res) {
  res.sendFile(__dirname + '/index.html')
})

dirmame 이라고 치면 현재 server.js 의 절대경로
dirname 뒤에 /index.html 추가시 /index.html 파일경로를 출력해준다

static 파일 (css) 추가하기

html 파일에서 css파일을 가져다 쓸 경우 우선 해당 css 파일이 들어있는 폴더를 서버에 등록해야한다

일반적으로 css 파일은 public 폴더 안에다 넣는다

app.use(express.static(__dirname + '/public'));

이렇게 서버가 있는 메인에 미들웨어처럼 넣어주면된다

폴더구조

구현하려는것

서버 : Node.js express
DB : MongoDB
기능 : 회원가입 CRUD

main

app.ts : 각 기능별 API method
server.ts : mongoDB 연결 method

mongoDB atlas

이전에 작성해둔 글 참고하여 연결시킨다

MongoDB 연결하기

# server.ts 
# 서버 접속 및 MongoDB 연결코드 

import mongoose from 'mongoose';

import app from './app';

const port = 3000;

mongoose.connect('mongodb://localhost/ex2').then(() => {
  console.log('Connected to MongoDB');
  app.listen(port, () => {
    console.log(`Example app listening on port ${port}`);
  });
});

 

# app.ts
import cors from 'cors' 
import express from 'express';

const app = express();

app.use(cors());
app.oprion('*', cors());
app.use(express.json());
app.use(express.urlencoed({extended: false});
        
app.post('/login', AuthRoutes.login);

app.post('/register', UserRoutes.register);
app.patch('/user', MAuth, UserRoutes.updateUser);
app.delete('/user', UserRoutes.deleteUser);
app.get('/user/:id', UserRoutes.findUser);

export default app;

각 Api 별로 나눠서 구현해보자

Model,Schema

MongoDB는 고정적인 스키마를 갖지 않기 때문에 mongoose를 사용해서 스키마를 생성하는데 데이터베이스 서버측에서 만드는 스키마가 아닌 우리의 웹서버가 DB에 들어있는 문서들을 객체화하여 사용 할 수 있도록 스키마를 설정해주어야 한다.

  • Model : 스키마를 사용해서 만드는 인스턴스로 DB에서 실제 작업을 처리할 수 있는 함수를 지니는 객체

  • Schema : 스키마는 컬렉션에 들어가는 문서 내부의 각 필드가 어떤 형식으로 되어 있는지 정의하는 객체

User Schema 는 기본적으로 id , password , name , regDate 를 값으로 가지도록 설정했다

# Models/User.ts

import { Schema, model } from 'mongoose';

export interface User {
  id: string;
  password: string;
  name?: string;
  regDate?: Date;
}

const userSchema = new Schema<User>(
  {
    id: { type: String, required: true, unique: true },
    password: { type: String, required: true },
    name: { type: String },
    regDate: { type: Date, default: Date.now },
  },
  { versionKey: false },
);

// versionkey : mongoDB 자동 업데이트 방지 일과성 유지
// Document : mongoDB 컬렉션 내 단일 레코드
// model : mongoose 에서 Schema 기반으로 생성되며 MongoDB 컬렉션과 상호 작용하는 인터페이스

const UserModel = model<User>('users', userSchema);

export default UserModel;

Register 구현해보기

#routes/User.ts

static register = async (req: Request, res: Response) => {
    const { id, password, name } = req.body;

    if (!id) {
      res.status(400).send(new ErrorResponse(-301, 'Id required'));
      return;
    } else {
      const user: { _id: Types.ObjectId } | null = await UserModel.exists({ id });

      if (user) {
        res.status(400).send(new ErrorResponse(-409, 'Id duplicated'));
        return;
      }
    }

    if (!password) {
      res.status(400).send(new ErrorResponse(-302, 'Password required'));
      return;
    }

    if (!name) {
      res.status(400).send(new ErrorResponse(-304, 'Name required'));
      return;
    }

    const mongoSession = await UserModel.startSession();
    mongoSession.startTransaction();

    try {
      const user: User | null = await new UserModel({
        id,
        password,
        name,
      }).save();
      if (!user) {
        await mongoSession.abortTransaction();
        res.status(500).send(new ErrorResponse(-1, 'Internal server error'));
      } else {
        await mongoSession.commitTransaction();

        res.status(200).json({
          msg: '회원가입이 완료되었습니다.',
          id: user.id,
          name: user.name,
        });
      }
    } catch (err: unknown) {
      await mongoSession.abortTransaction();

      res.status(500).send(new ErrorResponse(-1, 'Internal server error'));
    } finally {
      await mongoSession.endSession();
    }
  };

각 예외는 Status 상태코드를 최대한 적용하여 예외처리를 해봤다

Api Test

Post man 을 사용하여 만들어둔 api 를 테스트 해보자

User 의 Schema에서 정의해둔 id, name , password 를 입력해주면 메시지 표기와 함께 정상적으로 값이 들어갔음을 확인 가능하다

Login 구현해보기

계속해서 login 까지 만들어보자

class AuthRoutes {
  static login = async (req: Request, res: Response) => {
    const { id, password } = req.body;

    if (!id) {
      res.status(400).send(new ErrorResponse(-301, 'Id required'));

      return;
    }

    if (!password) {
      res.status(400).send(new ErrorResponse(-302, 'Password required'));

      return;
    }
    try {
      const user: User | null = await UserModel.findOne({ id, password });

      if (!user) {
        res.status(404).send(new ErrorResponse(-404, 'User not found'));
        return;
      }
      res.status(200).json({
        msg: 'User Login Success',
        userId: user.id,
      });
    } catch (err: unknown) {
      res.status(500).send(new ErrorResponse(-1, 'Internal server Error'));
    }
  };
}

Api Test

Update 구현해보기

이번에는 기존에 만들어둔 userts routes 를
fatch() 를 통해

static updateUser = async (req: Request, res: Response) => {
    const { id, name } = req.body;

    if (!id && !name) {
      res.status(400).send(new ErrorResponse(-341, 'id and name required'));
      return;
    }
    try {
      const mongoSession = await UserModel.startSession();
      mongoSession.startTransaction();

      try {
        const Userupdate: User | null = await UserModel.findOneAndUpdate(
          { id },
          { name },
          { runValidators: true, new: true },
        );

        if (!Userupdate) {
          await mongoSession.abortTransaction();
          res.status(404).send(new ErrorResponse(404, 'User not found'));
        } else {
          await mongoSession.commitTransaction();
          res.status(200).json({
            msg: 'name changed',
            name: Userupdate.name,
          });
        }
      } catch (error: unknown) {
        await mongoSession.abortTransaction();
        res.status(500).send(new ErrorResponse(-1, 'Internal server error'));
      } finally {
        mongoSession.endSession();
      }
    } catch (error: unknown) {
      res.status(500).send(new ErrorResponse(-1, 'Internal server error'));
    }
  };

Api Test

Delete 구현해보기

static deleteUser = async (req: Request, res: Response) => {
    const { id } = req.body;
    try {
      const mongoSession = await UserModel.startSession();
      mongoSession.startTransaction();

      const user: User | null = await UserModel.findOne({ id });

      if (!user) {
        res.status(404).send(new ErrorResponse(-341, 'User not found'));
        return;
      }
      try {
        const deleteUser = await UserModel.findOneAndDelete({ id });
        if (!deleteUser) {
          await mongoSession.abortTransaction();
          res.status(404).send(new ErrorResponse(404, 'User not found'));
        }
        await mongoSession.commitTransaction();
        res.status(200).send({ msg: 'user deleted' });
      } catch (error: unknown) {
        res.status(500).send(new ErrorResponse(-1, 'Internal server error'));
      } finally {
        mongoSession.endSession();
      }
    } catch (error: unknown) {
      res.status(500).send(new ErrorResponse(-1, 'Internal server error'));
    }
  };

Api Tets

Node.js Express와, MongoDB, typeScript 를 사용하여서 최대한 RestFul 하게 CRUD 를 연습해보았다 다음에는 JWT를 적용해봐야겠다

profile
기록하며 공부하는곳

0개의 댓글