본문 바로가기
  • Seizure But Okay Developer
FrontEnd/React 관련자료

리액트 기본 과정 정리 - 4 [props와 state]

by Sky_Developer 2019. 12. 8.
이 글은 Velopert 님의 인프런 - 누구든지 하는 리액트 강의 를 참고하여 작성한 글입니다. 혹시 저작권 상의 문제가 발생할시에 내리도록 하겠습니다.

개요

리액트 강의를 듣고 기본 개념을 정리하고 기억하기 위해 작성하였습니다. 공부하시는 다른 분들께 도움이 되었으면 합니다.

 

Props와 State

리액트 컴포넌트에서 다루는 데이터는 두 개로 나뉩니다, 바로 props와 state 입니다.

props는 부모 컴포넌트가 자식 컴포넌트에게 주는 값입니다. 자식 컴포넌트에선 props 를 받아오기만하고, 받아온 props를 직접 수정할 수 없습니다. 자식 입장에서 props는 읽기 전용입니다.

state는 컴포넌트 내부에서 선언하며 내부에서 값을 변경할 수 있습니다.

 

Props

props 에 대해서 그림으로 표현하자면 다음과 같습니다.

 

props는 부모 컴포넌트에서 자식 컴포넌트로 값을 전달할 때 사용됩니다. props를 사용하려면 위 그림의 태그 내용처럼 자식 컴포넌트에 넘겨줄 attribute 이름에 value를 할당해주면 됩니다. html 태그에 속성을 설정해주는 것처럼요.

 

예제를 보면 다음과 같습니다.

 

MyName.js

MyName 이라는 자식 컴포넌트를 선언하였고 props의 name 값을 표현합니다. 자식 컴포넌트를 부모 컴포넌트에서 사용하겠습니다.

 

App.js

먼저 MyName 컴포넌트를 import를 합니다. 그리고 MyName 자식 컴포넌트를 작성하고, name에 "NomadCoder" 라는 문자열 값을 할당하여 자식 컴포넌트에게 props 값을 전달해줍니다. 그리고 결과가 표시됩니다.

 

defaultProps 

가끔씩 실수로 props를 빠뜨릴때가 있을 수 있습니다. 또는 props를 일부로 비워야 하는 특정 상황일 때도 있구요. 이 때는 defaultProps로 props 의 기본값을 설정해줄 수 있습니다. 

 

defaultProps는 다음과 같이 설정할 수 있습니다.

 

부모 컴포넌트에서 props 값은 다음과 같이 비워져있습니다.

 

 

그리고 defaultProps는 다음 방법으로도 선언해서 사용할 수 있습니다.

 

둘 다 똑같은 방법이지만 static 문법을 사용해서 쓰는 것이 더 최신 문법입니다. 두번째 방법은 Babel 이 static 문법으로 쓰여진 것을 자바스크립트 변환을 하면서 아래로 내린 형태의 모습입니다. 그러므로 static 문법을 사용해서 하는 것을 권장합니다.

 

함수형 컴포넌트

함수형 컴포넌트는 방금 본 예제처럼 다른 기능은 사용하지 않고 props로 값을 받아서 보여주기만 할 때 주로 많이 사용됩니다.

함수형 컴포넌트는 다음과 같이 사용할 수 있습니다.

 

Component 를 사용하지 않으므로 import 하지 않습니다. 값을 할당하는 표현식 형태로 작성이 됩니다. 그리고 props가 인자로 전달되는데 여기선 해체 할당으로 값을 받아서 사용하였습니다.

 

함수형 컴포넌트와 클래스형 컴포넌트와의 차이는 state 기능과 Lifecycle 기능이 없다는 점입니다.

그리고 함수형 컴포넌트는 초기 마운트 하는 속도가 미세하게 좀 더 빠르고, 다른 불필요한 기능을 사용하지 않아 메모리 자원도 덜 사용합니다.

그러므로 수많은 컴포넌트들을 사용하고 단순히 값을 받아 화면에 보여주기만 하면 되는 부분에서 함수형 컴포넌트를 사용하면 속도를 최적화할 수 있습니다.

 

그러나 많은 컴포넌트를 사용하는게 아니라면 클래스형 컴포넌트와 속도상 차이는 크게 나지 않을 수 있습니다.

 

State

State를 그림으로 설명하면 다음과 같습니다.

 

 

state는 props 와 달리 컴포넌트 스스로 내부에 가지고 있는 값 으로 내부에서 변경할 수 있습니다.

state 에 있는 값을 바꾸기 위해선 this.setState 를 무조건 거쳐야 합니다.

리액트에서는 이 setState 함수가 호출되면 컴포넌트가 리렌더링 되도록 설계되어 있습니다.

 

다음 예제를 보겠습니다. Counter 컴포넌트를 만들어 플러스, 마이너스 버튼을 눌러 숫자가 증감되게끔 합니다.

 

 

부모 컴포넌트에서 Counter 컴포넌트를 사용합니다. Counter 컴포넌트는 다음과 같이 작성합니다.

 

 

이 때 state 를 Counter 클래스 안에서 객체 형태로 선언해서 사용합니다. 이처럼 클래스 블록 안에서 할당 연산자( = )로 인스턴스 속성을 지정하는 문법을 class fields 라고 합니다. 이 때 state 는 꼭 객체 형태로 작성해주어야 합니다.

 

이제 각 button을 눌렀을 때 숫자가 증감이 되게끔 해줄 겁니다. handleIncrease, handleDecrease 함수를 각각 만들겠습니다.

 

state에 값을 증가시키기 위해선 setState를 불러서 사용해야 합니다. 만약 setState 를 사용하지 않고 다음과 같이 state 값을 변경하려고 하면 컴포넌트는 값이 변경된 것을 감지하지 못하여 리렌더링 하지 않습니다. 꼭 setState 를 사용해서 state 값을 바꿉시다.

 

컴포넌트가 state 변화를 감지못함.

그래서 다음과 같이 setState 함수내에서 number 에 값을 1씩 증가하도록 만듭니다. 속성 이름은 state 객체의 number 이름을 그대로 작성하면 됩니다.

 

 

그리고 버튼에는 위 함수들을 onClick 이벤트와 연결해주었습니다. props 값을 사용할 때처럼 중괄호 안에 함수 이름을 작성하고 이를 해당 이벤트에 설정해주면 됩니다.

 

이 때 주의해야 될 것이 두가지 있습니다.

  • 이벤트 이름을 설정할 때 camelCase로 설정해주어야 합니다. onclick, onmousedown, onchange 는 onClick, onMouseDown, onChange 와 같이 적어야 합니다.
  • 이벤트에 전달해주는 값은 함수 여야 합니다. 만약 onClick={this.handleIncrease()} 와 같이 함수를 호출을 하면 렌더링을 할 때마다 해당 함수가 호출됩니다. 그러면 정말 큰 일이 발생합니다. 렌더링 -> 함수 호출 -> setState -> 렌더링 -> 함수 호출 -> 무한반복... 이렇게 되는 것이죠

그러므로 꼭 주의하셔야 합니다. 렌더링 함수에서 이벤트를 설정할 때 여러분이 만든 메소드를 호출하지 마세요!

 

이제 전체 코드를 보세요.

 

버튼 클릭 시 값이 증감됨

그런데 이들 함수를 작성할 때 일반함수가 아니라 화살표 함수로 작성하였습니다. 만약 일반함수로 작성을 하면 제대로 작동하지 않는데 왜냐하면 함수가 버튼의 클릭이벤트로 전달이 되는 과정에서 "this"와의 연결이 끊겨버리기 때문입니다. 그래서 this가 undefined로 나타나면서 제대로 작동하지 않는 것입니다.

 

일반 함수로 작성하면 this는 undefined가 뜸

차이를 보면 일반 함수를 사용했을 때 this 는 undefined가 뜨고 화살표 함수는 Counter 객체가 뜹니다.

일반 함수의 this는 메서드를 호출한 객체에 묶이는데, 화살표 함수의 this는 상위 스코프의 this를 가리킵니다. 그러므로 화살표 함수를 사용해야만 정상적으로 동작합니다.

 

그렇지만 일반 함수를 사용해서 동작할 수 있는 방법도 있습니다. 생성자 함수내에서 this를 함수 마다 바인딩하면 됩니다. 다음 코드를 볼까요?

 

생성자 함수는 컴포넌트가 만들어질 때마다 호출되는 함수입니다. 그리고 super를 호출하고 있는데 이게 어떤 의미일까요?

우리는 Component를 만들게 되면서 Component를 상속받았고, 여기에 constructor를 작성하게 되면 기존의 클래스 생성자를 덮어쓰게 됩니다. 그러므로, React Component가 지니고 있던 생성자를 super를 통해서 미리 실행하고 그 다음에 우리가 할 작업 (state 설정) 을 해주는 것입니다.

그리고 'handleIncrease 함수에서 쓰는 this는 생성자 함수에서 쓰는 this 이다' 는 것을 명시해주기 위해 handleIncrease 함수에 .bind(this)를 해주면 일반 함수로 작성해도 제대로 동작하는 것을 확인할 수 있습니다.

 

 

리액트에서 props로 값을 넘겨주는 방법과 state 값을 다루는 방법에 대해서 알아보았습니다. 다음에는 Lifecycle API에 대해서 알아보겠습니다.

출처

https://react-anyone.vlpt.us/04.html

classfields : https://helloworldjavascript.net/pages/270-class.html

 

댓글