• 카테고리

    질문 & 답변
  • 세부 분야

    풀스택

  • 해결 여부

    미해결

1. 라우터 연결 오류 2. req.response가 undefined

22.11.20 22:56 작성 조회수 667

0

수정 전 코드에서는 다음과 같은 오류가 뜨면서 라우터 연결이 안됩니다.

GET /v1/posts/my 라우터가 없습니다.

(1-1)  수정 전 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 v1 = require("./routes/v1");
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("/v1", v1);
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"), "번 포트에서 대기중");
});

 

(2-2) 수정 후 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 v1 = require('./routes/v1');
const v2 = require('./routes/v2');
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('/v1', v1);
app.use('/v2', v2);
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'), '번 포트에서 대기중');
});

 

수정 후에는 에러 메시지가 Cannot read properties of undefined (reading 'status')로 바뀌었습니다.

 

nodecat의 index.js

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

const router = express.Router();

const URL = "http://localhost:8002/v1";
axios.defaults.headers.origin = "http://localhost:4000";

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 },
      });
    }
  } catch (error) {
    console.error(error);
    if (error.response.status === 419) {
      delete req.session.jwt;
      return request(req, api);
    }
    return error.response;
  }
};

router.get("/mypost", async (req, res, next) => {
  try {
    const result = await request(req, "/posts/my");
    res.json(result.data);
  } catch (error) {
    console.error(error);
    next(error);
  }
});

router.get("/search/:hashtag", async (req, res, next) => {
  try {
    const result = await request(
      req,
      `/posts/hashtag/${encodeURIComponent(req.params.hashtag)}`
    );
    res.json(result.data);
  } catch (error) {
    console.error(error);
    next(error);
  }
});

module.exports = router;

 

nodebird-api의 middleware.js

const jwt = require("jsonwebtoken");

exports.isLoggedIn = (req, res, next) => {
  if (req.isAuthenticated()) {
    next();
  } else {
    res.status(403).send("로그인 필요");
  }
};

exports.isNotLoggedIn = (req, res, next) => {
  if (!req.isAuthenticated()) {
    next();
  } else {
    res.redirect("/");
  }
};

exports.verifyToken = (req, res, next) => {
  try {
    req.decoded = jwt.verify(req.headers.authorization, process.env.JWT_SECRET);
    // req.headers.authorization 유효한지 검사.
    // jwt토큰을 검증해서 req.decoded에 payload(데이터)부분이 담긴다.
    return next();
  } catch (error) {
    if (error.name === "TokenExpiredError") {
      // 유효기간 초과.
      return res.status(419).json({
        code: 419,
        // 2xx 3xx 4xx 5xx같이 앞자리만 용도에 맞으면 이후 값은 마음대로 정해도 된다.
        // 문서화만 제대로 하면 됨.
        message: "토큰이 만료되었습니다",
      });
    }
    return res.status(401).json({
      // 위조한 토큰인 경우.
      code: 401,
      message: "유효하지 않은 토큰입니다",
    });
  }
};

 

nodebird-api의 v1.js

const express = require("express");
const jwt = require("jsonwebtoken");
// api서버이기 때문에 nodebird에서 요청자에게로 정보를 제공할 라우터를 뚫어줘야 한다.
// 요청을 처리할 수 있는 라우터.

const { verifyToken } = require("./middlewares");
const { Domain, User, Post, Hashtag } = require("../models");

const router = express.Router();

router.post("/token", async (req, res) => {
  // 토큰을 발급해주는 라우터.
  const { clientSecret } = req.body;
  try {
    const domain = await Domain.findOne({
      // 도메인 등록했는지 검사.
      where: { clientSecret },
      // nodecat의 .env에 있는 CLIENT_SECRET값이
      // 도메인 clientSecret 등록되어있는지 검사.
      include: {
        model: User,
        attribute: ["nick", "id"],
      },
    });
    if (!domain) {
      // 도메인 등록 안되어있으면 에러.
      return res.status(401).json({
        code: 401,
        message: "등록되지 않은 도메인입니다. 먼저 도메인을 등록하세요",
      });
    }
    const token = jwt.sign(
      // 토큰 발급해주기 .sign()
      // 도메인 등록 되어 있으면 토큰 발급 해주기.
      {
        id: domain.User.id,
        nick: domain.User.nick,
        // - 부가적 데이터 넣어줌.
      },
      process.env.JWT_SECRET,
      // - signiture 만들어줌 위조 검사.
      {
        expiresIn: "1m", // 유효기간 1분.
        issuer: "nodebird", // 누가 발급해줬는가.
        // - 토큰 옵션
      }
    );
    return res.json({
      // 토큰 발급되면 nodecat에 돌려줌.
      // nodecat의 index.js의 tokenResult.data.token에 저장됨.
      code: 200,
      message: "토큰이 발급되었습니다",
      token,
    });
  } catch (error) {
    console.error(error);
    return res.status(500).json({
      code: 500,
      message: "서버 에러",
    });
  }
});

router.get("/test", verifyToken, (req, res) => {
  // 토큰이 제대로 발급되었는지 테스트 하는 라우터.
  res.json(req.decoded);
  // req.decoded는 verifyToken에서 나옴.
  // 이후 req.decoded는 nodecat의 index.js의 result.data안에 들어간다.
});

// nodebird의 data들을 보내주는 라우터 코딩.
router.get("./posts/my", verifyToken, (req, res) => {
  // 자기 자신의 정보를 가져올 수 있게 해주는것.
  Post.findAll({ where: { userId: req.decoded.id } })
    .then((posts) => {
      console.log(posts);
      // async await 방식이 아니라 post방식으로 작성됨.
      res.json({
        code: 200,
        payload: posts,
        // 코드와 데이터를 규격에 맞춰 보내줌.
      });
    })
    .catch((error) => {
      // 비동기 처리를 할때는 에러가 뭔지 기록해주어야 한다.
      console.error(error);
      return res.status(500).json({
        code: 500,
        message: "서버 에러",
      });
    });
});

// 해시태그로 검색하는 라우터
router.get(`/posts/hashtag/:title`, verifyToken, async (req, res) => {
  try {
    const hashtag = await Hashtag.findOne({
      where: { title: req.params.title },
    });
    if (!hashtag) {
      return res.status(404).json({
        code: 404,
        message: "검색 결과가 없습니다.",
      });
    }
    const posts = await hashtag.getPosts();
    return res.json({
      code: 200,
      payload: posts,
    });
  } catch (error) {
    console.error(error);
    return res.status(500).json({
      code: 500,
      message: "서버 에러",
    });
  }
});

module.exports = router;

 

질문 1.

수정 후 코드는 10.7의 코드는 붙여 넣기 한 것입니다. 수정 전에도 라우터는 연결된 것처럼 보였는데 라우터가 없다는 오류가 뜬 이유가 뭔가요?

 

질문 2.

수정 후에 뜨는 에러 메시지인 Cannot read properties of undefined (reading 'status')가

   if (error.response.status === 419) {
      // 토큰 만료 시 토큰 재발급 받기

이 부분에서 발생했습니다. error.response가 undefined라는 것 까지는 알겠는데

2-1. 왜 error.response가 undefined가 된것인지.

2-2. 어느 부분에서 오류가 발생해 error로 넘어간 것인지 전혀 감이 잡히지 않습니다.

답변 1

답변을 작성해보세요.

1

router.get("./posts/my", verifyToken, (req, res) => {
주소 앞에 . 붙어 있는 오타입니다.

error.response는 응답이 아예 오지 않았으면 undefined입니다. 서버에서 에러가 나서 응답이 오지 않은 것이죠. 서버를 봐야합니다.

빠른 답변 감사합니다 오타가 문제였네요...

강사님께서 '서버를 봐야 한다' 라고 말씀하셔서 다시 오타가 있는 상태로 돌려놓고 f12로 네트워크를 창을 띄웠는데요, status code가 500 Internal Server Error로 나와있었습니다.

이런 경우는 status code가 500이다 -> 내부 서버 오류이다 -> 서버에서 에러가 났다

이렇게 생각하고 서버에서 오류가 발생할 수 있는 여러가지 가능성을 검토하면 된다 이런 식으로 문제 해결을 해나가는 것이라고 이해하면 되는 걸까요?

네 맞습니다. 그리고 처음에는 라우터가 없습니다라고 떴는데 그건 404 에러 라우터에서 뜬 것이므로 라우터가 문제라는 걸 알 수 있었습니다.

네 답변 감사합니다.