Node.js 교과서 - 기본부터 프로젝트 실습까지

Node.js 교과서 - 기본부터 프로젝트 실습까지

(29개의 수강평)

646명의 수강생
55,000원
지식공유자 · 조현영
145회 수업 · 총 23시간 14분 수업
평생 무제한 시청
수료증 발급 강좌
수강 난이도 '초급'
사탕순대 프로필

혼자해보기는 정답이 없는건가요?? 사탕순대 4시간 전

깃허브에있는 코드가 정답이아니라 스스로 해보라는것인가요??

혹시 완성되있는코드는 따로 없을가요 다른사람이 해본것과 비교해보고싶은데

1
김건훈 프로필

request 작성 김건훈 21시간 전

https://developers.kakao.com/docs/restapi/tool#user-api#/v1/user/unlink

저 곳 설명에 의하면 https://kapi.kakao.com/v1/user/unlink 로 헤더에 Authorization: Bearer 4S4XZfAsIZ1RFqr9bwWc2N0-qK-lW3ocUMIZUQorDKYAAAFugtugRw 내용을 담아 request요청을 보내라고 하여

 if (req.user.dataValues.provider == 'kakao'){
        let option = {
            uri: 'http://kapi.kakao.com/v1/user/unlink',
            method: 'POST',
            headers: `Authorization: Bearer ${req.user.dataValues.token}`,
        }
        requset(option, (error, respons, body) => {
            if (error){
                console.error(error);
                return next(error);
            } else {
                console.log("카톡 리퀘스트로 됨!!");
                req.logout();
                req.session.destroy();
                res.redirect('/'); 
            }
        });
    } else{
        console.log("logout OK"+req.user.dataValues.token);
        req.logout();
        req.session.destroy();
        res.redirect('/');   
    }   

이렇게 작성했으나 보내지지 않았습니다 ㅠㅠ 혹시 request에 헤더를 넣어 보내는 법을 알려주실 수 있나요?

3
김건훈 프로필

카카오톡 로그아웃 안됨. 김건훈 1일 전

router.get('/logout', isLoggedIn, (req, res) => {
    req.logout();
    req.session.destroy();
    res.redirect('/');
    
})

라우터에 로그아웃을 잘 작성하였고 세션도 비워주지만 아이디와 비밀번호를 적지 않고 카카오톡 버튼을 눌러도 다른 카카오톡 아이디와 비밀번호를 입력하고 눌러도, 서버를 실행하고 처음 카카오톡 아이디로 로그인한 계정으로 다시 로그인 됩니다. Token이나 login방식에 문제가 있는 것 같은데 어디가 잘못된 것인지 감이 안잡힙니다.

https://devtalk.kakao.com/t/node-js-passport/48774

구글 검색 결과 같은 증상으로 카카오톡에 문의한 사람이 있었으나 저 해결책을 보고 무슨 의미인지 이해하지 못했습니다 ㅠㅜ...

6
Neo Faut 프로필

"crypto.createCipher" 사용시 deprecated (node12) Neo Faut 6일 전

강사님, 안녕하세요? 

crypto 부분에서 (책 3.5.5.2 양방향 암호화)

2019년 11월 현재 

"crypto.createCipher" 사용시 deprecated 되었다고 경고를 보내주고 있습니다. (node12)

"crypto.createCipheriv" 를 사용하라고 나오는데, 사용방법이 궁금합니다. iv 가 이니셜벡터라고 하는데 정확하게 어떤 것인지 잘 모르겠네요 ^^; 

node 8에서만 createCipher 를 사용해야 할까요?

감사합니다.

1
­멧돌짱 프로필

cookie.sign 관련 질문 하나 드리겠습니다. ­멧돌짱 6일 전

안녕하세요!!

강의 잘 듣구있습니다!!

Cookie: `connect.sid=${'s%3A' + cookie.sign(req.signedCookies['connect.sid'], process.env.COOKIE_SECRET)}`

헤더에 위와 같은 쿠키를 넣어 보내는 이유가 결국은

"같은 사람이 보냈다는 것을 서버에게 알려주기 위함" 이라고 말씀해주셨는데, 제가 궁금한 것은 

그렇다면 결국 최초 localhost:8005 에 접속했을 때, 서버가 보내는 set-cookie 의 값(=express 가 만든 세션 쿠키)과
's%3A' + cookie.sign()  이거로 인해 생긴 쿠키의 문자열?이 결과적으론 같다는 것으로 이해했습니다. 실제로 console 로 찍어보니 같게 나오기도 해서 일단 받아들였습니다.

제가 궁금한 것은
1. 이 2가지 쿠키가 어떻게 암호화가 과정을 거쳐서 
같은 의미를 나타내는 쿠키가 되는지 궁금합니다.

결국 req.sign() 이란 메서드가 서명된 쿠키를 같은 key로 암호화하기 때문에 같아지는 것인지...

2. req.signedCookies['connect.sid'] 는 말 그대로 서명된 쿠키인데, 이 서명된 쿠키를 굳이 왜 또 같은 secret-key 로 암호화하게 되는 것인지 ㅠㅠ...

express가 만든 세션 쿠키(req.signedCookies['connect.sid']) --> 클라이언트 ( 한 번 더 암호화?? 엄청 긴 이상한 string) --> 다시 express로 들어올 떄(req.signedCookies['connect.sid']) --> axios 로 보낼 때( sign() 메서드로 아까 클라이언트 보낼 때랑 같은 한 번더 암호화? 된 긴 string) 

위와 같은 흐름이 맞는지 궁금합니다.

저 스스로가 잘 이해를 못하고 있어서 질문이 좀 난해한 것 같아서 죄송합니다!! 

감사합니다.

콘솔로그 결과는 아래 첨부 해놓겠습니다!!

최초 connection 때의 header :  {"host":"localhost:8005","connection":"keep-alive","accept":"*/*","user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36","sec-fetch-site":"same-origin","sec-fetch-mode":"cors","referer":"http://localhost:8005/room/5dcbfa3fff28e31ffcb4d4bf?password=","accept-encoding":"gzip, deflate, br","accept-language":"ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7","cookie":"connect.sid=s%3APILsEVhtt485hYWal47Hi5LzVmU9xzMy.uKLfTbFcdOJLpo3id8g9Biw1Dg2EH9F7yWQtJqyOS7k; io=o0yIKhR3KcO_wrVBAAAC"}

req.signedCookies['connect.sid'] :  PILsEVhtt485hYWal47Hi5LzVmU9xzMy

cookie.sign(req.signedCookies['connect.sid'], process.env.COOKIE_SECRET) :  PILsEVhtt485hYWal47Hi5LzVmU9xzMy.uKLfTbFcdOJLpo3id8g9Biw1Dg2EH9F7yWQtJqyOS7k

axios로 /room/:id/sys 요청할 때 헤더 :  {"accept":"application/json, text/plain, */*","content-type":"application/json;charset=utf-8","cookie":"connect.sid=s%3APILsEVhtt485hYWal47Hi5LzVmU9xzMy.uKLfTbFcdOJLpo3id8g9Biw1Dg2EH9F7yWQtJqyOS7k","user-agent":"axios/0.19.0","content-length":"15","host":"localhost:8005","connection":"close"}

3
김건훈 프로필

로컬 로그인시 이런 문제가 발생합니다. 김건훈 8일 전

Missing credentials

왜 이런 문제가 발생하는지 모르겠습니다 ㅠ

1
nodeMoon 프로필

질문있습니다 nodeMoon 8일 전

https://www.inflearn.com/questions/12695

여기 질문 내용과 중복되어서 링크도 함께 남깁니다.

답변해주신 2번 내용에서 프론트 요청인지, 서버요청 인지에 따라 분기처리해주는게 좋다고 하셨는데, 마지막 대댓글처럼 main.pug에서 ajax요청시에는 프론트요청이고, call서버의 index.js에서 axios요청시에는 서버 요청인건 알겠는데... 이걸 어떻게 구분해서 처리하는건지 잘 모르겠습니다 ㅠㅠ

둘 다 값이 clientSecret에 담겨오는 것 같은데 ... 이 값이 어느 요청인지 구분하는 방법이 무엇인가요?

혹시 req.headers부분으로 구분하는건가요??

1
­멧돌짱 프로필

질문하나만 드리겠습니다. ­멧돌짱 9일 전

안녕하세요!!

강의 잘 듣고 있습니다.

다름 아니라 스스로 해보기 해설을 보기 전에 저 스스로 푸려고 고군분투 중인데, 

chat.on('connection', (socket) => {

      socket.to(roomId).emit('join', args) 

})

이 코드에서, 기존에 시스템 메세지를 프론트로 전달했었습니다.

제가 궁금한 점은 '최초로 방을 만든 사람'이 connection이 될 때는 저 emit 함수가 작동이 안되는지, 채팅창에 최초로 방 만든 사람의 정보가 뜨지 않습니다.

(Microsoft Edge 로 다음 사람이 들어올 때는 잘 됩니다!!)

저는 '최초로 방을 만든 사람'이 만든 순간에도 emit 함수를 작동시키고 싶은데 혹시 어떻게 하면 좋을까요?? 

감사합니다. 

2
nodeMoon 프로필

async await nodeMoon 12일 전

routes/page.js 파일에서

//메인페이지
router.get('/', (req, res, next) => {
Post.findAll({
include: {
model: User,
attributes: ['id', 'nick'],
},
order: [['createdAt', 'DESC']],
})
.then( (posts) => {
res.render('main', {
title: 'NodeBird',
twits: posts,
user: req.user,
loginError: req.flash('loginError'),
});
})
.catch ( (error) => {
console.error(error);
next(error);
});
});

이부분을 async로 바꾸고 싶어서..

router.get('/', async (req, res, next) => {
  try {
    const posts = await Post.findAll({
      include: {
        model: User,
        attributes: ['id', 'nick'],
      },
      order: [['createdAt', 'DESC']],
    });
  
   res.render('main', {
        title: 'Nodebird',
        twits: posts,
        user: req.user,
        loginError: req.flash('loginError'),
    });
  } catch (err) {
    console.error(err);
    next(err);
  }
});

이렇게 했는데...맞게 바꾼건가요?

await가 붙으면 작업이 끝날 때까지 기다린다는 의미라서 posts에 값이 담길 때까지 기다리고

담긴 후에 render가 실행된다는 의미 맞을까요?

제가 비동기 부분에 대한 이해가 좀 부족한것 같네요 ㅠㅠ

===========================================

아, 그리고 추가적으로

routes/post.js 에서

router.get('/hashtag', async (req, res, next) => {
const query = req.query.hashtag; //사용자가 검색하고자 하는 것
if (!query) { //아무것도 입력 안하면
return res.redirect('/');//기본페이지로
}
try {
const hashtag = await Hashtag.findOne( { where: { title: query }});
let posts = [];
if (hashtag) {//찾은 것이 있으면
posts = await hashtag.getPosts({ include: [{ model: User }]});//다대다관계 가져오면서, 사용자까지 넣어서 가져옴
}
return res.render('main', {
title: `${query} | NodeBird`,
user: req.user,
twits: posts,
});
} catch (error) {
console.error(error);
next(error);
}
});

마지막에 render하시면서 return을 붙이시던데, 특별한 이유가 있으신건가요?

1
Daniel Hwang 프로필

sequelize 사용 시 인코딩 에러인것 같습니다. Daniel Hwang 14일 전

첨부된 에러 캡쳐를 보시면, 한글이 인코딩 되지 않고 mysql에 전달되어서 발생한 것 같습니다.. 

인터넷에 검색해서 mysql 전체적인 인코딩 세팅을 변경하는 방법을 찾아서 해보고 있는데 잘 안되네요ㅠ

어떻게 하면 될까요?ㅠ

2
김건훈 프로필

같은 방식으로 코딩을 했는데요 이런 에러가 뜨네요. 김건훈 14일 전

D:\Study\Server\nodejs\nodejsLib\login\node_modules\sequelize\lib\model.js:230

        throw new Error(`A column called 'id' was added to the attributes of '${this.tableName}' but not marked with 'primaryKey: true'`);

        ^

Error: A column called 'id' was added to the attributes of 'users' but not marked with 'primaryKey: true'

    at Function._addDefaultAttributes (D:\Study\Server\nodejs\nodejsLib\login\node_modules\sequelize\lib\model.js:230:15)

    at Function.init (D:\Study\Server\nodejs\nodejsLib\login\node_modules\sequelize\lib\model.js:1052:10)

    at Sequelize.define (D:\Study\Server\nodejs\nodejsLib\login\node_modules\sequelize\lib\sequelize.js:428:11)

    at module.exports (D:\Study\Server\nodejs\nodejsLib\login\models\user.js:2:22)

    at Object.<anonymous> (D:\Study\Server\nodejs\nodejsLib\login\models\index.js:15:28)

    at Module._compile (internal/modules/cjs/loader.js:971:30)

    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1011:10)

    at Module.load (internal/modules/cjs/loader.js:822:32)

    at Function.Module._load (internal/modules/cjs/loader.js:730:14)

    at Module.require (internal/modules/cjs/loader.js:864:19)

    at require (internal/modules/cjs/helpers.js:74:18)

    at Object.<anonymous> (D:\Study\Server\nodejs\nodejsLib\login\app.js:11:21)

    at Module._compile (internal/modules/cjs/loader.js:971:30)

    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1011:10)

    at Module.load (internal/modules/cjs/loader.js:822:32)

    at Function.Module._load (internal/modules/cjs/loader.js:730:14)

    at Module.require (internal/modules/cjs/loader.js:864:19)

    at require (internal/modules/cjs/helpers.js:74:18)

    at Object.<anonymous> (D:\Study\Server\nodejs\nodejsLib\login\bin\www:7:11)

    at Module._compile (internal/modules/cjs/loader.js:971:30)

    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1011:10)

    at Module.load (internal/modules/cjs/loader.js:822:32)

npm ERR! code ELIFECYCLE

npm ERR! errno 1

npm ERR! login@0.0.0 start: `node ./bin/www`

npm ERR! Exit status 1

npm ERR!

npm ERR! Failed at the login@0.0.0 start script.

npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:

npm ERR!     C:\Users\dnatu\AppData\Roaming\npm-cache\_logs\2019-11-05T13_48_03_653Z-debug.log

테이블 코드는 선생님 코드와 같지는 않고 필드명과 테이블 명만 제가 원하는 대로 살짝 수정하긴 했습니다만 이것이 영향을 미치는 것 같지는 않습니다! index.js는 기본으로 생성되는 페이지 그대로 사용했습니다.

user.js

module.exports = (sequelize, DataTypes) => {
    return sequelize.define('user', {
        name:{
            type: DataTypes.STRING(5),
            allowNULL: false,
            unique: false,
        },
        id: {
            type: DataTypes.STRING(30),
            allowNULL: false,
            unique: true,
        },
        passwd: {
            type: DataTypes.STRING(20),
            allowNULL: false,
            unique: false,
        },
        age: {
            type: DataTypes.INTEGER(2).UNSIGNED,
            allowNULL: false,
            unique: false,
        },
        phone_num: {
            type: DataTypes.INTEGER(12).UNSIGNED,
            allowNULL: false,
            unique: true,
        },
        created_at: {
            type: DataTypes.DATE,
            allowNULL: false,
            defautlValue: sequelize.literal('now()'),
        },
        store_name:{
            type: DataTypes.STRING(20),
            allowNULL: false,
            unique: true,
        }
    }, {
        timestamp: false,
        underscored: true,
    });
};

// user 테이블 정리
// 이름(5), 아이디(30), 비밀번호(20), 나이(2), 휴대폰 번호(12), 계정생성일(DATE->default(now)), 마켓 이름(20)

iteam.js

module.exports = (sequelize, DataTypes) => {
    return sequelize.define('iteam', {
        iteam_name: {
            type: DataTypes.STRING(30),
            allowNULL: false,
            unique: false,
        },
        item_price: {
            type: DataTypes.INTEGER(6).UNSIGNED,
            allowNULL: false,
            unique: false,
        },
        item_count: {
            type: DataTypes.INTEGER(6).UNSIGNED,
            allowNULL: false,
            unique: false,
        },
        item_description: {
            type: DataTypes.TEXT,
            allowNULL: true,
            unique: false,
        },
        created_at:{
            type: DataTypes.DATE,
            allowNULL: false,
            defautlValue: sequelize.literal('now()'),
        },
    },{
        timestamp: false,
        underscored: true,
    });
};

// store 테이블 정리
// 마켓 이름, 물건명, 가격, 물품 개 수, 물품 설명, 등록일자

index.js

'use strict';

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};

let sequelize;
if (config.use_env_variable) {
  sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
  sequelize = new Sequelize(config.database, config.username, config.password, config);
}

fs
  .readdirSync(__dirname)
  .filter(file => {
    return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
  })
  .forEach(file => {
    const model = sequelize['import'](path.join(__dirname, file));
    db[model.name] = model;
  });

Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

//테이블 추가
db.User = require('./user')(sequelize, Sequelize);
db.Iteam = require('./iteam')(sequelize, Sequelize);

//관계 설정
db.User.hasMany(db.Iteam, { foreignKey: 'store_name', sourceKey: 'id' });
db.Iteam.belongsTo(db.User, { foreignKey: 'store_name', targetKey: 'id'});

module.exports = db;

2
김건훈 프로필

자동완성? 기능에 대해 질문있습니다. 김건훈 14일 전

제로초님은 user.js나 comment.js에서 DataTypes의 자동완성? 기능이 잘 실행되는데 저는 잘 안됩니다 ㅠㅜ. 예를 들어 'DataTypes.' 하시면 뒤에 Sequelize의 타입들이 주르륵 뜨는데 저는 뜨지 않네요. 제 에디터는 vscode인데 에디터의 차이인가요?

1
paul.moon 프로필

2.2 프런트엔드 자바스크립트 강의는 없나요? paul.moon 19일 전

책의 2.2.1 ~ 2.2.4 강의는 어디에 있는지요?

1
skdltm357 프로필

Error: secret option required for sessions skdltm357 19일 전

지금 jwt 와 session을 동시에 사용하려고 하고 있는데 둘 다 사용하려고 하면 Error: secret option required for sessions 이 에러가 나옵니다. 

그렇다고 session을 사용안하면 Login api 호출할 때 Error: passport.initialize() middleware not in use 에러가 납니다.

passport.initialize() 만 하게 되면 Error: Failed to serialize user into session 에러가 나옵니다..

혹시 jwt와 session을 동시에 사용할 수는 없나요 ?

jwt는 userid와 password를 담는 용도가 아니라 다른 용도로 사용하려고 하는 토큰입니다.

Error: secret option required for sessions를 해결하는 방법을 알려주시면 감사하겠습니다!

1
paul.moon 프로필

아래 getCandy()...에 대해서 paul.moon 20일 전

먼저 아래 글의 질문에 답해 주셔서 감사합니다. ^^;

그러면....
왜 교재에서는 
this.status.count-- 

이렇게 this를 사용해서
getCandy() 형태로 접근할 수  없는 걸 예시해 주셨는지....

this를 사용하는게 바람직한 이유라도 있는지 궁금 합니다. 

1
지식공유자 되기
많은 사람들에게 배움의 기회를 주고,
경제적 보상을 받아보세요.
지식공유참여
기업 교육을 위한 인프런
“인프런 비즈니스” 를 통해 모든 팀원이 인프런의 강좌들을
자유롭게 학습하는 환경을 제공하세요.
인프런 비즈니스