강의

멘토링

커뮤니티

인프런 커뮤니티 질문&답변

castinglife님의 프로필 이미지
castinglife

작성한 질문수

[리뉴얼] React로 NodeBird SNS 만들기

로그인 시도중 backend 에서 에러가 발생하면서 로그인이 되지 않습니다. 작성한 코드는 강사님이 작성하신 코드와 차이가 없는것 같습니다.

해결된 질문

작성

·

499

0

login(option)

login(Post)

아래는 backend  - log 입니다.

Executing (default): SELECT `id`, `email`, `nickname`, `password`, `createdAt`, `updatedAt` FROM `Users` AS `User` WHERE `User`.`email` = 'test@test.com';
Executing (default): INSERT INTO `Users` (`id`,`email`,`nickname`,`password`,`createdAt`,`updatedAt`) VALUES (DEFAULT,?,?,?,?,?);
Executing (default): SELECT `id`, `email`, `nickname`, `password`, `createdAt`, `updatedAt` FROM `Users` AS `User` WHERE `User`.`email` = 'test@test.com';
(node:11018) UnhandledPromiseRejectionWarning: Error: Include unexpected. Element has to be either a Model, an Association or an object.
    at Function._conformInclude (/Users/imac/Desktop/nodebird study/back/node_modules/sequelize/lib/model.js:383:11)
    at /Users/imac/Desktop/nodebird study/back/node_modules/sequelize/lib/model.js:319:59
    at Array.map (<anonymous>)
    at Function._conformIncludes (/Users/imac/Desktop/nodebird study/back/node_modules/sequelize/lib/model.js:319:39)
    at Function._baseMerge (/Users/imac/Desktop/nodebird study/back/node_modules/sequelize/lib/model.js:786:10)
    at Function._defaultsOptions (/Users/imac/Desktop/nodebird study/back/node_modules/sequelize/lib/model.js:823:17)
    at Function._injectScope (/Users/imac/Desktop/nodebird study/back/node_modules/sequelize/lib/model.js:3224:10)
    at Function.findAll (/Users/imac/Desktop/nodebird study/back/node_modules/sequelize/lib/model.js:1694:10)
    at Function.findOne (/Users/imac/Desktop/nodebird study/back/node_modules/sequelize/lib/model.js:1905:23)
    at /Users/imac/Desktop/nodebird study/back/routes/user.js:25:56
(node:11018) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:11018) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

에러 메세지를 읽은 결과...

원인이 두가지로 생각되는데요 

1. routes/user.js  에  route /login  부분의 요소가 연관 모델이 아니다.

2. routes/user.js  에  route /login  함수를 try catch 로 묶어 줘라.

 제가 이해한것이 맞는지 모르겠네요..

routes/user.js

const express = require('express');
const bcrypt = require('bcrypt');
const passport = require('passport');

const { User, Post } = require('../models');

const router = express.Router();

// 로그인 login
// 미들웨어 확장
router.post('/login', (req, res, next) => {
    passport.authenticate('local', (err, user, info) => { // 서버 에러, 성공, 클라이언트 에러
        if (err) {
            console.error(err);
            return next(err);
        }
        if (info) {
            return res.status(401).send(info.reasone); // 401: 허가되지 않음 , 403: 금지
        }
        return req.login(user, async (loginErr) => {    // passport login error 시
            if (loginErr) {
                console.error(loginErr);
                return next(loginErr);
            }
            const fullUserWithoutPassword = await User.findOne({
                where: { id: user.id },
                // attributes: ['id', 'nickname','email'],
                attributes: {
                    exclude: ['password']
                },
                include: [{
                    model: Post,
                    attributes: ['id'],
                }, {
                    medel: User,
                    as: 'Followings',
                    attributes: ['id'],
                }, {
                    model: User,
                    as: 'Followers',
                    attributes: ['id'],
                }]
            })
            // res.setHeader('Cookie', 'cxlhy')
            return res.status(200).json(fullUserWithoutPassword);
        });
    })(req, res, next);

}); 

// 로그아웃 logout
router.post('/logout', (req, res) => {
    req.logout();
    req.session.destroy(); // session 에 저장된 쿠키와 id 를 지운다.
    res.send('ok'); // 로그아웃 성공
});

// 회원가입 signup
router.post('/', async (req,res, next) => {     // POST /user
    try {
        const exUser = await User.findOne({
            where: {
                email: req.body.email,
            }
        });
        if (exUser) {
            return res.status(403).send('이미 사용 중인 아이디입니다.'); // 응답
        }
        const hashedPassword = await bcrypt.hash(req.body.password, 12);    // 10 ~ 13 - 해쉬화 한다.
        await User.create({
            email: req.body.email,
            nickname: req.body.nickname,
            password: hashedPassword,
        });

        // res.json();
        // res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
        // res.setHeader('Access-Control-Allow-Origin', '*');

        res.status(200).send('ok');  // res.status(200).send('ok');  응답
    } catch(error) {
        console.error(error);
        next(error); // status 500
    }
});
// router.delete('/', (req,res) => {       // DELETE /user
//     res.json({id: 1 });
// });

module.exports = router;

app.js

const express = require('express');
const cors = require('cors');
const session = require('express-session');
const cookieParser = require('cookie-parser');
const passport = require('passport');
const dotenv = require('dotenv');

const postAPIRouter = require('./routes/post');
const userAPIRouter = require('./routes/user');
const db = require('./models');
const passportConfig = require('./passport');


dotenv.config();

const app = express();
db.sequelize.sync()
    .then(() => {
        console.log('db 연결 성공');
    })
    .catch(console.error);
passportConfig();

app.use(cors({
    // origin: 'https://nodebird.com', // https://nodebird.com 에서 온 요청만 허용하겠다.
    origin: '*',    // 모든 요청 허용
    // credentials: false,  // 
}));
app.use(express.json());    // front server 에서 json 형식으로 데이터를 보냈을때 req.body 안에 data를 넣어준다.
app.use(express.urlencoded({ extended: true})); // from submit 했을때 urlencoded 방식으로 변환해준다.
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(session({
    saveUninitialized: false,
    resave: false,
    secret: process.env.COOKIE_SECRET,
}));
app.use(passport.initialize());
app.use(passport.session());

app.use('/post', postAPIRouter);
app.use('/user', userAPIRouter);

app.listen(3065, () => {
    console.log('서버 실행 중')
});

models/user.js

module.exports = (sequelize, DataTypes) => {
    const User = sequelize.define('User', {      // Mysql에는 users 테이블 생성
        //  id가 기본적으로 들어있다.
        email: {
            type: DataTypes.STRING(30),     // INTEGER: 정수, FLOAT: 소수(실수) BOOLEAN, FLOAT
            allowNull: false, // 필수
            unique: true,   // 고유한 값
        },
        nickname: {
            type: DataTypes.STRING(30),
            allowNull: false,   // 필수
        },
        password: {
            type: DataTypes.STRING(100),
            allowNull: false,   // 필수
        },
    }, {
        charset: 'utf8',
        collate: 'utf8_general_ci' // 한글 저장
    });
    User.associate = (db) => {
        db.User.hasMany(db.Post);
        db.User.hasMany(db.Comment);
        // Liked user 가 좋아요를 누른 post 를 가져온다..
        db.User.belongsToMany(db.Post, { through: 'Like', as: 'Liked' });

        db.User.belongsToMany(db.User, { through: 'Follow', as: 'Followers', foreignKey: 'FollowingId'});
        db.User.belongsToMany(db.User, { through: 'Follow', as: 'Followings', foreignKey: 'FollowerId'});
    };
    return User;

models/post.js

module.exports = (sequelize, DataTypes) => {
    const Post = sequelize.define('Post', {      // Mysql에는 posts 테이블 생성
        //  id가 기본적으로 들어있다.
        content: {
            type: DataTypes.TEXT,
            allowNull: false,
        },
        // RetweetId
    }, {
        charset: 'utf8mb4',
        collate: 'utf8mb4_general_ci' // 이모티콘 저장
    });
    Post.associate = (db) => {
        db.Post.belongsTo(db.User);
        db.Post.belongsToMany(db.Hashtag, { through: 'PostHashtag'} );
        db.Post.hasMany(db.Comment);
        db.Post.hasMany(db.Image);
        // user post 테이블간에 like 테이블이 생기고.. as 에 따라서 post.getLikers 처럼 게시글 좋아요..  누른 user 를 가져온다.
        // Likers 좋아요를 누른 user 를 가져온다.
        db.Post.belongsToMany(db.User, { through: 'Like', as: 'Likers' });
        db.Post.belongsTo(db.Post, { as: 'Retweet' })
    };
    return Post;
}

models/comment.js

module.exports = (sequelize, DataTypes) => {
    const Comment = sequelize.define('Comment', {      // Mysql에는 comments 테이블 생성
        //  id가 기본적으로 들어있다.
        content: {
            type: DataTypes.TEXT,
            allowNull: false,
        },
        // UserId: 1
        // PostId: 1
    }, {
        charset: 'utf8mb4',
        collate: 'utf8mb4_general_ci' // 한글 저장
    });
    Comment.associate = (db) => {
        db.Comment.belongsTo(db.User);
        db.Comment.belongsTo(db.Post);
    };
    return Comment;
}

sagas/user.js

import { all, put, fork, takeLatest, delay, call } from 'redux-saga/effects';
import axios from 'axios';

import { 
    LOG_IN_REQUEST, 
    LOG_IN_SUCCESS, 
    LOG_IN_FAILURE,

    LOG_OUT_REQUEST,
    LOG_OUT_SUCCESS, 
    LOG_OUT_FAILURE, 

    SIGN_UP_REQUEST, 
    SIGN_UP_SUCCESS, 
    SIGN_UP_FAILURE,

    FOLLOW_REQUEST,
    FOLLOW_SUCCESS,
    FOLLOW_FAILURE,

    UNFOLLOW_REQUEST,
    UNFOLLOW_SUCCESS,
    UNFOLLOW_FAILURE,

} from '../reducers/user';


function followAPI(data) {
    return axios.post('/api/follow', data);
}

function* follow(action) {
    try {
        // yield put({
        //     type: 'FOLLOW_REQUEST',
        // });

        // const result = yield call(followAPI, action.data);
        // ex> const result = yield call(logInAPI, action.data, a, b, c); a, b, c 인자를 추가시
        yield delay(1000);
        // throw new Error('')  // 아래 catch 로 넘어간다.
        yield put({
            type: FOLLOW_SUCCESS,
            data: action.data,
        });
    } catch (err) {
        yield put({         // put => dispatch 다.
            type: FOLLOW_FAILURE,
            data: err.response.data,
        });
    }
}


function unfollowAPI(data) {
    return axios.post('/api/unfollow', data);
}

function* unfollow(action) {
    try {
        // yield put({
        //     type: 'UNFOLLOW_REQUEST',
        // });

        // const result = yield call(unfollowAPI, action.data);
        // ex> const result = yield call(logInAPI, action.data, a, b, c); a, b, c 인자를 추가시
        yield delay(1000);
        // throw new Error('')  // 아래 catch 로 넘어간다.
        yield put({
            type: UNFOLLOW_SUCCESS,
            data: action.data,
        });
    } catch (err) {
        yield put({         // put => dispatch 다.
            type: UNFOLLOW_FAILURE,
            data: err.response.data,
        });
    }
}



function signUpAPI(data) {
    return axios.post( '/user', data); // localhostUrl ( http://localhost:3065 ) + /user
}

function* signUp(action) {
    try {
        // yield put({
        //     type: 'LOG_IN_REQUEST',
        // });

        const result = yield call(signUpAPI, action.data);
        // ex> const result = yield call(logInAPI, action.data, a, b, c); a, b, c 인자를 추가시
        // yield delay(1000);
        // throw new Error('')  // 아래 catch 로 넘어간다.
        console.log(result);
        yield put({
            type: SIGN_UP_SUCCESS,
            // data: result.data,
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: SIGN_UP_FAILURE,
            data: err.response.data,
        });
    }
}



function logOutAPI() {
    return axios.post('/api/logout');
}

function* logOut() {
    try {
        // yield put({
        //     type: 'LOG_OUT_REQUEST',
        // });

        // const result = yield call(logOutAPI);
        yield delay(1000);
        yield put({
            type: LOG_OUT_SUCCESS,
        });
    } catch (err) {
        yield put({         // put => dispatch 다.
            type: LOG_OUT_FAILURE,
            error: err.response.data,
        });
    }
}

function logInAPI(data) {
    return axios.post('/user/login', data);
}

function* logIn(action) {
    try {
        console.log('saga logIn');
        // yield put({
        //     type: 'LOG_IN_REQUEST',
        // });

        const result = yield call(logInAPI, action.data);
        console.log(result);
        // ex> const result = yield call(logInAPI, action.data, a, b, c); a, b, c 인자를 추가시
        // yield delay(1000);
        yield put({
            type: LOG_IN_SUCCESS,
            data: result.data,
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: LOG_IN_FAILURE,
            error: err.response.data,
        });
    }
}


function* watchFollow() {
    yield takeLatest(FOLLOW_REQUEST, follow);      // take =>logIn 액션이 실행될때까지 기다리겠다.
}
function* watchUnFollow() {
    yield takeLatest(UNFOLLOW_REQUEST, unfollow);      // take =>logIn 액션이 실행될때까지 기다리겠다.
}

function* watchLogIn() {
    yield takeLatest(LOG_IN_REQUEST, logIn);      // take =>logIn 액션이 실행될때까지 기다리겠다.
}

function* watchLogOut() {
    yield takeLatest(LOG_OUT_REQUEST, logOut);
}

function* watchSignUp() {
    yield takeLatest(SIGN_UP_REQUEST, signUp);      // take =>logIn 액션이 실행될때까지 기다리겠다.
}


export default function* userSaga() {
    yield all([
        fork(watchFollow),
        fork(watchUnFollow),
        fork(watchLogIn),
        fork(watchLogOut),
        fork(watchSignUp),
    ]);
}

sagas/index.js

// all   fork   call    put  delay   debounce   throttle   takeLatest   tabkeEvery   takeLeding    taekMaybe
import { all, fork } from 'redux-saga/effects';
import axios from 'axios';

import postSaga from './post';
import userSaga from './user';
import { localhostUrl } from '../config/config';

axios.defaults.baseURL = localhostUrl;


export default function* rootSaga() {
    yield all([             // all 은 배열을 받아서 배열안에 있는 명령들을 한번에 실행 시켜준다.
        fork(postSaga),
        fork(userSaga),
    ]);
};

front/config/config.js

export const domainUrl = 'http://api.nodebird.com';
export const localhostUrl = 'http://localhost:3065';

답변 2

1

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

const fullUserWithoutPassword 여기서 medel 오타입니다.

이게 아니라면 models/index.js에 오타가 있는 것으로 판단됩니다.

0

castinglife님의 프로필 이미지
castinglife
질문자

ㅠ.ㅜ 그러네요.. 감사합니다.

castinglife님의 프로필 이미지
castinglife

작성한 질문수

질문하기