[React/Node] 마켓컬리 클론코딩 10 - 로그인/로그아웃
- 로그인/로그아웃
(로그아웃 버튼 css를 까먹었다..)
처음해보는 작업이라 그래도 좀 오래 걸릴거라 생각했지만, 예상보다 많이 걸린 작업이었다.
온갖 에러와 정보 사이에서 어찌저찌 만들기는 했지만 확신이 없다.
잘못 이해한 부분이 있을지도 모르지만 일단은 그 과정에서 배운 것들을 차례차례 정리해보려한다.
로그인 관리 방식은 세션을 이용해서 구현하기로 하였다.
전에 했던 로그인 방식은 단순히 로그인 정보를 가지고 일치하는지를 판단하는것이 전부였는데, 이는 보안적으로도 문제가 있지만 새로고침이나 페이지 이동을 하면 로그인 상태가 유지가 안된다는 문제가 있었다.
상태 유지를 위해서 사용할 수 있는 방법에 쿠키가 있는데, 쿠키는 클라이언트 측에 저장하여 사용하며 새로고침등을 해도 삭제되지 않는다.
하지만 서버에서 관리되지 않기 때문에 탈취나 변조가 될 수 있는 위험이 존재하기 때문에 세션을 이용해 이 부분을 보완한다. 그렇기 때문에 쿠키와 세션을 이용해 관리할 수 있는데, 세션에 로그인 상태를 담고, 클라이언트에게 세션ID를 전달해 이를 쿠키에 저장하는 방식을 통해 관리할 수 있다. 이 방법은 안전하지만 서버와의 통신이 필요하다보니 상대적으로 느리다는 단점이 있긴 하다.
기존의 로그인 페이지를 보면 로그인 폼으로부터 전달받은 데이터를 DB와 대조시켜 로그인할 수 있도록 하였는데, 이제 대조 이후에 로그인 상태 처리에 변화를 줄 것이다.
로그인 정보가 일치하면 세션에 로그인이 되었음을 의미하는 값(isLoggedIn)을 true로 처리하고, 닉네임(ss_nickname)을 저장하도록 한다.
이제 이 세션 ID를 쿠키로 저장하고, 필요할때마다 이 ID값을 통해 이용할 수 있도록한다.
<index.js>
...
const session = require('express-session');
const FileStore = require('session-file-store')(session);
const corsOptions = {
origin: 'http://localhost:3000', // 클라이언트 도메인
credentials: true, // 인증 정보 포함 허용
};
...
app.use(cors(corsOptions));
app.use(session({
secret: 'kurly-jung',
resave: false,
saveUninitialized: true,
store: new FileStore(),
cookie: {
maxAge: 1000*60*10,
},
}));
...
일단 세션 기본 설정부터 한다.
세션을 사용하기 위해선 모듈을 설치해주어야한다.
npm i express-session
모듈을 설치하고 나면 session 옵션을 설정해주어야 하는데, 대충 위와 같이 해주면 된다.
secret은 비밀번호 같은 개념인데 일단 개인 프로젝트이니 마음대로 설정하였다.
resave는 세션에 변경이 없어도 저장하는 옵션이니 자원을 아끼기 위해 false로 처리한다.
saveUninitialized를 true로 설정하면 내용이 없는 상태의 값이 세션에 저장된다.
간단한 작업이지만, 기존에 서버와 연결하는 작업을 할 때 해두었던 cors 옵션이 이미 존재하기 때문에 무심코 지나치는 바람에 오랜 시간이 낭비되었었다.
여기서는 이제 쿠키를 이용하여야하는데, 도메인이 다른 서버끼리 통신을 할 때 쿠키를 이용한다면 cors 옵션에 credentials: true를 포함하여주어야한다.
<loginHandler.js>
const db = require('./DBinfo');
const postLoginList = (req, res) => {
const { user_id, user_pw } = req.body;
const query = "SELECT * FROM kurly.user WHERE user_id = ? AND user_pw =?";
const params = [user_id, user_pw];
db.query(query, params, (err, result) => {
if (err) throw err;
if (result.length > 0) {
req.session.is_logined = true;
req.session.ss_nickname = result[0].nickname;
req.session.save(function () {
res.json({ redirectPath: '/', ss_nickname: result[0].nickname });
});
}
});
};
module.exports = postLoginList;
로그인 폼으로부터 전달받은 데이터를 처리할 부분도 만들어준다.
SELECT문을 통해 확인하고 로그인에 성공하면 session.***문을 통해 세션에 데이터를 저장한다.
추가로 로그인에 성공하면 홈페이지로 이동시키기 위해 redirectPath: '/'를 전달한다.
<Login.js>
...
const checkUser = (e) => {
e.preventDefault();
axios.post('http://localhost:8000/api/login', loginData, {
withCredentials: true
})
.then((response) => {
sessionStorage.setItem('isLoggedIn', true);
sessionStorage.setItem('ss_nickname', response.data.ss_nickname);
const redirectPath = response.data.redirectPath;
navigate(redirectPath);
})
.catch((error) => {
console.error(error);
});
}
...
<Header.js>
...
const isLoggedIn = sessionStorage.getItem("isLoggedIn") === 'true';
const nickname = sessionStorage.getItem("ss_nickname");
let loginBar = null;
if (isLoggedIn) loginBar = <Logged nickname={nickname}></Logged>;
else loginBar = <None></None>;
...
function Logged({ nickname }) {
const navigate = useNavigate();
const logout = () => {
axios.post('http://localhost:8000/api/logout')
.then((response) => {
sessionStorage.removeItem("isLoggedIn");
sessionStorage.removeItem("ss_nickname");
const redirectPath = response.data.redirectPath;
navigate(redirectPath);
console.log(redirectPath);
})
.catch(error => {
console.error(error);
});
};
return (
<>
<div className="user_join">
<a href="/mypage">{nickname}님</a>
</div>
<div className="user_login">
<button onClick={logout}>로그아웃</button>
</div>
</>
)
}
function None() {
return (
<>
<div className="user_join">
<a href="/signup">회원가입</a>
</div>
<div className="user_login">
<a href="/login">로그인</a>
</div>
</>
)
}
...
로그인 상태 관리는 해결되었으니 헤더의 로그인/로그아웃 상태를 표시해줄 수 있도록 수정한다.
<logoutHandler.js>
const postLogout = (req, res) => {
req.session.destroy(err => {
if(err){throw err;}
res.json({ redirectPath: '/' });
})
};
module.exports = postLogout;
로그아웃은 간단하게 세션을 삭제하는 것으로 처리하도록 하였다.
이렇게 해서 로그인/로그아웃은 세션을 통해 구현하였다.
이제 다음으로는 메일 인증 서비스 구현을 해볼 생각이다.
https://memo-code.tistory.com/48
마켓컬리 클론코딩 11 - 장바구니 페이지
장바구니 페이지 장바구니 페이지에 기본적으로 필요한 기능은 '장바구니 담기(생성)'와 '장바구니 보기(읽기)'이다.장바구니를 보려면 일단 상품을 추가해야하기에 상품 상세 탭에서 '장바
memo-code.tistory.com