MDC-112 웹: MDC와 웹 프레임워크 통합

1. 소개

logo_components_color_2x_web_96dp.png

머티리얼 구성요소(MDC)를 통해 개발자는 머티리얼 디자인을 구현할 수 있습니다. Google의 엔지니어와 UX 디자이너로 구성된 팀에서 만든 MDC는 아름답고 기능적인 수십 가지의 UI 구성요소가 특징이며 Android, iOS, 웹, Flutter.material.io/develop에서 제공됩니다.

MDC Web은 Material Design 원칙을 준수하면서 모든 프런트엔드 프레임워크에 통합되도록 설계되었습니다. 다음 Codelab에서는 MDC Web을 기반으로 하는 React 구성요소를 빌드하는 방법을 안내합니다. 이 Codelab에서 배운 원칙은 모든 JavaScript 프레임워크에 적용할 수 있습니다.

MDC 웹 빌드 방법

MDC Web의 JavaScript 레이어는 구성요소당 3개의 클래스(Component, Foundation, Adapter)로 구성됩니다. 이 패턴을 통해 MDC 웹은 프런트엔드 프레임워크와 유연하게 통합할 수 있습니다.

Foundation에는 Material Design을 구현하는 비즈니스 로직이 포함되어 있습니다. Foundation은 HTML 요소를 참조하지 않습니다. 이렇게 하면 HTML 상호작용 로직을 어댑터로 추상화할 수 있습니다. Foundation에는 어댑터가 있습니다.

어댑터는 인터페이스입니다. 어댑터 인터페이스는 Material Design 비즈니스 로직을 구현하기 위해 Foundation에서 참조합니다. Angular 또는 React와 같은 다양한 프레임워크에서 Adapter를 구현할 수 있습니다. 어댑터의 구현은 DOM 구조와 상호작용합니다.

구성요소에는 기초가 있으며, 그 역할은

  1. 프레임워크 외 JavaScript를 사용하여 어댑터를 구현합니다.
  2. Foundation의 메서드에 프록시하는 공개 메서드를 제공합니다.

MDC Web에서 제공하는 기능

MDC 웹의 모든 패키지는 구성요소, 기초, 어댑터와 함께 제공됩니다. 구성요소를 인스턴스화하려면 루트 요소를 구성요소의 생성자 메서드에 전달해야 합니다. 구성요소는 DOM 및 HTML 요소와 상호작용하는 어댑터를 구현합니다. 그런 다음 구성요소Foundation을 인스턴스화하고 Adapter 메서드를 호출합니다.

MDC 웹을 프레임워크에 통합하려면 해당 프레임워크의 언어/구문으로 자체 구성요소를 만들어야 합니다. 프레임워크 구성요소는 MDC Web의 어댑터를 구현하고 MDC Web의 Foundation을 사용합니다.

빌드할 항목

이 Codelab에서는 Foundation 로직을 사용하여 Material Design React 구성요소를 구현하는 맞춤 어댑터를 빌드하는 방법을 보여줍니다. 프레임워크에 통합에 있는 고급 주제를 다룹니다. 이 Codelab에서는 React를 프레임워크의 예로 사용하지만 이 접근 방식은 다른 모든 프레임워크에 적용할 수 있습니다.

이 Codelab에서는 상단 앱 바를 빌드하고 상단 앱 바 데모 페이지를 다시 만듭니다. 상단 앱 바 작업을 시작할 수 있도록 데모 페이지 레이아웃이 이미 설정되어 있습니다. 상단 앱 바에는 다음이 포함됩니다.

  • 탐색 아이콘
  • 작업 항목
  • 짧은 변형, 항상 접히는 변형, 고정, 눈에 띄는 변형의 4가지 변형을 사용할 수 있습니다.

필요한 항목:

  • 최신 버전의 Node.js (JavaScript 패키지 관리자인 npm과 번들로 제공됨)
  • 샘플 코드 (다음 단계에서 다운로드)
  • HTML, CSS, JavaScript, React에 대한 기본 지식

웹 개발 경험 수준을 평가해 주세요.

초급 중급 고급

2. 개발 환경 설정

시작 Codelab 앱 다운로드

시작 앱은 material-components-web-codelabs-master/mdc-112/starter 디렉터리에 있습니다.

...또는 GitHub에서 클론

이 Codelab을 GitHub에서 클론하려면 다음 명령어를 실행하세요.

git clone https://github.com/material-components/material-components-web-codelabs
cd material-components-web-codelabs/mdc-112/starter

프로젝트 종속 항목 설치

시작 디렉터리 material-components-web-codelabs/mdc-112/starter에서 다음을 실행합니다.

npm install

많은 활동이 표시될 것이며 최종적으로 터미널에 설치가 완료된 것으로 표시됩니다.

22a33efc2a687408.png

시작 앱 실행

동일한 디렉터리에서 다음을 실행합니다.

npm start

webpack-dev-server이(가) 시작됩니다. 브라우저에서 http://localhost:8080/에 접속하여 페이지를 확인합니다.

b55c66dd400cf34f.png

완료되었습니다. 상단 앱 바 React 데모 페이지의 시작 코드가 브라우저에서 실행 중이어야 합니다. lorem ipsum 텍스트, 컨트롤 상자 (오른쪽 하단), 완성되지 않은 상단 앱 바가 표시됩니다.

4ca3cf6d216f9290.png

코드 및 프로젝트 살펴보기

코드 편집기를 열면 프로젝트 디렉터리가 다음과 같이 표시됩니다.

e9a3270d6a67c589.png

App.js 파일을 열고 <TopAppBar> 구성요소가 포함된 render 메서드를 확인합니다.

App.js

render() {
    const {isFixed, isShort, isRtl, isProminent, isAlwaysCollapsed, shouldReinit} = this.state;

    return (
      <section
        dir={isRtl ? 'rtl' : 'ltr'}
        className='mdc-typography'>
        {
          shouldReinit ? null :
          <TopAppBar
            navIcon={this.renderNavIcon()}
            short={isShort}
            prominent={isProminent}
            fixed={isFixed}
            alwaysCollapsed={isAlwaysCollapsed}
            title='Mountain View, CA'
            actionItems={this.actionItems}
          />
        }
        <div className={classnames('mdc-top-app-bar--fixed-adjust', {
          'mdc-top-app-bar--short-fixed-adjust': isShort || isAlwaysCollapsed,
          'mdc-top-app-bar--prominent-fixed-adjust': isProminent,
        })}>
          {this.renderDemoParagraphs()}
        </div>

        {this.renderControls()}
      </section>
    );
  }

이는 애플리케이션의 TopAppBar 진입점입니다.

render 메서드가 있는 맨 React Component 클래스인 TopAppBar.js 파일을 엽니다.

TopAppBar.js

import React from 'react';

export default class TopAppBar extends React.Component {
  render() {
    return (
      <header>
        TOP APP BAR
      </header>
    );
  }
}

3. 구성요소 구성

React에서 render 메서드는 구성요소의 HTML을 출력합니다. 상단 앱 바 구성요소는 <header /> 태그를 렌더링하며 다음과 같은 두 가지 주요 섹션으로 구성됩니다.

  1. 탐색 아이콘 및 제목 섹션
  2. 작업 아이콘 섹션

상단 앱 바를 구성하는 요소에 관해 궁금한 점이 있으면 GitHub의 문서를 참고하세요.

TopAppBar.jsrender() 메서드를 다음과 같이 수정합니다.

  render() {
    const {
      title,
      navIcon,
    } = this.props;

    return (
      <header
        className={this.classes}
        style={this.getMergedStyles()}
        ref={this.topAppBarElement}
      >
        <div className='mdc-top-app-bar__row'>
          <section className='mdc-top-app-bar__section mdc-top-app-bar__section--align-start'>
            {navIcon ? navIcon : null}
            <span className="mdc-top-app-bar__title">
              {title}
            </span>
          </section>
          {this.renderActionItems()}
        </div>
      </header>
    );
  }

이 HTML에는 두 개의 섹션 요소가 있습니다. 첫 번째 요소에는 탐색 아이콘과 제목이 포함되어 있습니다. 두 번째에는 작업 아이콘이 포함되어 있습니다.

다음으로 renderActionItems 메서드를 추가합니다.

renderActionItems() {
  const {actionItems} = this.props;
  if (!actionItems) {
    return;
  }

  return (
    <section className='mdc-top-app-bar__section mdc-top-app-bar__section--align-end' role='toolbar'>
      {/* need to clone element to set key */}
      {actionItems.map((item, key) => React.cloneElement(item, {key}))}
    </section>
  );
}

개발자는 TopAppBar를 React 애플리케이션으로 가져와 TopAppBar 요소에 작업 아이콘을 전달합니다. App.js에서 TopAppBar를 초기화하는 예시 코드를 확인할 수 있습니다.

render 메서드에서 사용되는 getMergedStyles 메서드가 누락되었습니다. TopAppBar 클래스에 다음 JavaScript 메서드를 추가하세요.

getMergedStyles = () => {
  const {style} = this.props;
  const {style: internalStyle} = this.state;
  return Object.assign({}, internalStyle, style);
}

this.classesrender 메서드에서 누락되었지만 나중에 다룹니다. 누락된 getter 메서드 this.classes 외에도 상단 앱 바가 올바르게 렌더링되기 전에 구현해야 하는 TopAppBar의 일부가 아직 있습니다.

상단 앱 바에서 여전히 누락된 React 구성요소는 다음과 같습니다.

  • 초기화된 기반
  • 기반에 전달할 어댑터 메서드
  • JSX 마크업
  • 변형 관리 (고정됨, 짧음, 항상 접힘, 눈에 띄게 표시됨)

접근 방식

  1. Adapter 메서드를 구현합니다.
  2. componentDidMount에서 기초를 초기화합니다.
  3. componentWillUnmount에서 Foundation.destroy 메서드를 호출합니다.
  4. 적절한 클래스 이름을 결합하는 getter 메서드를 통해 변형 관리를 설정합니다.

4. 어댑터 메서드 구현

프레임워크 외 JS TopAppBar 구성요소는 다음 어댑터 메서드를 구현합니다 (여기에 자세히 나열됨).

  • hasClass()
  • addClass()
  • removeClass()
  • registerNavigationIconInteractionHandler()
  • deregisterNavigationIconInteractionHandler()
  • notifyNavigationIconClicked()
  • setStyle()
  • getTopAppBarHeight()
  • registerScrollHandler()
  • deregisterScrollHandler()
  • registerResizeHandler()
  • deregisterResizeHandler()
  • getViewportScrollY()
  • getTotalActionItems()

React에는 합성 이벤트와 다양한 권장 코딩 관행 및 패턴이 있으므로 어댑터 메서드를 다시 구현해야 합니다.

어댑터 getter 메서드

TopAppBar.js 파일에서 TopAppBar에 다음 JavaScript 메서드를 추가합니다.

get adapter() {
  const {actionItems} = this.props;

  return {
    hasClass: (className) => this.classes.split(' ').includes(className),
    addClass: (className) => this.setState({classList: this.state.classList.add(className)}),
    removeClass: (className) => {
      const {classList} = this.state;
      classList.delete(className);
      this.setState({classList});
    },
    setStyle: this.setStyle,
    getTopAppBarHeight: () => this.topAppBarElement.current.clientHeight,
    registerScrollHandler: (handler) => window.addEventListener('scroll', handler),
    deregisterScrollHandler: (handler) => window.removeEventListener('scroll', handler),
    registerResizeHandler: (handler) => window.addEventListener('resize', handler),
    deregisterResizeHandler: (handler) => window.removeEventListener('resize', handler),
    getViewportScrollY: () => window.pageYOffset,
    getTotalActionItems: () => actionItems && actionItems.length,
  };
}

스크롤 및 크기 조절 이벤트 등록을 위한 어댑터 API는 프레임워크 외 JS 버전과 동일하게 구현됩니다. React에는 스크롤 또는 크기 조절을 위한 합성 이벤트가 없으며 네이티브 DOM 이벤트 시스템을 사용하기 때문입니다. 또한 getViewPortScrollY는 React의 API가 아닌 window 객체의 함수이므로 네이티브 DOM을 따라야 합니다. 어댑터 구현은 프레임워크마다 다릅니다.

get adapter 메서드에서 호출하는 this.setStyle가 누락되었을 수도 있습니다. TopAppBar.js 파일에서 누락된 JavaScript 메서드를 TopAppBar 클래스에 추가합니다.

setStyle = (varName, value) => {
  const updatedStyle = Object.assign({}, this.state.style);
  updatedStyle[varName] = value;
  this.setState({style: updatedStyle});
}

어댑터를 구현했습니다. 이 시점에서 전체 구현이 아직 완료되지 않았으므로 콘솔에 오류가 표시될 수 있습니다. 다음 섹션에서는 CSS 클래스를 추가하고 삭제하는 방법을 안내합니다.

5. 구성요소 메서드 구현

변형 및 클래스 관리

React에는 클래스를 관리하는 API가 없습니다. 네이티브 JavaScript의 추가/삭제 CSS 클래스 메서드를 모방하려면 classList 상태 변수를 추가합니다. TopAppBar에는 CSS 클래스와 상호작용하는 세 가지 코드가 있습니다.

  1. className 속성을 통한 <TopAppBar /> 구성요소
  2. addClass 또는 removeClass를 통한 어댑터 메서드
  3. <TopAppBar /> React 구성요소 내에 하드 코딩되어 있습니다.

먼저 TopAppBar.js 상단의 기존 가져오기 아래에 다음 가져오기를 추가합니다.

import classnames from 'classnames';

그런 다음 TopAppBar 구성요소의 클래스 선언 내에 다음 코드를 추가합니다.

export default class TopAppBar extends React.Component {
  constructor(props) {
    super(props);
    this.topAppBarElement = React.createRef();
  }

  state = {
    classList: new Set(),
    style: {},
  };

  get classes() {
    const {classList} = this.state;
    const {
      alwaysCollapsed,
      className,
      short,
      fixed,
      prominent,
    } = this.props;

    return classnames('mdc-top-app-bar', Array.from(classList), className, {
      'mdc-top-app-bar--fixed': fixed,
      'mdc-top-app-bar--short': short,
      'mdc-top-app-bar--short-collapsed': alwaysCollapsed,
      'mdc-top-app-bar--prominent': prominent,
    });
  }

  ... 
}

http://localhost:8080으로 이동하면 이제 컨트롤 체크박스가 DOM에서 클래스 이름을 켜거나 끌 수 있습니다.

이 코드를 사용하면 많은 개발자가 TopAppBar를 사용할 수 있습니다. 개발자는 CSS 클래스의 구현 세부정보에 관해 걱정하지 않고 TopAppBar API와 상호작용할 수 있습니다.

이제 어댑터를 구현했습니다. 다음 섹션에서는 기초를 인스턴스화하는 방법을 설명합니다.

구성요소 마운트 및 마운트 해제

Foundation 인스턴스화는 componentDidMount 메서드에서 발생합니다.

먼저 TopAppBar.js의 기존 가져오기 뒤에 다음 가져오기를 추가하여 MDC 상단 앱 바 기반을 가져옵니다.

import {MDCTopAppBarFoundation, MDCFixedTopAppBarFoundation, MDCShortTopAppBarFoundation} from '@material/top-app-bar';

다음으로 TopAppBar 클래스에 다음 JavaScript 코드를 추가합니다.

export default class TopAppBar extends React.Component {
 
  ... 

  foundation_ = null;

  componentDidMount() {
    this.initializeFoundation();
  }

  componentWillUnmount() {
    this.foundation_.destroy();
  }

  initializeFoundation = () => {
    if (this.props.short) {
      this.foundation_ = new MDCShortTopAppBarFoundation(this.adapter);
    } else if (this.props.fixed) {
      this.foundation_ = new MDCFixedTopAppBarFoundation(this.adapter);
    } else {
      this.foundation_ = new MDCTopAppBarFoundation(this.adapter);
    }

    this.foundation_.init();
  }
 
  ... 

}

한 가지 좋은 React 코딩 방법은 propTypes 및 defaultProps를 정의하는 것입니다. TopAppBar.js의 기존 가져오기 뒤에 다음 가져오기를 추가합니다.

import PropTypes from 'prop-types';

그런 다음 TopAppBar.js 하단의 구성요소 클래스 뒤에 다음 코드를 추가합니다.

import PropTypes from 'prop-types';

TopAppBar.propTypes = {
  alwaysCollapsed: PropTypes.bool,
  short: PropTypes.bool,
  fixed: PropTypes.bool,
  prominent: PropTypes.bool,
  title: PropTypes.string,
  actionItems: PropTypes.arrayOf(PropTypes.element),
  navIcon: PropTypes.element,
};

TopAppBar.defaultProps = {
  alwaysCollapsed: false,
  short: false,
  fixed: false,
  prominent: false,
  title: '',
  actionItems: null,
  navIcon: null,
};

이제 상단 앱 바 React 구성요소를 구현했습니다. http://localhost:8080으로 이동하면 데모 페이지를 사용할 수 있습니다. 데모 페이지는 MDC 웹의 데모 페이지와 동일하게 작동합니다. 데모 페이지는 다음과 같이 표시됩니다.

3d983b98c2092e7a.png

6. 마무리

이 튜토리얼에서는 React 애플리케이션에서 사용할 수 있도록 MDC Web의 Foundation을 래핑하는 방법을 다뤘습니다. GitHub 및 npm에는 프레임워크에 통합에 설명된 대로 MDC 웹 구성요소를 래핑하는 라이브러리가 몇 가지 있습니다. 여기에 있는 목록을 사용하는 것이 좋습니다. 이 목록에는 React 외에도 Angular, Vue와 같은 다른 프레임워크도 포함되어 있습니다.

이 튜토리얼에서는 MDC 웹 코드를 기초, 어댑터, 구성요소의 세 부분으로 나누기로 한 결정을 집중적으로 다룹니다. 이 아키텍처를 사용하면 구성요소가 모든 프레임워크에서 작동하는 동안 공통 코드를 공유할 수 있습니다. Material Components React를 사용해 주셔서 감사합니다. 새로운 라이브러리인 MDC React를 확인해 보세요. 이 Codelab에 만족하셨길 바랍니다.

적절한 시간과 노력을 들여 이 Codelab을 완료할 수 있었습니다.

매우 동의함 동의함 보통 동의하지 않음 전혀 동의하지 않음

앞으로 머티리얼 구성요소를 계속 사용하고 싶습니다.

매우 동의함 동의함 보통 동의하지 않음 전혀 동의하지 않음