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

Publication

Category

Recent Post

2023. 5. 12. 09:59 React/Architecture

Webpack v5.* 기반의 Module Federation 개념을 간단히 정리하고, 환경을 설정해 본다. 

 

Module Federation Concept

Micro Frontend를 위한 컨셉에서 출발

- 참조: https://mobicon.tistory.com/572

개별 빌드 배포

 

Host 모듈 & Remote 모듈

- 모듈: webpack 번들링으로 생성된 js, css, html 파일 묶음

- Host 모듈: 단일 webpack 모듈 -> 개별 번들링된다. 

- Remote 모듈: 단일 webpack 모듈 -> 개별 번들링된다. 

   + 빌드시 호스트/원격 모듈 따로 따로 빌드 관리된다. 원격 모듈은 다른 도메인에서 제공할 수도 있다.

- 컨테이너: 각각 따로 빌드되며 독립적인 애플리케이션이다. 

    + A, B 컨테이너가 존재하면 각자 상호 로딩가능한다. 

- Expose: 컨테이너가 로딩할 원격 모듈 설정

    + 자세한 예: 참조

    + expose 되는 것은 별도의 chunk file 이 생성된다. (즉 해당 chunk file만 로딩해서 사용함)

// Remote App의 webpack.config.js 에서 exposes하기 (App2)
  plugins: [
    // To learn more about the usage of this plugin, please visit https://webpack.js.org/plugins/module-federation-plugin/
    new ModuleFederationPlugin({
      name: 'app2',
      filename: 'remoteEntry.js',
      exposes: {
        './App': './src/App',
      },
      shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
    }),
    new HtmlWebpackPlugin({
      template: './public/index.html',
    }),
  ],
  
// Host App의 webpack.config.js에서 remote app 로딩하기 (App1)
  plugins: [
    new ModuleFederationPlugin({
      name: "app1",
      remotes: {
        app2: "app2@[app2Url]/remoteEntry.js",
      },
      shared: {react: {singleton: true}, "react-dom": {singleton: true}},
    }),
    new ExternalTemplateRemotesPlugin(),
    new HtmlWebpackPlugin({
      template: "./public/index.html",
    }),
  ],
  
// App1에서 index.js 에서 app2Url 설정으로 remote app url 설정 
// You can write your own logic here to determine the actual url
window.app2Url = "http://localhost:3002"
  
// Host App에서 app2의 App 비동기 로딩 (App1)
import React, {Suspense} from "react";
const RemoteApp = React.lazy(() => import("app2/App"));

const App = () => {
  return (
    <div>
      <div style={{
        margin:"10px",
        padding:"10px",
        textAlign:"center",
        backgroundColor:"greenyellow"
      }}>
        <h1>App1</h1>
      </div>
      <Suspense fallback={"loading..."}>
        <RemoteApp/>
      </Suspense>
    </div>)
}


export default App;

App1이 호스트 앱, App2가 리모트 앱

 

- 공유 모듈: 여러 컨테이너에서 같이 사용하는 모듈

    + 예: react, react-dom     

- 호스트 앱: 원격 모듈을 사용하는 컨테이너

    + 리모트 앱이 expose한 원격 모듈을 호스트 앱에서 비동기 로딩해서 사용한다. 

- 리모트 앱: 모듈을 expose하는 컨테이너

 

 

NX 기반 환경설정

NX는 module federation 설정의 Host & Remote App을 생성하고 환경설정에 대해 이해한다. (참조)

 

Portal App & Micro Apps 역할

- Portal App

   + Remote 앱이 되어서 다양한 packages의 모듈을 expose 한다.

- Micro App

   + Host 앱이 되어서 portal의 exposed module을 async loading하여 사용한다.

 

Dashboard App에서 widget을 사용, Portal App 에서 Dashboard App을 사용한다

 

Portal App 모듈과 Micro App 모듈의 분리

- Micro App은 필요한 모듈을 Portal App (remote app) 으로 부터 로딩하여 사용한다. 따라서 Micro App에서 필요한 모듈을 package.json에 설정하여 npm install 하여 로컬에 설치 후 사용하는 것이 아니라, runtime에 로딩하여 사용할 수 있다. 

- Micro App 개발시 참조하는 모듈을 로컬에 설치할 필요없이 개발을 진행할 수 있다. 

- 즉, Micro Frontend의 개념을 적용하여 개발을 진행한다.

 

명령어 예

- host라는 host app이 자동 생성된다

- store 이라는 remote app이 자동 생성된다.

- @nx/react:host 의 명령어에 따라서 module federation 관련한 설정 내역이 자동으로 생성된다. 

nx g @nx/react:host mf/host --remotes=mf/store

mf폴더 밑으로 host, store app 생성

- host app 생성파일들

  + main.ts 에서 bootstrap.tsx를 import 형식: project.json에서 main도 main.ts 로 설정됨 (기존은 main.tsx 하나만 존재)

  + module-federation.config.js 파일 생성: remote 설정

  + webpack.config.<prod>.js 파일들 생성 

  + project.json: serve 의 executor가 @nx/react:module-federation-dev-server 로 변경됨

 

또는 remote app만들 별도로 생성할 수 있다. 

npx nx g @nx/react:remote portal/store

- remote app 생성파일들

  + remote-entry.ts 

  +  main.ts 에서 bootstrap.tsx를 import 형식: project.json에서 main도 main.ts 로 설정됨 (기존은 main.tsx 하나만 존재)

  + module-federation.config.js 파일 생성: exposes 설정

  + webpack.config.<prod>.js 파일들 생성 

  + project.json: serve 의 executor가 @nx/react:module-federation-dev-server 로 변경됨

 

host를 실행하면

  + 관련된 remote도 자동으로 실행된다. (remote는 project.json의 static-server의 port로 자동 실행된다.)

  + 즉, host app과 remote app이 동시에 구동된다. 

nx serve mf-host --open

 

NX 기반 설정파일 이해하기

remote app 설정 파일들

- webpack.config.js

  + withModuleFederation은 node_modules/@nx/react/src/module-federation/with-module-federation.js 위치하고 있고, remote와 shared할 libraries를 자동으로 설정해 준다. 즉, remote빌드시 shared libraries는 external libs로 취급되어 번들파일에 포함되지 않는다. 

  + nx 명령을 통해 생성한 remote app에는 webpack.config.js와 webpack.config.prod.js 파일이 자동 생성 및 설정되어 있다.

const { composePlugins, withNx } = require('@nx/webpack');
const { withReact } = require('@nx/react');
const { withModuleFederation } = require('@nx/react/module-federation');

const baseConfig = require('./module-federation.config');

const config = {
  ...baseConfig,
};

// Nx plugins for webpack to build config object from Nx options and context.
module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config));

- module-federation.config.js 파일명은 변경하지 말고 그대로 사용해한다. 

  + shared쪽에 libraryName을 체크하여 singleton, strictVersion, requiredVersion을 설정할 수 있다. (host도 동일)

       > return undefined 이면 nx default 값 사용

       > return false 이면 shared library로 사용하지 않겠다는 의미이다. 

       > version을 명시하는 경우는 host와 remote간에 버전이 서로 틀릴 경우 사용한다. 

module.exports = {
  name: 'mf-store',
  exposes: {
    './Module': './src/remote-entry.ts',
  },
  shared: (libraryName, config) => {
    if (libraryName && libraryName.indexOf('@gv') >= 0) {
      config = { singleton: true, strictVersion: true, requiredVersion: '1.0.0' };
    }
    console.log('--- remote libraryName:', libraryName, config);
    return config;
  },
};

- project.json 

  + serve의 executor가 webpack-dev-server가 이니라, module-federation-dev-server 이다. 이는 host 기동시 remote도 자동 기동해 준다.

 "serve": {
      "executor": "@nx/react:module-federation-dev-server",
      "defaultConfiguration": "development",
      "options": {
        "buildTarget": "mf-store:build",
        "hmr": true,
        "proxyConfig": "apps/mf/store/proxy.conf.json",
        "port": 3001
      },
      "configurations": {
        "development": {
          "buildTarget": "mf-store:build:development"
        },
        "production": {
          "buildTarget": "mf-store:build:production",
          "hmr": false
        }
      }
    },

- remote-entry.ts 파일

  + host에 접근할 micro-frontend 애플리케이션

export { default } from './app/dashboard-app';

 

host app 설정 파일들

- webpack.config.js 개발시 내역은 remote app설정과 동일하다.

- webpack.config.prod.js 에는 remote app의 url path 가 설정된다.

// host의 webpack.config.prod.js 내역

const { composePlugins, withNx } = require('@nx/webpack');
const { withReact } = require('@nx/react');
const { withModuleFederation } = require('@nx/react/module-federation');

const baseConfig = require('./module-federation.config');

const prodConfig = {
  ...baseConfig,
  /*
   * Remote overrides for production.
   * Each entry is a pair of a unique name and the URL where it is deployed.
   *
   * e.g.
   * remotes: [
   *   ['app1', 'http://app1.example.com'],
   *   ['app2', 'http://app2.example.com'],
   * ]
   *
   * You can also use a full path to the remoteEntry.js file if desired.
   *
   * remotes: [
   *   ['app1', 'http://example.com/path/to/app1/remoteEntry.js'],
   *   ['app2', 'http://example.com/path/to/app2/remoteEntry.js'],
   * ]
   */
  remotes: [['mf-store', 'http://localhost:3001/']],
};

// Nx plugins for webpack to build config object from Nx options and context.
module.exports = composePlugins(withNx(), withReact(), withModuleFederation(prodConfig), (config) => {
  // Update the webpack config as needed here.
  // e.g. `config.plugins.push(new MyPlugin())`

  // .tsx 에서  import 구문 ordering 경고 문구 발생 해결하기
  // https://github.com/facebook/create-react-app/issues/5372
  const instanceOfMiniCssExtractPlugin = config.plugins.find(
    (plugin) => plugin.constructor.name === 'MiniCssExtractPlugin'
  );

  if (instanceOfMiniCssExtractPlugin) {
    instanceOfMiniCssExtractPlugin.options.ignoreOrder = true;
  }

  return config;
});

- module-federation.config.js host 자신 app의 명칭과 remote app 의 명칭을 설정한다. 

module.exports = {
  name: 'mf-host',
  remotes: ['mf-store'],
  shared: (libraryName, config) => {
    // ref: https://www.angulararchitects.io/en/aktuelles/the-microfrontend-revolution-part-2-module-federation-with-angular/
    if (libraryName && libraryName.indexOf('@gv') >= 0) {
      config = { singleton: true, strictVersion: true, requiredVersion: '1.0.0' };
    }
    console.log('--- host libraryName:', libraryName, config);
    return config;
  },
};

  + node_modules/@nx/react/src/module-federation/with-module-federation.js 소스 내역

      > NX에서 host의 sharedLibraries 에 대해 자동으로 목록을 만들어 준다. 

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.withModuleFederation = void 0;
const tslib_1 = require("tslib");
const utils_1 = require("./utils");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
/**
 * @param {ModuleFederationConfig} options
 * @return {Promise<AsyncNxWebpackPlugin>}
 */
function withModuleFederation(options) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const { sharedDependencies, sharedLibraries, mappedRemotes } = yield (0, utils_1.getModuleFederationConfig)(options);
        return (config, ctx) => {
            var _a;
            config.output.uniqueName = options.name;
            config.output.publicPath = 'auto';
            config.optimization = {
                runtimeChunk: false,
            };
            config.experiments = Object.assign(Object.assign({}, config.experiments), { outputModule: true });
            config.plugins.push(new ModuleFederationPlugin({
                name: options.name,
                library: (_a = options.library) !== null && _a !== void 0 ? _a : { type: 'module' },
                filename: 'remoteEntry.js',
                exposes: options.exposes,
                remotes: mappedRemotes,
                shared: Object.assign({}, sharedDependencies),
            }), sharedLibraries.getReplacementPlugin());
            return config;
        };
    });
}
exports.withModuleFederation = withModuleFederation;
//# sourceMappingURL=with-module-federation.js.map

 

- remotes.d.ts 는 ts에서 remote app의 모듈을 import 하기위한 definition 파일이다. 

// Declare your remote Modules here
// Example declare module 'about/Module';
declare module 'mf-store/Module';

- project.json 에 implicitDependencies 설정하면 mf-host와 mf-store는 하나의 애플리케이션으로 간주된다. (참조)

{
  "name": "mf-host",
  "$schema": "../../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "apps/mf/host/src",
  "projectType": "application",
  "implicitDependencies": ["mf-store"],

 

 

<참조>

https://fe-developers.kakaoent.com/2022/220623-webpack-module-federation/

 

Webpack Module Federation 도입 전에 알아야 할 것들 | 카카오엔터테인먼트 FE 기술블로그

유동식(rich) 실용성 있는 프로그램을 추구합니다. 클래식 기타와 Nutrition 공부를 취미로 삼고 있습니다.

fe-developers.kakaoent.com

https://stackblitz.com/github/webpack/webpack.js.org/tree/main/examples/module-federation?file=README.md 

 

Webpack.js Module Federation Example - StackBlitz

Run official live example code for Webpack.js Module Federation, created by Webpack on StackBlitz

stackblitz.com

NX module federation: https://nx.dev/recipes/module-federation

 

Module Federation and Micro Frontends

How to work with and setup Module Federation with Nx.

nx.dev

연재글: https://www.angulararchitects.io/en/aktuelles/the-microfrontend-revolution-module-federation-in-webpack-5/

 

The Microfrontend Revolution: Module Federation in Webpack 5 - ANGULARarchitects

Module Federation allows loading separately compiled program parts. This makes it an official solution for implementing microfrontends.

www.angulararchitects.io

 

posted by 윤영식
2017. 11. 15. 16:31 Meteor/Angular + Meteor

Angular CLI와 Meteor를 분리하고, Meteor를 단순히 API 서버로 사용하면서 Angular CLI는 Webpack Dev Server를 통해 4200 port로 브라우져에서 접속하면 Meteor Client는  3000 port를 통해 Meteor Server와 DDP 통신을 한다. (이전 블로그 참조) 이번에는 외부 라이브러리로 Clarity CSS Framework을 추가해 본다. 





Clarity CSS Framework


Twitter Bootstrap 처럼 CSS Framework이지만, clarity-ui, clarity-icon, clarity-angular 3개의 파트로 구분된다. clarity-ui는 css만 존재하고, clarity-icon은 이름처럼 아이콘에 관련된 부분이며, clarity-angular는 Angular 컴포넌트를 제공한다. 


  - Clarity 홈페이지

  - Clarity 소스

  - Clarity Seed 소스


// 아이콘 관련

$ npm install clarity-icons --save

$ npm install @webcomponents/custom-elements@1.0.0 --save


// UI CSS

$ npm install clarity-ui --save


// Angular 컴포넌트 

$ npm install clarity-angular --save


Clarity-Angular는 ngModule에서 import를 하면되고 나머지는 아래 장의 설명처럼 ng eject 이전 상태라면 angular-cli.json 에 설정하고, ng eject가 되었다면 webpack.config.js에 설정한다. 

// 메인 모듈

import { ClarityModule } from 'clarity-angular';


@NgModule({

imports: [

BrowserModule,

ClarityModule.forRoot()

],

...

})

export class AppModule {} 





@angular/cli 설정을 webpack.config.js로 옮기기


Clarity Seed를 보면 @angular/cli 기반으로 생성되어 있고, angluar-cli.json 안에 설정이 다음과 같이 되어있다. 진행중인 프로젝트에 Clarity icons, ui에 대한 외부 경로 설정을 한다. 

"styles": [
"styles.css",
"../node_modules/clarity-icons/clarity-icons.min.css",
"../node_modules/clarity-ui/clarity-ui.min.css"
],
"scripts": [
"../node_modules/@webcomponents/custom-elements/custom-elements.min.js",
"../node_modules/clarity-icons/clarity-icons.min.js"
],


해당 설정을 이전강좌의 Angular CLI + Meteor 프로젝트의 angular-cli.json을 넣고 ng eject를 하게 되면 다음과 같은 내용이 webpack.config.js에 자동 설정된다. 

  - Module안에 "exclude"와 "include"의 "src/styles.css"이 있는 모든 곳에 설정.

  - Plugin안에 new ConcatPlugin내용 추가

"module": {

{

"exclude": [

path.join(process.cwd(), "src/styles.css"),

path.join(process.cwd(), "node_modules/clarity-icons/clarity-icons.min.css"),

path.join(process.cwd(), "node_modules/clarity-ui/clarity-ui.min.css")

]

...

},

{

"include": [

path.join(process.cwd(), "src/styles.css"),

path.join(process.cwd(), "node_modules/clarity-icons/clarity-icons.min.css"),

path.join(process.cwd(), "node_modules/clarity-ui/clarity-ui.min.css")

]

...

}

...

},

"plugins": [

new ConcatPlugin({

"uglify": false,

"sourceMap": true,

"name": "scripts",

"fileName": "[name].bundle.js",

"fileToConcat": [

"node_modules/@webcomponents/custsom-elements/custom-elements.min.js",

"node_modules/clarity-icons/clarity-icons.min.js"

]

})

]





Clarity 테스트


Clarity CSS Framework은 업무적으로 요하는 컴포넌트 요소를 잘 정리해 놓았다. Application Layout에 대한 내역을 app.component.html에 추가한다. 

<div class="main-container">
<div class="alert alert-app-level">
Alert
</div>
<header class="header header-6">
Angular Meteor
</header>
<nav class="subnav">
Navigation
</nav>
<div class="content-container">
<div class="content-area">
Main
</div>
<nav class="sidenav">
Side Menu
</nav>
</div>
</div>


결과 화면





Clarity Icon 선택적으로 적용하기


plugins부분에 clarity-icons/clarity-icons.min.js 은 전체 아이콘을 포함하고 있다. 전체을 적용하지 않고, Core와 일부분만 적용하고 싶을 경우 다음과 같이 한다. 


// webpack.config.js에서 clarity-icons.min.js 제거

"plugins": [

new ConcatPlugin({

"uglify": false,

"sourceMap": true,

"name": "scripts",

"fileName": "[name].bundle.js",

"fileToConcat": [

"node_modules/@webcomponents/custsom-elements/custom-elements.min.js"

]

})

]


src/main.ts안에 import

import 'clarity-icons';
import 'clarity-icons/shapes/essential-shapes';
import 'clarity-icons/shapes/technology-shapes';


app.component.html안에 clr-icon 적용 테스트. 자세한 사항은 문서를 참조한다. Clarity-icons은  svg icon으로 사용자 정의 아이콘을 추가 할 수도 있다.

<div class="content-area">
<p><clr-icon shape="user" size="24"></clr-icon> lives in the Core Shapes set.</p>
<p><clr-icon shape="pencil" size="24"></clr-icon> lives in the Essential Shapes set.</p>
<p><clr-icon shape="tablet" size="24"></clr-icon> lives in the Technology Shapes set.</p>
</div>


posted by 윤영식
2017. 11. 8. 17:20 Meteor/Angular + Meteor

Ionic CLI 와 Meteor CLI 로 프로젝트 구성하기는 모바일 프로젝트를 진행할 때 사용하면 되고, 이번에는 Angular CLI 와 Meteor CLI를 통해 프로젝트 구성을 어떻게 하는지 살펴본다. 




Webpack 기반 프로젝트 초기화


Angular CLI를 통해 프로젝트를 생성한다. @angular/cli v1.5.0이 설치되고, webpack은 v3.8.1 이고, 내부적으로 @angular-devkit, @ngtools/webpack, @schematics등이 사용된다.

$ npm install -g @angular/cli


@angular/CLI 설치후 프로젝트를 생성한다. ng <command>의 상세 내용은 위키를 참조한다.

$ ng new <projectName>


Webpack 환경파일을 수정해야 하므로, eject 명령을 수행하고, 결과로 출력된 가이드에 따라 "npm install" 명령을 수행한다. eject 명령에 대한 다양한 options은 위키를 참조한다. eject시에 옵션을 주면 옵션이 적용된 webpack.config.js가 생성된다.

$ ng eject --aot --watch


====================================================

Ejection was successful.


To run your builds, you now need to do the following commands:

   - "npm run build" to build.

   - "npm test" to run unit tests.

   - "npm start" to serve the app using webpack-dev-server.

   - "npm run e2e" to run protractor.


Running the equivalent CLI commands will result in an error.

====================================================

Some packages were added. Please run "npm install".


$ npm install

$ npm run build


eject를 수행한 경우에는 "ng serve" 명령으로 테스트 서버를 뛰울 수 없다. package.json에 적용된 스크립트인 "npm start"를 수행하고 4200 port로 브라우져에서 

$ npm start


 10% building modules 3/3 modules 0 activeProject is running at http://localhost:4200/

webpack output is served from /

....


Webpack 환경 내역은 크게 entry, output, module (for loader), plugins 로 구성된다. (참조)

  - entry: 파일 위치

  - output: 결과 위치

  - module: css, .ts, .html 파일 관리 및 변환기 -> 자바스크립트 모듈로 만들기 위한 것. postfix가 "-loader" 이다. 로더는 파일단위 처리

  - plugins: 압축, 핫로딩, 복사, 옮기기등. 플러그인은 번들된 결과물을 처리





Angular CLI 환경에 Meteor 설정



이전 포스트처럼 루트에 api 폴더를 만들고 이를 Meteor의 백앤드로 사용토록 설정한다.

// webpack.config.js


const webpack = require('webpack');

...

resolve: {

  alias: {

    'api': path.resovle(__dirname, 'api/server'),

    ...

  }

}


externals: [ resolveExternals ],


plugins: [ ..., new webpack.ProvidePlugin({ __extends: 'typescript-extends' }) ],


node: { ..., __dirname: true }


// 맨 마지막에 넣음 

function resolveExternals(context, request, callback) {

  return resolveMeteor(request, callback) ||

    callback();

}

 

function resolveMeteor(request, callback) {

  var match = request.match(/^meteor\/(.+)$/);

  var pack = match && match[1];

 

  if (pack) {

    callback(null, 'Package["' + pack + '"]');

    return true;

  }

}


루트에 있는 tsconfig.json에 Meteor 백앤드 관련 내용을 추가한다.

"compilerOptions: {

"baseUrl": ".",

"module": "commonjs",

...

"skipLibCheck": true,

"stripInternal": true,

"noImplicitAny": false,

"types": [ "@types/meteor" ]

},

"include": [ ..., "api/**/*.ts" ],

"exclude": [ ..., "api/node_modules", "api" ]


src/tsconfig.app.json과 tsconfig.spec.json안에 api에 대한 exclude도 설정해야 한다.

// src/tsconfig.app.json

"exclude": [

   ...,

   "../api/node_modules"

]


// src/tsconfig.spec.json

"exclude": [ "../api/node_modules" ]


관련 패키지를 설치한다.

$ npm install --save-dev typescript-extends

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

$ npm install --save-dev tmp




Meteor Server API 생성 및 설정


Meteor CLI를 설치하고 api 명으로 Meteor 프로젝트를 생성한다.

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

$ meteor create api


api 폴더 밑의 필요없는 폴더를 삭제하고 루트에 있는 것으로 대체한다.

$ cd api

// 삭제

api$ rm -rf node_modules client package.json package-lock.json


// 심볼릭 링크

api$ ln -s ../package.json

api$ ln -s ../package-lock.json

api$ ln -s ../node_modules

api$ ln -s ../src/declarations.d.ts


Meteor 백앤드를 Typescript 기반으로 개발하기 위한 패키지를 설치한다.

api$ meteor add barbatus:typescript


api$ cd ..

$ npm install --save babel-runtime

$ npm install --save meteor-node-stubs

$ npm install --save meteor-rxjs


Typescript의 tsconfig.json 파일을 api 폴더안에 생성하고 다음 내역을 붙여넣는다.

{

  "compilerOptions": {

    "allowSyntheticDefaultImports": true,

    "declaration": false,

    "emitDecoratorMetadata": true,

    "experimentalDecorators": true,

    "lib": [

      "dom",

      "es2017"

    ],

    "module": "commonjs",

    "moduleResolution": "node",

    "sourceMap": true,

    "target": "es6",

    "skipLibCheck": true,

    "stripInternal": true,

    "noImplicitAny": false,

    "types": [

      "@types/meteor"

    ]

  },

  "exclude": [

    "node_modules"

  ],

  "compileOnSave": false,

  "atom": {

    "rewriteTsconfig": false

  }

}


Meteor의 server/main.js를 main.ts로 바꾼다. 위에서 설치한 meteor-rxjs는 클라이언트단의 Meteor를 RxJS Observable기반으로 사용할 수 있도록 한다.

// 예) Meteor 클라이언트단 Collection 구성

import { MongoObservable } from 'meteor-rxjs';


export const Chats = new MongoObservable.Collection('chats');




Meteor Client 준비


Meteor Server <-> Client 연동을 위해 Client를 Bundling한다.

$ sudo npm install -g meteor-client-bundler


번들링시에 Meteor Server 기본 주소는 localhost:3000 으로 설정된다. Meteor Client는 DDP를 이용하기 때문에 번들할 때 --config 옵션 또는 --url 옵션으로 Meteor Server 위치를 지정한다.  -s 옵션을 주면 Client<->Server 같은 버전의 패키지를 사용하고 -s 옵션을 주지않으면 config 설정내용을 참조한다. (참조)

$ meteor-client bundle --destination meteor.bundle.js --config bundler.config.json


// config file

{ "release": "1.6", "runtime": { "DDP_DEFAULT_CONNECTION_URL": "http://1.0.0.127:8100" }, "import": [ "accounts-base", "mys:accounts-phone", "jalik:ufs@0.7.1_1", "jalik:ufs-gridfs@0.1.4" ] }


package.json에 번들링 명령을 등록하고 수행한다. 

"scripts": {

   ...,

   "meteor-client:bundle": "meteor-client bundle -s api"

}


//번들링 - 최초 한번만 수행한다.

$ npm run meteor-client:bundle


Angular 클라이언트에서 Meteor Client를 사용하기 위해 src/main.ts에서 "meteor-client"를 import한다.

import "meteor-client";




Collection 생성하고 Meteor 기능 사용 테스트


api/server 에 model을 하나 만든다. Angular와 Meteor가 같이 사용하는 모델 타입이다.

// api/server/models.ts

export enum MessageType {

  TEXT = <any>'text'

}


export interface Chat {

  _id?: string;

  title?: string;

  picture?: string;

  lastMessage?: Message;

  memberIds?: string[];

}


export interface Message {

  _id?: string;

  chatId?: string;

  senderId?: string;

  content?: string;

  createdAt?: Date;

  type?: MessageType;

  ownership?: string;

}


api/server/collections 폴더를 생성하고 Chat 컬렉션을 생성한다. meteor-rxjs 는 RxJS로 Mongo Client를 wrapping해 놓은 것으로 Rx방식으로 Mongo Client를 사용할 수 있게 한다.

// api/server/collections/chats.ts

import { MongoObservable } from 'meteor-rxjs';

import { Chat } from '../models';


export const Chats = new MongoObservable.Collection<Chat>('chats'); 


// api/server/collections/messages.ts

import { MongoObservable } from 'meteor-rxjs';

import { Message } from '../models';


export const Messages = new MongoObservable.Collection<Message>('messages');


api/server/main.ts안에 샘플 데이터를 넣는다.

$ npm install --save moment


// api/server/main.ts

import { Meteor } from 'meteor/meteor';

import { Chats } from './collections/chats';

import { Messages } from './collections/messages';

import * as moment from 'moment';

import { MessageType } from './models';


Meteor.startup(() => {

  // code to run on server at startup

  if (Chats.find({}).cursor.count() === 0) {

    let chatId;


    chatId = Chats.collection.insert({

      title: 'Ethan Gonzalez',

      picture: 'https://randomuser.me/api/portraits/thumb/men/1.jpg'

    });


    Messages.collection.insert({

      chatId: chatId,

      content: 'You on your way?',

      createdAt: moment().subtract(1, 'hours').toDate(),

      type: MessageType.TEXT

    });


    chatId = Chats.collection.insert({

      title: 'Bryan Wallace',

      picture: 'https://randomuser.me/api/portraits/thumb/lego/1.jpg'

    });


    Messages.collection.insert({

      chatId: chatId,

      content: 'Hey, it\'s me',

      createdAt: moment().subtract(2, 'hours').toDate(),

      type: MessageType.TEXT

    });

  }

});


다음으로 Angular에 Chat 컬렉션을 사용한다.

// src/app/app.component.ts

import { Component, OnInit } from '@angular/core';

import { Chats } from '../../api/server/collections/chats';

import { Chat } from '../../api/server/models';


@Component({

  selector: 'app-root',

  templateUrl: './app.component.html',

  styleUrls: ['./app.component.css']

})

export class AppComponent implements OnInit {

  title = 'app';

  chats: Chat[];

  ngOnInit() {

    Chats.find({}).subscribe((chats: Chat[]) => this.chats = chats );

  }

}


// src/app/app.component.html 에 추가

<div> {{ chats | json }} </div>




Angular & Meteor 기동


Meteor 기동

$ cd api

api$ meteor


Angular 기동

$ npm start


Webpack dev server는 4200이고meteor client는 server에 websocket 3000 port로 접속을 한다. Meteor 의 mongo로 접속해서 chats collection을 확인해 본다.

$ meteor mongo

MongoDB shell version: 3.2.15

connecting to: 127.0.0.1:3001/meteor

Welcome to the MongoDB shell.

For interactive help, type "help".

For more comprehensive documentation, see

http://docs.mongodb.org/

Questions? Try the support group

http://groups.google.com/group/mongodb-user

meteor:PRIMARY> show collections

chats

messages



하단에 Chat 내역이 json 형식으로 출력된다.




<참조>


- Webpack v3 환경설정 요약

- Meteor Client bundler의 작도방식 by Uri

- Meteor Client Bundler Github


posted by 윤영식
2017. 9. 4. 15:51 카테고리 없음

Angular 최신 버전이 v5 beta-6를 향해 가고 있다. 9월에는 v5 release가 될 것으로 보인다. Angular에서 툴 기능으로 @angular/cli를 제공하고 있는데 오늘은 이것의 내부구조에 대해 연구해 본다. 



@angular/cli 설치 및 프로젝트 생성

설치는 npm 또는 yarn을 이용한다.

$> npm i -g @angular/cli


or 


$> yarn add global @angular/cli


Angular 기반 프로젝트를 생성해 보자.

$> ng new jamong


실행해 보자

$> ng serve




폴더 구조

angular/cli는 Automation Tool 과 Module loader로 webpack을 사용하고 있다. 웹팩의 환경파일을 루트 폴더에 생성한다. 
$> ng eject
=====================================================
Ejection was successful.

To run your builds, you now need to do the following commands:
   - "npm run build" to build.
   - "npm test" to run unit tests.
   - "npm start" to serve the app using webpack-dev-server.
   - "npm run e2e" to run protractor.

Running the equivalent CLI commands will result in an error.

=====================================================
Some packages were added. Please run "npm install".


설명에 따라 추가 명령을 수행한다. 

$> npm run build && npm start


// package.json의 build 와 start 스크립트 설정내역

  "scripts": {

    "ng": "ng",

    "start": "webpack-dev-server --port=4200",

    "build": "webpack",

    "test": "karma start ./karma.conf.js",

    "lint": "ng lint",

    "e2e": "protractor ./protractor.conf.js",

    "pree2e": "webdriver-manager update --standalone false --gecko false --quiet"

  },



webpack 오류가 난다면 webpack이  global설치가 않되어 있기때문이다. webpack을 설치한다. 

$> yarn add webpack --dev

또는

$> yarn add global webpack 

또는 

$> npm install -g webpack





Webpack 환경파일

webpack의 기본은 초기 참조할 entry파일과 결과 번들파일을 지정하는 것이다. 

$> webpack <entry file path> <output bundle file path>


예) webpack ./entry.js bunlde.js


간단한 번들링은 CLI를 사용해도 무방하지만 복잡한 옵션이 적용되어야 한다면 환경파일을 사용한다. 예) webpack.config.js 아래 예에서 ouput의 filename에 [name]은 entry의 'app' 키값이다. 

module.exports = {

  context: __dirname + '/app',

  entry: {

    app: './app.js'

  },

  output: {

    path: __dirname + '/dist',

    filename: '[name].bundle.js'

  }

}


환경설정 자세한 옵션은 공식문서를 참조한다. 여기서 주의할 것은 entry는 String, Object, Array로 설정가능하다. 

- String: 하나만 설정

- Array: output 번들링 파일에 순서적으로 합쳐진다.

- Object: SPA처럼 index.html에 적용되는 것이 아니라, index1.html 또는 index2.html 등 output이 각각의 html에 포함될 때 사용한다. 





Webpack Loader

Webpack에는 자바스크립트가 아닌 확장형식의 파일을 자바스크립트에서 동작할 수 있도록 해주는 로더(Loader)가 있다. 즉, 모든(CSS, Images, HTML...)을 모듈로 취급하게 해주는 핵심역할을 로더가 수행한다. 말 그대로 다양한 파일 확장자의 Module Loader의 줄임말이라 보면된다. (전체 목록 참조) 만일 설정중에 module, rules를 사용하지않고 loaders를 쓰거나, options대신 query를 쓰면 webpack 1에 대한 설명이다.


- 스타일: style, css

- 변환: typescript, coffeescript, ES2015


환경파일안에 module 밑으로 설정한다. 


module.exports = {

  entry: ...

  output: ...

  module: {

    rules: [

      {

        test: /\.css$/,

        use: [ 'style-loader', 'css-loader']

      }

    ]

    ...

  }


}


특히 Angular 개발시에는 Typescript를 사용하므로 sourcemap을 남기려면 'source-map-loader'를 module 프로퍼티에 설정한다. (sourcemap에 대해 webpack3에서는 plugin으로 설정도 가능하다.) enforce 설정값을 "pre"로 하면 자바스크립트 변환전에 sourcemap 을 만든다.

{

        "enforce": "pre",

        "test": /\.js$/,

        "loader": "source-map-loader",

        "exclude": [

          /(\\|\/)node_modules(\\|\/)/

        ]

}


개발시에는 Node.js기반의 webpack-dev-server를 통해 개발 페이지를 테스트할 수 있다. 옵션은 --inline (전체 페이지 리로딩) --hot (변경 컴포넌트만 리로딩)한다. 

// 전체 및 부분 리로딩 옵션 설정

$> webpack-dev-server --inline --hot




Webpack Plugins

플러그인은 Output 번들의 Chunk 또는 Compilation 레벨 파일에 대한 추가 작업을 수행한다. 예로 ulgifyJSPlugin은 번들 파일 사이즈를 줄이고, 코드를 못 알아보게 만들어 준다. 또는 extract-text-webpack-plugin의 경우는 css-loader, style-loader의 결과를 하나의 별도 외부 파일로 만들어 준다. (플러그인 목록 참조)


@angular/cli에서  ng eject로 나온 webpack.config.js안의 plugins설정 내역

"plugins": [

    new NoEmitOnErrorsPlugin(),

    new GlobCopyWebpackPlugin({

      "patterns": [

        "assets",

        "favicon.ico"

      ],

      "globOptions": {

        "cwd": path.join(process.cwd(), "src"),

        "dot": true,

        "ignore": "**/.gitkeep"

      }

    }),

    new ProgressPlugin(),

    new CircularDependencyPlugin({

      "exclude": /(\\|\/)node_modules(\\|\/)/,

      "failOnError": false

    }),

    new NamedLazyChunksWebpackPlugin(),

    new HtmlWebpackPlugin({

      "template": "./src/index.html",

      "filename": "./index.html",

      "hash": false,

      "inject": true,

      "compile": true,

      "favicon": false,

      "minify": false,

      "cache": true,

      "showErrors": true,

      "chunks": "all",

      "excludeChunks": [],

      "title": "Webpack App",

      "xhtml": true,

      "chunksSortMode": function sort(left, right) {

        let leftIndex = entryPoints.indexOf(left.names[0]);

        let rightindex = entryPoints.indexOf(right.names[0]);

        if (leftIndex > rightindex) {

          return 1;

        } else if (leftIndex < rightindex) {

          return -1;

        } else {

          return 0;

        }

      }

    }),

    new BaseHrefWebpackPlugin({}),

    new CommonsChunkPlugin({

      "name": [

        "inline"

      ],

      "minChunks": null

    }),

    new CommonsChunkPlugin({

      "name": [

        "vendor"

      ],

      "minChunks": (module) => {

        return module.resource &&

          (module.resource.startsWith(nodeModules) ||

            module.resource.startsWith(genDirNodeModules) ||

            module.resource.startsWith(realNodeModules));

      },

      "chunks": [

        "main"

      ]

    }),

    new SourceMapDevToolPlugin({

      "filename": "[file].map[query]",

      "moduleFilenameTemplate": "[resource-path]",

      "fallbackModuleFilenameTemplate": "[resource-path]?[hash]",

      "sourceRoot": "webpack:///"

    }),

    new CommonsChunkPlugin({

      "name": [

        "main"

      ],

      "minChunks": 2,

      "async": "common"

    }),

    new NamedModulesPlugin({}),

    new AotPlugin({

      "mainPath": "main.ts",

      "replaceExport": false,

      "hostReplacementPaths": {

        "environments/environment.ts": "environments/environment.ts"

      },

      "exclude": [],

      "tsConfigPath": "src/tsconfig.app.json",

      "skipCodeGeneration": true

    })

]


JHipster로 자동생된 webpack.config.dev.js의 plugins 설정 내역

plugins: [

        new BrowserSyncPlugin({

            host: 'localhost',

            port: 9000,

            proxy: {

                target: 'http://localhost:9060',

                ws: true

            }

        }, {

            reload: false

        }),

        new webpack.NoEmitOnErrorsPlugin(),

        new webpack.NamedModulesPlugin(),

        new writeFilePlugin(),

        new webpack.WatchIgnorePlugin([

            utils.root('src/test'),

        ]),

        new WebpackNotifierPlugin({

            title: 'JHipster',

            contentImage: path.join(__dirname, 'logo-jhipster.png')

        })

 ]




Production vs Development

개발과 운영 시점에 webpack 환경을 달리 적용하기 위해 보통  webpack.config.dev.js 와 webpack.config.prod.js를  따로 만들고 package.json의 script에 적용해 사용한다. package.json에 적용하고 싶지 않다며 gulp를 사용해도 된다. 


@angular/cli의 script 내역

 "scripts": {

    "ng": "ng",

    "start": "webpack-dev-server --port=4200",

    "build": "webpack",

    "test": "karma start ./karma.conf.js",

    "lint": "ng lint",

    "e2e": "protractor ./protractor.conf.js",

    "pree2e": "webdriver-manager update --standalone false --gecko false --quiet"

}


JHipster script 내역

"scripts": {

    "lint": "tslint --type-check --project './tsconfig.json' -e 'node_modules/**'",

    "lint:fix": "yarn run lint -- --fix",

    "ngc": "ngc -p tsconfig-aot.json",

    "cleanup": "rimraf target/{aot,www}",

    "clean-www": "rimraf target//www/app/{src,target/}",

    "start": "yarn run webpack:dev",

    "serve": "yarn run start",

    "build": "yarn run webpack:prod",

    "test": "karma start src/test/javascript/karma.conf.js",

    "test:watch": "yarn test -- --watch",

    "webpack:dev": "yarn run webpack-dev-server -- --config webpack/webpack.dev.js --progress --inline --hot --profile --port=9060",

    "webpack:build:main": "yarn run webpack -- --config webpack/webpack.dev.js --progress --profile",

    "webpack:build": "yarn run cleanup && yarn run webpack:build:main",

    "webpack:prod:main": "yarn run webpack -- --config webpack/webpack.prod.js --progress --profile",

    "webpack:prod": "yarn run cleanup && yarn run webpack:prod:main && yarn run clean-www",

    "webpack:test": "yarn run test",

    "webpack-dev-server": "node --max_old_space_size=4096 node_modules/webpack-dev-server/bin/webpack-dev-server.js",

    "webpack": "node --max_old_space_size=4096 node_modules/webpack/bin/webpack.js",

    "e2e": "protractor src/test/javascript/protractor.conf.js",

    "postinstall": "webdriver-manager update && node node_modules/phantomjs-prebuilt/install.js"

}


to be continued...



<참고>

- Webpack core concept

- 네이버 webpack 소개

- Webpack의 혼란스러운 사항들

- Webpack3에서 주의할 점


posted by 윤영식
2016. 2. 19. 12:10 Angular/Concept

모듈단위의 파일을 만들어 모듈간에 모듈을 로딩해서 사용하는 방법을 제시하는 것이 Module Loader의 역할이고, 만들어진 모듈을 어떻게 묶어 사용할지 제시하는 것이 Module Bundler의 역할이다. 이번 글에서는 Module Bundler에 대해 알아보자. 



            





파일 번들링의 일반적인 방식 


index.html 에 <script> 태그를 이용해 첫번째는 개발자가 직접 넣는 방법, 두번째는 Grunt 또는 Gulp의 프론트앤드 툴의 도움을 받아 자동으로 넣는 방법이 있다. 하지만 응답성능에 민감한 애플리케이션에는 초기 다량의 모듈 파일 전송이라는 네트워크 성능이슈를 야기한다. 따라서 운영시에는 좀 더 컴팩하게 파일을 묶을 필요가 있다. 

  - 파일을 합치고(Concatenate)

  - 압축(Minify)하는 과정을 거친다. 


통합하고 압축하는 역할에 대한 플러그인이 Grunt/Gulp에 모두 존재한다. 하지만 CommonJS 또는 AMD나 ES2015 네이티브 로더를 사용할 때는 브라우져에 진화적인 코드로 변환해야 한다. 이때 사용하는 것이 Browserify, Webpack, JSPM 와 같은 모듈 번들러이다. 




Browserify


NodeJS에는 다양한 패키지가 존재하고 NPM(Node Package Manager)를 통해 설치한다. 모든 패키지가 CommonJS에 맞춰 모듈 패턴으로 구현을 하고 있다. 만약 이를 브라우져에서 사용하고 싶을 경우에는 Browserify 의 도움을 받으면 된다.

  - CommonJS를 번들링할 때 사용한다. 

  - AST (Abstract syntaxt tree) 에 따라 require한 모듈에서 require하고 해당 모듈이 require하고 있는 모든 하위의 모듈의 의존 파일을 자동으로 묶어준다. 

  - browserify 명령에서 entry 파일만 지정하면 된다. 의존

// app.js

var math  = require('math');

...


// console

browserify app.js -o bundle.js 




Webpack


AMD 패턴의 모듈을 번들링 할 때는 RequreJS에서 제공하는 r.js를 사용할 수 있다. 하지만 애플리케이션에서 NodeJS의 모듈도 사용하고 AMD 패턴 모듈도 사용한다면 어떻게 될까?  

  - CommonJS, AMD, ES2015 방식에 대한 번들링이 가능하다 

  - Bunlde Chunk라는 단위 묶으로 나누어서 번들링이 가능하다 


* Naver D2 Webpack 상세설명 참조




RollupJS


웹팩과 유사한 차세대 모듈 번들러로는 RollupJS가 있다. 특히 모듈안에 있는 내용중 사용하지 않는 것은 제거하는 Tree-Shaking 기술이 존재한다. 

  - maths.js에 square와 cube 펑션을 export 한다

  - cube만 사용한다. 

// maths.js 

export function square(x) {

 return x*x;


export function cube(x) {

  return x*x*x;

}


// test.js 

import { cube } from './maths.js';

console.log( cube(5) ); 


Tree Saking을 한후 square를 제거한다. 나무를 흔들면 필요없는 것은 떨어지고 필요한 것만 남는 것과 같다. (예제)

(function () {

'use strict';


// This function gets included

function cube ( x ) {

// rewrite this as `square( x ) * x`

// and see what happens!

return x * x * x;

}


console.log( cube( 5 ) ); // 125

}());




 

JSPM 


SystemJS는 모듈을 로딩하는 일관된 API를(System.config, System.import) 제공하고 패키지를 받아오고 로딩하는 역할로 JSPM을 사용할 수 있다.

  - SystemJS를 위한 패키지 메니져이다. 

  - ES6 Module Loader로 불린다

  - npm, bower, GitHub으로 부터 로딩한다. 

  - 브라우져에서 NodeJS 패키지가 browserify와 똑같이 작동한다

  - 개발시에는 개별 파일로 관리하다가 프러덕션에서는 번들링을 한다. 

  - 사용자 가이드


Browserify ==> Webpack ===> JSPM 순서로 정리 해보자. 




Angular2에서의 JSPM/SystemJS
  - JSPM을 패키지 메니져로 사용
  - SystemJS를 모듈 로더로 사용
  - TypeScript를 ES2015 자바스크립트 슈퍼셋으로 사용
  - Angular 2 를 통해 웹, 모바일, 네이티브 개발 플랫폼으로 사용


Module Loader는 CommonJS, AMD, UMD, ES2015 스팩 방식이 있고, Module Bundler는 Browserify, Webapck에서 JSPM(+SystemJS) 방식으로 수렴되고 있다. Angular2를 하게되면 Module Loader로서의 SystemJS와 Module Bundler이면서 패키지 메니져 역할을 수행하는 JSPM을 알아둘 필요가 있다.  

  - Angular 2에서 Bundler에 따른 사용 모듈 목록



<참조>

  - 모듈 번들러 설명 

  - 모듈 번들러 비교 slideshare

  - Choose ES6 Modules

  - Grunt에서 파일들을 통합/압축하는 방법

  - Browserify 와 Webpack 비교 

  - ES2015 스팩  

  - ES2015의 import/export 이야기

  


posted by 윤영식
2015. 11. 14. 17:54 Angular/Concept

Angular2 구현 언어는 TypeScript이다. TypeScript를 배우기 위한 준비과정을 알아본다. 








Transpiler


  ES2015(EcmaScript 6) 스펙은 모던 브라우저에서 완벽히 구현되어 있지 않기 때문에 최소한 ES5기준에 맞춰서 사용을 해야 한다. TypeScript는 ES2015를 포함해 정적 타입 시스템과 ES7에서 나오는 데코레이터 @을 지원하는 슈퍼셋이다. 따라서 Babel로 ES2015를 ES5로 트랜스파일(Transpile) 하지 않고 TypeScript만의 트랜스파일러를 사용해야 한다.  


$ sudo npm install -g typescript 


TypeScript 파일의 확장자는 .ts파일로 한다. 그리고 자바스크립 파일로 트랜스파일하기 위해 tsc 명령을  사용한다. 결과물로 동일 명칭의 .js 파일이 생성된다.


$ tsc  <fileName>.ts 






Editor 


  Microsoft에서 mac에서도 사용할 수 있는 전용 Code Editor(Visual Studio Code)를 내놓았다. 설치 후 어느 위치에서든 code . 하면 현재 폴더의 파일 구조를 가지고 에디터가 열린다. 그리고 .ts 파일에 대한 스마트 위저드 헬퍼가 자동으로 뜨기 때문에 MS Visual Studio에서 코딩하는 착각을 불러 일으킨다. 필자는 최근 Sublime 3에서 Atom으로 코딩 툴을 옮겼는데 참 흥미롭게 사용중이다. 큰 파일의 경우 약간의 성능저하를 견딜 수 있다면 사용해 보길 권한다. 




Atom을 사용한다면 atom-typescript 패키지 설치를 통해 Visual Studio Code에서의 .ts 에 대한 스마트한 기능(Auto Complete, Type Info on hover...)을 그대로 사용할 수 있다. 설치후 F6를 클릭하면 tsc <fileName>.ts 명령을 수행하고 결과를 Notify UI 창으로 성공 실패여부를 알려준다. 


 





tsconfig.json


  tsconfig.json 파일은 TypeScript를 위한 프로젝트 단위 환경 파일이다. Exclude 하고 싶은 폴더나 파일, Compile 옵션, File 옵션등을 설정 할 수 있다. TypeScript의 컴파일러인 tsc 명령 수행시 참조한다. tsconfig.json의 전체 스키마를 참조한다.  또는 TypeScript Deep Dive 깃북의 설명을 참조한다.


{

    "exclude": [

        "node_modules",

        "bower_components"

    ],


    "compilerOptions": {

        "target": "es5",

        "sourceMap": true,

        "module": "commonjs",

        "declaration": false,

        "noImplicitAny": false,

        "removeComments": true,

        "noLib": false

    }

}






TypeScript 배우기 


TypeScript를 배우기 위해 ES2015의 문법을 먼저 익히는게 좋다. 

  - ES2015-features 사이트에서 새로운 기능들이 ES5 스펙과 어떤 차이가 있는지 눈여겨 보자. 

  - 동영상 강좌를 본다. 필자는 egghead.io의 동영상을 먼저 참조한다. (Pro는 유료이다)


공식홈페이지는 http://www.typescriptlang.org/ 이고, TypeScript 구현체 자체도 오픈소스로 깃헙에 공개되어 있다. 

  - 공식홈페이지에서 제공하는 TypeScript 스펙 배우기

  - TypeScript Deep Dive 깃북






TSD


TypeScript Definition Manager의 약어이다. 이미 나와있는 프레임워크(angular), 라이브러리(jquery) 같은 것을 사용하면서 애플리케이션을 TypeScript로 작성할 때 이들 구현체의 정의 내역을 미리 알고 코드에서 가이드를 주기위한 파일이다. 확장자는 angular.d.ts 또는 jquery.d.ts처럼 <name>.d.ts 가 붙는다. 예로 $('.awesome').show(); 라고 하면 타입스크립트는 $를 알지 못 한다. 먼저 declare var $:any; 라고 해주어야 사용할 수 있다.  


$ npm install -g tsd


tsd를 설치하고 tsd install <name> 으로 설치하면 수행위치에 typings 폴더 밑으로 파일을 다운로드한다. 


$ tsd init

// tsd.json 파일 

{

  "version": "v4",

  "repo": "borisyankov/DefinitelyTyped",

  "ref": "master",

  "path": "typings",

  "bundle": "typings/tsd.d.ts",

  "installed": {

    "jquery/jquery.d.ts": {

      "commit": "efd40e67ff323f7147651bdbef03c03ead7b1675"

    },

    "angularjs/angular.d.ts": {

      "commit": "efd40e67ff323f7147651bdbef03c03ead7b1675"

    }

  }

}


$ tsd install jquery

$ tsd install angular



이미 만들어져 있는 d.ts 파일을 이곳에서 http://definitelytyped.org/tsd/ 찾아 볼 수 있다. DefinitelyTyped 공식 홈페이지를 참조하자.






WorkFlow 


 Atom 에디터와 기본 툴을 설치했으니, 프로젝트를 위한 워크플로우를 만들어 보자.   


  - 프로젝트 폴더 만들기 

  - NodeJSGit 설치 

  - "npm install gulp -g" 설치 

  - npm 초기화 및 develop dependencies 설정 

$ npm init 


// package.json 첨부

"devDependencies": {

        "gulp": "^3.8.11",

        "gulp-debug": "^2.0.1",

        "gulp-inject": "^1.2.0",

        "gulp-sourcemaps": "^1.5.1",

        "gulp-tslint": "^1.4.4",

        "gulp-typescript": "^2.5.0",

        "gulp-rimraf": "^0.1.1",

        "del": *,

        "superstatic": *,

        "browser-sync": *

    }


// 의존 라이브러리 설치

$ npm install 


  - tsd 설치하고, init 초기화 명령으로 tsd.json 파일과 typings 폴더에 tsd.d.ts 파일을 자동 생성한다.

$ npm install -g tsd 


$ tsd init 



  - angular 의 TypeScript Definition 파일을 설치해 보자. 더 많은 라이브러리는 이곳에서 http://definitelytyped.org/tsd/ 에서 찾을 수 있다.

$ tsd install angular --save

 - angularjs / angular

   -> jquery > jquery


>> running install..

>> written 2 files:

    - angularjs/angular.d.ts

    - jquery/jquery.d.ts



$ tsd install jquery --save

 - jquery / jquery


>> running install..

>> written 1 file:

    - jquery/jquery.d.ts



  - gulpfile.config.js와 gulpfile.js를 https://github.com/DanWahlin/AngularIn20TypeScript 에서 복사해서 만든다. Gulp Task 설명


  - ts 파일 lint를 위해 tslint.json파일 만든다.

{

    "rules": {

        "class-name": true,

        "curly": true,

        "eofline": false,

        "forin": true,

        "indent": [true, 4],

        "label-position": true,

        "label-undefined": true,

        "max-line-length": [true, 140],

        "no-arg": true,

        "no-bitwise": true,

        "no-console": [true,

            "debug",

            "info",

            "time",

            "timeEnd",

            "trace"

        ],

        "no-construct": true,

        "no-debugger": true,

        "no-duplicate-key": true,

        "no-duplicate-variable": true,

        "no-empty": true,

        "no-eval": true,

        "no-imports": true,

        "no-string-literal": false,

        "no-trailing-comma": true,

        "no-trailing-whitespace": true,

        "no-unused-variable": false,

        "no-unreachable": true,

        "no-use-before-declare": true,

        "one-line": [true,

            "check-open-brace",

            "check-catch",

            "check-else",

            "check-whitespace"

        ],

        "quotemark": [true, "single"],

        "radix": true,

        "semicolon": true,

        "triple-equals": [true, "allow-null-check"],

        "variable-name": false,

        "whitespace": [true,

            "check-branch",

            "check-decl",

            "check-operator",

            "check-separator"

        ]

    }

}


  - Atom에서 atom-typescript를 설치해서 사용한다면 tsconfig.json도 만든다. 아래와 같이 설정값을 입력한다. 설정 값이 있어야 atom-typescript 기능을 사용할 수 있다. 보다 자세한 tsconfig.json 작성 문법을 참조한다.

{

    "compilerOptions": {

        "target": "es5",

        "module": "commonjs",

        "declaration": false,

        "noImplicitAny": false,

        "removeComments": true,

        "noLib": false

    },

    "filesGlob": [

        "./**/*.ts",

        "!./node_modules/**/*.ts"

    ],

    "files": [

        "./globals.ts",

        "./linter.ts",

        "./main/atom/atomUtils.ts",

        "./main/atom/autoCompleteProvider.ts",

        "./worker/messages.ts",

        "./worker/parent.ts"

    ]

}


  - gulp 명령을 수행하면 .js 파일과 .js.map 파일이 생성된다. 


  - TypeScript 소개 영상과 소스를 확인해 보자.






Start Kit 


  yeoman의 generator 처럼 TypeScript프로젝트 관련한 스타트 깃을 사용해 보자. 물론 angular2 기반으로 프로젝트를 가정한다. 

  - AngularClass에서 제공하는 angular2-webpack-starter 

  

   


  - AngularClass에서 제공하는 Angular2의 Server Rendering을 접목한 Universal Starter





참조 


  - TypeScript Workflow with Gulp

  - TypeScript in Awesome Angular2

 - Atom TypeScript 패키지 : atom-typescript



posted by 윤영식
2015. 8. 23. 17:48 React

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







준비 운동 


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


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

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

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

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

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

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

// package.json 일부 내역 


"dependencies": {

  "babel": "^5.8.21",

  "react": "^0.13.3",

  "classnames": "^2.1.3",

  "fbjs": "0.1.0-alpha.7"

},

"devDependencies": {

  "babel-core": "^5.8.22",

  "babel-loader": "^5.3.2",

  "css-loader": "^0.15.6",

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

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

  "style-loader": "^0.12.3",

  "url-loader": "^0.5.6",

  "webpack": "^1.11.0"

}

 

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

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

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

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

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

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

// webapck.config.js 내역 


module.exports = {

entry: {

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

},

output: {

path: './dist',

filename: '[name].js',

library: 'RSU',

libraryTarget: 'umd'

},

module: {

loaders: [

{

test: /\.jsx?$/,

exclude: /(node_modules|bower_components)/,

loader: 'babel-loader'

                       },

{

test: /\.css$/,

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

loader: 'style-loader!css-loader'

},

{

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

loader: 'url-loader?limit=8192'

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

                ]

},


externals: [

          {

              'react': {

                   root: 'React',

                   commonjs2: 'react',

                   commonjs: 'react',

                   amd: 'react'

             }

         }

     ]

}


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

{

    "stage" : 0

}




 

개발 시작


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

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

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

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

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

    + import ... from 

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

    + export default RSU 

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

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

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

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

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

    Step, Steps,

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

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

export default RSU;


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

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

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

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

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

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

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

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

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

const defaultProps = {
	className: ''
};

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

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

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

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

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

export default Card;


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






테스트 하기 


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

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

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

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

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

</html>


CardExample.js를 다음과 같다. 

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

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

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

class CardExample extends React.Component {

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

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

}


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

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

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

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

webpack result is served from /

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

Hash: 0d552960154cf0b24cf3

Version: webpack 1.11.0

Time: 2759ms

                 Asset    Size  Chunks             Chunk Names

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

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

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

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

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

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

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

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

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

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

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

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

     + 1 hidden modules

webpack: bundle is now VALID.


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


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




To Be Continue...

'React' 카테고리의 다른 글

[React] 다시 시작하기  (0) 2018.09.14
[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 윤영식
2015. 6. 22. 23:20 Angular/Concept

  ECMAScript는 자바스크립트의 토대를 구성하는 스크립트 언어 명세이다. ECMA는 "Europen Computer Manufacturers Association"의 약어로 국제 표준화 기구이며, ECMAScript는 ECMA-262, ECMA-402 스펙안에서 표준화되었다. ECMAScript 3.0이 1999년 발표된 이래로 ECMAScript 5.0 이 2009년 발표되고 5.1이 2011년 그리고 마침내 ECMAScript 6.0이 2015년 6월 공표되었다. 모던 브라우저가 대부분 ECMAScript 5.0  을 지원하 있고, 현재 브라우저가 5,6,7 버전들에 대해 어느 범위까지 지원하는지 테스트 해볼 수 있다. 


  Angular 2는 TypeScript를 기반으로 개발되고 있고, TypeScript는 ES6를 포함해 별도의 추가 기능을 포함한다. ES6 기반으로 애플리케이션을 개발할 경우 BabelJS를 통해 ES5로 컴파일할 수 있다. 따라서 ES6에 대해 알아보고 필요한 문법을 사용한 후 애플리케이션 배포시에 ES5로 적용로 한다. Babel 사용을 자동화 하기위해 Browserify또는 Webpack을 사용하거나 Grunt/Gulp를 사용할 수도 있다.  




기초 문법 익히기


  - 문법을 보기전에 드미트리 소스니코드의 설명을 잠시 훑어보자

    


  - ECMAScript 6에 새롭게 추가된 기능 알아보기

  - ECMAScript 6를 5와 비교해서 실습하기 

  - Exploring JS ES6 무료 서적





ECMAScript 5 전환 자동화하기


  - Babel을 Browserify와 연동하기, Babel을 Browserify+Gulp 연동하기

  - Babel을 Webpack과 연동하기

  - Babel을 Gulp와 연동하기

  - Babel을 Grunt와 연동하기




참조 

  - ECMAScript 6.0 스펙  (PDF)

  - ECMAScript 버전별 자료

posted by 윤영식
2015. 5. 15. 11:38 React

ReactJS(리액트)는 자바스크립트로 UI 컴포넌트를 만드는 프레임워크이다. 만들어진 컴포넌트를 사용할 수도 있고, 수정할 수 있고, 조합할 수 있으며 자신만의 UI 컴포넌트를 직접 만들 수 있다. 즉 프론트앤드 UI의 화면을 컴포넌트 조합으로 만들고, 화면의 데이터를 앵귤러처럼 자동 업데이트 해주지만 앵귤러와 틀리게 단방향 데이터 바인딩이다. 리액트는 앵귤러(AngularJS)의 지시자(Directive)와 비교되지만 훨씬 간단하고 성능 또한 월등하다. 이제 배우고 익혀 사용할 때가 되었다. 







개념잡기 


  - 쿼라(Quora)에서 이야기하는 장/단점을 살펴보자. 

     장점

       + Virtual DOM 을 JS로 구현해서 UI 컴포넌트의 속도가 엄청 빠르다. 

       + Component의 재사용과 복잡한 UI 컴포넌트 조합이 가능하다.

       + Uni-direction(단방향) 방식하에 데이터가 변경되면 관련된 모든 UI 컴포넌트가 변경된다.

       + commonJS / AMD 패턴과 잘 조합되고, 크롬 익스텐션을 통해 디버깅할 수 있다. 

     단점

       + 초보자들에겐 역시 러닝 커브가 존재한다.

       + 다른 프레임워크가 동작하기 위해선 부가적인 작업들이 필요하다. 

       + 앵귤러처럼 router, model등 Frontend Full Framework을 제공하지 않아서 꼭 다른 프레임워크와의 조합이 필요하다. 


  - 처음에는 무조건 공식 홈페이지의 가이드를 보자. 

      + Quick Start

      + Advanced Guides

      + Reference


  - 프리젠테이션으로 정리해 보자. 재미있게 진행되니 WTF는 날리지 말자.

     

  - egghead.io 에서 리액트에 관련하여 ReactJS, React Native, Flux Architecture를 동강으로 제공한다. 물론 무료가 있다. 
    * 유료강좌를 듣길 추천한다. 앵귤러와 리액트에 대한 좋은 강좌가 많다. 한달 대략 15달러
    + Render MethodProperties, State 사용하기

  - 제이쿼리를 리액트로 바꾸는 과정을 잘 설명해 주고 있다. 글을 읽고 다시 egghead.io 를 보면 이해가 될 것이다. 

    + jQuery to React 가이드

       내용중에 앵귤러를 사용하고 있는 개발자라면 앵귤러의 지사자에서 scope: { createXXX: '&' } 방식으로 자식의 부모의 함수를 호출 할 수 있다

       그리고 자식, 부모간에 scope 객체의 상속을 통해 scope 속성을 접근할 수 있다는 것도 알 수 있다. 

    + Flux 소개 아래 영상을 보면 react가 Virtual DOM을 통해 더 낳은 성능과 단순성을 통한 버그 가능성 제거를 이룰 수 있는지 가늠할 수 있다

       * Flux 사이트

      


  - 이제 다시 설치부터 기초 개념을 문서를 통해 쭉 살펴보자. 

  - ng-conf 말고 react-conf도 있다. 동영상을 통해 향후 방향을 점쳐보자. 
    

  - 리액트 컴포넌트를 찾고 싶을 경우 


  - 리액트 자료 

    + Awesome 자료 목록들

    + 앵귤러와 결합해서 사용하기 : NGREACT 


  - 리액트 컴포넌트 프레임워크 : 트위터 부트스트랩같은 조합

   + reapp.io


  - 새 술은 새 푸대에 넣자. 리액트가 UI 컴포넌트라면 Webpack 또는 Browserify 를 이용해 모듈화 하자
    + 리액트를 위한 웹팩의 starter kit

  - 디버깅은 Chrome Extention 설치하고 시작
  - 필요에 맞는 툴과 환경을 선택한다.

 


Immutable Data에 대한 이해 

불변 데이터에 대해 알아 두어야 한다. 예로 리스트를 생성하고 거기에 새로운 값을 넣으면 리스트를 가르키는 레퍼런스는 같을까 틀릴까? 기존 방식이라면 동일한 레퍼런스이겠지만 Immutable의 세계에 오면 값이 변경 되면 새로운 객체가 된다. 리액트에서는 성능향상을 위해 Immutable.js를 사용토록 권장하고 있다. 이것에 대해 발표한 영상을 보고 영상을 요약한 블로그 글을 보도록 한다. 
  - 영상 설명 블로그 [1], [2], [3]
  - 발표 영상

  - 최근 보고 있는 Clojure는 기본적으로 Immutable Data이다. 클로져 관련 블로그를 읽어보자.



자바스크립트 개념 장착

리액트를 하다 보면 this와 bind(this)를 자주 사용할 때가 있고, ES6로 전환하다보면 좀 더 빈번히 보게 된다. 제대로 이해하고 React 코드를 보자 
  - bind를 해주는 이유 Callback 때문 그렇다면 Callback을 이해하자
  - Callback은 자바스크립트가 Funtional Programming이 가능하게 해주고 Closure이다. 클로저를 이해하자
  - React에서 Object만드는 것이 많이 나오니 자바스크립트의 Object에 대해 알아보자
  - http://javascriptissexy.com/ 에서 다른 글도 참조하시라


'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
posted by 윤영식
prev 1 next