로그인 동작 원리

sykim·2019년 12월 23일
0

이해한대로 그림화 우선... 추후 수정 가능
image.png

front

사용자가 로그인 시

http://localhost:8080/api/user/login api 요청을 보낸다(post)

components/LoginForm.js :

로그인 버튼 클릭시 LOG_IN_REQUEST 리듀서가 진행을 실행한다.
id 는 userId, password 는 password 값으로 data에 담긴다.

...
const LoginForm = () => {
...
    const onSubmitForm = useCallback((e) => {
        e.preventDefault();
        dispatch({
            type: LOG_IN_REQUEST,
            data: {
                userId: id, 
                password
            }
        });
    }, [id, password]);
    return(
        <Form onSubmit={onSubmitForm}>
            <div>
                <label htmlFor="user-id">아이디</label>
                <br />
                <Input name="user-id" value={id} onChange={onChangeId} required />
            </div>
            <div>
                <label htmlFor="user-password">비밀번호</label>
                <br />
                <Input name="user-password" value={password} onChange={onChangePassword} type="password" required />
            </div>
            <div>
                <Button type="primary" htmlType="submit" loading={isLoggingIn}>로그인</Button>
                <Link href="/signup"><a><Button>회원가입</Button></a></Link>
            </div>
        </Form>
    )

sagas/user.js :

  • 사용자가 로그인 api 로 요청을 하면 응답을 한다.

  • 그 정보는 loginData 에 들어있다.

  • 그 응답된 정보는 const result = yield call(loginAPI, action.data); 에서 action.data로 받고 LOG_IN_SUCCESS 리듀서 단계에서 data: result.data 가 된다.

axios.defaults.baseURL = 'http://localhost:8080/api';
function loginAPI(loginData){
    // 서버에 요청을 보내는 부분
    // loginData 에는 userId 와 password 가 들어있을 것
    // 요청의 본문에 실려서 서버로 감
    // 서버에서는 req.body.userId, req.body.password
    // 1. 요청을 하면 응답을 해주는데  
    return axios.post('/user/login', loginData);
}
function* login(action){
    try {
        // 2. 그 응답은 로그인된 사용자 정보
        const result = yield call(loginAPI, action.data);
        // 서버 호출 끝나면 로그인 성공
        yield put({
            type: LOG_IN_SUCCESS,
            data: result.data,
        });
    } catch (e) {
        console.error(e);
        // 로그인 실패
        yield put({
            type: LOG_IN_FAILURE
        });
    }
}
function* watchLogin(){
    yield takeEvery(LOG_IN_REQUEST, login)
}
export default function* userSaga() {
    yield all ([
        fork(watchLogin),
    ]);
}

back

http://localhost:8080/api/user/login api 요청에 대한 처리

routes/user.js :

...
router.post('/login', (req, res, next) => { 
	// 1. 해당 경로로 요청이 오면 패스포트로 authenticate를 진행한다.
    // passport/index.js, passport/local.js 진행 후 
    // 성공 시 리턴 받은 user
    passport.authenticate('local', (err, user, info) => {
        if (err) {
            console.error(err);
            return next(err);
        }
        if (info) {
            return res.status(401).send(info.reason);
        }
        // 2. authenticate 성공시 req.login 리턴
        return req.login(user, (loginErr) => {
            if (loginErr) {
                return next(loginErr);
            }
        	// 3. password 삭제한 유저 기록 json 으로 보냄
            const filteredUser = Object.assign({}, user.toJSON());
            delete filteredUser.password;
            return res.json(filteredUser);
        });
    })(req, res, next);
});
...

passport/index.js :

  • 사용자가 로그인을 하면 프론트에서 받은 쿠키를 패스포트를 이용해 사용자 정보를 쿠키가 가진 id 값을 이용하여 식별한다.
const passport = require('passport');
const db = require('../models');
const local = require('./local');
module.exports = () => {
    passport.serializeUser((user, done) => {
        return done(null, user.id);
    });
    passport.deserializeUser( async (id, done) => {
        try {
            const user = await db.User.findOne({ 
                where : { id },
            });
            // 서버가 id 값으로 찾아온 유저 정보는 req.user 에 저장
            return done(null, user);
        } catch (e) {
            console.error(e);
            return done(e);
        }
    });

    local();
};

passport/local.js :

  • LocalStrategy을 이용해 가져온 사용자 정보를 userId, password로 설정
const passport = require('passport');
const { Strategy : LocalStrategy } = require('passport-local');
const bcrypt = require('bcrypt');
const db = require('../models');

module.exports = () => {
    passport.use(new LocalStrategy({
        usernameField: 'userId',
        passwordField: 'password',
    }, async (userId, password, done) => {
        try {
            const user = await db.User.findOne({
                where: {userId}
            });
            if (!user) {
                return done(null, false, { reason : '존재하지 않는 사용자입니다 '});
            }
            const result = await bcrypt.compare(password, user.password);
            if (result) {
                return done(null, user);
            }
            return done(null, false, { reason: '비밀번호가 틀립니다 ' })
        } catch (e) {
            console.error(e);
            return done(e);
        }
    }));
};

프론트엔드에서 사가를 이용해 유저 로그인을 위한 요청을 보내고
백엔드에서 패스포트를 이용해 프론트에서 보내준 쿠키 해석 후 사용자 정보 식별

profile
블로그 이전했습니다

2개의 댓글

comment-user-thumbnail
2019년 12월 23일
  • 서버는 로그인 여부를 프론트에서 보내는 쿠키로 판단한다.
답글 달기
comment-user-thumbnail
2019년 12월 23일

passport : 폼에서 전달한 id, pw로 req.user 만들기
-> model(user) : 데이터 스키마
-> route(user) : passport에서 인증 작업 시작

답글 달기