React

 

 

백엔드 노드 서버 구축하기

 

버전이 다르기 때문에 소스가 강좌와 다를 수 있다.

버전

next:  13.0.4

antd:  5.0.1

 

소스 : https://github.dev/braverokmc79/node-bird-sns

 

 

 

 

 

 

46. 회원가입 구현하기

 

강의 :

https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC/unit/48833?tab=curriculum

 

 

백엔드

app.js

~
const express = require('express');
const postRouter = require('./routes/posts');
const userRouter = require('./routes/user');
const db = require('./models');
const app = express();

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

app.use(express.json());
app.use(express.urlencoded({ extended: true }));



~



app.use('/post', postRouter);
app.use('/user', userRouter);

 

 

비밀번호  암호화 위해

$ npm i bcrypt

 

routes/user.js

const express = require('express');
const { User } = require('../models');
const bcrypt = require('bcrypt');

const router = express.Router();


router.get('/', (req, res) => {
    // User.create
    console.log(" get 백엔드 : ", req.body);
    res.json({ success: true })
});


router.post('/', async (req, res, next) => {

    try {
        console.log(" 백엔드 : ", req.body);

        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);
        await User.create({
            email: req.body.email,
            nickname: req.body.nickname,
            password: hashedPassword
        });

        res.status(201).send('ok');
    } catch (error) {
        console.error(" 회원 가입 error : ", error);
        next(error); //status 500
    }
});


module.exports = router;

 

 

 

 

 

 

 

47. CORS 문제 해결하기

강의 :

https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC/unit/48834?tab=curriculum

 

백엔드 서버에 cors 설치 

npm i cors

 

app.js

const express = require('express');
const postRouter = require('./routes/posts');
const userRouter = require('./routes/user');
const cors = require('cors');
const db = require('./models');
const app = express();


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

app.use(cors({
    //origin: 'https://nodebird.com'
    origin: true, // orign: true 로 설정해두면 * 대신 보낸 곳의 주소가 자동으로 들어가 편리합니다.
    credentials: false
}));

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

~

 

 

CORS?

CORS(Cross Origin Resource Sharing) 는 추가적인 HTTP 헤더를 사용해서
한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 자원에 접근할 수 있는 권한을 관리하는 체제입니다.
 

 

 

즉 위와 같이 domain-a.com 에서 같은 주소의 domain-a.com 에 접근하여 자원을 요청할때는 문제가 발생하지 않지만,
domain-b.com 에 요청할 경우 도메인이 다르기 때문에 보안상의 이유로 제한됩니다.
 

 

 

만약 서버에서 올바른 CORS 헤더를 포함한 응답을 반환하지 않으면 위와 같은 오류가 발생하게 됩니다.

위 예시에서는 자원을 가지고 있는 서버단의 도메인인 http://127.0.0.1:3000 과 자원을 요청한 http://localhost:8080 의 출처가 다르기 때문에 발생한 오류입니다.

Node.js에서 CORS 설정

CORS 패키지 설치

우선 CORS 미들웨어를 사용하기 위해 패키지를 설치합니다.

$npm i cors

모든 CORS Request 허용하기

모든 요청을 허용할 경우 다음과 같이 간단하게 미들웨어를 불러와서 추가해주는 것으로 해결할 수 있습니다.

 

app.js

const express = require("express");
const cors = require("cors");
const app = express();

app.use(cors()); // CORS 미들웨어 등록

// ...

 

Whitelist로 특정 주소만 허용하기

모든 CORS 요청을 허용해주는 것이 때로는 보안상의 문제로 이어질 수 있습니다.

만약 특정 호스트만 CORS 요청을 허용해주고 싶다면 다음과 같이 whitelist 를 추가해서 검증하는 방법이 있습니다.
요청을 허용할 주소를 담은 whitelist 를 생성해주고 요청 HTTP 헤더의 Origin 속성을 확인해서
일치하는 항목이 있을 경우 허용해주는 방식입니다.

app.js

const express = require("express");
const cors = require("cors");
const app = express();

const whitelist = ["http://localhost:8080"];
const corsOptions = {
  origin: function (origin, callback) {
    if (whitelist.indexOf(origin) !== -1) {
      callback(null, true);
    } else {
      callback(new Error("Not Allowed Origin!"));
    }
  },
};

app.use(cors(corsOptions)); // 옵션을 추가한 CORS 미들웨어 추가

// ...

참고 자료

 

 

 

 

 

 

 

 

48. 패스포트로 로그인하기.

강의 :

https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC/unit/48835?tab=curriculum

 

 

라이브러리 설치

$ npm i passport passport-local

$ npm i express-session

 

 

 

app.js

const express = require('express');
const postRouter = require('./routes/posts');
const userRouter = require('./routes/user');
const cors = require('cors');
const db = require('./models');
const passportConfig = require('./passport');

const app = express();


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


passportConfig();


~

 

passport/index.js

const passport = require('passport');

const local = require('./local');

module.exports = () => {

    passport.serializeUser(() => {

    });

    passport.deserializeUser(() => {

    });


    local();

}

 

 

passport/local.js

const passport = require('passport');
const { Strategy: LocalStrategey } = require('passport-local');
const bcrypt = require('bcrypt');
const { User } = require('../models');


module.exports = () => {
    passport.use(new LocalStrategey({
        usernameField: 'email',
        passwordField: 'password',
    }, async (email, password, done) => {

        try {

            const user = await User.findOne({
                where: { email }
            });

            if (!user) {
                return done(null, false, { reason: '존재하지 않는 사용자입니다.' })
            }

            const result = await bcrypt.compare(password, user.password);
            if (!result) {
                return done(null, false, { reason: '비밀번호가 틀렸습니다.' });
            }

            return done(null, user);

        } catch (error) {
            console.error(" 로그인 에러 : ", error);
            return done(error);
        }

    }));

}

 

 

routes/user.js

const express = require('express');
const { User } = require('../models');
const bcrypt = require('bcrypt');
const passport = require('passport');
const router = express.Router();

//passport.authenticate 미들웨어는 (req, res, next) 사용할수 없는 미들웨어인데 다음과 미들웨어 확장 같은 설정으로 사용
//POST  /user/login
router.post('/login', (req, res, next) => {

    console.log("로그인  ", req.body);

    passport.authenticate('local', (err, user, info) => {
        if (err) {
            console.error(err);
            console.error("1.로그인 에러 ", err);
            next(err);
        }
        //현재 info 가 존재하면 클라이언트 에러
        //info  예  => { reason: '존재하지 않는 사용자입니다.' } { reason: '비밀번호가 틀렸습니다.' }
        if (info) {
            //401 허가되지 않음 403 금지
            return res.status(401).send(info.reason);
        }

        return req.login(user, async (loginErr) => {
            if (loginErr) {
                console.error("2.로그인 에러 ", loginErr);
                return next(loginErr);
            }

            return res.status(200).json(user)
        });

    })(req, res, next);

});


~

 

 

 

 

 

 

 

 

 

 

 

49. 쿠키/세션과 전체 로그인 흐름.

 

강의 :

https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC/unit/48836?tab=curriculum

 

 

 

 

 


라이브러리 추가

$ npm i cookie-parser

$ npm i dotenv

 

app.js 다음 4가지 추가

app.use(cookieParser);
app.use(session());
app.use(passport.initialize());
app.use(passport.session());

 

 

app.js  =>  routes/user.js  => passport/index.js  에서 local 함수 실행 = > passport/local.js

 

app.js  => 

const express = require('express');
const postRouter = require('./routes/posts');
const userRouter = require('./routes/user');
const cors = require('cors');
const db = require('./models');
const passportConfig = require('./passport');
const passport = require('passport');
const session = require("express-session");
const cookieParser = require('cookie-parser');
const dotenv = require('dotenv');


dotenv.config();
const app = express();


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

app.use(cors({
    //origin: 'https://nodebird.com'
    origin: true, // orign: true 로 설정해두면 * 대신 보낸 곳의 주소가 자동으로 들어가 편리합니다.
    credentials: false
}));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.use(cookieParser());
app.use(session({
    secret: process.env.COOKIE_SECRET,
    resave: false,
    saveUninitialized: false
}));
app.use(passport.initialize()); //패스포트 초기화
app.use(passport.session());


passportConfig();






app.get('/', (req, res) => {
    res.send('hello express');
});

app.get('/api', (req, res) => {
    res.send('hello api');
});

app.get('/api/posts', (req, res) => {
    res.json([
        { id: 1, content: 'hello1' },
        { id: 2, content: 'hello2' },
        { id: 3, content: 'hello3' }
    ])
});

app.use('/post', postRouter);
app.use('/user', userRouter);



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


 

 

 

 

routes/user.js

const express = require('express');
const { User } = require('../models');
const bcrypt = require('bcrypt');
const passport = require('passport');
const router = express.Router();

//passport.authenticate 미들웨어는 (req, res, next) 사용할수 없는 미들웨어인데 다음과 미들웨어 확장 같은 설정으로 사용
//POST  /user/login
router.post('/login', (req, res, next) => {

    console.log("로그인  ", req.body);

    passport.authenticate('local', (err, user, info) => {

        if (err) {
            console.error(err);
            console.error("1.로그인 에러 ", err);
            next(err);
        }
        //현재 info 가 존재하면 클라이언트 에러
        //info  예  => { reason: '존재하지 않는 사용자입니다.' } { reason: '비밀번호가 틀렸습니다.' }
        if (info) {
            //401 허가되지 않음 403 금지
            return res.status(401).send(info.reason);
        }


        console.log("3 passport .req.login  실행  : ", user);

        return req.login(user, async (loginErr) => {
            console.log("로그인 처리 ");

            if (loginErr) {
                console.error("2.로그인 에러 ", loginErr);
                return next(loginErr);
            }

            console.log("로그인 성공 :  ", user);
            return res.status(200).json(user)
        });

    })(req, res, next);

});



//로그아웃
router.post('/logout', (req, res, next) => {
    req.logout(function (err) {
        if (err) { return next(err); }
        req.session.destroy();
        res.send('ok')
    });

});





~

 

passport/index.js

const passport = require('passport');
const local = require('./local');
const { User } = require('../models');


module.exports = () => {

    passport.serializeUser((user, done) => {
        //메모리에 많은 양의 데이터를 저장하면  메모리 낭비가 심하므로 user id 만 저장 한다.
        done(null, user.id);
    });


    //user id 를 통해 user 정보를 가져온다.
    passport.deserializeUser(async (id, done) => {
        try {
            const user = await User.findOne({ where: { id } })
            done(null, user); //req.user 저장
        } catch (error) {
            console.error(" passport.deserializeUser error : ", error);
            done(error);
        }
    });


    local();

}

 

 

 

passport/local.js

const passport = require('passport');
const { Strategy: LocalStrategey } = require('passport-local');
const bcrypt = require('bcrypt');
const { User } = require('../models');


module.exports = () => {
    console.log(" passport local.js module.exports ");
    passport.use(new LocalStrategey({
        usernameField: 'email',
        passwordField: 'password',
    }, async (email, password, done) => {

        try {

            const user = await User.findOne({
                where: { email }
            });

            if (!user) {
                return done(null, false, { reason: '존재하지 않는 사용자입니다.' })
            }

            const result = await bcrypt.compare(password, user.password);


            if (!result) {
                return done(null, false, { reason: '비밀번호가 틀렸습니다.' });
            }

            return done(null, user);

        } catch (error) {
            console.error(" 로그인 에러 : ", error);
            return done(error);
        }

    }));

}

 

 

 

dotenv  설정

.env

COOKIE_SECRET=1123@2312dajfj23rj2po4$#%@#
DB_PASSWORD=1234

 

config/config.js

const dotenv = require('dotenv');
dotenv.config();

module.exports = {
  "development": {
    "username": "react-nodebird",
    "password": process.env.DB_PASSWORD,
    "database": "react_nodebird",
    "host": "127.0.0.1",
    "post": "3306",
    "dialect": "mysql"
  },
  "test": {
    "username": "react-nodebird",
    "password": process.env.DB_PASSWORD,
    "database": "react_nodebird",
    "host": "127.0.0.1",
    "post": "3306",
    "dialect": "mysql"
  },
  "production": {
    "username": "react-nodebird",
    "password": process.env.DB_PASSWORD,
    "database": "react_nodebird",
    "host": "127.0.0.1",
    "post": "3306",
    "dialect": "mysql"
  }
}

 

 

 

 

 

 

 

50. 로그인 문제 해결하기

강의 :

https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC/unit/48837?tab=curriculum

 

로그인시 유저정보 뿐만 아니라  Post 및  Followers 및 Followings 데이터를 가져오는 것때문에 오류

 

routes/user.js

const express = require('express');
const { User, Post } = require('../models');
const bcrypt = require('bcrypt');
const passport = require('passport');
const router = express.Router();

//passport.authenticate 미들웨어는 (req, res, next) 사용할수 없는 미들웨어인데 다음과 미들웨어 확장 같은 설정으로 사용
//POST  /user/login
router.post('/login', (req, res, next) => {

    passport.authenticate('local', (err, user, info) => {
        if (err) {
            console.error(err);
            console.error("1.로그인 에러 ", err);
            next(err);
        }

        //현재 info 가 존재하면 클라이언트 에러 메시지 반환처리
        //info  예  => { reason: '존재하지 않는 사용자입니다.' } { reason: '비밀번호가 틀렸습니다.' }
        if (info) {
            //401 허가되지 않음 403 금지
            return res.status(401).send(info.reason);
        }

        return req.login(user, async (loginErr) => {

            if (loginErr) {
                return next(loginErr);
            }

            const fullUserWithoutPassword = await User.findOne({
                where: {
                    id: user.id
                },
                //원하는 정보만 attributes: ['id', 'nickname', 'email'],
                attributes: {
                    exclude: ['password']
                },
                include: [{
                    model: Post
                }, {
                    model: User,
                    as: "Followers"
                },
                {
                    model: User,
                    as: "Followings"
                }
                ]
            })

            return res.status(200).json(fullUserWithoutPassword)
        });

    })(req, res, next);

});



//로그아웃
router.post('/logout', (req, res, next) => {
    req.logout(function (err) {
        if (err) { return next(err); }
        req.session.destroy();
        res.send('ok')
    });

});




router.post('/', async (req, res, next) => {

    try {
        console.log(" 백엔드 : ", req.body);

        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);
        await User.create({
            email: req.body.email,
            nickname: req.body.nickname,
            password: hashedPassword
        });

        //res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3060'); //특정서버
        // res.setHeader('Access-Control-Allow-Origin', '*'); //모든서버
        res.status(201).send('ok');

    } catch (error) {
        console.error(" 회원 가입 error : ", error);
        next(error); //status 500
    }
});


module.exports = router;

 

 

 

 

 

프론트 엔드 nextjs 에서 다음과 같이 처리

 

components/LoginForm.js

import React, { useCallback , useEffect} from 'react';
import { Form, Input, Button } from 'antd';
import styled from 'styled-components';
import Link from 'next/link';
import useInput from '../hooks/useInput';
import { useDispatch, useSelector } from 'react-redux';
import { loginRequestAction } from './../reducers/user';


const ButtonWrapper = styled.div`
    margin-top:10px
`;

const FormWrapper = styled(Form)`
    margin-top: 5px;
    padding:10px;
`;


const LoginForm = () => {
    const dispatch = useDispatch();
    const { logInLoading, logInError } = useSelector((state) => state.user);
    const [email, onChangeEmail] = useInput('');
    const [password, onChangePassword] = useInput('');


    useEffect(() => {
        if(logInError){
            alert(logInError);
        }
    }, [logInError]);



~

 

 

뒤로가기 못하게 Router.replace('/');

   Router.push("/");

=>

   Router.replace("/");

 

pages/signup.js

~

const SignUp = () => {
    const dispatch = useDispatch();
    const { signUpLoading, signUpDone, signUpError, me } = useSelector((state) => state.user);
    const { schema } = useSimpleValidation();
    const [joinState, setJoinState] = useState(true);


    useEffect(() => {
        if (me && me.id) {
            Router.replace("/");
        }
    }, [me && me.id]);



~

 

 

 

 

 

 

 

 

 

51. 미들웨어로 라우터 검사하기

 

강의 :

https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC/unit/48838?tab=curriculum

 

 

 

로그인 했는지 안했는지 검사하는 미들웨어 만들기

 

routes/middlewares.js

exports.isLoggedIn = (req, res, next) => {
    // 패스포트에서 isAuthenticated 를 제공한다
    if (req.isAuthenticated()) {
        next();
    } else {
        res.status(401).send('로그인이 필요합니다.');
    }
}


exports.isNotLoggedIn = (req, res, next) => {
    if (!req.isAuthenticated()) {
        next();
    } else {
        res.status(401).send('로그인하지 않은 사용자만 접근 가능합니다.');
    }

}

 

로그인 유무 처리 미들웨어 적용

router.post('/login', isNotLoggedIn, (req, res, next) => {



~
//로그아웃
router.post('/logout', isLoggedIn, (req, res, next) => {
~

routes/user.js

const express = require('express');
const { User, Post } = require('../models');
const bcrypt = require('bcrypt');
const passport = require('passport');
const router = express.Router();
const { isLoggedIn, isNotLoggedIn } = require('./middlewares');



//passport.authenticate 미들웨어는 (req, res, next) 사용할수 없는 미들웨어인데 다음과 미들웨어 확장 같은 설정으로 사용
//POST  /user/login
router.post('/login', isNotLoggedIn, (req, res, next) => {

    passport.authenticate('local', (err, user, info) => {
        if (err) {
            console.error(err);
            console.error("1.로그인 에러 ", err);
            next(err);
        }

        //현재 info 가 존재하면 클라이언트 에러 메시지 반환처리
        //info  예  => { reason: '존재하지 않는 사용자입니다.' } { reason: '비밀번호가 틀렸습니다.' }
        if (info) {
            //401 허가되지 않음 403 금지
            return res.status(401).send(info.reason);
        }

        return req.login(user, async (loginErr) => {

            if (loginErr) {
                return next(loginErr);
            }

            const fullUserWithoutPassword = await User.findOne({
                where: {
                    id: user.id
                },
                //원하는 정보만 attributes: ['id', 'nickname', 'email'],
                attributes: {
                    exclude: ['password']
                },
                include: [{
                    model: Post
                }, {
                    model: User,
                    as: "Followers"
                },
                {
                    model: User,
                    as: "Followings"
                }
                ]
            })

            return res.status(200).json(fullUserWithoutPassword)
        });

    })(req, res, next);

});



//로그아웃
router.post('/logout', isLoggedIn, (req, res, next) => {
    req.logout(function (err) {
        if (err) { return next(err); }
        req.session.destroy();
        res.send('ok')
    });

});




router.post('/', isNotLoggedIn, async (req, res, next) => {

    try {
        console.log(" 백엔드 : ", req.body);

        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);
        await User.create({
            email: req.body.email,
            nickname: req.body.nickname,
            password: hashedPassword
        });

        //res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3060'); //특정서버
        // res.setHeader('Access-Control-Allow-Origin', '*'); //모든서버
        res.status(201).send('ok');

    } catch (error) {
        console.error(" 회원 가입 error : ", error);
        next(error); //status 500
    }
});


module.exports = router;

 

 

 

 

 

 

 

 

 

52. 게시글, 댓글 작성하기

강의 :

https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC/unit/48839?tab=curriculum

 

백엔드 

routes/posts.js

const express = require('express');
const { Post } = require('../models');
const { isLoggedIn, isNotLoggedIn } = require('./middlewares');
const router = express.Router();



//** passport 특성상 로그인 하면, 라우터 접근시 항상 deserializeUser 실행해서 req.user 를 만든다.  req.user.id로 접근해서 정보를 가져올 수 있다.
//POST  /post
router.post('/', isLoggedIn, async (req, res, next) => {
    try {
        const post = await Post.create({
            content: req.body.content,
            UserId: req.user.id
        });
        res.status(201).json(post);
    } catch (error) {
        console.error(" Post 에러 :  ", error);
        next(error);
    }
});



//POST 댓글  /post
router.post('/:postId/comment', isLoggedIn, async (req, res, next) => {     //POST /post/1/comment
    try {

        const post = await Post.findOne({
            where: { id: req.params.postId }
        });

        if (!post) {
            return res.status(403).send("존재하지 않는 게시글입니다.");
        }

        const comment = await Post.create({
            content: req.body.content,
            PostId: req.params.postId,
            UserId: req.user.id
        });

        res.status(201).json(comment);
    } catch (error) {
        console.error(" comment 에러 :  ", error);
        next(error);
    }
});




//DELETE  /post
router.delete('/', (req, res) => {
    res.json({ id: 1 })
});



module.exports = router;

 

 

 

 

프론트엔드

front/reducers/post.js

~

        //글작성
        case ADD_POST_REQUEST:
            draft.addPostLoading = true;
            draft.addPostDone = false;
            draft.addPostError = null;
            break;

        case ADD_POST_SUCCESS:
            draft.addPostLoading = false;
            draft.addPostDone = true;
            draft.mainPosts.unshift(action.data);
            break;

        case ADD_POST_FAILURE:
            draft.addPostLoading = false;
            draft.addPostError = action.error;
            break;


~

 

 

front/saga/post.js

~
//3-1. 글작성
function addPostAPI(data) {
    return axios.post('/post', { content: data });
}

//3-2.
function* addPost(action) {
    try {
        const result = yield call(addPostAPI, action.data);

        console.log("2. 게시글 등록 후 반환 : ", result);

        yield put({
            type: ADD_POST_SUCCESS,
            data: result.data
        });

        yield put({
            type: ADD_POST_TO_ME,
            data: result.data.id
        })



    } catch (err) {
        yield put({
            type: ADD_POST_FAILURE,
            error: err.response.data
        });
    }
}







//댓글
function addCommentAPI(data) {
    return axios.Comment(`/post/${data.postId}/comment`, data); //POST post/1/comment
}

function* addComment(action) {
    try {
        const result = yield call(addCommentAPI, action.data);

        yield put({
            type: ADD_COMMENT_SUCCESS,
            data: result.data
        });

    } catch (err) {
        yield put({
            type: ADD_COMMENT_FAILURE,
            error: err.response.data
        });
    }
}

function* watchAddComment() {
    yield takeLatest(ADD_COMMENT_REQUEST, addComment);
}







 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

53. credentials로 쿠키 공유하기

강의 :

https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC/unit/48840?tab=curriculum

 

도메인  URL이 다르면 쿠키전달도 안된다.

 

cros 처리는 proxy 를 이용한 방법 

백엔드에서 다음과 같이 credentials 에서 true 처리를 하여 쿠키 공유

app.js

app.use(cors({
    //origin: 'https://nodebird.com'
    origin: 'http://localhost:3060', // orign: true 로 설정해두면 * 대신 보낸 곳의 주소가 자동으로 들어가 편리합니다.
    credentials: true
}));

 

origin : true 하면 로그인 오류로 안된다.

 

 

 

 

 

프론트엔드에서 다음과 같이 

axios.defaults.withCredentials = true;

 

saga/index.js
 

import { all, fork } from 'redux-saga/effects';
import axios from 'axios';
import postSaga from './post';
import userSaga from './user';

axios.defaults.baseURL = 'http://localhost:3065';
axios.defaults.withCredentials = true;


export default function* rootSaga() {
    yield all([
        fork(postSaga),
        fork(userSaga),
    ]);
}

 

 

 

 

백엔드

routes/posts.js

const express = require('express');
const { Post, User, Image, Comment } = require('../models');
const { isLoggedIn, isNotLoggedIn } = require('./middlewares');
const router = express.Router();


//** passport 특성상 로그인 하면, 라우터 접근시 항상 deserializeUser 실행해서 req.user 를 만든다.  req.user.id로 접근해서 정보를 가져올 수 있다.
//POST  /post
router.post('/', isLoggedIn, async (req, res, next) => {
    try {
        const post = await Post.create({
            content: req.body.content,
            UserId: req.user.id
        });

        const fullPost = await Post.findOne({
            where: { id: post.id },
            include: [{
                model: Image,
            },
            {
                model: Comment
            }, {
                model: User,
            }
            ]
        })


        res.status(201).json(fullPost);
    } catch (error) {
        console.error(" Post 에러 :  ", error);
        next(error);
    }
});


~

 

 

 

 

 

 

 

 

 

54. 내 로그인 정보 매번 불러오기

 강의:

https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC/unit/48841?tab=curriculum

 

 

백엔드

routes/user.js

const express = require('express');
const { User, Post } = require('../models');
const bcrypt = require('bcrypt');
const passport = require('passport');
const router = express.Router();
const { isLoggedIn, isNotLoggedIn } = require('./middlewares');


//브라우저에서 새로고침 할때마다 요청처리 된다.
router.get('/', async (req, res, next) => {
    try {
        if (req.user) {
            const fullUserWithoutPassword = await User.findOne({
                where: {
                    id: req.user.id
                },
                attributes: {
                    exclude: ['password']
                },
                include: [{
                    model: Post,
                    attributes: ['id'],
                }, {
                    model: User,
                    as: "Followers",
                    attributes: ['id'],
                },
                {
                    model: User,
                    as: "Followings",
                    attributes: ['id'],
                }
                ]
            })
            res.status(200).json(fullUserWithoutPassword);
        } else {
            res.status(200).json(null);
        }
    } catch (error) {
        console.error("/ 쿠키 정보 가져오기 에러 :  ", error);
        next(error);
    }
});


~

 

 

 

 

프론트엔드

 

pages/index.js

import React, { useEffect, useCallback } from 'react';
import AppLayout from './../components/AppLayout';
import { useSelector, useDispatch } from 'react-redux';
import PostCard from './../components/PostCard';
import PostForm from './../components/PostForm';
import { LOAD_MY_INFO_REQUEST } from './../reducers/user';
import { LOAD_POSTS_REQUEST } from './../reducers/post';

const Index = () => {
    const dispatch = useDispatch();
    const { me } = useSelector((state) => state.user);
    const { mainPosts, hasMorePosts, loadPostsLoading } = useSelector((state) => state.post);

    useEffect(() => {
        dispatch({
            type: LOAD_MY_INFO_REQUEST
        });

        dispatch({
            type: LOAD_POSTS_REQUEST
        });
    }, []);


~

 

 

 

reducers/user.js

import produce from "immer";

export const initialState = {


~


    loadMyInfoLoading: false, //브라우저 새로고침시  유저정보 가져오기
    loadMyInfoDone: false,
    loadMyInfoError: null,

    me: null,
    signUpdata: {},
    loginData: {}
}




export const LOAD_MY_INFO_REQUEST = "LOAD_MY_INFO_REQUEST";
export const LOAD_MY_INFO_SUCCESS = "LOAD_MY_INFO_SUCCESS";
export const LOAD_MY_INFO_FAILURE = "LOAD_MY_INFO_FAILURE";




~
const reducer = (state = initialState, action) => produce(state, (draft) => {

    switch (action.type) {

        //브라우저 새로고침시  유저정보 가져오기
        case LOAD_MY_INFO_REQUEST:
            draft.loadMyInfoLoading = true;
            draft.loadMyInfoDone = false;
            draft.loadMyInfoError = null;
            break;

        case LOAD_MY_INFO_SUCCESS:
            draft.loadMyInfoLoading = false;
            draft.loadMyInfoDone = true;
            draft.me=action.data
            break;

        case LOAD_MY_INFO_FAILURE:
            draft.loadMyInfoLoading = false;
            draft.loadMyInfoError = action.error;
            break;


~









 

 

saga/user.js

import { all, fork, put, takeLatest, delay, call, throttle } 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,
    LOAD_MY_INFO_REQUEST, LOAD_MY_INFO_SUCCESS, LOAD_MY_INFO_FAILURE
} from '../reducers/user';



//브라우저 새로고침시  유저정보 가져오기
function loadMyInfoAPI() {
    return axios.get('/user');
}
function* loadMyInfo(action) {
    try {
        const result = yield call(loadMyInfoAPI)

        yield put({
            type: LOAD_MY_INFO_SUCCESS,
            data: result.data
        });

    } catch (err) {
        yield put({
            type: LOAD_MY_INFO_FAILURE,
            error: err.response.data
        });
    }
}
function* watchLoadMyInfo() {
    yield takeLatest(LOAD_MY_INFO_REQUEST, loadMyInfo);
}



~

//all 하면 한방에 배열로 적은 함수들이 실행처리 된다.
//fork , call 로 실행한다. all 은 fork 나 call 을 동시에 실행시키도록 한다.
//call 은 동기 함수 호출
//fork 는 비동기 함수 호출
export default function* userSaga() {
    yield all([
        fork(watchLogIn),
        fork(watchLogOut),
        fork(watchFollow),
        fork(watchUnFollow),
        fork(watchSignUp),
        fork(watchLoadMyInfo),
    ])
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

react nodjs

 

about author

PHRASE

Level 60  라이트

슬픔은 버릴 것이 아니다. 우리가 살아있는 한, 이것은 빛나는 기쁨과 같을 정도로 강력한 생활의 일부이다. 슬픔이 없다면 우리들의 품성은 지극히 미숙한 단계에 머물고 말 것이다. -로댕

댓글 ( 4)

댓글 남기기

작성