개인 개발 프로젝트/Material-UI 실습

[Material-UI 실습] 11. Stepper

종범2 2020. 2. 3. 23:02

Stepper 설명과 예시

Stepper는 프로세스를 표현하는 UI 컴포넌트로 Stepper, 컨텐츠, 버튼이 항상 같이 등장한다. 우선 Stepper를 사용하는 가장 간단한 예제를 작성해보았다.

 

첫 화면
스텝2 화면
스텝 3 화면
모든 스텝을 완료했을때 화면

이때 상단에 위치한 UI 컴포넌트가 Stepper로 재 스텝, 이미 지난 스텝, 남은 스텝을 적절하게 보여준다. Stepper 다음에는 step에 따라 정해진 컨텐츠를 보여주며, 마지막에는 스텝의 증가 감소를 조절하는 버튼을 보여준다.

 

첫 번째 step에서는 다음 버튼만 활성화하며 다음 버튼을 클릭하면 다음 step으로 넘어간다. 그 이후에는 이전 버튼과 다음 버튼을 모두 활성화하며 이전 버튼을 클릭하면 이전 step으로 돌아간다. 마지막 step에서는 다음 버튼의 글자를 완료로 바꿔 보여준다. 완료 버튼을 클릭하면 모든 스텝이 종료되었을 때의 내용을 보여주며 다시 시작하기 버튼을 클릭하면 첫 번째 step으로 돌아간다.

 

Stepper를 표현하는 UI 방식은 매우 다양한데 위의 예제는 가장 간단한 예제이다. hooks를 이용하여 예제를 구현하는 코드와 class를 이용하여 예제를 구현하는 코드를 적고 전체적으로 코드를 설명하겟다.

hooks를 이용하여 Stepper를 구현한 코드

StepperTutorial1.js

import React, { useState } from 'react';
import Stepper from '@material-ui/core/Stepper';
import Step from '@material-ui/core/Step';
import StepLabel from '@material-ui/core/StepLabel';
import Button from '@material-ui/core/Button';
import { makeStyles } from '@material-ui/core/styles';
const useStyles = makeStyles({
  stepper: {
    background: '#c7ecee'
  },
  content: {
    padding: '30px',
    width: '100%',
    height: '200px',
    background: '#dff9fb'
  },
  button: {
    padding: '30px',
    width: '100%',
    height: '30px',
    background: '#c7ecee'
  }
});
const StepperHooks = () => {
  const classes = useStyles();
  const [activeStep, setActiveStep] = useState(0);
  const steps = ['스텝 1', '스텝 2', '스텝 3'];
  const getStepContent = stepNumber => {
    switch (stepNumber) {
      case 0:
        return '스텝 1에서는 ... 하세요';
      case 1:
        return '스텝 2에서는 ... 하세요';
      case 2:
        return '스텝 3에서는 ... 하세요';
      default:
        return '알수없는 스텝입니다.';
    }
  }
  const handleNext = () => {
    setActiveStep(preActiveStep => preActiveStep + 1);
  }
  const handleBack = () => {
    setActiveStep(preActiveStep => preActiveStep - 1);
  }
  const handleReset = () => {
    setActiveStep(0);
  }
  return (
    <div>
      <Stepper className={classes.stepper} activeStep={activeStep}>
        {steps.map(label => (
          <Step key={label}>
            <StepLabel>
              {label}
            </StepLabel>
          </Step>
        ))}
      </Stepper>
      <div>
        {activeStep === steps.length ? (
          <div>
            <div className={classes.content}>모든 스텝을 완료하였습니다.</div>
            <div className={classes.button} >
              <Button variant="contained" color="primary" onClick={handleReset}>다시 시작하기</Button>
            </div>
          </div>
        ) : (
            <div>
              <div className={classes.content}>
                {getStepContent(activeStep)}
              </div>
              <div className={classes.button}>
                <Button
                  style={{marginRight:'10px'}}
                  variant="contained"
                  color="primary"
                  disabled = {activeStep === 0}
                  onClick={handleBack}>
                    이전
                </Button>
                <Button variant="contained" color="primary" onClick={handleNext}>
                  {activeStep === steps.length - 1 ? (
                    '완료'
                  ) : (
                      '다음'
                    )}
                </Button>
              </div>
            </div>
          )
        }
      </div>
    </div>
  )
}
export default StepperHooks;

hooks를 이용하여 Stepper를 구현한 코드

StepperTutorial2.js

import React, { Component  } from 'react';
import Stepper from '@material-ui/core/Stepper';
import Step from '@material-ui/core/Step';
import StepLabel from '@material-ui/core/StepLabel';
import Button from '@material-ui/core/Button';
import { withStyles } from '@material-ui/core/styles';
const styles  = {
  stepper: {
    background: '#c7ecee'
  },
  content: {
    padding: '30px',
    width: '100%',
    height: '200px',
    background: '#dff9fb'
  },
  button: {
    padding: '30px',
    width: '100%',
    height: '30px',
    background: '#c7ecee'
  }
};
class StepperClass extends Component{
  constructor(props) {
    super(props);
    this.state = {
      activeStep: 0
    };
  }
  handleNext = () => {
    this.setState({
      activeStep: this.state.activeStep+1
    });
  }
  handleBack = () => {
    this.setState({
      activeStep: this.state.activeStep-1
    });
  }
  handleReset = () => {
    this.setState({
      activeStep: 0
    });
  }
  getStepContent = (stepNumber) => {
    switch (stepNumber) {
      case 0:
        return '스텝 1에서는 ... 하세요';
      case 1:
        return '스텝 2에서는 ... 하세요';
      case 2:
        return '스텝 3에서는 ... 하세요';
      default:
        return '알수없는 스텝입니다.';
    }
  }
  render(){
    const { classes } = this.props;
    const steps = ['스텝 1', '스텝 2', '스텝 3'];
    const getStepContent = stepNumber => {
      switch (stepNumber) {
        case 0:
          return '스텝 1에서는 ... 하세요';
        case 1:
          return '스텝 2에서는 ... 하세요';
        case 2:
          return '스텝 3에서는 ... 하세요';
        default:
          return '알수없는 스텝입니다.';
      }
    }
    return(
      <div>
        <Stepper className={classes.stepper} activeStep={this.state.activeStep}>
        {steps.map(label => (
          <Step key={label}>
            <StepLabel>
              {label}
            </StepLabel>
          </Step>
        ))}
      </Stepper>
      <div>
      {this.state.activeStep === steps.length ? (
        <div>
          <div className={classes.content}>모든 스텝을 완료하였습니다.</div>
          <div   className={classes.button}>
            <Button variant="contained" color="primary" onClick={this.handleReset}>다시 시작하기</Button>
          </div>
        </div>
      ) : (
          <div>
            <div className={classes.content}>
              {getStepContent(this.state.activeStep)}
            </div>
            <div className={classes.button}>
              <Button
                style={{marginRight:'10px'}}
                variant="contained"
                color="primary"
                disabled = {this.state.activeStep === 0}
                onClick={this.handleBack}>
                  이전
              </Button>
              <Button variant="contained" color="primary" onClick={this.handleNext}>
                {this.state.activeStep === steps.length - 1 ? (
                  '완료'
                ) : (
                    '다음'
                  )}
              </Button>
            </div>
          </div>
        )
      }
    </div>
      </div>
      
    )
  }
}
export default withStyles(styles)(StepperClass);

 

코드 및 예제 설명

예제에서는 크게 Stepper 컴포넌트, 컨텐츠 및 버튼으로 구성되어있다. Stepper 컴포넌트 내부에는 모든 Step 컴포넌트가 들어있어야한다. 이를 반복문을 이용하여 구현하였다. 각 Step 컴포넌트 내부에는 StepLabel 컴포넌트를 이용하여 컴포넌트의 이름을 표시하도록하였다. Stepper 컴포넌트의 activeStep이라는 props에는 activeStep이라는 state를 전달한다. activeStep이라는 state에는 현재 step을 저장하며 0으로 초기화한다.  activeStep이라는 props에 특정 정수를 N을 넣으면 그 값에 따라 각 Step 컴포넌트가 지난 step인지 현재 step인지 남은 step인지 적절하게 표시한다.

 

그 다음에는 각 step에 맞는 컨텐츠와 버튼을 보여준다. 모든 step을 마쳤을 때와 그렇지 않을 때 보여줄 내용이 다르기 때문에 분기를 이용하여 구현하였다. 모든 step을 마친 경우에는 완료했다는 내용과 다시 시작하기 버튼을 보여준다. 다시 시작하기 버튼을 클릭하면 step값을 초기값인 0으로 설정하여 처음 화면을 보여준다. 모든 step을 마친 경우가 아니라면 각 step에 따른 컨텐츠와 이전 버튼과 다음 버튼을 보여준다. step값에 따른 컨텐츠는 getStepContent 함수에 step값을 전달하여 구한다. 버튼의 경우 마지막 step인 경우에는 다음 버튼이아니라 완료 버튼으로 보여주어야한다. 이전 버튼을 클릭하면 step값을 1씩 감소시키고 다음 버튼을 클릭하면 step값을 1씩 증가시킨다.

 

Steppe 컴포넌트에 관한 내용은 다음을 참고하였다.

https://material-ui.com/components/steppers/#stepper

 

Stepper React component - Material-UI

Steppers convey progress through numbered steps. It provides a wizard-like workflow.

material-ui.com

https://material-ui.com/api/stepper/

 

Stepper API - Material-UI

The API documentation of the Stepper React component. Learn more about the props and the CSS customization points.

material-ui.com

예제 확인

https://jb-material-ui-tutorial.herokuapp.com/

 

Material UI Tutorial

 

jb-material-ui-tutorial.herokuapp.com