Node.js๋กœ ํšŒ์›๊ฐ€์ž… ํ•˜๊ธฐ(๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™”) 6

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

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

1
npm install bcrypt

์ด๋ฒˆ์—๋„ ์ƒˆ๋กœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค. bcrypt๋ฅผ ์„ค์น˜ํ•˜์ž.

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

1
const bcrypt = require('bcrypt');

์ด์ „๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์„ค์น˜ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด server.js ์ƒ๋‹จ์— ์ด๋ ‡๊ฒŒ ์„ ์–ธ์„ ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

์‚ฌ์ „ ์ค€๋น„ 3 : join.ejs ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ

์•ž์— ์“ด ๊ฑฐ๋ž‘ ๋„ˆ๋ฌด ๋˜‘๊ฐ™์•„์„œ ๊ทธ๋ƒฅ ์ƒ๋žตํ•ด๋„ ๋  ๊ฒƒ ๊ฐ™์ง€๋งŒ ์ผ๋‹จ ์“ด๋‹ค. join.ejsํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์„œ /join์œผ๋กœ id์™€ pw๋ฅผ ์ „์†กํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ์ž.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<body>
    <%- include('nav.html') %>
    <div class="container mt-5">
      <form action="/join" method="POST">
        <h3>Join</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>

๊ฐœ๋ฐœ 1 : ๋ผ์šฐํ„ฐ ์ž‘์„ฑ & ํšŒ์›๊ฐ€์ž… ๋กœ์ง ๊ตฌํ˜„

GET, POST ๋ฐฉ์‹ ํ˜ธ์ถœ ๋ชจ๋‘ ์ž‘์„ฑํ•ด์ค€๋‹ค. POST๋กœ id์™€ pw๋ฅผ ๋ณด๋‚ผ ๋•Œ๋Š” ์•„๋ž˜์˜ ์ ˆ์ฐจ๋ฅผ ๊ฑฐ์ณ์•ผ ํ•œ๋‹ค. ์•„์ด๋”” ์ค‘๋ณต ์—ฌ๋ถ€ ํ™•์ธ -> ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™” -> DB์— ์ €์žฅ. ์‹คํŒจํ–ˆ์„ ๋•Œ๋Š” ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฆฌํ„ดํ•˜๋„๋ก ํ–ˆ๋‹ค. bcrypt.hash(pw, 10)์˜ hash ๊ฐ•๋„๋Š” 10์œผ๋กœ ์„ค์ •ํ–ˆ๋Š”๋ฐ ๋ณดํ†ต 10-12 ์ •๋„๋กœ ์„ค์ •ํ•œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
app.get('/join', function(์š”์ฒญ, ์‘๋‹ต){
    ์‘๋‹ต.render('join.ejs'); 
})

app.post('/join', async (req, res) => {
    try {
        const { id, pw } = req.body;

        // 1๏ธโƒฃ ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์•„์ด๋””์ธ์ง€ ํ™•์ธ
        const existingUser = await db.collection('login').findOne({ id });
        if (existingUser) {
            console.warn('โš ๏ธ ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์•„์ด๋””์ž…๋‹ˆ๋‹ค:', id);
            return res.status(400).send('์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์•„์ด๋””์ž…๋‹ˆ๋‹ค.');
        }

        // 2๏ธโƒฃ ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™”
        const hash = await bcrypt.hash(pw, 10); // ๋‘ ๋ฒˆ์งธ ์ธ์ž 10 โ†’ ํ•ด์‹œ ๊ฐ•๋„ (saltRounds)

        // 3๏ธโƒฃ DB์— ์ €์žฅ
        const result = await db.collection('login').insertOne({ id, pw: hash });
        console.log('โœ… ํšŒ์›๊ฐ€์ž… ์™„๋ฃŒ:', result);

        res.redirect('/'); // ํšŒ์›๊ฐ€์ž… ํ›„ ํ™ˆ์œผ๋กœ ์ด๋™
    } catch (error) {
        console.error('โŒ ํšŒ์›๊ฐ€์ž… ์‹คํŒจ:', error);
        res.status(500).send('ํšŒ์›๊ฐ€์ž… ์‹คํŒจ!');
    }    
});

๊ฐœ๋ฐœ 2 : ๋กœ๊ทธ์ธ ์‹œ ์•”ํ˜ธํ™” ๋œ ๊ฐ’ ๋น„๊ตํ•˜๋„๋ก ์ˆ˜์ •

๊ธฐ์กด ์ฝ”๋“œ์—๋Š” ๋ฐ”๋กœ pw๋ฅผ ํ‰๋ฌธ ๋Œ€์กฐํ•˜๋„๋ก ๋˜์–ด ์žˆ์—ˆ์ง€๋งŒ ์ด์ œ ์•”ํ˜ธํ™” ๋œ ๊ฐ’๋ผ๋ฆฌ ๋น„๊ตํ•ด์•ผ ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ๋กœ๊ทธ์ธ ๋ถ€๋ถ„ ๊ตฌํ˜„์—์„œ ์ž‘์„ฑํ•ด๋‘์—ˆ๋˜ ์ฝ”๋“œ๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•ด์ฃผ์ž.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
passport.use(new LocalStrategy({
    usernameField: 'id',
    passwordField: 'pw',
    session: true,
    passReqToCallback: false,
}, async function (์ž…๋ ฅํ•œ์•„์ด๋””, ์ž…๋ ฅํ•œ๋น„๋ฒˆ, done) {
    try {
        const ๊ฒฐ๊ณผ = await db.collection('login').findOne({ id: ์ž…๋ ฅํ•œ์•„์ด๋”” });
        if (!๊ฒฐ๊ณผ) {
            return done(null, false, { message: '์กด์žฌํ•˜์ง€ ์•Š๋Š” ์•„์ด๋””์ž…๋‹ˆ๋‹ค.' });
        }

        // ๋น„๋ฐ€๋ฒˆํ˜ธ ๋น„๊ต โ†’ bcrypt.compare() ์‚ฌ์šฉ
        const isMatch = await bcrypt.compare(์ž…๋ ฅํ•œ๋น„๋ฒˆ, ๊ฒฐ๊ณผ.pw);
        if (isMatch) {
            return done(null, ๊ฒฐ๊ณผ); // ๋กœ๊ทธ์ธ ์„ฑ๊ณต
        } else {
            return done(null, false, { message: '๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ‹€๋ ธ์Šต๋‹ˆ๋‹ค.' });
        }
    } catch (error) {
        return done(error);
    }
}));

์ด์ œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์•”ํ˜ธํ™” ํ•ด์„œ ํšŒ์›๊ฐ€์ž…์„ ํ•˜๊ณ , ์•”ํ˜ธํ™” ๋œ ๊ฐ’๋ผ๋ฆฌ ๋น„๊ตํ•ด์„œ ๋กœ๊ทธ์ธ์„ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.