Try T.M Engineer Blog

多摩市で生息するエンジニアが「アウトプットする事は大事だ」と思って始めたブログ

AWS CDKに挑戦する話

最近、JavaScript / TypeScriptの学習に取り組んでいるので、これを機会にCloud Formationのテンプレートを書く方法もYAMLからAWS CDKに移行しようと思う。

AWS CDKを学ぶにあたり、AWS CDK Workshopをオススメされたので、やってみた。

AWS CDK Workshopでは、最初はAPI Gateway + Lambdaの簡単なところから、中盤にテスト、後半はCodePiplineを使ったCDを学ぶことができる。

今までYAMLで書いていたところを、if文クラス関数を使って書くことができるのは、新鮮に感じることができた。

今後は、可能なかぎりCloud Formationのテンプレートを書く時はAWS CDKを使って書くようにして、慣れていきたい。

あと、AWS CDKのデプロイ時に以下エラーで躓いたので、残しておく。

Unable to resolve AWS account to use. It must be either configured when you define your CDK or through the environment

このエラーメッセージはCDKの中では有名で、解決策としてawsconfigcredencialファイルを(空でも)作成しておくというのがある。
このエラーメッセージに出くわしたら、このissuesを参考にすること。

しかし、私の場合、上記の設定をしてもエラーを解決できなかった。 cdk doctorコマンドを使用して、潜在的な問題がないかチェックしたところ、どうやら環境変数AWS_CONFIG_FILEAWS_SHARED_CREDENTIALS_FILEが 設定されているとデプロイできなくなる模様。
こちらの環境変数を削除したら、無事デプロイできるようになった。(めでたし、めでたし)

JavaScript Primerを読んだ話

JavaScriptへの理解を深めるために、JavaScript Primerを読み始めた。
第一部を読み終えたので、理解定着のためはじめて知った事大事だと思った事を書いておく。

セミコロンは付けておいた方が良い。

セミコロンを省略して書いている人のコードをよく見かける(私自身も省略して書く)のだが、「暗黙的なものへ頼ると意図しない挙動が発生する」との記述があり、セミコロンは常に書くようにしたほうが良いとのこと。

何故varがダメだったのか?

私がJavaScriptのコードを書くようになった時点で、既にletがあり、なんとなくvarは使ってはいけない風潮があったので特に気にはしていなかった。
しかし、「歴史を知ることは同じ過ちを繰り返さない事に繋がる」と、おじいちゃんに言われていた事を思い出し、歴史を知ることにした。

varがダメな理由は、大きく以下の2つがある。

  1. 同じ名前の変数を再定義できる
  2. 変数の巻き上げと呼ばれる意図しない挙動がある

1.は以下の通り。

var x = 'hoge'
var x = 'fuga'
console.log(x) // fuga

2.は以下の通り。巻き上げとは、ブロックスコープを無視して最も近い変数に紐づけてしまうこと。

// var宣言より前に参照してもエラーにならない
console.log(x); // => undefined
var x = "varのx";

function fn() {
    // 内側のスコープにあるはずの変数`x`が参照できる
    console.log(x); // => undefined
    {
        var x = "varのx";
    }
    console.log(x); // => "varのx"
}
fn();

たしかに、こんな事ができてしまうと意図しない挙動が起きる可能性が高そうです。
letが実装されたのも頷けます。

Nullish coalescing演算子

私の場合、PHPの方で先に知ったかもしれない・・・
JavaScriptにもあったんですね。nullishな値なのでundefinedもちゃんと含みます。

// 左辺がnullishであるため、右辺の値の評価結果を返す
console.log(null ?? "右辺の値"); // => "右辺の値"
console.log(undefined ?? "右辺の値"); // => "右辺の値"

// 左辺がnullishではないため、左辺の値の評価結果を返す
console.log(true ?? "右辺の値"); // => true
console.log(false ?? "右辺の値"); // => false
console.log(0 ?? "右辺の値"); // => 0
console.log("文字列" ?? "右辺の値"); // => "文字列"

Arrow Functionの特徴

これはだいたい知っている内容だったんですが、1つ気になる記述が・・・

  • arguments変数を参照できない

arguments変数って何?

function fn() {
    // `arguments`はインデックスを指定して各要素にアクセスできる
    console.log(arguments[0]); // => "a"
    console.log(arguments[1]); // => "b"
    console.log(arguments[2]); // => "c"
}
fn("a", "b", "c");

おぉ、引数を定義していないのに要素にアクセスできるのか!!
正直知らなかった(= = ;;

オブジェクトの複製

本書にはshallow copydeep copyのサンプルコードが載っていたので、これは是非とも参考にしたい。

in演算子とObject#hasOwnPropertyの違い

  • hasOwnPropertyメソッド ・・・ そのオブジェクト自身が指定したプロパティを持っているかを判定する。
  • in演算子 ・・・ オブジェクト自身が持っていなければ、そのオブジェクトの継承元であるprototypeオブジェクトまで探索して持っているかを判定する。
const foo = {
  foo: "Foo",
  bar: {
    bar: "Bar"
  }
};

// そのオブジェクト自身が指定したプロパティを持っているかを判定
console.log(foo.hasOwnProperty('foo')); // true
console.log(foo.hasOwnProperty(toString)); // false

// prototypeオブジェクトまで探索して持っているかを判定
console.log('foo' in foo); // true
console.log('toString' in foo); // true

こういった細かい違いは、はじめて知りました(= = ;;

JavaScript文字コードの話

このあたりはとても興味深い話だった。
JavaScript文字コードとしてUnicodeを採用しいて、エンコード方式としてUTF-16を採用している。と書いてあって、最初は「ふーん」だったのですが、そこからCode PointCode Unitの話になると、「あれ?これ知ってないとマズくない?」と思いました。

配列をフラットに・・・

Array#flatメソッドというのがあるようです。これは初めて知りました(= = ;;
おぉ、便利そう・・・

const array = [[["A"], "B"], "C"];

// 引数なしは 1 を指定した場合と同じ
console.log(array.flat()); // => [["A"], "B", "C"]
console.log(array.flat(1)); // => [["A"], "B", "C"]
console.log(array.flat(2)); // => ["A", "B", "C"]

// すべてをフラット化するには Infinity を渡す
console.log(array.flat(Infinity)); // => ["A", "B", "C"]

正規表現を使った文字取得

String#matchAllメソッドというのがあるようです。これも初めて知りました(= = ;; 今までRegExp#execメソッドで使っていた実装していたところが、String#matchAllメソッドで置き換え可能っぽいですね。

const str = "ABC あいう DE えお";
const alphabetsPattern = /[a-zA-Z]+/g;
// matchAllはIteratorを返す
const matchesIterator = str.matchAll(alphabetsPattern);
for (const match of matchesIterator) {
    // マッチした要素ごとの情報を含んでいる
    console.log(`match: "${match[0]}", index: ${match.index}, input: "${match.input}"`);
}
// 次の順番でコンソールに出力される
// match: "ABC", index: 0, input: "ABC あいう DE えお"
// match: "DE", index: 8, input: "ABC あいう DE えお"

クロージャ

本書のクロージャーの説明は「どういった仕組みでクロージャーを実現しているのか?」をメインに書いており、とても面白かったし、わかり易い説明だった。
クロージャー・・・あまり使う機会がないなぁと思いつつ、以下で使われてるよー。と書いてあり、このあたりも意識していきたい。

this

これは色々なところで記事が書かれていて、よく目にします。
が・・・正直、本書が一番わかり易い。

プロトタイプオブジェクトとプロトタイプチェーン

JavaScriptは、プロトタイプベースのオブジェクト指向言語であるわけですが、他にプロトタイプベースのオブジェクト指向言語ってあるんですかね?

  • プロトタイプオブジェクト ・・・ JavaScriptの関数オブジェクトのprototypeプロパティに自動的に作成される特殊なオブジェクト。
  • プロトタイプチェーン ・・・ オブジェクト自身からPrototype内部プロパティへと順番に探す仕組みのこと。

例外処理

「throw文はあらゆるオブジェクトを例外として投げられますが、基本的にErrorオブジェクトのインスタンスを投げることを推奨」という事が書いてあり、ちょっとドキッとしました。

Map / Set

Set便利ですよね。よく使います。
Weakシリーズ(WeakMap / WeakSet)の存在を初めてしりました。(= = ;;
普通のMap / Setとは挙動が異なる様なので、使いどころを選びそうです。

都道府県のデータを作ってみた話

都道府県」のデータを作りたい。
そんな要望を受けたので、色々と調べてみることにしました。

とはいえ、データがないと何も始まらないので、データを探すことに・・・

取得先候補1: 総務省が出している「全国地方公共団体コードから取得」

www.soumu.go.jp

都道府県・市区町村のデータ、政令指定都市もわかるようになっており、データの形としては○。
しかし、Excelで作られているため、何かしらツールを使って取得しないといけない・・・うーん(= = ;;

取得先候補2: 内閣府が出している「都道府県一覧APIから取得」

opendata.resas-portal.go.jp

APIなので取得は簡単◎。これを使えば楽じゃないかーと思ったが・・・
データが古く、最新のデータが入っていない(= = ;; うーん

取得先候補3: 国土交通省が出している「都道府県コード一覧から取得」

www.land.mlit.go.jp

これもAPI。最新のデータも入っているように見える。
しかし、更新履歴等が無く、APIがいつ更新されるかも不明なので、継続的に使用するのはちょっと怖い・・・うーん(= = ;; うーん

結果: データ取得先候補1を選定

Excelで作られてはいるが、データとしては揃っているので、これを使うことにしました。
Excelから抽出できるツールは、なんちゃってTypeScriptで作ります。
出力結果は、jsonのようでjsonじゃないファイルで作ります。

import * as XLSX from "xlsx";
import { createWriteStream } from 'fs'

// 読み込み用Excel
let workBook = XLSX.readFile('./assets/000618153.xls')

const sheetList = workBook.SheetNames
const sheet1    = workBook.Sheets[sheetList[0]]
const sheet2    = workBook.Sheets[sheetList[1]]

interface JsonSheet {
  '団体コード': string,
  '都道府県名\n(漢字)': string,
  '市区町村名\n(漢字)': string,
  '都道府県名\n(カナ)': string,
  '市区町村名\n(カナ)': string
}

const jsonSheet1: JsonSheet[] = XLSX.utils.sheet_to_json(sheet1)
// 政令指定都市Sheetは、ヘッダーが無いため配列で格納する
const arraySheet2: string[] = XLSX.utils.sheet_to_json(sheet2, {header: 1})

// 政令指定都市Sheetの団体コードは、StringとNumber型が混在しているため、Stringに統一させる
const arrayStringUnifySheet2 = arraySheet2.map(x => typeof x[0] === 'number' ? [String(x[0]), x[1], x[2]] : x)

const prefectures = jsonSheet1.filter(cel => !cel['市区町村名\n(漢字)'] && !cel['市区町村名\n(カナ)'])
const cities = jsonSheet1.filter(cel => cel['市区町村名\n(漢字)'] && cel['市区町村名\n(カナ)'])

// 書き込み用Jsonファイル
const src = createWriteStream('./assets/prefMaster.json')

prefectures.forEach( cel => {
  let prefCode = cel['団体コード'].slice(0, 2)

  let city = cities.filter(cel => prefCode === cel['団体コード'].slice(0, 2))
  let ordinanceDesignatedCity = arrayStringUnifySheet2.filter(cel => cel[0].slice(3, 5) !== '00' ? prefCode === cel[0].slice(0, 2) : false)

  city.forEach(x => {
    src.write(`{ "prefCode": "${prefCode}", "prefName": "${cel['都道府県名\n(漢字)']}", \
"cityCode": "${x['団体コード'].slice(0, 5)}", "cityName": "${x['市区町村名\n(漢字)']}" }`)
    src.write('\n')
  })
  if (ordinanceDesignatedCity) {
    ordinanceDesignatedCity.forEach(x => {
      src.write(`{ "prefCode": "${prefCode}", "prefName": "${cel['都道府県名\n(漢字)']}", \
"cityCode": "${x[0].slice(0, 5)}", "cityName": "${x[1]}" }`)
      src.write('\n')
    })
  }
})

よし!では、出力結果を見てみよう!

{ "prefCode": "01", "prefName": "北海道", "cityCode": "01100", "cityName": "札幌市" }
{ "prefCode": "01", "prefName": "北海道", "cityCode": "01202", "cityName": "函館市" }
{ "prefCode": "01", "prefName": "北海道", "cityCode": "01203", "cityName": "小樽市" }
{ "prefCode": "01", "prefName": "北海道", "cityCode": "01204", "cityName": "旭川市" }
// ・・・省略・・・
{ "prefCode": "47", "prefName": "沖縄県", "cityCode": "47362", "cityName": "八重瀬町" }
{ "prefCode": "47", "prefName": "沖縄県", "cityCode": "47375", "cityName": "多良間村" }
{ "prefCode": "47", "prefName": "沖縄県", "cityCode": "47381", "cityName": "竹富町" }
{ "prefCode": "47", "prefName": "沖縄県", "cityCode": "47382", "cityName": "与那国町" }

できたー。

まとめ

都道府県のデータを取得するのって、簡単だろう」と思ったら、意外とちゃんとしたデータって落ちていないものですね。
皆さんどうやって作っているのだろうか・・・

新年早々Vue.jsのSPA/SSR/SSGを整理してみた話

はじめに

明けまして、おめでとうございます。
まだまだコロナで苦しい日々が続きますが、皆様頑張ってのりきりましょう(T T;;

さて、新年早々(ホントは年末から)、Vue.jsを使ってSSR、SSGで作るのってどうするんだ?と思って、色々調べてたので、調べたことを纏めて書いておこうと思います。
SPA、SSRの定義から曖昧な人なので、定義から確認していくこと・・・

SPA vs SSR

  • SPA(Single Page Application) ・・・ 1つのHTML内でコンテンツのみを切り替えるアプリケーションのこと。
  • SSR(erevr Side Rendering) ・・・ コンテンツ毎にサーバー側でHTMLを生成してブラウザに返す仕組みのこと。

SPAの場合、コンテンツの切り替えをブラウザ側のJavaScriptを使って行うため、必ずしもサーバーが必要というわけではありません。
SSRの場合、HTMLをサーバーサイドで生成する必要があるため、必ずサーバーが必要になってきます。

f:id:special-moucom:20210103001142p:plain

どうしてSSRが必要なのか?

SSRを使う理由の1つとして「SEO」が上げられます。

クローラーは、完全に描画されたページ(HTML)を直接解析しています。
そのため、SPAのようなJavaScriptを使ってコンテンツを切り替えているようなページ(HTML)は、クローラー側で正しくページを評価できない(正確には、JavaScriptを正しく実行して、切り替えたコンテンツを解析してくれているかわからない)という問題があります。

とはいえ、昨今ではクローラーの機能も向上しており、今後はSPAで作られたページ(HTML)でも正しく評価されるようになると思われます。
それまでは、「SEO」を重視するのであればSSRが必要となります。

もう1つの理由は、JavaScriptのダウンロード時間です。

SPAは、JavaScriptを使ってコンテンツを切り替えるため、JavaScriptの容量が大きくなりがちです。
そうなると、JavaScriptのダウンロードに時間が掛かり、クローラーの評価を落とす要因になります。

こういった観点からもSSRが必要になってくるケースは多いと思われます。

SSR vs SSG(プリレンダリング(事前描画))

Vue.jsでは、「SEO」目的でSSRするなら、SSG(プリレンダリング(事前描画))がオススメですよ。と書いてある。

SSR vs プリレンダリング (事前描画)

もしあなたが、幾つかのマーケティングのページの SEO を向上させるためだけに SSR を調べているとしたら (たとえば /, /about, /contact など)、代わりに プリレンダリング (事前描画) を使用することをオススメします。 
HTML を急いでコンパイルするために Web サーバーを使用するのではなく、プリレンダリングは、ビルド時に特定のルートに対して静的な HTML ファイルを生成します。
利点はプリレンダリングを設定する方が遥かに簡単で、フロントエンドを完全に静的なサイトとして保つことができることです。

プリレンダリングとは事前描画のことで、コンテンツ毎に必要なHTMLを事前に作成しておく仕組みのこと。つまり、Static Site Generator(SSG)。
JavaScriptを使ってコンテンツを切り替えるが、切り替えたコンテンツは事前に作成しておいたHTMLを表示しようというもの。
そのため、必ずしもサーバーが必要というわけではありません。

f:id:special-moucom:20210103001152p:plain

HTMLは事前に作成しているため、クローラーからも正しく評価されるし、JavaScriptの容量も大きくなり辛いので、ちょうどSPAとSSRの良いとこ取りみたいな感じですね。

SSRを再評価してみる

じゃあ、これからはSSRではなく、全部プリレンダリングで作れば良いじゃん・・・
とは、大きな声では言えませんがクローラーの機能も向上しているのでSSRのメリットが少なくなっているのは事実です。

SSRは、SPAやプリレンダリングでは得られない以下のようなメリットもあるので、要件によってどれを選択するかを決めたほうがよさそうです。

  • クライアントサイドでAPIを実行してHTMLを生成するよりも、サーバーサイドでAPIを実行してHTMLを生成して表示した方が、クローラー側で正しくページを評価してくれる。
  • コンテンツ表示までの時間を短縮できる。
  • コンテンツのアップロードの度にビルドをする必要がないので、大量ページを必要とする場合にむいている。

また、どうせ裏の仕組み(API等)を作る必要があるのなら、いっそのことSSRにするという選択肢もありますね。
その裏の仕組みもJavaScriptを使って作るアイソモーフィック(Isomorphic)ユニバーサル(Universal)といった単語も最近では良く聞きます。

アイソモーフィックJavaScript

アイソモーフィック(Isomorphic)とは、「同型の」という意味で、クライアントサイドとサーバサイドを同じ言語(JavaScript)を使って実装して、コードの共有を行うこと。 つまり、「JavaScriptを使ってSSRやりましょう」イコール「アイソモーフィックJavaScriptでやりましょう」ということ。

ユニバーサルJavaScript

ユニバーサル(Universal)とは、「全般の」という意味で、クライアントサイドとサーバサイドだけでなく、モバイル端末や組み込みデバイス上まで同じ言語(JavaScript)を使って実装して、コードの共有を行うこと。 アイソモーフィックJavaScriptの考え方をさらに広げようといった意味が込められている。

上記のように、SSRは「SSRで作るメリット」があるので、ケースバイケースでSPA、SSR、SSGを選択しましょう。

最後に

去年末のAdvカレンダーで、Vue.jsのSSGフレームワークGridsomeについて調べたことを書きました。

qiita.com

データソースをGraphQLを使ってアクセスできる面白いフレームワークなので、興味のある方は是非読んでみてください。

さて、個人的な観点で、SPA、SSR、SSGについて、整理してみました。
タイトルに「Vue.jsの」と書きましたが、「Vue.js」の内容はあまり含んでいなかったですね。(Vue.jsのWebサイトから抜粋してきたものが多いですが・・・)

2020年を振り返るのと2021年の抱負について

2020年を振り返りたいと思います。 コロナの影響で生活スタイルに大きな変化があり、そのあたりも踏まえて書いていきたいと思うので、「仕事・プライベート」と(いつもの)「技術」の2つの角度から、2020年を振り返ります。

2020年はどんな年だった?

仕事・プライベート

まずは、2020年の「仕事・プライベート」面から見ていきます。

在宅ワークがメインになった

コロナの影響により、会社のオフィスが縮小して、在宅ワークがメインになりました。会社への出勤時間が無くなったので、その時間は家事・育児に労力を回せるようになりました。 これには、個人的に良い点と悪い点がありました。箇条書きにすると以下の通り。

  • 良い点

    • 家族と一緒にいられる時間が増えた
    • 家事(掃除、洗濯)が捗る
  • 悪い点

    • 子供が保育園から帰ってくると仕事にならない
    • 仕事のオン・オフの切り替えが曖昧
    • (定時以降に行われる)カンファレンスやイベントに参加できない

このあたり、小さな子供持つエンジニアの方々のブログを拝見すると、皆さん同じような意見を持たれる方が多いようですね。
特に辛いのが「子供が保育園から帰ってくると仕事にならない」ところです。
私の場合、仕事で残業が発生するときは、仕事を一旦定時で切り上げ、続きを子供が寝静まった後にします。
これが続くと、時間があれば会社のPC開く習慣ができ、「仕事のオン・オフの切り替えが曖昧」になってしまうという問題があります。

私自身、在宅ワークに憧れを持っていましたが、いざ実践するとなるとなかなか上手くいかないもので、現在進行中で苦労しています。

健康に気を使うようになった

在宅ワークになってから、健康に気を使うようになりました。在宅ワークになってから4、5ヵ月たったある日、体重計にのってみると体重が4、5キロ増えていることに気づき、驚きました。

スマートウォッチを購入して、毎日の歩数を計測してみると、通勤していたときは10000歩以上歩いていましたが、在宅ワークのときは、4000歩以下であることがわかりました。

改めて運動不足であることがわかり、毎日4000歩は必ず歩く様、目標設定することにしました。

※購入したスマートウォッチは、以下。

また、家の椅子がそんなに良いものでは無かったために、腰と首を痛めました。 あまりにも痛かったので、整体(カラダファクトリー)で見てもらうと、いかに自分の姿勢が悪かったのかを指摘されました。

以降、デスク周りのレイアウト(モニターの位置)を調整したり、改めて椅子も購入することにしました。

※購入した椅子は、以下

ブログの更新が止まった

在宅ワークになってから、ブログの更新が停止しました。
理由は2つあり、1つは会社内でブログのようなものを書くようになったこと。もう1つは、私は普段、通勤時間を使ってブログの記事を考えたりしていたのですが、その時間が無くなったことです。

アウトプットが減ったというわけではないのですが、外へのアウトプットは減っているので、このあたりは今後改善を目指したいと考えています。

技術

続いて、2020年の「技術」面を見ていきます。

Laravelに挑戦できた

仕事でLaravelを扱うプロジェクトに参加して、Laravelに触れる機会を頂きました。 その結果、Laravelへの理解がすごく深まった気がしています。

私は以前から上司にLaravelに触れるお仕事がしたいと手を上げていました。 その結果、上司から「こんな仕事もあるけど・・・どう?」みたいな形で、お話があり、すんなりとプロジェクトへ参画することができました。

こういったこともあるので、普段から自分のやりたいことに対して声を上げておくことは、重要だと改めて思いました。

AWSの資格を再取得できた

年初の話ですが、AWSソリューションアーキテクト(アソシエイト)、AWSデベロッパー(アソシエイト)の2つを無事、獲得することができました。 このあたりは、以下記事に纏めています。

kodak.hatenablog.com

WordPressのカスタムブロックを作った

仕事でWordPressを扱う案件で、カスタムブロックを作るお仕事を頂きました。 当時、Gutenbergの情報は少なかったので、作るのに大変苦労しましたが、Reactの理解も深まり、なによりPHPの世界からいきなりJavaScriptの世界に飛び込んだこともあって、作っていてとても楽しかったです。

qiita.com

オブジェクト指向デザインパターンを理解できた

改めて、オブジェクト指向デザインパターンリファクタリングを理解しました。 今までは「概念」だけはわかる状態で置いていたもの達を、ちゃんとした本を読むことで、「概念」から「理解」へ進めることができました。

github.com

kodak.hatenablog.com

2021年はどんな年にしたいか?

仕事・プライベート

さて、続いて2021年をどんな年にしていきたいか?「仕事・プライベート」面から見ていきます。

断捨離とスタイリッシュな仕事空間作り

家にいる時間が増えたので、自分の居場所を快適にしていきたいと思います。 在宅になって、いつでも配達を受け取れるようになったので、捨てようと思っていたPCや粗大ゴミが一気に片付きます。メルカリもやり易くなりました。 これによってストレスも解消されるので、断捨離とスタイリッシュな仕事空間作りを目指したいと思います!

老後について考える

35を過ぎたので、そろそろ老後について考えなくてはならない歳・・・ 「老後2000万円問題」なんてものもありましたね。老後に向けた貯蓄について、少しづつですが考えていきたいと思います。

ブログの継続

継続って難しいですよね。はい。私が一番苦手な分野です。 子供にも言っています。「いきなりはできないよ。何事も、ちょっとづつやることで、できるようになるんだよ。だから、毎日ちょっとづつやろうね。」と・・・ そのちょっとづつが難しいんですよね。 子供に言っているだけに、自分がお手本にならなくてはいけませんね

技術

最後に、2021年をどんな年にしていきたいか?「技術」面を見ていきます。

JavaScriptを得意な言語にする

うちの会社では最近、JavaScriptやTypeScriptができる人を増やしていこう。という目標を立てています。 バックエンドとフロントエンド共に、JavaScriptやTypeScriptでできていれば、コードの流用やフロントエンドの人でもバックエンドのコードが読める等、いろいろとメリットが多いからだそうです。 いわゆる、ユニバーサルやアイソモーフィック的な考え方ですね。

これには、私なりに色々と思うところはありますが、JavaScriptとTypeScriptは理解していないと積むことが多いので、まずは、ちょっとデキルからちゃんとデキルくらいにはなっていきたいと思います。

テストコードが書けるようになる

プログラムを作り終えた後に、テストコードを書くのにはだいぶ慣れてきた気がします。ですが、プログラムを作る時にテストコードを書く(つまり、テスト駆動開発)は、まだできていません。
なので、来年はテストコードをたくさん書いて、テスト駆動開発ができるようになる!そんな年にしたいと思います。

まとめ

2020年はコロナの影響で、大きく色々なことが変化を受けた年でした。 2021年はその変化の対応していく・・・そんな年になりそうです。(= = ;;