블로그 이미지
윤영식
Frontend Application Architecter, Full Stacker, KnowHow Dispenser and Bike Rider

Publication

Statistics Graph

Category

Recent Comment

2015.09.05 17:54 HTML5, CSS3

토요일 이른 아침 부슬부슬 비가 내리고 있다. 요즘들어 늦게 잠이 들기 때문에 7시가 되어도 이른 아침의 피곤함을 느낀다. 하지만 오늘은 기대하던 DOM 사상을 배우는 날이라 아침까지 챙겨먹으며 몸단장하고 지하철에 올랐다. 주말이라서 사람들이 없어서 다행이다. 사람들이 눈을 감고 잠을 자는지 명상을 하는지 생각하고 있는 사이 가산 디지털단지에 도착. 창업진흥원을 찾아 20층에 올라왔다. 강의장은 아래 모습으로 마치 헤커톤에 온듯 자유로운 공간을 가지고 있다. 바로 앞에 사자머리 제임스 고슬링 코스프레 아저씨가 버티고 있어서 시야를 방해한다. 그래도 React가 Virtual DOM을 만들어 지원하고 있는데 DOM을 알 수 있다면 이쯤이야... 






시작하기 


"DOM은 사용자를 고려한다" 

기술중심의 언어가 아닌 사용자 중심의 언어이다. DOM은 UI/UX를 제공하고 사용자의 접점이기 때문이다. 웹페이지 구성을 보면 HTML은 구조 CSS는 표현 DOM은 구조와 표현의 CRUD를 담당하고 통신은 HTTP, Socket, Ajax가 하고 Javascript는 컨트롤을 한다. 


"DOM은 동적이다."

동적 페이지를 제공하고, UI(User Interface)는 사용자 행동, 시스템 응답 중심이라면 UX(User eXperience)는 사용자 경험, 습관, 관습 중심이며 이벤트(Event)는 사용자 행동 인식 및 요구/요청에 응답한다. 이 모든 것은 DOM 기본으로 알아야 이해할 수 있다. 


"DOM은 도큐먼트로 작성한 것을 객체 개념으로 다양한 언어에서 접근토록 모델을 제공한다."

Document : "<!DOCTYPE html> ~ </html> 이 DOM 영역이다."

Object : 객체 개념으로 접근하고 객체 요소에는 행동과 속성이 있다. 이들은 key: value형태이다. 

Model : 다수의 언어에서 사용하기 위해 Interface 행태로 DOM을 제공한다. DOMString 형태로 표기한다. 


"Model을 바탕으로 Object를 만든다."

개발자 입장에서 중요한 것은 객체이고, 객체를 핸들링하는 것이다. 모델은 엔진이 사용해서 객체를 만들어 주는 시스템적인 것이다. Object로는 Document, Element, DOM Traversal, DOM/마우스/키보드/HTML/Mutation 이벤트, Selector, DOM View, CSSOM ( CSS Rule Set ) 등이다. 참조





DOM API/Level


"DOM의 기술적 의미는 API를 다루는 것이다."

DOM은 버전이라 하지않고 레벨이라 한다. DOM Level1은 마크업만 DOM Level2에서 CSS가 들어오고, 마우스 이벤트, DOM Traversal and Range가 들어옴. DOM 2부터 MS가 참여를 하지 않으므로 Cross Browsing 문제가 발생하기 시작한다. 관련된 iExplorer 버전은 6,7,8 이다. DOM 3는 XHTML 1.1 기준으로 나온다. 


"DOM Level 1, 2, 3 에서 HTML 5 로 넘어왔다."

많은 이벤트 타입이 생기고, CSSOM (CSS Object Model) 분리가 된다. 예로 CSS, Media Query, Selector 등이다.  


"HTML5의 핵심은 플랫폼이다."

오픈 플랫폼이며 표준을 바탕으로 하고, 최종 목적은 컨텐츠 제공이다. 브라우져 하나만 가지고 모든 것을 하겠다는 플랫폼의 의미가 들어왔다. 예로 Indexed DB 임베드. 네트워크가 끊어져도 작동을 할 수 있다. (스티브 잡스가 iPhone 4 발표때 HTML5를 이야기했다.)


"HTML5 분류는 마크업과 API이다."

마크업은 <video> <section>, 콘텐츠 모델이 새로 들어오고, <menu>, <i>, CSS 등이 재정이 된다. 

API는 <canvas> <svg>로 직접 표현이 가능하고, Web Socket, IndexedDB등을 JS로 제어해 간접 표현이 가능하다. 

* DOM 4는 HTML5 에서 분리되어 진행중이다. 




DOM 구성 요소 


"!doctype 최상위 요소 #document 이다."

Document -> Element -> Node

엘리먼트는 Element Interface를 Object로 생성한 것이다. 노드는 12개의 타입이 있다. 노드는 엘러멘트보다 더 작은 레벨이다. 노드도 오브젝트이다. 

크롬 DevTools를 통해 head의 Node목록을 보면 엔터(enter) 키가 있는 text까지 해서 총 7개의 노드가 존재한다. 



Document 객체의 속성을 살펴보자.

JS Bin on jsbin.com


- doctype

- implementation : 브라우저에서 지원하는 기능. 자바스크립트에서 사용할 일이 별로 없다. 




document 메소드


getElemntsByName : 반환값 배열

JS Bin on jsbin.com


getElementsByTagName : 태그의 이름, 반환값 배열 

getElementsByClassName : class 스타일 이름, 반환값 배열 

getElementById : id 이름. DOM Tree에서 가장 먼저 나온 id의 값을 리턴함 (만일 id가 중복되어 있다면), 객체


createElement / createTextNode / createAttribute / createDocumentFragment / createRange / importNode / adoptNode  ==> innerHTML 로 사용

Document Method-1 on jsbin.com


createAttribute

JS Bin on jsbin.com





Element


"엘리먼트 노드와 엘리먼트는 틀리다."

- 엘리먼트 노드 = <div id="sports">

- 엘리먼트 = <div> ~ </div> 


"엘리먼트는 오브젝트 관점과 Data 관점으로 보아야 한다."

HTML은 오브젝트이고 데이터가 있다로 인식을 변환한다. (DOM Spec Element Interface), 엘러먼트(div)에는 기본 13개의 속성이 존재한다. 노드의 명칭(nodeName)은 일반적으로 #을 붙여준다. 예로 #document로 표현한다. 


엘러먼트의 다양한 속성들 : nodeName, attributesclassList 많이 사용한다. <!doctype> 때문에 tagName 대신 nodeName 을 사용한다. 

Document Method-2 : createAttribute-2 on jsbin.com


"Element-client 는 CSS BoxModel과 관계한다."

clientHeight : scrollBar, border, margin을 미포함한다. 즉, 엘리먼트 높이와 padding만 포함. 

clientWidth : 엘리먼트 넓이와 padding만 포함.


- Element Client

"CSS BoxModel은 margin padding border 이다."

Margin은 내집의 울타리 밖으로 다른 집과의 공통 구역. Padding은 내 울타리안이다. Border는 내 울타리의 넓이이다. 

clientHeight, clientWidth, clientLeft, clientTop 예제 

Element Client-1 on jsbin.com


- Element Offset

"offset은 엘리먼트가 부모와 떨어진 거리이다."

position 속성(absolute, relative, fixed, static)에 따라 부모와의 offset 값이 틀려진다. 예로 마우스 클릭의 좌표값을 구할 때 사용. 

offsetHeight : 엘러먼트 높이 padding, scrollBar, baroder를 포함하고 margin 미포함 한다. 

offsetParent는 CSS offset과 관련이 있고 DOM tree에서 가장 가까운 선조 엘리먼트이다. 항상 offsetParent와 offsetElement가 같지는 않음 


- Element Scroll

scrollHeight : 엘리먼트 높이, padding 포함, scrollBar, border, margin 미포함 

Element Scroll on jsbin.com


- Element Child, 그외 것들 

childElementCount, childNodes : text 노드 포함 , children: text 노드 미포함 (Collection 타입)

childNodes는 7개이고 children은 3개이다. text는 엔터키 값을 갖는다. 

Element Child on jsbin.com

- Element Text

innerHTML : 텍스트 형태를 HTML 형태로 자식으로 첨부. HTML5에서 표준이 되었다. DOM3에서 속도가 느렸으나 현재는 빠름 

outerHTML : 텍스트 형태를 HTML 형태로 자신을 포함하여 대체한다. (주의)





Element 메소드 


getAttribute, setAttribute, hasAttribute, removeAttribute (delete는 반환하는 엘러먼트가 없고 remove는 반환해 준다)

appendChild, removeChild, replaceChild, cloneNode, hasChildNodes

appendChild를 통해 전/후 위치이동 예제

Element Method on jsbin.com


* replaceChild는 사용하지 말고 가능한 removeChild 후에 appendChild로 호출해서 사용한다. 즉, 한꺼번에 두가지 기능을 하지 않게 한다. 

* cloneNode는 노드의 모든 속성값을 가져옴. el.cloneNode(true);





DOM Traversal


DOM 트리 : DOM 노드가 구조적으로 연결한 형태 

노드 : DOM 트리 구성 요소 (12개의 노드 타입) - DOM Spec Node Interface

인터페이스의 상수 값은 NodeType 이다. 



"DOM Traversal은 이동은 Node 단위이다."

Node에는 text도 들어간다. 만일 node가 text이면 nodeType 값은 3 이다. node가 element이면 nodeType이 1 이다. DOM Traversal 프로퍼티인 firtNode, firstElementNode, lastChild, lastElementNode, nextSlibling, nextElementSlibling, previousSibling, previousElementSibling 을 이용해서 접근이 가능하다. 이대 *Element*가 붙으면 text노드를 제외한다. 


하루 종일 DOM에 대해 목이 잠길 때까지 열정 강의를 해주신 김영보 선생님에게 감사하다. 내일이 기대된다. 


신고
posted by peter yun 윤영식
2015.09.01 17:01 Meteor

페북 미티어 스쿨의 박승현님이 스마트링크 멤버를 위해 미티어 중급강좌를 시작했다. 첫회에는 Reactivity, 선언적 프로그래밍, Meteor Way, 발행/구독 (Pub/Sub), DDP, Template/Blaze 등에 대한 기초적인 부분을  집고 넘어갔다.  






한번 훑기


- 패키지 설치하면 .meteor안에 심볼릭 링크 (npm -g 의 폴더에 실제 설치됨)

- Live Hot Push(Reload) : CSS는 hot push 이지만 소스 변경은 hot reloading으로 두가지 다 일어난다

- Pub/Sub, Method Call : WebSocket 이 기반이다. 파일 업로드(Method Call 사용)

- WebApp 기본 패키지를 이용해서 RESTful 가능

- 서버 클라이언트간 싱크에 대한 이벤트 체크 API가 존재한다. 예) ready()

- 미니몽고 - 클라이언트 자바스크립트로 구현

- 퓨전페신져 : https://www.phusionpassenger.com/ 웹서버를 사용 stick session 으로 scale out 가능하게 해 줌 

- 로보몽고 또는 몽고쉐프를 사용해라

- 폴더

  + lib > client, server, private, public 즉, lib가 가장 먼저 로드된다. 

  + private은 Assets API를 통해 : 미리 fixture로 데이터를 등록할 때 사용한다. 

  + packages폴더 : 공통 컴포넌트들





Reactivity 


엑셀의 예를 생각 하면된다. 위키의 Reactive Programming을 봐도 엑셀에 대한 예로 설명되어 있다. 엑셀에서 데이터를 넣은 셀을 미티어에서는 Reactive Data Source라고 하고 엑셀의 더하기 sum(c1, c2)를 적용한 셀을 Reactive Computation이라 한다. 

  - Reactive Computation : 쉘의 펑션 sum(c1, c2)

  - Reactive Data Source : 쉘의 밸류  c1, c2

이것을 도형으로 그리면 다음과 같이 Reactive Data Source를 Reactive Computation이 감싸서 감시를하고 변경 발생시 변경을 알리게 된다. 변경은 또한 변경을 감지하는 Reactive Computation으로 전파(Transition)되어 변경을 반영한다. 예로 하위의 Reactive Data Source를 하위 Reactive Computation에서 감지하고 다시 Template에서 변경을 감지하고 DOM을 변경한다. 




미티어에서 이야기하는 Reactive Data Source와 Reactive Computation에 대한 대표적인 예를 보면 Reactive Data Source는 Reactive Computation에 감쌓여 있어야 반응을 한다는 것이다. 

Tracker.autorun(function () { Meteor.subscribe("messages", Session.get("currentRoomId")); 

});


미티어에서의 Reactive Data Source/Computation의 종류 


- Reactive Computation

  + Templates

  + Tracker.autorun

  + Blaze.render, Blaze.renderWithData


- Reactive Data Source 

  + Session variables

  + Database queries on Collections

  + Meteor.status

  + ready() method on a subscription handle

  + Meteor.user

  + Meteor.userId

  + Meteor.loggingIn


- mini-mongo (data source) <-> template.helpers list (list도 computation 이고) -> blaze의 {{#each list}} (list의 computation이 전이되어 each computation으로 와서 변경이 이루어진다)

- 필요악 : session, mini-mongo cursor, pub/sub의 ready() 는 전부 computation이 이루어 진다.

- 데이터 소스를 직접 만들기는 ReactiveVar 를 사용한다. Computation은 Tracker.autorun으로 구현 





템플릿


미티어에서 Blaze는 기본 템플릿 엔진이지만 템플릿이 모두 컴포넌트로 바뀐다. 단순 템플릿 스트링 이기 보다. React처럼 템플릿이 인스턴스로 변환이 되고 OnCreated, OnDestroyed, OnRendered가 인스턴스별로 호출이된다. 그러나 Template.<templateName>.helpers / events / rendered : 안에서 this는 Template 이므로 주의를 한다. 


  - Blaze : 서버의 데이터 변경에 따른 화면의 변경을 보여주기 

  - {{> }} 의 > 은 인스턴스를 만드는 것이다. 

  - 템플릿 레벨에서 라우팅을 하는 것으로 패턴이 바뀜 

  - Computation의 전이(Transition)를 통해 다이나믹하게 화면을 변경할 수 있다. 





참조 


Meteor의 Reactive의 해부


중급반 샘플 사이트 공개 
http://wagglechat.meteor.com/

ㄱ. 미티어 스쿨
http://meteorschool.com

ㄴ. 오픈튜토리얼 
https://opentutorials.org/course/1591

ㄷ. digveloper 블로그
[1강] 미티어 소개 
http://digveloper.ppillip.com/?p=482

[2강] 미티어폴더구조,템플릿
http://digveloper.ppillip.com/?p=486

[3강] 템플릿리뷰, 콜렉션
http://digveloper.ppillip.com/?p=495

[4강] publish(발행)/subscribe(구독) , 비개발자의 미티어 서비스개발기
http://digveloper.ppillip.com/?p=499

[5강] Router(라우터)
http://digveloper.ppillip.com/?p=502

[6강] Accounts(로그인 및 계정)
http://digveloper.ppillip.com/?p=507


신고
posted by peter yun 윤영식
2015.08.23 17:48 React

Semantic UIReact 컴포넌트로 만들고 NPM 저장소와 Meteor 저장소인 Atmosphere에 배포해 보자. 토요일 헤커톤 모임을 갖고 스마트링크 멤버분들과 함께 진행했다. 목표는 React Bootstrap 과 같은 오픈소스 패키지를 만들어 보는 것이다. 처음엔 활짝 웃고 있는 모습!







준비 운동 


함께 개발을 진행하는 관계로 코딩 컨벤션은 에어비엔비의 React 코드 컨벤션으로 한다.  다음으로 깃헙(GitHub) 저장소를 만들었다. 소셜 코딩은 역시 깃헙이다. 깃을 사용하므로 커밋 메세지를 잘 작성하면 별도의 CHANGELOG를 작성할 필요가 없다. 따라서 Git Commit에 대한 컨벤션도 앵귤러에서 사용하는 방식을 쓴다. 그리고 정보를 찾고 공유하는 것은 슬랙(Slack)을 이용하고, 할일에 대한 보드는 트렐로(Trello)를 이용해 서로 공유하고 있다. 트렐로를 슬랙과 연동하면 슬랙에서 모든 것을 확인 할 수 있다. 


일단 npm 초기화을 초기화하고 모듈을 설치한다.  package.json 에 설치하는 모듈은 다음과 같다. 

  - ES6를 사용하기에 Babel을 통해 ES5로 트랜스파일한다. 

  - React로 설치하고, classnames는 리액트 코드안에서 클래스명을 확장해 주는 기능을 한다. 

  - fbjs는 ES7의 dectorator를 사용해 React Start Kit에 있는 withStyle을 컴포넌트별로 적용해 보려 했으나 원하는 동작이 안된다. 제거해도 좋음. 

  - *-loader는 웹팩(Webpack)을 통해 전체 패키지를 하나의 배포 파일로 번들링하기 위해 설치한다. 

package.json에 설정을 넣고 한번에 npm install 해서 설치하자

// package.json 일부 내역 


"dependencies": {

  "babel": "^5.8.21",

  "react": "^0.13.3",

  "classnames": "^2.1.3",

  "fbjs": "0.1.0-alpha.7"

},

"devDependencies": {

  "babel-core": "^5.8.22",

  "babel-loader": "^5.3.2",

  "css-loader": "^0.15.6",

  "fbjs": "0.1.0-alpha.7",

  "semantic-ui-css": "^2.0.8",

  "style-loader": "^0.12.3",

  "url-loader": "^0.5.6",

  "webpack": "^1.11.0"

}

 

다음으로 웹팩 환경파일인 webpack.config.js 를 만든다.  웹팩은 사전에 설치할 것이 있는데 홈페이지의 Getting start를 보자. 그리고 어떻게 잘 사용할지는 페북 엔지니어인 피트헌트의 Webpack Howto를 참조한다. 설정 내용은 다음과 같다. 

  - 배포 파일 명칭은 reactjs-semantic-ui.js 파일이고, /src/index.js 파일에 모든 참조 내역을 작성한다. 

  - 배포시 접근하는 네임스페이스는 RSU (React Semanctic Ui) 이다. 예로 본 패키지를 리액트 코드에서 사용하면 RSU.Button 식의 접근이다. 

  - .js 또는 .jsx 확장자는 babel-loader로 ES6를 ES5로 트랜스파일한다. 

  - 웹팩은 css 파일과 이미지 파일도 함께 번들링 하므로 style-loader 와 css-loader를 설정하고, 이미지와 폰트 관련해서 url-loader를 설정한다. 

  - react.js 관련 파일은 웹팩으로 번들링시에 제외토록 externals를 설정한다. 

// webapck.config.js 내역 


module.exports = {

entry: {

'reactjs-semantic-ui': './src/index.js'

},

output: {

path: './dist',

filename: '[name].js',

library: 'RSU',

libraryTarget: 'umd'

},

module: {

loaders: [

{

test: /\.jsx?$/,

exclude: /(node_modules|bower_components)/,

loader: 'babel-loader'

                       },

{

test: /\.css$/,

                                exclude: /\.useable\.css$/,

loader: 'style-loader!css-loader'

},

{

test: /\.(png|jpg|svg|eot|woff|woff2|ttf)$/,

loader: 'url-loader?limit=8192'

} // inline base64 URLs for <=8k images, direct URLs for the rest

                ]

},


externals: [

          {

              'react': {

                   root: 'React',

                   commonjs2: 'react',

                   commonjs: 'react',

                   amd: 'react'

             }

         }

     ]

}


Babel 사용시 주의 점은 간혹 ES7 의 decorator를 사용하거나 특정 스펙 레벨까지 적용하고 싶다면 루트에 .babelrc를 생성하고 stage를 설정해야 한다. stage 설정 가이드를 참조하자.

{

    "stage" : 0

}




 

개발 시작


환경 준비하며 문제점을 해결하는데 3시간 가까이 소비를 했다. 본격적으로 시멘틱 UI를 리액트화 하기 위해 다음과 같은 순서로 작업을 한다. 

  - src/index.js안에 작성한 컴포넌트들을 설정한다. 

  - 시멘틱 UI가 구분해 놓은 Elements, Collections, Views ... 와 같이 구분해서 폴더를 만들어 그밑으로 컴포넌트를 만든다. 

  - 시멘틱 UI의 core css는 reset.css 로 Reset 클래스를 하나 만들었다. 

  - ES6 구문을 사용한다. ES5 Features를 참조하자 

    + import ... from 

    + { key : value } 가 동일 설정이면 { key1, key2 }로 나열 

    + export default RSU 

// Globals
import Reset from './globals/Reset';

// Elements
import { Button, Buttons } from './elements/Button';
import Container from './elements/Container';
import Dimmer from './elements/Dimmer';
import Divider from './elements/Divider';
import Flag from './elements/Flag';
import Header from './elements/Header';
import Icon from './elements/Icon';
import Image from './elements/Image';
import Input from './elements/Input';
import Label from './elements/Label';
import { List, Item } from './elements/List';
import Loader from './elements/Loader';
import Rail from './elements/Rail';
import Reveal from './elements/Reveal';
import Segment from './elements/Segment';
import { Step, Steps } from './elements/Step';

// Collections
import Breadcrumb from './collections/Breadcrumb/Breadcrumb';
import BreadcrumbDivider from './collections/Breadcrumb/BreadcrumbDivider';
import BreadcrumbSection from './collections/Breadcrumb/BreadcrumbSection';
import Form from './collections/Form/Form';
import Grid from './collections/Grid/Grid';
import Column from './collections/Grid/Column';
import Row from './collections/Grid/Row';
import Menu from './collections/Menu/Menu';
import Message from './collections/Message/Message';
import Table from './collections/Table/Table';

// Views
import Card from './views/card/Card';
import CardImage from './views/card/CardImage';
import CardContent from './views/card/CardContent';
import CardHeader from './views/card/CardHeader';
import CardMeta from './views/card/CardMeta';
import CardDescription from './views/card/CardDescription';

const RSU = {
    // Elements
    Button, Buttons,
    Container,
    Dimmer,
    Divider,
    Flag,
    Header,
    Icon,
    Image,
    Input,
    Label,
    List, Item,
    Loader,
    Rail,
    Reveal,
    Segment,

    Step, Steps,

    // Views
    Card,
    CardImage,
    CardContent,
    CardHeader,
    CardMeta,
    CardDescription,

    // Collections
    Breadcrumb,
    BreadcrumbDivider,
    BreadcrumbSection,
    Form,
    Grid,
    Row,
    Column,
    Menu,
    Message,
    Table
}
 

export default RSU;


리액트 컴포넌트는 다음과 같이 만든다. 1주차엔 시멘트 UI의 css를 적용해서 잘 나오는지만 확인하는 것으로 리액트 컴포넌트의 라이프사이클 메소드를 전부 설정해서 넣는다. 

  - 어빈앤비의 리액트 코딩 컨벤션에 따라 작성한다. 

  - shouldComponentUpdate 구문은 향후 Immutable.js 사용과 PureRenderMixin의 조합을 고려해 성능을 향상시켜야 한다. 

    이에 대한 좋은 글이 있으니 꼭 읽어보자.  Immutable.js에 대한 연재글 [1], [2], [3]

  - 리액트의 라이프 사이클 코드가 대부분의 컴포넌트에 중복해서 들어 있으므로 이것도 ES6 구문을 위한 리액트의 Mixin 패키지를 사용해 리팩토링해야 한다.

  - classNames( x, y, this.props.className )은 classnames 패키지를 사용해서 <Card className="ui card <상속 className>"> 을 설정해 준다.  

  - <div {...this.props} 를 하면 상위에 설정한다. 모든 key="value" 애트리리뷰트를 그대로 설정해준다. 단, 주의 점은 <div {...this.props} className...> 처럼 사용하면 className은 오버라이딩 된다는 점이다. 만일 <div className={componentClass} {...this.props}> 로 하고 <Card className="dowon" /> 를 사용하면 className={componentClass} 는 적용이 되지 않는다. 즉 태그안에서 중복된 키이면 뒤에 놓은 것으로 설정이 덮어써진다.

import React, { Component, PropTypes } from 'react';
import card from 'semantic-ui-css/components/card.css';
import classNames from 'classnames';

const propTypes = {
	className: PropTypes.string,
};

const defaultProps = {
	className: ''
};

class Card extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  };

  componentWillMount() {};
  componentDidMount() {};
  componentWillReceiveProps(nextProps) {};
  shouldComponentUpdate(nextProps, nextState) {
    return true;
  };
  componentWillUpdate(nextProps, nextState) {};
  componentDidUpdate(prevProps, prevState) {};
  componentWillUnmount() {}

  render() {
    var componentClass = classNames(
      'ui',
      'card',
      this.props.className
    );

    return (
      <div {...this.props} className={componentClass}>{this.props.children}</div>
    )
  };
}

Card.propTypes = propTypes;
Card.defaultProps = defaultProps;
 

export default Card;


학학 다들 컴포넌트 만드는데 저녁 7시를 치닫고 있다. 아 나만 힘들어...






테스트 하기 


만들어진 컴포넌트를 테스트하기 위해 src 폴더 외에 docs 폴더를 만들어 예제를 만든다. 

  - 시멘틱 UI 처럼 폴더 구조를 가지고 각각 index.html파일을 만든다. 

  - 패키지를 사용하는 입장이기 때문에 react.min.js를 넣고 테스트용이기 때문에 <script type="text/jsx;harmony=true"> 해석을 위해 JSXTransformer도 넣었다.

  - 테스트 코드는 Card.js 이면 CardExample.js 로 해서 작성한다. 

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title> Reactjs Semantic Ui </title> </head> <body style="margin-left: 20px; margin-top: 20px;"> <div id="app"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/JSXTransformer.js"></script> <script src="../../dist/reactjs-semantic-ui.js"></script> <script type="text/jsx;harmony=true" src="./CardExample.js"></script> <script type="text/jsx;harmony=true"> // Card React.render( <CardExample />, document.getElementById('app') ); // </script> </body> 

</html>


CardExample.js를 다음과 같다. 

  - import 구문이 안먹는다. 이유 아는분 메세지 좀 주세요. 그래서 render안에 RSU 네임스페이스 제거하고 사용한다. 

  - Card 관련 <div class="xxx"> 설정을 리액트 컴포넌트로 만들어 사용해 본것이다. 

// import { Card, CardImage, CardContent, CardHeader, CardMeta, CardDescription } from 'RSU';

class CardExample extends React.Component {

  render() {
    var Card = RSU.Card,
        CardImage = RSU.CardImage,
        CardContent = RSU.CardContent,
        CardHeader = RSU.CardHeader,
        CardMeta = RSU.CardMeta,
        CardDescription = RSU.CardDescription

    return (
      <div>
        <h3><a href="http://semantic-ui.com/views/card.html" target="_blank">Card</a></h3>
        <Card>
          <CardImage onClick={Alert} src="../assets/images/kristy.png" />
          <CardContent>
            <CardHeader desc="Kristy" />
            <CardMeta>
              <span className="date">Joined in 2013</span>
            </CardMeta>
            <CardDescription>
              Kristy is an art director...
            </CardDescription>
          </CardContent>
          <CardContent className="extra">
            <a>
              <i class="user icon"></i>
              22 Friends
            </a>
          </CardContent>
        </Card>
      </div>
    );
  }
 

}


이제 테스트를 위해 "npm install -g webpack-dev-server"를 설치하고 실행한다. 실행은 소스 루트에서 한다. 

  - 호출은 http://localhost:8080/docs/views 로 직접 경로를 지정한다. 

$ webpack-dev-server --progress --colors

  0% compilehttp://localhost:8080/webpack-dev-server/

webpack result is served from /

content is served from /Users/yunyoungsik/mobicon/open-sources/unplugdj/src/reactjs-semantic-ui

Hash: 0d552960154cf0b24cf3

Version: webpack 1.11.0

Time: 2759ms

                 Asset    Size  Chunks             Chunk Names

reactjs-semantic-ui.js  113 kB       0  [emitted]  reactjs-semantic-ui

chunk    {0} reactjs-semantic-ui.js (reactjs-semantic-ui) 110 kB [rendered]

    [0] ./src/index.js 418 bytes {0} [built]

    [1] ./src/elements/Button.js 4.6 kB {0} [built]

    [3] ./~/semantic-ui-css/components/button.css 870 bytes {0} [built]

    [4] ./~/css-loader!./~/semantic-ui-css/components/button.css 87.5 kB {0} [built]

    [5] ./~/css-loader/lib/css-base.js 1.51 kB {0} [built]

    [6] ./~/style-loader/addStyles.js 6.09 kB {0} [built]

    [7] ./src/utils/withStyles.js 3.86 kB {0} [built]

    [8] ./~/fbjs/lib/invariant.js 1.51 kB {0} [built]

    [9] (webpack)/~/node-libs-browser/~/process/browser.js 2.02 kB {0} [built]

   [10] ./~/fbjs/lib/ExecutionEnvironment.js 1.09 kB {0} [built]

     + 1 hidden modules

webpack: bundle is now VALID.


결과화면이다. 예들 상단에 시멘틱 UI쪽에 관련 링크를 걸기로 한다. 


앞으로 시멘틱 UI의 자바스크립트 코드를 리액트에 넣고 나머지 부분을 컴포넌트화 해야 한다. 그리고 성능 향상과 중복 코드 제거를 위한 Mixin을 이용한 리팩토링이 필요하다. 그 후엔 패키지 배포. 다음 시간에 다시 2차 헤커톤을 진행하기로 하고 저녁 10시에 종료! 




To Be Continue...

신고

'React' 카테고리의 다른 글

[React] Semantic-UI를 React 컴포넌트로 만들기 - 1  (0) 2015.08.23
[React] CSS Framework 선정하기  (0) 2015.08.15
[React] AngularJS와 ReactJS 비교  (0) 2015.07.01
[React] 배우는 방법  (0) 2015.05.15
posted by peter yun 윤영식
2015.08.20 18:05 Data Visualization/D3.js

올 하반기는 D3.js를 이용한 차트 라이브러리 만들기를 주된 작업으로 잡고 있다. 데이터 시각화를 위해 SVG를 자유자재로 다루기 위해 D3.js를 심도있게 이해하는 것이 어느 때보다 필요하기 때문이다. AngularJS/React/Meteor 는 데이터 시각화 서비스를 개발하기 위한 기술로 익히고 있다. 마이크 보스톡의 튜토리얼 레퍼런스에 나온 내용중 좋은 기사를 실습 요약한다. 





준비하기 

  

테스트를 위해 로컬에서 하지 않고 JSBin 서비스를 이용한다. GitHub 계정으로 로그인을 하고 자신이 원하는 테마(Theme)를 아래와 같이 설정할 수 있다. Monokai와 Sublime 키바인딩, FontSize 11로 설정을 했다. "Add library"에서 "D3 3.x" 를 선택하면 HTML에 자동으로 추가된다. 





엘러먼트 선택


D3는 jQuery의 Selector 처럼 Selection(셀렉션)을 통해 문서를 접근할 수 있다. 그리고 선택된 셀랙션에 대해 메소드 체이닝(Chaining Methods)를 통해 제어를 할 수 있다. 체이닝의 이점은 var 를 사용하지 않아도 되고 코드를 간결하게 유지할 수 있다. 

Let's Make Bar Chart on jsbin.com





차트 만들기 


HTML로 직접 바차트를 만들 수 있지만 D3를 이용해 어떻게 만드는지 보자. 

JS Bin on jsbin.com


코드의 순서는 다음과 같다. 

- var chart  = d3.select('.chart') : selector를 이용해서 Chart container를 선택한다. 

- var bar = chart.selectAll('div') : 데이터 조인 (data join)을 위해 셀랙션(selection)을 초기화 한다. 데이터 조인은 데이터 변경에 따라 엘러먼트를 생성, 업데이트, 삭제할 수 있는 것이다. (자세한 사항은 "Thinking with joins"를 참조한다)

- var barUpdate = bar.data(data) : selection.data를 통해 데이터를 셀랙션과 조인(join)한다. 

- var barEnter = barUpdate.enter().append('div') : 'div' 태그가 .chart 안에 존재하지 않기 때문에 barUpdate는 일어나지 않는다. 없을 경우 새로운 데이터에 대한 임의위치(placeholder)를 만들어 주기 위해 enter()를 호출한 후, 임의위치에 append('div')를 통해 div 태그를 신규 생성한다. 

- barEnter.style('width', ....) : 신규 생성된 div에 대한 스타일을 설정한다. D3에서는 attr(), style(), property()를 통해 엘러먼트를 설정할 수 있다. 

- barEnter.text(...) : 레이블을 설정한다. 





스케일 맞추기 


위의 소스를 보면 width에 대해 10 상수값이 쓰였다. 실제값을 정의역값이라하고 domain(도메인)이라하며, 10을 곱하여 나온 변경된 값을 치역(range)값이라 한다. 수치에 대한 일정한 스케일(Scale)을 만들어 주는 기능이 D3에서는 linear scale 이다. x에 대한 스케일을 만들어 보자.

JS Bin on jsbin.com





SVG를 사용해 만들기 


위에 소스는 HTML을 통해 만들었지만 SVG(Scalable Vector Graphics)를 이용해 표현해 보도록 한다. SVG를 사용하면 네이티브 레벨에서 다양한 모양을 만들어 낼 수 있다. HTML에 <!DOCTYPE html> 태그를 최상단에 넣어주고 사용할 수 있다. SVG는 HTML의 일반 스타일이 아닌 다른 스타일 명을 쓴다. 예로 background-color는 fill로 쓴다. SVG 프로퍼티 스펙을 참조한다. 좌표는 top-left 코너에서 시작한다. SVG로만 차트를 표현한 예이다. 

JS Bin on jsbin.com


- transform을 이용해 크기의 절대좌표값으로 translate(x, y)를 통해 이동을 하고 있다. 

- g 그룹 논리 태그를 통해 하위에 SVG rect으로 사각형을 표현한다. 

- text 태그를 통해 레이블을 설정한다. 


D3를 이용해 다시 표현해 보자. g 셀랙션은 data 배열 6개 만큼이 생성되어 rect과 text 태그를 통해 바차트를 그린다. 

JS Bin on jsbin.com


- x 스케일을 만든다 

- svg 의 크기를 결정한다 

- svg안에 g가 들어가므로 enter selection을 통해 추가하고 transform으로 g 위치를 옮긴다. 

- var bar 값은 data 값 수 만큼의 g 배열을 리턴 받아 rect과 text를 설정한다. rect과 text는 g로 부터 data 값을 받아 사용한다. 





데이터를 불러와서 차트그리기


D3는 TSV(Tab-seperated values)나 CSV(Comma-seperated values)로 파일 형식을 읽어와서 JSON으로 변환해 사용할 수 있다. d3.csv API를 참조한다. 테스트를 위해서 GitHub에 별도 저장소를 하나 만들고 data.tsv 파일을 하나 만든다. (탭으로 구분)

name value

Locke 4

Reyes 8

Ford 15

Jarrah 16

Shephard 23

Kwon 42


GitHub에 파일을 만들고 파일을 선택하고 "Raw" 버튼을 클릭하면 상단에 접근 주소가 나온다. 

예) https://raw.githubusercontent.com/mobiconsoft/d3_practice/master/data.tsv

사용할 Raw Data 주소이다. 


JSBin에서 TSV 파일을 호출해 보자. 

JS Bin on jsbin.com


- 데이터의 갯수를 모르기 때문에 호출후 x.domain 정의역 값을 설정한다. 

- 데이터는 [{name: 'Locke', value: 4}, ....] 와 같이 데이터가 오브젝트이므로 function(d) { return d.value; } 값을 사용한다. 

- tsv 호출시 값에 대한 컨버젼을 하지 않고 전부 string으로 취급하므로 type 펑션은 숫자로 변환하는 유틸이다. 





컬럼 차트로 전환하기 


수평 바차트를 수직 컬럼 차트로 변환해 보자. X축은 이름을 표현하고 Y축은 값을 표현할 것이다. X축과 같이 문자 알파벳으로 표현될 경우는 Ordinal 스케일을 사용해야 하고 치역의 값을 수량적으로 할 수 없기 때문에 자동으로 맞춰주는 rangeBands 또는 rangePoints 를 사용한다. rangeBands/Points는 각 시리즈의 넓이를 의미하고 시르즈(바)간의 padding을 지정해 주고 싶다면 rangeRoundBands를 통해 파라미터를 설정할 수 있다. 

JS Bin on jsbin.com


- X축은 이름이기 때문에 Ordinal 스케일이다. 

- bar의 X 스케일 만큼 이동하고, rect의 y점을 지정 후 그린다.

- x.rangeBand()는 바의 넓이를 자동으로 계산해 넘겨준다. 





축 추가하기 


컬럼 차트만 그려지니 썰렁하다. 축을 그려보자. D3는 축을 만들 수 있도록 d3.svg.axis() API를 제공하고, 축의 방향을 설정한 후 기존에 생성한 x, y Scale을 설정한다. 축에 스케일 설정을 하고 SVG의 g 그룹에 축을 설정할 때는 selection.call API를 사용한다. 축을 설정하면 <g class="y axis"> 또는 <g class="x axis"> 클래스가 자동 설정된다. 해당 값은 사용자가 아래와 같이 수정할 수 있다. 

<style>

.bar {

  fill: steelblue;

}


.axis text {

  font: 10px sans-serif;

}


.axis path,

.axis line {

  fill: none;

  stroke: #000;

  shape-rendering: crispEdges;

}


.x.axis path {

  display: none;

}

</style>


JSBin 코드를 보자.

JS Bin on jsbin.com


- SVG 넓이 안에 들어가는 차트를 위한 마진을 설정한다. 

- x, y 스케일을 구한다. 

- x, y 축을 스케일을 적용해 구한다. 

- 차트 영역을 설정하고 차트 셀랙션을 생성한다 

- 데이터가 들어오면 x, y 스케일에 대한 정의역(domain)값을 설정한다. 

- x, y 스케일에 대한 정의역 값이 설정된 후에야 차트 g 그룹에 x, y Axis를 설정할 수 있다. 정의역값이 설정되기 전에 축 설정이 되면 축의 Tick 값이 표현되지 않는다. 

- .bar 클래스를 선택하고 데이터를 조인하면 임의영역(placeholder)가 생성되고, 여기에 rect을 append하여 컬럼을 기존처럼 그린다.  




<참조>


- 원문 : Let's Make a Bar Chart  (보스톡의 소스)   

- SVG 스펙


신고
posted by peter yun 윤영식
2015.08.15 16:09 Meteor

트위터 부트스트랩리액트 컴포넌트로 만들어 놓은 것이 이미 있어서 미티어(Meteor) 에서 사용하는 방법을 알아본다. 



설치 하기 


  - 먼저 미티어 프로젝트 하나를 생성한다. 

$ meteor create test


  - Twiiter Bootstrap 을 React로 변환해 놓은 것 

    + https://github.com/react-bootstrap/react-bootstrap

    + 사용자 메뉴얼

    + webpack을 사용해 번들을 만들었다.


  - react-bootstrap을 미티어의 패키지를 설치한다

   + https://atmospherejs.com/firfi/meteor-react-bootstrap (설명서)

   + package.js 를 추가해서 미티어의 패키지로 만들고, atmosphere에 배포했다. 

$ meteor add firfi:meteor-react-bootstrap

Package.describe({ name: 'firfi:meteor-react-bootstrap', version: '0.0.4', // Brief, one-line summary of the package. summary: 'React-bootstrap port for Meteor', // URL to the Git repository containing the source code for this package. git: 'https://github.com/Firfi/meteor-react-bootstrap', // By default, Meteor will default to using README.md for documentation. // To avoid submitting documentation, set this field to null. documentation: 'METEOR.md' }); Package.onUse(function(api) { api.versionsFrom('1.0.5'); api.use('reactjs:react@0.2.1'); api.use('twbs:bootstrap@3.3.4', 'client'); api.addFiles('dist/react-bootstrap.js'); api.export(['ReactBootstrap']); }); Package.onTest(function(api) {

});


  - 미티어에 리액트를 사용하기 위한 공식 패키지를 설치한다. 

$ meteor add react




사용 하기 


  - FlowRouter와 ReactLayout을 설치한다. 

$ meteor add kadira:flow-router

# meteor add kadira:react-layout


  - 리액트 문구가 들어 있는 파일의 확장자는 .jsx가 되어야 한다. test.jsx 파일을 하나 만들고 다음과 같이 탭을 사용한다. 

    + ReactBootstrap 이 글로벌 변수이다. 

    + 리액트 컴포넌트 (App)는 미티어에서 글로벌 변수여야 한다. 

if (Meteor.isClient) {

  

  App = React.createClass({

    render() {

      return (

        <ReactBootstrap.TabbedArea defaultActiveKey={2}>

          <ReactBootstrap.TabPane eventKey={1} tab='Tab 1'>TabPane 1 content</ReactBootstrap.TabPane>

          <ReactBootstrap.TabPane eventKey={2} tab='Tab 2'>TabPane 2 content</ReactBootstrap.TabPane>

        </ReactBootstrap.TabbedArea>

      )

    }

  });

}


  - Flow Router를 설정한다. 

// test.js 

if(Meteor.isClient) {


  FlowRouter.route('/', {

    // do some action for this route

    action: function(params, queryParams) {

        // console.log("Params:", params);

        // console.log("Query Params:", queryParams);

        // ReactLayout.render(AppLayout, {

        //  content: <MainComponent />

        // })

        ReactLayout.render(App);

    }

  });

  

}


// test.html 

<head>

  <title>test</title>

</head>


  - 탭 결과 화면이 출력된다.

  



오류 해결


  - 크롬 DevTools에 React extension을 설치하고 결과를 보면 다음과 같이 <Unknown> 태그가 나온다. 

    

 - <Unknown>을 제거하기 위해 displayName을 지정한다. 

if (Meteor.isClient) {

  

  App = React.createClass({

    displayName: 'App',


    render() {

      return (

        <ReactBootstrap.TabbedArea defaultActiveKey={2}>

          <ReactBootstrap.TabPane eventKey={1} tab='Tab 1'>TabPane 1 content</ReactBootstrap.TabPane>

          <ReactBootstrap.TabPane eventKey={2} tab='Tab 2'>TabPane 2 content</ReactBootstrap.TabPane>

        </ReactBootstrap.TabbedArea>

      )

    }

  });

}







<참조>


  - npm을 미티어에서 직접 사용하고자 할 경우 meteorhacks:npm의 설정 가이드


신고
posted by peter yun 윤영식
prev 1 2 3 4 5 6 7 8 9 ... 85 next

티스토리 툴바