게시판 구현 중 리액트 리렌더링 방지! 노란선
첫번째 문제는
인터넷의 한 게시글을 통하여 게시판을 제작해보는도중 몇몇 요소에 노란색선이 생겨있었다.
내가 css를 넣었나싶어 찾아봐도 없었고, 개발자도구를 끄면 사라져서 난감하였다.
알고보니 리액트 개발자도구에서 Highlight updates when components render. 에 체크가 되있을 시
렌더링되는 요소를 하이라이트해주는것인데, 이 요소들이 헛된 렌더링을 계속하고있기때문에
노란색선이 나타나고 있는것으로 짐작하였다.
두번째 문제는 공부하던 게시물에선 입력까지 포스팅이 안되어있어서 직접 수정기능 구현하였는데,
수정을 누르니 ck에디터가 준비될 시 나오는 콘솔이 3번이나 뜨는것이다.
이 사태를 해결하기 위해서 우선 리렌더링이 일어나는 상황이 무엇인지 알아봐야했다.
state 변경이 있을 때
- react 에서 유동적인 데이터를 저장하기 위해서 state 라는 것을 이용한다. 이때 state 값을 바꿔주기 위해서는 state 를 직접 조작해서는 안되고 setState() 메서드를 이용해 주어야한다. 왜냐하면 리액트는 state 의 변경이 감지되면 리렌더링을 해주는 데 메서드를 사용하지 않고 직접 바꿔주게 되면 리액트가 state 의 변경을 감지하지 못하게 된다.
새로운 props이 들어올 때
- 전달받은 props 값이 업데이트 됬다면 리 렌더링 된다.
부모 컴포넌트가 렌더링 될 때
- 새로운 prop 이 들어오지 않더라고 부모컴포넌트가 리렌더링 된다면 자식컴포넌트 역시 리렌더링이 된다.
출처 https://seungddak.tistory.com/109
위 세가지의 이유 중 하나거나, 그 이상이라는것이다.
그걸 알아보기위해선 이제 짜놓은 코드를 확인해봐야한다.
노란색선이 표시되고있는 펄스는, 부모컴포넌트(App)의 상태값 showModal의 boolean값을 받아온다.
true일 경우 modal에 할당된 div를 출력하고, false일 경우 "펄스"라는 문자열을 출력하는 삼항연산자로 이루어져있다.
그저 눈으로만 읽어서 정확한 답을 찾는건 나에겐 어렵다.
나는 보통 코드의 이곳저곳을 뚝딱거리며 AB테스트를 백번이고 천번이고 하는 스타일이다.
그런 나에게 먼저 눈에 보인건 모달창이 렌더링 됐을 시 HTML 태그의 구성이다.
<div>
<div style={Backgrounds}>
<div>
<div className='form-wrapper'>
//....ck에디터 코드
</div>
</div>
//....수정sumit 버튼
</div>
</div>
이런식으로 쓸모없는 div태그로 계속해서 감싸져있었던것이다.
보통 이런 마크업언어 정리는 최종적으로 하는편이었는데, 리액트는 개발과정에서 이를 알려주니
고마웠다. 아무튼간에 위에 보이는 쓸모없는 태그들을 제거해보자.
modal의 한쌍의 div를 제거하고, return에서 div를 <></>로 변경하였다.
놀랍게도 이것만으로 modal요소의 노란선은 사라졌다.
정확히는 return에서 <div>를 fragments로 바꾸는 순간 사라졌다.
React.memo(DataChange)로도 해결된다.
행복하지만 슬프게도 왜 해결되었는지 모른다. 알아가보자
우선 짧게 Fragment에 대해 다시 보자.
Fragments
- 컴포넌트가 여러 엘리먼트를 return 할때 jsx규칙상 하나의 태그로 묶어서 return 해줘야 한다.
이때 fragments를 사용하면 dom에 별도의 노드를 추가하지 않고 여러자식을 그룹화 할 수 있다.
- <React.Fragment> 를 축약하여 <>로 사용 가능
DataChange 컴포넌트가 return하는 요소는 조건에 따라서 modal과, "펄스"뿐이었다.
나는 모달창이 화면에서 하나의 요소로 구성되있을줄 알았다.(당연히 그게 정상적인 코드일테니까)
근데 그것이 아니었다.
아래는 부모컴포넌트에서 게시물을 map()으로 뿌려주는 코드다.
컴포넌트가 포함되어있어, 게시물갯수 만큼 DataChange 컴포넌트가 생성된다.
DataChange컴포넌트는 게시물을 수정을 눌렀을 때 한번만 등장하면 되는데,
그냥 아무 의미도없이 여러개가 렌더링되고있는 상태였다.
이 컴포넌트를 그룹화시키지않고 방치해두니 노란선이 생기는것으로 짐작된다.
이는 아직 리액트의 작동방식을 정확히 파악하지 못하여 확신을 할 수가 없어 이쯤에서 마무리한다.
이번엔 Movie Review에 잡히는 노란선을 해결해보자.
개발자도구로 커서를 올려보니, 가장 상위계층위 부모인 div요소에 생기고있었다.
우선 어떤 조건에서 노란선이 사라지는지, App을 구성하는 코드들을
하나씩 삭제해보고 저장하여 확인하는것을 반복하였다.
위의 useEffect코드를 제거하면 노란선이 사라지는것을 알 수 있었다.
더 정확히는 deps의 viewContent만 제거해도 노란선은 사라졌다.
이 코드의 기능은, useEffect의 기본기능대로 첫 렌더링 시 한번 무조건 실행되어, 게시물을 출력되게끔 data를 넘겨주고
그 후로는 viewContent의 상태값이 바뀔때마다 실행된다.
실행 시 api에서 data를 받아와서 setViewContent에 할당해주는것이고, data는 아래처럼 JSON으로 구성되어있다.
[{"idx":1,"title":"ㅁㅇㄴ","content":"<p>ㅁㄴㅇㅇㅁㄴ</p>"},]
그렇다면 안에 axios를 따로 구현하여 등록,수정,삭제시에 실행되게하여 그때마다 data를 불러오는것은 어떨까?
이렇게 useEffect로는 첫 렌더링시에만 data를 받아오게끔하고,
dataUpdate라는 함수를 따로 생성하여 각 버튼함수에 추가해보자.
이렇게 수정해주니 노란선은 사라지고, 기능도 정상적으로 작동한다.
이것으로 리렌더링 이슈는 모두 해결!
이제 두번째 문제다.
사실 위에 리렌더링 이슈를 해결하면서 원인을 알 수 있었다.
게시물 하나당 수정과 삭제버튼을 구현해놨는데, 이때 DataChange컴포넌트도 같이 들어가있어서
게시물 갯수만큼 모달창이 렌더링되고있는 상태였다.
이는 두번째 문제였던 여러번 출력되던 console.log와도 직결되는 문제였다.
이는 map()밖으로 빼주어서 간단히 해결하였다. 거의 실시간으로 기록중이라 위에 코드랑 또 다르다.