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),
    ])
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

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

댓글 ( 4)

댓글 남기기

작성