프로그래밍 언어/NODE JS

다른 서비스에서 호출하기

· 코딩마이데이

API 제품 서버를 만들었으니 API를 사용하는 서비스로 만들어봅시다. 이 서비스는 다른 서버에게 요청을 보내므로 클라이언트 역할을 합니다. API 제공자가 아닌 API 사용자의 입장에서 진행하는 것이며, 바로 NodeBird 앱의 데이터를 가져오고 싶어 하는 사용자입니다. 보통 그 데이터를 가공해 2차적인 서비스를 하려는 회사가 API를 이용하곤 합니다. 예를 들어 쇼핑몰들이 있으면, 쇼핑몰들의 최저가를 알려주는 서비스가 2차 서비스가 됩니다. 우리의 2차 서비스 이름은 NodeCat입니다.

nodebird-api 폴더와 같은 위치에 nodecat-api 폴더와 같은 위치에 nodcat이라는 새로운 폴더를 만듭니다. 별도의 서버이므로 nodebird-api와 코드가 섞이지 않게 주의합니다.

{
  "name": "nodecat",
  "version": "0.0.1",
  "description": "노드버드 2차 서비스",
  "main": "app.js",
  "scripts": {
    "start": "nodemon app"
  },
  "author": "Cherry",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.19.2",
    "cookie-parser": "^1.4.5",
    "dotenv": "^8.2.0",
    "express": "^4.17.1",
    "express-session": "^1.17.1",
    "morgan": "^1.10.0",
    "nunjucks": "^3.2.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.3"
  }
}

 

콘솔

$ npm i

 

이 서버의 주목적은 nodebird-api의 API를 통해 데이터를 가져오는 것입니다. 가져온 데이터는 JSON 형태이므로 퍼그나 넌적스 같은 템플릿 엔진으로 데이터를 렌더링할 수도 있습니다. 서버 파일과 에러를 표시할 파일을 생성합니다.

 

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

dotenv.config();
const indexRouter = require('./routes');

const app = express();
app.set('port', process.env.PORT || 4000);
app.set('view engine', 'html');
nunjucks.configure('views', {
  express: app,
  watch: true,
});

app.use(morgan('dev'));
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('/', 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'), '번 포트에서 대기중');
});

 

사용하지 않는 미들웨어는 걷어내고 최소한으로 app.js를 구성했습니다.

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

 

API를 사용하려면 먼저 사용자 인증을 받아야 하므로 사용자 인증이 원할하게 진행되는지 테스트하는 라우터를 만들어봅시다. 조금 전에 라우터를 만들어봅시다. 조금 전에 발급받은 clientSecret을 .env에 넣었습니다. 여러분이 발급받은 clientSecret 키를 .env를 넣으세요.

COOKIE_SECRET=nodecat
CLIENT_SECRET=7d67444e-fd01-4f9b-8680-f72464d02a57

 

nodecat/routes/index.js

const express = require("express");
const axios = require("axios");

const router = express.Router();

router.get("/test", async (req, res, next) => {
  // 토큰 테스트 라우터
  try {
    if (!req.session.jwt) {
      // 세션에 토큰이 없으면 토큰 발급 시도
      const tokenResult = await axios.post("http://localhost:8002/v1/token", {
        clientSecret: process.env.CLIENT_SECRET,
      });
      if (tokenResult.data && tokenResult.data.code === 200) {
        // 토큰 발급 성공
        req.session.jwt = tokenResult.data.token; // 세션에 토큰 저장
      } else {
        // 토큰 발급 실패
        return res.json(tokenResult.data); // 발급 실패 사유 응답
      }
    }
    // 발급받은 토큰 테스트
    const result = await axios.get("http://localhost:8002/v1/test", {
      headers: { authorization: req.session.jwt },
    });
    return res.json(result.data);
  } catch (error) {
    console.error(error);
    if (error.response.status === 419) {
      // 토큰 만료 시
      return res.json(error.response.data);
    }
    return next(error);
  }
});

module.exports = router;

 

 

GET /test 라우터는 NodeCat 서버가 토큰 인증 과정을 테스트해보는 라우터입니다. 이 라우터의 동작 과정은 다음과 같습니다. 요청이 왔을 때 세션에 발급받은 토큰이 저장되어 있지 않다면, POST http://localhost:8002/v1/token 라우터로부터 토큰을 발급받습니다. 이때 HTTP 요청의 본문에 클라이언트 비밀 키를 실어 보냅니다.

 

발급에 성공했다면(응답 데이터의 code 속성이 200이면 성공한 것이라고 약속함), 발급받은 토큰으로 다시 GET http://localhost:8002/v1/test에 접근하여 토큰이 유효한지 테스트해봅니다. 이때는 JWT 토큰을 요청 본문 대신 authorization 헤더에 넣었습니다. 보통 인증용 토큰은 이 헤더에 주로 넣어 전송합니다.

 

실제로 GET /test 라우터를 사용해봅시다. 콘솔을 하나 더 띄워 서버(localhost:4000)를 실행합니다. nodebird api(localhost:8002)도 실행 중이어야 합니다.

 

NodeCat과 NodeBird API 서버에서 모두 실행

$ npm start
// 각각 다른 포트에서 대기 중
4000번 포트에서 대기 중
8002번 포트에서 대기 중

 

http://localhost:4000/test로 접속하면 됩니다. 요청을 받는 http://localhost:8002와 다른 주소임을 기억하세요. localhost:8002는 API 서비스를 제공하는 nodebird-api 서버고, localhost:4000은 API 서비스를 사용하는 NodeCat 서버입니다. 접속하면 다음과 같이 발급받은 토큰의 내용이 표시됩니다.

 

♥ 그림 10-7 토큰 테스트 화면
{"id":1,"nick":"제로초","iat":1577251700,"exp":1577251760,"iss":"nodebird"}

 

잘 동작하는 것 같지만 아직 코드에 부족한 부분이 있습니다. 1분을 기다린 후 http://localhost:4000/test 라우터에 다시 접속하면, 토큰이 만료되었다는 메시지가 뜹니다.

 

♥ 그림 10-8 토큰 만료 화면
{"code":419,"message":"토큰이 만료되었습니다"}

 

토큰의 유효 기간이 1분이었으므로 1분 후에는 발급받은 토큰을 갱신해야 합니다. API 서버에서 에러 코드와 에러 메시지를 상세하게 보내줄수록 클라이언트가 무슨 일이 일어났는지 이해하기 쉽습니다. 토큰이 만료되었을 때 갱신하는 코드를 추가해야 한다는 것을 잊지 말길 바랍니다.