본문 바로가기

웹_프론트_백엔드/프론트엔드

2021.03.14

1. serializeUser

 : 사용자 정보 객체를 세션에 아이디(객체)로 저장

 

   passport.serializeUser((user, done) => {
       //done(null, user);     // id만 세션에 저장
       done(null, user;        // user 객체 세션에 저장
   });

 

 

2. deserializeUser

 : 세션에 저장한 아이디(객체)를 통해서 사용자 정보 객체를 불러옴

 

   passport.deserializeUser((user, done) => {

       console.log('deserializeUser() 호출!', user);

       done(null, user);
   });

 

 

3. passport.session() 미들웨어가 메소드를 호출

   조회한 정보를 req.user 저장

 

 

4. [저번 수업시간에 마무리 못한 부분 완료] passport 모듈을 이용한 로컬 회원가입, 로그인

** /config/passport.js

const local_signup = require('./passport/local_signup');
const local_login = require('./passport/local_login');

module.exports = function(app, passport) {
    console.log('config/passport 호출!');

    passport.serializeUser((user, done) => {
        console.log('serializeUser() 호출!', user);
        done(null, user);
    });

    passport.deserializeUser((user, done) => {
        console.log('deserializeUser() 호출!', user);
        done(null, user);
    });

    passport.use('local-signup', local_signup);
    passport.use('local-login', local_login);
}

 

** passport.js

const express = require('express');
const bodyParser = require('body-parser');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const expressSession = require('express-session');

const passport = require('passport');                           // npm i passport

const static = require('serve-static');
const path = require('path');
const expressErrorHandler = require('express-error-handler');   // npm i express-error-handler
const app = express();
const router = express.Router();

app.use(cookieParser());
app.use(expressSession({
    secret: '!@#$%^&^*&(',
    resave: false,
    saveUninitialized: true,
    cookie: { maxAge: 60 * 60 * 1000 }
}));
app.use(logger('dev'));
// passport의 세션을 사용하려면 그 전에 express의 세션을 사용하는 코드가 먼저 나와야 한다.
app.use(passport.initialize());        // passport 초기화
app.use(passport.session());
app.use(bodyParser.urlencoded({extended: false}));
app.use('/public', static(path.join(__dirname, "public")));

app.use('/', router);
const errerHandler = expressErrorHandler({
    static: {
        '404': './public/404.html'
    }
});

app.use(expressErrorHandler.httpError(404));
app.use(errerHandler);

app.set('views', __dirname + "/views");
app.set('view engine', 'ejs');

const config = require('./config/config');
const database = require('./database/database');

const configPassport = require('./config/passport');
configPassport(app, passport);

const userPassport = require('./routes/route_member');
userPassport(router, passport);

app.listen(config.server_port, () => {
    console.log(`${config.server_port} 포트로 서버 실행 중...`);
    database.init(app, config);
});

 

** /routes/route_member.js

module.exports = function(router, passport) {
    console.log('route_member 호출!');

    router.route('/').get((req, res) => {
        res.render('index.ejs');
    });

    router.route('/login').get((req, res) => {
        res.render('login.ejs');
    });

    router.route('/signup').get((req, res) => {
        res.render('signup.ejs');
    });

    router.route('/signup').post(passport.authenticate('local-signup', {
        successRedirect: '/profile',
        failureRedirect: '/signup',
        failureFlash: true
    }));

    router.route('/login').post(passport.authenticate('local-login', {
        successRedirect: '/profile',
        failureRedirect: '/login',
        failureRedirect: true
    }));

    router.route('/profile').get((req, res) => {
        if(!req.user) {
            console.log('사용자 인증이 안된 상태!');
            res.redirect('/');
            return;
        }
        console.log('사용자 인증 상태!')
        if(Array.isArray(req.user)) {
            res.render('profile.ejs', {user: req.user[0]});
        } else {
            res.render('profile.ejs', {user: req.user});
        }
    });
}

 

** /views/index.ejs

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>메인</title>
</head>
<body>
    <h2>메인</h2>
    <p>안녕하세요. passport 테스트 페이지에 오신 것을 환영합니다.</p>
    <p><a href='/login'>로그인</a> | <a href="/signup">회원가입</a></p>
</body>
</html>

 

** /views/login.ejs

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>로그인</title>
</head>
<body>
    <h2>로그인</h2>
    <form action='/login' method="post">
        <p><label>아이디 : <input type="text" name="userid"></label></p>
        <p><label>비밀번호 : <input type="password" name="userpw"></label></p>
        <p><button type="submit">로그인</button></p>
        <p>계정이 없으신가요? <a href="/signup">회원가입</a> | <a href="/">메인으로</a></p>
    </form>
</body>
</html> 

 

** /views/signup.ejs

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>회원가입</title>
</head>
<body>
    <h2>회원가입</h2>
    <form action='/signup' method="post">
        <p><label>아이디 : <input type="text" name="userid"></label></p>
        <p><label>비밀번호 : <input type="password" name="userpw"></label></p>
        <p><label>이름 : <input type="text" name="name"></label></p>
        <p><label>나이 : <input type="text" name="age"></label></p>
        <p><button type="submit">완료</button></p>
        <p>이미 계정이 있으신가요? <a href="/login">로그인</a> | <a href="/">메인으로</a></p>
    </form>
</body>
</html>

 

** /views/profile.ejs

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>프로필</title>
</head>
<body>
    <h2>프로필</h2>
    <hr/>
    <p>아이디 : <%=user.userid%></p>
    <p>이름 : <%=user.name%></p>
    <p>나이 : <%=user.age%></p>
    <p><a href="/logout">로그아웃</a></p>
</body>
</html>

 

 

 

5. passport 모듈을 이용한 페이스북 회원가입, 로그인

** /views/login.ejs

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>로그인</title>
</head>
<body>
    <h2>로그인</h2>
    <form action='/login' method="post">
        <p><label>아이디 : <input type="text" name="userid"></label></p>
        <p><label>비밀번호 : <input type="password" name="userpw"></label></p>
        <p><button type="submit">로그인</button></p>
        <p>계정이 없으신가요? <a href="/signup">회원가입</a> | <a href="/">메인으로</a></p>
    </form>
    <p><a href="/auth/facebook">페이스북 로그인</a></p>
</body>
</html> 

 

** /routes/route_member.js

module.exports = function(router, passport) {
    console.log('route_member 호출!');

    router.route('/').get((req, res) => {
        res.render('index.ejs');
    });

    router.route('/login').get((req, res) => {
        res.render('login.ejs');
    });

    router.route('/signup').get((req, res) => {
        res.render('signup.ejs');
    });

    router.route('/signup').post(passport.authenticate('local-signup', {
        successRedirect: '/profile',
        failureRedirect: '/signup',
        failureFlash: true
    }));

    router.route('/login').post(passport.authenticate('local-login', {
        successRedirect: '/profile',
        failureRedirect: '/login',
        failureRedirect: true
    }));

    router.route('/profile').get((req, res) => {
        if(!req.user) {
            console.log('사용자 인증이 안된 상태!');
            res.redirect('/');
            return;
        }
        console.log('사용자 인증 상태!')
        if(Array.isArray(req.user)) {
            res.render('profile.ejs', {user: req.user[0]});
        } else {
            res.render('profile.ejs', {user: req.user});
        }
    });

    router.route('/auth/facebook').get(passport.authenticate('facebook', {
        scope: ['public_profile', 'email']
    }));

    router.route('/auth/facebook/callback').get(passport.authenticate('facebook', {
        successRedirect: '/profile',
        failureRedirect: '/'
    }));
}

 

** /config/passport.js

const local_signup = require('./passport/local_signup');
const local_login = require('./passport/local_login');
const facebook = require('./passport/facebook');

module.exports = function(app, passport) {
    console.log('config/passport 호출!');

    passport.serializeUser((user, done) => {
        console.log('serializeUser() 호출!', user);
        done(null, user);
    });

    passport.deserializeUser((user, done) => {
        console.log('deserializeUser() 호출!', user);
        done(null, user);
    });

    passport.use('local-signup', local_signup);
    passport.use('local-login', local_login);
    passport.use('facebook', facebook(app, passport));
}

 

** /config/passport/facebook.js

const FacebookStrategy = require('passport-facebook').Strategy;     // npm i passport-facebook
const config = require('../config');

module.exports = function(app, passport) {
    return new FacebookStrategy({
        clientID: config.facebook.clientID,
        clientSecret: config.facebook.clientSecret,
        callbackURL: config.facebook.callbackURL,
        profileFields: ['id', 'displayName', 'name', 'gender', 'picture.type(large)', 'email']},
        (accessToken, refreshToken, profile, done) => {
            console.log('passport의 facebook 호출');
            console.dir(profile);

            let database = app.get('database');
            database.MemberModel.findOne({userid: profile.id}, (err, user) => {
                if(err) return done(err);

                if(!user) {
                    const user = new database.MemberModel({
                        name: profile.displayName,
                        userid: profile.id,
                        provider: 'facebook',
                        authToken: accessToken,
                        facebook: profile._json
                    });

                    user.save((err) => {
                        if(err) throw err;
                        return done(null, user);
                    });
                } else {
                    return done(null, user);
                }
            });
        });
}

 

** /views/profile.ejs

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>프로필</title>
</head>
<body>
    <h2>프로필</h2>
    <hr/>
    <p>아이디 : <%=user.userid%></p>
    <p>이름 : <%=user.name%></p>
<%
    if(user.provider == 'facebook') {
%>
    <p>이메일 : <%=user.facebook.email%></p>
<%
    } else {
%>
    <p>나이 : <%=user.age%></p>
<%
    }
%>
    <p><a href="/logout">로그아웃</a></p>
</body>
</html>

 

 

 

6. 로그아웃

** /routes/route_member.js

module.exports = function(router, passport) {
    console.log('route_member 호출!');

    router.route('/').get((req, res) => {
        res.render('index.ejs');
    });

    router.route('/login').get((req, res) => {
        res.render('login.ejs');
    });

    router.route('/signup').get((req, res) => {
        res.render('signup.ejs');
    });

    router.route('/signup').post(passport.authenticate('local-signup', {
        successRedirect: '/profile',
        failureRedirect: '/signup',
        failureFlash: true
    }));

    router.route('/login').post(passport.authenticate('local-login', {
        successRedirect: '/profile',
        failureRedirect: '/login',
        failureRedirect: true
    }));

    router.route('/profile').get((req, res) => {
        if(!req.user) {
            console.log('사용자 인증이 안된 상태!');
            res.redirect('/');
            return;
        }
        console.log('사용자 인증 상태!')
        if(Array.isArray(req.user)) {
            res.render('profile.ejs', {user: req.user[0]});
        } else {
            res.render('profile.ejs', {user: req.user});
        }
    });

    router.route('/auth/facebook').get(passport.authenticate('facebook', {
        scope: ['public_profile', 'email']
    }));

    router.route('/auth/facebook/callback').get(passport.authenticate('facebook', {
        successRedirect: '/profile',
        failureRedirect: '/'
    }));

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

 

 

7. socket.io 모듈

 : 웹소켓을 이용하면 클라이언트에 실시간으로 데이터를 전송할 수 있다.
   socket을 구현하는 것으로 WebSocket 개발을 쉽게 하기 위한 방법이다.
   클라이언트에서 EventListener로 새로운 정보를 받아 데이터를 업데이트한다.

 

 

 

8. socket.io으로 할 수 있는 것

   ① 실시간 데이터 처리

   ② 메세징 서비스

   ③ 바이너리 스트리밍, 문서 공동작업 등

 

 

9. 여러 개의 클라이언트를 서버로 접속하게 하기 위한 방법

   ① cors 모듈(socket.io 적용 안함, ajax로 변환)

   ② package.json에 proxy 설정

 

 

10. socket.io 서버 추가(아까 작업한 거 config, database, public, routes, views, package.json 가져오기)

** 1_socket_io.js

const express = require('express');
const bodyParser = require('body-parser');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const expressSession = require('express-session');
const passport = require('passport');
const static = require('serve-static');
const path = require('path');
const expressErrorHandler = require('express-error-handler');
const socketio = require('socket.io');      // npm i socket.io
const cors = require('cors');               // npm i cors

const app = express();
const router = express.Router();

app.use(cookieParser());
app.use(expressSession({
    secret: '!@#$%^&^*&(',
    resave: false,
    saveUninitialized: true,
    cookie: { maxAge: 60 * 60 * 1000 }
}));
app.use(logger('dev'));
app.use(passport.initialize());
app.use(passport.session());
app.use(bodyParser.urlencoded({extended: false}));
app.use('/public', static(path.join(__dirname, "public")));

app.use('/', router);
const errerHandler = expressErrorHandler({
    static: {
        '404': './public/404.html'
    }
});

app.use(expressErrorHandler.httpError(404));
app.use(errerHandler);

app.set('views', __dirname + "/views");
app.set('view engine', 'ejs');

const config = require('./config/config');
const database = require('./database/database');

const configPassport = require('./config/passport');
configPassport(app, passport);

const userPassport = require('./routes/route_member');
userPassport(router, passport);

const server = app.listen(config.server_port, () => {
    console.log(`${config.server_port} 포트로 웹 서버 실행중...`);
    database.init(app, config);
});

const io = socketio(server);
console.log('socket.io 서버 준비 완료!');

io.sockets.on('connection', (socket) => {
    console.dir(`connection : ${socket.request.connection._peername}`);
    socket.remoteAddress = socket.request.connection._peername.address;
    socket.remotePort = socket.request.connection._peername.port;
    console.dir(`socket.remoteAddress : ${socket.remoteAddress}`);
    console.dir(`socket.remotePort : ${socket.remotePort}`);
});

 

 

 

11. 클라이언트 웹소켓

   https://socket.io/docs/v3/client-installation/index.html

 

 

12. socket.io 클라이언트 추가(아래의 코드는 클라이언트가 서버에 붙는지 여부 확인)

** /public/client1.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>클라이언트 1</title>
    <script src="https://cdn.socket.io/3.1.3/socket.io.min.js" 
            integrity="sha384-cPwlPLvBTa3sKAgddT6krw0cJat7egBga3DJepJyrLl4Q9/5WLra3rrnMcyTyOnh" crossorigin="anonymous"></script>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" 
            integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script>
        let host;
        let port;
        let socket;

        $(function() {
            $('#connectButton').on('click', function(event) {
                console.log('connectButton 호출!');
                host = $('#hostInput').val();       // localhost
                port = $('#portInput').val();       // 3000
                connectToServer();
            });
        });

        function connectToServer() {
            console.log('connectToServer 호출!');
            const url = `http://${host}:${port}`;   // http://localhost:3000
            socket = io.connect(url)
            console.log('socket 객체 생성!');
            socket.on('connect', function() {
                console.log('웹소켓 서버에 연결했습니다.');
            });
        }
    </script>
</head>
<body>
    <!-- http://localhost:3000/public/client1.html -->
    <h2>클라이언트 1</h2>
    <hr/>
    <p>접속 ip : <input type="text" id="hostInput" value="localhost"></p>
    <p>접속 port : <input type="text" id="portInput" value="3000"></p>
    <p><input type="button" id="connectButton" value="서버접속"></p>
    <hr/>
    <p>결과 : </p>
    <div id="result"></div>
</body>
</html>

 

 

 

13. socket.io로 채팅 기능 구현

** /public/client2.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>클라이언트 2</title>
    <script src="https://cdn.socket.io/3.1.3/socket.io.min.js" 
            integrity="sha384-cPwlPLvBTa3sKAgddT6krw0cJat7egBga3DJepJyrLl4Q9/5WLra3rrnMcyTyOnh" crossorigin="anonymous"></script>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" 
            integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script>
        let host;
        let port;
        let socket;

        $(function() {
            $('#connectButton').on('click', function(event) {
                console.log('connectButton 호출!');
                host = $('#hostInput').val();       // localhost
                port = $('#portInput').val();       // 3000
                connectToServer();
            });

            $('#sendButton').on('click', function(event) {
                const sender = $('#sender').val();  // apple
                const rev = $('#rev').val();        // all
                const data = $('#data').val();      // 채팅내용 ...

                const output = {sender:sender, recepient:rev, commend: 'chat', type:'text', data:data}
                console.log(`서버로 보낼 데이터 : ${JSON.stringify(output)}`);

                if(socket == undefined) {
                    alert('서버에 연결하지 못했습니다. 서버 연결을 먼저 시도하세요.');
                    return;
                }
                socket.emit('message', output);
            });
        });

        function connectToServer() {
            console.log('connectToServer 호출!');
            const url = `http://${host}:${port}`;   // http://localhost:3000
            socket = io.connect(url)
            console.log('socket 객체 생성!');
            socket.on('connect', function() {
                console.log('웹소켓 서버에 연결했습니다.');

                socket.on('message', function(message) {
                    console.log(JSON.stringify(message));
                    console.log(`수신 메시지 : ${message.sender}, ${message.recepient}, ${message.commend}, ${message.data}`);

                    println(`수신 메시지 : ${message.sender}, ${message.recepient}, ${message.commend}, ${message.data}`);
                });
            });

            function println(data) {
                $('#result').append(`<p>${data}</p>`);
            }
        }
    </script>
</head>
<body>
    <!-- http://localhost:3000/public/client2.html -->
    <h2>클라이언트 2</h2>
    <hr/>
    <p>접속 ip : <input type="text" id="hostInput" value="localhost"></p>
    <p>접속 port : <input type="text" id="portInput" value="3000"></p>
    <p><input type="button" id="connectButton" value="서버접속"></p>
    <hr/>
    <p>보내는 사람 아이디 : <input type="text" id="sender" value="apple"></p>
    <p>받는 사람 아이디 : <input type="text" id="rev" value="all"></p>
    <p>메시지 : <input type="text" id="data"></p>
    <p><input type="button" id="sendButton" value="전송"></p>
    <hr/>
    <p>결과 : </p>
    <div id="result"></div>
</body>
</html>

 

** 1_socket_io.js

const express = require('express');
const bodyParser = require('body-parser');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const expressSession = require('express-session');
const passport = require('passport');
const static = require('serve-static');
const path = require('path');
const expressErrorHandler = require('express-error-handler');
const socketio = require('socket.io');      // npm i socket.io
const cors = require('cors');               // npm i cors

const app = express();
const router = express.Router();

app.use(cookieParser());
app.use(expressSession({
    secret: '!@#$%^&^*&(',
    resave: false,
    saveUninitialized: true,
    cookie: { maxAge: 60 * 60 * 1000 }
}));
app.use(logger('dev'));
app.use(passport.initialize());
app.use(passport.session());
app.use(bodyParser.urlencoded({extended: false}));
app.use('/public', static(path.join(__dirname, "public")));

app.use('/', router);
const errerHandler = expressErrorHandler({
    static: {
        '404': './public/404.html'
    }
});

app.use(expressErrorHandler.httpError(404));
app.use(errerHandler);

app.set('views', __dirname + "/views");
app.set('view engine', 'ejs');

const config = require('./config/config');
const database = require('./database/database');

const configPassport = require('./config/passport');
configPassport(app, passport);

const userPassport = require('./routes/route_member');
userPassport(router, passport);

const server = app.listen(config.server_port, () => {
    console.log(`${config.server_port} 포트로 웹 서버 실행중...`);
    database.init(app, config);
});

const io = socketio(server);
console.log('socket.io 서버 준비 완료!');

io.sockets.on('connection', (socket) => {
    console.dir(`connection : ${socket.request.connection._peername}`);
    socket.remoteAddress = socket.request.connection._peername.address;
    socket.remotePort = socket.request.connection._peername.port;
    console.dir(`socket.remoteAddress : ${socket.remoteAddress}`);
    console.dir(`socket.remotePort : ${socket.remotePort}`);

    socket.on('message', function(message) {
        console.log('message 이벤트를 받았습니다.');
        console.dir(message);

        if(message.recepient == 'all') {
            console.log('모든 클라이언트에게 메시지를 보냅니다.');
            io.sockets.emit('message', message);
        }
    });
});

 

'웹_프론트_백엔드 > 프론트엔드' 카테고리의 다른 글

2021.03.20  (0) 2021.05.11
2021.03.20  (0) 2021.04.20
2021.03.13  (0) 2021.04.17
2021.03.07  (0) 2021.04.02
2021.03.06  (0) 2021.03.14