Node.js๋กœ ์„ธ์…˜ ๋ฐฉ์‹ ๋กœ๊ทธ์ธ ๊ตฌํ˜„ํ•˜๊ธฐ 5

๐Ÿ™‹โ€โ™‚๏ธ1. Node.js๋กœ 5๋ถ„๋งŒ์— API ๋งŒ๋“ค๊ธฐ
๐Ÿ™‹โ€โ™‚๏ธ2. Node.js๋กœ POST API ๋งŒ๋“ค๊ธฐ
๐Ÿ™‹โ€โ™‚๏ธ3. Node.js๋กœ ๋ฐ์ดํ„ฐ ์ €์žฅํ•˜๊ธฐ
๐Ÿ™‹โ€โ™‚๏ธ4. Node.js์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ์— ์ธ๋ฑ์Šค ๋ถ™์ด๊ธฐ, ๋ฐ์ดํ„ฐ ์‚ญ์ œํ•˜๊ธฐ
์ด๋ฒˆ์—๋Š” ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด๋ณด์ž. ์ œ์ผ ๊ธฐ๋ณธ์ ์ธ ์„ธ์…˜ ์ €์žฅ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•˜๊ณ , ์•”ํ˜ธํ™”๋Š” ๊ณ ๋ คํ•˜์ง€ ์•Š๋Š”๋‹ค(์›๋ž˜๋Š” ๊ผญ ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™” ํ•ด์•ผ ํ•จ!).

์‚ฌ์ „ ์ค€๋น„ 1 : ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜

1
npm install passport passport-local express-session

ํ„ฐ๋ฏธ๋„์— ์œ„์™€ ๊ฐ™์ด ์ž…๋ ฅํ•ด์„œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ์„ค์น˜ํ•ด์ค€๋‹ค. ๋กœ๊ทธ์ธ, ๋กœ๊ทธ์ธ ๊ฒ€์ฆ, ์„ธ์…˜ ์ƒ์„ฑ์„ ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด๋‹ค. ์‹ค์ œ ์„œ๋น„์Šค ์‹œ์—๋Š” express-session ๋ง๊ณ , MongoDB์—์„œ ์„ธ์…˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒŒ ์ข‹๋‹ค.

์‚ฌ์ „ ์ค€๋น„ 2 : ์„ค์น˜ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค require

1
2
3
4
5
6
7
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const session = require('express-session');

app.use(session({secret : '๋น„๋ฐ€์ฝ”๋“œ', resave : true, saveUninitialized: false}));
app.use(passport.initialize());
app.use(passport.session()); 

์œ„์—์„œ ์„ค์น˜ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด server.js ์ƒ๋‹จ์— ์ด๋ ‡๊ฒŒ ์„ ์–ธ์„ ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. app.use()๋Š” ์ด ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•˜๊ฒ ๋‹ค๋Š” ์„ ์–ธ์ด๋‹ค.

์‚ฌ์ „ ์ค€๋น„ 3 : MongoDB์— ๊ฐ€์ƒ์˜ ์‚ฌ์šฉ์ž ์ •๋ณด ๋งŒ๋“ค๊ธฐ

ํšŒ์›๊ฐ€์ž… ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ๋กœ๊ทธ์ธ ์‹œ๋„๋ฅผ ํ•˜๋ ค๋ฉด ์ž„์˜์˜ ๊ณ„์ •์ด ํ•˜๋‚˜ ํ•„์š”ํ•˜๋‹ค. MongoDB์— ์ƒˆ ์ปฌ๋ ‰์…˜์„ ํ•˜๋‚˜ ๋งŒ๋“ค๊ณ , Insert Document ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ๊ฐ•์ œ๋กœ ํšŒ์› ๋ฐ์ดํ„ฐ๋ฅผ ํ•˜๋‚˜ ๋„ฃ์–ด๋ณด์ž.
MongoDB

์ด์ œ ์‚ฌ์ „ ์ค€๋น„๋Š” ๋ชจ๋‘ ๋๋‚ฌ๊ณ , ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋ฉด ๋œ๋‹ค.
์–ด๋–ค ์‚ฌ๋žŒ์ด ๋กœ๊ทธ์ธ ์‹œ๋„๋ฅผ ํ•˜๋ฉด ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ DB์— ์žˆ๋Š”์ง€ ๊ฒ€์‚ฌํ•˜๊ณ , ๊ฒฐ๊ณผ๊ฐ€ ์žˆ์œผ๋ฉด ์„ธ์…˜์„ ํ•˜๋‚˜ ์ƒ์„ฑํ•˜๋ฉฐ ์„ฑ๊ณต ํŽ˜์ด์ง€๋กœ ์ด๋™์‹œํ‚จ๋‹ค. ๋กœ๊ทธ์ธ์— ์‹คํŒจํ•˜๋ฉด ์‹คํŒจ ํŽ˜์ด์ง€๋กœ ์ด๋™์‹œํ‚ค๋ฉด ๋์ด๋‹ค.

๊ฐœ๋ฐœ 1 : ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ & ๋ผ์šฐํŒ…

/login์œผ๋กœ ๋ฐฉ๋ฌธํ–ˆ์„ ๋•Œ ๋ณด์—ฌ์ค„ ํŽ˜์ด์ง€๋ฅผ ํ•˜๋‚˜ ์ค€๋น„ํ•ด์•ผ ํ•œ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด login.ejs ํŒŒ์ผ์„ ์ƒ์„ฑํ–ˆ๋‹ค. (๋ฐ”๋”” ๋ถ€๋ถ„๋งŒ ๊ฐ€์ ธ์™”์Œ)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
    <%- include('nav.html') %>
    <div class="container mt-5">
      <form action="/login" method="POST">
        <h3>Login</h3>
        <div class="form-group">  
          <h5 class="mt-4">์•„์ด๋””</h5> 
          <input class="form-control form-control-sm" type="text" placeholder="id" name="id">
        </div>
        <div class="form-group"> 
          <h5 class="mt-4">๋น„๋ฐ€๋ฒˆํ˜ธ</h5>
          <input class="form-control form-control-sm" type="password" placeholder="password" name="pw">
        </div>
          <button type="submit" class="btn btn-primary" data-toggle="button" aria-pressed="false">
          ์ €์žฅํ•˜๊ธฐ
        </button>
      </form>
    </div>

    (...)
  </body>

๊ทธ๋ฆฌ๊ณ  /login์ด ํ˜ธ์ถœ๋˜๋ฉด ejsํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋„๋ก ๋ผ์šฐํŒ…๋„ ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

1
2
3
app.get('/login', function(์š”์ฒญ, ์‘๋‹ต){
    ์‘๋‹ต.render('login.ejs'); 
}) 

๊ฐœ๋ฐœ 2 : ์•„์ด๋””์™€ ํŒจ์Šค์›Œ๋“œ ๊ฒ€์‚ฌ

๋ˆ„๊ตฐ๊ฐ€ loginํผ์—์„œ ID์™€ PW๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ์ „์†ก ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด, ์ด ํšŒ์› ์ •๋ณด๊ฐ€ ์œ ํšจํ•œ์ง€ ๊ฒ€์‚ฌํ•ด์•ผ ํ•œ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋ฉด Local ๋ฐฉ์‹์œผ๋กœ ์•„์ด๋””/๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ธ์ฆ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. failureRedirect ๋ถ€๋ถ„์€ ์ธ์ฆ์— ์‹คํŒจํ–ˆ์„ ๋•Œ ์ด๋™์‹œํ‚ฌ ๊ฒฝ๋กœ์ด๋‹ค.

์ด ํŽธ๋ฆฌํ•œ ๊ธฐ๋Šฅ์€ ์œ„์—์„œ ์ถ”๊ฐ€ํ•œ passport ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

1
2
3
4
5
6
//passport.authenticate๋ฅผ ํ†ตํ•ด ์‘๋‹ต ์ „์— local ๋ฐฉ์‹์œผ๋กœ ์•„์ด๋””/๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ธ์ฆ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. 
app.post('/login', passport.authenticate('local', {
    failureRedirect: '/fail'
}), function (req, res) {
    res.redirect('/'); 
});

๊ฐœ๋ฐœ 3 : ๊ฒ€์‚ฌ๋ฅผ ํ•ด์ฃผ๋Š” ์„ธ๋ถ€์ ์ธ ์ฝ”๋“œ

2๋ฒˆ์˜ ์ฝ”๋“œ๋งŒ ์ž‘์„ฑํ•œ๋‹ค๊ณ  ๋กœ๊ทธ์ธ์ด ๋˜์ง€๋Š” ์•Š๋Š”๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด passport ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ •์ƒ ๋™์ž‘ํ•˜๋„๋ก ์ถ”๊ฐ€ ์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ์ง์ ‘ ์ฐฝ์กฐํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๊ณ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์˜ˆ์ œ ์ฝ”๋“œ์— ๋‚˜์™€์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
passport.use(new LocalStrategy({
    usernameField: 'id', //์‚ฌ์šฉ์ž๊ฐ€ ์ œ์ถœํ•œ ID๊ฐ€ ์–ด๋””์— ์ ํ˜€์žˆ๋Š”์ง€ 
    passwordField: 'pw', //์‚ฌ์šฉ์ž๊ฐ€ ์ œ์ถœํ•œ PW๊ฐ€ ์–ด๋””์— ์ ํ˜€์žˆ๋Š”์ง€(input name๊ฐ’)
    session: true, //์„ธ์…˜ ์ƒ์„ฑ ์—ฌ๋ถ€ 
    passReqToCallback: false, //์•„์ด๋””, ํŒจ์Šค์›Œ๋“œ ๋ง๊ณ  ๋‹ค๋ฅธ ์ •๋ณด๊ฒ€์‚ฌ๊ฐ€ ํ•„์š”ํ•œ์ง€ 
}, async function (์ž…๋ ฅํ•œ์•„์ด๋””, ์ž…๋ ฅํ•œ๋น„๋ฒˆ, done) {
    try {
        const ๊ฒฐ๊ณผ = await db.collection('login').findOne({ id: ์ž…๋ ฅํ•œ์•„์ด๋”” });
        if (!๊ฒฐ๊ณผ) {
            return done(null, false, { message: '์กด์žฌํ•˜์ง€ ์•Š๋Š” ์•„์ด๋””์ž…๋‹ˆ๋‹ค.' });
        }

        if (์ž…๋ ฅํ•œ๋น„๋ฒˆ === ๊ฒฐ๊ณผ.pw) {
            return done(null, ๊ฒฐ๊ณผ); // ๋กœ๊ทธ์ธ ์„ฑ๊ณต
        } else {
            return done(null, false, { message: '๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ‹€๋ ธ์Šต๋‹ˆ๋‹ค.' });
        }
    } catch (error) {
        return done(error);
    }
}));

๊ฐœ๋ฐœ 4 : ๋กœ๊ทธ์ธ์ด ์„ฑ๊ณตํ–ˆ๋‹ค๋ฉด ์„ธ์…˜์„ ๋งŒ๋“ค์ž

๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•˜๋ฉด ์„ธ์…˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์ฟ ํ‚ค๋กœ ๋ณด๋‚ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ์ด๊ฒƒ๋„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งจ ์œ„์—์„œ ๋ฏธ๋ฆฌ ์ถ”๊ฐ€ํ•ด๋‘์—ˆ๋‹ค. ์•„๋ž˜์˜ ์ฝ”๋“œ๋งŒ ์ž…๋ ฅํ•˜๋ฉด ์œ ์ € id ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์„ธ์…˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ์ƒ์„ฑ๋œ ์„ธ์…˜ ์•„์ด๋””๋ฅผ ์ด์šฉ ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
passport.serializeUser(function (user, done) {
    done(null, user.id); // ์„ธ์…˜์— ์‚ฌ์šฉ์ž id ์ €์žฅ
});

passport.deserializeUser(async function (์•„์ด๋””, done) {
    try {
        const ๊ฒฐ๊ณผ = await db.collection('login').findOne({ id: ์•„์ด๋”” });
        if (!๊ฒฐ๊ณผ) {
            return done(null, false);
        }
        done(null, ๊ฒฐ๊ณผ); // ์„ธ์…˜์—์„œ ์‚ฌ์šฉ์ž ์ •๋ณด ๋ณต์›
    } catch (error) {
        done(error);
    }
});

ํ…Œ์ŠคํŠธ : ์ •์ƒ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ

์ด์ œ localhost:8080์—์„œ ์•„๊นŒ ๋งŒ๋“ค์–ด ๋‘” ๊ณ„์ • ์ •๋ณด๋กœ ๋กœ๊ทธ์ธ์„ ์‹œ๋„ํ•œ ํ›„, ์ฟ ํ‚ค๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด ์ •์ƒ์ ์œผ๋กœ session์ด ์ƒ์„ฑ๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์„ธ์…˜

๊ฐœ๋ฐœ 5 : ๋กœ๊ทธ์ธ ํ•œ ์œ ์ €๋งŒ ๋ณผ ์ˆ˜ ์žˆ๋Š” ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ

๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์—ˆ์œผ๋‹ˆ ๋กœ๊ทธ์ธ ํ•œ ์œ ์ €๋งŒ ๋ณผ ์ˆ˜ ์žˆ๋Š” ํŽ˜์ด์ง€๋„ ๋งŒ๋“ค์–ด ๋ณด๋ฉด ์ข‹๊ฒ ๋‹ค. ๋งˆ์ดํŽ˜์ด์ง€ mypage.ejs ํ•˜๋‚˜๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž. ์•„๋ž˜์™€ ๊ฐ™์ด ์•„์ฃผ ์‹ฌํ”Œํ•œ ๋‚ด์šฉ๋งŒ ์žˆ์–ด๋„ ๊ดœ์ฐฎ๋‹ค.

1
2
3
4
5
6
<body>
    <%- include('nav.html') %>
    
    <h1>My Page</h1>
    <p><%= user.id%>์˜ ๋งˆ์ดํŽ˜์ด์ง€์ž…๋‹ˆ๋‹ค.</p>
</body>

์œ„ ejs ํŽ˜์ด์ง€๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก server.js์—์„œ ๋ผ์šฐํŒ…์„ ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ์ด ๋•Œ, ๋กœ๊ทธ์ธ ์ฒดํฌ๋ฅผ ํ•˜๋ ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. get ์•ˆ์— loginCheck์ฒ˜๋Ÿผ ์ž„์˜๋กœ ๋งŒ๋“  ๋ฏธ๋“ค์›จ์–ด๋ฅผ ๋„ฃ์œผ๋ฉด /mypage ์š”์ฒญ๊ณผ mypage.ejs ์‘๋‹ต ์‚ฌ์ด์— loginCheck๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

1
2
3
4
app.get('/mypage', loginCheck, function (์š”์ฒญ, ์‘๋‹ต) {
    console.log("๋กœ๊ทธ์ธํ•œ ์œ ์ €:", ์š”์ฒญ.user); // ๋กœ๊ทธ์ธํ•œ ์œ ์ € ์ •๋ณด
    ์‘๋‹ต.render('mypage.ejs', {user: ์š”์ฒญ.user}) // ๋กœ๊ทธ์ธํ•œ ์œ ์ € ์ •๋ณด๋ฅผ EJS๋กœ ์ „๋‹ฌ
}) 

server.js ์•„๋ž˜์ชฝ ์–ด๋”˜๊ฐ€์— loginCheck๋„ ๊ตฌํ˜„ํ•ด๋ณด์ž. ์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๋ฉด ๋์ด๋‹ค. deserializeUser ํ•จ์ˆ˜์—์„œ req.user์— ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋ฏธ๋ฆฌ ์ €์žฅํ•˜๋„๋ก ๊ตฌํ˜„ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ฆ‰ req.user๋Š” deserializeUser๊ฐ€ ๋ณด๋‚ด์ค€ ๋กœ๊ทธ์ธ ์œ ์ €์˜ ์ •๋ณด๋ผ๊ณ  ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.

1
2
3
4
5
6
7
function loginCheck(req, res, next) {
    if (req.user) { // ๋กœ๊ทธ์ธ ์ƒํƒœ ํ™•์ธ. 
        next(); // ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด๋กœ ์ด๋™
    } else {
        res.redirect('/login'); // ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
    }
}