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

Publication

Statistics Graph

Recent Comment

2018.12.13 16:39 Angular/Architecture

Angular 프로젝트를 위한 구조 설계 요건에 대해 알아본다. 


  - 프레임워크 선택 고려 사항

  - 개발 생산성과 유지 보수성을 높일 수 있는 방안




프레임워크 선택 고려 사항


프레임워크는 잘 정돈된 놀이터와 같아 기구를 타고 놀면 된다. 하지만 라이브러리의 경우는 툴박스와 같아서 박스안에는 망치, 드라이버, 못같은 것만 있다. 그럼 놀 것을 어떻게 만들지는 도구를 사용하는 사용자의 몫이다. 현재 UI 프레임워크/라이브러리는 Angular, React, Vue 등이고 풀스택으로는 Meteor를 선택해서 사용하고 있다. Angular 프레임워크와 React UI 라이브러리 또는 Meteor 선택시 고려사항을 개인의견으로 간략히 정리한다. 


Angular Framework

  - 대규모 협업 팀에 유리: 대략 10명이상으로 HTML Publisher와 Javascript 개발자가 분리되어 있을 경우

  - JQuery가 low level의 javascript로 개발해 오던 개발자들이 접근하기에 수월하다고 판단함

  - Typescript, RxJS 반드시 알아야 한다.


React Library

  - HTML Publisher와 Javascript 개발을 같이하는 스타트업 또는 프론앤드 개발자에게 유리

  - 원하는 것들을 선택해 적용할 수 있다. 


Meteor

  - FullStack 으로서 javascript가지고 전부 개발하고 싶을 경우

  - 아이디어를 빠르게 구현하고 검증 받고 싶을 경우

  - UI는 Angular, React, Vue 등으로 선택해 사용할 수 있으나, React가 적합하다 판단



그러나 어떤 것을 선택하든 공통적으로 고려해야 하는 사항으로 애플리케이션 아키텍쳐이다. 이에 대한 좋은 영상이 있어 보기를 권한다. 




애플리케이션 아키텍쳐



위의 영상을 정리해 보면 다음과 같다. 

  - Layered Architecture 구성

  - 역할 분담

    + Module: 업무 최소 단위, 하나를 제거해도 다른 것에 영향을 주지 않는 단위, Module은 Sandbox만 알뿐이다. 

    + Sandbox: Module은 Sandbox를 통해 Application Core나 Base Library를 접근한다.

         - 일관성을 유지시킴

         - 시큐리티 가드 역할

         - 공통 Interface 역할

    + Application Core: Module을 관리(Control) 한다. 

         - Module의 Life Cycle을 담당한다.

         - Module 간 통신을 담당한다 (inter-module communication)

         - Module을 직접 가져다 쓰지 않고, Sandbox의 공통 API를 통해 명령을 내리고, 듣는다. 

         - General error handling

         - Extension을 통해 Application core기능을 확장한다.  

            

   + Extension 확장

       - Error handling

       - Ajax communication: 공통 Format (request/response) 통신, 서버 오류 관리

       - New module capabilities

       - General Utils

       - What you want

    + Base Library

       - 개발자들이 직접 base library를 건드리게 하지 말자. 나중에 바뀔 수 있다. 

       - Browser normalization

       - General purpose utilities: Paser/serializer, Object / DOM manipulation, Ajax Communication

       - low-level extension 제공: application extension처럼 base library를 확장한 것들을 제공한다. 

         


정리

  - Base Library만이 사용중인 브라우저를 알고 있다. 아키텍쳐의 다른 layer에서 알 필요가 없다.

  - Application Core만이 사용중인 Base Library를 알고 있다. 아키텍쳐의 다른 layer에서 알 필요가 없다. 

  - Module은 Sandbox의 존재외에는 아무것도 모른다. 아키텍쳐의 나머지에 대해선 아무것도 모른다.


장점

  - 하나의 프레임워크상에서 서로 다른 멀티 애플리케이션을 만들 수 있다. 즉, 기존의 컴포넌트를 재사용함으로서 시간을 줄일 수 있다. 

  - 느슨한 연결(loose coupling)으로 인해 모듈 단위 테스트가 가능해짐.

  - 확장 가능한 자바스크립트 아키텍쳐는 하나의 블록을 교체한다고 해도 두려워 할 필요가 없는 상태가 되어야 한다. 즉, 이를 달성할 수 있다.




유지 보수성 높이기


유지보수가 좋은 코드는 다음의 특성을 갖는다. 

  - 직관적이다. Intuitive

  - 코드 이해가 쉽다 Understable

  - 적용하기 쉽다 Adaptable

  - 확장하기 쉽다 Extendable

  - 디버깅하기 쉽다 Debuggable


이를 위해 해야할 것들. 
  - Code Style, Code Convention 지키기
  - Naming 잘 하기: 이름에 대한 길이에 대해 걱정하지 말자.
  - Javascript에서 css 핸들링 하지 말자 
  - event object를 바로 다른 function의 argument로 넘기지 말고, 필요한 정보만을 준다. 
  - 사용전 object undefined 체크
  - string 값들은 별도의 Config Data 객체에 담아서 사용한다.

    

  - 빌드 자동화 하자

    


'Angular > Architecture' 카테고리의 다른 글

[Architecture] 애플리케이션 구조 설계 - 1  (0) 2018.12.13
posted by peter yun 윤영식
2018.12.04 15:13 Meteor/React + Meteor

컴포넌트에 style을 직접 적용하는 대신, class를 적용해 본다. 


  - meteor 기반에서 css 적용의 어려운 부분들

  - styled-component를 사용한 적용

  - 적용소스




styled-components 적용하기


meteor는 자체 builder를 통해 패키지를 빌드하는 관계로 webpack과 같은 custom 설정에 제약이 있어 보인다. 물론 잘 알지 못해서 아직 찾지 못했을 수도 있지만 다음과 같은 class 적용이 안된다. 

  - import * as style from './link.scss': from 문구를 사용하지 못 한다. 나머지 다양한 class적용 방법도 잘 안되는 듯 하다. 

  - import './link.scss' 만 사용가능하다. local 적용이 아닌 global 적용을 한다. <head> 태그에 prefix없이 들어간다. 


Meteor의 제약사항을 고려하여 별도의 scss 파일 작성을 최대한 줄이고, 컴포넌트의 style을 확장하기 위해 styled-components를 사용토록 한다. 

$ meteor npm install --save styled-components


imports/ui/sdk/eui/flexgroup.style.tsx 파일 을 생성한다. 

  - 테스트로 확장 컴포넌트로 부터 받은 색을 적용해 본다. 

  - props는 camelCase로 설정한다.

import styled from 'styled-components';

import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui';


export const StyledWidthEuiFlexGroup = styled(EuiFlexGroup)`

  width: ${props => props.width || '100%'};

`;


export const StyledLongEuiFlexItem = styled(EuiFlexItem)`

  max-width: 400px;

  input {

    background: ${props => props.background || 'yellow'};  // 동적으로 bgcolor를 적용한다.

    color: white;

  }

`;


export const StyledShortEuiFlexItem = styled(EuiFlexItem)`

  max-width: 100px;

`;


AddLink.tsx에서 사용한다. 

import { StyledLongEuiFlexItem, StyledShortEuiFlexItem } from '../../sdk/eui/flexgroup.style';


class AddLink extends React.Component<AddLinkProps, any> {

...

  makeForm = ({handleSubmit, submitting, pristine}) => {

    return (

      <form onSubmit={handleSubmit}>

        <EuiFlexGroup direction="row" gutterSize="s">

          <EuiFlexItem><EInput name="title" component="input" type="text" placeholder="Title" /></EuiFlexItem>

          <StyledLongEuiFlexItem background="red">

             <EInput name="url" component="input" type="text" placeholder="Url" />

          </StyledLongEuiFlexItem>

          <StyledShortEuiFlexItem>

            <EuiButton type="submit" fill disabled={submitting || pristine}>Add Link</EuiButton>

          </StyledShortEuiFlexItem>

        </EuiFlexGroup>

      </form>

    );

  }

...

}



Login.tsx에도 적용한다. 

  - FlexGroup  의 width=600px으로 설정한다. 

  - JPage, JRow를 설정한다. (아래에서 계속 개발)

import { JPage, JRow } from '../layouts/common.style';


export default class Login extends React.Component<LoginProps, LoginState> {

...

  makeForm = ({ handleSubmit, submitting, pristine }) => {

    return (

      <form onSubmit={handleSubmit}>

        <StyledWidthEuiFlexGroup width="600px" direction="row" gutterSize="s">

          <EuiFlexItem><EInput name="email" type="email" placeholder="Email" /></EuiFlexItem>

          <EuiFlexItem><EInput name="password" type="password" placeholder="Passowrd" /></EuiFlexItem>

          <StyledShortEuiFlexItem>

                <EuiButton type="submit" disabled={submitting || pristine}>Login</EuiButton>

          </StyledShortEuiFlexItem>

        </StyledWidthEuiFlexGroup>

      </form>

    );

  };


  public render() {

    return (

      <JPage>

        <JRow padding="10px" fontSize="20px">Login to short Link</JRow>

        <JRow>{this.state.error ? <p>{this.state.error} </p> : undefined}</JRow>

        <JRow><Form onSubmit={this.onLogin} render={this.makeForm} /></JRow>

        <JRow><Link to="/signup">Have a account?</Link></JRow >

      </JPage>

    );

  }

}


JPage와 JRow 컴포넌트를 imports/ui/layouts/common.style.ts 생성한다. 

  - JPage는  padding

  - JRow는 flexbox의 row direction으로 설정한 컴포넌트이다.

  - 컴포넌트의 properties를 통해 값을 받아 동적으로 설정한다. 

import styled from 'styled-components';


export const JPage = styled.div`

  display: block;

  position: relative;

  padding: 10px;

  height: 100%;

  width: 100%;

`;


export const JRow = styled.div`

  display: flex;

  justify-content: ${ props => props.align || 'flex-start' };

  align-items: ${ props => props.align || 'center' };

  width: ${ props => props.width || '100%' };

  padding: ${ props => props.padding || '5px' };

  font-size: ${ props => props.fontSize || '14px' };

`;



styled-components를 통해 얻을 수 있는 장점


  - 기존 컴포넌트의 style 확장을 컴포넌트 개념을 할 수 있다. 기존은 .css 작성

  - 일반 태그를 style을 동적으로 적용할 수 있는 컴포넌트로 만들어 React컴포넌트에서 Common 컴포넌트처럼 사용할 수 있다. 즉 Style만을 담당하는 컴포넌트임.




<참조>

- class 적용 방법들

- styled-components 홈페이지

posted by peter yun 윤영식
2018.12.03 16:09 Meteor/React + Meteor

Ant Design 컴포넌트 또는 Elatstic UI 같은 컴포넌트 적용방법을 알아보자. 


  - Ant Design 컴포넌트 적용

  - Elastic UI  컴포넌트 적용

  - 적용 소스




Final Form 에 Ant Design 컴포넌트 적용하기


적용하기-1 블로그에서 Antd을 설치했다. Ant Design의 Form을 사용하여 Login, SignOut, AddLink의 Input을 수정한다. 

  - imports/ui/sdk/antd/antd-final-input.tsx 파일 생성

  -antd 의 input react 컴포넌트를  react-final-form의 input의 custom component로 설정한다. (third component support )

import * as React from 'react';

import { Input } from 'antd';

import { Field } from 'react-final-form';


// antd 의 react input component를 react-final-form에서 사용할 수 있는 custom component로 만듦

const AntdInput = ({input, ...rest}) => {

  return (

    <Input {...input} {...rest} />

  )

}


// 최종 애플리케이션에서 사용할 react-final-form 컴포넌트

const AInput = (props: any) => {

    return (

      <Field {...props} component={AntdInput} />

    );

}


export default AInput;


antd의 layout 컴포넌트은 Col, Row를 사용해서 레이아웃을 잡는다. 

import { Row, Col, Button } from 'antd';

import AInput from '../sdk/antd/antd-final-input';


export default class Login extends React.Component<LoginProps, LoginState> {

...

  makeForm = ({ handleSubmit, submitting, pristine }) => {

    return (

        <form onSubmit={handleSubmit}>

          <Col span={4}><AInput name="email" type="email" placeholder="Email" /></Col>

          <Col span={4}><AInput name="password" type="password" placeholder="Passowrd" /></Col>

          <Col span={2}><Button type="primary" htmlType="submit" disabled={submitting || pristine}>Login</Button></Col>

        </form>

    );

  };


  public render() {

    return (

      <div>

        <h1>Login to short Link</h1>

        {this.state.error ? <p>{this.state.error} </p> : undefined}

        <Row gutter={5}>

          <Form onSubmit={this.onLogin} render={this.makeForm} />

        </Row>

        <Row gutter={5}>

          <Link to="/signup">Have a account?</Link>

        </Row>

      </div>

    );

  }

}



AddLink.tsx, Signup.tsx도 동일하게 바꾸어 본다. 




Final Form에 Elastic UI 적용하기 


eui(Elastic UI)는 Elastic Search의 React기반 오픈소스이다. Elastic Search의 Kibana에서 사용중이다. AntD대신 Eui를 적용해 본다. 

eui는 yarn install 만 지원하는 관계로 Meteor에서 yarn을 사용하기 위해서 다음 과정을 최초 한번 설정한다. 

$ meteor npm i -g yarn

$ meteor yarn info 

yarn info v1.12.3


$ rm -rf node_modules (MS는 윈도우 명령으로)

$ rm package-lock.json


// package.json 내용 설치

$ meteor yarn 


  - @elastic/eui 패키지 설치

  - css 설정

// eui는 npm install을 지원하지 않는다. 

$ meteor yarn add  @elastic/eui


// client/main.tsx에서 import한다. 

import '@elastic/eui/dist/eui_theme_light.css';


// client/theme.less 안은 import는 주석처리한다.

// @import '{}/node_modules/antd/dist/antd.less';


Lisk화면을 다음과 같이 전환한다. 


react-final-form에 EuiFieldText (input tag) 컴포넌트를 적용한다. 

  - imports/ui/sdk/eui/eui-final-input.tsx 파일 생성

import * as React from 'react';

import { EuiFieldText } from '@elastic/eui';

import { Field } from 'react-final-form';


const EuiInput = ({ input, ...rest }) => {

  return (

    <EuiFieldText {...input} {...rest} />

  )

}


const EInput = (props: any) => {

  return (

    <Field {...props} component={EuiInput} />

  );

}


export default EInput;


imports/ui/Info.tsx 에서 EuiPage 관련 컴포넌트로 레이아웃을 꾸민다. 

  - EuiPageHeader, EuiPageContent로 나눔

  - 태그안의 정렬은 FlexBox가 적용된 EuiFlexGroup과 EuiFlexItem을 사용한다.

  - width, padding은 style일 직접 설정한다.

import {

  EuiFlexGroup, EuiFlexItem, EuiButton, EuiPage, EuiSpacer,

  EuiPageBody, EuiPageHeader,  EuiPageContent, EuiPageContentBody, EuiTitle

} from '@elastic/eui';


class Info extends React.Component<InfoProps, any> {

...

  render() {

    return (

      <EuiPage>

        <EuiPageBody>

          <EuiPageHeader>

              <EuiFlexGroup justifyContent="spaceBetween">

                <EuiFlexItem grow={1} style={{ paddingLeft: 20 }}>

                  <EuiTitle size="m"><h1>Add Link & List</h1></EuiTitle>

                </EuiFlexItem>

                <EuiFlexItem style={{ maxWidth: 130, paddingRight: 30 }}>

                  <EuiButton style={{ maxWidth: 100 }} onClick={this.onLogout}>Log out</EuiButton>

                </EuiFlexItem>

              </EuiFlexGroup>

          </EuiPageHeader>

          <EuiPageContent>

            <EuiPageContentBody>

              <EuiFlexGroup direction="column" justifyContent="spaceBetween">

                <EuiFlexItem style={{ maxWidth: 800 }}>

                  <AddLink /> 

                </EuiFlexItem>

                <EuiSpacer />

                <EuiFlexItem style={{ maxWidth: 400 }}>

                  {this.linkList()}

                </EuiFlexItem>

              </EuiFlexGroup>

            </EuiPageContentBody>

          </EuiPageContent>

        </EuiPageBody>

      </EuiPage>

    );

  }

}


imports/ui/pages/link/AddLink.tsx도 수정한다.

  - EuiFlexGroup으로 레이아웃 적용: flexbox direction은 row이다.

  - EInput 컴포넌트 적용

  - EuiButton 적용하기: type="submit" 설정

  - AntD적용 내용은 모두 주석처리한다. 

import EInput from '../../sdk/eui/eui-final-input';

import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';



class AddLink extends React.Component<AddLinkProps, any> {

...

  makeForm = ({handleSubmit, submitting, pristine}) => {

    return (

      <form onSubmit={handleSubmit}>

        {/* <Col span={4}><AInput name="title" component="input" type="text" placeholder="Title" /></Col>

        <Col span={4}><AInput name="url" component="input" type="text" placeholder="Url" /></Col>

        <Col span={2}><Button type="primary" htmlType="submit" disabled={submitting || pristine}>Add Link</Button></Col> */}

        <EuiFlexGroup direction="row" gutterSize="s">

          <EuiFlexItem><EInput name="title" component="input" type="text" placeholder="Title" /></EuiFlexItem>

          <EuiFlexItem><EInput name="url" component="input" type="text" placeholder="Url" /></EuiFlexItem>

          <EuiFlexItem style={{ maxWidth: 100 }}>

            <EuiButton type="submit" fill disabled={submitting || pristine}>Add Link</EuiButton>

          </EuiFlexItem>

        </EuiFlexGroup>

      </form>

    );

  }


  public render() {

    return (

      // <Row gutter={5}>

        <Form onSubmit={this.handleSubmit} render={this.makeForm} />

      // </Row>

      );

  }

}


Link.tsx도 수정한다. 

  - EuiButton 사이즈 small 설정

  - EuiButton minWidth 적용: default가 120px 이므로 overrriding을 minWidth: 40 을 설정한다

class Link extends React.Component<LinkProps, any> {

...

  public render() {

    const { link } = this.props;

    return (

      <li key={link._id}>

        <a href={link.url} target="_blank">{link.title}</a>

        <EuiButton size="s" style={{minWidth: 40, marginLeft: 10}} onClick={this.deleteLink}>x</EuiButton>

      </li>

    );

  }

}


Login.tsx, Signup.tsx도 eui 컴포넌트로 변경해 본다. 개인적으로 FlexBox를 많이 사용하는데 이에대한 컴포넌트 레벨 지원을 Eui가 제공하므로, 앞으로 Eui 컴포넌트를 사용한다. 다음은 style을 직접 적용하는 방식이 아니라 class 적용방법을 알아본다. 




<참조> 

- Ant Design React Component

- Elastic UI React Component 

- Meteor에서 yarn 사용하기 - 마지막 답변 참조

posted by peter yun 윤영식
2018.11.30 15:48 Meteor/React + Meteor

React의 route 설정을 리팩토링해보고, createRef() 를 사용하지 않고 form을 변경해 본다. 


  - react-router v4에 맞는 redux 환경 재구성

  - react-final-form  적용하기

  - 사용자별 Link 목록 보여주기

  - 적용소스




React Router v4에 맞는 Redux 설정


react-router-redux는 react router v2와 v3에 맞는 패키지이고 react router v4에 맞는 connected-react-router로 교체해야 한다. recct-router-redux 설정 상태로는 redux action이 작동하지 않는다.

// 설치

$ meteor npm install --save connected-react-router

// 삭제

$ meteor npm uninstall --save react-router-redux

$ meteor npm uninstall --save @types/react-router-redux


imports/ui/store.ts 리팩토링

  - Routes.tsx의 browserHistory 를 store.ts로 옮김

  - connectRouter로 routerReducer를 생성

  - RouterAction, LocationChangeAction을 추가

import { createBrowserHistory } from 'history';

import { connectRouter, RouterAction, LocationChangeAction } from 'connected-react-router';


export const browserHistory = createBrowserHistory();


const rootReducer = combineReducers({

  router: connectRouter(browserHistory),

  links: linkReducer

});


export type RootState = StateType<typeof rootReducer>;


type ReactRouterAction = RouterAction | LocationChangeAction;

export type RootAction = ReactRouterAction | LinkAction;


imports/ui/Routes.tsx 리팩토링

  - react-router v4의 Router를 사용하지 않고, connected-react-router의 ConnectedRouter를 사용

  - 인자로 store.ts 에서 생성한 browserHistory를 사용

import { ConnectedRouter } from 'connected-react-router';

import store, { browserHistory } from './store';


export const Root = (

  <Provider store={store}>

    <ConnectedRouter history={browserHistory}>

      <Switch>

        <Route exact path="/" component={Login} />

        <Route path="/main" component={App} />

        <Route path="/signup" component={Signup} />

        <Route path="/links" component={InfoContainer} />

        <Route path="*" component={NotFound} />

      </Switch>

    </ConnectedRouter>

  </Provider>

);


이제 Redux Chrome Extension에서 LOCATION_CHANGE 액션을 볼 수 있다. 




final-form 통해 입력


form 관련부분을 final-form으로 변경한다. React버전의 react-final-form을 사용한다. 

$ meteor npm install --save final-form 

$ meteor npm install --save react-final-form


로그인 화면부터 react-final-form으로 변경해 본다. 

  - react-final-form의 Form, Field import

  - Form 의 onSubmit={this.onLogin}을 통해 입력한 values 객체를 받기, 보통 폼 필드의 name을 key로하여 json 객체를 받는다. 

  - <input> 태그를 <Field> 태그로 변경

  - <button> 태그에 disabled 속성추가

  - 필요없는 부분 추석처리: React.createRef()

import * as React from 'react';

import { Link } from 'react-router-dom';

import { Meteor } from 'meteor/meteor';

import { Form, Field } from 'react-final-form';


export interface LoginProps {

  history: any;

}


export interface LoginState {

  error: string;

}


export default class Login extends React.Component<LoginProps, LoginState> {

  // email: any = React.createRef();

  // password: any = React.createRef();


  constructor(props) {

    super(props);

    this.state = {

      error: ''

    };

  }


  onLogin = ({email, password}) => {

    // e.preventDefault();

    // let email = this.email.current.value.trim();

    // let password = this.password.current.value.trim();

    if (!email || !password) {

      this.setState({error: 'Please input email and password both'});

      return;

    }

    Meteor.loginWithPassword({ email }, password, (err) => {

      if (err) {

        this.setState({ error: err.reason });

      } else {

        this.setState({ error: '' });

      }

    });

  }


  makeForm = ({ handleSubmit, submitting, pristine, values }) => {

    return (

      <form onSubmit={handleSubmit}>

        <Field name="email" component="input" type="email" placeholder="Email" required/>

        <Field name="password" component="input" type="password" placeholder="Passowrd"/>

        {/* <input type="email" ref={this.email} name="email" placeholder="Email" />

        <input type="password" ref={this.password} name="password" placeholder="Password" /> */}

        <button type="submit" disabled={submitting || pristine}>Login</button>

      </form>

    );

  };


  public render() {

    return (

      <div>

        <h1>Login to short Link</h1>

        {this.state.error ? <p>{this.state.error} </p> : undefined}

        <Form onSubmit={this.onLogin} render={this.makeForm} />

        <Link to="/signup">Have a account?</Link>

      </div>

    );

  }

}


Signup.tsx, AddLink.tsx 도 변경한다. 




사용자별 Link 목록 보여주기


사용자를 추가하여 각 사용자가 등록한 목록만 보기 위해서 pub/sub 설정에서 userId  파라미터를 넘겨주어 본인 목록만 조회하여 publish 한다. 

// imports/ui/Info.tsx 맨 하단의 subscribe시에 자신의 아이디를 파라미터로 보낸다. 

export default compose(

  withTracker(() => {

    const connection = Meteor.subscribe('links', {userId: Meteor.userId()});

    return {

      links: Links.find().fetch(),

      loading: !connection.ready()

    };

  }),

  connect(mapProps)

)(Info);


imports/api/links.ts에서 userId를 파라미터로 find한다. 

if (Meteor.isServer) {

  Meteor.publish('links', ({userId}) => {

    console.log('userId:', userId);

    if (!userId) {

      return this.ready();

    }

    return Links.find({owner: userId});

  });

  ...

}




<참조>

- connected-react-router 저장소

- react-final-form 소개 영상

posted by peter yun 윤영식
2018.11.28 16:56 Meteor/React + Meteor

Meteor와 React 환경을 좀 더 Production에 가깝게 만들어 본다. 


  - Insecure 제거 및 allow/deny 설정

  - React의 Refs에 대해 변경

  - Meteor methods/call 사용한 목록 조회

  - SimpleSchema 적용

  - 적용소스




Insecure 패키지 제거 및 Allow/Deny 설정


모든 컬렉션에 대한 자동 subscribe에 대한 autopublish 패키지는 이미 제거를 했고, 다음으로 insecure 패키지를 제거하여 컬렉션의 접근 권한을 제어하는 allow/deny를 설정한다. 

$ meteor remove insecure


insecure를 제거하게 되면 read만 가능하고 update, delete, insert가 불가능하다. 따라서 권한부분은 서버에서 실행되기 때문에 allow/deny설정을 Meteor.isServer블럭안에서 해주어야 한다.

  - allow: true로 설정된 것만 가능하고 그외는 모두 불가능하다. (가능한 것이 적으면 allow 설정)

  - deny: true로 서정된 것만 불가능하고 그외는 모두 가능하다.  (불가능한 것이 적으면 deny 설정)

// api/links.ts

import { Meteor } from 'meteor/meteor';

import { Mongo } from 'meteor/mongo';


const Links = new Mongo.Collection('links');


if (Meteor.isServer) {

  Meteor.publish('links', () => Links.find());

  Links.allow({

    insert (userId: string, doc: any) {

      console.log('insert doc:', doc);

      return (userId && doc.owner === userId);

    },

    remove(userId: string, doc: any) {

     console.log('delete doc:', doc);

      return (userId && doc.owner === userId);

    }

  })

}

export default Links;


 allow에서 owner 아이디를 비교하므로 epic에서 owner 값을 설정한다. 

// imports/ui/pages/link/link.epic.ts

const addLink: Epic = (

  action$,

  store

) =>

  action$.pipe(

    filter(isOfType(actions.ADD_REQUEST)),

    switchMap(action => {

      const { title, url } = action.payload;

      const owner = Meteor.userId();

      return insertCollection(Links, { title, url, owner, createdAt: new Date() })

    }),

  ...

);


userId는 로그인을 하게되면 이미 서버와 동기화 되어 서버가 가지고 있는 userId를 첫번째 파라미터로 넘겨준다. 두번째 파라미터 doc은 insert할 때 입력한 파라미터값 이고, remove의 doc은 저장된 몽고디비의 document이다. 

// meteor run을 수행한 콘솔창에 찍힌다. 

=> Meteor server restarted

I20181123-15:00:36.619(9)? insert doc: { title: 'google2',

I20181123-15:00:36.676(9)?   url: 'http://www.google.com',

I20181123-15:00:36.676(9)?   owner: 'AmMBJzZ33Nc8Bsqhe',

I20181123-15:00:36.676(9)?   createdAt: 2018-11-23T06:00:36.611Z }

I20181123-15:00:39.955(9)? remove doc: { _id: 'Zrb8jGSpt2AyRn4Q3',

I20181123-15:00:39.955(9)?   title: 'google2',

I20181123-15:00:39.955(9)?   url: 'http://www.google.com',

I20181123-15:00:39.955(9)?   owner: 'AmMBJzZ33Nc8Bsqhe',

I20181123-15:00:39.955(9)?   createdAt: 2018-11-23T06:00:36.611Z }




Form 입력의 React의 Refs 변경


React에서 DOM객체 접근 방법은 3가지 이고, string과  콜백펑션말고 16.3 버전이후 나온 createRefs()를 사용한다. 

  - ref="string"

  - ref={callback-function}

  - createRefs()


DOM 객체를 받을 변수를 선언하고, ref={this.변수}로 할당한다. 값 접근은 this.변수.current 객체를 통한다.

// imports/ui/pages/Login.tsx

.. 중략 ..

export default class Login extends React.Component<LoginProps, LoginState> {

  email: any = React.createRef();

  password: any = React.createRef();


  constructor(props) {

    super(props);

    this.state = {

      error: ''

    };

  }


  onLogin = (e: any) => {

    e.preventDefault();


    let email = this.email.current.value.trim();

    let password = this.password.current.value.trim();

    Meteor.loginWithPassword({ email }, password, (err) => {

      if (err) {

        this.setState({ error: err.reason });

      } else {

        this.setState({ error: '' });

      }

    });

  }


  public render() {

    return (

      <div>

        <h1>Login to short Link</h1>

        {this.state.error ? <p>{this.state.error} </p> : undefined}

        <form onSubmit={this.onLogin}>

          <input type="email" ref={this.email} name="email" placeholder="Email" />

          <input type="password" ref={this.password} name="password" placeholder="Password" />

          <button>Login</button>

        </form>

        <Link to="/signup">Have a account?</Link>

      </div>

    );

  }

}


Signup.tsx과 AddLink.tsx도 동일하게 변경한다. 




Meteor methods/call을 사용한 저장/삭제


imports/sdk/utils/ddp.util.ts 에 method call에 대한 observable 반환 메소드 추가

export function insertCall(methodName: string, params: any): Observable<RequestModel> {

  return from(new Promise((resolve, reject) => {

    Meteor.call(methodName, params, (error, result) => {

      if (error) {

        reject({ error: true, result: { ...error }, params: { ...params } });

      }

      if (typeof result === 'string' || typeof result === 'number') {

        resolve({ success: true, result, params: { ...params } });

      } else {

        resolve({ success: true, result: { ...result }, params: { ...params } });

      }

    });

  }));

}


export function removeCall(methodName: string, _id: string): Observable<RequestModel> {

  return from(new Promise((resolve, reject) => {

    Meteor.call(methodName, _id, (error, result) => {

      if (error) {

        reject({ error: true, result: { ...error }, params: { _id } });

      }

      if (typeof result === 'string' || typeof result === 'number') {

        resolve({ success: true, result, params: { _id } });

      } else {

        resolve({ success: true, result: { ...result }, params: { _id } });

      }

    });

  }));

}


imports/api/links.ts 에서 Meteor.methods를 추가한다. 

if (Meteor.isServer) {

 ... 

  Meteor.methods({

    insertLink(params: any) {

      if (!this.userId) {

        throw new Meteor.Error('Please login');

      }

      return Links.insert(params);

    },

    removeLink(_id: string) {

      if (!this.userId) {

        throw new Meteor.Error('Please login');

      }

      return Links.remove(_id);

    }

  });

}


imports/ui/pages/link/link.epic.ts 에서 collection 을 호출하지 않고, Meteor.call을 호출 한다. takeUntil은 브라우져 이동등을 할때 호출을 끊는 역할을 한다. 현재는 주석처리로 미구현상태임.

const addLink: Epic = (

  action$,

  store

) =>

  action$.pipe(

    filter(isOfType(actions.ADD_REQUEST)),

    switchMap(action => {

      const { title, url } = action.payload;

      const owner = Meteor.userId();

      return insertCall('insertLink', { title, url, owner, createdAt: new Date() })

      // return insertCollection(Links, { title, url, owner, createdAt: new Date() })

    }),

    map((response: RequestModel) => {

      if (response.error) {

        return actions.addLinkFailed({ ...response.result })

      }

      return actions.addLinkSuccess(response.result)

    }),

    // takeUntil(action$.pipe(

    //   filter(isOfType(actions.ADD_REQUEST))

    // ))

  );


const removeLink: Epic = (

  action$,

  store

) =>

  action$.pipe(

    filter(isOfType(actions.DELETE_REQUEST)),

    switchMap(action => {

      return removeCall('removeLink', action.payload);

      // return removeCollection(Links, action.payload);

    }),

    map((response: RequestModel) => {

      if (response.error) {

        return actions.removeLinkFailed({ ...response.result, ...response.params })

      }

      return actions.removeLinkSuccess(response.params._id);

    }),

    // takeUntil(action$.pipe(

    //   filter(isOfType(actions.ADD_REQUEST))

    // ))

  );




SimpleSchema 적용하기 


Validation을 위해 simple schema 패키지를 설치한다. 

$ meteor npm install --save simpl-schema


/imports/api/links.ts 에 schema를 정의한다. 

import { Meteor } from 'meteor/meteor';

import { Mongo } from 'meteor/mongo';

import SimpleSchema from 'simpl-schema';


const Links = new Mongo.Collection('links');


if (Meteor.isServer) {

 ...

  const linkSchema = new SimpleSchema({

    title: {

      type: String,

      min: 3

    },

    url: {

      type: String

    },

    owner: {

      type: String

    },

    createdAt: {

      type: Date 

    }

  });


  Meteor.methods({

    insertLink(params: any) {

      if (!this.userId) {

        throw new Meteor.Error('Please login');

      }

      try {

        linkSchema.validate(params);

        return Links.insert(params);

      } catch(e) {

        throw new Meteor.Error('no valid schema');

      }

    },

   ...

  });

}

export default Links;


account에 대한 validate도 정의해 본다. imports/api/ 폴더아래에 account-validate.ts 파일을 생성하고 server/main.ts에서 import한다. 

// account-validate.ts

import { Meteor } from 'meteor/meteor';

import { Accounts } from 'meteor/accounts-base';

import SimpleSchema from 'simpl-schema';


if (Meteor.isServer) {

  Accounts.validateNewUser((user) => {

    const email = user.emails[0].address;


    try {

      new SimpleSchema({

        email: {

          type: String,

          regEx: SimpleSchema.RegEx.Email

        }

      }).validate({ email });

    } catch(e) {

      throw new Meteor.Error(400, e.message);

    }


    return true;

  });

}


// server/main.ts

import { Meteor } from 'meteor/meteor';

import '../imports/api/links';

import '../imports/api/account-validate';


Meteor.startup(() => {

});


// Singup.tsx 파일에 테스트를 위해 noValidate를 추가한다. 

<form onSubmit={this.onCreateAccount} noValidate>




<참조>

React createRefs() 사용하기

- Meteor methods/call 사용하기

posted by peter yun 윤영식
2018.11.26 16:05 BlockChain

2018 W3C 컨퍼런스를 요약한다. 관심분야는 Web3.0, WebRTC, Game, BlockChain 이다. 물론 경품도...




Web 3.0


web2.0 시대에 사용자의 데이터를 가지고 정보로 가공하여 장사를 하고 있다. 플랫폼 독점 기업의 특징. 예) 구글, 페이스북


탈중앙화된 웹의 필요성

  - 데이터 정보 제공자에게 더 많은 인센티브를 줘야 한다. 

  - 사용자가 정보의 주인

  - 거대 플랫폼으로부터 자유로운

  - 장애와 공격으로 부터 자유로운


데이터의 탈중앙화가 목표

  - 데이터가 서비스 업체의 저장소에 있는 것이 아니라 프로토콜 내에 있는 것이다. 

  - 여러 블록체인 서비스는 새로운 프로토콜의 서비스이다. 예) 스팀잇

  - 구조의 변경을 의미하고 팀버너스리가 Web3.0을 주창하고 있다.


변경되는 점

  - 프로토콜 개발은?

  - 애플리케이션 개발은?j


libp2p

  - CASTO: 리모트몬스터에서 하고 있는 서비스: live stream broadcast를 위한 decentralized web

    MiniStreamer 소스코드

  - IPFS 정의




CASTO


분산화 방송 플랫폼 코어 기술을 만들고 있음.

  - 블록체인: 서버 + DB + 분산시스템 + 보상시스템

  

이더리움선택

  - geth

  - remix: 웹 IDE 

  - solidity: 개발 언어

  - web3.js: geth 접근, JSONRPC 사용

  - metamask: web3.js 포함, 이더리움 월렛 역할, 사용자 Auth(컨트랙트 실행권한)  

    => 로컬 개발 내역을 웹으로통해 geth node로 접근할 때 반드시 필요하다. 

    => Ganache 사이트 참조


메타마스크

  - 소스분석

  - 소스의 web3는 메타마스크가 주입해 준것이다. 

  - 신규는 비동기 async await 사용 가능 (구버전은 callback방식)

// https://github.com/MobiconSoft/simple-blog-example-on-ethereum/blob/master/src/index.js

let web3js = new Web3(web3.currentProvider);




Serverless P2P 구축



- IPFS의 근간이 되는 libp2p 라이브러리를 근간으로 사용하는 애플리케이션일뿐...

- libp2p 브라우져, Node.js, Javascript 지원

- 연결과정

  + discover: peer 끼리 dial 

  + 연결받은 peer가 handle을 상대 peer에게 전달한다. 

- mutliAddr를 제공하여 접속

  + 서로 다른 네트워크 환경이어도 접근이 가능하다. 

- lib2p2는 TCP, WebRTC, WebSockets, UDP를 제공한다.

- Node 장애에서도 무정지 버시스 대응이 가능하다.

// 로컬 노드에서 수행하기 (소스)

$ npm install -g libp2p-webrtc-star

$ start-signal --port 12345


- 만일 electron등의 애플리케이션으로 수행을 하면 해당 애플리케이션 WebRTC server 즉, Node역할을 할 수 있다. 




HTML5 기반 블록체인 서비스


WebRTC + Blockchain 기반의 웹 게임 서비스의 개발: PlayDapp

  - 블록체인에서 웹, WebRTC가 굉장히 중요해진다. 

  - 통화로: 비트코인, 캐시, 이더리움, EOS, 리플만 사용


사용이유

  - 고객의 데이터로 이익창출이지만 고객에게 돌아가는 보상이 없다.

  - 데이터 중앙집중 관리로 해킹 공격 대상

  - 서비스의 영속성 문제: 경영 문제시 서비스 Shutdown


web + p2p + blockchain 을 통한 platform의 확산이 예상됨.




<참조>

- 블록체인이 불러온 Web3.0

- IPFS란 무엇인가?

- Awesome IPFS 목록

- 이재호님 Serverless libp2p 발표 슬라이드


posted by peter yun 윤영식
2018.11.25 18:05 AI Deep Learning

자연어 처리 강좌를 정리한다. 





개념


- 문장을 분해하여 관계를 만들어 주어야 한다. 

- 데이터를 서로 연결하다보면 2차원 이상으로 발전한다. 

- 자연어는 모델링은 쉽지만, 전처리와 결과의 해석이 중요한다. 결과에 따른 해석이 따라 의미가 틀려진다. 이는 이미지/음성과 틀린 부분이다.

- 음운론, 형태론, 통사론, 의미론, 추리론

- Document -> Tokenizing -> Streaming/Tagging (컴퓨터가 이해하는 방식으로 배치하기) -> 최종 Word2Vec 에서 사용함




Tokenizing


- 구글 Colab을 사용한다.

- Token: document에서 쓸만한 것들로 의미를 같는 문자열 -> Tokenizing

- Colab에서 필요한 모듈 설치하기 in ubuntu

! apt-get update

! apt-get install g++ openjdk-8-jdk 

! pip3  install  nltk konlpy wordcloud matplotlib gensim 


! apt-get install fonts-nanum*

! apt-get install fontconfig

! fc-cache -fv

! cp /usr/share/fonts/truetype/nanum/Nanum* /usr/local/lib/python3.6/dist-packages/matplotlib/mpl-data/fonts/ttf/

! rm -rf /content/.cache/matplotlib/*


- import nltk # Natural Language ToolKit 사용

  + sent_tokenize, word_tokenize

- import re  #정규표현식 사용

// 패키지 사용

text = """갤럭시(GalaxyNote9)노트9. 2018년 08월 폭발적인 인기를 이끌고 있습니다. 

담당자 010-222-9999. 홍보팀 010-8888-9999"""

from nltk import sent_tokenize, word_tokenize, FreqDist

sent_tokenize(text)

tokens = word_tokenize(text)


// 정규표현식

import re

tokenizer = re.compile(r'[가-힣]+')

tokenizer.findall(text)




Stemming & Tagging


순서 Tokenizing -> Stemming -> Tagging을 한다. 일반/평문이 결과값이 잘나오고, 강조문/도치문/압축문은 일반/평문으로 만들어하는게 좋다. 


Stemming: 정규화

- 토큰의 어근/어간 추출

- 가능한 비슷한 의미 형태로 묶어주기 => 원형화한다

   예) 가다, 가니, 가고 => 가- 

         산뜻하다, 산뜻하니, 산뜻하고 => 산뜻하-


Tagging: 문법/Filter

- Token별 높은 확률의 태그(속성값/문법)을 추가 

- 단어의 품사별로 묶음: 동사, 명사, 부사


// 토큰

text = "Don't hesitate to ask question"

from nltk.tokenize import TreebankWordTokenizer

tokenizer = TreebankWordTokenizer()

token = tokenizer.tokenize(text)


// 태그

from nltk import pos_tag

pos_tag(token)


// 한글처리

// 테크

from konlpy.tag import Okt

twitter = Okt()

twitter.pos('서울R&D캠퍼스 수업자료')


//토큰, 스테밍을 같이 처리 stem === stemming

text = "워런 버핏은 삼성전자가 아닌 애플주식을 왜 샀을까"

print(twitter.pos(text, stem="true"))




Word Cloud로 시각화하기


- Visualization (시각화)

- 연설문을 워드클라우드로 표현하기 

// 필요 모듈 설치

! apt-get update

! apt-get install g++ openjdk-8-jdk 

! pip3  install  nltk konlpy wordcloud matplotlib gensim 


! apt-get install fonts-nanum*

! apt-get install fontconfig

! fc-cache -fv

! cp /usr/share/fonts/truetype/nanum/Nanum* /usr/local/lib/python3.6/dist-packages/matplotlib/mpl-data/fonts/ttf/

! rm -rf /content/.cache/matplotlib/*

speech_text = "https://raw.githubusercontent.com/YongBeomKim/nltk_tutorial/master/data/pyongyang_fin.txt"

script_text = "https://raw.githubusercontent.com/YongBeomKim/nltk_tutorial/master/data/movie_memories_of_murder_2003.txt"

font_file = "/usr/local/lib/python3.6/dist-packages/matplotlib/mpl-data/fonts/ttf/NanumGothicCoding.ttf"


// 워드클라우드 사용

import requests

import pandas as pd

import matplotlib.pyplot as plt

from wordcloud import WordCloud

from nltk import FreqDist

from nltk.tokenize import word_tokenize

import nltk

nltk.download('punkt')


texts = requests.get(speech_text).text

texts[:100]


// 그리기 

%matplotlib inline

wcloud = WordCloud(font_file).generate(texts)

plt.figure(figsize=(12,12))

plt.imshow(wcloud)

plt.axis('off')


명사만을 사용하여 word cloud 표현

from konlpy.tag import Okt

twitter = Okt()

tokens = twitter.pos(texts, stem=True)

tokens_noun = [token[0] for token in tokens if token[1] == 'Noun']

texts_noun = " ".join(tokens_noun)

texts_noun[:300]




Deep Learning 수행


Natural Language Processing + Deep Learning을 결합하여 챗봇을 만들어가기

  - RNN, LSTM, GRU가 챗봇만드는 기본 알고리즘

  - Data 입력/전처리 (Train Data) -> Cell 선택 및 모델 구성 -> Training / Modeling / Validation -> Model 평가 

  - Input -> Hidden Layer -> Output

  - Input Data 를 구성하는 Word2Vec


Word2Vec는 문장에 따라 데이터를 구조화한다. 

  - 하나의 중심데이터와 주변데이터로 구분함. 

  - 단어간 유사도 검사

  - 구조자체가 작아서, 딥러닝이라 할 수 없다. 

  - 로지스틱 회귀식(2 Cell)을 통해 분류한다. 

  - Corpus (말뭉치) -> Vocabulary Builder -> Context Builder -> Input -> Output 

  


새로운 방식

  - Input 하나에 Output 여러개를 통해 학습 => Skip-gram (sg)

  - try -> error를 통해 맞는 길로 찾아는 방식

과거 방식

  - Input 여러개에 대한 Output 으로 중심 단어 하나 => CBOW (Content Back of Word)


     



실습


  - 20차원의 구조를 만든다. 

  - Dimension(차원)안에 토큰을 넣는다. 비슷한 토큰은 같은 차원에 넣는다. 

  - 빈도수가 적은 것은 하나의 차원에 묶는다. 이안에 빈도수가 높은 것은 중심축에 놓는다. 

    



- size: 차원 30개 (중요)  <== 실습 단어가 151개여서 유의미화를 위해 30차원으로 줄여서 설정했음.

- window: 주변 데이터

- min_count: 10번이상 등장한 단어만 (중요)

- hs: ?

- workers: cpu parallel 동작

- sg: skip-gram

// 전처리후 

...

// 명사 저장

sentences = txtnoun(sentences, skip=skips, tags=["Noun"])

script_file = 'scripts.txt'

with open(script_file, 'w', encoding="utf-8") as file:

   file.write(sentences)


// gensim 이용 모델 만들기

%%time

from gensim.models import word2vec

data = word2vec.LineSentence(script_file)

model = word2vec.Word2Vec(data, size=30, window=2, min_count=10, hs=1, workers=4, iter=100, sg=1)

model_file = "script.model"

model.save(model_file)


결과

CPU times: user 1.59 s, sys: 113 ms, total: 1.71 s, Wall time: 1.7 s


// 모델의 단어수 

model = word2vec.Word2Vec.load(model_file)

len(model.wv.vocab.keys())

list(model.wv.index2word)


결과

151 


// 근접 백터값 확인하기, 참깨밭을 제거하고 추출해 봄

model.wv.most_similar(['현장','백광호'], negative=['참깨밭'], topn=20)


30차원을 시각화하기 위해 skitlearn의 TSNE를 이용해서 2차원으로 변경해서 표현한다. 




<참조>

- 강좌 슬라이드, 전체 실습 파일

- 정규 표현식 정리, 실습하는 서비스

KoNLP

- Word2Vec 관련 이론

posted by peter yun 윤영식
2018.11.22 14:37 Meteor/React + Meteor

Meteor의  accounts-password를 패키지를 통한 로그인 환경 만들기를 해본다. 


  - accounts-password 패키지를 통한 미티어 사용자 가입/로그인 사용 방법 알기

  - React Router 설정

  - 미티어의 Tracker.autorun을 통해 reactivity computation과 resource 사용 방법 알기

  - 소스코드




미티어 사용자 관리


accounts-password 패키지를 설치하고, 가입 및 로그인 화면을 만든다. 

$ meteor add accounts-password

$ meteor npm install --save bcrypt

// Link 사용을 위해 

$ meteor npm install --save react-router-dom


Signup.tsx와 Login.tsx 생성한다. 

  - email, password의 값을 읽기 위하여 typescript방식의 public refs를 정의한다. 

  - Accounts.createUser를 통해 사용자를 등록한다.

// Singup.tsx

import * as React from 'react';

import { Link } from 'react-router-dom';

import { Accounts } from 'meteor/accounts-base'


export interface SignupProps {}

export default class Signup extends React.Component<SignupProps, any> {

  // @see https://goenning.net/2016/11/02/strongly-typed-react-refs-with-typescript/

  public refs: {

    email: HTMLInputElement,

    password: HTMLInputElement

  }


  constructor(props) {

    super(props);

    this.state = {

      error: ''

    };

  }


  onCreateAccount = (e: any) => {

    e.preventDefault();

    let email = this.refs.email.value.trim();

    let password = this.refs.password.value.trim();

    Accounts.createUser({email, password}, (err) => {

      if (err) {

        this.setState({ error: err.reason });

      } else {

        this.setState({ error: '' });

      }

    });

  }


  public render() {

    return (

      <div>

        <h1>Signup to short Link</h1>

        { this.state.error ? <p>{this.state.error} </p> : undefined}

        <form onSubmit={this.onCreateAccount}> 

          <input type="email" ref="email" name="email" placeholder="Email"/>

          <input type="password" ref="password" name="password" placeholder="Password"/>

          <button>Create Acount</button>

        </form>

        <Link to="/login">Already have a account?</Link>

      </div>

    );

  }

}


Login.tsx

  - refs 정의

  - Meteor.loginWithPassword를 통해 로그인한다.

import * as React from 'react';

import { Link } from 'react-router-dom';

import { Meteor } from 'meteor/meteor';


export interface LoginProps {

  history: any;

}

export default class Login extends React.Component<LoginProps, any> {

  public refs: {

    email: HTMLInputElement,

    password: HTMLInputElement

  }


  constructor(props) {

    super(props);

    this.state = {

      error: ''

    };

  }


  onLogin = (e: any) => {

    e.preventDefault();

    let email = this.refs.email.value.trim();

    let password = this.refs.password.value.trim();

    Meteor.loginWithPassword({ email }, password, (err) => {

      if (err) {

        this.setState({ error: err.reason });

      } else {

        this.setState({ error: '' });

      }

    });

  }


  public render() {

    return (

      <div>

        <h1>Login to short Link</h1>

        {this.state.error ? <p>{this.state.error} </p> : undefined}

        <form onSubmit={this.onLogin}>

          <input type="email" ref="email" name="email" placeholder="Email" />

          <input type="password" ref="password" name="password" placeholder="Password" />

          <button>Login</button>

        </form>

        <Link to="/signup">Have a account?</Link>

      </div>

    );

  }

}


NotFound.tsx 파일도 생성한다. 

import * as React from 'react';

export default () => <div>Not Found Page</div>




React Router 설정


라우팅 설정을 통해 가입, 로그인후 Link화면으로 이동하는 설정한다. 


history 모듈을 설치한다.

$ meteor npm install --save history


client/main.tsx에 Route설정을 한다.

import * as React from 'react';

import { Meteor } from 'meteor/meteor';

import { render } from 'react-dom';

import { Provider } from 'react-redux';

import { Route, Router, Switch } from 'react-router-dom';

import { createBrowserHistory } from 'history';

 

import App from '../imports/ui/App'

import Login from '../imports/ui/pages/Login';

import Signup from '../imports/ui/pages/Signup';

import InfoContainer from '../imports/ui/Info';

import NotFound from '../imports/ui/pages/NotFound';

import store from '../imports/ui/store';


const browserHistory = createBrowserHistory();

const Root = (

  <Provider store={store}>

    <Router history={browserHistory}>

      <Switch>

        <Route exact path="/" component={Login} />

        <Route path="/main" component={App} />

        <Route path="/signup" component={Signup} />

        <Route path="/links" component={InfoContainer} />

        <Route path="*" component={NotFound} />

      </Switch>

    </Router>

  </Provider>

);


Meteor.startup(() => {

  render(Root, document.getElementById('react-target'));

});


links는 인증된 사용자만 볼 수 있으므로 Tracker.autorun이라는 Reactivity Computation 영역에서 Meteor.user()라는 Reactivity Source가 로그인 상태인지 검사한다. 

  - 로그인 상태가 아니면 무조건 login화면으로 이동

  - 로그인 상태이면서 인증이 필요없는 화면인 경우 link화면으로 이동

// client/main.tsx 

import { Tracker } from 'meteor/tracker';


Tracker.autorun(() => {

  const isAuthenticated = !!Meteor.user();

  if (isAuthenticated) {

    const pathname = browserHistory.location.pathname;

    const unauthenticatedPages: any = ['/', '/signup'];

    const isUnauthenticatedPage = unauthenticatedPages.includes(pathname);

    if (isUnauthenticatedPage) {

      browserHistory.push('/links'); // main

    } else {

      browserHistory.push(pathname);

    }

  } else {

    browserHistory.push('/'); // login

  }

});


Meteor.startup(() => {

  render(Root, document.getElementById('react-target'));

});


imports/ui/Info.tsx 에 logout 기능 추가

...

import { Accounts } from 'meteor/accounts-base';


interface InfoProps {

  links: any;

  loading: boolean

}


class Info extends React.Component<InfoProps, any> {


  linkList() {

    const { links, loading } = this.props;

    if (loading) {

      return <div>loading...</div>

    } else {

      return <LinkList links={links} />

    }

  }


  onLogout = () => {

    Accounts.logout();

  }


  render() {

    return (

      <div>

        <button onClick={this.onLogout}>Log out</button>

        <AddLink />

        {this.linkList()}

      </div>

    );

  }

}




가입 및 로그인 테스트 확인하기


가입을 하고, 로그인을 한다. 

Chrome Extension 으로 MiniMongoExplorer를 설치하고 user Collection의 내용을 확인한다. 


또는 Chrome DevTool의 Application에서 로그인 사용자의 userId와 console에서 확인해 볼 수 있다. 또한 Link페이지에서 로그아웃을 해보고 콘솔을 확인해 본다. 




<참조>

- 크롬 확장툴 MiniMongoExplorer

- 세번째 글 소스

posted by peter yun 윤영식
2018.11.16 16:07 Meteor/React + Meteor

Meteor에서 Redux 환경을 설정하고, 애플리케이션에 적용해 본다.


  - Meteor Collection의 Pub/Sub 설정

  - Collection의 CRUD 서비스 클래스 개발

  - Redux 환경 설정

  - Redux의 action, reducer 개발 및 store 설정

  - 애플리케이션에 Redux action 호출하기

  - 소스코드 




MongoDB Collection Pub/Sub 설정


기본설치를 하게 되면 autopublish 미티어 패키지가 설치되어 있다. 삭제한다.

$ meteor remove autopublish


삭제를 하면 links 컬렉션으로 부터 데이터를 받지 못한다. 이제 필요한 것을 받기 위해 publish, subscribe를 설정한다. 

  - server/main.tsx에서 insertLink 코드를 제거한다.

  - server/main.tsx에서 links 코드 import 문을 수정한다. 

// main.tsx 전체코드

import { Meteor } from 'meteor/meteor';

import '../imports/api/links';  // 서버에서 사용하기 위해 반드시 import해야 한다. 하지 않으면 collection 생성, 제어를 못 한다.


Meteor.startup(() => { }); // 텅빈 코드 블록


  - imports/api/links.ts 에 publish 한다. 

if (Meteor.isServer) {

  Meteor.publish('links', () => Links.find());

}


  - subscribe를 사용할 때 미티어의 Tracker.autorun을 사용하지 않고, React의 props에 subscribe정보를 바로 맵핑해 주는 withTracker를 사용하기 위해 react-meteor-data 패키지를 사용한다.

$ meteor npm install --save react-meteor-data


  - imports/api/ui/Info.tsx에 withTracker를 설정한다. 

import { Meteor } from 'meteor/meteor';

import Links from '../api/links';


interface InfoProps {

links: any;

loading: boolean;

}


// 맨 하단

export default withTracker(() => {

       const handle = Meteor.subscribe('links');

return {

links: Links.find().fetch(),

loading: !handle.ready()

}

})(Info);




MongoDB Collection CRUD


간단한 Link CRUD 애플리케이션을 만든다. 


  - Collection의 CRUD 로직을 담은 service를 생성한다. imports/ui밑에 link폴더를 생성한다. 

// imports/ui/link/link.service.ts

import Links from '../api/links';


class LinkService {

  addLink({title, url}) {

    Links.insert({title, url, createAt: new Date()});

  }


  removeLink(_id: string) {

    Links.remove(_id);

  }


  updateLink(_id: string, {title, url}) {

    Links.update(_id, {title, url, updateAt: new Date()});

  }

}


export const linkService = new LinkService();


  - imports/ui/link/AddLink.tsx 파일 추가

import * as React from 'react';

import { linkService } from './links.service';

export interface AddLinkProps {

}

export default class AddLink extends React.Component<AddLinkProps, any> {

  handleSubmit = (e: any) => {

    //ignore validation

    e.preventDefault();

    const param = {

      title: e.target.title.value,

      url: e.target.url.value

    }

    linkService.addLink(param);

  };


  public render() {

    return (

      <form onSubmit={this.handleSubmit} >

        <input type="text" name="title" placeholder="title" />

        <input type="text" name="url" placeholder="url" />

        <button>Add Link</button>

      </form>

    )

  }

}


  - imports/ui/App.tsx에서 <Info/> 만 남기고 삭제하고, Info.tsx에 AddLink를 추가한다. 

// App.tsx

class App ... {

  render() { 

    return (

      <div></InfoContainer/></div>

    )

  }

}


// Info.tsx
class Info ... {
  render() {
    return(
      <div> 
          <AddLink />
          ....
       </div>
    )
  }
}


  - link폴더 밑에 LinkList.tsx와 Link.tsx를 추가한다. 

// Link.tsx

import * as React from 'react';

export interface LinkProps {

  link: any

}


export default class Link extends React.Component<LinkProps, any> {

  public render() {

    const { link } = this.props;

    return (

      <li key={link._id}>

        <a href={link.url} target="_blank">{link.title}</a>

      </li>

    );

  }

}


// LinkList.tsx

import * as React from 'react';

import Link from './Link';

export interface LinkListProps {

  links: any[]

}


export default class LinkList extends React.Component<LinkListProps, any> {

  public render() {

    const links = this.props.links.map(

      link => <Link link={link}/>

    );

    return (

      <div>

        <h2>Links</h2>

        <ul>{links}</ul>

      </div>

    );

  }

}



  - Info.tsx에서 LinkList를 사용토록 변경한다. 

class Info extends React.Component<InfoProps, any> {

  linkList() {

    const { links, loading } = this.props;

    if (loading) {

      return <div>loading...</div>

    } else {

      return <LinkList links={links} />

    }

  }


  render() {

    const { links } = this.props;

    return (

      <div>

        <AddLink />

        {this.linkList()}

      </div>

    );

  }

}


  - 마지막으로 link 삭제를 한다. 

// imports/ui/link.Link.tsx

import * as React from 'react';

import { linkService } from './links.service';


export interface LinkProps {

  link: any

}


export default class Link extends React.Component<LinkProps, any> {

  removeLink = () => {

    const { link } = this.props;

    linkService.removeLink(link._id);

  }


  public render() {

    const { link } = this.props;

    return (

      <li key={link._id}>

        <a href={link.url} target="_blank">{link.title}</a>

        <button onClick={this.removeLink}> x </button>

      </li>

    );

  }

}





Redux 관련 패키지 설치


redux 기본 패키지를 설치한다. 

$ meteor npm install --save redux react-redux react-router-redux

$ meteor npm install --save-dev @types/react-redux  @types/react-router-redux


redux action, reducer을 보다 편하게 사용할 수 있는 typesafe-actions 모듈을 설치한다.

$ meteor npm install --save typesafe-actions 


redux에서 비동기를 처리할 수 있는 redux-observable 과 rxjs 모듈을 설치한다. 

$ meteor npm install --save redux-observable rxjs


state 갱신으로 인한 반복 rendering을 제거해 성능향상을 위한 reselect 모듈도 설치한다. 

$ meteor npm install --save reselect




Redux 코드 개발


link action 개발

  - action type constants 정의

  - action Model 정의

  - action  에 대한 정의

// imports/ui/pages/link/link.action.ts

import { action } from 'typesafe-actions';


/**************************

 * constants, model

 **************************/

// constants

export const ADD_REQUEST = '[link] ADD_REQUEST';

export const ADD_SUCCESS = '[link] ADD_SUCCESS';

export const ADD_FAILED = '[link] ADD_FAILED';

export const DELETE_REQUEST = '[link] DELETE_REQUEST';

export const DELETE_SUCCESS = '[link] DELETE_SUCCESS';

export const DELETE_FAILED = '[link] DELETE_FAILED';

export const CHANGE = '[link] CHANGE';


// model

export type LinkModel = {

  _id?: string;

  title?: string;

  url?: string;

  visited?: boolean;

  error?: boolean;

  errorMsg?: any;

  success?: boolean;

  createdAt?: any;

  updatedAt?: any;

};


/**************************

 * actions, action-type

 **************************/

export const addLink = (params: LinkModel) => action(ADD_REQUEST, params);

export const addLinkSuccess = (params: LinkModel) => action(ADD_SUCCESS, params);

export const addLinkFailed = (params: LinkModel) => action(ADD_FAILED, params);

export const removeLink = (id: string) => action(DELETE_REQUEST, id);

export const removeLinkSuccess = (id: string) => action(DELETE_SUCCESS, id);

export const removeLinkFailed = (id: string) => action(DELETE_FAILED, id);

export const changeLink = (id: string) => action(CHANGE, id);


link reducer 개발

  - Link State 정의

  - LinkAction 타입 

  - Link reducer 분기

  - reselect를 이용한 selector는 별도로 만들지 않고, link.reduer.ts 파일이 함께 둔다. (파일이 너무 많아져서...)

// imports/ui/pages/link/link.reducer.ts

import { ActionType } from 'typesafe-actions';

import { combineReducers } from 'redux';

import { createSelector } from 'reselect';

import * as actions from './link.action';


/**************************

 * state

 **************************/

// state

export type LinkState = {

  list: actions.LinkModel[],

  linkFilter: string

};


/**************************

 * reducers

 **************************/

export type LinkAction = ActionType<typeof actions>;

export const linkReducer = combineReducers<LinkState, LinkAction>({

  list: (state = [], action) => {

    switch (action.type) {

      case actions.ADD_FAILED:

        return state;

      case actions.ADD_SUCCESS:

        return [...state, action.payload];

      case actions.DELETE_FAILED:

        return state;

      case actions.DELETE_SUCCESS:

        return state.filter(item => item._id === action.payload);

      case actions.CHANGE:

        return state.map(

          item =>

            item._id === action.payload

              ? { ...item, visited: !item.visited }

              : item

        );

      default:

        return state;

    }

  },

  linkFilter: (state = '', action) => {

    switch (action.type) {

      case actions.CHANGE:

        if (action.payload === 'visited'){

          return '';

        } else {

          return 'visited';

        }

      default:

        return state;

    }

  }

})


/**************************

 * selectors

 **************************/

export const getLinks = (state: LinkState) => state.list;

export const getLinkFilter = (state: LinkState) => state.linkFilter;

export const getFilteredLinks = createSelector(getLinks, getLinkFilter, (links, linkFilter) => {

  switch (linkFilter) {

    case 'visited':

      return links.filter(t => t.visited);

    default:

      return links;

  }

});


redux-observable을 이용한 epic을 만든다.

  - async 처리

  - rxjs 이용

  - takeUntil은 cancel을 위한 장치이다.

// imports/ui/pages/link/link.epic.ts

import { Epic, combineEpics } from 'redux-observable';

import { filter, map, switchMap, takeUntil } from 'rxjs/operators';

import { isOfType } from 'typesafe-actions';


import Links from '../../../api/links';

import { insertCollection, removeCollection, RequestModel } from '../../sdk';

import * as actions from './link.action';


const addLink: Epic = (

  action$,

  store

) =>

  action$.pipe(

    filter(isOfType(actions.ADD_REQUEST)),

    switchMap(action => {

      const { title, url } = action.payload;

      return insertCollection(Links, { title, url, createdAt: new Date() })

    }),

    map((response: RequestModel) => {

      if (response.error) {

        return actions.addLinkFailed({ ...response.result })

      }

      return actions.addLinkSuccess(response.result)

    }),

    // takeUntil(action$.pipe(

    //   filter(isOfType(actions.ADD_REQUEST))

    // ))

  );


const removeLink: Epic = (

  action$,

  store

) =>

  action$.pipe(

    filter(isOfType(actions.DELETE_REQUEST)),

    switchMap(action => {

      return removeCollection(Links, action.payload);

    }),

    map((response: RequestModel) => {

      if (response.error) {

        return actions.removeLinkFailed({ ...response.result, ...response.params })

      }

      return actions.removeLinkSuccess(response.params._id);

    }),

    // takeUntil(action$.pipe(

    //   filter(isOfType(actions.ADD_REQUEST))

    // ))

  );


export const linkEpic = combineEpics(addLink, removeLink);


Meteor Client Collection 을 Observable로 전환

  - meteor client collection의 insert, remove시에 call 결과를 Observable로 변환하여 반환하는 유틸을 만든다. 

  - 이는 redux-observable의 epic에서 async 데이터를 switchMap 오퍼레이터로 다루기 위함이다. 

// imports/sdk/util/ddp.util.ts

import { from, Observable } from 'rxjs';


export type RequestModel = {

  error?: boolean;

  success?: boolean;

  result: any;

  params?: any;

}


export function insertCollection(collection: any, params: any): Observable<RequestModel> {

  return from(new Promise((resolve, reject) => {

    collection.insert(params, (error, result) => {

      if (error) {

        reject({ error: true, result: { ...error }, params: { ...params } });

      }

      if (typeof result === 'string' || typeof result === 'number') {

        resolve({ success: true, result, params: {...params} });

      } else {

        resolve({ success: true, result: { ...result }, params: { ...params } });

      }

    });

  }));

}


export function removeCollection(collection: any, _id: string): Observable<RequestModel> {

  return from(new Promise((resolve, reject) => {

    collection.remove(_id, (error, result) => {

      if (error) {

        reject({ error: true, result: { ...error }, params: { _id } });

      }

      if (typeof result === 'string' || typeof result === 'number') {

        resolve({ success: true, result, params: { _id } });

      } else {

        resolve({ success: true, result: { ...result }, params: { _id } });

      }

    });

  }));

}




RootReducer와 Store 설정


link관련 action, reducer, epic 개발이 끝나면 이를 등록하는 설정을 한다. 

  - root Reducer 정의

  - root State  타입 정의

  - root Action 타입 정의

  - root Epic 정의

  - root Store 설정

// imports/ui/store.ts

import { combineEpics, createEpicMiddleware } from 'redux-observable';

import { createStore, applyMiddleware, combineReducers, compose } from 'redux';

import { RouterAction, LocationChangeAction } from 'react-router-redux';

import { routerReducer } from 'react-router-redux';

import { StateType } from 'typesafe-actions'; 


import { linkEpic } from './pages/link/link.epic';

import { linkReducer, LinkAction } from './pages/link/link.reducer';


/***********************

 * root reducer

 ***********************/

const rootReducer = combineReducers({

  router: routerReducer,

  links: linkReducer

});


/***********************

 * root state

 ***********************/

export type RootState = StateType<typeof rootReducer>;


type ReactRouterAction = RouterAction | LocationChangeAction;

export type RootAction = ReactRouterAction | LinkAction;


/***********************

 * root epic

 ***********************/

const composeEnhancers =

  (process.env.NODE_ENV === 'development' &&

    (window as any) &&

    (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||

  compose;


const rootEpic = combineEpics(linkEpic);

const epicMiddleware = createEpicMiddleware();


/***********************

 * root store

 ***********************/

function configureStore(initialState?: object) {

  // configure middlewares

  const middlewares = [epicMiddleware];

  // compose enhancers

  const enhancer = composeEnhancers(applyMiddleware(...middlewares));

  // create store

  const store = createStore(rootReducer, enhancer);

  // run epic: https://redux-observable.js.org/docs/basics/SettingUpTheMiddleware.html

  epicMiddleware.run(rootEpic);

  return store;

}


const store = configureStore();

export default store;


애플리케이션에 store를 최종 설정한다. 

// client/main.tsx

import * as React from 'react';

import { Meteor } from 'meteor/meteor';

import { render } from 'react-dom';

import { Provider } from 'react-redux';


import App from '../imports/ui/App'

import store from '../imports/ui/store';


const Root = (

  <Provider store={store}>

    <App />

  </Provider>

);


Meteor.startup(() => {

  render(Root, document.getElementById('react-target'));

});


window 객체 인식을 위해 type definition을 정의한다. 

  - 루트에 typings 폴더 생성

  - typings/index.d.ts 파일 생성

declare var window: any;




Application에서 Redux Action 호출하기


add/remove link 리덕스 액션을 애플리케이션에 적용한다. link.service.ts 는 사용하지 않는다. 

  - AddLink.tsx에 addLink 액션 적용

// imports/ui/link/AddLink.tsx

import * as React from 'react';

import { connect } from 'react-redux';

import { addLink } from './link.action';


export interface AddLinkProps {

  addLink: Function;

}


class AddLink extends React.Component<AddLinkProps, any> {

  handleSubmit = (e: any) => {

    //ignore validation

    e.preventDefault();

    const param = {

      title: e.target.title.value,

      url: e.target.url.value

    }

    const { addLink } = this.props;

    addLink(param);

  };


  public render() {

    return (

      <form onSubmit={this.handleSubmit} >

        <input type="text" name="title" placeholder="title" />

        <input type="text" name="url" placeholder="url" />

        <button>Add Link</button>

      </form>

    )

  }

}


export default connect(undefined, { addLink })(AddLink);


  - Link.tsx에 removeLink 액션 적용

// imports/ui/pages/link/Link.tsx

import * as React from 'react';

import { removeLink } from './link.action';

import { connect } from 'react-redux';


export interface LinkProps {

  link: any,

  removeLink: Function

}

class Link extends React.Component<LinkProps, any> {

  removeLink = () => {

    const { link, removeLink } = this.props;

    removeLink(link._id);

  }


  public render() {

    const { link } = this.props;

    return (

      <li key={link._id}>

        <a href={link.url} target="_blank">{link.title}</a>

        <button onClick={this.removeLink}> x </button>

      </li>

    );

  }

}


export default connect(undefined, { removeLink })(Link);





<참조>

- 블로그 소스 코드

react-redux-typescript-guide 소스

- redux-actions 사용하기

- redux-observable 사용하기

- typesafe-actions 사용하기

- reselect 사용하여 성능 최적화 하기

posted by peter yun 윤영식
2018.11.13 16:42 Meteor/React + Meteor

Meteor v1.8이 되면서 CLI에 react기반 애플리케이션 자동 생성 기능이 추가되었다. 여기에 Typescript와 Redux을 접목해서 개발환경을 꾸며 본다. 


  - React기반 Meteor 애플리케이션 생성

  - SCSS 환경설정

  - Typescript 환경설정

  - 미티어의 Reactivity Computation인 withTracker 확인

  - AntDesign CSS Framework 설정

  - 소스코드




React 기반환경 만들기 


meteor를 설치한다. 

// Node Version Manager를 통해 Node LTS 버전을 설치한다. 최신 Meteor 1.8 은 Node v8.*을 사용한다.

$ nvm install 8.12.0


// meteor를 설치한다. 

$ curl https://install.meteor.com | sh


// 향후 meteor v1.8.1 버전이 릴리즈 되면 성능향상을 위해 업그레이드를 반드시 수행한다. 

$ meteor update --release 1.8.1


애플리케이션 생성

// 명령어: meteor create --react <AppName>

$ meteor create --react react-first

$ cd react-first

$ meteor run




SCSS 환경 전환


.css파일을 .scss파일로 전환한다. 이를 지원하기 위해 scss 패키지를 설치한다. 

$ meteor add fourseven:scss




Typescript 관련 모듈 설치


meteor의 typescript 지원 패키지를 설치한다.

$ meteor add barbatus:typescript


@types와 필요 모듈을 설치한다.

$ meteor npm install --save-dev @types/meteor 

$ meteor npm install --save-dev @types/node

$ meteor npm install --save-dev @types/react

$ meteor npm install --save-dev @types/react-dom


$ meteor npm install --save-dev typescript

$ meteor npm install --save-dev tslint

$ meteor npm install --save-dev tslint-react




Typescript 환경 전환


root에 tsconfig.json 파일 추가

{

    "compilerOptions": {

        "target": "es6",

        "allowJs": true,

        "jsx": "preserve",

        "moduleResolution": "node",

        "types": [

            "node"

        ]

    }

}


root에 tslint.json 파일 추가 

// 과도한 lint가 귀찮아 extends는 주석처리함 

{

    // "extends": [

    //     "tslint:latest",

    //     "tslint-react"

    // ],

    "rules": {

        "quotemark": [

            true,

            "single",

            "jsx-double"

        ],

        "ordered-imports": false,

        "no-var-requires": false

    }

}


root의 client와 imports, server 폴더안의 파일 명칭 .jsx / .js 를 .tsx / .ts 로 변경한다. tests 폴더는 .js 그대로 둔다.



마직막으로 package.json에서 mainModule의 client를 main.tsx로 수정한다. 





.tsx 파일 내용을 typescript에 맞게 수정하기


1) import 문 수정

// client, imports 폴더안의 모든 .tsx 파일


// react import

import React from 'react'; ==> import * as React from 'react';


// 절대경로를 상대경로로 수정

import App from '/imports/ui/App'; ==> import App from '../imports/ui/App';


// .jsx 파일을 확장자 제거 App.tsx

import Hello from './Hello.jsx' ==> import Hello from './Hello';

import Info from './Info.jsx' ==> import Info from './Info';


2) class 문 수정

class Hello extends Components {  ==> class Hello extends React.Component {


3) class에 Props 타입 설정

// info.tsx


interface InfoProps {

  links: any;

}


class Info extends React.Component<InfoProps, any> {

  ...

}


4) export default 문 수정

    withTracker는 Meteor의 Reactivity Computation 공간이다. Computation에는 Meteor의 Reactivity Resource가 위치한다. 즉, source가 변경되면 Computation영역이 재실행된다. 

// imports/ui/info.tsx 

export default InfoContainer = withTracker(...)

   ==> 

export default withTracker(...);


// imports/api/links.ts

export default Links = new Mongo.Collection('links');

   ==>

const Links = new Mongo.Collection('links');

export default Links;


이제 다시 meteor run 을 실행한다. 



Ant Design 설치하기


CSS Component를 제공하는 antd를 설치한다. 

$ meteor npm install --save antd

$ meteor npm install --save-dev @types/antd


client/main.tsx 안에 antd css 를 import 한다. 

import '../node_modules/antd/dist/antd.css';


또는, antd.less 파일을 직접 import할 수 있다. 

$ meteor add less

$ meteor npm install --save indexof


// client/theme.less 파일 생성하고 antd.less 파일 import하기

@import '{}/node_modules/antd/dist/antd.less';


antd의 Menu를 App.tsx에 적용해 본다. 

import * as React from 'react';

import Hello from './Hello';

import Info from './Info';

import { Menu, Icon } from 'antd';


const SubMenu = Menu.SubMenu;

const MenuItemGroup = Menu.ItemGroup;


class App extends React.Component {

  state = {

    current: 'mail',

  };


  handleClick = (e) => {

    console.log('click ', e);

    this.setState({

      current: e.key,

    });

  }


  render() {

    return (

      <div>

        <Menu

          onClick={this.handleClick}

          selectedKeys={[this.state.current]}

          mode="horizontal"

        >

          <Menu.Item key="mail">

            <Icon type="mail" />Navigation One

            </Menu.Item>

          <Menu.Item key="app" disabled>

            <Icon type="appstore" />Navigation Two

            </Menu.Item>

          <SubMenu title={<span className="submenu-title-wrapper"><Icon type="setting" />Navigation Three - Submenu</span>}>

            <MenuItemGroup title="Item 1">

              <Menu.Item key="setting:1">Option 1</Menu.Item>

              <Menu.Item key="setting:2">Option 2</Menu.Item>

            </MenuItemGroup>

            <MenuItemGroup title="Item 2">

              <Menu.Item key="setting:3">Option 3</Menu.Item>

              <Menu.Item key="setting:4">Option 4</Menu.Item>

            </MenuItemGroup>

          </SubMenu>

          <Menu.Item key="alipay">

            <a href="https://ant.design" target="_blank" rel="noopener noreferrer">Navigation Four - Link</a>

          </Menu.Item>

        </Menu>

        <Hello />

        <Info />

      </div>

    );

  }

}


export default App;




지금까지의 적용 소스코드, Redux 환경 설정하기는 다음 글에서..




<참조>


- meteor-react-typescript 코드 샘플

- meteor changelog

- meteor에 antd .less 파일 import하기 (데모소스)

- meteor-react-typescript-boilerplate 소스

posted by peter yun 윤영식
2018.10.23 16:52 BlockChain

Ethereum 의 Mining 부분의 동작원리 발표를 정리한다.



Ethereum 

  - P2P Network + Mining의 결합 

  - Mining



소스 설명

  - miner/miner.go

  - worker가 대부분의 mining 로직을 처리한다.

  - new worker가 실행되면 4가지 루프가 go routine 기반으로 계속 돈다. 

  - 발표 자료: 

3. How does go-ethereum work__Aiden.pptx





참조

https://steemit.com/@sigmoid sigmoid 님의 go-ethereum 분석기 : geth의 거의 모든 부분을 잘 정리해주셨습니다. 개인적으로 정말 많은 도움이 되었습니다.

https://github.com/NAKsir-melody/go-ethereum sigmoid 님의 go-ethereum 주석 한글화 프로젝트

http://www.notforme.kr/block-chain/geth-code-reading 자바 개발자의 go-ethereum 소스 분석기 : geth의 entry point부터 차근차근 잘 설명해주셨습니다. 처음 공부를 시작하시는 분들은 이 분의 글을 먼저 보시면 큰 도움이 되실겁니다.

https://blog.seulgi.kim/ 코드박스 김슬기님의 블로그 : 이더리움에 대한 수준 높은 포스트가 정말 많습니다. 직접적으로 소스코드를 다루는 글은 없지만 이더리움의 기술적인 부분을 이해하시는데 정말 큰 도움이 됩니다.

https://tech.etherstudy.net/ethereum/geth/delve/debug/consensus/lifecycle/2018/08/02/geth-consensus-lifecycle-debug.html 임완섭님의 이더리움 컨센서스 라이프사이클 디버깅 : 디버깅을 통해 소스코드를 보다 더 이해하기 쉽게 해주셨습니다. 역시 큰 도움이 되실겁니다.


posted by peter yun 윤영식
2018.10.23 16:14 BlockChain

Onther Devcon 1 세미나에 참여하여 이더리움 2.0 의 현재와 진행방향에 대해 알아보러 왔다. 특별히 인원제한으로 인해 당첨(?)이 되어야 올 수 있었는데 다행이 행운을 얻어 참여할 수 있었다. 




Ethereum 2.0 개괄


발표: 정우현

내용: 2.0

  - Sharding + Casper FFG 메인

  - Multi Layer 확장성 개선

  - p2p 개선작업

  - signature 알고리즘 개선

  - EVM의 병렬처리 개선 -> web assembly기반


Casper FFG

  - PoW를 대체

  - Staking ether 방식

  - Finality(피넬리티): 만들어진 블록이 최종적인지 판단 가능


P2P 프로토콜

  - devp2p에서 libp2p로 변경

  - IPFS 프로토콜

 

PoW Main Chain -> Beacon Chain -> Shard Chain -> VM

  - Beacon chain이 Shard chain을 validating한다.

  - 현재는 Beacon이 개발 메인이다. 내년 release?

  - Stake of Proof는 Beacon Chain에 존재하고, Shard chain은 TX와 DATA를 핸들링한다.

  - 처리용량은 Shard chain까지 가면 제곱으로 증가한다. 

  - Beacon Chain Spec: 어려 팀에서 빠르게 개발되어 가고 있다.



Beacon chain 이외 Ethereum 2.0에서 다루고 있는 내용

  - 각 기능을 각각의 다른 팀들이 알아서 만드는 중


2.0 의 개발 범위



Plasma vs Sharding

  - Sharding: Validator, Proposer, Committee

    Plasma: Operator, User, Challenger

  - Shard Chain은 1024개의 새로운 체인을 추가

  

eWASM

  - EVM의 대체물로 개발

  - Google WASM (Web Assembly)를 사용

posted by peter yun 윤영식
2018.10.18 14:52 AI Deep Learning

네패스의 뉴로모픽 인공신경망 장비를 통한 실습 워크샵 내용을 정리한다. RBF 알고리즘을 사용하는 칩이다. 




Radial Bassis Function (RBF)

중심점으로부터 일정한 거리에 있는 데이터만 골라낸다. 거리에 대한 정보를 끄집에 내는게 중점이다. 

  - 거리를 표한하는 법은 가우시안 분포(정규분포)를 사용한다

  - 가우시안 함수를 여러개 중첩시켜 다양한 함수(=인공지능)를 만들 수 있다. 

  - 입력: 특징정보를 준다.

  - 네패스 칩은 하나의 가우시안 칩으로 중첩해서 사용할 수 있다.





뉴로모픽 NM500



  - 576개의 뉴론이 있다.

  - 각 뉴론은 독립적으로 작동한다. 

  - 실시간으로 학습하고 모델을 만들어 낼 수 있다. 

  - 전처리된 feature를 넣어주어야 RBF를 통해 처리를 할 수 있다. 모든 것을 다 해주는 것은 아니다. 

  - 디지털화 가능한 모든 데이터 입력은 가능하다. 

  - 학습된 뉴런들만 작동한다. 

  

사용예

  - 라켓에 칩을 붙여서 스윙의 괘적이 잘 되고 있는지 알려준다. 

  - 어선에서 잡은 물고기중에서 원하는 것만을 골라냄 

  - 칩하나로 할 수 있는것

   + Fish sorting: 200 뉴런 사용

   + Glass Inspection: 유리의 이물질 발견 - 800 뉴런 사용

   + Motion classification: 움직임 괘적 추적 - 128 뉴런 사용




NM500 뉴런


뉴론안에 context끼리 서로 맞아야 연결된다. 네트워크를 구분하는 용도로 사용한다. 예로 얼굴인식이면 얼굴인식 뉴런끼리 연결되도록 한다.

Active Influence field 값이 학습을 진행하면서 정교하게 정해진다.

DEG (DeGenerate Flag)은 Minimum Influence Filed이하로 내려가면 작동한다.

Difference(차이) 절대값은 L1, Lsup을 사용한다. 





Knowledge Studio를 통한 실습


이미지에 대해 학습을 하고 딕텍션을 할 수 있다. 

  - context를 만든다.

  - 이미지를 선택한다. 

  - 이미지에 ROI라는 사이즈를 만들어 Full scan 학습을 한다.

  - 디텍션으로 가서 학습시킨 이미지를 찾거나, novelty를 선택하여 이전 이미지와 차이가 나는 부분을 찾는다. 


예) 100원 동전이 없는 이미지를 context 100사이즈, ROI w/h = 80, stride = 80으로 학습을 하고, 100원 동전이 있는 이미지로 교체후 디덱션을 해서 이전 이미지와 틀린 부분이 무엇인지 찾는다. 



참조

  - 제너럴비젼사와 MOU하여 뉴로모픽 NM500을 만든 것임

  - 확률 가우시안이란



posted by peter yun 윤영식
2018.10.17 15:11 Data Visualization/Vega

Vega는 선언적인 차트 생성 라이브러리이다. 좀 더 추상화하고 쉽게 사용할 수 있도록 Vega-lite를 제공한다. C3.js 또는 네이버의 C3.js 확장판인 Billboard.js를 생각하면 된다.




특징


- SVG, Canvas를 지원한다. 

- Javascript코드없이 JSON환경으로 차트를 생성한다. 

- Editor, Recommendation Charting해주는 Voyager등을 제공한다.


테스트: https://vega.github.io/editor/#/ 접속해서 다음 환경값을 넣는다. 


{
"$schema": "https://vega.github.io/schema/vega-lite/v2.json",
"data": {"url": "data/seattle-weather.csv", "format": {"type": "csv"}},
"mark": "bar",
"encoding": {
"x": {"timeUnit": "month", "field": "date", "type": "ordinal"},
"y": {"aggregate": "mean", "field": "precipitation", "type": "quantitative"}
},
"config": {"axisY": {"minExtent": 30}}
}





개념 


Vega-lite 소개 동영상을 보고 전반적으로 이해하자. 


좀 더 자세한 개념설명





사용예


Elastic Search에서는 Customizing Chart를 만들기 위해 Vega를 기능으로 추가하여 사용할 수도 있다.


Elastic Search에서 Vega 구현 예제 소스

Vega, Vega-lite, 또는 Vega Editor, Vega Voyager등은 Standalone library 형태로 애플리케이션에 포함하여 사용할 수 있다.




'Data Visualization > Vega' 카테고리의 다른 글

[Vega] Vega, Vega-lite 넌 뭐니  (0) 2018.10.17
posted by peter yun 윤영식
2018.09.14 17:31 React

실제 프로젝트에서 사용을 안하다보니 자꾸 처음 내용이 익숙해 지지 않은 상태에서 잊어버리고 만다. React + Typescript기반을 다시 시작해 본다. 



Install React with Typescript

React와 Typescript 환경을 만든다

// 2018.9 현재 LTS NodeJS 버전

$ nvm use 8.12.0

// typescript v3.0.3 으로 업데이트

$ npm i -g typescript 

$ npm i -g create-react-app (or yarn global add create-react-app)


// TYPESCRIPT-CSS 

$ create-react-app my-app --scrips-version=react-scripts-ts


package.json에 eject기능이 있어서 webpack config를 밖으로 추출하여 직접 핸들링할 수 있게 한다. 

$ cd my-app

$ npm run eject



SCSS 환경구축

css 환경을 scss 환경으로 바꿔준다. 

// scss loader 설치

$ yarn add node-sass sass-loader --dev


App.css와 index.css의 확장자를 .scss로 바꾸고, App.tsx, index.tsx의 import 문 확장자를 .scss로 바꾼다. 

import './App.scss';


config/webpack.config.dev.js 와 webpack.config.prod.js 파일안에 scss설정을 추가한다. 빨간 부분을 

      {

            test: /\.(css|scss)$/,

            use: [

              require.resolve('style-loader'),

              {

                loader: require.resolve('css-loader'),

                options: {

                  importLoaders: 1,

                },

              },

              {

                loader: require.resolve('postcss-loader'),

                options: {

                  // Necessary for external CSS imports to work

                  // https://github.com/facebookincubator/create-react-app/issues/2677

                  ident: 'postcss',

                  plugins: () => [

                    require('postcss-flexbugs-fixes'),

                    autoprefixer({

                      browsers: [

                        '>1%',

                        'last 4 versions',

                        'Firefox ESR',

                        'not ie < 9', // React doesn't support IE8 anyway

                      ],

                      flexbox: 'no-2009',

                    }),

                  ],

                },

              },

              {

                loader: require.resolve("sass-loader"),                

                options: { } 

              }

            ],

      },


기존 App.scss내용을 다음과 같이 바꾸어 확인해 본다. 

.App {

  text-align: center;

  &-logo {

    animation: App-logo-spin infinite 20s linear;

    height: 80px;

  }


  &-header {

    background-color: rgb(197, 40, 40);

    height: 150px;

    padding: 20px;

    color: white;

  }


  &-title {

    font-size: 1.5em;

  }


  &-intro {

    font-size: large;

  }

}

yarn start하여 점검!



Component LifeCycle


일반 컴포넌트

  constructor -> componentWillMount -> render -> componentDidMount -> componentWillUnmount


props, state 사용 컴포넌트

  componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate

  - shouldComponentUpdate: true, false로 다음으로 이벤트를 넘길수도 안할수도 있음




Component Type

PureComponent

  shouldComponentUpdate에서 Shallow compare하므로 reference를 바꾸어 주어야 render가 호출됨. 


Functional Component (Stateless Function Component)

  const myComp: Reat.SFC<Props> = (props) => {...}

import * as React from“ react”

interface WelcomeProps {

  name: string,

}


const Welcome: React.SFC < WelcomeProps > = (props) => {

  return <h1 > Hello, {

    props.name

  } < /h1>;

}



Router for SPA

react-router v4

   BrowserRouter, Route, Link, NavLink, Redirect 사용

   BrowserRouter는 window.history.pushState() 로 동자하는 라우터

   RouteComponentProps: route되면서 url의 파라미터를 props로 받음. history, match 속성을 가짐.

<BrowserRouter>

   <Route exact={true} path="/" component={} or render={} or children={} >

      <Link to="/a" />

      <NavLink activeStyle={{ color: red }} to="/b" /> 

   </Route>

   <Redirect from="/a" to="/b" />

</BrowserRouter>


Switch로 감쌈

  <Switch>

    <Route ... />

    <Route ... /> 

    <Route ... />

 </Switch>



Redux

react-redux

  여러  action을 reducer를 통해 하나의 store를 만든다.

     - action 타입을 만들고 action 객체를 만드는 펑션을 (action creator) 정의한다.

     - action을 처리하는 reducer 펑션을 정의한다. (Pure function, immutable)

        reducer 펑션을 action별로 나눈 다음 사용시에는 combineReducers 로 합쳐서 사용한다. 

     - reducer를 createStore에 넣어주면 single Store가 만들어진다. 

        store에는 getState(), dispatch(액션), subscribe(리스너),  replaceReducer(다른리듀서) 4개 메소드가 있음

   redux를 react에 연결하기

      - componentDidMount: subscribe

      - componentWillUnMount: unsubscribe

      - Provider는 context에 store를 담아 모든 component에서 store를 사용할 수 있도록 한다. 

         Provider를 제공하는 react-redux를 사용한다.  

   connect를 통해 컴포넌트에 연결한다. 

      - App에 대한 High order component이다.

      - 전체 state의 모양과 action creator를 파라미터로 넣어준다.

$ yarn add redux react-redux @types/redux @types/react-redux


const mapStateToProps = (state: { age: number}) => {

  return {

     age: state.age

  }

}


const mapDispatchToProps = (dispatch: Function) => {

  return {

      onAddAge: () => { dispatch(addAge()); }

  };

}


interface AppProps {

  age: number;

  onAddAge: void;

}


connect(mapStateToProps, mapDispatchToProps)(App);


class App<{props: AppProps}> extends Component {

    return <div>{this.props.age}</div>

}


  dispatch안에서 async처리를 할 수 있다.   

  applyMiddleware는 dispatch 전후에 처리할 수 있다. 

  action이 async일 때 미들웨어인 redux-thunk를 이용한다.



<참조>

- 2017 Typescript Korea 강의 (유튜브)

- scss 적용하기

- scss loader 설정하기, Class 사용 in Velopert

- Typescript + React 환경 만들기 in Velopert

- React LifeCycle 설명

- Component Type - SFC 설명

- React Router in Velopert 소개





'React' 카테고리의 다른 글

[React] 다시 시작하기  (0) 2018.09.14
[React] Semantic-UI를 React 컴포넌트로 만들기 - 1  (0) 2015.08.23
[React] CSS Framework 선정하기  (0) 2015.08.15
[Flux] Flux 배우는 방법  (0) 2015.07.04
[React] AngularJS와 ReactJS 비교  (0) 2015.07.01
[React] 배우는 방법  (0) 2015.05.15
posted by peter yun 윤영식
2018.08.22 14:40 AI Deep Learning/Tensorflow

이찬우님의 텐서플로우 유튜브강좌를 정리한다. 




강좌 3


로지스틱 비용함수를 만들기

  - 좌측과 우측에 대한 convex 를 만들기 위한 식

cost = tf.reduce_sum(-y*tf.log(output)-(1-y)*tf.log(1-output), reduction_indices=1)


prediction과 label끼리의 정확도를 판단하기 

  - 각자의 벡터 매칭이 맞으면 1, 틀리면 0으로 하는 값을 다시 n*1 벡터로 만든다. 

  - 해당 백터의 값에 대한 평균을 구한다. 

  - 이때 1, 0값은 bool이어서 float32로 변환하여 계산한다.

  - 잘 맞으면 평균 1 이 나온다. 

comp_pred = tf.equal(tf.argmax(output, 1), tf.argmax(y, 1))

accuracy = tf.reduce_mean(tf.cast(comp_pred, tf.float32))


모델 저장하기

  - Training시킨 모델을 저장하는 것을 Checkpoint라 한다.

  - 저장내용에는 Weight과 Model이 저장될 수 있다. 

  - Weight관련 Variable을 저장한후 Save한다.

  - 저장시 유의점은 Variable, placeholder 함수의 파라미터중 하나인 Name이 자동으로 지정된다. 

W_o = tf.Variable(tf.truncated_normal(shape=[HIDDEN2_SIZE, CLASSES], dtype=tf.float32))

b_o = tf.Variable( tf.zeros([CLASSES]), dtype=tf.float32)


param_list = [W_h1, b_h1, W_h2, b_h2, W_o, b_o]

saver = tf.train.Saver(param_list)


hidden1 = tf.sigmoid(tf.matmul(x, W_h1) + b_h1)

hidden2 = tf.sigmoid(tf.matmul(hidden1, W_h2) + b_h2)


....

for i in range(1000):

    _, loss = sess.run([train, cost, accuracy], feed_dict = feed_dict)

    if i % 100 == 0:

        saver.save(sess, './tensorflow_3_lec.ckpt')

        ...




강좌 4


저장된 Weight Restoring하기 

  - save 할 때 Widget의 Variable에 name을 지정한다. 

  - 

x = tf.placeholder(tf.float32, shape=[None, INPUT_SIZE], name='x')

y = tf.placeholder(tf.float32, shape=[None, CLASSES], name='y')

W_h1 = tf.Variable(tf.truncated_normal(shape=[INPUT_SIZE, HIDDEN1_SIZE], dtype=tf.float32), name='W_h1')

b_h1 = tf.Variable(tf.zeros([HIDDEN1_SIZE]), dtype=tf.float32, name='b_h1')

hidden1 = tf.sigmoid(tf.matmul(x, W_h1) + b_h1, name='hidden1')

hidden2 = tf.sigmoid(tf.matmul(hidden1, W_h2) + b_h2, name='hidden2')

output = tf.sigmoid(tf.matmul(hidden2, W_o) + b_o, name='output')

...

saver.restore(sess, './tensorflow_3.ckpt')




강좌 5


Tensorboard는 디버깅 용도이다. 공식 튜토리얼을 참조한다.

  - name_scope는 묶음 단위이다.

  - scalar: 로그 데이터 남기기

  - tf.summary.merge_all()

  - tf.summary.FileWriter(<dir>, session.graph) 

# 가설함수 

with tf.name_scope('hidden_layer_1') as h1scope:

    hidden1 = tf.sigmoid(tf.matmul(x, W_h1) + b_h1, name='hidden1')


with tf.name_scope('hidden_layer_2') as h2scope:

    hidden2 = tf.sigmoid(tf.matmul(hidden1, W_h2) + b_h2, name='hidden2')

    

with tf.name_scope('output_layer') as oscope:

    output = tf.sigmoid(tf.matmul(hidden2, W_o) + b_o, name='output')


....


# 수행 

sess= tf.Session()

sess.run(tf.global_variables_initializer())


merge = tf.summary.merge_all()


for i in range(1000):

    _, loss, acc = sess.run([train, cost, accuracy], feed_dict = feed_dict)

    if i % 100 == 0:

        train_writer = tf.summary.FileWriter('./summaries/', sess.graph)


$ tensorboard --logdir=./summaries 수행한다. 




강좌 6


Loading Data in Tensorflow 참조. CSV파일 읽기

  - decode_csv로 콤마기반의 csv파일을 읽어들인다.

  - record_defaults = [[1], [1], [1], [1], [1], [1], [1], [1], [1]]  Fixed 자리수에서 비어있는 값에 대한 default value이다. 

  - start_queue_runners는 Session.run이 수행되기전에 호출해서 queue를 실행해서 file이 queue에서 대기토록 한다.

  - Coordinator 는 Thread 관리를 수행한다.

!

Image 읽기

  - FixedLengthRecordReader로 읽음

  - decode_raw를 사용함




to be continue...




<참조>

  - 텐서플로우의 체크포인트 설명

  - 텐서플로우 Save & Restore

  - 파이썬의 With 구문 이해 





'AI Deep Learning > Tensorflow' 카테고리의 다른 글

[Chanwoo Jacod Lee] Tensorflow 강좌 정리  (0) 2018.08.22
posted by peter yun 윤영식
2018.08.21 16:52 AI Deep Learning/Read Paper

Data2Vis 논문에 대한 개념을 알아본 후 다른 곳에 응용을 하려면 어떻게 어떤 단계를 거쳐서 진행해야 할지 실험을 해본다. 




준비


컴파일 환경

Python v3.7

Tensorflow v1.9

Anaconda기반에서 구동한다.




Step-1) 모델 환경설정


train_options.json 에 정의된 Model의 파라미터 내용

  - Data2Vis는 Attention 메카니즘을 가지는 Encoder-Decoder 아키텍쳐이다. 

  - 2-layer bidirectional RNN encoder/decoder를 사용한다.

  - GRU보다 LSTM이 보다 좋은 성능을 나타내서 LSTM을 사용한다. 


Loss(Cost)와 Training 함수를 포함한 모델(Model)은 AttentionSeq2Seq를 사용하고, 해당 모델에 대한 환경설정 파일은 example_configs/nmt_large.yml에 정의되어 있다. 

  - 데이터: source와 target 정보의 위치를 지정한다. 

  - 가설/비용 함수: Encoder/Decoder 를 구성하고 inference 파라미터등도 설정한다. Encoder/Decoder의 Cell은 LSTMCell을 사용한다.

  - Training 함수: Adam optimizer 사용



Step-2) Data 전처리


모델을 Training시키기 위해서 Dataset의 Field를 numeric, string, temporal, ordinal, categorical등으로 분류를 해놓는다. 이에 대한 Output(Labeled)으로 Vega-lite문법에 맞추어 환경파일을 각각 만든다. 

  - sourcedata/*.sources 또는 *.targets 파일중에 dev.sources와 dev.targets를 보면 dataset의 index당 vega-lite spec을 매칭했다.

  - vega-lite문법에서 data 필드만 제외한다.

  - 총 3가 성격의 sources, targets를 준비한다.

    + dev

    + train

    + vocab

  - dataset의 필드를 특별히 str<index>, num<index> 로 변환한다.

  - 데이터 전처리를 위한 스크립트는 utils/*.py에 있다.

  - 데이터 전처리 전의 실데이터는 testdata/*.json에 vega-lite의 다양한 spec은 examples/*.json 에 있다.

//dev.sources 

[{"num0": 0, "num1": null, "str0": "Small", "str1": "AMERICAN AIRLINES", "str2": "AUSTIN-BERGSTROM INTL", "str3": "Approach", "str4": "Day", "str5": "None", "str6": "Unknown bird - small", "num2": 0, "str7": "MD-80", "str8": "8/1/95 0:00", "str9": "Texas", "num3": 0}]

[{"num0": 0, "num1": 140, "str0": "Small", "str1": "US AIRWAYS*", "str2": "CHARLOTTE/DOUGLAS INTL ARPT", "str3": "Approach", "str4": "Day", "str5": "None", "str6": "European starling", "num2": 0, "str7": "B-737-300", "str8": "7/19/99 0:00", "str9": "North Carolina", "num3": 0}]


//dev.targets
{"encoding": {"y": {"field": "str0", "type": "nominal", "selected": true, "primitiveType": "string"}, "x": {"type": "quantitative", "field": "num2"}}, "mark": "point"}
{"encoding": {"y": {"field": "str3", "type": "nominal", "selected": true, "primitiveType": "string"}, "x": {"type": "quantitative", "field": "num0"}}, "mark": "tick"}




Step-3) 모델 생성하기


모델 환경설정과 Training을 위한 source, target 데이터가 준비되었다면 모델을 생성한다. 

  - procject-directory 위치를 변경한다.

  - bin/train.py를 수행을 위한 파라미터이다.

  - vizmodel로 ckpt파일을 생성되므로 별도 지정을 해보자. (data2vis에 이미 생성된 ckpt가 존재한다.)



Step-4) 추론 검증


Data2Vis는 Model을 미리 ckpt로 저장해 놓았고, WebDemo가 존재한다. webserver.py 는 Flask로 구성하여 간단하게 다음의 작업을 수행한다.

  - 웹화면에서 Generate Example 버튼을 클릭하면 examplesdata/*.json에서 실제 dataset 을 random하게 읽어온다.

  - 실데이터의 field를 str, num으로 바꾸어 inference에 넣은후 Vega-lite spec를 output로 받는다.

  - 출력으로 나온 Vega-lite spec에 data 필드에 실데이터를 맵핑하여 최종 Vega-lite spec를 만들어 HTTP response를 한다. 

또는 command console에서 직접 수행해 볼 수 있다. 



<참조>


- 구글 
   tf-seq2seq 튜토리얼
   seq2seq NMT 튜토리얼

- Data2Vis 논문

posted by peter yun 윤영식
2018.08.16 15:06 AI Deep Learning/Read Paper

Data2Vis는 seq2seq를 통해 입력되는 데이터를 기반으로 출력으로 차트를 자동생성한다.




개념


해당 논문을 이해하기 위해 다음과 같은 단어의 개념을 이해해야 한다. 소스에서도 같은 용어를 쓰기 때문에 소스이해를 위해서도 중요하다. 


 - Data2Vis의 데모 사이트에 가면 간략한 설명이 나와 있다.


    

  

  - Attention mechanism을 이용한 encoder-decoder 아키텍쳐 모델이다.  

  - key/value 쌍의 데이터를 입력으로 하고 Vega-Lite기반의 출력을 생성한다. Vega-Lite는 JSON기반으로 차트를 생성해주는 스펙이다. 

  - 특징

     + encoder는 최종 context vector 하나로 만든다. 이것을 C 라고 표현한다. 위이 그림에서 가운데 위치한 C이다. 

     + decoder는 학습할 때 encoder의 "C"와 "<go>답안"을 입력받아 "답안<eos>"를 출력하는 학습을 한다. (참조)

     + encoder, decoder의 길이를 정해야 한다. 무한정일 수 없다.

     + 여기서 encoder, decoder는 동시에 학습할 수 있다. (참조)

     + 정답이 있는 데이터만 S2S 학습이 가능하다.

     + 단어들에 대한 벡터화한 수치 사전이 필요하다. (참조)

  - beam search

     + RNN의 학습 과정에서 트리 탐색 기법으로 쓰임

     + 최고우선탐색(Best-First-Search)기번을 기본으로 회되 기억해야 하는 노드 수를 제한해 효율성을 높이는 방식

     + beam : 사용자가 기억해야 하는 노드 수

  - LSTM

     + Backward Propagation(역전파)할 때에 Gradient Vanishing이나 Exploding되는 현상을 막기 위해 LSTM을 사용한다. 

     + 역전파할 때 미분한다. Gradient는 결국 기울기 이고, 미분또한 기울기를 구하는 것으로 역전파를 할 때 미분의 값이 작을 때 Gradient Vanshing이 발생하고, 클때 exploding이 발생한다.

     + Gradient Vanishing에 대한 자세한 설명은 영덕의 연구소를 참조한다.  





소스 설치 및 실행


소스를 깃헙에서 클론한다.

$ git clone https://github.com/victordibia/data2vis.git


환경 설정

  - Anacoda를 설치

  - Python v3.6.5 사용

  - Tensorflow v1.9.0 사용 (Anaconda Navigator UI에서 설치하지 않고 conda CLI로 버전을 지정해서 설치한다.)

$ conda install -c conda-forge tensorflow=1.9.0


모듈 설치

  - requirements.txt는 node.js의 package.json역할

$ cd data2vis

$ pip install -r requirements.txt


실행하기 

$ python webserver.py


브라우져에서 http://localhost:5016/  호출


디버깅하기

MS Code에서 다음 항목을 추가한다.

  - port: listen 포트

  - model_dir: 모델이 있는곳, 이곳에 seq2seq의 환경파일인 train_options.json 파일이 존재해야 한다. 

     해당 파일은 training 시킨 결과를 통해 자동으로 생성된다. 훈련시키는 방법에 대해서는 두번째 글 참조.

  - beam_width: 사용자가 기억해야 하는 노드수 5개

{

    "version": "0.2.0",

    "configurations": [

        {

            "name": "Python: Data2Vis - Flask (0.11.x or later)",

            "type": "python",

            "request": "launch",

            "program": "${workspaceFolder}/webserver.py",

            "env": {

                "FLASK_APP": "${workspaceFolder}/webserver.py",

                "FLASK_ENV": "development"

            },

            "args": [

                "--port=5016",

                "--model_dir=vizmodel",

                "--beam_width=15"

            ]

        },

        {

            "name": "Python: Current File",

            "type": "python",

            "request": "launch",

            "program": "${file}"

        },

        .....

}


샘플 실행

  - 좌측 examples 메뉴를 클릭하고 입력창에 1이상의 값을 넣고, "Generate Examples" 버튼을 클릭하면 차트가 생성된다. 





seq2seq 모듈


구글이 개발한 tf-seq2seq 모듈 소스을 data2vis 폴더에 그대로 copy해 놓은 상태이다.  모델을 학습하고 검증하는 것은 실제 seq2seq가 하므로 tf-seq2seq 사용방법을 알아야 한다. 

   - tf-seq2seq 소개 블로그: Goolge NMT 논문 필수

   - seq2seq에 대한 기본 설명은 Arxiv의 Neural Machine Translation 논문을 참조한다. 

   - Tensorflow의 seq2seq 사용법



Configuration Training

  - 환경파일에는 Input data, model, training parameter를 정의한다.

  - vismodel의 train_options.json 파일을 사용한다.

  - optimizer 종류와 learning_rate등을 지정. Adam 옵티마이저를 사용.

  - vocab_target, vocab_source 임베딩을 위한 벡터 카운트를 만들기 위해 파일 지정

  - decoder, encoder class와 params을 설정. 둘 다 LSTMCell 사용

  - attention class와 params 설정

  - inference, bridge, embedding 설정

  - source/target.max_seq_len 으로 string의 크기 지정

  - 모델 옵션 설명

  - 인코더 옵션 설명

  - 디코더 옵션 설명


{

    "model_class": "AttentionSeq2Seq",

    "model_params": {

        "optimizer.name": "Adam",

        "decoder.class": "seq2seq.decoders.AttentionDecoder",

        "inference.beam_search.beam_width": 5,

        "decoder.params": {

            "rnn_cell": {

                "dropout_input_keep_prob": 0.5,

                "num_layers": 2,

                "cell_params": {

                    "num_units": 512

                },

                "dropout_output_keep_prob": 1.0,

                "cell_class": "LSTMCell"

            },

            "max_decode_length": 2000

        },

        "optimizer.learning_rate": 0.0001,

        "source.reverse": false,

        "source.max_seq_len": 500,

        "attention.params": {

            "num_units": 512

        },

        "attention.class": "seq2seq.decoders.attention.AttentionLayerDot",

        "vocab_target": "sourcedata/vocab.target",

        "target.max_seq_len": 500,

        "optimizer.params": {

            "epsilon": 8e-07

        },

        "bridge.class": "seq2seq.models.bridges.ZeroBridge",

        "vocab_source": "sourcedata/vocab.source",

        "encoder.params": {

            "rnn_cell": {

                "dropout_input_keep_prob": 0.5,

                "num_layers": 2,

                "cell_params": {

                    "num_units": 512

                },

                "dropout_output_keep_prob": 1.0,

                "cell_class": "LSTMCell"

            }

        },

        "encoder.class": "seq2seq.encoders.BidirectionalRNNEncoder",

        "embedding.dim": 512

    }

}



Training 

  - 모델과 교육데이터가 갖추어져 있으면 훈련을 수행한다.

  - /sourcedata안에 source, target의 trainig data가 존재한다.

  - utils/data_gen.py에서 /examples 폴더의 vega spec을 읽어와 training data를 만들고 있다. 



Prediction

  - 모델 Training을 받은 후 예측을 시작할 수 있다. 

  - DecodeText 클래스를 사용하고, Input pipeline은 ParallelTextInputPipeline을 사용함. 

    + DecodeText는 모델 예측을 가져와 표준 출력으로 추력하는 작업을 수행함

    + DumpAttention과 DumpBeams을 이용해 모델 수행시 디버깅을 할 수 있다. 파일로 쓰는 것임.

    + input pipline은 데이터를 읽는 방법을 정의한다.



Decoding with Beam Search

  - 빔 검색은 번역 성능을 향상시키는 일반적으로 사용되는 디코딩 기술이다. 

  - 빔 검색은 메모리에 가설 또는 빔(beam)을 놓고 가장 높은 점수인 것을 선택한다. 



Evaluating specific checkpoint

  - Training을 통해 다양한 모델의 체크포인트를 저장한다.

  - BLEU (bilingual evaluation understudy)를 통해 번역 성능 평가. 

 


Checkpoint에 대한 설명

- Saving

  + model 을 만드는 코드 의존적인 포멧을 갖는다.

  + 체크포인트는 training하며 생성된 모델의 버전이다.

  + Estimator가 checkpoint를 model_dir 위치에 저장한다. 

  + events 파일은 tensorboard가 시각시에 사용한다. 

  + Saver를 통해 체크포인트를 Saving/Restoring 한다. 


checkpoint

events.out.tfevents.timestamp.hostname

graph.pbtxt

model.ckpt-1.data-00000-of-00001

model.ckpt-1.index

model.ckpt-1.meta

model.ckpt-200.data-00000-of-00001

model.ckpt-200.index

model.ckpt-200.meta


- Restoring

  + Estimator는 train()을 호출하면 model의 그래프를 model_fn()을 호출해서 생성한다. 

  + Estimator는 최근의 checkpoint를 통해 새로운 모델의 weight을 초기화 한다. 






webserver.py 이해


파이썬 웹서비스는 Flask를 이용한다.  data2vis/static 과 templates가 Flask운영을 위해 사용된다. 


webserver.py 실행 순서

- port, vizmodel, beam_width를 아규먼트를 받는다.

- vizmodel/train_options.json을 기반으로 TrainOption 오브젝트를 생성

train_options = training_utils.TrainOptions.load(model_dir_input)


- model params을 사용해 model class를 생성함. AttensionSeq2Seq.py (attension_seq2seq.py)

model_params = _deep_merge_dict(model_params, _maybe_load_yaml(model_params))

model = model_cls(params=model_params, mode=tf.contrib.learn.ModeKeys.INFER)


- inference task 생성. DecodeText 생성

if (str(tdict["class"]) == "DecodeText"):

        task = task_cls(tdict["params"], callback_func=_save_prediction_to_dict)


- ParallelTextInputPipeline pipeline 생성

input_pipeline_infer = input_pipeline.make_input_pipeline_from_def(

    fl_input_pipeline,

    mode=tf.contrib.learn.ModeKeys.INFER,

    shuffle=False,

    num_epochs=1)


- inference를 사용하는 (Tensorflow) graph 생성.

  + seq2seq/inference/inference.py에서 pipeline과 batch_size를 통해 input function을 만들고

  + input function의  feature와 label을 model의 파라미터로 사용해서 model의 build를 호출한다. 

predictions, _, _ = create_inference_graph( model=model, input_pipeline=input_pipeline_infer, batch_size=batch_size)


- Listen을 하고, Flask의 routing을 설정한다. "Generate Examples" 버튼 클릭시 호출 

  + test data를 사용한다. 

  + normalize를 해준다. (foward_norm, backward_norm)

  + decode result를 가지고 vega spec을 만들어 return한다. 

@app.route("/examplesdata")

def examplesdata():

    source_data = data_utils.load_test_dataset()

    f_names = data_utils.generate_field_types(source_data)

    data_utils.forward_norm(source_data, destination_file, f_names)


    run_inference()

    

    decoded_string_post = data_utils.backward_norm(decoded_string[0], f_names)


    try:

        vega_spec = json.loads(decoded_string_post)

        vega_spec["data"] = {"values": source_data}

        response_payload = {"vegaspec": vega_spec, "status": True}

    except JSONDecodeError as e:

        response_payload = {

            "status": False,

            "reason": "Model did not produce a valid vegalite JSON",

            "vegaspec": decoded_string

        }

    return jsonify(response_payload)





<참조>


  - Data2Vis 소개글, 깃헙 소스, Arxiv 링크

  - deeep 블로그
     seq2seq 에 대한 쉬운 설명글

     Attention 메카니즘설명 (소개한 Arxiv 링크)

  - ratsgo 블로그
     RNN과 LSTM 이해

     seq2seq를 이용한 뉴스 제목 추출하기
     설명에 대한 소스 (2018년 tensorflow 버전에 맞지않다)

     beam search 이해 in Recursive Neural Network

  - 구글 제공
     seq2seq 문서

  - Tensorflow의 seq2seq 한글 설명, 2014년 Arxiv에 소개된 seq2seq pdf

  - 라온피플 블로그
     RNN, LSTM, GRU 소개

  - 영덕의 연구소 블로그
     Gradient Vanishing 문제 개념

  - Naivsphere 블로그

    SGD (Stochastic Gradient Descent)에 대한 글

  - 카카오 IT 브런치
     BLEU: NMT 평가 방식 설명

  - skymind.ai

  - epoch, batch_size 용어 이해 (MNIST epoch, batch 설명)

posted by peter yun 윤영식
2018.08.11 16:12 AI Deep Learning

텐서플로우 강좌를 들으면서 다음의 모델을 실습으로 쥬피터에 코딩을 했다. 여기서 Cost function을 짤때 log함수를 왜 사용하고 앞에 - (마이너스)값은 왜 붙이는지 정리해 본다. 



준비

  - 아나콘다 설치

  - jupyter notebook 실행

  - python v3 


import tensorflow as tf


input_data = [[1, 5, 3, 7, 8, 10, 12]]

label_data = [0, 0, 0, 1, 0]


INPUT_SIZE = 7

HIDDEN1_SIZE=10

HIDDEN2_SIZE=8

CLASSES = 5

Learing_Rate = .05


x = tf.placeholder(tf.float32, shape=[None, INPUT_SIZE])

y = tf.placeholder(tf.float32, shape=[CLASSES])


feed_dict = {x: input_data, y: label_data}


W_h1 = tf.Variable(tf.truncated_normal(shape=[INPUT_SIZE, HIDDEN1_SIZE], dtype=tf.float32))

b_h1 = tf.Variable( tf.zeros([HIDDEN1_SIZE]), dtype=tf.float32)

hidden1 = tf.sigmoid(tf.matmul(x, W_h1) + b_h1)


W_h2 = tf.Variable(tf.truncated_normal(shape=[HIDDEN1_SIZE, HIDDEN2_SIZE], dtype=tf.float32))

b_h2 = tf.Variable( tf.zeros([HIDDEN2_SIZE]), dtype=tf.float32)

hidden2 = tf.sigmoid(tf.matmul(hidden1, W_h2) + b_h2)


W_o = tf.Variable(tf.truncated_normal(shape=[HIDDEN2_SIZE, CLASSES], dtype=tf.float32))

b_o = tf.Variable( tf.zeros([CLASSES]), dtype=tf.float32)

output = tf.sigmoid(tf.matmul(hidden2, W_o) + b_o)


cost = tf.reduce_mean(-y*tf.log(output)-(1-y)*tf.log(1-output))

train = tf.train.GradientDescentOptimizer(Learing_Rate).minimize(cost)


sess= tf.Session()

init = tf.initialize_all_variables()

sess.run(init)


for i in range(10):

    _, loss = sess.run([train, cost], feed_dict = feed_dict)

    print('step:', i)

    print('lost:', loss)


//결과

step: 0
lost: 0.794413
step: 1
lost: 0.780786
step: 2
lost: 0.767388
step: 3
lost: 0.754181
step: 4
lost: 0.741128
step: 5
lost: 0.728179
step: 6
lost: 0.715276
step: 7
lost: 0.702345
step: 8
lost: 0.689285
step: 9
lost: 0.675957



cost(비용) 함수의 목적은 비용 판단을 통해 올바른 W(가중치, 기울기)와 b(바이어스, 시작점)을 찾는 것이다. 다시 말하면 목표하는 W과 b를 찾을 수 있다면, 어떤 형태가 되었건 비용함수라고 부를 수 있다는 뜻이다. 



Inference => Loss => Training => Evaluation  순서로 진행을 한다. 좋은 예제로 MNIST.py 구글 강좌 예제 소스를 먼저 참조해 보자.


- Inference: 가설함수 수립

- Loss: 비용함수 수립

- Training: 최적화 작업 수행하여 Loss (Cost)가 작아지는 W, b 값을 구함

- Evaluation: 검증은 Training을 통해 구해진 W, b가 가설함수에 적용되어 test data를 넣었을 때, Labeled data와 일치하는지 검증함




Step-1) 가설함수 

  - matmul 은 Matrix Mutiply의 약어이다. 

  - sigmoid는 binary classification의 한계를 넘기 위해 적용. sigmoid 그래프는 중심축 0을 중심으로 좌측은 0으로, 우측을 1로 수렴한다.

  - 가설함수 (Hyphothesis):  H(x) = sigmoid(Wx + b) 로 결과값은 0 또는 1의 값을 갖는다. 수학 공식으로 하면,

     H(x) = 1 / (1 + math.exp(-(Wx + b))

     소스에서는  output = tf.sigmoid(tf.matmul(hidden2, W_o) + b_o) 이 가설함수이다.



Step-2) 비용함수

  - 가설함수를 정의했다면 해당 가설 함수의 적정한 W, b를 찾기 위해 비용(Cost) 함수를 정의한다.

     비용함수는 예측값과 실제값의 차이에 대한 평균값을 구한다. 로지스틱 회귀에 사용되는 실제 비용함수를 수학 공식으로 하면,

     cost = (1/n) * sum( -y_origin * log(sigmoid(Wx + b)) - (1 - y_origin) * log(1 - (sigmoide(Wx + b))) 

       n: 트레이닝 데이이터 수

       y_origin: 트레이닝에 에 사용될 x에 대한 입력값

     소스에서는  cost = tf.reduce_mean(-y*tf.log(output)-(1-y)*tf.log(1-output)) 이 비용함수이다. 


  - 비용함수에서 가설함수의 e를 사용하는게 아니라 log를 사용하는 이유는 e를 통해 비용함수를 그리면 다음과 같이 나오기 때문에 매끈한 경사를 만들기 위해 e의 역치함수인 log를 사용한다. 

    비용함수는 y=1 일때와 y=0 일때는 나누어 계산한다. 


  - 측정(실제 입력)값 y=1 일때는 -log(H(x)), -log(가설함수) 즉 -log(sigmoide(Wx + b)) 를 사용하고 그래프로 보면 sigmoid(Wx+b)가 0~1사이에 있고 -log(0~1)을 그린다. 

     H가 1이면 cost(1) = 0 이 되고, H가 0이면 cost = 무한대가 된다.

     좌측의 밥그릇이 된다.

     측정(실제 입력)값 y=0 일때는 가설값이 0이 되어야 한다. 이는 0에 가까울 수록 cost는 0에 가까워야 한다. -log(1 - sigmoid(Wx + b)) 되고 log(1 - (0~1)) 이 된다. 

    우측의 밥그릇이 된다.

     요약하면 -log(h)는 좌측, -log(1-h)는 우측이다. 


  * 주의할 것은 Linear Regression (binary classification)을 하기 위해 평균에 제곱을 하지 않는다. 

 

  - y=0 일때와 y=1일때의 각 수식을 하나의 수직으로 만들기 위해 다음 공식을 사용해 합쳐서 표현한다. 소스에서는 -y*tf.log(output)-(1-y)*tf.log(1-output) 이 된다. 

  


      y * A + (1-y) * B => y * -log(H(x)) + (1-y) * -log(1 - H(x)) => -y*log(H(x)) - (1-y)*log(1 - H(x))  => -( y*log(H(x)) + (1-y)*log(1 - H(x)) )


  - 이것을 다시 재구성하면 다음과 같다. 소스에서는 cost = tf.reduce_mean(-y*tf.log(output)-(1-y)*tf.log(1-output)) 이다.

  



Step-3) 옵티마이저

  -  코스트함수가 정해지면 옵티마이저를 설정한다. 

      경사하강법 (GradientDescent)는 cost(W)을 미분을 적용해서 W의 다음 위치를 계산하는 공식이다. 다음 위치로 이동하는 것은 Learning Rate (이동하는 Step 크기)로 정해진다.

  - W 와 b의 적정값을 계산하기 위해 비용함수를 만들었다면 학습을 통해 GradientDescent가 W와 b값을 구한다. 여기서 나온 W,b를 통해 예측을 수행한다.

  - 소스는  tf.train.GradientDescentOptimizer(Learing_Rate).minimize(cost) 이다.

  - GradientDescent Optimizer는 gradient를 계산해서 변수에 적용하는 일을 동시에 하는 함수이다. W와 b를 적절하게 계산해서 변경하는 역할을 하며, 그 진행 방향이 cost가 작아지는 쪽으로 수행한다. train을 수행하게 되면 텐서 그래프의 모든 변수의 값이 자동 변경되며 계산된다. 소스는 _, loss = sess.run([train, cost], feed_dict = feed_dict) 이다.


  - 또한 텐서를 run하기 전에 그래프에 연결된 모든 variable을 초기화해야 한다. 

  - 옵티마이저 설명 참조

  



Step-4) 가절 검증

  - Accuracy or Evaluation

  - output 은 hypothesis 가설함수로 이것을 실제 테스트를 해본다. 

  - Linear에서 x, y 데이터를 placeholder를 통해 train 시키고, 최종 W,b가 구해진 가설함수에 대해서 test 데이터를 넣어 보고 예측이 맞는지 검증한다. 

  - Linear Regression 예 (Linear Regression에 대한 이해 참조)

import tensorflow as tf

x_data = [1.,2.,3.]

y_data = [1.,2.,3.]


W = tf.Variable(tf.random_uniform([1], -100., 100.))

b = tf.Variable(tf.random_uniform([1], -100., 100.))


X = tf.placeholder(tf.float32)

Y = tf.placeholder(tf.float32)


h = W * X + b

cost = tf.reduce_mean(tf.square(h - Y))


rate = tf.Variable(0.1)

op = tf.train.GradientDescentOptimizer(rate)

train = op.minimize(cost)


init = tf.initialize_all_variables()


sess = tf.Session()

sess.run(init)


for step in range(2001):

    sess.run(train, feed_dict = {X: x_data, Y: y_data})

    if step % 100 == 0:

        print(step, sess.run(cost, feed_dict={X: x_data, Y: y_data}), sess.run(W), sess.run(b))


print('Test:', sess.run(h, feed_dict={X: 5}))




<참조>

- 김성훈 교수님 강좌 요약 블로그

- 조대협의 로지스틱 회귀 분석 블로그

- Tensorflow 유튜브 강의

- 로지스틱 회귀 이해

- 선형, 비선형 회귀 모델의 이해

- Tensorflow의 자료형 이해, 상수/변수/플레이스홀더 이해

- 선형 회귀를 Tensorflow로 구현하기

posted by peter yun 윤영식
2018.07.31 15:25 AI Deep Learning

인공지능 공부하기 위한 여정을 정리하고 계속 업데이트 한다.  하루에 2시간 이상 꼭 공부를 해야 강좌를 완료할 수 있다. 





Machine Learning & Deep Learning


김성훈 교수님의 인프런 강좌

   - 모두를 위한 딥러닝 - 기본적인 머신러닝과 딥러닝 강좌

   - 모두를 위한 딥러닝 - Deep Reinforcement Learning



앤드류응 교수님의 코세라 강좌

   - 머신러닝



머신러닝 Engineer 학습경로

 Machine Learning Engineer 커리큘럼


    + Supervised Learning: Hypothesis  set <-> DAG (비순환 그래프)를 만드는 과정
    + Disttribution: Binary classification (이진분류), Multicase classification (다중 분류), Linear regression (선형 회귀), Multimodal linear regression (다항 회귀)


  - 테리님 왈 다음과 같은 과정으로 공부하길 추천함
   <초급>
    - 조대협님의 머신러닝 블로그글 (내가 추가한 것임)
   <중급>


T 아카데미의 인공지능 강좌

   - 인공지능을 위한 머신러닝 알고리즘

     + 김성훈 & 앤드류응 교수님의 강좌수강 후 개념 정리용 (단, 수학공식이 많이 나옴, 수학방을 이용하자)

     + 설명중 회귀 모델에 대한 보다 자세한 설명

     + 설명중 우도(가능도-Likelihood) 자세한 설명



Microsoft의 데이터 과학 전문 프로그램

  - 필수 과목 11개

  - 과정당 12시간: 하루 2시간 계산하면 과정당 1주가 걸림 -> 총 11주 프로그램     

  - 필요한 것을 선별해서 보면 좋을 듯함.




Interactive Data


Python 배우기

  - T 아카데미의 Python 프로그래밍: 파이썬 3 기반 기초 

  - T 아카데미의 Python을 활용한 데이터분석 기초: Jupyter Notebook기반으로 Pandas, Seaborn 실습

  - T 아카데미의 Python을 이용한 데이터분석 실습

  - Edwith의 머신러닝을 위한 Python



Tensorflow 배우기 

  - Tensorflow 공식 홈페이지

  - 골빈해커의 텐서플로우 강좌

  - 이찬우님의 텐서플로우 유튜브 강좌

  - Tensorflow로 MNIST 실습 SlideShare



CNN 이해

  - 초보자가 이해하기 좋은 CNN 이해 SlideShare

  - CNN 스탠포드 강좌 - CS231n



RNN 이해

  - 초보자가 이해하기 좋은 CNN, RNN 이해 SlideShare

  - RNN LSTM 이해 - Colah




용어 이해

 - Softmax 이해

 - Activation Function 이해

 - Back Propagation 이해. 수식으로 설명, 코드로 설명

 - ReLu 이해: 역전파의 오류 방지

 - 알고리즘 복잡도 이해 - Big O == "최악의 경우(시간복잡도가 클 경우)에도 이 시간 정도면 된다" 라는 의미

 - Log 함수 이해

 - tanh 이해

 - LSTM 이해




To be continued... 


사실 계속 공부해도 감 잡기는 정말 힘들다. 프로젝트를 해봐야 뭐라도 하나 잡고 갈듯하다. 




<참조>

- 인공지능과 머신러닝 학습 경로

테리님 블로그

조대협님 블로그

- 조경현 교수님의 뉴욕댁 머신러닝 강좌 소개소스

- 조경현 교수님이 참조하였다는 머신러닝의 강의 자료

- 시그모이드 함수 설명

- Activation Function 설명

- 역전파(Backpropagation) 설명, 잘 이해해야 함 (Adnrej Karpathy), Andrej의 RNN 이야기

- 로그함수 설명

- 수학방로그함수 좀더 기초적 설명

- 수학 기호 명칭 - 수식을 볼려면 기호의 의미를 알아야 한다.

- 미분에 대한 쉬운 이해

- MNIST - yann.lecun.com

posted by peter yun 윤영식