이 글은 Velopert 님의 인프런 - 누구든지 하는 리액트 강의 를 참고하여 작성한 글입니다. 혹시 저작권 상의 문제가 발생할시에 내리도록 하겠습니다.
개요
리액트 강의를 듣고 기본 개념을 정리하고 기억하기 위해 작성하였습니다. 공부하시는 다른 분들께 도움이 되었으면 합니다. 이번 글은 개인적으로 강의에서 중요하다고 생각되는 부분만 담았으므로 참고 부탁드립니다.
데이터 추가
우리 애플리케이션의 상태 데이터는 App 컴포넌트에서 관리하겠습니다. 나중에 가면 상태를 컴포넌트에서 분리하여 따로 관리하는 방법도 있습니다.
앞서 자식 컴포넌트인 PhoneForm 에서 받은 데이터를 App 의 상태에 추가하겠습니다. 이를 위해 우선 App 컴포넌트의 state에 information 배열을 만듭니다. 이 배열에 데이터를 저장할 겁니다.
배열에 데이터를 추가할 때 주의해야 할 점이 있습니다.
불변성 유지
리액트에서는 불변성을 꼭 유지해주어야 합니다.
state 값을 바꿀 때는 직접적으로 수정하지 말고 꼭 setState를 사용해야하고,
내부의 배열이나 객체를 바꿀 때는 기존의 배열이나 객체를 수정하지 않고 기존 배열이나 객체를 기반으로 새로운 배열 또는 객체를 만들어 값을 주입해주어야 합니다.
그러므로 push, splice, unshift, pop 같은 내장함수는 배열 자체를 직접 수정하게 되므로 적합하지 않습니다. 그 대신에 기존의 배열에 기반하여 새 배열을 만드는 함수인 concat, slice, map, filter 같은 함수를 사용해야 합니다.
리액트에서 불변성 유지가 중요한 이유는 불변성을 유지해야 리액트에서 모든 것들이 필요한 상황에 리렌더링 되도록 설계할 수 있고, 그렇게 해야 나중에 성능도 최적화 할 수 있기 때문입니다.
그래서 this.state.information.push(data) 가 아닌 this.state.information.concat(data) 로 작성을 하여 기존의 배열은 수정하지 않고 새로운 배열을 만들어 데이터를 집어넣고 이를 기존 배열과 대체해주는 겁니다.
다시 코드로 돌아와서 설명을 이어가겠습니다.
또 비구조화 할당 문법을 사용해서 코드 단축을 할 수 있고 가독성도 괜찮아집니다.
그리고 데이터를 추가할 때 각 데이터마다 고유한 ID 값을 넣어줍니다. id 는 전역으로 선언하고 0으로 초기화합니다. App 컴포넌트의 handleCreate 함수에서 information 배열에 값을 추가할 때 id 값을 증가연산자로 증가시키면서 같이 추가시킵니다.
이렇게 고유한 ID 값을 넘겨주는 이유는 무엇이냐면, 배열에 있는 값들을 렌더링 해줄 때 고유한 key 값이 있어야 하기 때문입니다. 관련한 설명은 조금 뒤에 살펴보겠습니다.
concat 함수의 인자로 data와 id를 넘길 때 몇 가지 방법이 있지만 확산 전개 연산자와 Object.assign 함수를 이용한 방법을 소개하겠습니다. 먼저 확산연산자 입니다.
이처럼 배열뿐만 아니라 객체에서도 확산 연산자를 사용할 수 있습니다. 이를 Object.assign 함수를 이용해서도 똑같이 구현할 수 있습니다.
그런데 ID 값은 state 값에 따로 넣어주지 않고, 전역으로 선언해 사용하고 있습니다. 왜냐하면 ID 값은 렌더링 되는 값이 아니기 때문에 굳이 setState를 통해 넣어줄 필요가 없습니다. setState를 하는 이유는 컴포넌트에서 상태 값이 수정됐을 때 리렌더링을 꼭 하게끔 하기 위함인데, ID 값은 굳이 렌더링을 시켜야 하는 값이 아니고 데이터를 추가했을 때 참조하는 값이기 때문에 굳이 state에 넣어주지 않아도 됩니다.
이제 render 함수에서 information 값을 문자열로 변환하여 보여줍니다.
데이터 렌더링
이제는 위 배열을 컴포넌트로 변환해서 바꿔주겠습니다. 초반에 언급했듯이 리액트를 다루는 것은 자바스크립트를 사용하는 것과 매우 비슷합니다. 컴포넌트를 여러 개 렌더링 하기 위해서는 앵귤러 & 뷰 처럼 디렉티브 같은 것을 사용 할 필요없이 그냥 자바스크립트 배열의 내장함수인 map 을 사용하면 됩니다.
map 함수
map 함수에 대한 내용은 공식 문서를 참고해주세요
MDN : Array.proptotype.map()
컴포넌트 만들기
우리는 두 개의 컴포넌트를 만들겠습니다.
- PhoneInfo : 각 전화번호 정보를 보여주는 컴포넌트입니다.
- PhoneInfoList : 여러 개의 PhoneInfo 컴포넌트들을 보여줍니다.
우선 PhoneInfo 부터 확인해볼까요?
우선 info 라는 객체를 props 로 받아와서 렌더링을 해줄 것입니다. 그런데 우리가 실수로 info 값을 전달해주는 것을 까먹는다면 컴포넌트가 렌더링 할 수 없다며 오류(크래쉬)가 발생할 것입니다. info 가 undefined 일 때에는 비구조화 할당을 통해 내부의 값을 받아올 수 없기 때문입니다.
그래서 defaultProps 를 통해서 info의 기본값을 설정해주어 문제가 생기는 것을 방지합니다.
다음으로 PhoneInfoList 컴포넌트를 보겠습니다.
여기서 props 로 넘어온 data가 배열이므로 여기에 map 함수를 사용해서 배열 내 값을 PhoneInfo 컴포넌트에게 전달을 해줍니다. 여기서 key를 사용하는 이유는 컴포넌트를 여러개 렌더링하게 될 때 고유한 값을 정해줌으로써 업데이트 성능을 최적화 하기 위해서 입니다.
앞서서 '배열에 있는 값들을 렌더링 해줄 때 고유한 key 값이 있어야 하기 때문' 의 이유는 바로 이것입니다.
배열 렌더링, key
배열을 렌더링할 때 사용하는 key가 없다면 어떻게 되는지 보겠습니다.
위 사진과 같이 a, b, c, d 요소들을 가진 배열이 있고 이를 토대로 div 엘리먼트로 만들어져 있는 상태입니다. 여기서 b, c 엘리먼트 사이에 새로운 값이 들어온다고 가정하겠습니다. 그러면 다음과 같이 작동합니다.
b, c 사이에 엘리먼트가 하나 더 늘어나는 것이 아니라 c가 z가 되고, d는 c가 됩니다. 그리고 아래에 d 엘리먼트가 새로 생깁니다.
그리고 여기서 a가 사라졌다고 가정해보겠습니다. 그러면 어떻게 작동할까요?
언뜻 보기에는 a 만 사라진 것으로 보이지만 아닙니다.
먼저 a가 b로 바뀌고, b는 z, z는 c, c는 d로 바뀌고 마지막에 위치한 d는 사라지는 식입니다. 굉장히 비효율적이죠?
만약에 key가 있다면 어떻게 작동할까요? 우선 key값은 고유해야 하므로 같은 key 값이 있으면 안됩니다.
다음과 같은 상황에서 b,c 사이에 새로운 값이 들어온다고 가정해보겠습니다. 그러면 다음과 같이 동작합니다.
(들어오고 있는 애니메이션을 찍은거라 어색하게 느껴질 수 있습니다. 새로운 값이 들어온 것입니다)
다른 값들은 가만히 있고 단순히 b,c 사이에 key=5 를 가진 z 값이 새로 추가만 됩니다.
만약 원래 배열에서 값이 사라진다면 어떻게 될까요?
(마찬가지로 삭제되는 애니메이션을 캡쳐해서 어색하게 느껴질 수 있습니다..!)
다른 작업없이 단순히 a 엘리먼트만 삭제됩니다.
예제를 통해서 key가 왜 사용되는지 알아보았습니다. key는 리액트에서 내부적으로 추가하거나, 삭제, 수정할 때 그 작업을 효율적으로 하기 위해서 사용되는 값입니다.
만약 컴포넌트에 key 값을 넘겨주지 않고 렌더링을 한다면 경고 메시지가 뜨지만 작동을 합니다. 이때는 기본적으로 배열의 index가 key로 사용이 됩니다.
하지만 기억해야 할 것은 성능상 효율적으로 사용하기 위해선 key 값을 꼭 직접 지정해주어야 합니다 ( 배열의 index 값을 key로 쓰지 말기..! )
지금까지 데이터를 배열에 어떻게 등록할지, 등록한 데이터를 어떻게 보여줄 지 배웠습니다. 이번에 기억해야 할 것은 배열을 렌더링 하게 될 때는 꼭 고유값을 key로 사용해야 한다는 것입니다. 그리고 불변성 유지를 위해 데이터를 조작할 때 기존의 배열을 건드리지 않는 방식으로 해야 하는 것입니다.
'FrontEnd > React 관련자료' 카테고리의 다른 글
리액트 기본 과정 정리 - 9 [불변성을 지키는 이유와 업데이트 최적화] (0) | 2019.12.13 |
---|---|
리액트 기본 과정 정리 - 8 [배열 다루기 (2) 제거와 수정] (0) | 2019.12.12 |
리액트 기본 과정 정리 - 6 [input 상태 관리하기] (0) | 2019.12.11 |
리액트 기본 과정 정리 - 5 [LifeCycle API] (0) | 2019.12.09 |
리액트 기본 과정 정리 - 4 [props와 state] (0) | 2019.12.08 |
댓글