기존에 만들었던 노드버드 서비스가 기동된 상태에서 localhost:8002 로 별도의 api 서버를 만든다. routes 나 controllers 에서 auth 를 제외한 파일들을 지워주고 index.js 를 만들어 준다. 그리고 indexRouter를 생성해준다.

 

//app.js 파일

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

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');
nunjucks.configure('views', {
  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(passport.initialize());
app.use(passport.session());


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'), '번 포트에서 대기중');
});

 

Domain 테이블을 만들어준다.

 

// models/domain.js 파일

const Sequelize = require("sequelize");

class Domain extends Sequelize.Model {
  static initiate(sequelize) {
    Domain.init(
      {
        host: {
          type: Sequelize.STRING(80),
          allowNull: false,
        },
        type: {
          type: Sequelize.ENUM("free", "premium"),
          allowNull: false,
        },
        clientSecret: {
          type: Sequelize.UUID,
          allowNull: false,
        },
      },
      {
        sequelize,
        timestamps: true,
        paranoid: true,
        modelName: "Domain",
        tableName: "domains",
      }
    );
  }

  static associate(db) {
    db.Domain.belongsTo(db.User);
  }
}

module.exports = Domain;

 

계정은 기존에 8001에서 만들었던 계정을 사용하고 로그인 전 화면과 로그인 후에 화면은 아래와 같다.

 

controllers에서 Domain 만드는 코드까지 작성한 후에 화면은 아래와 같다.

// controllers/index.js 파일

const { User, Domain } = require("../models");
const { v4: uuidv4 } = require("uuid");

exports.renderLogin = async (req, res, next) => {
  try {
    const user = await User.findOne({
      where: { id: req.user?.id || null },
      include: { model: Domain },
    });
    res.render("login", {
      user,
      domains: user?.Domains,
    });
  } catch (err) {
    console.error(err);
    next(err);
  }
};
exports.createDomain = async (req, res, next) => {
  try {
    await Domain.create({
      UserId: req.user.id,
      host: req.body.host,
      type: req.body.type,
      clientSecret: uuidv4(), //랜덤한 uuid를 넣어준다.
    });
    res.redirect("/");
  } catch (err) {
    console.error(err);
    next(err);
  }
};

 

Executing (default): INSERT INTO `domains` (`id`,`host`,`type`,`clientSecret`,`createdAt`,`updatedAt`,`UserId`) VALUES (DEFAULT,?,?,?,?,?,?);

 

토큰이 여러 종류가 있는데 JWT (JasonWebToken) 을 많이 사용한다.

헤더, 페이로드, 시그니처 3종류로 나누어져 있다.

jwt의 단점은 내용물이 들어있기 때문에 용량이 크다.

lecture-call 이라는 클라이언트 역할의 서버와

lecture-api 라는 api를 제공하는 서버의 코드를 작성한다. 작성한 후에 localhost:4000/test 로 접속을 하면 lecture-api 서버에서는 아래와 같이 200 코드가 출력되고 토큰을 클라이언트로 넘겨준다.

 

POST /v1/token 200 30.022 ms - 250
GET /v1/test 200 3.573 ms - 78

 

자기가 작성한 게시글 과 해시태그 검색하는 코드를 작성한 후 결과는 아래와 같습니다.

 

작성한 글 결과 화면

 

해시태그 검색한 결과 화면

 

API 사용량 제한을 위해서는 express-rate-limit 을 설치해줘야 한다.

D:\code\node\lecture-api>npm i express-rate-limit

added 1 package, and audited 300 packages in 3s

41 packages are looking for funding
  run `npm fund` for details
 

 

- v1 버전을 사용했을 때 : call 하는 클라이언트 서버의 request 함수에서 throw error 로 처리하면 아래와 같이 에러코드만 나오고 안내 메세지가 안 나온다.

 


//controller/

const request = async (req, api) => {
    try {
      if (!req.session.jwt) {
        // 세션에 토큰이 없으면
        const tokenResult = await axios.post(`${URL}/token`, {
          clientSecret: process.env.CLIENT_SECRET,
        });
        req.session.jwt = tokenResult.data.token; // 세션에 토큰 저장
      }
      return await axios.get(`${URL}${api}`, {
        headers: { authorization: req.session.jwt },
      }); // API 요청
    } catch (error) {
      if (error.response?.status === 419) {
        // 토큰 만료시 토큰 재발급 받기
        delete req.session.jwt;
        return request(req, api);
      } // 419 외의 다른 에러면
      throw error;
      // return error.response;
    }
  };

 

return error.message 로 변경하면 아래와 같이 안내 메세지가 화면에 출력 된다.

v2로 변경하고 나서는 아래와 같이 정상적으로 호출된다.

 

하지만 1분에 1번만 호출되도록 제한했기 때문에 다시 호출하면 정상 호출 안된다.

 

웹브라우저에서 api를 호출할 때는 아래와 같이 cors 에러가 발생한다. 기존에는 서버에서 직접 api를 호출 했기 때문에 cors 에러가 발생하지 않았다.

 

cors 에러를 해결하기 위해 cors를 설치하고 추가해주면 아래와 같이 해결된다.

D:\code\node\lecture-api>npm i cors

added 1 package, and audited 301 packages in 3s

41 packages are looking for funding
  run `npm fund` for details

 

'Javascript > Node' 카테고리의 다른 글

노드 교과서 섹션 6  (0) 2023.08.27
노드 교과서 섹션 8  (0) 2023.08.23
노드 교과서 섹션 10  (0) 2023.08.19
노드 교과서 섹션 11  (0) 2023.08.18
노드 교과서 섹션 12  (0) 2023.08.17

+ Recent posts