본문 바로가기

React 실습

yoga 프로젝트 작업일지 - 3

728x90

#  들어가기에 앞서..

 

YogaA!를 만들면서 여러 작업에 관여하면서 기여했지만, 일단 내가 맡은 할당량만 블로그에 리뷰합니다.

그리고 디자인적인 부분의 구현은 스킵합니다.

자세한 작업을 알고 싶다면, 깃허브 주소를 남길테니 가서보시길..

 


저는 로그인및, 회원 가입, 헤더, 푸터의 디자인 및 기능 추가를 맡아서 진행했습니다.

 

서버단 부터 구현을 들어갔습니다.

여러 미들웨어가 있고 app.js에서 라우팅하는 형태이지만, 저는 로그인 회원가입을 맡았으므로 그 부분에 코드만 리뷰합니다.

 

[ 서버단 구현 ]

 

{먼저 회원가입을 구현합니다}

 

exprss를 이용하여 서버를 구현했으므로 import하고, crypto를 이용하여 해쉬를 적용했습니다

jsonwebtoken 라이브러리를 이용하여 라이브러리를 구현햇으며, secret안에 값은 UID문자기반 해쉬 키 같은 것이라고 생각하시면 됩니다.

Member은 mongoDB스키마 모델을 정의한 JS파일입니다.

 

[  user.js ]

const { Router } = require("express");
const crypto = require("crypto");
const { Member } = require("../../models");
const router = Router();
const jwt = require("jsonwebtoken");
const jwtSecret = {
    "secret": "005c9780fe7c11eb89b4e39719de58a5"
}

 

[ Member ]

shortId는 key로 이용할 id를 쉽게 만들기 위해 사용한 라이브러리입니다.

const { Schema } = require("mongoose");
const shortId = require("./types/short-id");

module.exports = new Schema({
    shortId,
    email: String,
    password: String,
    nickname: String
}, {
    timestamps: true
});

 

[ user.js ]

 

클라이언트에서 주석으로 표기한 url로 요청을 하면 해당 라우터에서 요청을 받고, 목적에 맞게 처리합니다.

( 코드 설명 )

1 .body로 email, password, nickname을 받습니다.

2. mongoDB함수인 findOne을 이용하여 이미 있는지 확인합니다.

3. 없다면, 전체적인 유효성 검사를 실시합니다.

// http://localhost:8080/user/register
// 회원 가입
router.post("/register", async (req, res, next) => {
    let { email, password, nickname } = req.body;

    let chkEmail = await Member.findOne({ email });

    if (chkEmail) {
        res.json({
            status: false,
            stateEmail: false,
            message: "이미 존재 하는 이메일이네용!"
        })
        return;
    }

    const emailRegex =
      /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i;

    const passwordRegex =
        /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&#]{8,}$/;
    
    const emailValueCheck = emailRegex.test(email);
    const passwordValueCheck = passwordRegex.test(password);

    if (!emailValueCheck) {
        res.json({
            status: false,
            stateEmail: false,
            message: "이메일 형식이 잘못되었네용!!"
        })
        return;
      }
    else if (!passwordValueCheck) {
        res.json({
            status: false,
            statePassword: false,
            message: "비번 형식이 잘못되었네용!!"
        })
        return;
    }
    else if (nickname === undefined || nickname.length === 0) {
        res.json({
            status: false,
            stateNickname: false,
            message: "닉네임을 입력해주세용!!"
        })
    }
    
    ....

 

[ user.js ]

 

( 코드 설명 )

1. 유효성 검사가 완료되면 패스워드는 해쉬를 한 후 저장합니다.

2. mongoDB에 create함수를 사용하여 스키마에 데이터를 넣고, res로 json형식으로 회원 가입 성공적으로 완수했다는 메세지를 보내줍니다.

3. 해쉬는 앞서 말했듯이 cryto라이브러리를 이용했으며 라이브러리 받아서 적용한 함수는 아래와 같습니다.

...
	// 해쉬 사용
    const hashPwd = passwordHash(password)
    
    console.log(nickname);
    // 디비에 저장
    await Member.create({
        email,
        password: hashPwd,
        nickname
    })

    res.json({
        status: true,
        message: "회원가입이 완료 되었습니다."
    })

})
// 해쉬 함수
const passwordHash = (password) => {
    return crypto.createHash("sha1").update(password).digest("hex");
}

[ user.js ]

{로그인을 구현해보겠습니다}

 

( 코드 설명 )

1. 주석의 내용처럼 url이 요청되면 로그인 처리를 담당하는 라우팅 함수 입니다.

2. 우선 req.body로 이메일과 패스워드르 받습니다.

3. findOne을 이용하여 존재하는 이메일인지 확인합니다.

4. 이메일이 존재한다고 확인되면 입력받은 패스워드를 해쉬한다음, findOne으로 찾은 비밀번호와 지금 입력 받은 비밀번호가 입력됬는지 확인하는 작업을 합니다.

5. 틀렸다면 이메일이 틀렸다는 메세지를 클라이언트에게 보냅니다.

// http://localhost:8080/user/login
// 로그인
router.post("/login", async (req, res, next) => {
    const { email, password } = req.body;

    const chkEmail = await Member.findOne({ email });
    if (!chkEmail) {
        res.json({
            status: false,
            message: "존재 하지 않는 이메일이네용.. 호호호"
        })
        return;
    }
    // 해쉬를 해서 해쉬된 비밀번호끼리 비교하려고
    const hashedPwd = passwordHash(password);
    // 비교 => 비밀번호 불일치
    if (hashedPwd !== chkEmail.password) {

        res.json({
            status: false,
            messgage: "비밀번호가 틀렸음!"
        })
        return;
    }

 

{ 로그인이 정상적으로 완료해도 되는 상황이면 토큰을 배포합니다. 이 토큰 관련 코드는 라이브러리에서 이런 형식으로 하라고 형식화 되서 구조화된 코드이므로 설명하고 말것도 없습니다}

 

한줄 한줄의 내용은 주석으로 설명햇습니다.

 

// 일치
    jwt.sign({
        email: chkEmail.email, // 이메일과 이름을 jwt로 변경해주기 위해 작성
        nickname: chkEmail.nickname
    }, jwtSecret.secret, { //jwtConfig.secret는 암호문자열임
        expiresIn: '1d'     // 기간을 줌 (1d => 하루, 1h => 한시간, 1m => 1분)
    }, (error, token) => {
        if (error) {
            //토큰을 제대로 발행하지 못하고 오류가 났을경우.
            res.status(401)
                .json({ status: false, message: "토큰 발행 실패" });
        } else {
            //정상적으로 토큰을 발행 했을경우,
            res.json({
                status: true,
                accessToken: token, //토큰 값을 브라우저에게 넘겨주는 부분
                email: chkEmail.email,
                nickname: chkEmail.nickname
            })
        }
    })
})

 

이렇게 서버단에 로그인, 회원 가입에 필요한 서버단에 구현이 완료됬습니다.


[ 전체 코드 ]

 

const { Router } = require("express");
const crypto = require("crypto");
const { Member } = require("../../models");
const router = Router();
const jwt = require("jsonwebtoken");
const jwtSecret = {
    "secret": "005c9780fe7c11eb89b4e39719de58a5"
}
// const jwtConfig = require("./../../config/jwtConfig");
// const nodemailer = require("nodemailer");


// http://localhost:8080/user/register
// 회원 가입
router.post("/register", async (req, res, next) => {
    let { email, password, nickname } = req.body;

    let chkEmail = await Member.findOne({ email });

    if (chkEmail) {
        res.json({
            status: false,
            stateEmail: false,
            message: "이미 존재 하는 이메일이네용!"
        })
        return;
    }

    const emailRegex =
      /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i;

    const passwordRegex =
        /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&#]{8,}$/;
    
    const emailValueCheck = emailRegex.test(email);
    const passwordValueCheck = passwordRegex.test(password);

    if (!emailValueCheck) {
        res.json({
            status: false,
            stateEmail: false,
            message: "이메일 형식이 잘못되었네용!!"
        })
        return;
      }
    else if (!passwordValueCheck) {
        res.json({
            status: false,
            statePassword: false,
            message: "비번 형식이 잘못되었네용!!"
        })
        return;
    }
    else if (nickname === undefined || nickname.length === 0) {
        res.json({
            status: false,
            stateNickname: false,
            message: "닉네임을 입력해주세용!!"
        })
    }

    // 해쉬 사용
    const hashPwd = passwordHash(password)
    
    console.log(nickname);
    // 디비에 저장
    await Member.create({
        email,
        password: hashPwd,
        nickname
    })

    res.json({
        status: true,
        message: "회원가입이 완료 되었습니다."
    })

})

// http://localhost:8080/user/login
// 로그인
router.post("/login", async (req, res, next) => {
    const { email, password } = req.body;

    const chkEmail = await Member.findOne({ email });
    if (!chkEmail) {
        res.json({
            status: false,
            message: "존재 하지 않는 이메일이네용.. 호호호"
        })
        return;
    }
    // 해쉬를 해서 해쉬된 비밀번호끼리 비교하려고
    const hashedPwd = passwordHash(password);
    // 비교 => 비밀번호 불일치
    if (hashedPwd !== chkEmail.password) {

        res.json({
            status: false,
            messgage: "비밀번호가 틀렸음!"
        })
        return;
    }
    // 일치
    jwt.sign({
        email: chkEmail.email, // 이메일과 이름을 jwt로 변경해주기 위해 작성
        nickname: chkEmail.nickname
    }, jwtSecret.secret, { //jwtConfig.secret는 암호문자열임
        expiresIn: '1d'     // 기간을 줌 (1d => 하루, 1h => 한시간, 1m => 1분)
    }, (error, token) => {
        if (error) {
            //토큰을 제대로 발행하지 못하고 오류가 났을경우.
            res.status(401)
                .json({ status: false, message: "토큰 발행 실패" });
        } else {
            //정상적으로 토큰을 발행 했을경우,
            res.json({
                status: true,
                accessToken: token, //토큰 값을 브라우저에게 넘겨주는 부분
                email: chkEmail.email,
                nickname: chkEmail.nickname
            })
        }
    })
})


// 해쉬 함수
const passwordHash = (password) => {
    return crypto.createHash("sha1").update(password).digest("hex");
}

module.exports = router;

 


 

후기

 

이렇게 서버단에 구현이 끝났습니다.

다음 포스팅에는 클라이언트 부분에 구현으로

전체적인 포스팅을 끝내겠습니다.

헤더, 푸터는 디지안적인 요소의 구현이 많아서 스킵하고

깃허브 주소만 남기려고 합니다.

728x90