프로그래밍 언어/NODE JS

Nodebird 서비스와 데이터베이스를 공유하는 프로젝트 구조 갖추기(1)

· 코딩마이데이

다른 서비스에 Nodebird 서비스의 게시글, 해시태그, 사용자 정보를 JSON 형식으로 제공할 것입니다. 단, 인증을 받은 사용자에게만 일정한 할당량 안에서 API를 호출할 수 있도록 허용할 것입니다.

우선 nodebird-api 폴더를 만들고 package.json 파일을 생성합니다. npm init으로 생성한 후 dependenciese들을 설치해도 되고, 이제 package.json을 복사해도 됩니다. 새로 추가된 패키지는 uuid이며, 고유한 랜덤 문자열을 만들어내는 데 사용됩니다.

{
  "name": "nodebird-api",
  "version": "0.0.1",
  "description": "NodeBird API 서버",
  "main": "app.js",
  "scripts": {
    "start": "nodemon app",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Cherry",
  "license": "ISC",
  "dependencies": {
    "bcrypt": "^4.0.1",
    "cookie-parser": "^1.4.5",
    "dotenv": "^8.2.0",
    "express": "^4.17.1",
    "express-session": "^1.17.1",
    "morgan": "^1.10.0",
    "mysql2": "^2.1.0",
    "nunjucks": "^3.2.1",
    "passport": "^0.4.1",
    "passport-kakao": "1.0.0",
    "passport-local": "^1.0.0",
    "sequelize": "^5.21.7",
    "uuid": "^7.0.3"
  },
  "devDependencies": {
    "nodemon": "^2.0.3"
  }
}

 

package.json에 적힌 패키지를 설치합니다.

$ npm i

 

Nodebird에서 config, models, passport 폴더와 내용물들을 모두 복사해서 nodebird-api 폴더에 붙어 넣습니다. routes 폴더에서는 auth.js와 middlewares.js만 그대로 사용합니다. 마지막으로 .env 파일을 복사합니다. 다른 폴더와 파일은 새로 나올 때 마다 직접 생성하면 됩니다.

다음은 에러를 표시할 파일입니다. views 폴더를 만들고 그 폴더 안에 error.html 파일을 생성합니다.

 

nodebird-api/views/error.html

<h1>{{message}}</h1>
<h2>{{error.status}}</h2>
<pre>{{error.stack}}</pre>

 

nodebird-api/app.js

const express = require("express");
const path = require("path");
const cookieParser = require("cookie-parser");
const passport = require("passport");
const morgan = require("morgan");
const session = require("express-session");
const nunjucks = require("nunjucks");
const dotenv = require("dotenv");

dotenv.config();
const authRouter = require("./routes/auth");
const indexRouter = require("./routes");
const { sequelize } = require("./models");
const passportConfig = require("./passport");

const app = express();
passportConfig();
app.set("port", process.env.PORT || 8002);
app.set("view engine", "html", {
  express: app,
  watch: true,
});
sequelize
  .sync({ force: false })
  .then(() => {
    console.log("데이터베이스 연결 성공");
  })
  .catch((err) => {
    console.error(err);
  });

app.use(morgan("dev"));
app.use(express.static(path.join(__dirname, "public")));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser, process.env.COOKIE_SECRET);
app.use(
  session({
    resave: false,
    saveUninitialized: false,
    secret: process.env.COOKIE_SECRET,
    cookie: {
      httpOnly: true,
      secure: false,
    },
  })
);
app.use("/auth", authRouter);
app.use("/", indexRouter);

app.use((req, res, next) => {
  const error = new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
  error.status = 404;
  next(error);
});

app.use((err, req, res, next) => {
  res.locals.message = err.message;
  res.locals.error = process.env.NODE_ENV !== "production" ? err : {};
  res.status(err.status || 500);
  res.render("error");
});

app.listen(app.get("port"), () => {
  console.log(app.get("port"), "번 포트에서 대기 중");
});

 

포트 번호를 8002로 했으므로 9장의 Nodebird 앱 서버(8001번 포트) 및 추후에 만들 클라이언트인 NodeDog 서버(포트 번호 4000)와 같이 실행할 수 있습니다. 콘솔을 하나 더 열어서 서버를 실행하면 됩니다.

 

도메인을 등록하는 기능이 새로 생겼으므로 도메인 모델을 추가해봅시다. 도메인 모델을 추가해봅시다. 도메인은 인터넷 주소를 뜻합니다.

const Sequelize = require("sequelize");

module.exports = class Domain extends Sequelize.Model {
  static(sequelize) {
    return super.init(
      {
        host: {
          type: Sequelize.STRING(80),
          allowNull: false,
        },
        type: {
          type: Sequelize.ENUM("free", "premium"),
        },
        clientSecret: {
          type: Sequelize.UUID,
          allowNull: false,
        },
      },
      {
        sequelize,
        timestamps: true,
        paranoid: true,
        modelName: "Domain",
        tableName: "domains",
      }
    );
  }
  static associate(db) {
    db.Domain.belongsTo(db.User);
  }
};

 

도메인 모델에는 인터넷 주소(host)와 도메인 종류(type), 클라이언트 비밀 키(clientSecret)가 들어갑니다.

type 컬럼을 보면 처음 보는 ENUM이라는 속성을 가지고 있습니다. 넣을 수 없는 값을 제한하는 데이터 형식입니다. 무료(free)나 프리미엄(preminum) 중에서 하나의 종류만 선택할 수 있게 했고, 이를 어겼을 때 에러가 발생합니다.

클라이언트 비밀 키는 다른 개발자들이 Nodebird의 API를 사용할 때 필요한 비밀키입니다. 이 키가 유출되면 다른 사람을 사칭해서 요청을 보낼 수 있으므로, 유출되지 않도록 주의해야 합니다. 한 가지 안전 장치로서, 요청을 보낸 도메인까지 일치해야 요청을 보낼 수 있게 제한을 둘 것입니다. clientSecret 컬럼을 UUID라는 타입을 가집니다. UUID는 충돌 가능성이 매우 적은 랜덤한 문자열입니다.