이해한대로 그림화 우선... 추후 수정 가능
사용자가 로그인 시
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>
)
사용자가 로그인 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),
]);
}
http://localhost:8080/api/user/login api 요청에 대한 처리
...
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);
});
...
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();
};
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);
}
}));
};
프론트엔드에서 사가를 이용해 유저 로그인을 위한 요청을 보내고
백엔드에서 패스포트를 이용해 프론트에서 보내준 쿠키 해석 후 사용자 정보 식별