-
Notifications
You must be signed in to change notification settings - Fork 3
트러블 슈팅 (Trouble Shooting)
JaeJune Ryu edited this page Jul 31, 2022
·
10 revisions
개발하면서 있었던 이슈와 고민해서 해결한 방법을 기록합니다.
- 문제 상황
- useEffect의 의존성 배열을 사용하여 해당 변수가 변할 때만 렌더링을 시키려고 할 때, useEffect 특성상 마운트될 때도 실행이 되어 문제가 발생하였습니다.
- 해당 코드에서 마운트될 때 토큰 유효성을 검증하고, 상태가 변하면 그에 맞게 페이지 이동을 기대했습니다.
- 그러나, 밑에 있는 useEffect가 마운트될 때에도 실행이 되어 Welcome 페이지를 거치고 피드 페이지로 이동하는 문제가 발생하였습니다.
const [isTokenValid, setIsTokenValid] = useState(false);
// 마운트 될 때 토큰 유효성 검증 + 유효성 상태 변경
useEffect(() => {
(async function checkTokenAvailable() {
try {
const accessToken = localStorage.getItem('accessToken');
const res = await axios.get('https://mandarin.api.weniv.co.kr/user/checktoken', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
(await res.data.isValid) && setIsTokenValid((state) => !state);
} catch (error) {
console.log(error);
}
})();
}, []);
// 토큰 유효 상태가 변하면 상태에 맞게 페이지 이동
useEffect(() => {
setTimeout(() => {
if (localStorage.getItem('accessToken') && isTokenValid) {
history.push('/home-empty');
} else {
history.push('/welcome');
}
}, 2000);
}, [isTokenValid]);
- 해결 방법
- 따라서, useEffect가 오직 의존성 배열 내부의 상태가 변할때만 실행되도록 코드를 수정했습니다.
const mount = useRef();
const [isTokenValid, setIsTokenValid] = useState(false);
useEffect(() => {
(async function checkTokenAvailable() {
try {
const accessToken = localStorage.getItem('accessToken');
const res = await axios.get('https://mandarin.api.weniv.co.kr/user/checktoken', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
(await res.data.isValid) && setIsTokenValid((state) => !state);
} catch (error) {
console.log(error);
setTimeout(() => {
history.push('/welcome');
}, 2000);
}
})();
}, []);
useEffect(() => {
if (!mount.current) {
mount.current = true;
} else {
setTimeout(() => {
if (localStorage.getItem('accessToken') && isTokenValid) {
history.push('/home-empty');
}
}, 2000);
}
}, [isTokenValid]);
- useEffect의 비동기적 특성을 해결하기 위해서 useLayoutEffect 훅을 활용하였습니다.
useLayoutEffect(() => {
setTimeout(() => {
if (localStorage.getItem('accessToken') && isTokenValid) {
history.push('/home-empty');
}
}, 2000);
}, [isTokenValid]);
- 문제 상황
- 아이디, 비밀번호 Input 태그 안에 모두 값이 들어가야 버튼을 활성화시키는 기능을 구현 도중에 문제가 발생했습니다.
- 다음과 같은 코드에서 setState 함수가 비동기적으로 실행이 되어서 동작이 한 단계씩 밀리는 문제가 생겼습니다.
- 아이디에 값을 입력하고, 비밀번호에 2글자를 입력해야 버튼이 활성화되는 문제가 발생했습니다.
const [id, setId] = useState('');
const [password, setPassword] = useState('');
const [isEmpty, setIsEmpty] = useState(true);
// ID, PW 동적으로 업데이트
const onHandleUserId = (e) => {
setId(e.target.value);
};
const onHandleUserPassword = (e) => {
setPassword(e.target.value);
};
if (id && password) {
setIsEmpty(false);
} else {
setIsEmpty(true);
}
- 해결 방법
- 따라서, useEffect를 이용하여 id, password가 변할때만 작동하도록 구현하였습니다.
useEffect(() => {
if (id && password) {
setIsEmpty(false);
} else {
setIsEmpty(true);
}
}, [id, password]);
- 문제 상황
- Redux Store에서 유저정보를 관리하는데, 새로고침 시 전역으로 관리하고 있는 상태 데이터가 초기화되는 문제가 발생하였습니다.
- 해결 방법
- Redux Store와 로컬 스토리지를 연결해주는 Redux-persist 라이브러리를 도입하여 이를 방지했습니다.
// src/store/index.js
import { combineReducers } from 'redux';
import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import UserInfoReducer from './UserInfo';
import PostInfoReducer from './PostInfo';
import OtherUserInfoReducer from './OtherUserInfo';
import ProductInfoReducer from './ProductInfo';
const persistConfig = {
key: 'UserInfo',
storage,
whitelist: ['UserInfoReducer', 'PostInfoReducer', 'OtherUserInfoReducer', 'ProductInfoReducer'],
};
const rootReducer = combineReducers({
UserInfoReducer,
PostInfoReducer,
OtherUserInfoReducer,
ProductInfoReducer,
});
export default persistReducer(persistConfig, rootReducer);
// src/index.js
const root = ReactDOM.createRoot(document.getElementById('root'));
const store = createStore(rootReducer);
const persistor = persistStore(store);
root.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>
);
- 문제 상황
- 서버와의 통신을 통해서 데이터를 받은 뒤에 화면에 렌더링을 하기 때문에 사용자 경험 측면에서 나쁜 문제가 발생했습니다.
- 해결 방법
- 당장 렌더링 최적화에 대한 공부는 늦었다고 생각해서 로딩 중일때 사용자에게 알리는 컴포넌트를 만들어 사이에 렌더링했습니다.
- 렌더링 최적화에 대한 공부를 진행하여 추후에 리팩토링 예정입니다.
- 문제 상황
- 하단 탭 메뉴를 클릭해 프로필 페이지로 이동 시 데이터를 요청해 받아 올 수 있도록 버튼 동작으로 서버 요청 로직을 작성하였습니다.
- 게시글 업로드 및 삭제 시 변경된 게시글 정보가 바로 업데이트되지 않고 다른 페이지로 이동했다 돌아와야만 새로운 데이터 정보가 렌더링되는 문제가 발생하였습니다.
- TabMenu에서 프로필, 게시글, 상품 정보를 불러오고 있어 탭 메뉴를 클릭할 때만 서버로부터 데이터를 받아 오고 있었습니다.
- 해결 방법
- 게시글 정보를 불러오는 컴포넌트로 함수를 옮겨 데이터의 변경이 있을 때마다 업데이트되도록 구현하였습니다.
- 문제 상황
- 위에서 데이터를 업데이트해 오는 시점은 정상적으로 맞춰졌으나, 게시글 정보를 가지고 오는 배열인
postList
가 undefined 값을 가지며 데이터를 불러오지 못하는 문제가 발생하였습니다. - 한 컴포넌트 내에서 useSelector와 dispatch를 한 번에 동작시켜 dispatch 이전에 선언된 useSelector가 먼저 동작하며 빈 배열을 가지고 오게 되었습니다.
- 위에서 데이터를 업데이트해 오는 시점은 정상적으로 맞춰졌으나, 게시글 정보를 가지고 오는 배열인
const { UserAccount } = useSelector((state) => state.UserInfoReducer);
const { postList } = useSelector((state) => ({
postList: state.PostInfoReducer.postList
}));
useEffect(() => {
getPost();
}, []);
const getPost = async () => {
try {
const accessToken = localStorage.getItem("accessToken");
const res = await axios.get(
`https://mandarin.api.weniv.co.kr/post/${UserAccount}/userpost`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-type": "application/json"
}
}
);
const postList = res.data;
dispatch({ type: "GET_POST", postList });
} catch (error) {
console.log(error);
}
};
- 해결 방법
- 부모 컴포넌트인 UserProfile에서 dispatch로 리듀서 함수를 동작시키고, 자식 컴포넌트인 PostCard에서 useSelector를 사용해 게시글 데이터를 가져오도록 분리하였습니다.
// PostCard.jsx
const dispatch = useDispatch();
useEffect(() => {
(async function getProductAndPost() {
try {
const accessToken = localStorage.getItem("accessToken");
const res = await axios.get(
`https://mandarin.api.weniv.co.kr/post/${UserAccount}/userpost`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-type": "application/json"
}
}
);
const postList = res.data.post;
dispatch({ type: "GET_POST", postList });
} catch (error) {
console.log(error);
}
})();
}, []);
// UserProfile.jsx
const { postList } = useSelector((state) => ({
postList: state.PostInfoReducer.postList,
}));
- 문제 상황
- 게시글 수정 페이지로 이동 시 기존 작성 내용을 화면에 불러오고 싶었으나 모든 게시글 수정 화면에서 가장 최근에 작성된 게시글 정보를 불러오는 문제가 발생하였습니다.
- 현재 게시글의
postId
로 서버에 데이터를 요청해 정상 응답은 받고 있었으나 렌더링이 안 되는 상황이었습니다. - state 값을 직접 변경하려고 시도해 로직이 올바르게 동작하지 않았습니다.
const [updateText, setUpdateText] = useState('');
const [updateImage, setUpdateImage] = useState([]);
const getPost = async (updateText, updateImage) => {
try {
const token = localStorage.getItem('accessToken');
const res = await axios.get(`https://mandarin.api.weniv.co.kr/post/${postId}`, {
headers: {
Authorization: `Bearer ${token}`,
'Content-type': 'application/json',
},
});
updateText = res.data.post.content;
updateImage = res.data.post.image;
} catch (error) {
console.log(error);
if (error.response.data.message === '존재하지 않는 게시글입니다.') {
alert('존재하지 않는 게시글입니다.');
}
}
};
- 해결 방법
- state 값을 직접 변경하던 방식에서
setUpdateText
,setUpdateImage
를 사용해 상태를 변경하도록 수정하였습니다.
- state 값을 직접 변경하던 방식에서
const [updateText, setUpdateText] = useState('');
const [updateImage, setUpdateImage] = useState([]);
const getPost = async () => {
try {
const token = localStorage.getItem('accessToken');
const res = await axios.get(`https://mandarin.api.weniv.co.kr/post/${postId}`, {
headers: {
Authorization: `Bearer ${token}`,
'Content-type': 'application/json',
},
});
setUpdateText(res.data.post.content);
setUpdateImage(res.data.post.image.split(','));
} catch (error) {
console.log(error);
if (error.response.data.message === '존재하지 않는 게시글입니다.') {
alert('존재하지 않는 게시글입니다.');
}
}
};
- 문제 상황
- jsx파일의 Return값 Fragment에서 비동기로 데이터 api가 불러와지지 않음
- 해결방법
- 경로는 제대로 불러온줄 알았는데 경로를 제대로 불러오지 못한것 부터가 시작이었습니다. useEffect로 axios를 불러오고 setPostList 함수에 useState를 담아 postList에 해당 리스트들을 출력할 계획이었습니다. 맨처음에는
setPostList(res.data)
로 지정해두면 밑에 뜨는 data의 값들이 불러와지고, 이걸 이용해 useState를 집어넣으려 했습니다만 뒤에 posts까지 잡아 넣어줘야 바로 경로를 타고 들어갈 수 있다는 오류를 발견할 수 있었습니다. - 해당 post값까지 불러와야 map으로 임의의 매개변수로 돌려지는 값에 대응할때 오류없이 경로를 지정해 줄 수 있었습니다.
- map을 사용함에 있어서도 key값 사용함에 있어서, 모든 반복되는 값에 대응하는 것이 아닌 변경이 있는 값에다만 지정해 주는 것이라는것 또한 다시한번 깨달을 수 있었습니다.
- 경로는 제대로 불러온줄 알았는데 경로를 제대로 불러오지 못한것 부터가 시작이었습니다. useEffect로 axios를 불러오고 setPostList 함수에 useState를 담아 postList에 해당 리스트들을 출력할 계획이었습니다. 맨처음에는
useEffect(() => {
(async function getPost() {
try {
const accessToken = localStorage.getItem('accessToken');
const res = await axios.get(`https://mandarin.api.weniv.co.kr/post/feed`, {
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-type': 'application/json',
},
});
setPostList(res.data.posts);
setIsRendered(true);
} catch (error) {
console.log(error);
}
})();
}, []);