かなで技術日誌

プログラミングやエンジニアリング周りについて

主なアウトプットはScrapboxObsidianにまとめてます。

最近やってること

あまりにも更新しなさすぎなので、最近やってることについて書いていく。 今後もちゃんと日誌にして雑多なことを書いていって、まとまった技術的な知見はzennに書いていく感じにしていきたい。

業務

去年はずっとTypeScriptとGCPとTerraformをやっていた。

TypeScriptはType Challengeは全くやっていないが一通り業務で自分で実装もするしレビューもしたのでそれなりに実装できていると思う。 ただ信頼できない外部からの入力に対しては脆く、実践導入はできなかったがio-tsとか使えばもっと楽になったかもしれない。

github.com

Terraformはぶっつけ本番で実戦投入して、良かったこともあるし知識不足でうまくいかなかったこともあり、全部含めて学びになった。(次はもっとうまくやるぞ)

ここ数ヶ月は大したことはやっていないが、直近はplaywrightでJavaのWebアプリケーションのE2Eテストを実装している。E2Eテスト、慣れてないのもあるかもしれないが壊れやすくて難しい。

TypeScript

プライベートでpuppeteerでスクレイピングしたりそのpuppeteerをkickするAPIをExpressとかNestJSで実装するなど。

Reactは1月に社内の課題でviteとかesbuildとか使ってフォームをいい感じにvalidationするのをTDDでやってた。

とはいえ型周りの仕様に疎い(inferとか)のでType Challengeを4月はやりたい。(時間あるのか?)

github.com

Go

現職の社内プロジェクトでGoでAPIを隙間時間で開発したりTerraformの環境構築したりしている。 如何せん本業の隙間なので時間があまりない。

Goのhot reloadに何がいいかを同僚と検討していて、reflexがいいんじゃないかという話になり採用している。

github.com

現状ではGoでしか使ってないが、他の使い道がありそうで良さげ。

Database Clientはいまいち何が主流なのかあまりわかってない。SQL firstなsqlcも良さそうだしGraphQLと相性が良いentも良さそうな感はあるが、そんなに使えてないので不明。

github.com

https://entgo.io/

Rust

個人で細々とAPIを開発している。actix-webがベタだけど、axumも良いかもしれないと思い始めている。

blog-dry.com

blog.varwww.com

その他趣味

ちまちまとやっていた雀魂で雀聖になった。このぐらいはいけると思っていたので今も粛々とやっている。基本に忠実に打って、若干引き気味ぐらいがちょうど良いという感じがある。一向聴からは思っているよりも押せないというのを科学する麻雀で確認できたのとNAGAと対戦して序盤の切り順を矯正できたのが良かった。天鳳もやっていきたいが時間がない。

同僚に布教したら自分よりもどハマりしており毎日やっている。

React+Contentful+Netlify+Google Domainで個人ブログを作る

久しぶりの投稿です。TypeScript+Reactの素振り用に前から気になっていたHeadless CMSのContentfulを使ってブログをNetlifyにホスティングしてみました。今ならCloudflare Pagesとかになるんでしょうか。

Netlifyから提供されるドメインではなく独自ドメインを設定する方法も合わせて追記しています。今回はGoogle Domainを使いました。お名前.comは使いたく無い

NextでもVueでも同じようにできると思うので置き換えてください。

参考になるか分かりませんが、記事の例で使用している自分の実装はこちらです。あくまで参考程度ですが、特にeslintとかprettierとかの設定は参考にしないでください。

話すこと

  • Contentfulでの設定方法
  • Contentfulのmodelの型定義
  • SDKの使い方
  • Netlifyでのデプロイ手順
  • GitHub Actionsの設定
  • Google Domainでのドメイン取得方法

話さないこと

  • TypeScript自体
  • Reactの環境構築
  • 全体の実装

お品書き

  1. Contentful設定
  2. SDKの使用方法
  3. modelの型定義
  4. Netlify設定&デプロイ
  5. ドメイン取得

Contentful設定

Contentful自体の日本語での説明はこちら(次世代Headless CMS「contentful」事始め)をみてください。

何はともあれログインしてspaceを作成します。基本Freeで大丈夫です。

f:id:kana_kanade:20210405224154p:plain

spaceを作成したらmodelを作成します。これが実際のデータ構造になります。

Contentfulは多くのfieldを用意しています。ReferenceはRDBの外部キーのような使い方ができます。今回はカテゴリとタグを紐づけるのに使用しました。 今回は記事のカテゴリとタグ、記事自体のmodelを用意します。 f:id:kana_kanade:20210406082733p:plain

まずカテゴリのmodelです。 f:id:kana_kanade:20210406082530p:plain 次にtagのmodelです。 f:id:kana_kanade:20210406082546p:plain 最後に記事のmodelにカテゴリとタグを紐付けます。 f:id:kana_kanade:20210406082551p:plain 記事本文は今回はMarkdownを指定してください。描画時にMarkdownをhtmlに変換します。 f:id:kana_kanade:20210406083042p:plain カテゴリとタグの紐付けはReferencesを選択するとOne ReferencesとMany Referencesが選択できます。カテゴリは記事に対して一対一、タグは一対多で一つの記事に複数紐づくようにしました。 f:id:kana_kanade:20210406083712p:plain

最終的に以下のようになります。 f:id:kana_kanade:20210406082524p:plain

最後に記事を取得するためのAPI keyを生成します。ヘッダーのSettings→API KeysからAdd API Keyを選択して生成します。 後でContentfulのSDKを使いますが、その際にSpace IDとContent Delivery API - access tokenが必要になります。 f:id:kana_kanade:20210406084536p:plain

これでContentfulは終わりです。

SDKの使用方法

Reactの一通りの環境構築が終わっている前提で、SDKのinstallと使い方の説明をします。

SDKをinstallします。

npm install --save contentful

記事一覧を取得する実装です。 createClientにSpace IDとaccess tokenを渡してContentfulClientApiインスタンスを生成します。 その後getEntriesで全体を取得できます。 ここでは実装していませんが、検索条件を付与することも可能です。詳しくはドキュメントを読んでください。

型定義については後で説明します。

import {ContentfulClientApi, createClient, Entry} from 'contentful';
import {Article as ContentfulArticle} from '../domain/contentful/article';

const getClient = (): ContentfulClientApi => {
  return createClient({
    space: process.env.REACT_APP_CONTENTFUL_SPACE_ID as string,
    accessToken: process.env.REACT_APP_CONTENTFUL_DELIVERY_API as string,
    resolveLinks: true,
  });
};
const getContent = async (
  contentType: string
): Promise<Entry<ContentfulArticle>[]> => {
  const client = getClient();
  const response = await client.getEntries<ContentfulArticle>({
    content_type: contentType,
  });
  return response.items;
};

modelの型定義

Contentfulの記事はここをanyにしてごまかしている実装がよく検索でヒットします。JavaScriptではなくTypeScriptを使っているのでanyに逃げずにちゃんと型定義をします。

先ほどのgetEntriesの戻り値の型はPromise<EntryCollection<T>>>です。 その戻り値のitemプロパティの型はEntry<T>でこれがmodelの実体になります。このTに自分で設定したmodelの型定義をあててあげれば型をつけて取得できます。

Entry型は以下のようなデータ構造になっています。

export interface Entry<T> {
    sys: Sys;
    fields: T;
    toPlainObject(): object;
    update(): Promise<Entry<T>>;
}

sysはuniqueなidや作成日時、更新日時などのメタデータが定義されています。 fieldsがモデルの定義になります。

よって今回の型定義は以下のようになります。

import {Sys} from 'contentful';
export type Category = {
  fields: {
    name: string;
  };
  sys: Sys;
};
export type Tag = {
  fields: {
    name: string;
    slug: string;
  };
  sys: Sys;
};
export type Article = {
  category: Category;
  content: string;
  tag: Tag[];
  title: string;
};

Article型はEntry型のfieldsプロパティの型を定義してsysプロパティの型定義は不要です。

Netlify設定&デプロイ

NetlifyはGitHub連携でpush時に自動でデプロイするのとSDKでのGitHub Actionsでのデプロイがあります。 starter planではビルドが月300分までなので、頻繁にデプロイしたいならGitHub Actionsでビルドしてデプロイするとお得です。 以下のような.github/workflows/deploy.yamlを用意してください。

masterブランチへのpush時に以下のworkflowが実行されます。

name: Deploy

on:
  push:
    branches:
      - master

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout source code
        uses: actions/checkout@v2

      - name: Cache node_modules
        uses: actions/cache@v1
        with:
          path: node_modules
          key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.OS }}-build-
            ${{ runner.OS }}
      - name: Setup Node
        uses: actions/setup-node@v1
        with:
          node-version: 14.x

      - name: npm install and build
        run: |
          yarn install
          yarn build
      - name: Deploy to netlify
        run: npx netlify-cli deploy --dir=./build
        env:
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}

まずAUTH_TOKENとSITE_IDですが、アプリケーション設定画面に遷移するとNew access tokenとあるので押下するとAUTH_TOKENが取得できます。 f:id:kana_kanade:20210510234707p:plain

SITE_IDはhttps://app.netlify.com/sites/[自分のサイト名]/settings/generalに遷移するとAPI IDとあるのでこれがSITE_IDです。

f:id:kana_kanade:20210510234754j:plain

これらをGitHub Actionsで使うために環境変数として登録します。 GitHubリポジトリのSettingタブからSecretsを選択すると環境変数が登録できます。 f:id:kana_kanade:20210516182707p:plain

Google Domainsでカスタムドメイン設定

Google DomainsGoogleが提供するドメイン管理サービスです。 日本だとお名前.comがありますが、個人的には使いたくないので今回はGoogle Domainsを使います。

これ読んででいいですか?ダメ?

大切なのは、Google DomainsのカスタムリソースレコードにnetlifyのIPアドレスとwwwつきドメインを追加してください。 f:id:kana_kanade:20210531221952p:plain

netlifyのドメイン設定もここにあるんですが、

NetlifyのDomain ManagementからAdd Custom Domainで以下のような設定されていればおkです。 f:id:kana_kanade:20210531233456p:plain

最後にbaseURLを設定したカスタムドメインにすれば終わりです!

まとめ

覚えてしまえばそんなに難しく無いかなと思います

今度はNext.jsでcloudflare pagesにデプロイしてみたいです。

Eloquent Model FactoryでMaximum function nesting level of '256' reached, aborting!

Eloquentのfactoryでテストデータを用意してテストを実行した際、以下のようなエラーが突如発生した。

PHP Fatal error:  Maximum function nesting level of '256' reached, aborting! path/to/vendor ...

結論

依存関係が循環するfactoryを定義して実行しており、factoryの実行自体でエラーにならなかったため。

内容

これは大量の再帰呼び出しを行った際に発生するエラーです。 再帰呼び出しの回数が非常に多い場合、メモリにひたすら実行された処理の結果が積み重ねられ、スタックオーバーフローが発生する可能性があるため、PHPでは一定回数を超えるとエラーが発生します。

このエラーになる回数自体はXDebugの設定で変更できます。

teratail.com

ですが、今回テスト対象の実装には再帰呼び出しもなければ大量のループもありませんでした。 本当の原因はエラーの原因はfactoryの実装で以下のような実装になっていました。

factory(Room::class, function(Faker $faker){
    return [
        'name' => 'room name',
        'house_id' => factory(House::class)->create()->id
    ];
});
factory(House::class, function(Faker $faker){
    return [
        'name' => 'house name',
        'room_id' => factory(Room::class)->create()->id
    ];
});

RoomモデルとHouseモデルが循環しており、実質無限に再帰的に相互で呼び出しを行う処理が作られていました。
しかし、Houseモデルが参照しているhousesテーブルにはroom_idは存在しません。 存在しないカラムをfactoryで定義しているのでエラーになりそうですが、Eloquentのfactoryでは存在しないカラム自体ではエラーにならず、今回のエラーになるまでずっと処理が止まらなかったということです。

想定していなかった動作だったので原因の発見に少し時間がかかりました。 個人的には存在しないカラムを定義しているのであればエラーを発生させて欲しいですね。

社内勉強会でLTやった

www.slideshare.net

社内勉強会で乱数と擬似乱数について発表しました。

内容的にはそんなに深いことはしていないですが、難しかったと言う声が多かったのでちょっと意外でしたが、非エンジニアも見るのでそれはそうかもと言う感ある。

内容的には、CSPRNGと真の乱数生成器以外は暗号技術に使わないでねってことと、線形合同法はよく考えて使ってねって内容でした。

まだメルセンヌツイスターとXorshiftはアルゴリズムがよく分かっていないのでまたちゃんと勉強したいですね。

2019年読んだ本と2020年やること・やらないこと

あとで見返せるようにするのと、やることを宣言するために。

2019年読んだ本

言語系

フレームワーク

ネットワーク

OS

セキュリティ

他にも細かく色々あるがとりあえずこんなところ。

2020年やること

今年は計算機科学の基礎固めとエンジニアリングの両立で行きたいと思います。

エンジニアリング領域

上期はこっちをやる。

計算機科学基礎

下期はおそらくこっち。
アルゴリズムとデータ構造、コンピュータアーキテクチャが必須で、最後は読めたら。一年でざっと読了できればいいなぐらい。

アルゴリズムとデータ構造枠として

  • CTF
    • 正解率〜40%の問題は基本的に解けるようになる。(Reversingは微妙かも)
  • C, C++
    • CS周りの勉強やCTFで必要なので。
  • AtCoder
    • 緑には最低でもなる。

本当はサーバサイドKotlinとかRustとかもやりたいが、今年は上記をやっていくことを優先。

2020年やらないこと

エンジニアとして優先度が相対的に低いもの。多分来年にやることになりそう。

  • 英語
  • MLとかkaggleなど
  • 離散数学(まとめて時間は割かないの意で必要になったら勉強する)

去年は上期は特定のフレームワークとか言語ばかりだったので、ひたすら計算機科学とエンジニアリングの基礎を固めていくようにしたい。

フレームワークとかパブリッククラウドなどは必要な時に必要に応じてやる程度にしてあまり深入りしない方針にします。

追記 大切ぽいことが書いてあったので

nowokay.hatenablog.com

2019年

年越しそば食べたのとポケモンで盛大に順位溶かしたので。

1〜3月

前職で開発やってた。Spring BootとAngular1を2:8ぐらいの割合で書いてた。

4〜6月

単体試験(実際の内容は結合試験)をやってた。
この辺から転職するつもりで動く。
現職の面接の前に2社で面接があり落ちた。うち1社はエージェントの紹介で面接を受けた会社が、あまりにも酷い態度で憤慨した記憶があり、エージェントはダメだと思った。
確か6月半ばぐらいに内定通知が来て、そこから辞める話をしたら営業がゴネ始めて(社会人としてのマナーがどうとしか言ってなかった)とりあえず常駐先とは統合試験がある7月いっぱいまでで交渉。

Scala触ったりEffective Javaとか読んでた。

7〜9月

7月は統合試験があり残業時間が80こそ超えなかったものの70は超えてた気がする。(でも基本給低いので残業代はそこまで)
この時期エクセルスクショしかやってなかった上に忙しかったので最悪だった。
8月に転職し、9月まで社内向けでLaravelで改修やったり新規案件やってた。(新規案件は諸事情により凍結となった)

10〜12月

10月からすでに進行している案件のヘルプ?としてエンジニアが欲しいとのことでアサインされる。
だいたいLaravelとVueやってた。
技術書典にも行った。

12月は社内で使ってる稼働入力と日報管理アプリのリライトでRailsとNuxt触ったりDjangoAPI実装したりしてた。
年末はフロントエンドエンジニアの部署で燃えつつある案件のヘルプで非同期周りを書いて終わった。(DOMを返すAPI is 何)



今年は人権がある環境に転職できたので総じて良かったと言えるでしょう。
ただ、転職前はもっと色々やっていた気がするので、転職して充足感もありインプットはともかくアウトプットが微妙だったので、来年は社内外ともにアウトプットを意識していきたい。