수강이 제한됩니다.
다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
안녕하세요 fs 권한 관련 질문드립니다
1. fs 모듈을 이용해서, /etc/hosts 파일을 수정할 때, 유저의 권한 문제를 어떻게 해결해야 할 지 모르겠습니다.웹사이트를 만들고 있습니다. 이 웹은 집중 및 생산성에 도움이 되고자 하는 기능을 담고 있습니다.유저가 특정 링크를 제 웹에 추가하면, 그 링크에 대한 접속을 차단하는 기능입니다. 물론 해제도 할 수 있습니다. 이 기능을 위해서, etc/hosts 파일(맥 기준)에 대한 수정 권한이 필요했습니다. api로 만들어서 해당 기능 함수를 실행시키려 했고, 결과는 permission 에러가 났습니다. 만약, 제 로컬에서, 저만 쓸 용도로 하면은 "sudo 기능함수파일" 을 사용하면 될 해결이 될 문제인데, 다른 사용자들의 로컬에서는 sudo 권한 및 관리자 권한을 어떻게 적용시켜줘야 할 지를 모르겠습니다. read는 잘 됩니다. 아래는 권한 에러가 나오는 곳의 코드와 api를 통해 hosts파일을 수정하려 할 때 나오는 에러입니다// 로직 fs.writeFile(filePath, completeContent, (err) => { if (err) { return console.log('Error!', err); } }); // 에러 Error! [Error: EACCES: permission denied, open '/etc/hosts'] { errno: -13, code: 'EACCES', syscall: 'open', path: '/etc/hosts' } 2. 또, 제가 etc/hosts(맥) 이나 C:\Windows\System32\drivers\etc\hosts(윈) 파일을 수정하는 것이 법적인 분쟁 소지가 될 수 있을까요?권한을 요구하는 작업이다보니, 유저가 제 웹의 기능을 사용하고 나서, 그 후로 다른 작업을 할 때, 안 되는 게 있다면, 이에 대한 분쟁(?) 의 소지가 있을 수도 있겠다는 생각을 했습니다.제 로직은, 차단할 링크를 배열에 담아두고, 그 링크가 hosts 파일의 Line에 추가되있다면, 그 라인만 없애는 식으로 해서, 기존의 내용은 건들지 않습니다. [제로초 강좌 질문 필독 사항입니다]질문에는 여러분에게 도움이 되는 질문과 도움이 되지 않는 질문이 있습니다.도움이 되는 질문을 하는 방법을 알려드립니다.https://www.youtube.com/watch?v=PUKOWrOuC0c0. 숫자 0부터 시작한 이유는 1보다 더 중요한 것이기 때문입니다. 에러가 났을 때 해결을 하는 게 중요한 게 아닙니다. 왜 여러분은 해결을 못 하고 저는 해결을 하는지, 어디서 힌트를 얻은 것이고 어떻게 해결한 건지 그걸 알아가셔야 합니다. 그렇지 못한 질문은 무의미한 질문입니다.1. 에러 메시지를 올리기 전에 반드시 스스로 번역을 해야 합니다. 번역기 요즘 잘 되어 있습니다. 에러 메시지가 에러 해결 단서의 90%를 차지합니다. 한글로 번역만 해도 대부분 풀립니다. 그냥 에러메시지를 올리고(심지어 안 올리는 분도 있습니다. 저는 독심술사가 아닙니다) 해결해달라고 하시면 아무런 도움이 안 됩니다.2. 에러 메시지를 잘라서 올리지 않아야 합니다. 입문자일수록 에러메시지에서 어떤 부분이 가장 중요한 부분인지 모르실 겁니다. 그러니 통째로 올리셔야 합니다.3. 코드도 같이 올려주세요. 다만 코드 전체를 다 올리거나, 깃헙 주소만 띡 던지지는 마세요. 여러분이 "가장" 의심스럽다고 생각하는 코드를 올려주세요.4. 이 강좌를 바탕으로 여러분이 응용을 해보다가 막히는 부분, 여러 개의 선택지 중에서 조언이 필요한 부분, 제 경험이 궁금한 부분에 대한 질문은 대환영입니다. 다만 여러분의 회사 일은 질문하지 마세요.5. 강좌 하나 끝날 때마다 남의 질문들을 읽어보세요. 여러분이 곧 만나게 될 에러들입니다.6. 위에 적은 내용을 명심하지 않으시면 백날 강좌를 봐도(제 강좌가 아니더라도) 실력이 늘지 않고 그냥 코딩쇼 관람 및 한컴타자연습을 한 셈이 될 겁니다.
- 해결됨[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
시퀄라이즈 연결질문...
require('dotenv').config(); const PORT = process.env.PORT; const dotenv = require('dotenv'); const express = require('express'); const compression = require("compression"); const methodOverride = require("method-override"); const cors = require("cors"); const userRouter = require("./routes/userRoute"); const boardRouter = require("./routes/boardRoute"); const authRouter = require('./routes/auth'); // 인증 라우터 const ejsMate = require('ejs-mate'); const path = require('path'); const session = require('express-session'); const morgan = require('morgan'); const nunjucks = require('nunjucks'); const passport = require('passport'); const passportConfig = require('./passport'); const { sequelize } = require('../models/index'); const cookieParser = require('cookie-parser'); const app = express(); passportConfig(); nunjucks.configure('views', { express : app, watch : true, }); sequelize.sync({ force: true }) .then(() => { console.log("데이터 베이스 연결 성공") }) .catch((err) => { console.log(err); }); app.engine('ejs', ejsMate) app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views')) app.use(express.urlencoded({ extended: true })); app.use(compression()); app.use(express.json()); app.use(express.urlencoded({extended:true})); app.use(methodOverride()); app.use(cors()); app.use(cookieParser(process.env.COOKIE_SECRET)); app.use( session({ resave: false, saveUninitialized: false, secret: process.env.COOKIE_SECRET, cookie: { httpOnly: true, secure: false, }, }), ); //! express-session에 의존하므로 뒤에 위치해야 함 app.use(passport.initialize()); // 요청 객체에 passport 설정을 심음 app.use(passport.session()); // req.session 객체에 passport정보를 추가 저장 // passport.session()이 실행되면, 세션쿠키 정보를 바탕으로 해서 passport/index.js의 deserializeUser()가 실행하게 한다. app.use('/', userRouter); app.use('/', boardRouter); app.use('/auth', authRouter); app.get('/', (req,res)=>{ res.render('home') }) app.listen(PORT, () => { console.log(`SERVING ON THE ${PORT}`); }) module.exports = app;<env파일>DB_HOST = localhostDB_USER = rootDB_PASS = tkfkdgo12DB_NAME = nodejsPORT = 3000JWT_SECRET = softsquared_jwt_secret_key_07040014087COOKIE_SECRET = cookiesecretKAKAO_ID = bcb213727449d2b53039dcca5f541c02const Sequelize = require('sequelize'); const path = require('path'); const env = process.env.NODE_ENV || 'development'; const User = require('./user'); // config/config.json 파일에 있는 설정값들을 불러온다. // config객체의 env변수(development)키 의 객체값들을 불러온다. // 즉, 데이터베이스 설정을 불러온다고 말할 수 있다. const config = require('../config/config')[env] const db = {}; // new Sequelize를 통해 MySQL 연결 객체를 생성한다. const sequelize = new Sequelize(config.database, config.username, config.password, config) // 연결객체를 나중에 재사용하기 위해 db.sequelize에 넣어둔다. db.Sequelize = Sequelize; db.sequelize = sequelize; User.init(sequelize); // 모듈로 꺼낸다. module.exports = db; <models 폴더 내 index.js>const Sequelize = require('sequelize'); const path = require('path'); const env = process.env.NODE_ENV || 'development'; const User = require('./user'); // config/config.json 파일에 있는 설정값들을 불러온다. // config객체의 env변수(development)키 의 객체값들을 불러온다. // 즉, 데이터베이스 설정을 불러온다고 말할 수 있다. const config = require('../config/config')[env] const db = {}; // new Sequelize를 통해 MySQL 연결 객체를 생성한다. const sequelize = new Sequelize(config.database, config.username, config.password, config) // 연결객체를 나중에 재사용하기 위해 db.sequelize에 넣어둔다. db.Sequelize = Sequelize; db.sequelize = sequelize; User.init(sequelize); // 모듈로 꺼낸다. module.exports = db; <config.json>{ "development": { "username": "root", "password": "tkfkdgo12", "database": "nodejs", "host": "localhost", "dialect": "mysql" }, "test": { "username": "root", "password": null, "database": "database_test", "host": "127.0.0.1", "dialect": "mysql" }, "production": { "username": "root", "password": null, "database": "database_production", "host": "127.0.0.1", "dialect": "mysql" } } <오류메시지>비밀번호 설정도 다해줬는데 모르겠습니다 ㅠㅠ
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
res.setHeader vs res.cookie
res.setHeader("Set-Cookie", [ "mycookie=test; HttpOnly; SameSite=Lax; Path=/", ]);res.setHeader로 Set-Cookie를 보내면 브라우저에 cookie가 저장됩니다. res.cookie("mycookie", encodeURIComponent("test"), { expires: new Date(), httpOnly: true, sameSite: "lax", path: "/", });cookieParser를 사용하면 Set-Cookie가 header에 들어가기는 하지만 브라우저엔 저장되지 않았습니다.저장시키려면 어떻게 해야 하나요?
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
실무에서의 에러처리에 대해 질문드립니다.
만약 존재하지 않는 url에 유저가 접근했을 때,서버에서 에러가 발생했을 때 화면에 보여줘야 하는 파일까지 같이 넘겨주는지,아니면 서버에서는 status코드만 넘기고 프론트에서 status코드를 받아서 프론트가 화면에 에러를 표시하는지가 궁금합니다.요약하면 에러가 발생했을 때 화면에 보이는 페이지를 서버가 담당하나요 아니면 프론트가 담당하나요?
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
Sequelize 질문
안녕하세요 One-to-Many 관계ondelete : "cascade" 관련 질문이 있습니다 게시판을 만들어서, 댓글 테이블과 대댓글 테이블을 One-to-Many관계로 해서, 댓글을 지우면 해당 댓글에 달린 대댓글도 같이 삭제가 되도록 ondelete : "cascade" 설정을 했습니다. models/commnet(댓글).js const Sequelize = require("sequelize"); module.exports = class Comment extends Sequelize.Model { static init(sequelize) { return super.init( { reply: { type: Sequelize.TEXT, allowNull: false, }, nick: { type: Sequelize.STRING(50), allowNull: false, }, }, { sequelize, timestamps: true, underscored: false, modelName: "Comment", tableName: "comments", paranoid: true, charset: "utf8", collate: "utf8_general_ci", } ); } static associate(db) { db.Comment.belongsTo(db.Post); db.Comment.belongsTo(db.User); db.Comment.hasMany(db.Recomment, { onDelete: "CASCADE", hooks: true }); } }; models/recomment(대댓글).jsconst Sequelize = require("sequelize"); module.exports = class Recomment extends Sequelize.Model { static init(sequelize) { return super.init( { re_reply: { type: Sequelize.TEXT, allowNull: false, }, nick: { type: Sequelize.STRING(50), allowNull: false, }, UserId: { type: Sequelize.INTEGER(40), allowNull: false, }, }, { sequelize, timestamps: true, underscored: false, modelName: "Recomment", tableName: "recomments", paranoid: true, charset: "utf8", collate: "utf8_general_ci", } ); } static associate(db) { db.Recomment.belongsTo(db.Comment); db.Recomment.belongsTo(db.Post); } }; Sequelize 공식 문서에 따라( https://sequelize.org/docs/v6/other-topics/hooks/#one-to-one-and-one-to-many-associations ) db.Comment.hasMany(db.Recomment, { onDelete: "CASCADE", hooks: true });db.Recomment.belongsTo(db.Comment);다음과 같이 작성했습니다. (안 되어서 양쪽에 {onDelete: "CASCADE", hooks : true} 를 다 넣어보기도 했지만 역시나 안 되었습니다.)DB는 MySQL을 사용하고 있습니다. 위와 같이 한 후, npm start 를 하여서 MySQL에서 comment테이블과 recomment테이블이 생성되었는데, association에 의해 생긴 CommentId 속성이 onDelete set null 로 되어 있어서, migration을 진행했습니다. 처음에는 changeColumn 을 실행했는데, CommentId 기존의 것은 그대로 있고, onDelete cascade가 적용된 CommnetId가 하나 더 생기는 이상한 일이 발생해서, removeColumn 을 migration으로 진행한 후, 아래와 같이 addColumn을 진행해서, MySQL에서는 원하는 설정으로 나왔습니다."use strict"; /** @type {import('sequelize-cli').Migration} */ module.exports = { async up(queryInterface, Sequelize) { return queryInterface.addColumn("Recomments", "CommentId", { type: Sequelize.INTEGER(40), references: { model: "comments", key: "id", }, onDelete: "CASCADE", onUpdate: "CASCADE", }); }, async down(queryInterface, Sequelize) { return queryInterface.removeColumn("Recomments", "CommentId"); }, }; 그런데param.id로 CommentId를 받아와서 해당 id의 데이터를 comment테이블에서 삭제하도록 했을 때, const CommentId = parseInt(req.params.id, 10); await Comment.destroy({ where: { id: CommentId } });comment테이블에서 삭제가 되었으나, recomment 테이블에서는 삭제가 되지 않습니다..ㅠ 어떻게 해야 할까요?
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
module.exports와 exports 사이의 참조관계 재연결 관련
강의 내용 관련 질문드립니다.CJS 사용시 module.exports와 exports는 같은 객체를 참조하고 있지만, module.exports에 값을 할당할 경우 참조관계가 끊어지고 exports는 해당 파일 스코프에서 단순 객체로 사용된다는 점 까지는 이해했습니다.하지만 예시로 참조를 다시 연결하기 위해 아래와 같은 방식을 소개해 주셨는데, 해당 방식을 직접 하드코딩 해본 결과 참조값이 여전히 끊어져 있는 것이 확인되었습니다.const odd = '홀수입니다.'; const even = '짝수입니다.'; exports.odd = odd; exports.even = even; module.exports = { odd, even, }; console.log(exports); // { odd: '홀수입니다.', even: '짝수입니다.' } console.log(module.exports); // { odd: '홀수입니다.', even: '짝수입니다.' } console.log(exports === module.exports); // false혹시 다른 조건이 만족된다면 해당 참조관계를 재연결 시킬 수 있는지 궁금합니다.강의 내용은 exports와 this편 4:47 부분입니다.
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
sse https프로토콜 사용시
sse는 http 프로토콜을 사용하는데,https 프로토콜을 사용하는 서버에서 데이터를 받아오면 오류가 나나요? https 프로토콜을 사용하는 서버에서 실습을 진행해보고 있는데, sse가 작동하지 않아서 질문올립니다
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
app.use와 app.get의 차이점이 궁금합니다.
안녕하세요. 수업 잘 듣고 있습니다.한가지 궁금해서 문의드립니다.app.use와 app.get을 사용하셨는데, set와 get의 차이점이 무엇인지 잘 모르겠습니다.
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
제로초님 우분투 서버 관리에 대해서 궁금한 점이 있습니다.
앞서 설명하실때 git을 사용함에 있어서 제가 작성한 프로젝트를 master라고 했을때 origin으로 설정된 제 깃 허브 주소로console : git push origin master를 했을때 오리진인 제 깃 허브에 master인 제가 작업중인 코드가 올라간다고 이해를 했습니다. 그렇다면 제가 수정을 할때마다 Modified라고 감시받고 있었는데 따로 계속 올려야 하는 건가요? 아니면 그냥 일정 시간마다 수정된 것을 감지하고 git이 git hub로 파일을 수정한 상태로 올려주는건가요? 그리고 우분투를 사용할때 git hub에서 clone으로 소스코드를 가져오셨는데 제가 깃 허브에 올라온 내용을 수정한 코드를 우분투에서는 계속해서 수정할수는 없는건가요? 혹여나 수정이 불가피하다면 우분투에 제 클론을 삭제하고 다시 깃허브에서 재클론해서 가져와야 하는 건가요?프로젝트를 가볍게 진행중인데 궁금점이 생겨서 질문 드립니다 감사합니다.
- 해결됨[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
메서드 스푸핑 질문
제로초님 블로그를 보다가 method-override 패키지를 알게되고 좀더 찾아보니까 메서드 스푸핑에 대해서 알게 되었습니다.<form method="POST" action="/posts/1"> <input type="hidden" name="_method" value="PUT"> </form>강의에선 그냥 이벤트 맊아놓고 오로지 자바스크립트로 ajax 요청 때리도록 하셨는데, 대부분의 현업에서는 메서드 스푸핑은 쓰지 않는지 궁금합니다.
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
관계 쿼리 질문입니다
위의 코드는 제로초님께서 예시로 설명해주신 코드입니다.사용자 데이터베이스와 연결된 댓글 데이터베이스에서 아이디가 1인 댓글의 아이디와, 사용자를 불러오는 쿼리로 이해하고 있습니다. 이것은 제가 작성한 쿼리입니다.의도한 의미는 다음과 같습니다사용자중에서, 현재 req의 저장된 id와 일치하는 사용자를 찾고, 그 사용자와 함께 그 사람의 following관계에 있는 데이터를 불러온다.그러나 실행하면, followings를 찾을 수 없다는 오류가 나옵니다. 사실 뭐가 문제인지 모르겠는데, 혹시 위와 같이 include해서 관계 쿼리를 사용할때, where을 include 밖에 작성하면 안되는 것인가요?그렇다면 제로초 남께서 설명해주신 관계쿼리의 다른 방법으로먼저 사용자를 불러오고,user.getFollowings 을통해서 following 관계에 있는 사람들을 불러와야 하는 것인가요?위와 같은 구현을 하고자 할때, user.getFollowing을 사용하지 않고, include를 통해서 쿼리를 작성할 수 있다면 코드를 알려주시면 감사하겠습니다
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
1. 라우터 연결 오류 2. req.response가 undefined
수정 전 코드에서는 다음과 같은 오류가 뜨면서 라우터 연결이 안됩니다.GET /v1/posts/my 라우터가 없습니다.(1-1) 수정 전 nodebird-api의 app.jsconst 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.jsconst 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.jsconst 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.jsconst 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.jsconst 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로 넘어간 것인지 전혀 감이 잡히지 않습니다.
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
네스트와 익스프레스의 차이가 뭐죠?
노드강의 있으신데 익스프레스 강의는 별도로 없고노드강의에서 익스프레스 학습을 추가 해놓으셨는데요.네스트 강의는 별도로 만드셨다 말이죠.네스트와 익스프레스의 차이가 뭔가요?익스프레스는 단순 require('express') 하고 쓰면, 익스프레스 프레임워크를 사용하는 건가요?
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
노드가 동적타입 인가요?
타입스크립트는 정적타입이잔아요.노드는 동적타입 언어인가요?순전히 궁금해서 여쭤봅니다.
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
템플릿 라이브러리
리액트 혹은 뷰를 쓰게 된다면,nunjucks , pog ? 같은 템플릿은 사용이 거의 안됩니까?
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
nodemon app 명령어로 시작할 때 에러 질문합니다.
아래와 같은 에러가 납니다..data 를 찾을수 없다고 하는것 같습니다.해당 에러는 nodemon app 명령어로 서버를 시작하고 주소창에 http://localhost:3000/을 입력했을 떄 의 결과 로그입니다.
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
노드모듈 계층도 질문합니다.
ch6. - 6.1 -6.2 -6.3 위와 같이 폴더가 계층구조일때,/ch6 위치에서 nodemon, cookie-parser 모듈을 추가한다면,6.1 과 6.2, 6.3 폴더에도 의존성이 추가 되는가요?폴더별로 따로따로 모듈을 추가해줘야 하는가요?
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
전역 상태 관리 질문드립니다.
react에서는 redux를 보통 쓰던데템플릿 엔진 사용할 때는 전역 상태는 어떻게 관리하나요? res.locals에 저장해서 사용하나요? 아니면 여기서도 redux를 사용하나요?
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
소스코드 4.2 / restFront.js 49번 라인
에러명 : ReferenceError: window is not defined 변경 : 47번째줄 window -> global위와 같이 해결하면 문제없죠? 에러명 : ReferenceError: document is not defined변경 : global.document.getElementById(...) 2.위와같이 변경해줘도 해결이 안되는중입니다. 어떻게 고쳐줘서 해결해야 하죠?
- 미해결[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지
에러 처리하기
http://localhost:8080 200 (ok)https://localhost:8080 failed 위와같이 나오는 원인이 무엇인지 알고 있으신지요? 제공하신 소스의 4-1 폴더server1-1.js 코드입니다.