로그인한 후 /beans 페이지에 접속하고, 로그아웃한 후 다른 아이디로 로그인했을 때 /beans 페이지가 리렌더링되지 않는 문제가 있었다.
문제 상황
app.js에서 useEffect를 통해 랜더링될 때마다 isLogin 상태를 확인하고 setLogin하도록 되어있었다.
그렇기 때문에 isLogin을 props로 받는 하위에 있는 컴포넌트들도 isLogin 상태가 변화될 때마다 리랜더링될 것이라고 예상하였다.
예를 들면,
test1으로 로그인하고 beans페이지로 간 후, beans페이지에서 로그아웃을 하고 test2로 로그인을 하였을 때, test2가 좋아하는 원두목록이 나오도록 beans페이지가 리랜더링되는 것이다.
결과,
하지만, 예상과는 달리, beans페이지는 리랜더링되지 않고, 새로고침을하거나 다른페이지를 들어갔다가 되돌아와야 상태가 변화되었다.
나의 착각...
isLogin이 상태변화되면 props로 받는 하위컴포넌트가 리렌더링되는 것은 맞다. 하지만, 하위 컴포넌트의 state를 변경해 주지 않았기 때문에, 보여지는 beans페이지가 그대로였던 것이다.
그래서, 해결은
각 컴포넌트에 isLogin를 props로 전달하여 상태 변화에 따라 useEffect가 실행되도록 하고, loginId에 해당하는 값을 요청하여 state값을 변경해주니 제대로 동작하는 것을 확인할 수 있었다.
굉장히 간단한 문제였고, react의 기본적인 개념이었는데, 많은 state, props로 구현하다보니 기본적인 부분을 놓치게 되었던 것 같다.
export default function Beans({ loginId }) {
const [allBeanName, setAllBeanName] = useState([]);
const [beans, isLoading, setBeans] = useLoading([], getAllBeans, loginId);
const [openModal, cardBeanInfo, cardPostInfo, beanModal, closeModal] = useBeanModal(beans);
...}
export function useLoading(inital, httpService, arg) {
const [data, setData] = useState(inital);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
setIsLoading(true);
httpService()
.then((res) => {
setData(res);
setIsLoading(false);
})
.catch((err) => {
console.log(err.response);
});
}, [arg]);
return [data, isLoading, setData];
}
회원가입 과정에서 아이디를 중복검사하는 단계가 있다. 최종적으로는 버튼을 눌러서 중복판별 요청을 보내도록 구현하였지만, 글자를 입력할 때마다 실시간으로 중복판별할 수 있는 로직도 괜찮다고 생각하였다.
하지만 한 글자를 쓸 때마다 중복판별을 위해 서버요청을 보내는 것은 비효율적인 것 같다.
네이버의 경우를 보니 onBlur가 되었을 때 한번 서버 요청을 보내는 것을 확인하였고, 이처럼 한번, 혹은 적은 수의 서버요청으로 중복판별하는 방법도 있었다.
이 프로젝트에서 로그인 부분은 내 파트가 아니기때문에 원하는 방향으로 진행하지는 못했지만, 다음 프로젝트 때에는 다음과 같은 방법으로 구현하면 좋을 것 같다.
setTimeout으로 판별하는 방법이 있을 것이다. 즉 input 창이 onFocus되고 user가 onkeydown을 멈추고 1초 후에 한번 요청을 보내는 것이다. 이렇게하면 적은 수의 요청으로 중복판별을 할 수 잇다.
네이버처럼 onblur되었을 때 하는 것이다. 하지만 user가 이를 모르고 계속 중복판별을 기다리며 onblur하지 않을 수 있는 문제점이 있기는 한 것 같다.
//TODO 모든 원두 가져오기
export const getAllBeans = async () => {
const res = await axios.get(`${http}/bean/all-beans`);
return res.data.beanList;
};
//TODO 필터링 원두 가져오기
export const getFilterBeans = async (data) => {
const res = await axios.get(`${http}/bean/filter-beans`, { params: { ...data } });
return res.data;
};
백엔드 분과 대화를 하다가 나온 주제이기도 하다.
모든 원두를 가져오는 것과 필터링한 원두를 가져오는 rest api의 엔드포인트를 굳이 분기할 필요가 없었고, 분기를 하니 더 번거로운 작업이었다고 하였다.
즉, GET 원두는 /bean로 하고, params를 통해 filter인지 아닌지를 판별할 수 있기 때문에 굳이 엔드포인트를 나눌 필요는 없었다.
{
postCotents: {
title: 'this is titlethis is titlethis is title',
imageUrl: '/asset/beans/bean4.jpg',
beanRatio: {
자바: 4,
케냐: 2,
'탄자니아 킬리만자로 AA': 4,
'탄자니아 킬리만자로 A': 4,
'탄자니아 킬리만자로': 4,
},
water: 15,
waterTemp: 80,
content:
'게시물 내용 이것은 게시물 내용 이것은 게시물 내용 이것은\n 게시물 내',
userId: 'meme',
createAt: '2020-12-20 시분초',
},
comments: [
{
userId: '작성자id입니다',
commentId: 1,
comment: '댓글 내용댓글 내용댓글 내용댓글 내용',
createAt: '2020-12-20',
},
{
userId: 'userId',
commentId: 2,
comment: '댓글 내용댓글 내용댓글 내용댓글 내용',
createAt: '2020-12-20',
},
{
userId: '작성자임',
commentId: 3,
comment: '댓글 내용댓글 내용댓글 내용댓글 내용댓글 내용댓글 내용댓글 내용댓글 내용댓글 내용',
createAt: '2020-12-20',
},
{
userId: '작성자id입니다',
commentId: 4,
comment: '댓글 내용',
createAt: '2020-12-20',
},
],
};
http 배포로 진행하려 했으나 구글 크롬의 2월 업데이트 내용(쿠키 전송 이슈) 때문에 https 배포로 전환하게 됐고, 그것때문에 여러 CORS 에러가 발생
sameSite : none => 항상 쿠키를 보내줄 수 있다. 다만 쿠키 옵션 중 Secure 옵션이 필요하다
secure : https 프로토콜을 이용하여 통신하는 경우에만 쿠키를 전송
app.use(
cors({
origin: [
'https://localhost:3000', 'http://localhost:3000',
'https://www.beanus.tk', 'http://www.beanus.tk',
'https://practic.beanus.tk', 'http://practic.beanus.tk'
],
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
})
);
sendAccessToken: (res, accessToken) => {
res.cookie('accessToken', accessToken, {
httpOnly: true,
secure: true,
sameSite: 'None',
expires: new Date(Date.now() + 1000 * 60 * 60 * 48),
});
},