-
항해99 9기 4주차 (2022_10_19) 조금 뒤늦은 회고카테고리 없음 2022. 10. 19. 09:44
주의! 개발공부 한 달 차의 대단히 자의적이며 주관적인 공부 기록입니다.
개념틀과 어휘가 강학상의 혹은 현업상의 그것과 비교하여 크게 다를 수 있습니다. (사실 일단 일부러 자의적으로 구성해나가고 있는 점도 다소 있습니다.)
때문에 혹여 이 블로그로부터 정보를 길어가는 일은 몹시 적절하지 못합니다.
방문자에게 이 블로그의 쓸모는 관찰 정도에 있을 것입니다.
'한 달 차는 이런 걸 보고 이런 오해를 하는 구나' 정도 말이지요.
하기의 혼잡한 글은 트러블 슈팅에 대한 여러 이해의 시도 ( = 많은 오해)에 대한 기록이다.
결론부터 말하면, 자바스크립트가 코드를 읽고 평가하는 순서, 그리고 중복된 라우터의 문제다.
예컨대,
1. routes 폴더 안 posts.js의 이 부분. ( localhost:3000/posts/:postId )
2. routes 폴더 안 likes.js의 이 부분. ( localhost:3000/posts/likes )
2를 1의 위로 (말 그대로 코드 위치) 올렸으면 문제가 해결되었을 것.
이는 자바스크립트가 위에서 아래로 읽어오며 사후적으로 평가하기 때문이다.
혹은 다음의 에러로그 미들웨어도 그렇다.
// middlewares/error-handler.middleware.js const errorLogger = (error, request, response, next) => { console.error(error); next(error); // errorLogger -> errorHandler }; const errorHandler = (error, req, res, next) => { const status = error.status || 400; res.status(status); res.json({ errorMessage: error.message }); }; module.exports = { errorLogger, errorHandler };
//app.js const express = require('express'); const Http = require('http'); require('dotenv').config(); const cookieParser = require('cookie-parser'); const { errorLogger, errorHandler } = require('./middlewares/error-handler.middleware'); const app = express(); const http = Http.createServer(app); app.use(cookieParser()); app.use(express.json()); app.use('/', require('./routes/users.routes')); app.use('/posts', require('./routes/posts.routes')); app.use('/likes', require('./routes/likes.routes')); app.use('/comments', require('./routes/comments.routes')); app.use(errorLogger); // Error Logger app.use(errorHandler); // Error Handler module.exports = http;
연대기 순의 시간을 통과하기 때문에, next() 로 넘어간 에러를 받기 위해서는 저런 조치가 필요하다.
전역 미들웨어로서, 에러로거나 에러핸들러는 경로를 지정하는 라우터 미들웨어들의 아래에 위치해야 한다.
사실 이것도 정확한 설명은 전혀 되지 못한다. 그냥 그렇다 하더라는 것을 옮길 뿐이다.
다만 이와 관련하여 공부의 주제로 떠오른 것들은 이러하다.
1.크로노스 : 자바 스크립트의 연대기적 런타임 (위에서 아래로 정직하게 읽어온다는 뜻에서)
2.카이로스 : 비동기 함수를 비롯해 연대기의 옆으로 빠져나가지만, 실현될 것이 예정되어 있는 사건들.
A. 상상적인 것 : 어휘적 환경 lexical environment.
B. 상징적인 것 : interface, class, module, function...
A.상상적인 것과 관련해서는 우리 모두가 익숙하다.
내가 '나'라고 생각하는 '상상된 나'는 실제 나와 다르다.
그 차이에서 많은 일이 발생한다. 긍정적이든, 부정적이든.
적어도 자바스크립트에서,
코드가 스스로를 상상하는 모습은 실제 작용과 다르다.
코드는 1.크로노스를 지나치며 많은 모순에 맞닥뜨린다.
그리고 모순에서 많은 작용이 발생한다. 긍정적이든, 부정적이든.(아주 오래된 말로, 변증법적 작용이다.)
호이스팅이라는 것도, 사람들이 TDZ라고 부르게 된 것도 이 차이, 혹은 모순의 틈에서 나온다.
코드는 이 차이를 봉합해가며 개연성을 강화하기도 한다. 이를테면 클로저가 그렇다.
그러나 코드에게는 상징적인 것이 "소여" 되어 있다.
모든 통사적 규칙들이 그러하고, 객체에 대한 여러 계열적 질서도 그러하다.
인터페이스 혹은 추상 클래스의 경우, 어떤 기능을 구현할 것을 강제하기도 한다.
그것에서 다형성이 창발된다.
이것은 코드 스스로가 해내는 것이 아니다. 말 그대로 상상과 인식, 런타임 이전에 주어진 것이지.
아래의 트러블 슈팅에 대한 글은 몹시 혼란스럽고 잘못된 추측도 많으나, 기록으로 남겨둔다.
개인과제 : router, req.params, 경로 관련한 삽질의 기록GET /posts/likes : Cannot read properties of null (reading 'postId')
Executing (default): SELECT `postId`, `userId`, `nickname`, `title`, `content`, `likes`, `createdAt`, `updatedAt` FROM `Posts` AS `Posts` WHERE `Posts`.`postId` = 'likes'; GET /posts/likes : Cannot read properties of null (reading 'postId')
아래의 로그와 코드들을 종합하면, 아마도...
1. routes 폴더 안 posts.js의 이 부분. ( localhost:3000/posts/:postId )
router.get("/:postId", async (req, res) => {
try{
const postNum = req.params.postId;
2. routes 폴더 안 likes.js의 이 부분. ( localhost:3000/posts/likes )
router.get("/likes", authMiddleware, async(req,res) => ...
1이 2의 수행에 영향을 주었던 것으로 보인다.
대체 어떻게?
한 라우터 함수에서 규정된 get method가, req.params 로 URI의 일부를 가져왔는데.
파일도, 경로도 다른 곳의 라우터 함수에서 쓴 get method에 어떻게 영향을 주어서...
WHERE `Posts`.`postId` = 'likes';
이런 이상한 쿼리를 날릴 수 있을까?
(쿼리를 날리는 주체는 시퀄라이즈로 보인다.)
req.params
When you use a regular expression for the route definition, capture groups are provided in the array using req.params[n], where n is the nth capture group. This rule is applied to unnamed wild card matches with string routes such as /file/*:
즉, 정규식 검사에서의 하나의 패턴으로 캡쳐 그룹을 만들어 가져온다. 따라서 인덱스를 불러도 해당 URI가 튀어나오는 것.
// GET /file/javascripts/jquery.js console.dir(req.params[0]) // => "javascripts/jquery.js"
If you need to make changes to a key in req.params, use the app.param handler. Changes are applicable only to parametersalready defined in the route path.
키를 변경하려면 app.param 핸들러로 가라니.
에러로그
Executing (default): SELECT `postId`, `userId`, `nickname`, `title`, `content`, `likes`, `createdAt`, `updatedAt` FROM `Posts` AS `Posts` WHERE `Posts`.`postId` = 'likes'; GET /posts/likes : Cannot read properties of null (reading 'postId')
백번 저 에러로그를 토해내며 문제가 되었던 부분
// routes/likes.js const express = require("express"); const authMiddleware = require("../middlewares/auth-middleware"); const { Op } = require("sequelize"); const { Posts } = require("../models"); const { Likes } = require("../models"); const router = express.Router(); //좋아요 게시글 조회 : 로그인 필요 //라우터를 수정한 지금의 버전. 이전에는 라우터로부터 /posts 프리픽스 경로를 받은 후 /likes라고 지정되어 있었다. //즉, localhost:3000/posts/likes가 URI 였다. router.get("/",authMiddleware, async (req, res) => { const {user} = res.locals; console.log(user.userId) // 1 (userId 숫자) try { const likeList = await Likes.findAll({ where: {userId : user.userId}, order: [['likes', 'DESC']], attributes: {exclude: ["createdAt", "updatedAt", "likes"]}, include: [{ model: Posts, key: 'postId', attributes:['createdAt', 'updatedAt', 'likes' ] }], }); console.log(likeList); res.status(200).json({ data: likeList }); } catch (error) { const message = `${req.method} ${req.originalUrl} : ${error.message}`; console.log(message); res.status(400).json({ message }); } });
문제가 되었던 라우터.
// routes/lndex.js const express = require("express"); const Posts = require("./posts"); const Likes = require("./likes"); const Comments = require("./comments"); const router = express.Router(); router.use('/posts/', Posts); router.use('/comments/', Comments); router.use('/posts/', Likes); // 이후 기본경로를 /likes 로 변경 module.exports = router;
문제의 원천(아마도)
// routes/posts.js const express = require("express"); const authMiddleware = require("../middlewares/auth-middleware"); const { Users } = require("../models"); const { Posts } = require("../models"); const router = express.Router(); //...생략 //게시글 상세 조회 : 토큰필요 없음. router.get("/:postId", async (req, res) => { try { const postNum = req.params.postId; if (!postNum) { // TODO: Joi를 사용하지 않음 res.status(400).json({ message: '데이터 형식이 올바르지 않습니다.' }); return; } const post = await Posts.findOne({ where: { postId:postNum }, include: [{ //associate 이후 include 실험 model : Users, key: 'userId', attributes:['userId', 'nickname', 'createdAt','updatedAt'] }], }); res.status(200).json({ data: post }); } catch (error) { const message = `${req.method} ${req.originalUrl} : ${error.message}`; console.log(message); res.status(400).json({ message }); } });