본문 바로가기

Node.js

Node.js(16) - Backend 구현(dotenv, config, 예외처리) 및 User Table 추가(ORM 적용: sequelize 활용)

728x90
반응형

1) Node.js

   1-1) Back-end 구현(dotenv, config, 예외처리)

   1-2) Back-end User Table 추가(ORM 적용: sequelize) 

 

 

 

 

 

 

 

 

1) Node.js

1-1) Back-end 구현(dotenv, config, 예외처리)

| GET /comments
| GET /comments/:id
| POST /comments
| PUT /comments/:id
| DELETE /comments/:id

dotenv(.env) 라이브러리: 시스템 환경변수를 ".env 파일"을 읽어서 적용시키는 것을 의미함!

참고로 ".env 파일" 상의 "NODE_ENV=development"는 개발모드를 의미한다!!

 

Router --> Controller --> Service --> Repository --> pool

exceptions 디렉토리: 예외처리 역할

 

 

 

 

back

server.js

// require("dotenv").config;
// --> require를 통해 dotenv 파일을 가져와서 config를 호출하면 ".env 파일"을 읽고 해당 파일에서 변수를 읽고 시스템 환경변수(process.env)에 해당 변수값을 잠깐 넣어놓음!
// --> 이렇게 되면 "const PORT = process.env.PORT || 3000;"의 경우, "process.env" 객체의 PORT 변수에 ".env 파일"에서 지정한 PORT 값인 3001이 할당됨!!
const express = require("express");
const app = express();
const config = require("./config");
const HttpException = config.exception.HttpException;
// const PORT = process.env.PORT || 3000;

const router = require("./routes");

app.use(router);
app.use((error, req, res, next) => {
  // 응답 코드 500
  // if (error instanceof Error) {}
  console.error(error);
  if (error instanceof HttpException) {
    res.json({
      isError: true,
      message: error.message,
      status: error.status,
    });
  } else if (error instanceof Error) {
    res.json({
      isError: true,
      message: error.message,
    });
  }
});

app.listen(config.port, async () => {
  console.log(`server start ${config.port}`);
});

 

 

config.js

require("dotenv").config();
const HttpException = require("./exceptions/HTTPException");

const host = process.env.DB_HOST || "127.0.0.1";
const port = process.env.DB_PORT || 3306;
const user = process.env.DB_USER || "본인 MySQL user명";
const password = process.env.DB_PASSWORD || "본인 MySQL 비밀번호";
const database = process.env.DB_DATABASE || "comments";

const config = {
  exception: {
    HttpException,
  },
  port: process.env.PORT || 3000,
  db: {
    host,
    port,
    user,
    password,
    database,
  },
};

module.exports = config;

 

 

exceptions/HTTPException.js

class HttpException extends Error {
  constructor(message) {
    super(message);
    this.status = 500;
  }
}

// const e = new HTTPException("요청 데이터가 없음!!", 500);
// console.log(e);
// console.log(e.message, e.status);

// console.log(typeof e); // "e"는 인스턴스화 된 것이기에 타입은 객체(object)이다!!

module.exports = HttpException;

 

 

routes/index.js

const express = require("express");
const router = express.Router();
const commentRouter = require("../comment/comment.route");

router.use("/comments", commentRouter);

module.exports = router;

 

 

routes/comment.route.js

const express = require("express");
const router = express.Router();
const { controller } = require("./comment.module");

router.get("/", (req, res, next) => controller.getList(req, res, next));

module.exports = router;

 

 

comment/comment.controller.js

class CommentController {
  constructor({ commentService }) {
    this.commentService = commentService;
  }

  async getList(req, res, next) {
    try {
      const comments = await this.commentService.list();
      res.json(comments);
    } catch (e) {
      next(e);
    }
  }
}

module.exports = CommentController;

 

 

comment/comment.service.js

/*
module.exports = (repository) => {
  return {
    list: async () => {
      const list = await repository.findAll();
      return list;
    },
  };
};
*/

class CommentService {
  constructor({ commentRepository, config }) {
    this.commentRepository = commentRepository;
    this.config = config;
    this.HttpException = config.exception.HttpException;
  }

  async list() {
    try {
      const list = await this.commentRepository.findAll();
      // if (list.length === 0) throw new Error("내용이 없음");
      throw "내용이 없음";
      return list;
    } catch (e) {
      // throw new Error(e);
      throw new this.HttpException(e);
    }
  }
}

module.exports = CommentService;

 

 

comment/comment.repository.js

/*
module.exports = (mysql) => {
  return {
    findAll: async () => {
      const result = await mysql.query("SELECT * FROM Comment");
      return result;
    },
  };
};
*/

// 바로 아래의 CommentRepository class와 비슷한 원리의 생성자 함수
// function Repository(mysql) {
//   // this = {}
//   this.mysql = mysql;

//   this.list = async () => {
//     this.mysql;
//   };
//   // return this
// }

class CommentRepository {
  constructor({ mysql }) {
    this.mysql = mysql;
  }

  async findAll() {
    try {
      const [list] = await this.mysql.query("SELECT * FROM Comment");
      return list;
    } catch (e) {
      throw new Error(e);
    }
  }

  view() {}

  update() {}

  create() {}
}

module.exports = CommentRepository;

 

 

comment/comment.module.js

/*
const mysql = require("../models");
const repository = require("./comment.repository")(mysql);
const service = require("./comment.service")(repository);
const controller = require("./comment.controller")(service);

repository.findAll().then((data) => console.log(data));
service.list().then((data) => console.log(data));
*/

// 인스턴스를 생성해서 내보내주는 역할이 "comment.module.js"의 역할이다!!
const config = require("../config");
const mysql = require("../models");
const CommentRepository = require("./comment.repository");
const CommentService = require("./comment.service");
const CommentController = require("./comment.controller");

const repository = new CommentRepository({ mysql });
const service = new CommentService({ commentRepository: repository, config });
const controller = new CommentController({ commentService: service });

// repository.findAll().then((data) => {
//   console.log(data);
// });

// service.list().then((data) => {
//   console.log(data);
// });

module.exports = {
  repository,
  service,
  controller,
};

 

 

models/index.js

// require("dotenv").config();
const mysql = require("mysql2");
const config = require("../config");

// const host = process.env.DB_HOST || "127.0.0.1";
// const port = process.env.DB_PORT || 3306;
// const user = process.env.DB_USER || "본인 user명";
// const password = process.env.DB_PASSWORD || "본인 MySQL 비밀번호";
// const database = process.env.DB_DATABASE || "comments";

const pool = mysql.createPool(config.db).promise();

// const pool = mysql
//   .createPool({
//     host,
//     port,
//     user,
//     password,
//     database,
//   })
//   .promise();

// pool.query("SELECT * FROM Comment").then((data) => console.log(data));

module.exports = pool;

 

 

.env

PORT=3001
NODE_ENV=development
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=본인 user명
DB_PASSWORD=본인 MySQL 비밀번호
DB_DATABASE=comments

 

 

 

 

 

1-2) Back-end User Table 추가(ORM 적용: sequelize)

ORM
정의: "Object Relational Mapping"이라는 의미로, 데이터베이스 상의 데이터를
"Table" 형태로 저장하는 "RDBMS"와

실제 application layer에서 작업하여 요청 및 응답을 처리해주는 Web Server의 경우

Table 형태의 데이터를 객체(Object) 형태로 사용하여 데이터를 조작하게 된다.

이때 기존의 RDBMS처럼 Table을 만들어서 거기에 객체를 담는 것이 아니라

객체로 Table을 생성하면서 관리하는 것이 더 편하지 않을까하는 관점에서 등장한 것이

바로 "ORM"이라 볼 수 있다.

즉, RDBMS가 SQL 문법을 통해 Table을 생성하고, 각 Record를 추가/조회/수정/삭제하였는데

이를 객체로 관리한다고 하는 것이 "ORM"이라 할 수 있다!

 

ORM 설정: 이는 "SQL 구문을 사용하지 않을 수도 있다"라는 의미이다!

 

CREATE TABLE User(
  id int(11) PRIMARY KEY AUTO_INCREMENT,
  userid VARCHAR(30) NOT NULL
);

 

class User {
  constructor() {
    this.id
    this.userid
  }
}

User.find() // SELECT * FROM User;


예를 들어 위의 User class를 가지고 바로 위의 SQL 구문(CREATE 문)을 실행하려 하는 것이 "ORM"이다!!

 


Node.js - sequelize, typeORM(typeORM은 typescript 기반이라 지금 배우기는 이르다!!)
ORM을 배우면 향후 NoSQL을 배우기 쉬어짐!!

 

ORM을 설정하고 간단히 사용까지 할 예정!!

 

ORM을 통해 SQL 속성을 객체로 만들기 때문에 model 파일(models 디렉토리를 이미 구축해놓음!)을 새로 생성함!

 

sequelize를 가지고 ORM을 구현함!
model을 생성해서(객체를 만들어서) "Table"을 생성함!

 

 

User

  • controller `postSignup`
  • service `signup`
  • repository `addUser`

 

 

 

back

server.js

// require("dotenv").config;
// --> require를 통해 dotenv 파일을 가져와서 config를 호출하면 ".env 파일"을 읽고 해당 파일에서 변수를 읽고 시스템 환경변수(process.env)에 해당 변수값을 잠깐 넣어놓음!
// --> 이렇게 되면 "const PORT = process.env.PORT || 3000;"의 경우, "process.env" 객체의 PORT 변수에 ".env 파일"에서 지정한 PORT 값인 3001이 할당됨!!
const express = require("express");
const app = express();
const config = require("./config");
const HttpException = config.exception.HttpException;
// const PORT = process.env.PORT || 3000;
const { sequelize } = require("./models");

const router = require("./routes");

app.use(router);
app.use((error, req, res, next) => {
  // 응답 코드 500
  // if (error instanceof Error) {}
  console.error(error);
  if (error instanceof HttpException) {
    res.json({
      isError: true,
      message: error.message,
      status: error.status,
    });
  } else if (error instanceof Error) {
    res.json({
      isError: true,
      message: error.message,
    });
  }
});

app.listen(config.port, async () => {
  await sequelize.sync({ force: true });
  console.log(`server start ${config.port}`);
});

 

 

config.js

require("dotenv").config();
const HttpException = require("./exceptions/HTTPException");

const host = process.env.DB_HOST || "127.0.0.1";
const port = process.env.DB_PORT || 3306;
const user = process.env.DB_USER || "본인 user명";
const password = process.env.DB_PASSWORD || "본인 MySQL 비밀번호";
const database = process.env.DB_DATABASE || "comments";

const config = {
  exception: {
    HttpException,
  },
  env: process.env.NODE_ENV || "development",
  port: process.env.PORT || 3000,
  db: {
    development: {
      username: user,
      password: password,
      database: database,
      port: port,
      host: host,
      dialect: "mysql",
    },
    test: {
      username: user,
      password: password,
      database: database,
      port: port,
      host: host,
      dialect: "mysql",
      logging: false,
    },
  },
};

module.exports = config;

 

 

exceptions/HTTPException.js

class HttpException extends Error {
  constructor(message) {
    super(message);
    this.status = 500;
  }
}

// const e = new HTTPException("요청 데이터가 없음!!", 500);
// console.log(e);
// console.log(e.message, e.status);

// console.log(typeof e); // "e"는 인스턴스화 된 것이기에 타입은 객체(object)이다!!

module.exports = HttpException;

 

 

user/user.controller.js

class UserController {
  constructor({ UserService }) {
    this.UserService = UserService;
  }

  async postSignup(req, res, next) {
    try {
      const { userid, userpw, username } = req.body;
      const response = await this.UserService.signup({
        userid,
        userpw,
        username,
      });
      res.status(201).json(response);
    } catch (e) {
      next(e);
    }
  }
}

module.exports = UserController;

 

 

user/user.service.js

class UserService {
  constructor({ UserRepository }) {
    this.UserRepository = UserRepository;
  }

  async signup({ userid, userpw, username }) {
    try {
      const user = await this.UserRepository.addUser({
        userid,
        userpw,
        username,
      });
      return user;
    } catch (e) {
      throw new Error(e);
    }
  }
}

module.exports = UserService;

 

 

user/user.repository.js

class UserRepository {
  constructor({ User }) {
    this.User = User;
  }

  async addUser(payload) {
    try {
      const user = await this.User.create(payload);
      return user;
    } catch (e) {
      throw new Error(e);
    }
  }

  async getUserByUserId({ userid }) {
    const user = await this.User.findOne({
      where: {
        userid: userid,
      },
    });

    return user.dataValues;
  }
}

module.exports = UserRepository;

 

 

user/user.module.js

const {
  sequelize: {
    models: { User },
  },
} = require("../models");

const UserRepository = require("./user.repository");

const repository = new UserRepository({ User });

repository.addUser({ userid: "qwer", userpw: "1234", username: "sangbeom" });
repository.getUserByUserId({ userid: "qwer" }).then((data) => {
  console.log(data);
});
// console.log(repository);

 

 

models/index.js

const config = require("../config");
const Sequelize = require("sequelize");

const env = config.env; // development
const db = config.db[env];

const sequelize = new Sequelize(db.database, db.username, db.password, db); // model 객체가 들어감!!
// console.log(sequelize);

// const user = require("./user.model"); // function
// user(sequelize, Sequelize);

const user = require("./user.model")(sequelize, Sequelize);
// console.dir(user);
console.log(user);

console.log(sequelize.models.User === user);

module.exports = {
  Sequelize,
  sequelize,
};

// console.log(typeof Sequelize); // function("Sequelize"는 정확히는 class에 해당함!!)

// console.log(db, env);
// console.log(config);

 

 

models/user.model.js

// 모델을 만드는 방법
// 1) class --> 정적 메서드
// 2) 함수형(function) --> 아래 코드는 함수를 사용하여 모델을 만드는 방식이다!!
module.exports = (sequelize, DataTypes) => {
  // "define" 함수는 모델을 생성해줌!!
  // 첫번째 인자값: 객체의 속성명(object의 이름 정의)
  // 두번째 인자값: Table field 정보
  // 세번째 인자값: Table option 정보
  return sequelize.define(
    "User",
    {
      userid: {
        type: DataTypes.STRING(30),
        allowNull: false,
        unique: true,
      },
      userpw: {
        type: DataTypes.STRING(64),
        allowNull: false,
      },
      username: {
        type: DataTypes.STRING(30),
        allowNull: false,
      },
      gender: {
        type: DataTypes.STRING(2),
        defaultValue: "여자",
      },
    },
    {
      freezeTableName: true,
      // timestamps: false,
    }
  );
};

 

 

.env

PORT=3001
NODE_ENV=development
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=본인 user명
DB_PASSWORD=본인 MySQL 비밀번호
DB_DATABASE=comments