【Nuxt3 + Vite + ngrok】Nuxt3でngrokを使ったフロントエンド開発におけるホットリロード(hmr/)問題の解決方法

Nuxt3 と ngrok を使ったフロントエンド開発で
HMR 問題を解決する方法
Nuxt3 と ngrok を使ったフロントエンド開発で
HMR 問題を解決する方法

Nuxt3 は、ポートフォワーディングをせずに内部の local だけで開発が完結する場合は、何も考えずに WebSocket を使ってホットリロードしてくれます。

下記の左図は、通常のシンプルな開発環境(localhost:3000)における開発の図で、右図が ngrok を介した場合の Nuxt3 の開発です。

ngrok を介した開発では、local の任意のポートをポートフォワーディングしているため、https 付きのアドレスが吐き出されます。

Nuxt3の開発例

しかし、Nuxt3 と ngrok を組み合わせて開発を行う際、ホットリロード(Hot Module Replacement, HMR)がうまく動作しないという問題に遭遇することがあります。

この記事では、そのような問題を解決する方法を紹介します。

設定失敗の例

Nuxt3 と Vite を使用している場合、nuxt.config.js で Vite の設定を行います。

当初、ngrok にポートフォワーディングしている状態で、ホットリロードを行う際に以下のように誤った設定をしていました。

// nuxt.config.js
vite: {
  server: {
    https: true,
    hmr: {
      protocol: 'wss',
      host: 'localhost',
      clientPort: 443,
      path: 'hmr/',
    },
  },
},

この設定ではHMRが正常に動作しない問題が発生します。

path に設定されている hmr/ が、指定されている clientPort では解決できずに、延々とリトライを繰り返してしまう結果になります。

また、path を消去したとしてもエラーが発生します。

HTTPS 通信の場合のプロトコルは、確かに protocol: 'wss' で間違いないのですが、port: 443 で通信しようとしているため、こちらはすでに利用されているポートとして弾かれる結果になります。

解決方法

問題の解決には、nuxt.config.js の Vite の設定をシンプルにすることが有効でした。

// nuxt.config.js
vite: {
  server: {
    hmr: {
      host: 'localhost',
    },
  },
},

詳しい設定を書くと下記のようになります。 ※この書き方をわざわざしなくても上記の設定だけで十分ですが、今回は説明のために敢えて詳細の設定(デフォルト値)を記載しています。

// nuxt.config.js
vite: {
    server: {
      host: '0.0.0.0',
      port: 3000,
      hmr: {
        protocol: 'ws',
        host: 'localhost',
        port: 24678,
      },
    },
  },
  1. protocol: 'wss'clientPort: 443 を削除することで、不必要なセキュリティ設定が排除されました。

  2. ホスト設定 host: 'localhost' を残していますが、これは ngrok がローカルホストにトンネリングするためです。

  3. hmr のポート設定は、デフォルトの24678を設定することです。ここで誤って3000を指定しないようにしてください。

ポイント

ngrok は自動的に WebSocket もトンネリングしてくれるので、HTTPS 通信など考えず、特別な設定は不要です。

以上のように、設定をシンプルにすることで HMR の問題が解決しました。 Nuxt3 と ngrok を使った開発を行う際には、このような設定が有効です。

参考にしたサイト

Vite公式ドキュメント(server.hmr設定)

https://ja.vitejs.dev/config/server-options.html#server-hmr

ngrok公式ドキュメント(WebSocketのトンネリングについて)

https://ngrok.com/docs/using-ngrok-with/websockets/

Vite GitHubリポジトリ(Sever モデルの選択について)

GitHub - sapphi-red/vite-setup-catalogue: This repository contains several example of Vite setups.

【私の作業環境】エンジニアの作業環境を紹介するコーナーを新設しました!!

【私の作業環境】エンジニアの作業環境を紹介するコーナーを新設しました!!
【私の作業環境】エンジニアの作業環境を紹介するコーナーを新設しました!!

こんにちは、N2i Tech Blog 編集長の和田です。

N2i Tech Blog が発起して以来、毎週投稿できていることに驚きを隠せません🥳

それもこれも投稿するための記事を書いてくださっているみんなのおかげですね🙌

Tech Blog の運営に当たって月に一回の定例会を設けているのですが、新たな施策のアイデアを頂いたので今回はその第1回目の記事として投稿させていただきます。

その施策とはズバリ...

新シリーズ【私の作業環境】!!

ということで、N2i の社員のみんなのことをもっと知るべく、「私の作業環境」というシリーズ記事を執筆することになりました!!

簡単に説明すると、エンジニアのみんなの作業環境について紹介していくシリーズです🤓

物理的な環境・お気に入りのツール・ワークフローなんでも構いません。

皆さんの作業環境のこだわりポイントを紹介させていただきます!


【私の作業環境】エンジニア 和田

和田の作業環境
和田の作業環境

ということで、初回は Tech Blog 編集長を務めさせていただいる 和田の作業環境紹介です。
デスク全体はこんな感じです。
筆者自身、自分の作業環境に関してはかなりこだわりを持っているのですが、全部説明すると一冊の本が書けそうなので今回は下記について紹介させていただきます。

  1. 配線の美しさ
  2. 視界を妨げないモニターライト
  3. 疲労軽減トラックボールマウス
  4. 万が一のための蓋付きマグカップ

1. 配線の美しさ

デスク全体を見て分かる通り、デスク上から見える配線ができるかぎり少なくなるように配線にこだわっています。

モニター・モニターライト・マイク・スピーカー・キーボードなどなど、有線で繋げているものが複数ある中、視認できる配線はたった2本だけ😏

デスク上がごちゃごちゃしていると、集中力がかき乱されるのであまりものを置かないように心がけています。 配線はデスク下に格納していたり、モニターアーム内に忍ばせていたりと見せないための努力をしています💪

2. 視界を妨げないモニターライト

視界を妨げないモニターライト
視界を妨げないモニターライト

次にモニターライトです。先程のデスクにあまり物を置きたくないというこだわりにも通ずるのですが、このモニターライトはモニターの上に載せるだけ🙌

ライトの光が直接視界に入り込まないようになっているので眩しくないです。

部屋を暗くして作業する
部屋を暗くして作業する

筆者は夜はこんな感じでモニターライトのみの光とヘッドホンを付けて作業するのが好きです。

3. 疲労軽減トラックボールマウス

疲労軽減トラックボールマウス
疲労軽減トラックボールマウス

マウスをトラックボール式のものに変えてから全く疲れなくなりました。
マウスを動かす必要がないため、画面を見ている最中に右腕だけマウスを探しにいくこともなくなりストレスフリーで作業ができます。

4. 万が一のための蓋付きマグカップ

万が一のための蓋付きマグカップ
万が一のための蓋付きマグカップ

電子機器以外でこだわっているのがこの蓋付きのマグカップです。
卓上の電子機器に万が一飲み物がこぼれたらコワイので蓋付きの物を使っています。
倒れたとしても被害が最小限に抑えられる上に、保温の効果もあり大活躍中なのでオススメです!
アウトドア用の容量多いマグカップだとコーヒーを淹れる回数も少なくて済むのが嬉しいですね。


まとめ

最後まで読んでくださりありがとうございました!

いかがでしたでしょうか?第一回「私の作業環境」シリーズとして、N2i Tech Blog 編集長の和田の作業環境を紹介させていただきました。

キーボードがたくさんあることや、その他のこだわりポイントは、今後の記事で改めて紹介させていただければと思います😅

Vue2 の公式サポート終了に伴い、Options API から Composition API に書き換えてみる。

Vue2 の公式サポート終了に伴い、Options API から Composition API に書き換えてみる
Vue2 の公式サポート終了に伴い、Options API から Composition API に書き換えてみる

Web illustrations by Storyset

こんにちは、開発チームでエンジニアをしている和田です。

最近は、Vue2 で記述されたプロジェクトを Vue3 および Nuxt3 に書き換える毎日です👨‍💻

ということで今回は、Options API で記述された Vue2 のコンポーネントを Composition API で記述する過程について記事にしたいと思います。

Vue2からVue3・Nuxt3 へ移行する背景

Vue2 のプロジェクトを Nuxt3 へと移行するに至った背景としては、主に Vue2 の公式サポートが終了することにあります。

2023年12月に Vue2 の公式サポートが終了することが発表されています。このままプロジェクトを放置すると、Vue2 に依存するライブラリなどもアップデートできず脆弱性を抱え続けることになるため、一念発起してプロジェクト全体の変更をすることになりました🤓

  1. Options API で記述された Vue2 コンポーネントを Composition API に書き換える
  2. Composition API で記述されたプロジェクト全体を Vue3 に引き上げる
  3. Vue3 のプロジェクトをベースに Nuxt3 に移行する

このような流れで作業をしていきます。

この記事の対象読者: Vue2からVue3への乗り換えを検討している人

今回の記事では、Vue2 の Options API で記述されたコンポーネントを Composition API に書き換えるまでを解説します。

Composition API で記述されたコンポーネントを Nuxt3 のプロジェクトで使えるようにする方法については、今後の記事で解説させていただきます🙇‍♂️


Vue2とVue3、Options APIとComposition APIの関係について

前提知識として Vue2, Vue3, Options API, Composition API の関係性について述べておきます。

Options API について

Options API とは、Vue2 の代表的な記法であり下記のようなサンプルコードで書けます。

<script>
export default {
    name: 'Counter',
    data: () => {
        return {
            count: 0,
        };
    },

    methods: {
        countUp() {
            this.count++;
        },
        reset() {
            this.count = 0;
        },
    },
};
</script>

data の中でリアクティブな値 count を管理し、リアクティブに値を操作するための処理を methods 内に記述するという形式です。

この記法の問題として、this を使ってリアクティブに値を操作することになるため、View と処理を分離することができないということが挙げられます。

Composition API について

Composition API とは、Vue3 の代表的な記法です。
Vue2 のプロジェクトにも導入することができ、リアクティブな値の操作と View を切り離せるのが特徴です。

import { ref } from "vue";

export const useCounter = () => {
    const count = ref(0);

    const addCounter = () => {
        count.value++;
    };
    const resetCounter = () => {
        count.value = 0;
    };

    return {
        count,
        addCounter,
        resetCounter,
    };
};
import { useCounter } from '@/ ... /useCounter'

export default defineComponent({
    name: 'Counter'
    setup() {
        const { count, addCounter, resetCounter } = useCounter();
    }
});

composables というディレクトリに、リアクティブな値の処理を記述しておけば、どのコンポーネントからでも簡単に呼び出すことができます。


Vue2 Options API で記述されたコンポーネント

今回は Options API で記述された下記のコンポーネントについて見ていきます。
説明の都合上、<style /> の記述は省略します。

<template>
  <button
    :class="{ [`btn-${buttonType}`]: true }"
    :disabled="hasPermission === false || isDisabled === true"
  >
    <span>{{ buttonText }}</span>
  </button>
</template>

<script>
import { mapGetters } from 'vuex';

export default {
  name: 'ButtonBase',
  props: {
    buttonText: { type: String, required: true },
    buttonType: { type: String, default: 'primary' },
    isDisabled: { type: Boolean, default: false },
    rejectedRoleIds: { type: Array, default: () => [] },
  },
  computed: {
    ...mapGetters('staff', ['staff']),
    hasPermission() {
      return this.judgeHasPermission(
        this.staff,
        this.rejectedRoleIds,
      );
    },
  },
  methods: {
    judgeHasPermission(staffObj, rejectedRoleIds) {
      const staffObject = staffObj;
      if (typeof staffObject !== 'object') return false;

      if (rejectedRoleIds.length === 0) return true;

      return (
        staffObject.role &&
        rejectedRoleIds.includes(staffObject.role.id) === false
      );
    },
  },
};
</script>

それぞれの props の説明は下記のようになります。

props 用途
buttonText ボタン中央に表示するテキスト
buttonType ボタンの class を利用するための文字列
isDisabled ボタンの非活性フラグ
rejectedRoleIds ボタンの押下制限を行うユーザーの role ID の配列

Composition API に書き換える

先程のコードの仕様をほとんど変更せずに Composition API に書き換えると次のようになります。

<template>
  <button
    :class="{ [`btn-${buttonType}`]: true }"
    :disabled="hasPermission === false || isDisabled === true"
  >
    <span>{{ buttonText }}</span>
  </button>
</template>

<script>
import { computed, defineComponent } from '@vue/composition-api';

export default defineComponent({
  name: 'ButtonBase',
  props: {
    buttonText: { type: String, required: true },
    buttonType: { type: String, default: 'primary' },
    isDisabled: { type: Boolean, default: false },
    rejectedRoleIds: { type: Array, default: () => [] },
  },

  setup(props, context) {
    const store = context.root.$store;
    const staff = store.getters['staff/staff'];

    const hasPermission = computed(() =>
      judgeHasPermission(staff, props.rejectedRoleIds),
    );

    const judgeHasPermission = (staffObj, rejectedRoleIds) => {
      const staffObject = staffObj;
      if (typeof staffObject !== 'object') return false;

      if (rejectedRoleIds.length === 0) return true;

      return (
        staffObject.role &&
        rejectedRoleIds.includes(staffObject.role.id) === false
      );
    };

    return {
      hasPermission,
    };
  },
});
</script>

変更前は、this.staff としてユーザーの情報を取得していたところを const staff = store.getters['staff/staff']; として this を使わないので簡単です。
setup の中に関数をシンプルに記述できる点でも便利ですね。

今回の変更について

今回、変更したのはシンプルなコンポーネントだったため、劇的に変わった点はありませんでした😅

defineComponent() を使った記法ではなく、<script setup /> を使った setup 構文というものを使えば、もっとシンプルな記述ができるようなので次回以降の記事で解説していきたいと思います!

【作業効率化】エンジニアが使う開発ツールまとめてみた!おすすめを厳選して紹介

おすすめ開発ツール一覧
おすすめ開発ツール一覧

Business illustrations by Storyset

こんにちは、開発チームでエンジニアをしている和田です。

先日、コードを書いている最中にふと

「エンジニアって、めっちゃたくさん開発ツール使うよな... 何個くらい使ってんだろ 🤔」

と疑問に感じました。

ということで、今回の記事では、普段使っている開発ツールを一覧にしてまとめてみようと思います。

この記事の中で、読者のエンジニアライフに役立つような便利な開発ツールを見つけていただけると嬉しいです🙏

Warp

www.warp.dev

まずは、ターミナルアプリより Warp です。
Rust で作られた高速なターミナルで、モダンなターミナルでは当たり前のコマンドの補完・履歴検索・マルチカーソル・画面分割などはもちろん、AIが打ち間違えたコマンドから正しいコマンドを予測してくれたりと超便利です。

筆者は以前、後述の iTerm を使っていましたが、Warp の速さと利便性を実感してからこちらに乗り換えました🙌

デフォルトで用意されているカラーテーマも豊富で、おしゃれにターミナルを使いたい人にもおすすめです!

brew install --cask warp でインストールできます。

iTerm

iterm2.com

言わずと知れた有名なターミナル iTerm 。
設定ファイルをカスタムすれば、自分好みの見た目や機能を盛り込めるので便利です。
Mac に元から入っている Terminal よりも設定がわかりやすかったり、機能が豊富な定番のターミナルですね。

brew install --cask iterm2 でインストールできます。

Visual Studio Code

code.visualstudio.com

エンジニアなら知らない人はいない無料のコードエディタ VS Code です。
豊富な拡張機能や外部ツールとの連携が強みですよね。使わない日はないくらいお世話になってます🙇‍♂️

brew install --cask visual-studio-codeでインストールできます。

Google Chrome

www.google.com

VS Code 以上に毎日使うのが Chrome です。Web の開発なら使わないことはないでしょう。

brew install --cask google-chrome でインストールできます👍

Docker Desktop

www.docker.com

これが無いと何も始まらん...というくらい重宝している Docker のデスクトップアプリです。
開発の都合上、Docker が立ち上がってないと開発ができないので終始起動してます🐳

brew install --cask docker でインストールできます。

Beekeeper Studio

www.beekeeperstudio.io

モダンな DBMS のクライアントツールです。
主要な DBMS にマルチ対応しており、Beekeeper Studio の中で SQL の補完、フォーマットをしてくれたりとかなり便利。

Beekeeper Studio の開発者の方は、DB関連のツールに古めかしい外観のものが多いことに疑問を持ち、開発に至ったそうです。brew install --cask beekeeper-studio でコミュニティ版をインストールできます。

コミュニティ版は、データの一括ダウンロードができないのがちょっと残念😢

Sequel Ace

Sequel Ace

Sequel Ace

  • Moballo, LLC
  • 開発ツール
  • 無料
apps.apple.com

DBMS クライアントツールの定番である Sequel Pro からフォークして作られたツールです。

本家の Sequel Pro はなかったダークモードが Sequel Ace には搭載されており、UI も改善されています。使い勝手は全く同じなのでおすすめです👍

brew install --cask sequel-ace でインストールできます。

pgAdmin4

www.pgadmin.org

PostgreSQL 専用のクライアントツールです。
過去に PostgreSQL の geojson 形式のデータに対応しているツールを探していたときに見つけました。

brew install --cask pgadmin4 でインストールできます🐘

Medis

getmedis.com

Redis を使っている開発者にはおすすめのクライアントツールです。
ポートを接続すれば、一般的な DBMS のクライアントツールと同様に Redis に保存されているデータを GUI から確認することができます。

brew install --cask medis でインストール。

Postman

www.postman.com

エンドポイントを登録しておけば、ボタン1つで簡単に実行できるようにしてくれるツール。
バックエンドの開発はもちろん、フロントエンドの開発でも重宝します。
繰り返し実行機能をうまく活用すれば、ダミーデータの大量登録に活用できたりとかなり便利です🙌

brew install --cask postman でインストールできます🧑‍🚀

Fork

git-fork.com

チーム開発をしていると、 Git Branch を GUI 上で確認したいときってありますよね。
そんなときに役に立つのが Fork です。この手の Git のクライアントツール系は色々試しましたが、機能面・UI面で一番気に入ってます。

brew install --cask fork でインストール🍴

Raycast

www.raycast.com

開発ツールではなく、ランチャーアプリですが Raycastを使わない日はないので紹介させてください。
エンジニアフレンドリーなランチャーアプリとされており、プログラミング言語フレームワークの公式ドキュメントを Raycast から検索できたりします!

brew install --cask raycast でインストール。

最後に

最後まで読んでくださりありがとうございました🙇‍♂️

いかがだったでしょうか?

今回、紹介した他にも開発に利用しているアプリケーションはありますが、特に使っているアプリを選んで紹介させていただきました!

皆さんのエンジニアライフのお役に立てれば幸いです。

【OSSコードリーディング入門】gimeiはどのように人名を返しているのか

「良いエンジニアになるには良いコードをたくさん読むべし」と偉い人が言っていました。
しかし、良いコードとは何でしょうか。そして、それはどこにあるのでしょうか。
自分は「良いコード」とは「広く世界で使われているもの」を1つの答えだと考えています。
つまり広く世界で使われているOSSのコードを読んでみることが、良いエンジニアになるための助けになります。
OSSのコードリーディングを通して、さまざまな設計・パターン・技法を学ぶことができます。

OSSのコードを読む心構え

とはいえ、いきなりOSSのコードを読むのは敷居が高いです。
自分が1年ほど、さまざまなOSSのコードを読んできた中で、感じた大切なことは4つあります。

  • 全てを理解しようとしないこと(膨大な量のコードを全て把握するのは作者でも難しい)
  • 適切なボリューム感を選択すること(巨大なコードベースを持つOSSを最初に選ぶと辛い)
  • 普段、触れている技術に近いOSSを選択すること
  • 可能な限り、使い慣れたものを選択すること

以上、4つを踏まえて、N2iと親和性のあるRubyで書かれたOSSのコードを読んでみたいと思います。 適切なボリュームかつ実際にN2iで使用しているgimeiというOSSを選択しました。

github.com

gimeiについて

OSSのコードを読む前に最低限の使い方を把握しておくのが良いです。
githubのREADME.mdを参考にしつつgimeiについて簡単に紹介しておきます。
gimeiは日本人の名前や、日本の住所をランダムに返すライブラリです。

gimei = Gimei.name
gimei.kanji          #=> "斎藤 陽菜"
gimei.hiragana       #=> "さいとう はるな"
gimei.katakana       #=> "サイトウ ハルナ"

ここでは Gimei.nameを最初に呼び出していることを覚えておいてください。

引用元: gimei/README.md at main · willnet/gimei · GitHub

いざコードを読んでみる

コードをgithubからクローンするかブラウザを使ってファイルを閲覧するでも何でもOKです。
コード内を何度も検索することになるので、個人的にはコードをクローンしてお気に入りのエディタで読み進めるのがおすすめです。

gemの場合、最初に開くべきファイルはlib/gimei.rbです。
なぜこのファイルなのかというと、gemはファイル構造が決まっておりlib直下にgem名と同名のファイルが必ず配置されています。 全ての処理はこのファイルから始まります。
他言語のOSSであっても、最初に開くべきファイルを把握しておくのが重要です。

require 'forwardable'
require 'yaml'
:

class Gimei
  extend Forwardable
  GENDERS = [:male, :female].freeze

  def_delegators :@name,
  :
end

gimei/lib/gimei.rb at main · willnet/gimei · GitHub

先ほどGimei.nameを最初に呼び出していたことを思い出してください。
このファイルではGimeiクラスが定義されています。Gimei.nameの呼び出しが可能ということはnameがクラスメソッドとして定義されているはずです。

class Gimei
  class << self
    def name(gender = nil)
      Name.new(gender)
    end
  end
end

ありました...。
nameが呼び出しされるとNameクラスのインスタンスを作成して返しています。
次はrequire 'gimei/name'を頼りにNameクラスの実装を見てみます。

Nameクラスの実装

Nameクラスのインスタンスを作成しているためinitializeが呼び出されます。
ここでは3つのインスタンス変数が定義されていることが分かります。@genderには引数に指定がなければ、性別(maleかfemale)がランダムで束縛されます。
残りの@firstにはFirstクラス、@lastにはLastクラスのインスタンスがそれぞれ束縛されています。

class Gimei::Name
  def initialize(gender = nil)
    @gender = gender || Gimei::GENDERS.sample(random: Gimei.config.rng)
    @first = First.new @gender
    @last = Last.new
  end
end

同じファイルにFirstとLastクラスが定義されています。
First(名前)では性別が重要になります。男性なら倫太郎、女性ならまゆりとかでしょうか。

class First
  def initialize(gender = nil)
    @gender = gender || Gimei::GENDERS.sample(random: Gimei.config.rng)
    @name = NameWord.new(Gimei.names['first_name'][@gender.to_s].sample(random: Gimei.config.rng))
  end
end

Last(氏名)では性別は関係ないので、Firstクラスと比べるとシンプルですね。

class Last
  def initialize
    @name = NameWord.new(Gimei.names['last_name'].sample(random: Gimei.config.rng))
  end
end

どちらのクラスも@nameにNameWordクラスのインスタンスを束縛しています。
インスタンス作成時にGimei.names['last_name']を指定しています。一体、どのような値が指定されているのでしょうか。

gimei/lib/gimei/name.rb at main · willnet/gimei · GitHub

Gimei.namesと人名一覧の読み込み

Gimei.namesが返す値を知るために、再びlib/gimei.rbのコードを見てみます。
標準ライブラリのyamlを利用して/data/names.ymlの中身を読み込んでいることが分かります。 Gimeiクラスではクラスメソッドとインスタンス変数を使ってシングルトンクラスを実装しています。
@names ||= ...と組み合わせることで状態を記録して、YAMLファイルの読み込みを、一度だけ行うように上手く作られていますね。

class Gimei
  class << self
    def names
      @names ||= YAML.load_file(File.expand_path(File.join('..', 'data', 'names.yml'), __FILE__))
    end
  end
end

🖱: クラスメソッドとインスタンス変数を使ったシングルトンクラスの実装例

class Sample
  attr_reader :state
  
  def self.set_variable(value)
    @state ||= value
  end
  
  def self.get_variable
    puts "state: #{@state}"
  end
end

Sample.get_variable
Sample.set_variable(1)
Sample.get_variable
Sample.set_variable(2)
Sample.get_variable

# state: 
# state: 1
# state: 1


names.ymlには以下のように人名の情報が「漢字、ひらがな、カタカナ」の順に記録されています。

first_name:
  male:
    - ['愛斗', 'あいと', 'アイト']
    - ['愛登', 'あいと', 'アイト']
  female:
    - ['阿愛', 'ああい', 'アアイ']
    - ['安唯', 'あい', 'アイ']
last_name:
  - ['佐藤', 'さとう', 'サトウ']
  - ['林', 'はやし', 'ハヤシ']

ymlファイルから読み込んだ値はハッシュとして扱えるため、以下のように人名の一覧を取得することが可能です。

irb(main):015:0> Gimei.names['first_name']['male'].class
=> Array
irb(main):016:0> Gimei.names['first_name']['male'].first
=> ["愛斗", "あいと", "アイト"]

NameWordのインスタンス作成時に指定していたNameWord.new(Gimei.names['last_name'].sampleの場合、人名の一覧からランダムに1件が選択されます。

gimei/lib/gimei.rb at main · willnet/gimei · GitHub

人名を返す

いよいよ最終段階です。
NameWordクラスでは指定された人名の情報(漢字、ひらがな、カタカナ)を配列として保持しており、取得したい情報に対応するメソッドが定義されています。

class NameWord
  def initialize(name)
    @name = name
  end

  def kanji
    @name[0]
  end

  def hiragana
    @name[1]
  end

  def katakana
    @name[2]
  end
  :
end

処理を遡り、Gimei.name.kanjiを実行して、人名の漢字表記を取得する場合の動きを見てみます。
Gimei::Nameクラスのインスタンス作成時に@first@lastが宣言されており、それぞれのインスタンス変数にはymlファイルから読み込んだ人名の情報がランダムに束縛されているのでした。

class Gimei::Name
  attr_reader :first, :last, :gender

  def initialize(gender = nil)
    @gender = gender || Gimei::GENDERS.sample(random: Gimei.config.rng)
    @first = First.new @gender
    @last = Last.new
  end

  def kanji
    "#{last.kanji} #{first.kanji}"
  end
end

gimei/lib/gimei/name.rb at main · willnet/gimei · GitHub

メソッドの移譲

kanjiメソッドでは@first@lastクラスのkanjiメソッドを呼び出しています。
しかしFirst、Lastクラスにはkanjiメソッドを定義している箇所はありません。どういうことなのでしょうか...。
ここでは「移譲」という方法が採用されています。メソッドの移譲がFirstクラスの2~3行目にてForwardableモジュールを使って設定されています。

class First
  extend Forwardable
  def_delegators :@name, :kanji, :hiragana, :katakana, :to_s, :romaji
end

docs.ruby-lang.org

Forwardableに定義されたdef_delegatorsを使用すると、別クラスのメソッドを代わりに呼び出すようになります。
ここでは@nameに束縛されているNameWordクラスのkanji, hiragana, katakana...が、それぞれ該当します。

class NameWord
  def kanji
    @name[0]
  end
  :
end

こうしてFirst、Lastクラスから人名の情報を取得することが可能となりました。
そして最後に、取得した氏名と名前を組み合わせて人名を返すというのがgimeiの内部で行われていることでした。

"#{last.kanji} #{first.kanji}"
# 岡部 倫太郎

まとめ

  • GimeiクラスからGimei::Nameクラスのインスタンスを作成する
  • Gimei::NameではFirstとLastそれぞれのインスタンスを保持している
  • インスタンス作成時、ymlファイルを読み込みランダムな情報を1件取得している
  • FirstとLastクラスはそれぞれNameWordクラスのインスタンスを保持している
  • FirstとLastクラスはメソッドをNameWordクラスに移譲して人名を返す

大量の情報は外部ファイルに置いておく、メソッドを移譲するなど、新たな学びがありました。
このようにOSSのコードを読んでみると、自分の知らないメソッドや実装方法に出会うことができるので、非常に面白いです。

みなさんもぜひチャレンジしてみてください。

N2i の古株エンジニア 田中さんへインタビュー!!

田中さんへインタビュー
田中さんへインタビュー

Work illustrations by Storyset

こんにちは、技術ブログ編集長の和田です。

今回は、「社内のエンジニアに自信のキャリアや仕事への取り組み方などについてインタビュー」シリーズの第3回目の記事になります。

前回は、N2i のテックリードであるエンジニア 杉浦さんにお話を伺いました。まだ、読まれてない方はぜひご覧下さい🙏

techlog.n2i.jp

今回は筆者が日頃からお世話になっているシニアエンジニアの田中さんにお話を伺いたいと思います。

これまでの経歴

-- まずは田中さんの経歴について質問をさせてください。田中さんは N2i で働かれるまでに建築業界や音楽業界などいろんな業界で働かれていた経験があるそうですね。エンジニアになろうと思ったのはなぜでしょうか?

体力的な問題が一番の理由ですね😅
建築も音楽や映像制作も現場作業で、体力が必要とされれます。長期的に働くことを考えると、デスクワークにすることが自分に合っていると思いエンジニアを選びました。

プログラミング自体は幼少期から経験があり得意だったので、そのような背景もあり今の職に至ります。

-- 幼少期からプログラミング ?! 具体的にどんな環境で勉強されたんですか ?

父が当時流行っていた PC8001 というコンピュータを所有していました。
父と一緒に PC8001 を使って BASIC を勉強したのが始まりでしたね。
しばらくして、メールのやり取りができるものだったり、インターネット掲示板などが流行り始めて、Web 系の技術にも触れてきました。

田中さんが当時使用されていた実際の書籍
田中さんが当時使用されていた実際の書籍

-- すごい... まさしくインターネット黎明期に生きてきたんですね。エンジニアになって良かったと感じることはありますか?

開発の計画や時間の使い方を自分で、決めやすいのはエンジニアになって良かったと感じますね。

建築や映像制作などに比べると、アプリケーションの開発は工夫次第でうまくスケジュールを調整しやすいです。

-- エンジニアとして初めて参加された案件は何だったんですか?

映像制作会社で働いていた時代に、結婚式の余興のシステムを開発したのが仕事としては最初でしたね。

結婚式の参加者が撮影した写真をスクリーン上に写してみんなで共有できるようなものです。

開発していた当初、人工知能が流行り始めたこともあって、撮影した写真に笑顔の人が多ければ高得点として判定されるような仕組みを組み込んだりしました。

田中さん自身について

-- 田中さんの得意なことはなんですか?

初対面で誰とでも話せることですね。趣味が広いことも有り、どんな話題でも話せるのが自分の強みだと思ってます。

-- どうやっていろんな知識を取り入れているんでしょうか?

気になったことはメモして、退勤後や休日に少しの時間だけでも必ず調べるように心がけています。

いろんなニュースについて細かく時間を分けて長期的に触れておくことが、いろんな知識を吸収するコツですかね...

-- 逆に苦手なことはなんですか?

芸術的なセンスが自分には無いと感じています...😭

フロントエンドのデザインなどを作るのは少し苦手かもしれませんね。デザイナーの方たちの「1px でもズレてはいけない」というこだわりは凄いと思います。

こういった感性だったり特技がないのがつらいですね...

-- たしかに芸術的な感性って全員がもってるものではないですよね。エンジニアの業務には支障はないと思うんですけど、そのあたりはどうなんでしょう?

言葉が通じない外国の方などに、手ぶらの状態でなにかアピールできるものがほしいと思ってしまうんですよ。

「自分はこんな人なんですよ」と紹介できるような特技ですね。何か一つでもこういった特技があればよかったな~って思います笑

-- 前回の杉浦さんもそうだったように、自分の強みや弱みを把握しているのはシニアエンジニアあるあるなのかもしれないですね。

-- 今回は田中さんにインタビューをさせていただきました。本日はありがとうございました!


今回のインタビューアーは前回インタビューをさせていただいた杉浦さんと岡部さんに担当していただきました。
改めまして田中さん、ありがとうございました!

N2iの開発をリードするエンジニア杉浦さんにインタビュー!

こんにちは、技術ブログ運営チームの岡部です。
今回は前回より始まった「社内のエンジニアに自身のキャリアや仕事への取り組み方などについてインタビュー」の第2回目の記事になります。前回は香川県にお住まいのエンジニア 安田さんにお話を伺いました。 まだ読まれてない方はぜひご覧ください。

techlog.n2i.jp

今回はN2iのテックリードであるエンジニア 杉浦さんにお話を伺いたいと思います。

これまでの経歴

-- まずは多くの人が気になっている杉浦さんの経歴について質問させてください。N2iに入社するまでどんな経験をされてきましたか?

新卒でシステム会社に入社してSIerとして社会インフラのシステムやECサイトを開発していました。
10~11年勤めた後、WEB制作の会社に1年ほど勤務した後にフリーランスになりました。それからはWEBシステムの開発をしてます。

-- フリーランスだったとはお聞きしていましたが、SIerをされていたのは知りませんでした。N2iにはどのような経緯で入社されたのでしょうか?

名古屋のシェアオフィスで仕事をしていた時にN2i代表の篭橋さんに出会いました。
「一回、*1事務所に来て」と言われてフリーランス時代からN2iの仕事をやっていました。
WEB制作だと自分の力が活かせなくてモヤモヤしていたのですが、N2iでWEBシステムの開発を通じて、凄く成長を感じましたね。

ただ、技術力が上がると早く仕事が終わってしまうので、時給制だと単価が下がってしまい金額面でキツくなったのと、当時、帰り道でよく吉野さんと一緒になることがあって「フリーランス辛いよね...」と話している時にN2iに誘われて入社しました。

過去の案件について

-- 社員になる前からN2iとは付き合いがあったんですね。少し話は変わりますが、杉浦さんが新卒で初めて参加した案件はどのような案件だったのでしょうか?

あまり覚えていないのですが、どこかの工場の製品管理システムでした。
開発の担当ではなかったんですが、入社2日目にバグが見つかってなぜか自分が徹夜して対応した記憶があります(笑)

-- 入社して2日目に徹夜でバグ対応とは中々にハードな案件ですね...。それから様々な案件に携わってきたかと思いますが、最も難しかった案件についても教えてください

とある巨大な施設のシステムですかね。
センサーから受け付けるイベントをコンピューターで状態管理するというものでした。
数値計算の用件が難しくビジネスロジックの実装にとても苦労しました。
あとはUIのリアルタイム描画もWEBシステムのようにボタンなどを手軽に描画できるものではなく、線を1本ずつ引いて四角形を作って、影を入れて立体的にしてボタンを描画する...ようなレベルでの実装だったので、とても大変でした。

SPAを触り始めた時は「なんて楽なんだ」と感動した記憶があります。

普段の業務について

杉浦さんお手製のパンと焙煎したコーヒー豆

-- 杉浦さんは普段はどのような業務をされているのでしょうか?

新規機能開発の事前調査や設計をしたり、既存のアーキテクチャの改善だったりをやっています。
あとは開発メンバーからのコードレビュー依頼や、問い合わせの対応もやっています。

-- めちゃくちゃ幅広いですね(笑) 幅広い知識が求められると思いますが、業務のために勉強していることなどはあるんでしょうか?

自分は座学が苦手だと思っているので必要性を感じた時に集中的に取り組む事が多いですね。
とはいえ事前知識がないとアイディアが出てこないので、GCPのサービス一覧を定期的にチェックしたりはしています。
学習という感覚はあまりなくて、考えてどうするかを得て学んでいるなと感じています。
道具を使いこなす事が好きなんですよね。

職種について

-- 杉浦さんがエンジニアになろうと思ったきっかけは何だったんでしょうか?

自分は昔から集中力がなくて幼稚園ぐらいから落ちこぼれてしまって学業成績が悪かったです。
ただ親族がみなエンジニア(特殊機械や木型など)だったので、手に職をつけたいという考えが強くありました。
それから工業高校に進学して、basicというプログラミング言語を触ってみたら面白くて興味が湧いたので専門学校に進みました。

他のことには集中できなかったんですが、プログラミングは徹夜しても出来たので仕事としてやりたいと思ったのがきっかけです。
エンジニア以外の選択肢はなかったですね。

-- プログラミングには興味が湧いて集中できたというのが何とも杉浦さんらしいです。エンジニアになって良かったと感じる点はありますか?

必ずしも努力すれば良いという訳ではないことです。
複雑な事を頑張ってやるよりも、シンプルで単純なことを選択していくことが重要だと感じています。
あとは自分のように浮き沈みがあっても、影響があまりないというの良い点ですね。安定して同じ作業にずっと取り組むのは自分には辛いと思います。

-- たしかに複雑なことを頑張るよりも、単純なことに分割するのはエンジニアリングにおいて非常に重要な考え方ですよね。他に仕事をする上で気をつけていることがあれば教えてください

エンジニア以外の方の話を素直に聞くことですかね。
どうしてもエンジニアは他の立場の方と対立してしまうことがありますが、自分と違う視野で考えている人の意見は参考になります。
あとは自分のこだわりを突き通すよりも、サービスにとってそれが良いのか悪いのかを選択するようにしていますし、先ほども少し話しましたが、複雑だなぁと感じた時は細かくシンプルで単純なことに分割するように意識しています。

-- 一緒に仕事をしていて杉浦さんの「とりあえず意見を聞く」という姿勢を感じていましたが、そういうことだったんですね。今回は杉浦さんにインタビューをさせていただきました。本日はありがとうございました!

ありがとうございました!

今回のインタビューアーは前回、インタビューをさせて頂いた安田さんに担当して頂きました。
改めまして安田さん、ありがとうございました。

*1:当時は伏見にオフィスがありました