프로그래밍 언어/NODE JS

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

· 코딩마이데이

이제 새로 생성한 도메인 모델을 시퀄라이즈와 연결합니다. 사용자 모델과 일대다 관계를 가지는데, 사용자 한 명이 여러 도메인을 소유할 수고 있기 때문입니다.

models/index.js

const Sequelize = require("sequelize");
const env = process.env.NODE_ENV || "development";
const config = require("../config/config")[env];
const User = require("./user");
const Post = require("./post");
const Hashtag = require("./hashtag");
const Domain = require("./domain");

const db = {};
const sequelize = new Sequelize(
  config.database,
  config.username,
  config.password,
  config
);

db.sequelize = sequelize;
db.User = User;
db.Post = Post;
db.Hashtag = Hashtag;
db.Domain = Domain;

User.init(sequelize);
Post.init(sequelize);
Hashtag.init(sequelize);
Domain.init(sequelize);

User.associate(db);
Post.associate(db);
Hashtag.associate(db);
Domain.associate(db);

module.exports = db;

 

nodebird-api/models/user.js

const Sequelize = require("sequelize");

module.exports = class User extends Sequelize.Model {
  static init(sequelize) {
    return super.init(
      {
        email: {
          type: Sequelize.STRING(40),
          allowNull: true,
          unique: true,
        },
        nick: {
          type: Sequelize.STRING(15),
          allowNull: false,
        },
        password: {
          type: Sequelize.STRING(100),
          allowNull: true,
        },
        provider: {
          type: Sequelize.STRING(10),
          allowNull: false,
          defaultValue: "local",
        },
        snsId: {
          type: Sequelize.STRING(30),
          allowNull: true,
        },
      },
      {
        sequelize,
        timestamps: true,
        underscored: false,
        modelName: "User",
        tableName: "users",
        paranoid: true,
        charset: "utf8",
        collate: "utf8_general_ci",
      }
    );
  }

  static associate(db) {
    db.User.hasMany(db.Post);
    db.User.belongsToMany(db.User, {
      foreignKey: "followingId",
      as: "Followers",
      through: "Follow",
    });
    db.User.belongsToMany(db.User, {
      foreignKey: "followerId",
      as: "Followings",
      through: "Follow",
    });
    db.User.hasMany(db.Domain);
  }
};

 

다음은 로그인하는 화면입니다. 카카오 로그인은 제외했습니다. 카카오 로그인을 추가하려면 카카오 개발자 사이트에서 http://localhost:8002 도메인을 추가로 등록해야 합니다.

 

nodebird-api/views/login.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>API 서버 로그인</title>
    <style>
      .input-group label {
        width: 200px;
        display: inline-block;
      }
    </style>
  </head>
  <body>
    {% if user and user.id %}
    <span class="user-name">안녕하세요! {{user.nick}}님</span>
    <a href="/auth/logout">
      <button>로그아웃</button>
    </a>
    <fieldset>
      <legend>도메인 등록</legend>
      <form action="/domain" method="post">
        <div>
          <label for="type-free">무료</label>
          <input type="radio" id="type-free" value="free" />
          <label for="type-premium">프리미엄</label>
          <input type="radio" id="type-premium" name="type" value="premium" />
        </div>
        <div>
          <label for="host">도메인</label>
          <input
            type="text"
            id="host"
            name="host"
            placeholder="ex) zerocho.com"
          />
        </div>
        <button>저장</button>
      </form>
    </fieldset>
    <table>
      <tr>
        <th>도메인 주소</th>
        <th>타입</th>
        <th>클라이언트 비밀키</th>
      </tr>
      {% for domain in domains %}
      <tr>
        <td>{{domain.host}}</td>
        <td>{{domain.type}}</td>
        <td>{{domain.clientSecret}}</td>
      </tr>
      {% endfor %}
    </table>
    {% else %}
    <form action="/auth/login" id="login-form" method="post">
      <h2>NodeBird 계정으로 로그인하세요.</h2>
      <div class="input-group">
        <label for="email">이메일</label>
        <input id="email" type="email" name="email" required autofocus />
      </div>
      <div class="input-group">
        <label for="password">비밀번호</label>
        <input id="password" type="password" name="password" required />
      </div>
      <div>회원가입은 localhost:8001에서 하세요.</div>
      <button id="login" type="submit">로그인</button>
    </form>
    <script>
      window.onload = () => {
        if (new URL(location.href).searchParams.get("loginError")) {
          alert(new URL(location.href).searchParams.get("loginError"));
        }
      };
    </script>
    {% endif %}
  </body>
</html>

 

위 코드에는 도메인을 등록하는 화면도 포함되어 있습니다. 로그인하지 않았다면 로그인 창이 먼저 뜨고, 로그인한 사용자에게는 도메인 등록 화면을 보여줍니다.

 

nodebird-api/routes/index.js

const express = require("express");
const { id: uuidv4 } = require("uuid");
const { User, Domain } = require("../models");
const { isLoggedIn } = require("./middlewares");

const router = express.Router();

router.get("/", async (req, res, next) => {
  try {
    const user = await User.findOne({
      where: { id: (req.user && req.user.id) || null },
      include: { model: Domain },
    });
    res.render("login", {
      user,
      domains: user && user.Domains,
    });
  } catch (err) {
    console.error(err);
    next(err);
  }
});

router.post("/domain", isLoggedIn, async (req, res, next) => {
  try {
    await Domain.create({
      UserId: req.user.id,
      host: req.body.host,
      type: req.body.type,
      clientSecret: uuidv4(),
    });
    res.redirect("/");
  } catch (err) {
    console.error(err);
    next(err);
  }
});

module.exports = router;

 

http://localhost:8002 접속 화면