티스토리 뷰

개발공부/🟩 Node.js

[NodeJS] Express와 MongoDB로 웹서비스 만들기 - 2 (1)

2022. 8. 4. 17:58

 

 

Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

 

 

 

 회원가입 구현하기 

 

▶ 구현할 회원가입 기능 설명

 

이메일, 이름, 패스워드의 간단한 정도만 사용

 

1)  회원가입 페이지 구현

2)  script를 이용해 이메일 형식, 비밀번호 길이 및 비밀번호확인 문자열과 일치여부 확인

3)  form을 이용해 post 요청 전송

4)  회원가입 처리 및 redirect

 

 

 

*  비밀번호 저장 시 암호화하여 저장 (Hash)

 

Hash

:  문자열을 되돌릴 수 없는 방식으로 암호화하는 방법

→  hash 출력값으로는 사용자의 원래 비밀번호를 알아낼 수 없음

→  비밀번호의 Hash 값을 데이터베이스에 저장하고, 로그인 시 전달된 비밀번호를 Hash하여 저장된 값과 비교해 로그인을 처리

 

-  Node.js의 기본제공 모듈인 crypto모듈을 사용하여 hash값을 얻을 수 있음

-  간단하게 sha1알고리즘을 사용하거나 보다 강력한 sha224, sha256 등의 알고리즘도 사용 가능

const hash = crypto.createHash('sha1');
hash.update(password);
hash.digest("hex");

 

 

 

 

 

▶ 회원가입 구현하기

 

1) 회원가입 페이지 만들기

...
form(action="/join" method="post" onsubmit="return check()")
    table
        tbody
            tr
                td 이메일
                td: input(type="text" name="email")
            tr
                td 이름
                td: input(type="name" name="name")
            tr
                td 비밀번호
                td: input(type="password" name="password")
            tr
                td 비밀번호 확인
                td: input(type="password" name="password_confirm")
            tr
                td(colspan="2")
                    input(type="submit" value="가입하기")
...

...
script.
    function check() {
        const email = document.querySelector('[name="email"]').value;
        if (!/^\S+@\S+\.\S+$/.test(email)) {
            alert('이메일 형식이 올바르지 않습니다.');
            return false;
        }
        
        const password = document.querySelector('[name="password"]').value;
        if (password.length < 8) {
            alert("최소 8자리 이상의 비밀번호를 설정해 주세요.");
            return false;
        }
        
        const passwordConfirm = document.querySelector('[name="password_confirm"]').value;
        if (password != passwordConfirm) {
            alert('비밀번호 확인이 일치하지 않습니다.');
            return false;
        }
        
        return true;
    }

 

 

 

 

2)  회원가입 요청 처리하기

 

-  비밀번호 hash 값 저장

-  이미 존재하는 회원인지 체크

-  가입완료 후 메인화면으로 redirect

router.post(... => {
	const { email, name, password } = req.body;
	const pwHash = getHash(password);	// getHash는 crypto를 이용해 비밀번호를 암호화하도록 따로 만들어서 export한 함수
    
	const exists = await User.findOne({ email });
	if (exists) {
		throw new Error('이미 가입된 메일입니다');
	}
    
	await User.create({
		email,
		name,
		password: pwHash,
	});
    
	res.redirect('/');
});




// getHash 코드
const crypto = require('crypto');

module.exports = (password) => {
  const hash = crypto.createHash('sha1');
  hash.update(password);
  return hash.digest("hex");
}

 

 

 

 

 

 

 

 로그인 구현하기 

 

▶ 로그인 기능 설명

 

1)  로그인 화면 구성하기

2)  passport-local strategy로 로그인 구현하기

3)  passport.js 설정하기

4)  passport로 요청 처리하기

 

 

* Passport.js

:  Express.js 어플리케이션에 간단하게 사용자 인증 기능을 구현하게 도와주는 패키지

→  유저 세션 관리 및 다양한 로그인 방식 추가 가능

 

-  passport는 다양한 로그인 방식을 구현하기 위해 strategy라는 인터페이스를 제공

-  strategy 인터페이스에 맞게 설계된 다양한 구현체들이 있음 (facebook, google, ...)

-  passport-local은 username, password를 사용하는 로그인의 구현체

 

 

 

 

 

▶ 로그인 기능 구현하기

 

1)  로그인 화면 구성하기

...
form(action="/auth" method="post" onsubmit="return check()")
    table
        tbody
            tr
                td 이메일
                td: input(type="text" name="email")
            tr
                td 비밀번호
                td: input(type="password" name="password")
            tr
                td(colspan="2") 
                td: input(type="submit" name="로그인")
...

...
script.
    function check() {
        const email = document.querySelector('[name="email"]').value
        if (!email) {
            alert("이메일을 입력해 주세요.");
            return false;
        }
        
        const password = document.querySelector('[name="password"]').value
        if (!email) {
            alert("비밀번호를 입력해 주세요.");
            return false;
        }
        return true;
    }

 

 

 

2)  passport-local strategy로 로그인 구현하기

const config = {	// 아이디 패스워드 필드 설정 필수!
	usernameField: 'email',
	passwordField: 'password',
};

const local = new LocalStrategy(config, async (email, password, done) => {
    try {
        const user = await User.findOne({ email });
        if (!user) {
        	throw new Error('회원을 찾을 수 없습니다.');
        }
        if (user.password !== password) {	// user.password는 hash값이므로 입력받은 password도 hash값으로 만들어서 비교해야함
        	throw new Error('비밀번호가 일치하지 않습니다.');
        }
        
        // 세션에 저장되는 유저 정보의 최소화
        done (null, {
            shortId: user.shortId,
            email: user.email,
            name: user.name,
        });
    } catch (err) {
	    done(err, null);
    }
})

 

 

 

3)  passport.js 설정하기

 

-  작성한 strategy를 passport.use를 이용해 사용하도록 선언 필요

-  passport.use를 이용해 strategy를 사용하도록 선언한 후

passport.authenticate를 사용해 해당 strategy를 이용해 요청을 처리할 수 있음

const local = require('./strategies/local');
passport.use(local);

 

 

 

4)  passprot.js로 post 요청 처리하기

 

-  passport.authenticate함수를 http 라우팅에 연결하면

:  passport가 자동으로 해당하는 strategy를 사용하는 request handler를 자동 생성

 

-  express-session과 passport.session()을 사용하면

:  passport가 로그인 시 유저 정보를 세션에 저장하고 가져오는 동작을 자동으로 수행

--- routes/auth.js ---
router.post('/', passport.authenticate('local');


--- app.js ---
const session = require('express-session');

app.use(session({ 
    secret: 'secret', 
    resave: false, 
    saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
app.use('/auth', authRouter);

 

 

 

5)  session 유저 활용하기

 

session을 이용해 user를 사용할 때 serializeUser 와 deserializeUser  설정 필요

→  이는 세션에 user 정보를 변환하여 저장하고 가져오는 기능을 제공

(ex. 회원 id만 세션에 저장하고 사용 시 회원 정보를 디비에서 찾아서 사용)

 

※ 세션 사용 시 위 두 함수를 작성하지 않으면 passport 로그인이 동작하지 않음

passport.serializeUser((user, callback) => {
	callback(null, user);
});

passport.deserializeUser((obj, callback) => {
	callback(null, obj);
});

 

 

 

 

▶ 로그아웃 기능 구현하기

 

passport는 req.logout 함수를 통해 세션의 로그인 정보를 삭제하여 로그아웃 기능을 구현 가능

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

 

 

 

▶ 로그인 확인 미들웨어

 

로그인을 필수로 설정하고 싶을 경우 미들웨어를 사용하여 체크 가능

function loginRequired(req, res, next) {
    if (!req.user) {
        res.redirect('/');
        return;
    }
    next();
}

app.use('/posts', loginRequired, postsRouter);

 

 

 

 

 

 

 

 Session Store 

 

▶ Session 이란?

 

:  웹 서버가 클라이언트의 정보를 클라이언트별로 구분하여 서버에 저장하고

클라이언트 요청 시 Session ID를 사용하여 클라이언트의 정보를 다시 확인하는 기술

 

※ 클라이언트가 정보를 저장하고, 요청 시 정보를 보내는 Cookie와 대조됨

 

 

 

Session 작동 방식

 

서버는 세션을 생성하여 세션의 구분자인 Session ID를 클라이언트에 전달

→  클라이언트는 요청 시 session id를 함께 요청에 담아서 전송

→  서버는 전달받은 session id로 해당하는 세션을 찾아 클라이언트 정보를 확인

 

 

 

 

 

▶  Express.js의 Session & Session Store

 

-  express-session 패키지를 사용하여 간단하게 session 동작을 구현 가능

→  특별한 설정 없이 자동으로 session 동작 구현 (자동으로 session id를 클라이언트에 전달,  session id로 클라이언트 정보 확인)

 

 

 

Session Store

:  세션을 저장하는 저장소

 

express-session 패키지는 session을 기본적으로 메모리에 저장  (프로세스가 종료되면 메모리에 저장된 세션들도 전부 삭제된다)

→  따라서 현재 구현된 어플리케이션을 종료 후 다시 실행하면 모든 유저의 로그인이 해제됨

→  혹은 서버가 여러 대가 있을 경우 서버 간 세션 정보가 공유되지 않음

이미지출처 : 엘리스 Node.js와 MongoDB Ⅱ - 03 Express.js와 MongoDB로 웹서비스 만들기2 강의자료

 

 

 

 

 

 

▶  MongoDB를 Session Store로 사용하기

 

-  connect-mongo 패키지를 이용해 MongoDB를 session store로 사용 가능

-  connect-mongo 패키지는 express-session 패키지의 옵션으로 전달 가능

-  자동으로 session 값이 변경될 때 update되고 session이 호출될 때 find 함

 

 

-  connect-mongo 패키지를 사용해 express-session 설정 시 store 옵션에 전달하고 mongoUrl을 설정

-  세션데이터를 몽고디비에 저장하고 관리하는 기능을 자동으로 수행

const MongoStore = require('connect-mongo');

app.use(session({ 
    secret: 'SeCrEt', 
    resave: false, 
    saveUninitialized: true,
    store: MongoStore.create({ 
    	mongoUrl: 'mongoUrl',
    }),
}));

 

 

 

 

 

 

 회원과 게시글 연동하기 

 

▶  너무 길어져서 다음 포스팅으로..

 

2022.08.05 - [🔩 Back-End/🟩 노드 | Node.js] - [NodeJS] Express와 MongoDB로 웹서비스 만들기 - 2 (2)

 

[NodeJS] Express와 MongoDB로 웹서비스 만들기 - 2 (2)

Node.js Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. nodejs.org  회원과 게시글 연동하기 ▶ 회원-게시글 연동 기능 설명 1) 게시글 작성 시 로그인된 회원 정보를 작성자로..

limelbe.tistory.com

 

 

 

 


이 글은 엘리스 AI트랙 5기 강의를 들으며 정리한 내용입니다.

반응형
프로필사진
개발자 삐롱히

프론트엔드 개발자 삐롱히의 개발 & 공부 기록 블로그