이슈노트 7
이슈 내용
사이드프로젝트에 백엔드서버를 Express와 Mongoose를 이용해 만들 계획을 하고 구글링을 통해 유투브 채널 CodingTheSmartWay.com의 The MERN Stack Tutorial을 알게 되었었는데 드디어 마지막 컨텐츠가 업데이트가 되어서 The MERN Stack Tutorial - Building A React CRUD Application From Start To Finish - Part 4를 통해 튜토리얼을 진행중 개발자도구 콘솔창에 워닝코드가 발생함.
원인
todos-list.component에 라이프사이클 메서드중 componentDidMount에서 axios를 통하여 데이터를 요청했는데 componentWillUnMount를 써서 수정을 하거나 취소를 해줘야 메모리 누수가 없다는 거 같다.
과정
-
워닝 코드 확인
-
구글링
방법
-
(https://github.com/material-components/material-components-web-react/issues/434)
-
기존에는 isMounted()를 지원하였으나 현재는 안티 패턴이라고 한다.
해결
- 나는 1번으로 해결하였다.
export default class TodosList extends Component {
_isMounted = false; // 추가코드
constructor(props) {
super(props);
this.state = { todos: [] };
}
componentDidMount() {
this._isMounted = true; // 추가코드
axios
.get("http://localhost:4000/todos/")
.then(response => {
if (this._isMounted) {
this.setState({ todos: response.data });
}
})
.catch(error => {
console.log(error);
});
}
// 중략
componentWillUnmount() {
this._isMounted = false; // 추가코드
console.log("componentWillUnmount");
}
// 중략
}
이유
방법 2번대로 Promise로 구현해보고 싶었으나 내가 느끼기에는 코드가 너무 길어보여서 비효율적이라는 생각이 들었다. 또한 내생각대로 제대로 되는거 같지도 않아 (Promise를 잘 몰라서 그런걸지도) 좀 더 글을 검색해보다가 리액트 코리아 이 글의 이재호님의 댓글을 보고 결정하게 되었다.
최종 과정과 해결
우선 멘토님이 내 포스팅을 보시고 코드리뷰 후 async/await로 비동기를 동기식코드처럼 작성하는 코드를 알려 주셨고 그때의 문제의 원인은 setState가 비동기안에 있어서 발생한 문제라 생각했었다. 그래서 이 코드면 _isMounted는 필요가 없겠구나 생각했다.
다음날 나는 멘토님이 알려주신대로 코드를 바꿔보았다.
componentDidMount() {
this.getTodos();
}
getTodos = async () => {
try {
const response = await axios.get("http://localhost:4000/todos/");
this.setState({ todos: response.data });
} catch (error) {
console.log(error);
}
};
componentDidUpdate() {
this.getTodos();
}
componentWillUnmount() {
console.log("componentWillUnmount");
}
사실 맨 처음 코드에서 생략한 부분이 있었는데 튜토리얼시 componentDidMount()와 componentDidUpdate()코드가 똑같았었다.그걸 이번엔 중복을 줄여보려고 getTodos라는 함수를 만들어서 각각 넣어주었다. 그런데 결과는 처음 마주했던 워닝 코드가 또 뜨는 것이다. 😢
또 _isMounted를 넣어주니 워닝 코드는 사라졌으나 찜찜한 마음으로 멘토님께 메일을 보냈고 멘토님께서 내가 놓친 부분을 피드백으로 알려주셨다. 문제의 원인은 component DidUpdate()에 setState였다!
워낙 리액트 초보(공부한지 1Mㅋ)에 한동안 리액트코드를 안봐서 그런지 라이프사이클도 다 까먹어서 결국 난 또 삽질을 하고 말았다.😐
멘토님의 피드백을 토대로 코드를 수정하니 이제 _isMounted와 componentWillUnmount()를 제거할 수 있었다! 😁
[최종코드]
export default class TodosList extends Component {
constructor(props) {
super(props);
this.state = { todos: [] };
}
componentDidMount() {
this.getTodos();
}
getTodos = async () => {
try {
const response = await axios.get("http://localhost:4000/todos/");
this.setState({ todos: response.data });
} catch (error) {
console.log(error);
}
};
componentDidUpdate(prevProps) {
return this.state.todos !== prevProps.todos;
}
todoList() {
// 중략
}
render() {
// 중략
}
삽질 이유를 정리해보자면
- 워닝코드에서 update에 대한 메시지가 있었는데 내가 보고싶은 것만 보고 그부분에 대해서만 해결하려고 했다. (영알못이라 더 그런거 같음)
- 유투브채널 강의자를 너무 믿었다. (?)
- componentDidMount()와 componentDidUpdate()코드가 같은게 계속 마음에 걸리긴 했었다. 하지만 코딩 관련아카데미도 운영하는데 코드가 잘못 될리 없다고 생각하기도 했다 “세바스찬아재 이러깁니까” 😠
- 제일 결정적인건 내가 리알못이라는거 라이프사이클을 이해못한 내가 제일 잘못했다 ㅋ;