[2025 ์ƒ๋ฐ˜๊ธฐ] ๐Ÿชด์‹์ง‘์‚ฌ๋“ค - ์›น์•ฑ ๋ฐ˜๋ ค์‹๋ฌผ ๊ด€๋ฆฌ ๋ฐ ์ง„๋ฃŒ ์„œ๋น„์Šค

๋จผ์ € ์›น์‚ฌ์ดํŠธ์˜ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด๋ณด์ž!
๋กœ๊ทธ์ธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ์— ์•ž์„œ ์ฟ ํ‚ค์™€ ์„ธ์…˜ ์ค‘ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•  ๊ฒƒ์ธ์ง€ ๊ณ ๋ฏผํ–ˆ๋‹ค.

๐Ÿช์ฟ ํ‚ค

์ฟ ํ‚ค๋Š” ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ์ƒํƒœ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. ์„œ๋ฒ„๋Š” ์‚ฌ์šฉ์ž์˜ ์›น ๋ธŒ๋ผ์šฐ์ €์— ์ฟ ํ‚ค๋ฅผ ๋ณด๋‚ด๋ฉฐ ๋ธŒ๋ผ์šฐ์ €๋Š” ์„œ๋ฒ„์—์„œ ๋ณด๋‚ธ ์ฟ ํ‚ค๋ฅผ ์ €์žฅํ•˜๊ณ  ์ดํ›„ ์š”์ฒญ๊ณผ ํ•จ๊ป˜ ๋™์ผํ•œ ์„œ๋ฒ„๋กœ ๋‹ค์‹œ ์ „์†กํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰ ๋‚˜์˜ ์ •๋ณด์— ๊ด€ํ•œ ๋ชจ๋“  ๊ฒƒ์„ ๊ธฐ์–ตํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ด€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์„ธ์…˜

์„ธ์…˜์€ ์ฟ ํ‚ค๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•˜์ง€๋งŒ, ์ •๋ณด๋ฅผ ์„œ๋ฒ„ ์ธก์—์„œ ๊ด€๋ฆฌํ•œ๋‹ค๋Š” ์ ์—์„œ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค. ์„œ๋ฒ„๋Š” ํด๋ผ์ด์–ธํŠธ์— Session ID๊ฐ’์„ ๋ถ€์—ฌํ•˜๊ณ  ์ •๋ณด๋Š” ์„œ๋ฒ„์— ์ €์žฅํ•œ๋‹ค. ์ดํ›„ ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์— ๋”ฐ๋ผ ์„œ๋ฒ„์—์„œ Session ID๋ฅผ ์กฐํšŒํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€ ์‚ฌ์šฉํ•œ๋‹ค.

์„ธ์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜์ž!

์„ธ์…˜์€ย ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ย ๊ฐ™์€ย ์ธ์ฆย ์ •๋ณด๋ฅผย ์ฟ ํ‚ค์—ย ์ €์žฅํ•˜์ง€ย ์•Š๊ณ ย ์„œ๋ฒ„์—ย ์ €์žฅํ•œ๋‹ค.ย ๋Œ€์‹ ย ์ฟ ํ‚ค์—๋Š”ย ์‚ฌ์šฉ์žย ์‹๋ณ„์ž์ธย session-id๋ฅผย ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.ย ย ์ด์ฒ˜๋Ÿผย ์„ธ์…˜๋„ย ์ฟ ํ‚ค๋ฅผย ์ด์šฉํ•˜์ง€๋งŒย ์ถ”์ •ย ๋ถˆ๊ฐ€๋Šฅํ•œย session-id๋ฅผย ์ฃผ๊ณ ๋ฐ›๊ธฐย ๋•Œ๋ฌธ์—ย ๋ณด์•ˆ์ƒย ์•ˆ์ „ํ•˜๋‹ค.ย 
๋”ฐ๋ผ์„œย ๋…ธ์ถœ๋˜๋ฉดย ์•ˆย ๋˜๋Š”ย ์ค‘์š”ํ•œย ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋Š”ย ์„ธ์…˜์„ย ์ด์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค.


express-session ๋ฏธ๋“ค์›จ์–ด

์„ธ์…˜ ๊ด€๋ฆฌ์šฉ ๋ฏธ๋“ค์›จ์–ด์ด๋‹ค.
ํŠน์ • ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ด๋‘˜ ๋•Œ ์œ ์šฉํ•˜๊ฒŒ ์“ฐ์ธ๋‹ค.
์„ธ์…˜์€ ์‚ฌ์šฉ์ž๋ณ„๋กœ req.session ๊ฐ์ฒด ์•ˆ์— ์œ ์ง€๋œ๋‹ค.


app.use(session({
    secret: 'your_secret_key',
    resave: false,
    saveUninitialized: false,
    cookie: { 
        secure: false, // ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ HTTPS๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ
        maxAge: 1000 * 60 * 60 // 1์‹œ๊ฐ„ ๋™์•ˆ ์„ธ์…˜ ์œ ์ง€
    }
}));
  

๋จผ์ € ์„ธ์…˜์„ ์•”ํ˜ธํ™”ํ•˜๊ธฐ ์œ„ํ•œ secret_key๋ฅผ ๊ฐ€์ ธ์™”๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉ์ž์˜ ์„ธ์…˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์— ์„œ๋ฒ„๋Š” ์„ธ์…˜์„ ๋‹ค์‹œ ์ €์žฅํ•˜์ง€ ์•Š๋„๋ก ์„ค์ •ํ•˜์˜€๋‹ค. ๋˜ํ•œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ถˆํ•„์š”ํ•œ ๋นˆ ์„ธ์…˜ ์ฟ ํ‚ค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด saveUninitialized๋ฅผ false๋กœ ํ•ด์„œ, ์‹ค์ œ ์„ธ์…˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์„ค์ •๋˜๊ธฐ ์ „๊นŒ์ง€๋Š” ์„ธ์…˜์„ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ•˜๋„๋ก ํ•˜์˜€๋‹ค.
secure๊ฐ’์ด true์ผ ๊ฒฝ์šฐ์—๋Š” HTTPS ์—ฐ๊ฒฐ์—์„œ๋งŒ ์ฟ ํ‚ค๊ฐ€ ์ „์†ก๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ HTTPS๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ด์„œ ์ฟ ํ‚ค์˜ ์„ค์ •๊ฐ’์œผ๋กœ secure๋ฅผ false๋กœ ํ–ˆ๋‹ค.

๊ฐœ๋ฐœ์ด ์™„๋ฃŒ๋œ ํ›„์— ๋ฐฐํฌ ํ™˜๊ฒฝ์—์„œ๋Š” secure๋ฅผ true๋กœ ๋ณ€๊ฒฝํ•  ์˜ˆ์ •์ด๋‹ค!!!!



app.post('/login', async (req, res) => {
    const { ID, password } = req.body;
    try {
        const user = await fetchUser(ID);
        if (!user) {
            return res.json({ success: false, message: "์กด์žฌํ•˜์ง€ ์•Š๋Š” ์•„์ด๋””์ž…๋‹ˆ๋‹ค." });
        }
        if (password !== user.password) {
            return res.json({ success: false, message: "๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ‹€๋ ธ์Šต๋‹ˆ๋‹ค." });
        }

        // ์„ธ์…˜์„ ์ด์šฉํ•˜์—ฌ ๋กœ๊ทธ์ธ ์ƒํƒœ ์ €์žฅ
        req.session.user = { 
            ID: user.ID, 
            nickname: user.nickname 
        };

        // ์ถœ์„ ๊ธฐ๋ก
        const attendanceRecorded = await recordAttendance(user.ID);
        if (attendanceRecorded) {
            console.log(`${user.ID}๋‹˜์˜ ์ถœ์„์ด ๊ธฐ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.`);
        } else {
            console.log(`${user.ID}๋‹˜์€ ์ด๋ฏธ ์˜ค๋Š˜ ์ถœ์„ํ–ˆ์Šต๋‹ˆ๋‹ค.`);
        }

        res.json({ success: true, message: "๋กœ๊ทธ์ธ ์„ฑ๊ณต" });
    } catch (error) {
        console.error('๋กœ๊ทธ์ธ ์—๋Ÿฌ:', error);
        res.status(500).json({ success: false, message: "๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค." });
    }
});

  

fetchUser(ID)๋Š” ์‚ฌ์šฉ์ž ID๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜์ด๋‹ค.
๋งŒ์•ฝ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ user๋ฅผ ์ฐพ์ง€ ๋ชปํ–ˆ๋‹ค๋ฉด return์„ ์ด์šฉํ•ด ๋” ์ด์ƒ ์ง„ํ–‰๋˜์ง€ ๋ชปํ•˜๋„๋ก ํ–ˆ๋‹ค.
๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•˜๋ฉด ์„ธ์…˜์— ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์ €์žฅํ•œ๋‹ค.
req.session.user์— ์•„์ด๋””์™€ ๋‹‰๋„ค์ž„์„ ์ €์žฅํ–ˆ๋‹ค.
๋”ฐ๋ผ์„œ ์ดํ›„ ์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ๋ˆ„๊ตฌ์ธ์ง€ ์‹๋ณ„ ๊ฐ€๋Šฅํ•˜๋ฉฐ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค!


์•„์ด๋”” ์ฐพ๊ธฐ ์ฝ”๋“œ


app.post('/find-id', async (req, res) => {
    const { email } = req.body;

    try {
        const user = await fetchUserByEmail(email);

        if (!user) {
            return res.json({ success: false, message: "๋“ฑ๋ก๋œ ์ด๋ฉ”์ผ์ด ์—†์Šต๋‹ˆ๋‹ค." });
        }

        res.json({ success: true, message: "์•„์ด๋””๋ฅผ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค.", ID: user.ID });
    } catch (error) {
        console.error('์•„์ด๋”” ์ฐพ๊ธฐ ์—๋Ÿฌ:', error);
        res.status(500).json({ success: false, message: "์•„์ด๋”” ์ฐพ๊ธฐ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค." });
    }
});
  

๋จผ์ € ํด๋ผ์ด์–ธํŠธ๊ฐ€ email์„ ์„œ๋ฒ„๋กœ ๋ณด๋‚ด๋ฉด req.body์—์„œ ์ด๋ฉ”์ผ ๊ฐ’์„ ์ถ”์ถœํ•˜์—ฌ ๋ณ€์ˆ˜ email์— ์ €์žฅํ•œ๋‹ค.
๊ทธ๋ฆฌ๊ณ  fetchUserByEmail(email)์„ ์ด์šฉํ•ด ์‚ฌ์šฉ์ž์˜ ์ด๋ฉ”์ผ์„ ๊ฐ€์ ธ์˜จ๋‹ค.
ํ•ด๋‹น ์ด๋ฉ”์ผ์„ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž๊ฐ€ ์กด์žฌํ•˜๋ฉด user.ID๋ฅผ ํด๋ผ์ด์–ธํŠธ์— ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ–ˆ๋‹ค.
์œ„์˜ ์ฝ”๋“œ์™€ ๋น„์Šทํ•˜๊ฒŒ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ฐพ๊ธฐ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ–ˆ๋‹ค!!!


๋น„๋ฐ€๋ฒˆํ˜ธ ์ฐพ๊ธฐ ์ฝ”๋“œ


app.post('/find-password', async (req, res) => {
    const { ID, email } = req.body;

    try {
        const user = await fetchUserByIdAndEmail(ID, email);

        if (!user) {
            return res.json({ success: false, message: "์•„์ด๋”” ๋˜๋Š” ์ด๋ฉ”์ผ์ด ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค." });
        }

        // ์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ƒ์„ฑ ๋ฐ ์ €์žฅ
        const newPassword = generateRandomPassword();
        const hashedPassword = await hashPassword(newPassword);
        await updateUserPassword(ID, hashedPassword);

        // ์ด๋ฉ”์ผ๋กœ ์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ฐœ์†ก
        await sendEmail(email, "๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ •", `์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ: ${newPassword}`);
        res.json({ success: true, message: "์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ด๋ฉ”์ผ๋กœ ๋ฐœ์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค." });
    } catch (error) {
        console.error('๋น„๋ฐ€๋ฒˆํ˜ธ ์ฐพ๊ธฐ ์—๋Ÿฌ:', error);
        res.status(500).json({ success: false, message: "๋น„๋ฐ€๋ฒˆํ˜ธ ์ฐพ๊ธฐ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค." });
    }
});
  
์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ƒ์„ฑ ๋ฐ ์•”ํ˜ธํ™”

generateRandomPassword()๋Š” ์ƒˆ๋กœ์šด ์ž„์‹œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.
hashPassword(newPassword)๋Š” ์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์•”ํ˜ธํ™”ํ•˜๋Š” ๋น„๋™๊ธฐ ํ•จ์ˆ˜์ด๋‹ค.
์ด ์•”ํ˜ธํ™”๋œ ๊ฐ’์ธ hashedPassword๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ๋‹ค.

await updateUserPassword(ID, hashedPassword);
๊ทธ๋ฆฌ๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ํ•ด๋‹น ID๋ฅผ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“  ์•”ํ˜ธํ™”๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ ์—…๋ฐ์ดํŠธ ํ–ˆ๋‹ค.


ํšŒ์›๊ฐ€์ž… ์ฝ”๋“œ

ID๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์‹ ๊ทœ ๊ฐ€์ž…์ž๋“ค์„ ์œ„ํ•œ ํšŒ์›๊ฐ€์ž… ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜์˜€๋‹ค.
๋”ฐ๋ผ์„œ ์ค‘๋ณต ID๊ฐ€ ๋ฐœ๊ฒฌ๋˜๋ฉด, ๊ฐ€์ž…์ด ๋˜์ง€ ์•Š๊ณ  โ€œ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ID์ž…๋‹ˆ๋‹ค.โ€๋ผ๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ์ถœ๋ ฅ๋˜๋„๋ก ํ–ˆ๋‹ค.


app.post('/signup', async (req, res) => {
    const { ID, nickname, email, password, confirmPassword } = req.body;
    try {
        const exists = await fetchUser(ID);
        if (exists) {
            return res.json({ success: false, message: '์ด๋ฏธ ์กด์žฌํ•˜๋Š” ID์ž…๋‹ˆ๋‹ค.' });
        }
        if (!ID || !nickname || !email || !password || !confirmPassword) {
            return res.json({ success: false, message: '๋ชจ๋“  ํ•ญ๋ชฉ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.' });
        }
        if (password !== confirmPassword) {
            return res.json({ success: false, message: '๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.' });
        }
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        if (!emailRegex.test(email)) {
            return res.status(400).json({ success: false, message: '์˜ฌ๋ฐ”๋ฅธ ์ด๋ฉ”์ผ ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.' });
        }
        const newUser = { ID, nickname, email, password, createdAt: new Date().toISOString() };
        await createUser(newUser);
        return res.json({ success: true, message: 'ํšŒ์›๊ฐ€์ž…์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.' });
    } catch (error) {
        console.error('ํšŒ์›๊ฐ€์ž… ์—๋Ÿฌ:', error);
        return res.status(500).json({ success: false, message: 'ํšŒ์›๊ฐ€์ž… ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.' });
    }
});
  

newUser ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด์•˜๋‹ค.
๊ทธ๋ฆฌ๊ณ  createUser(newUser)๋กœ ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ–ˆ๋‹ค.


๐Ÿ’ป ๋ฐฑ์—”๋“œ - ๋งˆ์ดํŽ˜์ด์ง€ ์ฝ”๋“œ ๊ตฌํ˜„ ๋ณด๋Ÿฌ๊ฐ€๊ธฐ