坂本研のゼミ室

研究室のお知らせ管理をホワイトボードからWebに移行したいのでβ版作ってみた

宮崎大学アドベントカレンダー2019 6日目の記事です。

qiita.com

はじめに

研究室内のホワイトボードによる情報伝達が難しくなってきたため、 研究室のメンバーが連絡事項を投稿・削除できるWeb掲示板(β版)を作成しました。

この記事では主に、ページとコンポーネントソースコードを紹介します。 その他のソースコードはこちらにあります。(他機能追加中のため修正するかもしれません🙇‍♂️)

github.com

以下の環境で動作確認しています。

$ node -v
v11.10.1

使用技術

作ったもの

f:id:TakaShinoda:20191203010811p:plain

機能

  • ID, 投稿されたメッセージ内容, 日付(月/日)をFirebase Realtime Databaseに追加
  • Firebase Realtime Databaseから新着10件のデータを取得して表示
  • 指定されたIDのメッセージを削除
  • Firebase Authenticationを用いた認証機能などはまだ実装できていません🙇‍♂️

関連記事は以下にまとめていますのでご覧いただけましたら幸いです。

環境構築

  • React
npm install --save react react-dom
  • Redux
npm install --save redux react-redux redux-thunk
  • Next.js
npm install --save next
  • Firebase
npm install --save Firebase
  • Material-UI
npm install @material-ui/core

実装

{
    "scripts": {
        "dev": "next",
        "build": "next build",
        "start": "next start",
        "export": "next export"
    }
}
  • 上の環境構築のコマンド打ってインストールする

  • next.config.jsを作成して以下のようにしてindex.jsをトップページにする。

module.exports = {
    exportPathMap: function() {
        return {
            '/': {page: '/' }
        }
    } 
}
  • pagesフォルダ(Next.jsのWebページを配置する場所)を作成する。
  • componentsフォルダ(コンポーネント関係をまとめる場所)作成する。

ページを作っていく

ここからはpagesフォルダ内での作業です。

  • index.jsを作成
import Link from 'next/link';  //リンクを表示するために<Link>タグを使用しています
import Layout from '../components/Layout';  //後述するLayoutコンポーネントを読み込んでいます。

export default () => (
    <Layout header='Whiteboard' title='Home'>
        <Link href='./contact_board'>
            <a>連絡板 &gt;&gt;</a>
        </Link>
         {/**今後他のページを増やすときは以下のようにしてください */}
        {/**<Link href='./ファイル名'>
            ここは<a>以外も使えます
        </Link>*/}
    </Layout>
);
  • 連絡事項が表示されるページcontact_board.jsを作成
import Link from 'next/link';
import Layout from '../components/Layout';
import Firelist from '../components/Firelist';
import Button from '@material-ui/core/Button';

export default () => (
    <Layout header='Whiteboard' title='連絡板'>
        <Firelist />
        <br />
        <Link href='/contact_add'>
            <Button variant="contained" color="primary">新規投稿</Button>
        </Link>
        &nbsp;
        <Link href='/contact_del'>
            <Button variant="contained">投稿削除</Button>
        </Link>
        <br />
        <Link href='/'>
            <a>&lt;&lt; 戻る</a>
        </Link>
    </Layout>
)
  • 新規投稿するページcontact_add.jsを作成
import Link from 'next/link';
import Layout from '../components/Layout';
import Fireadd from '../components/Fireadd';

export default () => (
    <Layout header='Whiteboard' title='新規投稿'>
        <Fireadd />
        <Link href='/contact_board'>
            <a>&lt;&lt; 戻る</a>
        </Link>
    </Layout>
);
  • 投稿を削除するページcontact_del.jsを作成
import Link from 'next/link';
import Layout from '../components/Layout';
import Firedelete from '../components/Firedelete';
import Firelist from '../components/Firelist';

export default () => (
    <Layout header='Whiteboard' title='投稿削除'>
        <Firedelete />
        <Firelist />
        <Link href='/contact_board'>
            <a>&lt;&lt; 戻る</a>
        </Link>
    </Layout>
);
  • _app.jsを作成
import App, { Container } from 'next/app';
import React from 'react';
import withReduxStore from '../lib/redux-store';
import { Provider } from 'react-redux';

class _App extends App {
    render() {
        const {
            Component,
            pageProps,
            reduxStore
        } = this.props
        
        return (
            <Container>
                <Provider store={reduxStore}>
                    <Component {...pageProps} />
                </Provider>
            </Container>
        );
    }
}

export default withReduxStore(_App)

コンポーネントを作っていく

ここからはomponentsフォルダ内での作業です。

レイアウト用のコンポーネント
  • 全体のレイアウトを行うLayout.jsxを作成

HeaderとFooterを読み込んで、コンテンツは{this.props.children}としています.

import React, { Component } from 'react';
import Header from './Header';
import Footer from './Footer';
import style from '../static/Style';

class Layout extends Component {
    render() {
        return (
            <div>
                {style}
                <Header header={this.props.header} title={this.props.title} />
                {this.props.children}
                <Footer footer="Copyright (C) 2019 hoge inc." />
            </div>
        );
    }
}

export default Layout;
  • Header.jsxを作成
import React, { Component } from 'react';

class Header extends Component {
    render() {
        return (
            <header>
                <div>{this.props.header}</div>
                <h1>{this.props.title}</h1>
            </header>
        );
    }
}

export default Header;
  • Footer.jsxを作成
import React, { Component } from 'react';

class Footer extends Component {
    render() {
        return (
            <footer>
                <div>{this.props.footer}</div>
            </footer>
        );
    }
}

export default Footer;

レイアウトコンポーネントができたのでスタイルシートを用意します。

一旦components(作業ディレクトリ/components)フォルダから、作業ディレクトリに戻ってstaticフォルダを作成しそこに移動する。((作業ディレクトリ/static)

  • デザインを好きなように記述するStyle.jsを作成する。
export default 
<style>
{`
body {
    margin:10px;
    padding:5px;
    color:#669;
}
//以下略

`}
</style>;
Firebaseにデータを追加・取得・削除するコンポーネント

components(作業ディレクトリ/components)フォルダに戻ります。

取得・追加・削除のソースコードGitHubにあります。

  • 連絡事項を取得するFirelist.jsx
  getFireData() {
    let db = firebase.database();
    let ref = db.ref("sample/");
    let self = this;
    ref
      .orderByKey()
      .limitToLast(10)
      .on("value", (snapshot) => {
        self.setState({
          data: snapshot.val()
        });
      });
  }

  getTableData() {
    let result = [];
    if (this.state.data == null || this.state.data.length == 0) {
      return [
        <tr key="0">
          <th>NO DATA</th>
        </tr>
      ];
    }
    for (let i in this.state.data) {
      result.push(
        <tr key={i}>
          <th>{this.state.data[i].ID}</th>
          <td>{this.state.data[i].message}</td>
          <th>{this.state.data[i].date}</th>
        </tr>
      );
    }
    return result;
  }
  • 新規追加するFireadd.jsx
    doChangeMsg(e) {
        this.setState({
            msg_str: e.target.value
        })
    }

    doAction(e) {
        this.addFireData();
        Router.push('/contact_board');
    }

    getLastID() {
        let db = firebase.database();
        let ref = db.ref('sample/');
        let self = this;
        ref
        .orderByKey()
        .limitToLast(1)
        .on("value", snapshot => {
            let res = snapshot.val();
            for(let i in res) {
                self.setState({
                lastID: i
                });
                return;
            }
        });
    }

    addFireData() {
        if (this.state.lastID == -1) {
            return;
        }
        let id = this.state.lastID * 1 + 1;
        let db = firebase.database();
        let date = new Date().toString().slice(4,10); // 日付を取得
        let ref = db.ref('/sample/' + id);
        ref.set({
            ID: id,
            message: this.state.msg_str,
            date: date
        });
    }
  • 投稿を削除するFiredelete.jsx
    doChange(e) {
        this.setState({
            id_str: e.target.value
        })
    }

    doAction() {
        let result = confirm('本当に削除してもよろしいですか?');
        if(result) {
            this.deleteFireData();
        }
        Router.push('/contact_board');
    }

    deleteFireData() {
        let id = this.state.id_str;
        let db = firebase.database();
        let ref = db.ref('sample/' + id);
        ref.remove();
    }

Next.jsでFirebaseの処理を行う

firebaseのオブジェクトは、最初に一度初期化の処理を行わないといけないのですが、Next.jsでは複数のページにアクセスできるので、ファイルによってはスクリプトが複数回実行されてエラーになります。 そのため、Reduxを組み込んでストアに共通する値を保管するようにしました。(作業ディレクトリ/store.js)

import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import firebase from "firebase";

//Firebaseの初期化(自分のFirebaseプロジェクトからコピーしてくる)
const firebaseConfig = {
  apiKey: "",
  authDomain: "",
  databaseURL: "",
  projectId: "",
  storageBucket: "",
  messagingSenderId: "",
  appId: ""
};

var fireapp;
try {
  firebase.initializeApp(firebaseConfig);
} catch (error) {
  console.log(error.message);
}

export default fireapp;

const initial = {
}

function fireReducer (state = initial, action) {
  switch (action.type) {
      case 'TESTACTION':
        return state;

      default:
          return state;
  }
}
  
export function initStore(state = initial) {
  return createStore(fireReducer, state, applyMiddleware(thunkMiddleware))
}

このstore.jsがどこから読み込まれるかというと、これを作成する必要があります。(作業ディレクトリ/lib/redux-store.js) このredux-store.jsでimportされる時に一度だけスクリプトが読み込まれます。

import React, { Component } from 'react';
import { initStore } from '../store';

const isServer = typeof window === 'undefined'
const _NRS_ = '__NEXT_REDUX_STORE__'

//ストア作成
function getOrCreateStore (initialState) {
  if (isServer) {
    return initStore(initialState)
  }


  if (!window[_NRS_]) {
    window[_NRS_] = initStore(initialState)
  }
  return window[_NRS_]
}

//Appコンポーネントを作成
export default (App) => {
  return class AppWithRedux extends Component {
    static async getInitialProps (appContext) {
      const reduxStore = getOrCreateStore()
      appContext.ctx.reduxStore = reduxStore
      let appProps = {}
      if (typeof App.getInitialProps === 'function') {
        appProps = await App.getInitialProps(appContext)
      }
      return {
        ...appProps,
        initialReduxState: reduxStore.getState()
      }
    }

    constructor (props) {
      super(props)
      this.reduxStore = getOrCreateStore(props.initialReduxState)
    }

    render () {
      return <App {...this.props}
        reduxStore={this.reduxStore} />
    }
  }
}

おわりに

Databaseのルールを設定してなかったので、書き換えられ放題な状況でした。 また、現在ゴミ捨てなどの何かしらの当番を管理できるような機能を追加中です。

Adobe XDでUIデザインやってみた

宮崎大学アドベントカレンダー2019 5日目の記事です

qiita.com

はじめに

  • 様々なWebサイトやアプリのUIを見てなんかすごいと思った事ありませんか?

私はありました。

また、サポーターズCoLobの『エンジニア向け - わかりやすい「デザイン」の話』という勉強会に参加したのもUIデザインに興味を持ったきっかけの一つです。

この記事では、私がやった事や参考にさせていただいる本やWebサイトを紹介します。

Adobe XD

Adobe XDとは

Adobe XDは、共同作業を促進するパワフルで使いやすいプラットフォーム。webサイトやモバイルアプリ、音声インターフェイス、ゲームなどのデザイン制作をチーム全体でスムーズにおこなうことができます。

公式サイトより引用

無料で体験版をインストールできて、チュートリアルである程度の操作を学ぶ事ができます。

www.adobe.com

Cocoda!

Cocoda!というWebサイトでは、UIデザインの基礎練習や毎日のお題に取り組んで力をつける事ができます。また、他のユーザの方のデザインを見る事ができたり、デザイナーを募集してる企業と実際に繋がる事もできるようです。

私も実際にお題に取り組んでみました!

  • フードデリバリーアプリのリスト画面 f:id:TakaShinoda:20191204030239p:plain
  • 音楽アプリの再生画面 f:id:TakaShinoda:20191204030258p:plain
  • 料理アプリの検索画面 f:id:TakaShinoda:20191204030311p:plain
  • メッセージアプリのメッセージ画面 f:id:TakaShinoda:20191204030327p:plain

cocoda-design.com

ノンデザイナーズ・デザインブック

こちらは有名は本です。 デザイナーじゃない人のためにデザインの基本となる4つの原則が書かれています。 この原則を知ると普段目にするデザインの良い・悪いが言語化できるようになります!

www.amazon.co.jp

Themes - iOS - Human Interface Guidelines - Apple Developer

AppleのDeveloperサイトです。文字の大きさは何ポイント以上にするべきか、タップするボタンの要素は何×何ポイントの大きさで作成するべきか、など詳しく書いてありiOSの洗練されたデザインコンセプトを知る事ができます。

developer.apple.com

ColorDrop

無料のカラーパレットツールです。ワンクリックでカラーコードをコピーできて使いやすいです。 配色デザインに迷ったときはおすすめです。

colordrop.io

おわりに

デザインってセンスがいるんだろうな〜と思っていましたが、それ以前に、良いと思うデザインはとてもロジカルに作られている事を学びました。

opencv_traincascadeで分類器作る時に苦しんだ事

はじめに

Opencvのtraincascadeでカスケード分類器を作ろうとやってみた時に、以下のようなエラーがでてきたのでその対応をメモします。

PARAMETERS:

cascadeDirName: ./cascade/bunrui/
vecFileName: ./vec/t_RGB.vec
bgFileName: ./neg/nglist.txt
numPos: 40
numNeg: 15
numStages: 20
precalcValBufSize[Mb] : 1024
precalcIdxBufSize[Mb] : 1024
acceptanceRatioBreakValue : -1
stageType: BOOST
featureType: HOG
sampleWidth: 24
sampleHeight: 24
boostType: GAB
minHitRate: 0.995
maxFalseAlarmRate: 0.5
weightTrimRate: 0.95
maxDepth: 1
maxWeakCount: 100
Number of unique features given windowSize [24,24] : 9

===== TRAINING 0-stage =====
<BEGIN
POS count : consumed   40 : 40
Train dataset for temp stage can not be filled. Branch training terminated.
Cascade classifier can't be trained. Check the used training parameters.

調べると情報が少なく

  • 不正解画像の数が足りない
  • 改行コードが違う
  • WindowSizeの指定が違う

などあり試してみましたがダメでした。

解決策

ファイル構成は以下のようになっています。

.
├── opencv_traincascade
├── pos //正解画像ファイル
│   ├── pos.png
├── vec //正解画像のベクトルファイル
│   ├── pos.vec
├── neg //不正解画像ファイル
│   ├── ng.png //不正解画像を複数枚用意
│   ├── nglist.txt
├── cascade //分類器用のファイル
│   ├── ◯◯◯ //任意のファイル名
├── 略

解決策としては、nglist.txtにある不正解画像のリストにopencv_traincascadeからのパスを通す事で解決しました。

変更前

ng1.png
ng2.png
ng3.png
ng4.png
ng5.png


変更後

./neg/ng1.png
./neg/ng2.png
./neg/ng3.png
./neg/ng4.png
./neg/ng5.png


リストが多すぎて一つ一つパス通すの大変だ、という方はこちらを使ってみてください。
各行の文字列の先頭に./neg/という文字列を追加して出力します。

<?php
$file = fopen("nglist.txt", "r");
 
if($file){
  while ($line = fgets($file)) {
    echo "./neg/".$line;
  }
}

fclose($file);
?>


これでopencv_traincascadeを実行する事で、分類器を作成する事ができました。

create-react-appで作成したReactアプリをテストしてみる

はじめに

宮崎大学 Advent Calendar 2019の記事です。
qiita.com

ソースコードはこちら。
github.com


テストって何?

プログラムの振る舞いを確認し品質を保証する目的で行う。
手動はつらいので、『こういう入力があったらこういう動作』をあらかじめ記述しテストフレームワークに任せる事で自動化する。

Jest

  • Facebookが開発
  • JavaScriptユニットテストツール
  • __tests__フォルダ内のファイルまたは、ファイル名が◯◯.spec.js, ◯◯.test.jsに対してテストを実行する
  • srcフォルダのsetupTests.jsから設定を読み込む
  • create-react-appにはあらかじめJestが同梱されている
  • 日本語ドキュメントもあるので詳しく知りたい人はぜひ

jestjs.io

Enzyme

airbnb.io

環境構築

$ node -v
v11.10.1

新しいReactアプリを作る

npx create-react-app my-app

以下のようなファイル構成になっている
f:id:TakaShinoda:20191105050011p:plain

プロジェクトを実行

npm start

実際にテストしてみる

まずは、App.jsを以下のように書き換える

import React, { Component } from 'react';
import './App.css';

class App extends Component {
  render() {
    return (
      <div>
        <h1>みやだいもうくん</h1>
      </div>
    );
  }
}

export default App;

次にApp.test.jsを書き換える(.test.jsにテストを記述していく)

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

test('innerHTMLに含まれているか確認', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App />, div);
  //<App />内のinnerHTMLに「みやだいもうくん」が含まれているかどうかをテストする
  //”toContain”は含まれているかどうかをチェックする関数
  expect(div.innerHTML).toContain('みやだいもうくん');
  ReactDOM.unmountComponentAtNode(div);
});

テストを実行してみる。

npm test

このようになれば成功。テストが通りました。
f:id:TakaShinoda:20191120232731p:plain

Jestによるテストの書き方メモ

test("最初のテスト", () => {
    expect(1 + 1).toEqual(2);
});
/** test("テストの名前", () => {});
 * 第1引数: テストにつける名前
 * 第2引数: テストに実行して欲しい内容をアロー関数で記述
 * 
 * テスト (test) を宣言
 * テストの名前は「最初のテスト」
 * 実行してほしい内容は(1 + 1) の結果が、(2) と等しくなること (toEqual) を期待 (expect) します
 */

テスト実行のながれ

f:id:TakaShinoda:20191121003115p:plain

コンポーネントの存在を確認

ます、子コンポーネントとしてCount.jsxを作成

import React, { Component } from 'react';

class Count extends Component {
    constructor(props){
        super(props);
        this.state = {
            count: 0
        }
        this.doAdd = this.doAdd.bind(this);
    }

    doAdd() {
        this.setState({
            count: this.state.count +1
        });
    }

    render() {
        return (
            <div>
                <button onClick={this.doAdd}>+1ボタン</button>
                <p>{this.state.count}</p>
            </div>
        );
    }
}

export default Count;

次に、enzymeとenzyme-adapter-react-16をインストール
(16はreactのバージョン)

npm install enzyme enzyme-adapter-react-16

srcフォルダ内にsetupTests.jsを作成し以下のように記述する
(Jestはsrc/setupTests.jsを探して設定を読み込む)

import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

Enzyme.configure({ adapter: new Adapter() });

App.test.jsにテストを追加する

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import Count from './Count';
import { shallow, mount, render } from 'enzyme';

it('innerHTMLに含まれているか確認', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App />, div);
  //<App />内のinnerHTMLに「みやだいもうくん」が含まれているかどうかをテストする
  //”toContain”は含まれているかどうかをチェックする関数
  expect(div.innerHTML).toContain('みやだいもうくん');
  ReactDOM.unmountComponentAtNode(div);
});

test('子コンポーネントが存在するか確認', () => {
  /** Appコンポーネントをshallowレンダリング */
  const wrapper = shallow(<App />);

  /** 各コンポーネントの数を取得し、1であればOK */
  expect(wrapper.find(Count).length).toBe(1);
});

setStateで値が更新されているか確認

名前を入力して表示するName.jsxを作成

import React, { Component } from 'react';

class Name extends Component {
    constructor(props){
        super(props);
        this.state = {
        name: 'hoge'
        }
        this.onChange = this.onChange.bind(this);
    }

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

    render() {
        return (
            <div>
                <input type='text' onChange={this.onChange} placeholder="名前を入力" />
                <p>こんにちは! {this.state.name}さん</p>
            </div>
        );
    }
}

export default Name;


Name.test.jsを作成して、Name.jsxのテストを記述する

import React from 'react';
import Name from './Name';
import { shallow } from 'enzyme';

test('setStateで<Form>のnameが変更されるかを確かめる', () => {
    const wrapper = shallow(<Name />);
    /** <Form />のstate.nameが’’ではない。(初期stateは{name: ‘hoge’}) */
    expect(wrapper.state().name).not.toEqual('')
    /** setStateでnameをみやだいもうくんに更新 */
    wrapper.setState({ name: 'みやだいもうくん'});
    /** state.nameがみやだいもうくんならOK */
    expect(wrapper.state().name).toEqual('みやだいもうくん')
});

コンポーネントで受け取ったpropsがレンダリングされているか確認

App.jsからpropsを受け取る子コンポーネントとしてMessage.jsxを作成
今回はテキストを受け取るだけです。
App.jsに追加する。

import React, { Component } from 'react';
import Count from './Count';
import Name from './Name';
import Message from './Message';

class App extends Component {
  render() {
    return (
      <div>
        <h1>みやだいもうくん</h1>
        <Count />
        <Name />
        <Message message = '宮崎へ' />
      </div>
    );
  }
}

export default App;

Message.jsx

import React, { Component } from 'react';

class Message extends Component {
    render() {
        return (
            <div>
                <p>ようこそ! {this.props.message}</p>
            </div>
        );
    }
}

export default Message;


Message.test.js

import React from 'react';
import Message from "./Message";
import { shallow } from 'enzyme';

test('受け取ったpropsの値を表示できているか確認', () => {
    /*'佐賀へ'という値をtextに渡して、Messageコンポーネントをshallowレンダリング*/
    const wrapper = shallow(<Message message={'佐賀へ'} />);
    /** レンダリングされたテキストが'ようこそ! 佐賀へ'であればOK */
    expect(wrapper.text()).toBe('ようこそ! 佐賀へ');
    /** props.messageの値を'World'に変更 */
    wrapper.setProps({ message: 'World' });
    /** レンダリングされたテキストが'ようこそ! World'であればOK */
    expect(wrapper.text()).toBe('ようこそ! World');
});

モックを使ったテスト

モックを使う事で、乱数を使ったテストなどの実際には実行してほしくないテストの際に代わりとなる値を返すことができます。

アラートのテスト

最後に、アラート表示のテストを行います。
Alert.jsxを作成

import React, { Component } from 'react';

class Alert extends Component {
    constructor(props){
        super(props);
        this.state = ({
            notification: 'himuka'
        });
        this.onClick = this.onClick.bind(this);
    }

    onClick() {
        alert(this.state.notification);
    }

    render() {
        return (
            <div>
                <button onClick={this.onClick}>
                    アラート
                </button>
            </div>
        );
    }
}

export default Alert;

Alert.jsxのテストを記述するAlert.test.jsを作成

import React from 'react';
import Alert from './Alert';
import { shallow } from 'enzyme';

test('アラート表示されているか確認', () => {
  /** モック関数を作成 */
  window.alert = jest.fn();
  const wrapper = shallow(<Alert onClick={window.alert('hogehoge')} />);
  /** クリックイベントをシュミレート */
  wrapper.simulate('click');
  /** モック関数が1回呼び出される */
  expect(window.alert.mock.calls.length).toBe(1)
  /** toHaveBeenCalledWithは特定のFunctionが特定の引数で呼び出されたかを検証 */
  expect(window.alert).toHaveBeenCalledWith('hogehoge');
});




フロントエンド カンファレンス福岡2019から学んだこと

はじめに

11/16(土)に九州産業大学で開催されたフロントエンド カンファレンス福岡2019に参加しました。 この記事では、私が得た学びを紹介します。

2019年テーマ

新しい視点を見つけよう

登壇資料

登壇資料まとめを作ってくださっている方のQiitaです qiita.com

学び

今回得られた学びを全て紹介する事はできませんが、一部個人的にピックアップしたものを紹介させていただきます。

TypeScriptがデファクトスタンダードであると肌で感じることができた

ヤフー株式会社のブースにJavaScript総選挙があり、多くのエンジニア、デザイナーの方々が投票していました。 TypeScriptがデファクトスタンダードであることはいろんな記事でなんとなく知ってはいましたが、実際に現場で働かれている方々の投票結果を見ることができたのは学生の自分にとっては新鮮でした。

f:id:TakaShinoda:20191117170602j:plain


フロントエンドの歴史を知ることで今の技術の嬉しみがわかった

VueやReactからフロントエンドの勉強を始めたので、jQueryやってないためそれらの何が嬉しいかわからない事が多々あるのですが、 過去の問題をどう解決してきたのか、歴史を知る事で知見を深めることができました。

歴史から学ぶ現代のフロントエンド 登壇者 外松 俊尚さん (サイボウズ株式会社)

テストの必要性や書き方・考え方を得ることができた

Testing Trophyの考え方や、Cypressについて知ることができました。 自分もJestとEnzymeでテストがかけるように勉強中だったので、今回一番見たかったセッションでした。

明日からはじめるテストのあるフロントエンド開発 登壇者 向井 咲人さん (サイボウズ株式会社)

フロントエンドでのセキュリティの必要性を知る事ができた

セキュリティはフロント側も無関係ではなく、DOM-based XSSなどに配慮することが必要であること。 オリジン間でデータを保護するための様々な仕組みが用意されている事を知ることができました。 普段趣味でフロントやっている時は、セキュリティについてあまり考えることがなかったので新しい視点を得ることができました。

これからのフロントエンド セキュリティ 登壇者 長谷川 陽介さん (株式会社セキュアスカイ・テクノロジー)

おわりに

今回初めてフロントエンド カンファレンス福岡に参加させていただきました。 自分の勉強不足でなかなか理解できない事もありモチベーションが上がるきっかになりました。 これは登壇者・スポンサー・スタッフの皆様に支えられたものであり深く感謝いたします。 絶対に今回得られた学びを今後に活かしていきます。

スムーズに環境構築できた記事紹介(Docker + Visual Studio Code + LaTeX)

はじめに

企業から読書レポートの課題が出たのをきっかけに、
Cloud LaTeXは卒論時期にとても動作が遅く苦労した思い出があるので
今後の学会や論文執筆に備えて、
DockerとVisual Studio CodeLaTexの環境構築を行いました。

こちらの記事を参考にさせていただき、
本記事では、DockerやVisual Studio Code拡張機能のインストール方法を詳しくまとめます。
最後まで環境構築するためにはこちらをみてください
korosuke613.hatenablog.com

Dockerのインストール

こちらからインストール
Docker Hub

インストールしたらコマンドで試しに動かしてみました
コマンドや使い方はこちらの記事を参考にしました
qiita.com

docker-composeのインストール

こちらで最新版を確認してインストール(2019/10/28はv1.25.0)
Releases · docker/compose · GitHub

curl -L https://github.com/docker/compose/releases/download/1.25.0-rc3/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose

docker-composeコマンドを実行できるように実行権限を与る

chmod +x /usr/local/bin/docker-compose

インストールされたか確認

docker-compose --version

成功

docker-compose version 1.25.0-rc3, build c7e82489

一連のdocker-composeのインストールはこちらの記事を参考にさせていただきました
qiita.com

Visual Studio Code拡張機能をインストール

LaTeX Workshop

LaTeX Workshopは、Visual Studio CodeLaTeX の統合環境を追加するextensionです。
入力支援、複数のコンパイル設定の使い分け、文書のアウトライン表示、
マウスを合わせたときの数式や引用のプレビューやパッケージのドキュメントの表示などの機能があります。

詳しくは(Visual Studio Code/LaTeX - TeX Wiki)
f:id:TakaShinoda:20191029001250p:plain

Remote - Containers

Dockerコンテナ上で動作している開発環境に接続して、コンテナ上でVSCodeを動作させてるかのようになる。
f:id:TakaShinoda:20191029001309p:plain

おわりに

現在自分の環境では、ビルドして生成されたpdfがVisual Studio Code上では表示できません...
解決策調べてます。
Missing PDF after updating VSCode. · Issue #1494 · James-Yu/LaTeX-Workshop · GitHub

npm run buildの実行時にError: Module did not self-registerの対応

はじめに

Next.js, Reactで開発しているプロジェクトをビルドした時に下記のようなエラーが出たのでその対応をメモします。

> Build error occurred
Error:Module did not self-register.
    at Object.Module._extensions..node (internal/modules/cjs/loader.js:977:18)
    at Module.load (internal/modules/cjs/loader.js:790:32)
    at Function.Module._load (internal/modules/cjs/loader.js:703:12)
    at Module.require (internal/modules/cjs/loader.js:830:19)
    at require (internal/modules/cjs/helpers.js:68:18)
    at Object.<anonymous> (/Users/shinoda/Desktop/Whiteboard/node_modules/grpc/src/grpc_extension.js:32:13)
    at Module._compile (internal/modules/cjs/loader.js:936:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:947:10)
    at Module.load (internal/modules/cjs/loader.js:790:32)
    at Function.Module._load (internal/modules/cjs/loader.js:703:12) {
  type: 'Error'
}

対応

下記の記事を参考にさせていただきNode.jsのバージョンを下げることで対応することができました。
utano.jp

具体的にはv12.11.1 -> v11.10.1に切り替えました。
またその際には下記の記事を参考にさせていただきました。
qiita.com

おわりに

これでビルドが成功してFirebaseでデプロイすることができました。
今後は、CircleCIとFirebaseの連携やPWAにしていきたいと思います。