본문 바로가기
  • SDXL 1.0 + 한복 LoRA
  • SDXL 1.0 + 한복 LoRA
Development/Till60

React 살펴보기

by 마즈다 2017. 1. 31.
반응형



React 개요


지난 시간까지 mongoDB 모델링을 가볍게, 아~주 가볍게 살펴보았다.

그리고 몇차례에 걸쳐 수정했으나 여전히 변경의 여지가 남아있는 찜찜한 상태로 글을 마쳤다.

일단 현 상태로도 정상적으로 기능은 하기에 mongoDB에 대한 깊은 내용은 좀 더 뒤로 미루고

코드가 더 복잡해지기 전에 js쪽 소스들을 React로 conversion 하고자 한다.


최초 Till60 개발에 대한 글을 작성했을 때도 언급했듯이 React 역시 처음 다뤄보는 기술이다.

그런 만큼 우선은 공식 홈페이지에서 개요라도 집고 넘어가야 할 것 같아 우선 오늘은 공식

홈페이지의 메인페이지 내용을 정리해보고자 한다.


1. 기능 목표


기능적으로는 추가 개발 내용이 없다.


2. 기술 목표


기존에 jQuery와 Bootstrap으로 개발된 코드를 React로 conversion.

우선 이번에는 React에 대해 기본 개념을 잡고 다음부터 Sign In, Sign Up 등의 가벼운 화면 작업을

시작하도록 한다.


3. 기술 적용 내용


아래 내용은 React 공식 홈페이지의 메인 페이지 내용을 의역한 것이다.


————————————————————————————————————————————————


1. React는 UI의 상호작용을 보다 쉽게 처리할 수 있는데, 변경된 데이터와 관련된 내용만을 업데이트하고

렌더링 하는 방식으로 이를 효율적으로 수행한다. 이러한 방식은 결국 예측 가능한 코드를 만들 수 있게

하고 따라서 디버깅이 쉬워진다.


2. 자신의 상태를 관리할 수 있는 캡슐화된 컴포넌트를 만듦으로써 복잡한 UI를 쉽게 구성할 수 있으며

이러한 로직을 자바스크립트로 작성하기 때문에 다양한 데이터 형식을 쉽게 전달할 수 있고 또 DOM

에 의존하지 않고 상태를 유지할 수 있다.


3. React는 특정한 기술 스택에 의존하지 않고 React 내에서 새로운 기능들을 개발 할 수 있도록 구현

되었다. 또한 React Native를 이용하면 Node를 이용하여 서버측에 렌더링 하거나 강력한 모바일

앱에 렌더링 하는 것도 가능하다.


————————————————————————————————————————————————


  • 간단한 컴포넌트


React의 기본은 입력 데이터를 전달받아 화면에 표시해주는 render()함수를 구현하는 것이다.

아래 코드는 XML 구문과 유사한 JSX를 이용하여 구현된 코드이다. 하지만 JSX 사용이 필수적인

것은 아니다.


class HelloMessage extends React.Component {

  render() {

    return <div>Hello {this.props.name}</div>;

  }

}


ReactDOM.render(<HelloMessage name="Jane" />, mountNode);

위의 코드에서 눈여겨 봐야 할 것은 HelloMessage라는 클래스 이름과 ReactDOM.render의 호출이다.

ReactDOM.render의 호출을 통해 비로소 화면에 데이터가 출력되는데 이 함수 안에서 HelloMessage

클래스가 어떻게 사용되고 있는지 또 그 안에 name이라는 property가 어떻게 HelloMessage 클래스로

전달되는지(ReactDOM.render 함수의 파라미터에 있는 name은 this.props.name이라는 형태로

HelloMessage 클래스 안으로 전달된다)를 주의해서 보자. 이 코드의 결과는 브라우저 화면에 “Hello Jane”

이라는 문자열을 출력하게 된다.


  • 상태를 갖는 컴포넌트


클래스 내에서 this.props를 이용하여 접근 가능한 입력 데이터 외에도 this.state로 접근 가능한 내부 상태

데이터도 관리할 수 있다. 컴포넌트의 상태가 변경되면 render() 함수를 다시 호출하여 렌더링 된 마크업

문서를 업데이트하게 된다.


class Timer extends React.Component {

  constructor(props) {

    super(props);

    this.state = {secondsElapsed: 0};

  }


  tick() {

    this.setState((prevState) => ({

      secondsElapsed: prevState.secondsElapsed + 1

    }));

  }


  componentDidMount() {

    this.interval = setInterval(() => this.tick(), 1000);

  }


  componentWillUnmount() {

    clearInterval(this.interval);

  }


  render() {

    return (

      <div>Seconds Elapsed: {this.state.secondsElapsed}</div>

    );

  }

}


ReactDOM.render(<Timer />, mountNode);


Component의 lifecycle에 대해서는 추후 자세히 알아보기로 하고 여기서는 간단하게 흐름만 알아보도록 하자


1) 우선 constructor에서 이 컴포넌트의 상태를 나타낼 this.state에 0값으로 설정된 secondsElapsed라는 

    요소를 할당하고 있다.

2) render() 함수가 호출된다.

3) Component의 lifecycle에 의해 componentDidMount가 호출되며 이 안에서 자바스크립트의 setInterval

    함수를 통해 1초에 한 번씩 Timer 클래스에 구현된 tick()함수를 호출한다.

4) tick()함수에서는 이전 상태 값을 파라미터로 전달받아 여기에 1을 더한 후 this.state에 할당된 seconds-

    Elapsed에 값을 새로 설정한다.


이러한 과정을 거쳐서 변경된 this.state만을 새롭게 렌더링하여 화면에 보여주게 된다.


  • Application


props와 state를 이용하여 만든 ToDo Application이다. 이 예제에서는 state를 이용하여 사용자가 입력한

텍스트 뿐만 아니라 항목들의 목록을 추적하여 관리한다. 이러한 과정에서 event handler는 마치 inline으로 

렌더링 되는 것 처럼 보이지만 실제로는 event delegation을 이용하여 수집되고 구현된다.


class TodoApp extends React.Component {

  constructor(props) {

    super(props);

    this.handleChange = this.handleChange.bind(this);

    this.handleSubmit = this.handleSubmit.bind(this);

    this.state = {items: [], text: ''};

  }


  render() {

    return (

      <div>

        <h3>TODO</h3>

        <TodoList items={this.state.items} />

        <form onSubmit={this.handleSubmit}>

          <input onChange={this.handleChange} value={this.state.text} />

          <button>{'Add #' + (this.state.items.length + 1)}</button>

        </form>

      </div>

    );

  }


  handleChange(e) {

    this.setState({text: e.target.value});

  }


  handleSubmit(e) {

    e.preventDefault();

    var newItem = {

      text: this.state.text,

      id: Date.now()

    };

    this.setState((prevState) => ({

      items: prevState.items.concat(newItem),

      text: ''

    }));

  }

}


class TodoList extends React.Component {

  render() {

   return (

      <ul>

       {this.props.items.map(item => (

          <li key={item.id}>{item.text}</li>

        ))}

      </ul>

    );

  }

}


ReactDOM.render(<TodoApp />, mountNode);


앞서 상태를 갖는 컴포넌트에서와 같이 흐름을 살펴보도록 하자.


1) TodoApp 클래스에서는 생성자에서 이벤트를 처리할 두 함수인 handleChange와 handleSubmit을

    this. handleChange와 this. handleSubmit에 할당한다. this.state에는 items라는 배열과 text라는

    문자열로 구성된 object를 할당한다.

2) handleChange 함수는 event 객체를 인자로 전달받아 this.state 중 text에 event target에 입력된

    값을 할당한다.

3) handleSubmit에서는 handleChange에서 this.state에 할당한 text 값과 날짜를 기반으로 한 id

    값을 이용하여 새로운 item을 생성하고 이 item을 this.state의 items 배열에 추가(concat)한 후

    setState 호출로 인해 새로 렌더링이 실행된다.

4) TodoList 클래스는 render 함수만 정의되어있으며 파라미터로 전달받은 item 객체를 이용하여 

    li 리스트를 만든다.


ReactDOM.render 함수가 호출되면 1)에서 초기화가 진행되고 TodoApp 클래스의 render() 함수가

호출되는데 render 함수 내에서는 먼저 this.state 중 items 배열을 파라미터로 하여 TodoList 클래스를

렌더링한다. 즉, 4)의 과정이 실행된다. 이어서 input 폼에는 onChange 이벤트 발생 시  handleChange

함수를 호출한다. 2)의 과정이 실행되는 것이다. input의 value는 최초 상태 또는 item이 등록된 후에는

This.state.text 값이 ‘’로 설정되므로 비어있게 될 것이다. 버튼에는 this.state의 items 배열을 참조하여

현재까지 등록된 item + 1의 수가 표시된다. 마지막으로 Add 버튼을 클릭하면 handleSubmit 함수가

호출되면서 3)의 과정이 진행된다.


  • 컴포넌트에서 외부 플러그인 사용


React는 컴포넌트에서 외부 라이브러리나 프레임워크와 인터페이스 할  때 유연할 뿐만 아니라 hooks를

제공하고 있다. 아래 예제는 Markdown 라이브러리인 remarkable을 이용하여 textarea의 text를

실시간으로 변환하는 예제이다.


class MarkdownEditor extends React.Component {

  constructor(props) {

    super(props);

    this.handleChange = this.handleChange.bind(this);

    this.state = {value: 'Type some *markdown* here!'};

  }


  handleChange() {

    this.setState({value: this.refs.textarea.value});

  }


  getRawMarkup() {

    var md = new Remarkable();

    return { __html: md.render(this.state.value) };

  }


  render() {

    return (

      <div className="MarkdownEditor">

        <h3>Input</h3>

        <textarea

          onChange={this.handleChange}

          ref="textarea"

          defaultValue={this.state.value} />

        <h3>Output</h3>

        <div

          className="content"

          dangerouslySetInnerHTML={this.getRawMarkup()}

        />

      </div>

    );

  }

}


ReactDOM.render(<MarkdownEditor />, mountNode);


마찬가지로 흐름을 살펴보면


1) MarkdownEditor의 생성자에서 handleChange를 bind하고 this.state에는 value라는 키의 문자열

    값을 할당한다.

2) handleChange 함수에서는 textarea의 값을 받아 setState를 통해 상태를 변화시키고 다시 렌더링한다.

3) getRawMarkup 함수에서는 remarkable 라이브러리 객체를 생성하고 this.state.value에 저장된

    문자열을 remarkable 객체를 이용하여 렌더링한 후 리턴한다.

ReactDOM.render가 호출되면 1)의 초기화가 진행되고 render 함수가 호출되면 textarea의 onChange 

이벤트 처리를 통해 내용이 변경될 때마다 handleChange 함수를 호출하고 this.state를 변경한다. 

content라는 클래스명을 가진 div에서는 dangerouslySetInnerHTML을 통해 this.state에 할당된

값을 화면에 출력하는데 이 때 getRawMarkup() 함수를 호출한다. 즉, 3)의 과정을 거친 내용이 화면에

출력된다.


4. 참조 (site 또는 서적)


React 공식 홈페이지 : https://facebook.github.io/react/


5. 확인 사항 (의문 사항)


실제 HTML과 jQuery만으로 구현된 웹 페이지를 React로 conversion하면서 좀 더 심화된

내용 확인이 필요하다.


6. 평가


오늘은 간단히 React 홈페이지의 메인 페이지에 있는 4가지 예제을 살펴보았다. 아직은 그야말로

맛보기 정도에 불과하지만 그래도 대략 감을 잡기에는 부족하지 않은 내용이었다. 앞으로 현재까지

진행된 Till60 홈페이지를 React로 conversion하면서 보다 깊은 내용을 확인해보도록 하겠다.

반응형