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하면서 보다 깊은 내용을 확인해보도록 하겠다.

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

SessionDB 개발 (Web Storage를 이용한 간이 DB)


원래는 이번 주에 Till60의 Prototype을 개발해서 전체적인 흐름을 한 번  짚어볼 예정이었다. 항상 구체적인 무엇인가로부터 출발하는 것이 

보다 명확한 진행을 가능학 해준다고 믿고 있는 나로서는 Prototype 개발은 꽤나 중요한 과정이다.


그런데 어느정도 동작 가능한 Prototype을 개발하려 했더니 아무래도 데이터 처리를 위한 준비가 번거로웠다. 물론 H2 Database같은 

In-memory db가 지원되어 간편하게 사용할 수 있지만 Prototype 개발에는 그조차 사치로 여겨졌다. 좀더 간편하게 데이터 흐름까지

짚어볼 수는 없을까?


곰곰이 생각을 하다가 sessionStorage를 이용해보는 것은 어떨까 하는데 생각이 미쳤다. 많은 제약이 있기는 하지만 개인 프로젝트의

Prototype을 만드는 정도에는 충분히 역할 할 것 같았다. 기왕 생각이 여기에 미친 바에는 차라리 제대로 기능을 만들어보자고 결심하고 

이틀간 열심히 지지고 볶아 sessionStorage와 localStorage를 DB처럼 사용할 수 있는 javascript 모듈과 AdminLTE UI 라이브러리를

이용해 Admin 화면까지 만들어보았다.


시스템에 대한 자세한 내용은 링크로 대신하고 이 글에서는 간단하게 화면 구성과 사용법을 적어보고자 한다.


소스코드 : https://github.com/mazdah/SessionDB



간단한 소개


앞서 말한대로 이 툴은 sessionStorage와 localStorage를 DB 처럼 사용할 수 있게 만든 툴이다. 가장 큰 장점이라면 아무런 설정 없이

바로 DB  처럼 사용 가능하며 단 하나의 파일로 CRUD의 처리를 모두구현할 수 있다는 점, 그리고 네트워크 연결 없이도 로컬에서 DB와 유사한

기능을 사용할 수 있어 프로젝트 초기 아무런 세팅도 안되어 있어 멍청한깡통 PC가 되버린 개발 PC에서도 충분히 흐름을 짚어볼 수 있다는 

점이 장점이라고 할 수 있을 것이다. 그리고 기본 javascript로 구현되어있어다른 javascript 라이브러리도 필요가 없다. 다만 Admin화면을 

위해서는 JQuery와 Bootstrap이 필요하다.


다만 Web Storage 기반인만큼 RDBMS보다는 NoSQL에 가깝고 데이터를 원격으로 공유할 수 없다는 점 등은 단점이 될 것이다.

하지만 데이터 공유 문제는 Export/Import 기능으로 어느 정도 만회가 될 수 있을 듯하다.


기능 설명


일단 초기 화면은 다음과 같다. Dashboard에서는 sessionStorage와 ocalStorage에 생성된 테이블의 갯수와 목록, 그리고 각 테이블에

생성된 데이터의 갯수가 표시된다.





좌측의 메뉴를 보면 가장 상단에는 sessionStorage와 localStorage를 선택하는 select 컨트롤이 있다. 이후 작업은 여기서 선택한 

storage에서 일어나게 된다.


다음은 앞서 본 대시보드로 이동하는 메뉴이며 3번째 메뉴는 생성된 테이블 목록을 보여주는 메뉴이다. 각 테이블 이름을 클릭하면 해당 

테이블에 대한 작업을 할 수 있는 화면으로 이동한다.


다음은 Create Table 버튼으로 이 버튼을 누르면 테이블 생성 화면으로 이동한다. 마지막으로 sessionStorage를 주로 이용하게 되므로 

Admin 화면에서 바로 테스트용 페이지로 이동 가능하도록 페이지 이동 기능을 추가하였다. Input 박스에 이동할 페이지 경로를 입력하고 

Go 버튼을 클릭하면 현재 윈도우에서 해당 페이지로 이동하게 된다.




먼저 Create Table 버튼을 클릭하여 테이블을 생성해보자. 테이블 생성은 매우 간단하며 테이블 이름을 입력한 후 Create Table 버튼을 

클릭하면 테이블이 생성된다. 물론 앞서 말한 것처럼 좌측 상단에 선택한 storage 영역에 테이블이 생성된다. 테이블은 아주 간단하게

 {“table”:[]}의 구조로 만들어지며 앞으로 table을 Key로 하는 배열 영역에 데이터들이 저장된다.





테이블을 생성했으니 이제 데이터를 insert 해보자. SessionDB는 스키마가 없는 JSON 기반의 DB이다. 따라서 테이블 생성시에는

아무런 구조 없이 그냥 빈 배열 하나가 만들어진다. 따라서 최초에 데이터를 입력할 때는 아래 그림과 같이 JSON 포맷으로 문자열을 

입력해주어야 한다.



아래와 같이 입력되었다.



하지만 한 번 데이터가 들어간 이후에는 입력된 데이터의 key를 기준으로 가상의 컬럼을 생성한다. 따라서 데이터 입력 시 아래와 같은 화면이 

나타난다.

데이터 입력시 편의를 위해 이렇게 구성은 하였으나 이로 인해 schemeless의  유연성이라는 장점은 포기했다. 즉, 한 테이블의 모든 데이터는 

동일한 갯수와 이름의 key로 구성되어야 정상적으로 사용이 가능할 것이다.


입력된 데이터는 테이블 화면에 목록으로 보여지며 항목 우측에 삭제와 수정 버튼 그리고 하단에 Export, Import, Drop Table 등의 기능 

버튼이 있다.


차례차례 살펴보면…

삭제 버튼은 당연히 데이터를 지우는 기능이다. 클릭하면 데이터가 바로 지워진다. 수정으 Update 기능에 해당하며 클릭하면 아래 이미지와 

같은 팝업이 뜨고 내용을 변경한 후 Update Row 버튼을 클릭하면 데이터가 수정된다.




Export Table 버튼을 클릭하면 화면 하단의 Textarea에 테이블 전체 데이터가 JSON 포맷의 문자열로 표시된다. 다만 기본 테이블 구조와 

다른 것은 tableName Key가 추가되어있다는 것이다. 이 문자열을 그대로 복사해 다른 PC에 설치된 툴에서 Import Table을 이용하면 

불편하나마 데이터를 공유할 수 있다.




Import Table은 Export Table을 했을 때 출력되는 포맷의 JSON 문자열로 테이블에 데이터를 입력하는 기능으로 2가지 옵션이 있다. 

각각의 이름대로 추가하기를 선택하면 기존 데이터에 Import 할 데이터가 덧붙여 지며 대체하기를 선택하면 기존 데이터는 삭제되고 새로운 

데이터로 테이블이 채워진다.


추가하기 선택 시


대체하기 선택 시



마지막으로 Drop Table은 이름 그대로 테이블 자체를 삭제해버리므로 저장된 데이터도 모두 지워진다.


이와 같은 기능으로 필요한 테이블과 테스트용 데이터를 미리 생성한 후 테스트 페이지들을 API를 이용해 구현하여 사용하면 쉽게 데이터 

처리가 가능한 Prototype을 만들 수 있을 것이다.


아직도 개선할 부분이 많이 보이지만 일단 개인적인 목적으로 만든만큼 써가면서 필요한 부분을 수정해 나가야겠다.


이제 다음주 부터는 진짜로 Till60 Prototype 개발에 집중해야겠다~

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

최초 작성일 : 2011/05/18 11:17 


한 일년 아이폰 앱 개발한다고 설레발 치는 동안
웹의 판도가 많이도 바뀐 것 같다.

여전히 대다수의 SI 프로젝트는 이전과 큰 변화 없이 다만 각각의
구성 요소들에 대한 버전 업 정도의 수준에 머물고 있는 듯하지만
하부 구조의 패러다임이 바뀌게 되면 전체적인 변화는 불가피한 것...

사실상 자바스크립트는 브라우저단에서 보조적인 역할을 하는 가벼운 스크립트 언어
정도로 생각을 했는데 이제는 그 위상이 여느 메이저급 language들을 위협할
정도가 되어버린 것 같다.

 UI면 UI, 비지니스 로직이면 비지니스 로직
웹이면 웹, 모바일이면 모바일
이미 전천후 팔방미인 언어가 되어버린지 오래인 것 같다.

내가 기술을 설명할 능력은 안되니 링크로 대신...^^;;;

http://dev.paran.com/2011/05/17/nowjs-nodejs/

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^