본문 바로가기
웹 프론트엔드/React 리액트 (기초)

[React] 아이디, 비번 입력 Input 컴포넌트를 만들어보자

by 지구인한군 2020. 9. 29.

2020/08/18 - [Web프론트엔드/리액트] - [React] 리액트 개발환경 구축

2020/08/21 - [Web프론트엔드/리액트] - Visual Studio Code 및 React 관련 플러그인 설치

2020/09/09 - [Web프론트엔드/리액트] - [React] 리액트 테스트 앱 생성 및 수정

2020/09/11 - [Web프론트엔드/리액트] - [React] 리액트에서 사용하는 JSX

2020/09/11 - [Web프론트엔드/리액트] - [React] 리액트의 컴포넌트를 알아보자

2020/09/15 - [Web프론트엔드/리액트] - [React] 컴포넌트의 데이터 관리 ① - props 프로퍼티

2020/09/19 - [Web프론트엔드/리액트] - [React] 컴포넌트의 데이터 관리 ② - state 스테이트

2020/09/21 - [Web프론트엔드/리액트] - [React] 컴포넌트의 데이터 관리 ③ - 콜백 함수

2020/09/22 - [Web프론트엔드/리액트] - [React] 컴포넌트의 형태를 분류해보자

2020/09/24 - [Web프론트엔드/리액트] - [React] 컴포넌트의 Lifecycle 생명주기 함수 - 이론

2020/09/24 - [Web프론트엔드/리액트] - [React] 컴포넌트의 Lifecycle 생명주기 함수 - 실습

2020/09/28 - [Web프론트엔드/리액트] - [React] 컴포넌트의 DOM 관련 이벤트 - 무한 scroll 예제

 


 지금까지 리액트 관련 글을 쓰면서 배웠던 것들을 이용해 Input 컴포넌트를 만들어 보겠습니다.

최대한 배웠던 기초적인 것들만 이용했으며, 약간의 자바스크립트 관련 로직도 나옵니다. 먼저 실행화면을 보시죠!

 

 앞으로 만들 InputComponent가 두 개 들어가 있습니다. 아이디와 비밀번호 창인데 한글이 입력되면 에러 메시지가 나오며, 비밀번호 창은 방금 입력한 것을 뺀 나머지는 전부 가려져 있습니다. 그럼 App.js부터 가시죠!

 

// ..\test_project\src\App.js

import React, { Component } from "react";
import InputComponent from "./components/InputComponent";

class App extends Component {
  // 하위 컴퍼넌트에서 불러오는 콜백 함수
  chagneInput(name, value) {
    console.log(`chagneInput() name : ${name} , value : ${value}`);
  }

  render() {
    return (
      <div>
        <InputComponent
          label="아이디 : "
          name="id"
          value="아이디를 입력하세요"
          errorMessage="한글 입력중..."
          autoFocus={true}
        />
        <br />
        <InputComponent
          label="비밀번호 : "
          name="pass"
          //type="password"
          value="비밀번호를 입력하세요"
          errorMessage="한글 입력중..."
          onChange={this.chagneInput}
        />
      </div>
    );
  }
}

export default App;

 

 InputComponent 두 개 배치되어 있습니다. 중요 프로퍼티로는 name, value, type, onChange 등이 있습니다. changeInput() 콜백 함수는 하위 컴포넌트에서 불러주므로 다양한 방법으로 응용이 가능합니다.

자 다음은 드디어 오늘의 주인공인 InputComponent.jsx 보시겠습니다.

 

// ..\test_project\src\components\InputComponent.jsx

import React, { PureComponent } from "react";
import PropTypes from "prop-types";

class InputComponent extends PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      value: props.value, // 상위 프로퍼티로 초기화
      isError: false, // 입력 오류가 있는지
      isFirst: true, // 처음 입력창 포커스
    };

    // 콜백 함수 바인딩
    this.setRef = this.setRef.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.handleFocus = this.handleFocus.bind(this);
  }

  // ref 프로퍼티 연결
  setRef(ref) {
    this.ref = ref;
  }

  // 입력창 변경 이벤트
  handleChange(e) {
    const { name, onChange } = this.props;
    console.log(
      `handleChange() name : ${name} , e.target.value : ${e.target.value}`
    );

    // 콜백 함수가 있을 경우
    if (onChange) {
      // 한글 입력 체크
      const pattern_kor = /[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/;
      let checkError = false;
      if (pattern_kor.test(e.target.value)) checkError = true;

      // 비밀번호 입력창은 문자 치환
      let strValue = String(e.target.value);
      if (name === "pass") {
        for (var i = 0; i < strValue.length - 1; i++) {
          strValue = strValue.replace(strValue[i], "*");
        }
      }

      // 변경된 스테이트 값
      this.setState({ isError: checkError, value: strValue });

      // 상위 컴포넌트 콜백 함수 실행
      onChange(name, e.target.value);
    }
  }

  // 입력창에 포커스
  handleFocus(e) {
    // 처음 포커스시에는 입력창 초기화
    if (this.state.isFirst) {
      this.setState({ value: "", isFirst: false });
    }
  }

  // 컴포넌트 마운트
  componentDidMount() {
    // 포커스 맞추기
    if (this.props.autoFocus) {
      this.ref.focus();
    }
  }

  render() {
    // 프로퍼티 받아서 화면 출력
    const { label, name, type, errorMessage } = this.props;
    return (
      <label>
        {label}
        <input
          id={`input_${name}`}
          value={this.state.value}
          type={type}
          ref={this.setRef}
          onChange={this.handleChange}
          onFocus={this.handleFocus}
        />
        {this.state.isError && errorMessage}
      </label>
    );
  }
}

InputComponent.propTypes = {
  label: PropTypes.string,
  name: PropTypes.string.isRequired,
  value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  type: PropTypes.oneOf(["text", "password", "number", "price"]),
  autoFocus: PropTypes.bool,
  errorMessage: PropTypes.string,
  onChange: PropTypes.func,
  onFocus: PropTypes.func,
};

InputComponent.defaultProps = {
  value: "",
  type: "text",
  autoFocus: false,
  onChange: () => {},
  onFocus: () => {},
};

export default InputComponent;

 

 소스가 길어 보이지만 특별한 것은 없고, 이때까지 배운 내용들에서 사용하는 변수가 조금 늘어난 정도입니다. 주석을 달아놓았으니 실습을 해보시면 더욱 이해가 잘 되시라 생각하고, 특히 handleChange() 부분이 중요합니다.

하지만 이번 InputComponent 소스는 리액트의 개발 철학인 컴포넌트의 재활용 측면에서는 잘 못 설계되어 있습니다. 앞으로 배울 리액트의 다양한 기능으로 더욱더, 재활용할 수 있는 컴포넌트를 만들어 갈 것입니다.

 

오늘은 여기까지 입니다. 이때까지 배운 내용으로만 실습한 것이라 다소 무리가 있는 소스이지만, 리액트의 기초적인 부분을 확실히 복습할 수 있는 기회라 생각합니다.

 

그럼 오늘도 참 쉽죠?


2020/09/29 - [Web프론트엔드/리액트] - [React] 리액트 기초 관련 글 정리 (개발환경, 데이터, 컴포넌트 등)

댓글