확장성을 고려한 리액트 컴포넌트

2019. 4. 1. 21:48coding

  1. 컴포넌트의 기본 속성

    리액트는 속성의 기본값을 설정할 수 있는 기능으로 defaultProps를 정적 클래스 속성으로 추가할 수 있다. defaultProps를 설정하면 컴포넌트 속성이 누락되었을 때 기본값을 렌더링할 수 있는 이점이 있다. 예를 들어 사용자 정의가 가능한 라벨을 렌더링하는 컴포넌트를 만들 때 사용자 정의 값이 누락된 경우 기본값을 보여줄 수 있다. 또 기본값을 설정하여 같은 값을 반복해서 다시 설정하는 것을 피할 수 있다.

     class DatePicker extends React.Component {
         ...
     }
     DatePicker.defaultProps = {
         currentDate: Date(),
         rows: 4,
     }
    
     class Button extends React.Component {
         render(){
             return <button>{this.props.buttonLabel}</button>
         }
     }
     Button.defaultProps = {buttonLabel: 'Submit'} //정적 클래스 속성 부여
     //buttonLabel 속성이 누락되면 'Submit' 이라는 속성이 설정된다.
  2. React 속성 타입과 유효성 검사

    1. React 컴포넌트 클래스에 propTypes 정적 속성을 이용하면 속성 타입을 설정할 수 있다. 속성 타입 기능은 자료형을 강제하는 대신 경고를 보여준다. development mode에서는 속성 타입이 일치하지 않으면 콘솔에서 경고 메시지를 확인할 수 있다. (production mode에서는 경고 문구 나타나지 않음)

       class Datepicker extends React {
           ...
       }
       Datepicker.propTypes = {
           currentDate: PropTypes.string,
           rows: PropTypes.number,
           locale: PropTypes.oneOf(['US', 'CA'])
       ] //프론트엔드의 사용자 입력 유효성 검사를 propTypes에 의존하는 것은 좋지 않음.
    2. React 15.5 이후 버전에서는 prop-types라는 별도 패키지로 제공되기 때문에 prop-types를 HTML에 패키지 추가해야 한다. 패키지는 전역객체 window.PropTypes가 된다. (확인해 보기)

    3. isRequired를 타입에 추가하면 필수 속성으로 지정할 수 있다.

    4. propTypes를 사용하여 타입 유효성 검사를 할 수도 있다. 사용자 정의 유효성 검사를 구현하기 위해 Error 인스턴스를 반환하는 표현식을 생성한다.

       Button.propTypes = {
           handler: PropTypes.func.isRequired, //함수를 값으로 하는 handler 필수
           title: PropTypes.string //title속성은 문자열 값을 선택 적용 가능
           email(props, propName, componentName) {
               let emailRegularExpression = ... //email 유효성 검사를 위한 정규표현식 사용하는 방법 나열
               if(!emailRegularExpression.test(props[propName])){
                   return new Error('Email Validation Fail')
               }
           }
       }
  3. 자식 엘리먼트 렌더링

    1. children 속성은 모든 자식을 {this.props.children} 으로 렌더링할 수 있다. 단순 렌더링 이외에도

      를 추가해서 자식 엘리먼트와 함께 렌더링할 수 있다.
       class Content extends React {
           render()[
               return(
                   <div>{this.props.children}</div>
               )
           }
       ]
      
       ReactDOM.render(
           <div>
               <Content> //부모 컴포넌트 Content는 <h1>과 <p>를 자식으로 갖는다.
                   <h1>React</h1>
                   <p>Rocks</p>
               </Content>
           </div>
           document.getElementById('content')
       }
    2. children 속성은 자식 엘리먼트가 하나 이상 있는 경우 배열이 되어 {this.props.children[0]} 처럼 개별 엘리먼트에 접근이 가능하다. (하나뿐이면 배열이 아니다) 이때 React.Children.count(this. props.children)을 사용하면 자식 엘리먼트의 수를 정확하게 확인할 수 있다. count외에도 map(), toArray()같은 다른 메서드도 있다.

  4. 코드 재사용을 위한 React 고차 컴포넌트 (Higher Order Component, HOC) 생성하기
    (리액트 이해 - Higher Order Component로 컴포넌트 재사용하기)

    1. 고차 컴포넌트를 이용하면 컴포넌트에 추가적인 로직을 적용해서 컴포넌트를 향상시킬 수 있다. (고차 컴포넌트를 통해 다른 컴포넌트가 기능을 상속받는 패턴 → 고차 컴포넌트를 통해 코드 재사용이 가능하다.)

    2. HOC는 리액트 컴포넌트를 인자로 받아서 새로운 리액트 컴포넌트를 리턴하는 함수이다. EnhancedReactComponent는 ReactComponent 의 Props를 변경한다거나, 리액트 컴포넌트에 새로운 props를 추가하여 전달하거나 아예 새로운 컴포넌트를 return할 수 있다.

       const HOC = ReactComponent => EnhancedReactComponent;
    3. 리액트에서 컴포넌트를 만들때 가장 많이 사용되는 패턴은 컴포지션 패턴이다.

      • 컴포지션 패턴 알아보기
      • 고차 컴포넌트 자세히 알아보기
    4. HOC를 만드는 방법에는 Class기반 컴포넌트를 리턴하는 방법과 Function 기반 컴포넌트를 리턴하는 방법이 있다.

       const withHOC = WrappedComponent => {
           const newProps = {
               loading: false,
           };
           return props => {
               return <WrappedComponent {...props} {...newProps} />
           }
       }; //Functional Component 리턴
      
       const withHOC = WrappedComponent => {
         const newProps = {
           loading: false,
         };
         return class extends React.Component {
           render() {
             return <WrappedComponent {...this.props} {...newProps} />
           }
         }
       }; //Class Component 리턴
      
       export default withHOC(AnyComponent); //HOC 사용할 때
      
       const withLoading = (WrappedComponent) => (props) =>
         props.isLoading
           ? <div>Loading ...</div>
           : <WrappedComponent { ...props } />
       //isLoading prop을 받아서 true일땐 로딩 컴포넌트를 렌더링하고,
       //false일 땐 원하는 컴포넌트를 렌더링하는 HOC
      
       export default withLoading(TodoList); 
       //TodoList라는 컴포넌트를 withLoading HOC로 래핑해서 로딩중일때는
       //로딩 화면을 보여주도록 처리한다.
  5. 프레젠테이션 컴포넌트와 컨테이너 컴포넌트

    1. 프레젠테이션 컴포넌트
      1. 보통 DOM과 스타일에 구조만 추가한다. 속성은 사용하지만 상태를 갖지는 않는다. 상태비저장 프레젠테이션 컴포넌트는 함수로 작성할 수 있다.
      2. 자식 컴포넌트를 감싸서 스타일을 입히기 위해 this.props.children을 사용한다. 데이터나 상태를 다루는 경우는 별로 없다. → 컨테이너 컴포넌트가 데이터와 상태를 주로 관리
    2. 컨테이너 컴포넌트
      1. 상태를 갖는다. 고차 컴포넌트로 컨테이너 컴포넌트에 데이터 소스를 주입하기도 한다.