[React.JS] 강좌 5편 컴포넌트의 State 와 Props 사용하기


liste

이번 강좌에서는 Component 에서 사용 할 데이터를 다루는 State 와 Props 에 대하여 알아보겠습니다.

1. 시작하기

강좌 4편 – Component 생성 및 모듈화 에서 사용하던 프로젝트를 계속해서 사용하겠습니다.

index.html

index.js

 

src/components/App.js

src/components/Header.js

src/components/Conent.js

 

 

2. props

props 는 컴포넌트에서 사용 할 데이터 중 변동되지 않는 데이터를 다룰 때 사용됩니다. parent 컴포넌트에서 child 컴포넌트로 데이터를 전할 때, props 가 사용됩니다. 예제를 통하여 이에 대하여 알아보겠습니다.

2.1 props 추가하기

컴포넌트에서 immutable (변하지 않는)  데이터가 필요 할 땐,
render() 메소드의 내부에 안에 { this.props.propsName } 형식으로 넣고,
컴포넌트를 사용 할 때, < > 괄호 안에 propsName="value를 넣어 값을 설정합니다.

Header 컴포넌트와 Content 컴포넌트가 props를 사용하도록 업데이트 해보겠습니다.

Header.js

위와 같이 props 값이 렌더링 될 위치에 { this.props.propsName } 를 넣습니다.

Content.js

contentTitle 와 contentBody props 를 넣어주었습니다.


2.2 props 사용하기

자, 이제 App 컴포넌트에도 props 를 넣어주고, App 컴포넌트에서 사용되는 props 값을 child 컴포넌트들로 전달하겠습니다.

App.js

 

index.js

 


2.3 기본 값 설정하기

props 값을 임의로 지정해주지 않았을 때 사용할 기본값을 설정하는 방법을 알아보도록 하겠습니다.

기본값을 설정 할 땐, 컴포넌트 클래스 하단에 className.defaultProps = { propName: value } 를 삽입하면 됩니다.

App.js

index.js


2.4 Type 검증(Validate)하기

컴포넌트 에서 원하는 props 의 Type 과 전달 된 props 의 Type 이 일치하지 않을 때 콘솔에서 오류 메시지가 나타나게 하고 싶을 땐, 컴포넌트 클래스의 propTypes 객체를 설정하면 됩니다. 또한, 이를 통하여 필수 props 를 지정할 수 있습니다. 즉, props 를 지정하지 않으면 콘솔에 오류 메시지가 나타납니다.

한번 Content 컴포넌트의 propTypes을 지정 해 볼까요?

Content.js

두 props 의 Type 를 모두 string 을 지정하고,

body는 .isRequired 를 추가하여 필수 props 로 설정하였습니다.

 

자, 이제 App 컴포넌트에서 잘못된 값을 줘보도록 하겠습니다.

App.js

contentTitle 엔 숫자를 지정하였고, contentBody에는 빈 값을 전달하도록 설정하였습니다.

Validation이 실패하면 브라우저에서 다음과 같은 오류가 나타납니다.

이미지 3

테스트가 끝나면 2.3 의 App.js  상태로 되돌리세요.


 

 

 

 

 

 

 

 

 

예제

예제를 통하여 여러 종류의 Type 를 Validate 하는 방법을 알아보도록 하겠습니다. (reference: React.js 메뉴얼)

3. State

컴포넌트에서 유동적인 데이터를 다룰 때, state 를 사용합니다.. React.js 어플리케이션을 만들 땐, state를 사용하는 컴포넌트의 갯수를 최소화 하는 것 을 노력해야합니다. 예를들어, 10 개의 컴포넌트에서 유동적인 데이터를 사용 하게 될 땐, 각 데이터에 state를 사용 할 게 아니라, props 를 사용하고 10 개의 컴포넌트를 포함시키는 container 컴포넌트를 사용하는것이 효율적입니다.

3.1 기본적인 사용 방법

StateExample.js (미리보기: JSFiddle)

  • state 의 초기 값을 설정 할 때는 constructor(생성자) 메소드에서 this.state= { } 를 통하여 설정합니다.
  • state 를 렌더링 할 때는 { this.state.stateName } 을 사용합니다.
  • state 를 업데이트 할 때는 this.setState() 메소드를 사용합니다. ES6 class에선 auto binding이 되지 않으므로, setState 메소드를 사용 하게 될 메소드를 bind 해주어야 합니다. (bind 하지 않으면 React Component 가 가지고있는 멤버 함수 및 객체에 접근 할 수 없습니다.)

 

4. 적용: State 와 Props

유동적인 데이터를 렌더링하며, parent 컴포넌트와 communicate 하는 예제 컴포넌트 RandomNumber 를 만들어봅시다.

RandomNumber.js

랜덤 숫자를 나타내는 h1 element와, 클릭 하면 새로운 랜덤값으로 바꾸는 button element를 렌더링 합니다.

이 컴포넌트에서는 두가지 prop을 사용합니다.

  1. number: 랜덤 값
  2. onUpdate: function 형태의 prop 으로서, parent 컴포넌트에 정의된 메소드를 실행 할 수 있게 합니다.

코드 설명

  • Line 8: props 로 받은 함수를 실행합니다.
  • Line 11 ~ 14: React 컴포넌트의 생성자입니다. super(props) 로 상속받은 React.Component 의 생성자 메소드를 실행 한 후, 저희가 입력한 코드를 실행합니다.. 13번 줄에서는 update 메소드에서 this.props 에 접근 할 수 있도록 binding 을 해줍니다.
  • Line 20: 버튼을 클릭하였을 시 update() 메소드를 실행합니다.

이제, parent 컴포넌트인 App 컴포넌트에서 RandomNumber 컴포넌트를 사용해봅시다.

App.js

코드 설명

  • Line 5: RandomNumber.jsimport 합니다.
  • Line 12: 초기 state 를 설정합니다.
  • Line 16: updateValue() 메소드에서 this.setState 에 접근 할 수 있도록 bind 합니다.
  • Line 20~22: state 를 변경 할 때는 setState({key: value}) 메소드 를 사용합니다.
  • Line 31-32: RandomNumber 컴포넌트를 사용합니다.

 

출력물

Untitled-1

마치면서..

propsstate, 생긴건 비슷하지만 용도는 다릅니다. 헷갈리지 않도록 다음 특성을 기억하세요.

특성 props state
parent 컴포넌트에 의해 값이 변경 될 수 있는가? 아니오
컴포넌트 내부에서 변경 될 수 있는가? 아니오

 

이 포스트에서 사용된 코드는 GitHub 에서 리뷰 할 수 있습니다.

 

다음 강좌에서는 비슷한 코드를 반복해서 렌더링할 때 사용 되는 keysmap 개념에 대하여 알아보겠습니다.

liste

  • 이성필

    react를 공부하게 되면서 들어오게 됐습니다. 우선 좋은 글 감사합니다!
    수정해야 할 부분이 있는 것 같습니다. 3.1 예제 코드 20번째 줄에서
    {_updateHeader.bind(this)} => {this._updateHeader.bind(this)}
    this가 빠진 것 같습니다.

  • Sang Ik Bae

    정말 재밌게 배우고있습니다 좋은 자료감사합니다!

  • Steve

    app.js 가 맞나요? 앞전 강좌에서 였고 였는데요.

    저렇게 바꾸니 아무것도 출력되지 않습니다.

    • root 과 bundle.js 가 맞습니다 🙂
      제가 강좌를 초기에 작성 할 땐 app / app.js 로 사용하였는데,

      최근에 지금까지 썼던 게시물들을 변경하면서 GitHub에 올린코드만 수정하고
      이 게시물의 html 부분을 생략했네요 !

      지적감사합니다

  • Yunseop Song

    처음부분에 src/App.js가 아니라 src/component/App.js 로 되어야할것 같아요 나머지 두개 component들도 그렇게 되어있네요

    • 강좌를 작성하고 나중에 구조를 좀 바꿨는데
      수정을 덜 한게 있네요

      발견해주셔서 감사합니다!

      그나저나.. 나중에 시간나면 강좌들을 처음부터 다시 작성해야겠어요 ㅎㅎ
      요즘 세미나를 준비하고 있어서 강좌를 첫 부분부터 리뷰를 하고있는데 이제보니 좀 난해하게 작성한것 같아요..

      현재 블로그에 작성된것들은 ‘초안’ 에 불과하니..
      앞으로도 이상한 부분을 발견하시면 지적해주시면 감사하겠습니다!

      근데 유독 이 강좌만 실수를 정말 많이 했네요 ㅋㅋ

  • jh

    궁금한점이 있는데요 StateExample.js의 constructor함수 안에서 super(props)를 하는 이유가 뭔가요?

    • class StateExample extends React.Component {

      보시다시피 StateExample 클래스는 React.Component 클래스를 “상속” 하지요.
      super 함수는, 상속받은 부모패런트의 생성자 메소드를 실행시켜줍니다.

      React 컴포넌트가 생성될때, 생성자의 인수로는 props 가 있기 때문에,
      그대로 전달해주어서 React 컴포넌트의 생성자를 먼저 실행하고, 저희가 정의한 클래스의 생성자를 실행하는것이랍니다.

      • jh

        다변 감사합니다.
        또 궁금한점이 있는데요 StateExample.js안에서는 props를 사용하지 않고 state만 사용하고 있는데 constructor안에서는 꼭 super(props)를 해야하는건가요?

        • 저는 습관적으로 super(props) 를 작성하는데
          constructor에서 this.props 에 접근할 필요가 없다면 그냥 super() 만 작성하셔도 무방합니다.

  • Dev_Senna

    5과로 넘어오면서 App.js의 하단부가 ReactDOM.render(, document.getElementById(‘app’)); 으로 갑자기 바뀌었네요. 바꾸고 나서 페이지에 아무것도 렌더링이 안 되네요. 깃허브에 올리신 소스를 보니 export default App; 로 끝나도록 되어 있는데 후자 맞는 거겠죠?

    • 엇! 그 부분도 수정해야했는데 까먹었네요 ㅎㅎㅎ
      말씀하신 부분은 수정되었습니다.

      감사합니다 ~

  • SyaLot

    저 constructor에 this.이름 으로 선언해서 전역 변수처럼 사용해도 되는건가요? 일단 해보니까 되기는 하는데 이게 문법적으로 맞는지가 궁금합니다. 만약 이방식이 안된다면 this.state말고 컴포넌트에 대한 전역 변수 사용법을 알수 있을까요??
    (렌더가 필요없고 오직 전역으로 접근해 값을 쓰고 지우고가 가능한 변수가 필요한데 어떻게 해야할지 모르겠네요 ㅠㅠ)

    • 문법적으로 잘못된것은 없습니다.
      말씀하신대로, 만약에 렌더링이랑 전혀 관계가 없다면, state를 사용하지 않으셔도 됩니다. (예를들어, 특정 timeoutId를 설정하고, 컴포넌트가 unmount 될 때 해당 타임아웃을 클리어하려면, this.timeoutId = … 이런식으로 설정을해도 전혀 무방하죠.

      추가적으로, 만약에 해당 값을 다른 컴포넌트 (부모컴포넌트나 형제 컴포넌트) 에서 접근을 해야한다면 나중에 redux 를 사용해보시는것을 추천드립니다.

  • 김령민

    좋은 자료 공개해주셔서 정말 감사합니다.

    내용 중 3. State 에서

    “예를들어, 10 개의 컴포넌트에서 유동적인 데이터를 사용 하게 될 땐, 각 데이터에 state를 사용 할 게 아니라, props 를 사용하고 10 개의 컴포넌트를 포함시키는 container 컴포넌트를 사용하는것이 효율적입니다.”
    라는 항목을

    “예를들어, 10 개의 컴포넌트에서 각각 state를 이용하여 유동적인 데이터를 다루기 보다는, 10 개의 컴포넌트에는 props를 각각 사용하고, 이 컴포넌트들을 포함하는 container 컴포넌트 1개에만 state를 사용하는 것이 효율적입니다.”

    라고 이해하면 될까요?

    • 예 그렇습니다.
      사실상 상황에 따라 다릅니다.

      더 자세하게 설명을 드리자면 state는 그 컴포넌트 내부에서만 사용될때만 사용하는것이 옳습니다.

      하지만, 만약에 그 state값을 다른 컴포넌트에서 접근 할 일이 있다면, 부모 (container) 컴포넌트에서 state를 사용하고 그 값을 props로 자식컴포넌트에서 받는것이 많이 사용되는 방식입니다.

      제가 이 포스트에서 좀 난해하게 설명한것 같기도 하네요 🙂 요즘 리액트 포스트를 리뉴얼 중인데 나중에 좀 더 이해하기 쉽게 수정해보겠습니다

  • 이동주

    이번 강좌부터 index.js 에서 ReactDOM.render(, document.getElementById(‘app’));을 해주던 거에서 App.js 에서 해주는 걸로 바뀌었는데 그거에 대한 설명이 없는것 같아서요.
    그럼 현재 엔트리포이트인 index.js 의 소스는 어떻게 수정해야 하나요??

    • 강좌의 구조를 나중에 수정했었는데
      이 강좌는 수정을 덜했었네요.

      이전에는 App.js를 entry point 로 사용했었거든요.

      지금 상황으로서는, 현재 App.js가 entry point입니다. 현재코드가 제대로 작동하게 하려면 App코드 하단의 ReactDOM.render를 제거하시고 export default App;
      를 입력하세요

      그리고 App 컴포넌트에 props전달할때는 index파일을 수정하시구요

      빠른 시일내애 수정하도록 하겠습니다.

  • 이상규

    4번 예제 RandomNumber.js에서 13번째 줄에 this.update => this.updateNumber 아닌가요?

    • 이성화 SeongHwa Lee

      저도 그것 때문에 한참 고생했네요ㅠㅠ감사합니다.

  • SangHeon Lee

    this.update = this.update.bind(this);
    => this.updateNumber = this. updateNumber.bind(this);
    인거 같습니다. 예전에는 함수가 update였나봐요 ㅎㅎ
    좋은강좌 감사합니다. ㅎㅎ

    • 이제서야 수정했네요.. ㅋㅋ 감사합니다 :ㅇ

  • DongUk Seo

    리액트 강좌 잘보고있습니다.!
    이번 실습에서 randomize를 누르면
    Uncaught TypeError: Cannot read property ‘props’ of null라는 오류가
    updateNumber의 this.props.onUpdate(value);에서 발생하는데, 어떻게해야할까요?
    좋은강좌 감사합니다 ㅎㅎㅎㅎ

    • DongUk Seo

      는 잘되네요! 죄송합니다. 단순 실수였어요

  • 정우딱

    강의 정말 감사히 잘보고있습니다!
    제가 초보라서 궁금한점이있는데 4.적용 state와 props에서 코드를보면 app.js에서 state 초기값을 설정하셨는데 randomnumber.js에서 초기값을 설정해놓을 수도 있는건가요?
    제가 생각하기에는 오히려 randomnumber.js에서 초기값까지 설정을 해놓으면 깔끔해보일거같은데 그렇게하면 에러가나는건지 되는건지 잘모르겠어서 질문드려요. 허접한질문이지만 대답해주시면 감사하겠습니다!

  • 박지훈

    tutorial 하는 도중에
    app.js 에서 import ReactDOM from ‘react-dom’; 이 부분이 github에서는 없었습니다.
    제외하고나니 정상적으로 출력이 되네요

  • Jung Han

    좋은강좌입니다!

  • Jaehun Cho

    상위 컴포넌트에서 하위 컴포넌트 객체를 불러 올때는 redux를 사용해야되는건가요?

    • 그런 경우에는 Ref 를 사용해서 하위컴포넌트를 불러올수는 있습니다. 하지만 대부분의 상황에서는 하위컴포넌트에서 상위컴포넌트의 메소드를 가져오는것이 좋은 패턴입니다.

      Redux 를 사용한다면, 하위 컴포넌트 객체를 가져오는게 아니라 스토어 안에있는 같은 정보에 접근 할 수 있게됩니다.

      조만간 컴포넌트 구성에 대한 강좌를 올릴건데 그 강좌를 보시면 좀 더 이해가 잘 될거에요 😉

      참고:
      Ref 를 사용하여 하위 컴포넌트 객체를 가져와서 사용하는 적절한 용도는 DOM 작업을 할 때 입니다. 예를 들어서 인풋에 포커스를 준다던지, 스크롤바의 위치를 가져오거나 움직인다던지 할때를 말하죠.

      • Jaehun Cho

        상위 컴포넌트에서 하위 컴포넌트 state 정보를 가져올려고 했던건데
        state와 props 만으로 안되는거였군요 redux를 사용해봐야겠어요!!
        조언 감사합니다~!!

        • 안된다기보다는 리액트 어플리케이션에서 권장되지 않는 패턴이라고 이해하시면 됩니다~

  • 이종현

    onClick 시, {this.update} 로 변경해야 할 것 같습니다.
    버튼 클릭시마다 에러가 나서 왜 그런가 보니,
    bind 는 해놓고, 정작 method를 직접 호출하고 있어서, this 를 인식 못 하고 있더라구여.
    아마 GitHub 소스에는 수정되어 있을 것으로 예상 됩니다.
    강의 잘 보고 있습니다.
    감사합니다.