ChatGPTで質問するときの工夫

最近では、プロンプトエンジニアリングという言葉は一部の界隈でしか聞かなくなりましたが、ChatGPTを活用した仕事術という形で結構広まってきました。

メリットもあればデメリットもあるため、業務での使用を禁止している企業の話題も出てきています。

使い方を間違えなければ強い味方になるのは間違いないので、今回は自分の知らないことを検索して調べるのではなく、ChatGPTに聞いてみます。

とはいえ、合っているかどうかわからないと正しさを検証できないので、ここでは「フェーン現象」について質問してみました。

普通に聞いてみる

まずは普通に聞いてみましょう。GPT 4も使えたのですが、使えない方もいると思いますので、GPT 3.5で質問しています。

Q:フェーン現象について教えてください。

A:フェーン現象(フェーンげんしょう)は、風が山脈を越える際に発生する気象現象の一種です。この現象は、風が山脈の風下側(山を背にして吹く側)に吹く際に、急激な温度上昇や乾燥化が起こることを特徴としています。以下にフェーン現象の主な要因と影響を説明します。

フェーン現象の主な要因:
1.風上側の山脈:風が山脈を越える際、山を登る際には空気が上昇し、冷却されていきます。しかし、山を下る際には空気が急速に下降し、圧縮されます。この圧縮過程により、空気が急激に温まる現象が発生します。
2.エンタルピーの変化:風が山を上る際には、山を登ることに伴ってエネルギーが消費されるため、気温が低下します。一方、山を下る際には、エネルギーが放出されるため気温が上昇します。これによって、風下側での温度上昇が起こるのです。

フェーン現象の影響:
1.温暖化と乾燥化:フェーン風が吹くことにより、空気が急激に温まるため、気温が上昇します。このため、風下側の地域では急激な温暖化が起こります。また、フェーン風は湿度も低下させるため、風下側の地域は乾燥化することがあります。
2.気象条件の変化:フェーン風は局地的な気象条件を変化させることがあります。たとえば、雲の消失や雨の停止などが起こることがあります。
3.風速の変化:フェーン風はしばしば風速を増加させることがあります。これは、山脈を下る空気が圧縮されて加速するためです。
4.地域差:フェーン現象は地域によって異なる影響を持ちます。山脈や地形、風の方向などによってフェーン風の特性が変わり、影響も異なる場合があります。

フェーン現象は、気象学や地形に関する興味深い現象であり、その影響は風下側の生態系や農業、気象予測にも影響を与えることがあります。

内容が正しいとかの前に読む気にならないですよね。制約条件を追加することで文章量自体を減らすことは可能ですが、そもそもの文章が硬いので、ぱっと見て頭に入ってきません。

制約条件を設定する – 読者の年齢層を指定する

制約条件を追加したわかりやすくしましょう。ここでは12歳以下でもわかるようにすることを制約条件として追加します。

Q:フェーン現象について教えてください。
# 制約条件
・12歳以下にもわかる内容にする

A:もちろんです!フェーン現象は、風の影響で起こる天気の変化を説明する現象です。

まず、山や山脈の向こう側に風が吹くと、風は山を越えるときに上昇します。このとき、風は山を迂回するように上昇し、山の上に達すると冷えて湿度が高まります。そして、山の反対側に下降するとき、空気は山を越える前よりも乾燥して暖かくなります。
この暖かく乾燥した空気は、山を越えた向こう側で風として吹き出し、空気中の湿度が低いため雲が発生しにくく、晴れた日が続くことがあります。また、この現象によって気温が急激に上昇することもあります。

つまり、フェーン現象は、山を越えた風が上昇・冷却・乾燥し、山の反対側で下降・温暖・乾燥することで、天気の変化を引き起こす現象なんですよ!

文章量が減りつつ、内容も簡単になっていますね。まったく知らない用語や事柄を調べる場合、いきなり理論的なことを書かれても頭に入ってこないので、低年齢の方に向けて書いてもらうことでわかりやすい内容で書いてくれるようになります。

制約条件を設定する – ポイントを強調する

もう一つ制約条件を追加してみましょう。文章を最初から読むのはつらいので、ポイントとなる箇所を太字にしてもらいます。

Q:フェーン現象について教えてください。
# 制約条件
・12歳以下にもわかる内容にする
・ポイントとなる部分を太字にする

A:もちろんです!フェーン現象は、風が山を越えるときに起こる特別な気象のことを指します。これをわかりやすく説明しましょう。

まず、山と風の関係を考えてみましょう。山は高いところですよね。風が山に当たると、風は山を登っていくのですが、山を越えるときには面白いことがおこるのです。
フェーン現象では、風が山を登るときに山の風が強く吹き、山の向こう側(風の吹かない側)に下りるときに風が温かく乾燥した状態に変わるのです。
具体的に説明すると、風が山を登るとき、山の斜面を登るにつれて空気が上昇します。そして、上昇すると、空気は高いところで冷たくなります。その冷たい空気が山のてっぺんを越えると、山の向こう側へ降りていくとき、空気が下りながら温まるんです。

これがフェーン現象の大まかな仕組みです。フェーン現象が起こると、山を越えた向こう側では風が暖かくて乾燥しているため、雨が降りにくくなります。そのため、フェーン現象が起こる地域では、雨が少ないことがよくあります。

簡単にまとめると、風が山を登っていくときに冷たくなり、山を越えて下りてくるときに温かくなる現象がフェーン現象です。これによって、山を越えた向こう側では晴れていることが多いですよ!

最後の太字部分がポイントでここを読んでから他の箇所を読むと理解しやすいです。

内容を深掘りする

概要を理解したところで、追加の質問をして理解を深めましょう。ここでは、空気が高いところで冷たくなる理由を聞いてみます。

Q:「空気は高いところで冷たくなります」とありますが、空気が高いところで冷やされる理由を教えてください。
# 制約条件
・12歳以下にもわかる内容にする
・ポイントとなる部分を太字にする

A:もちろんです!空気が高いところで冷やされる理由を教えますね。

空気は実は、高いところに行くほど薄くなっているんです。イメージすると、山を登るとどんどん空気が薄くなるような感じです。そして、薄い空気は温度を持ちにくい性質があるんです。
こう考えると、低いところにある空気は分子同士がたくさん集まっていて、一緒に熱を持っています。でも高いところに行くと、空気の分子が少なくなるので、熱を持ちにくくなるんです。
たとえば、山を登ると、頂上に行くにつれて空気が薄くなるので、その空気は冷たく感じることがあります。これと同じように、空気が高いところにあると、分子同士のぶつかり合いも少なくなって、熱を逃がしやすくなるんです。だから、空気が高いところで冷えてしまうんですね。

要するに、空気は高いところほど薄くて、分子同士が離れているので熱を持ちにくく、それが高いところでの冷たさの理由なんです!

太字にするのはうまくいきませんでしたが、内容の深掘りはできています。回答が気に入らなければRegenerateしてください。

まとめ

ChatGPTに質問をする場合、制約条件を追加することで制約条件にあった回答を得られるようになります。

今回試した、読者の年齢層の指定やポイントとなる箇所を太字にするなどすると、回答を理解しやすくなります。常に読者の年齢層を下げて質問できるかというと場合によりますが、年齢層の指定や概要のみを回答してもらうことで、理解しやすくなります。概要を理解したところで、詳細を一つずつ聞く方が理解しやすいだけでなく、適切な回答を得られやすくなります。

ChatGPTに質問するやり方は検索エンジンで検索するよりもずっと早く答えにたどり着くことができます。ある程度概要を理解したり、関連する用語などを把握してからそれらの情報を元に検索エンジンで調べた方が、検索の精度が上がりますし、情報の精査もしやすいです。

適切に使用することで効率的に情報収集することができます。

リアクティブプログラミング

Angularのサービスの説明をする前にリアクティブプログラミングについて説明します。

リアクティブプログラミングとは

リアクティブプログラミングは、データの流れ(ストリーム)に着目し、データを受け取るたびに処理を処理するプログラミングパラダイムです。

リアクティブプログラミングは、用語が独特で概念の通常のプログラミングとは少し異なっているため、理解しづらい場合があります。また、実装系によっても違いがあります。今回は一般的なリアクティブプログラミングについて説明し、次回RxJSについて説明します。

リアクティブプログラミングの用語

リアクティブプログラミングには多くの用語があり、異なるライブラリやフレームワークによって少し異なる言い回しがされることもあります。しかし、以下はリアクティブプログラミングのコンセプトを理解する上で基本となる用語のリストです。これらの用語は、多くのリアクティブプログラミングの文脈で一般的に使用されています。

発行者(Publisher)

生産者(Producer)やオブザーバブル(Observable)とも呼ばれます。データやイベントを生み出し、それをストリームとして発行するエンティティです。発行者は、一つまたは複数の購読者にデータを非同期的に供給する役割を果たします。

購読者(Subscriber)

消費者(Consumer)やオブザーバー(Observer)とも呼ばれます。発行者からのデータやイベントを受け取り、それを処理するエンティティです。購読者は、発行者からデータを受け取ることを購読する(subscribe)と言われ、この購読関係を通じてデータが非同期的に供給されます。

操作(Operators)

購読者が購読しているデータストリームからデータを受け取る際に適用する操作です。フィルタ(filter)やマップ(map)など様々な操作があります。

バックプレッシャー(Back pressure)

データの発行速度と購読速度の不均衡を管理する概念。発行側が高速で、購読側がそれに追いつけない場合に対処するための機構を提供します。

ColdとHot

  • Cold:購読が開始されるまでデータを発行しない
  • Hot:購読の有無に関係なくデータを発行する

リアクティブプログラミングの概念

リアクティブプログラミングの概念について説明します。

発行者はデータを発行し、データストリームを形成します。データの発行は一般に非同期で行われますが、同期で行われることもあります。

購読者は発行者を購読し、データを待ち受けます。データを受け取るたびに処理を実行します。ただし、すべてのデータを処理するわけではなく、Operatorsによって受け取るかどうかをフィルタすることもできます。

購読者が処理できる量には限界があります。発行者がデータを発行するペースよりも購読者が処理できるペースの方が遅い場合、発行済み未処理のデータをどうするかという問題がでてきます。購読者の処理能力を発行者の発行能力が上回る状態に対する対処を行う機構をバックプレッシャーと言います。具体的な対処方法としては、

  • 購読者が購読可能なデータ量を発行者に伝え、発行者はそれに合わせてデータを発行する
  • 発行者の発行しているデータをバッファリングする
  • 受け取れないデータは捨てる

などがあります。どの戦略が適しているかはデータの性格(失われると困るのか、多少失われても問題ないのか)やデータ発行の処理(購読者の要求に応じて発行できるのか、とりあえず生成し続けるしかないのか)によって変わります。

リアクティブプログラミングのユースケース

リアクティブプログラミングを適用できるユースケースをいくつか挙げます。

  1. リアルタイムデータのストリーミング:フィンテック、株価のストリーミングや、ライブスポーツのスコア更新など
  2. リアルタイムのダッシュボード:ユーザーアクティビティ、システムメトリクス、eコマースの販売統計など、リアルタイムで更新する必要があるダッシュボード
  3. チャットアプリケーション:ユーザー間でリアルタイムにメッセージを交換するアプリ
  4. オンラインゲーム:特にマルチプレイヤーオンラインゲームで、プレイヤー間のリアルタイムのインタラクションが必要
  5. イベント駆動のマイクロサービスアーキテクチャ:サービスが非同期的にデータを交換する
  6. 非同期なバッチ処理:大量のデータを効率的に処理する必要がある場面で、特にデータの供給速度が不均一な場合
  7. リアルタイムの通知システム:ユーザーにリアルタイムで通知やアラートを提供するシステム
  8. センサーデータの処理:IoTデバイスからのリアルタイムデータを効率的に収集、分析、反応する
  9. 動的なネットワーク応答:ユーザーのリクエストに応じて、非同期的にコンテンツを動的に生成・配信するウェブサービス
  10. リソース制限のある環境:例えばモバイルデバイスや組み込みシステムなど、リソースが制限されている環境での非同期処理やリアルタイム処理

最近ではWebアプリケーションで使用することが多いため、サーバーが発行者、クライアントが購読者という組み合わせや画面のコンポーネント間での使用をイメージしやすいですが、サービス間でのやりとりや非同期バッチなどに使用することもあります。

まとめ

サービスを作成しようとするとRxJSを使うことになるため、先にリアクティブプログラミングについて説明しました。次回はRxJSについて説明します。

Angularでモジュール(NgModule)を作成する

前回作成したコンポーネントがAppModuleに登録されているので、SharedModuleを作成してそちらに登録するように変更します。

これは後からモジュールを作成するパターンになりますが、一般的には先にモジュールを作成する方が多いため、今後使用するUserModuleも作成しておきます。

モジュールとは

Angularのモジュールは、アプリケーションの一部を構成し、関連するコンポーネント、ディレクティブ、パイプ、サービスなどをグループ化するコンテナです。モジュールはアプリケーションの機能を整理し、再利用しやすくする役割を果たします。

特に、Angularのモジュールは@NgModuleデコレータを使用して定義され、以下の主要なメタデータプロパティを持ちます:

  • declarations: モジュール内で使用されるコンポーネント、ディレクティブ、パイプを宣言します。
  • imports: 他のモジュールがエクスポートしたクラスを使用できるように、このモジュールが他のモジュールに依存していることを指定します。
  • exports: 他のモジュールで使用できるように、宣言されたクラスの一部をエクスポートします。
  • providers: アプリケーション全体で使用できるサービスのプロバイダーを登録します。
  • bootstrap: アプリケーションの起点となるルートコンポーネントを指定します。

モジュールを作成する

モジュールを作成するにはng generate moduleコマンドを使用します。

--dry-runオプション

モジュールを作成する前に--dry-runオプションについて確認しておきましょう。

--dry-runオプションはコマンドを実行せずに、コマンドで作成・更新される内容を確認できるオプションです。前回のようにコンポーネントを指定する際に、appから指定すればいいのかappの下から指定すればいいのか迷うことがあると思います。このような場合には、--dry-runオプションでどんなファイルが作成されてどんなファイルが更新されるかを確認するとよいでしょう。

$ ng generate module shared --dry-run
CREATE src/app/shared/shared.module.ts (192 bytes)

NOTE: The "--dry-run" option means no changes were made.

実際に実行してみると、作成されるモジュールのパスと名前が確認でき、「NOTE: The “–dry-run” option means no changes were made.」と変更がなかった旨のメッセージ表示されています。

SharedModuleはルーティングを必要としない共通コンポーネント・サービス等をまとめたモジュールですので、--routingオプションなしで実行します。

では、--dry-runなしで実行しましょう。

$ ng generate module shared
CREATE src/app/shared/shared.module.ts (192 bytes)

気づいたかもしれませんが、コンポーネントのときとは違ってモジュールクラスのファイルが作成された以外に更新されたファイルなどはありません。すなわち、作成したモジュールが自動的にどこかのモジュールに自動登録されることはありません。

まずはSharedModuleを修正します。まずは修正前の状態を確認しておきましょう。

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';



@NgModule({
  declarations: [],
  imports: [
    CommonModule
  ]
})
export class SharedModule { }

前回作成したTitleComponentを追加していきます。モジュールに属するコンポーネント、ディレクティブ、パイプはdeclarationsに記載します。加えて、他のモジュールに公開するためにexportsに記載します。

修正後のモジュールが以下のようになります。

import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { TitleComponent } from './components/title/title.component';

@NgModule({
  declarations: [TitleComponent],
  imports: [CommonModule],
  exports: [TitleComponent],
})
export class SharedModule {}

次にAppModuleを修正します。これまではTitleComponentapp.component.htmlで使用するためにAppModuleに登録していました。

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { TitleComponent } from './shared/components/title/title.component';

@NgModule({
  declarations: [AppComponent, TitleComponent],
  imports: [BrowserModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

今後は、SharedModuleをインポートすることでTitleComponentを使用できるようにします。まずはdeclarationsからTitleComponentを削除し、その代わりにimportsTitleComponentが登録されているSharedModuleを追加します。

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { SharedModule } from './shared/shared.module';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, SharedModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

実際に動作するか確認してみましょう。http://localhost:4200/にアクセスし、これまでどおり

のように表示されれば正しく修正できています。

UserModuleを作成する

次に今後使用するUserModuleを作成します。

モジュール名は単数形か複数形か

結論からいうと、チームによって異なります。これは個人的な見解となりますが、単数形と複数形で意味が異なる単語もあるため、単数形で書くことを基本にして複数形の意味を使いたい場合は複数形を使用する、というのがよいのではないかと考えています。

モジュールを作成する

ではUserModuleを作成します。今回はルーティングを伴うモジュールとするため、--routingオプションを使用します。また、今回は省略形であるng g mコマンドで実行します。--dry-runオプションをつけて確認しましょう。

$ ng g m user --routing --dry-run
CREATE src/app/user/user-routing.module.ts (247 bytes)
CREATE src/app/user/user.module.ts (272 bytes)

NOTE: The "--dry-run" option means no changes were made.

appディレクトリの下にuserディレクトリが作成され、user.module.tsが作成されます。加えて、ルーティングモジュールであるuser-routing.module.tsも作成されることが確認できます。

期待したとおりに作成されることを確認できたので、--dry-runオプションを外して実行します。

$ ng g m user --routing
CREATE src/app/user/user-routing.module.ts (247 bytes)
CREATE src/app/user/user.module.ts (272 bytes)

UserModuleは現時点ではコンポーネントなど何も属していないのでこのままにしておきます。とりあえず作成したコードだけ確認しておきます。

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { UserRoutingModule } from './user-routing.module';


@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    UserRoutingModule
  ]
})
export class UserModule { }
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class UserRoutingModule { }

これまでに作成したモジュールとは異なり、user-routing.module.tsにはroutesという定数があります。使い方については今後確認していきます。

変更をコミット&プッシュする

毎度のことですが、ここまでの変更をコミット&プッシュします。

$ git add . 
$ git commit -m 'create SharedModule and UserModule'
✔ Preparing lint-staged...
✔ Running tasks for staged files...
✔ Applying modifications from tasks...
✔ Cleaning up temporary files...
[main ed5311a] create SharedModule and UserModule
 4 files changed, 33 insertions(+), 3 deletions(-)
 create mode 100644 src/app/shared/shared.module.ts
 create mode 100644 src/app/user/user-routing.module.ts
 create mode 100644 src/app/user/user.module.ts
$ git push
Enumerating objects: 15, done.
Counting objects: 100% (15/15), done.
Delta compression using up to 10 threads
Compressing objects: 100% (10/10), done.
Writing objects: 100% (10/10), 1.12 KiB | 1.12 MiB/s, done.
Total 10 (delta 5), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (5/5), completed with 4 local objects.
To github.com:t0k0sh1/angular-tutorial.git
   90cd6c9..ed5311a  main -> main

まとめ

今回はSharedModuleを作成し、TitleComponentAppModuleからSharedModuleに移しました。今後は共通コンポーネントはSharedModuleに登録していくことにします。

また、今後に備えてルーティング設定をもったUserModuleも作成しました。こちらは今後使用していきます。

次回は画面にメッセージを表示するコンポーネントの作成を通じてサービスを使ったコンポーネント間のデータの受け渡しについて見ていきます。

AngularプロジェクトにTailwind CSSを導入する

今回はTailwind CSSを導入し、簡単なコンポーネントを作成します。

Tailwind CSSとは


Tailwind CSSは、高度にカスタマイズ可能なユーティリティファーストのCSSフレームワークです。ユーティリティファーストとは、小さい単位(ユーティリティ)のクラスを組み合わせてデザインを構築するアプローチのことで、そのための多くのプリセットクラスが提供されています。

特徴

  1. ユーティリティファースト: HTMLの中で直接、スタイリングができるため、CSSファイルを跨いで行ったり来たりする必要がありません。
  2. カスタマイズ可能: プロジェクトの特定のデザイン要件に合わせて、設定ファイルを使って色、サイズ、余白などをカスタマイズすることが可能です。
  3. レスポンシブデザイン: レスポンシブデザインのプレフィックスが提供されているため、さまざまなデバイスサイズでの表示を容易にコントロールできます。
  4. プラグインシステム: カスタムユーティリティやコンポーネントを簡単に追加するためのプラグインシステムも提供しています。

Angularでのサポート状況

Angular v11.2から公式にサポートされています。

Tailwind CSSを導入する

では、AngularプロジェクトにTailwind CSSを導入していきます。基本的には公式の手順に従って進めます。

$ npm install -D tailwindcss postcss autoprefixer

added 26 packages, changed 1 package, and audited 1161 packages in 3s

186 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
$ npx tailwindcss init

Created Tailwind CSS config file: tailwind.config.js

必要なパッケージをインスト−ルし、tailwind.config.jsが作成されます。

続いてtailwind.config.jsを以下のように変更します。

/** @type {import('tailwindcss').Config} */
module.exports = {
- content: [],
+ content: [
+   "./src/**/*.{html,ts}",
+ ],
  theme: {
    extend: {},
  },
  plugins: [],
}

修正後のtailwind.config.jsは以下のようになります。

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.{html,ts}'],
  theme: {
    extend: {},
  },
  plugins: [],
};

続いてsrc/styles.cssを以下のように編集します。

/* You can add global styles to this file, and also import other style files */
@tailwind base;
@tailwind components;
@tailwind utilities;

元々あるコメントは追加しても削除してもどちらでも構いません。

ここまででインストールは完了です。実際に使用可能か動作確認をしてみます。

まずは開発サーバーを起動します。

$ ng serve
✔ Browser application bundle generation complete.

Initial Chunk Files   | Names         |  Raw Size
vendor.js             | vendor        |   1.98 MB | 
polyfills.js          | polyfills     | 333.16 kB | 
styles.css, styles.js | styles        | 242.41 kB | 
main.js               | main          |  46.29 kB | 
runtime.js            | runtime       |   6.53 kB | 

                      | Initial Total |   2.59 MB

Build at: 2023-08-06T07:20:58.691Z - Hash: 34fc5b5c11ac8fa2 - Time: 4089ms

** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **


✔ Compiled successfully.

ng serveコマンドは開発サーバーを起動するコマンドです。デフォルトでは4200ポートを使用します。

動作確認をするために、src/app/app.component.htmlを以下のように書き換えます。

<h1 class="text-3xl font-bold underline">Hello world!</h1>

元々ある大量のコードはすべて削除しています。

変更保存をするとすぐに反映されます。では、http://localhost:4200/にアクセスし、以下のように表示されればTailwind CSSが有効になっています。

Tailwind CSSを便利にする拡張機能をインストールする

Tailwind CSSでのプログラミングを便利にする拡張機能をいくつかご紹介します。

Tailwind CSS IntelliSense

Tailwind CSSのオートコンプリート、リント、class属性で設定されるスタイルのプレビューなどの機能を提供する拡張機能です。

Tailwind Fold

Tailwind CSSを使用するとclass属性が長くなりがちです。これはコード全体の可読性低下にもつながるため、この拡張機能で普段は省略表示しておくとよいでしょう。

Tailwind Documentation

Tailwind CSSのドキュメントにすばやくアクセスするための拡張機能です。最近ではチートシートサイトがいくつもりますので、そちらの方が使いやすければこの拡張機能は特に使う必要はありません。

コンポーネントを作成する

動作確認は行っていますが、実際にコンポーネントを作成してみましょう。

ここで作成するコンポーネントは先ほど動作確認のために作成したタグをコンポーネント化してみましょう。

コンポーネントのひな形を自動生成する

ng generateコマンドを使ってコンポーネントのひな形を作成します。コンポーネントを作成する場合はcomponentを指定して、その後に生成するコンポーネントのパスを書きます。

$ ng generate component shared/components/title
CREATE src/app/shared/components/title/title.component.css (0 bytes)
CREATE src/app/shared/components/title/title.component.html (20 bytes)
CREATE src/app/shared/components/title/title.component.spec.ts (552 bytes)
CREATE src/app/shared/components/title/title.component.ts (198 bytes)
UPDATE src/app/app.module.ts (390 bytes)

コマンドは省略形もありますが、今回は省略せずに書きました。また、コンポーネントのパスにはsrc/app/を含まない点に注意してください。

コンポーネントを作成する

このコンポーネントは呼び出し元からテキスト(先ほど作成したタグのHello world!の部分)を受け取って表示するコンポーネントにします。

まずはtitle.component.tsから作成します。自動生成したコードは次のようになっています。

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

@Component({
  selector: 'app-title',
  templateUrl: './title.component.html',
  styleUrls: ['./title.component.css']
})
export class TitleComponent {

}

詳しくは別途解説しますが、コンポーネントを使用する側から何か値を受け取るには@Inputディレクティブを使用します。

修正後のtitle.component.tsは以下のとおりです。

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

@Component({
  selector: 'app-title',
  templateUrl: './title.component.html',
  styleUrls: ['./title.component.css'],
})
export class TitleComponent {
  @Input() title: string;

  constructor() {
    this.title = '';
  }
}

titleというメンバ変数に@Input()を付与し、呼び出し元から受け取れるようにしています。

次にtitle.component.htmlを作成します。

<h1 class="text-3xl font-bold underline">{{ title }}</h1>

先ほどTitleComponentに追加したtitleを表示しています。

では実際に使ってみましょう。app.component.htmlを以下のように修正します。

<app-title title="{{ title }}" />

app-titleコンポーネントのtitle属性にAppComponenttitleを設定します。

AppComponenttitleメンバに設定されている文言が表示されれば期待したとおりの動作になっています。

テストを行う

最後にテストを行いましょう。

今回作成したTestComponentとHTMLを変更したAppComponentの両方を修正します。

まずはTestComponentのテストです。

import { ComponentFixture, TestBed } from '@angular/core/testing';

import { TitleComponent } from './title.component';

describe('TitleComponent', () => {
  let component: TitleComponent;
  let fixture: ComponentFixture<TitleComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [TitleComponent],
    });
    fixture = TestBed.createComponent(TitleComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create the title component', () => {
    expect(component).toBeTruthy();
  });

  it('should have as title an empty string', () => {
    expect(component.title).toEqual('');
  });

  it('should display the title', () => {
    const expected = 'Test title';
    component.title = expected;
    fixture.detectChanges();

    const compiled = fixture.nativeElement;
    expect(compiled.querySelector('h1').textContent).toContain(expected);
  });
});

次にAppComponentのテストです。

import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { TitleComponent } from './shared/components/title/title.component';

describe('AppComponent', () => {
  beforeEach(() =>
    TestBed.configureTestingModule({
      declarations: [AppComponent, TitleComponent],
    }),
  );

  it('should create the app', () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.componentInstance;
    expect(app).toBeTruthy();
  });

  it(`should have as title 'angular-tutorial'`, () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.componentInstance;
    expect(app.title).toEqual('angular-tutorial');
  });

  it('should render title', () => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();
    const compiled = fixture.nativeElement as HTMLElement;
    expect(compiled.querySelector('app-title h1')?.textContent).toContain('angular-tutorial');
  });
});

テストの実行はng testコマンドを使用します。

$ ng test
✔ Browser application bundle generation complete.
06 08 2023 20:16:25.133:WARN [karma]: No captured browser, open http://localhost:9876/
06 08 2023 20:16:25.140:INFO [karma-server]: Karma v6.4.2 server started at http://localhost:9876/
06 08 2023 20:16:25.140:INFO [launcher]: Launching browsers Chrome with concurrency unlimited
06 08 2023 20:16:25.141:INFO [launcher]: Starting browser Chrome
06 08 2023 20:16:25.984:INFO [Chrome 115.0.0.0 (Mac OS 10.15.7)]: Connected on socket A-bmpclSUPRNDYqsAAAB with id 34000686
Chrome 115.0.0.0 (Mac OS 10.15.7): Executed 6 of 6 SUCCESS (0.035 secs / 0.024 secs)
TOTAL: 6 SUCCESS

テストではブラウザが開き結果が表示されます。

テストは起動しっぱなしにしておいて、コードを変更すればリロードされます。

コミットとプッシュを行う

ここまでの変更をコミット、プッシュします。

$ git add .
$ git commit -m 'add tailwind css'
✔ Preparing lint-staged...
✔ Running tasks for staged files...
✔ Applying modifications from tasks...
✔ Cleaning up temporary files...
[main 90cd6c9] add tailwind css
 11 files changed, 428 insertions(+), 633 deletions(-)
 create mode 100644 src/app/shared/components/title/title.component.css
 create mode 100644 src/app/shared/components/title/title.component.html
 create mode 100644 src/app/shared/components/title/title.component.spec.ts
 create mode 100644 src/app/shared/components/title/title.component.ts
 create mode 100644 tailwind.config.js
$ git push
Enumerating objects: 26, done.
Counting objects: 100% (26/26), done.
Delta compression using up to 10 threads
Compressing objects: 100% (14/14), done.
Writing objects: 100% (17/17), 5.50 KiB | 1.10 MiB/s, done.
Total 17 (delta 6), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (6/6), completed with 6 local objects.
To github.com:t0k0sh1/angular-tutorial.git
   58ad1f8..90cd6c9  main -> main

まとめ

今回はTailwind CSSを導入し、コンポーネントを作成しました。

今回sharedにTitleComponentを作成したのですが、このコンポーネントはAppModuleに含まれています。AppModuleにすべてのコンポーネントを含めるのではなく、いくつかのモジュールに分けた方が管理しやすいです。

次回はSharedModuleを作成してTitleComponentを含めるように変更する手順を解説します。

AngularプロジェクトにHuskyを導入する

今回、Huskyを導入しますが、これにはトレードオフ(メリットとデメリット)があります。

メリット:

  1. コードの品質を保つ: Huskyはコードがリポジトリにコミットされる前に自動的にリントやテストを行うことができます。これにより、間違ったコードやコーディング規約に適合しないコードがマージされるのを防ぐことができます。
  2. 自動化: Huskyを設定することで、手動でリントやテストを行う手間を省くことができます。
  3. チームの一貫性: Huskyをプロジェクトに組み込むことで、全ての開発者が同じリントやテストを実行することを保証することができます。

デメリット:

  1. 初期設定の複雑さ: Huskyの設定は少々複雑であり、最初に設定する際には時間と労力が必要となります。
  2. 間違った設定による問題: Huskyが間違って設定された場合、全く関係のないファイルがコミットの対象になってしまったり、正常なコードでもコミットできなくなる可能性があります。
  3. コミットの遅延: もし大きなプロジェクトでテストが時間を要する場合、Huskyによりコミットが遅延する可能性があります。ただし、これは通常はリントやテストの速度問題であり、Husky自体の問題ではありません。

最も注意しなければならないのは、修正した箇所とは関係ない箇所でリントエラーが発生してコミットができないという自体が起こる可能性があるか、という点です。特に途中から導入する場合はリントエラーだらけでその対応に追われるということがあります。

リントエラーが解消したコードがコミットされることが理想的ではありますが、時と場合によってはそれよりもコミットすることが優先事項であることもありますし、プロジェクトの事情で修正箇所以外のエラーは一旦見なかったことにしたいということもあると思います。

また、コードベースが非常に大きく、リントに時間がかかってコミットがストレスになるというリスクもあります。こちらについては対策がありますので、併せてご紹介します。

Huskyとは

Huskyは、JavaScriptプロジェクトにおいてGitのフック(特定のGitイベントが発生した時に実行されるスクリプト)を簡単に管理できるようにするツールです。

Gitフックは、コミット、プッシュなどのGit操作の前後に自動的に実行されるスクリプトです。これを活用することで、コードの品質を一定に保つためのチェックやテストを自動化したり、特定の操作を制限したりすることが可能になります。

しかし、GitフックはGitリポジトリごとに設定され、通常はGitリポジトリ自体と一緒にバージョン管理されません。これは、他の開発者が同じフックを設定するのを難しくするだけでなく、フックの変更を追跡するのも困難にします。

Huskyはこれらの問題を解決します。Huskyを使用すると、フックはプロジェクトの一部としてバージョン管理でき、設定はすべての開発者間で共有できます。また、Huskyの設定は通常、プロジェクトのpackage.jsonファイルに記述され、設定の変更もGitを通じて追跡することができます。

このように、HuskyはJavaScriptプロジェクトにおけるGitフックの管理を効率化し、プロジェクト全体のコード品質を向上させる強力なツールとなることができます。

Huskyのインストール

まずはじめにHuskyをインストールします。

$ npm install husky --save-dev

added 1 package, and audited 1098 packages in 1s

165 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

インストール自体に難しいところはありません。

pre-commitフックを設定する

では早速pre-commitフックを設定しましょう。

$ npx husky install
husky - Git hooks installed

上記のコマンドを実行すると.huskyディレクトリが作成されます。

次にpre-commitフックを追加します。実行するコマンドは前回設定したnpm run lintにします。

$ npx husky add .husky/pre-commit "npm run lint"
husky - created .husky/pre-commit

では実際に実行してみましょう。Huskyをインストールしたときの変更のコミットがまだなので、これをコミットします。

$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   package-lock.json
        modified:   package.json

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        .husky/

no changes added to commit (use "git add" and/or "git commit -a")
$ git add .  
$ git commit -m 'install husky'

> angular-tutorial@0.0.0 lint
> ng lint


Linting "angular-tutorial"...

All files pass linting.

[main e0c8f78] install husky
 3 files changed, 21 insertions(+)
 create mode 100755 .husky/pre-commit

コミット時にリントが実行されていることが確認できます。

pre-commitフックでフォーマットを行う

次にpre-commitフックでPrettierを実行するようにしてみましょう。ついでに動作を確認もしてみます。

まず、Huskyのコマンドにはpre-commitフックを削除するコマンドがありません。では変更したい場合はどのようにするかを見ていきます。

現在の状態を確認する

まず、現在の状態はnpm run lintを設定してある状態です。

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run lint

ここに新たなコマンドを追加する場合は変わらずnpx husky addコマンドを使用します。

$ npx husky add .husky/pre-commit "npm run format"
husky - updated .husky/pre-commit

npx husky addコマンドを実行することで現在設定されているコマンドに加えて新たなコマンドがpre-commitフックに追加されます。

次にnpm run formatを削除してnpm run lintだけの状態にしてみます。これを実現するには、npx husky setコマンドを使用してnpm run lintを設定することでnpm run lintだけの状態になります。

$ npx husky set .husky/pre-commit "npm run lint"
husky - created .husky/pre-commit

このコマンドの実行するとnpm run lintだけになっていることが確認できます。

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run lint

最後にすべて削除する方法を確認します。先ほど使用したnpx husky setコマンドを使って削除ができます。

$ npx husky set .husky/pre-commit ""
husky - created .husky/pre-commit

上記コマンドを実行するとコマンドが削除されます。

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"


pre-commitフックでlintとフォーマットの設定を行う

話を戻してpre-commitフックにnpm run lintコマンドとnpm run formatを設定します。npx husky addコマンドを2回呼び出してコマンドを追加します。

$ npx husky add .husky/pre-commit "npm run lint"
husky - updated .husky/pre-commit
$ npx husky add .husky/pre-commit "npm run format"
husky - updated .husky/pre-commit

これにより期待したとおりコマンドが登録されます。

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"


npm run lint
npm run format

では、動作確認をしていきます。.husky/pre-commitが変更されているのでこれをコミットすることで動作確認とします。

$ git add .
$ git commit -m 'update pre-commit hook'

> angular-tutorial@0.0.0 lint
> ng lint


Linting "angular-tutorial"...

All files pass linting.


> angular-tutorial@0.0.0 format
> prettier "src/**/*.{js,jsx,ts,tsx,html,css,scss}" --write

src/app/app.component.css 9ms
src/app/app.component.html 85ms
src/app/app.component.spec.ts 71ms
src/app/app.component.ts 3ms
src/app/app.module.ts 2ms
src/index.html 1ms
src/main.ts 2ms
src/styles.css 0ms
[main aeba2e0] update pre-commit hook
 1 file changed, 2 insertions(+)

git commitをするとリントに続いてフォーマットが行われていることが確認できます。

リントおよびフォーマットの対象を限定する

現在は非常にコードが少ないためリントやフォーマットの時間がそれほどかかりません。ただ、実際のプロジェクトでは非常にたくさんのコードがある場合もあります。そのような場合、リントやフォーマットに時間がかかるだけでなく、対応した範囲とは関係ない箇所でエラーになってしまい、コミットがいつまでもできない、ということが起きることは容易に想像できます。

こういった問題を解決する一つの方法として、lint-stagedを導入します。

lint-stagedをインストール

普通のパッケージと同じようにインストールしていきます。

$ npm install --save-dev lint-staged

added 37 packages, and audited 1135 packages in 3s

180 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

lint-stagedを設定する

lint-stagedを設定していきます。まずはHuskyがpre-commitフックで実行するコマンドをnpx lint-stagedに変更します。

$ npx husky set .husky/pre-commit "npx lint-staged"
husky - created .husky/pre-commit

次にlint-stagedでnpm run lintコマンドとnpm run formatを実行するようにします。

{
  "name": "angular-tutorial",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test",
    "lint": "ng lint",
    "format": "prettier \"src/**/*.{js,jsx,ts,tsx,html,css,scss}\" --write"
  },
+ "lint-staged": {
+   "*.{js,jsx,ts,tsx}": [
+     "npm run lint"
+   ],
+   "*.{js,jsx,ts,tsx,html,css,scss}": [
+     "npm run format"
+   ]
+ },
  "private": true,
  "dependencies": {
    "@angular/animations": "^16.1.0",
    "@angular/common": "^16.1.0",
    "@angular/compiler": "^16.1.0",
    "@angular/core": "^16.1.0",
    "@angular/forms": "^16.1.0",
    "@angular/platform-browser": "^16.1.0",
    "@angular/platform-browser-dynamic": "^16.1.0",
    "@angular/router": "^16.1.0",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.13.0"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^16.1.6",
    "@angular-eslint/builder": "16.1.0",
    "@angular-eslint/eslint-plugin": "16.1.0",
    "@angular-eslint/eslint-plugin-template": "16.1.0",
    "@angular-eslint/schematics": "16.1.0",
    "@angular-eslint/template-parser": "16.1.0",
    "@angular/cli": "~16.1.6",
    "@angular/compiler-cli": "^16.1.0",
    "@types/jasmine": "~4.3.0",
    "@typescript-eslint/eslint-plugin": "5.62.0",
    "@typescript-eslint/parser": "5.62.0",
    "eslint": "^8.44.0",
    "eslint-config-prettier": "^8.9.0",
    "husky": "^8.0.3",
    "jasmine-core": "~4.6.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.2.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.1.0",
    "lint-staged": "^13.2.3",
    "prettier": "^3.0.0",
    "typescript": "~5.1.3"
  }
}

"lint-staged"がコマンドの設定を行っている箇所です。リントとフォーマットで対象としたいファイルが異なるため分けて設定しています。

pre-commitフックを確認するまえに、npx lint-stagedコマンドを実行して動作を確認してみましょう。

$ npx lint-staged
→ No staged files found.

ステージされているファイルが何もないため、処理が行われませんでした。

では、現在変更されているコードをステージしてから実行してみましょう。

$ npx lint-staged
→ No staged files match any configured task.

少しメッセージが変わりました。ステージされているファイルはありましたが、条件にマッチするファイルがなかったため、処理が行われませんでした。

では、1つだけ処理対象となるファイルを変更してステージして動作を確認してみましょう。

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  title = 'angular-tutorial';
}

app.component.tsを以下のように変更します。

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
* title = 'angular-tutorial1';
}

titleをちょっとだけ変更しています。

変更をステージして実行してみます。

$ git add .
$ npx lint-staged
✔ Preparing lint-staged...
❯ Running tasks for staged files...
  ❯ package.json — 4 files
    ❯ *.{js,jsx,ts,tsx} — 1 file
      ✖ npm run lint [FAILED]
    ✔ *.{js,jsx,ts,tsx,html,css,scss} — 1 file
↓ Skipped because of errors from tasks. [SKIPPED]
✔ Reverting to original state because of errors...
✔ Cleaning up temporary files...

✖ npm run lint:
Error: Invalid values:
  Argument: project, Given: "/Users/t0k0sh1/Workspace/angular-tutorial/src/app/app.component.ts", Choices: "angular-tutorial"

> angular-tutorial@0.0.0 lint
> ng lint /Users/t0k0sh1/Workspace/angular-tutorial/src/app/app.component.ts 

実行したところエラーになりました。

結論からいうと、lint-stagedに指定したコマンドが原因です。リントを行うためにeslintを使用しているのではなく、ng lintを使用しています。

ng lintはプロジェクト全体に対して実行するコマンドのため、ステージしたファイルに対して限定的に使用することができません。ですので、少しコマンドを変更します。

{
  "name": "angular-tutorial",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test",
    "lint": "ng lint",
    "format": "prettier \"src/**/*.{js,jsx,ts,tsx,html,css,scss}\" --write"
  },
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
*     "eslint --fix"
    ],
    "*.{js,jsx,ts,tsx,html,css,scss}": [
      "npm run format"
    ]
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^16.1.0",
    "@angular/common": "^16.1.0",
    "@angular/compiler": "^16.1.0",
    "@angular/core": "^16.1.0",
    "@angular/forms": "^16.1.0",
    "@angular/platform-browser": "^16.1.0",
    "@angular/platform-browser-dynamic": "^16.1.0",
    "@angular/router": "^16.1.0",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.13.0"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^16.1.6",
    "@angular-eslint/builder": "16.1.0",
    "@angular-eslint/eslint-plugin": "16.1.0",
    "@angular-eslint/eslint-plugin-template": "16.1.0",
    "@angular-eslint/schematics": "16.1.0",
    "@angular-eslint/template-parser": "16.1.0",
    "@angular/cli": "~16.1.6",
    "@angular/compiler-cli": "^16.1.0",
    "@types/jasmine": "~4.3.0",
    "@typescript-eslint/eslint-plugin": "5.62.0",
    "@typescript-eslint/parser": "5.62.0",
    "eslint": "^8.44.0",
    "eslint-config-prettier": "^8.9.0",
    "husky": "^8.0.3",
    "jasmine-core": "~4.6.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.2.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.1.0",
    "lint-staged": "^13.2.3",
    "prettier": "^3.0.0",
    "typescript": "~5.1.3"
  }
}

実行するコマンドをnpm run lintからeslint --fixに変更しました。変更したついでに--fixオプションをつけて修正もできるようにしてあります。

再度実行してみましょう。

$ npx lint-staged
✔ Preparing lint-staged...
✔ Hiding unstaged changes to partially staged files...
✔ Running tasks for staged files...
✔ Applying modifications from tasks...
✔ Restoring unstaged changes to partially staged files...
✔ Cleaning up temporary files...

今度はすべて成功しています。

最後にコードを整理してコミット&プッシュ

app.component.tsに加えた変更は不要なので、戻しておきます。

$ git restore --staged src/app/app.component.ts
$ git checkout -- src/app/app.component.ts

それ以外の変更は正式に採用するので、ステージしてコミットし、これまでの変更も含めてプッシュしておきます。

$ git add .
$ git commit -m 'set up husky and lint-staged'
→ No staged files match any configured task.
[main 58ad1f8] set up husky and lint-staged
 3 files changed, 504 insertions(+), 3 deletions(-)
$ git push
Enumerating objects: 19, done.
Counting objects: 100% (19/19), done.
Delta compression using up to 10 threads
Compressing objects: 100% (13/13), done.
Writing objects: 100% (16/16), 5.23 KiB | 892.00 KiB/s, done.
Total 16 (delta 7), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (7/7), completed with 3 local objects.
To github.com:t0k0sh1/angular-tutorial.git
   f20d6df..58ad1f8  main -> main

まとめ

少し長くなりましたが、Huskyとlint-stagedを導入してリントとフォーマットをpre-commitフックで実行できるようになりました。

もう少し設定が必要ですが、それは必要になったときに設定することとします。

開発を行う前に結構時間がかかりましたが、こういった設定は最初の段階で整備しておいた方がよいです。開発途中で導入しようとすると、これまでの分の修正に追われ、本来やるべきことに集中できなくなりますので、最初に説明しました。

次回はTailwind CSSを導入してコンポーネントを作成してみたいと思います。

AngularプロジェクトにPrettierとESLintを導入する

AngularプロジェクトではPrettierとESLintは最初から導入されてはいません。プロジェクト作成後にセットアップすることで利用可能となります。

実際のプロジェクトではPrettierとESLintは導入した方がよいので、実際にセットアップしていきます。

ESLintを導入する

Angular CLIにはng lintというコマンドが用意されています。プロジェクト作成直後はESLintが導入されていないため、lintの実行の代わりにELintのインストールを行うことができます。実際にやってみましょう。

$ ng lint
? Would you like to share pseudonymous usage data about this project with the Angular Team
at Google under Google's Privacy Policy at https://policies.google.com/privacy. For more
details and how to change this setting, see https://angular.io/analytics. No
Global setting: enabled
Local setting: disabled
Effective status: disabled
Cannot find "lint" target for the specified project.
You can add a package that implements these capabilities.

For example:
  ESLint: ng add @angular-eslint/schematics

Would you like to add ESLint now? Yes
ℹ Using package manager: npm
✔ Found compatible package version: @angular-eslint/schematics@16.1.0.
✔ Package information loaded.

The package @angular-eslint/schematics@16.1.0 will be installed and executed.
Would you like to proceed? Yes
✔ Packages successfully installed.
    
    All @angular-eslint dependencies have been successfully installed 🎉
    
    Please see https://github.com/angular-eslint/angular-eslint for how to add ESLint configuration to your project.
    
    We detected that you have a single project in your workspace and no existing linter wired up, so we are configuring ESLint for you automatically.
    
    Please see https://github.com/angular-eslint/angular-eslint for more information.
    
CREATE .eslintrc.json (991 bytes)
UPDATE package.json (1424 bytes)
UPDATE angular.json (3085 bytes)
✔ Packages installed successfully.

一番最初の質問はAngular CLIの使用状況データを提供するかどうかを聞いています。その後に@angular-eslint/schematicsのインストールを行うかの確認があります。基本的にはデフォルトのまま進めていただいて構いません。

インストールが完了すると、以下が行われます。

  • AngularプロジェクトでESLintを使用する基本的なパッケージがインストールされる
  • lintを実行するためのスクリプトがpackage.jsonに追加される
  • ESLintの設定ファイル(.eslintrc.json)が追加される
  • angular.jsonにESLintに関する設定が追加される

これでlintが使用可能になります。実際に使ってみましょう。

$ ng lint

Linting "angular-tutorial"...

All files pass linting.

ESLint拡張機能を導入する

Visual Studio Codeを使用している場合は、ESLint拡張機能を導入するとリアルタイムに違反をチェックできるようになりますので、導入をお勧めします。

拡張機能にはいくつか設定項目がありますが、特に設定変更しなくても動作します。

Prettierを導入する

次にPrettierを導入します。こちらはAngular CLIに専用のコマンドが用意されているわけではないため、インストールおよび設定作業は手動で行います。

Prettierをインストールしますが、先にインストールしたESLintと競合した設定が一部あるため、eslint-config-prettierを併せてインストールします。

$ npm install --save-dev prettier eslint-config-prettier

added 2 packages, and audited 1097 packages in 4s

164 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

これでインストール作業が完了しました。続いて設定作業を行います。

ESLintとPrettierを併用できるようにする設定を追加する

.eslintrc.jsonにPrettierを併用できるようにするための設定を追加します。

{
  "root": true,
  "ignorePatterns": ["projects/**/*"],
  "overrides": [
    {
      "files": ["*.ts"],
      "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:@angular-eslint/recommended",
        "plugin:@angular-eslint/template/process-inline-templates",
+       "prettier"
      ],
      "rules": {
        "@angular-eslint/directive-selector": [
          "error",
          {
            "type": "attribute",
            "prefix": "app",
            "style": "camelCase"
          }
        ],
        "@angular-eslint/component-selector": [
          "error",
          {
            "type": "element",
            "prefix": "app",
            "style": "kebab-case"
          }
        ]
      }
    },
    {
      "files": ["*.html"],
      "extends": [
        "plugin:@angular-eslint/template/recommended",
        "plugin:@angular-eslint/template/accessibility"
      ],
      "rules": {}
    }
  ]
}

"prettier"を追記します。前の行にカンマをつけるのを忘れないように気をつけてください。

.prettierrc.jsonを作成する

Prettierの設定ファイルである.prettier.jsonを作成します。

$ touch .prettierrc.json

ファイルを作成したら以下のように記述します。

{
  "printWidth": 120,
  "singleQuote": true
}

ここでは1行の文字数を120文字、文字列でシングルクォーテーションを使用するように設定しています。シングルクォーテーションを使用する設定はEditorConfigの設定ファイル.editorconfigに記載されている

[*.ts]
quote_type = single

と一致するように設定してください。ダブルクォーテーションを使用したい場合は、.prettierrc.jsonの設定を"singleQuote": falseにし、.editorconfigの設定をquote_type = doubleに変更します。ただし、Angularプロジェクトを作成したときに自動生成されているコードはシングルクォーテーションを使用しているため、特段理由がなければシングルクォーテーションを使用してください。

コマンドでフォーマットできるようにする

package.jsonを編集してコマンドでフォーマットできるようにしましょう。

{
  "name": "angular-tutorial",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test",
    "lint": "ng lint",
+   "format": "prettier \"src/**/*.{js,jsx,ts,tsx,html,css,scss}\" --write"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^16.1.0",
    "@angular/common": "^16.1.0",
    "@angular/compiler": "^16.1.0",
    "@angular/core": "^16.1.0",
    "@angular/forms": "^16.1.0",
    "@angular/platform-browser": "^16.1.0",
    "@angular/platform-browser-dynamic": "^16.1.0",
    "@angular/router": "^16.1.0",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.13.0"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^16.1.6",
    "@angular-eslint/builder": "16.1.0",
    "@angular-eslint/eslint-plugin": "16.1.0",
    "@angular-eslint/eslint-plugin-template": "16.1.0",
    "@angular-eslint/schematics": "16.1.0",
    "@angular-eslint/template-parser": "16.1.0",
    "@angular/cli": "~16.1.6",
    "@angular/compiler-cli": "^16.1.0",
    "@types/jasmine": "~4.3.0",
    "@typescript-eslint/eslint-plugin": "5.62.0",
    "@typescript-eslint/parser": "5.62.0",
    "eslint": "^8.44.0",
    "eslint-config-prettier": "^8.9.0",
    "jasmine-core": "~4.6.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.2.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.1.0",
    "prettier": "^3.0.0",
    "typescript": "~5.1.3"
  }
}

コマンド名は何でも構いませんが、ここではnpm run formatで実行できるようにしています。

これから動作確認のためにコマンドを実行しますが、実行するといくつかのファイルが変更されます。変更したくない場合は記事を読むだけにしてください。

$ npm run format

> angular-tutorial@0.0.0 format
> prettier "src/**/*.{js,jsx,ts,tsx,html,css,scss}" --write

src/app/app.component.css 9ms
src/app/app.component.html 84ms
src/app/app.component.spec.ts 72ms
src/app/app.component.ts 3ms
src/app/app.module.ts 3ms
src/index.html 1ms
src/main.ts 2ms
src/styles.css 1ms

実行するといくつかのファイルについて変更されたことが表示されます。

Prettier – Code formatter拡張機能を導入する

Visual Studio Codeを使用している場合はPrettier – Code formatter拡張機能を導入することで、コードフォーマットを自動化することができます。

こちらは拡張機能導入後にいくつかの設定を行う必要があります。主な設定項目は以下の3つです。

  • Format On Paste
  • Format On Save
  • Default Formatter

他からコピーしたコードをペーストしたときにフォーマットする場合はFormat On Pasteの設定にチェックを入れてください。これは好みかもしれませんが、ペースト後に少しいじってから保存することがほとんどなので私はチェックを入れていません。

次に保存時にフォーマットを行うようにすることでコードフォーマットを自動化できます。基本的にはチェックを入れていただくとよいのですが、自動フォーマットを有効化していると操作性が気になる場合があるかもしれません。実際に試していただいて好みに合うように調整してください。

デフォルトのフォーマッタをPrettierに変更します。これによりPrettierが有効化します。一つだけ注意点があり、PrettierはPythonには対応していません。そのため、Pythonでの開発もVisual Studio Codeで行う場合は、デフォルトのフォーマッタをPrettierに変更した上で、settings.jsonに以下の記述を追加してください。

  "[python]": {
    "editor.defaultFormatter": null,
  },

これにより.pyファイルではPrettierをフォーマッタとして使用しなくなります。少し脱線しますが、blackをフォーマッタとして使用している場合は、

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "python.formatting.provider": "black",
  "[python]": {
    "editor.defaultFormatter": null,
  },
}

のような記載になります。

変更をコミットする

前回からずっとコミットしていなかったので、ここまでの変更をコミットしておきます。

$ git add .
$ git commit -m 'install eslint and prettier'
[main f20d6df] install eslint and prettier
 11 files changed, 3137 insertions(+), 327 deletions(-)
 create mode 100644 .eslintrc.json
 create mode 100644 .prettierrc.json

本記事で使用しているプロジェクトは以下にありますので、必要に応じて参考にしてみてください。

まとめ

ちょっと長くなったので今回はここまでにします。次回はHuskyを使ってpre-commitフックでESLintとPrettierを自動実行するようにしたいと思います。

ただし、若干のトレードオフがあるため、その点についても解説します。

Angular向けのVSCode拡張機能をインストールする

Visual Studio CodeでAngularプロジェクトの開発をするために必要な拡張機能をインストールしていきます。

ここで紹介する拡張機能は以下の3つです。

  • Angular Language Service
  • Angular Snippets (Version 16)
  • EditorConfig for VS Code

ここに掲載している拡張機能は必須といえるものを選んでいます。すべての拡張機能がAngularプロジェクトの作成直後から使用可能です。

では、一つずつ見ていきましょう。

Angular Language Service

Angularチーム公式の拡張機能で、テンプレートファイル内でのIntelliSense機能や定義へのジャンプなど、様々な便利な機能を提供しています。

Angularアプリケーションの開発を行う際は必ず入れておきましょう。

Angular Snippets (Version 16)

いくつかのバージョン対応版が見つかると思いますが、今回使用するバージョンは16.xですので、対応するバージョンの拡張機能を使用しています。

強力な拡張機能であることは間違いありませんが、人によっては使用する/しないがはっきり分かれると思います。最近ではGitHub CopilotなどのAIを活用したコード生成も手段としてあるので、必要性を感じない場合はインストールしなくても問題ありません。

EditorConfig for VS Code

Angularプロジェクトを作成すると、.editorconfigファイルが作成されていますので、これを有効化させるために導入します。

実務のことを考えるとPrettierやESLintを導入する必要がありますが、プロジェクト作成直後では使える状態になっていないため、今回の拡張機能から除外しました。

EditorConfigを使うべきか否か

Prettierを導入する場合、EditorConfigは必要かどうか、という議論があるようです。機能的には重複している部分があるため、EditorConfigを使わずにPrettierのみを使用する、という選択肢もアリという主張はそれなりに筋が通っています。

ただ、PrettierとEditorConfigは共存可能で、VSCode以外のIDEを自由に使用するという選択肢がある場合、多くのIDEでサポートされているEditorConfigの設定ファイルを残しておくことで、Prettierの設定が行われていない場合やGitのpre-commit時にしかPrettierが動作しない状況などにおいても必要最低限のフォーマットが行われるというメリットがあります。

そのため、本記事では必要な拡張機能にEditorConfig for VS Codeを挙げています。

まとめ

今回はAngularプロジェクトの開発に最低限必要なVisual Studio Codeの拡張機能3つをインストールしていきました。

実用面を考えると、PrettierとESLintの導入は必要ですので、次回はPrettierとESLintの導入とHuskyによるpre-commitフックの設定を行っていきましょう。

Angular CLIのインストールとプロジェクトの作成

AngularはGoogleによって開発され、維持されているオープンソースのJavaScriptフレームワークであり、主にSingle Page Application(SPA)の開発に使用されます。AngularはTypeScriptで書かれており、高度なツールやエディタのサポートを享受できます。

Angularの特徴

以下がAngularの主な特徴です。

  1. コンポーネントベースのアーキテクチャ: Angularはコンポーネントベースのアーキテクチャを採用しており、アプリケーションは再利用可能なコンポーネントに分割されます。これにより、コードの管理が容易になり、再利用性と可読性が向上します。
  2. 依存性注入: Angularは依存性注入パターンを採用しており、依存オブジェクトをコンポーネントに提供します。これにより、コードの再利用性とテストのしやすさが向上します。
  3. データバインディング: Angularは双方向データバインディングをサポートしており、モデルとビュー間の同期を自動的に保つことができます。
  4. ルーティング: Angularのルーティング機能により、アプリケーション内でのページ間の移動が容易になります。
  5. Angular CLI: Angular Command Line Interface(CLI)を使用すると、プロジェクトの作成、開発、テスト、ビルドが容易になります。
  6. テスト容易性: Angularは単体テストからエンドツーエンド(E2E)テストまで、テストを容易にするツールとライブラリを提供します。

他の主要なJavaScriptフレームワーク、特にReactやVue.jsと比較した場合、Angularはフルスタックなフレームワークで、多くの機能を提供します。一方で、ReactやVue.jsはライブラリとしてスタートし、他のライブラリやツールと組み合わせて使用することでフレームワークのような機能を達成します。そのため、ReactやVue.jsはより柔軟性がありますが、セットアップや学習曲線が少し複雑になる可能性があります。

また、AngularはTypeScriptを使用しますが、ReactやVue.jsはJavaScriptを使用します(ただし、TypeScriptのサポートもあります)。TypeScriptは静的型付けを提供し、大規模なプロジェクトでは開発効率やコードの品質を向上させますが、一方で学習コストがかかることも事実です。

Angularは日本国内においてはReactやVue.jsと比べるとメジャーではありませんが、まったく使われていないというわけではありません。主に企業向けシステムを中心に利用されています。

Angular CLIのセットアップとプロジェクトの作成

Angularのプロジェクト作成およびコードのひな形の生成にはAngular CLIを使用します。

本記事では、執筆時点で最新版の16.1.6を使用します。基本的にAngularで使用するNode.jsは同時期の安定版をサポートしているため、ここではNode.jsは18.17.0、npmは9.8.1を使用します。また、全体を通してパッケージマネージャーはnpmを使用します。

$ node -v
v18.17.0
$ npm -v
9.8.1

以下のコマンドを入力してAngular CLIをインストールします。

$ npm install -g @angular/cli

added 240 packages in 13s

36 packages are looking for funding
  run `npm fund` for details

Angular CLIのインストール成功すると、ngコマンドが使用可能になります。versionサブコマンドでインストールしたAngular CLIのバージョンを確認してみましょう。

$ ng version

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 16.1.6
Node: 18.17.0
Package Manager: npm 9.8.1
OS: darwin arm64

Angular:
...

Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.1601.6 (cli-only)
@angular-devkit/core         16.1.6 (cli-only)
@angular-devkit/schematics   16.1.6 (cli-only)
@schematics/angular          16.1.6 (cli-only)

Angular CLI、Node.js、パッケージマネージャー(npm)およびインストールしたパッケージのバージョンを確認することができます。

開発者全員で同じバージョンのAngular CLIを使用する場合は、latestの部分を指定のバージョンに変更します。

$ npm install -g @angular/cli@16.1.6

added 240 packages in 15s

36 packages are looking for funding
  run `npm fund` for details

システム開発においてはバージョンを明示的に指定するこちらの方法でインストールしてください。

Angularプロジェクトの作成

Angular CLIのインストールが完了したら、Angularプロジェクトを作成します。

プロジェクト名は任意の名前で構いませんが、本記事ではangular-tutorialというプロジェクト名にします。

Angular CLIでAngularプロジェクトを作成するには、newサブコマンドを使用します。

$ ng new angular-tutorial

使用するAngular CLIのバージョンによって変わる場合がありますが、コマンド実行後にいくつか質問が表示されます。ここではすべてデフォルト値のまま進めることにします。

$ ng new angular-tutorial
? Would you like to add Angular routing? No
? Which stylesheet format would you like to use? CSS
CREATE angular-tutorial/README.md (1069 bytes)
CREATE angular-tutorial/.editorconfig (274 bytes)
CREATE angular-tutorial/.gitignore (548 bytes)
CREATE angular-tutorial/angular.json (2750 bytes)
CREATE angular-tutorial/package.json (1047 bytes)
CREATE angular-tutorial/tsconfig.json (901 bytes)
CREATE angular-tutorial/tsconfig.app.json (263 bytes)
CREATE angular-tutorial/tsconfig.spec.json (273 bytes)
CREATE angular-tutorial/.vscode/extensions.json (130 bytes)
CREATE angular-tutorial/.vscode/launch.json (470 bytes)
CREATE angular-tutorial/.vscode/tasks.json (938 bytes)
CREATE angular-tutorial/src/main.ts (214 bytes)
CREATE angular-tutorial/src/favicon.ico (948 bytes)
CREATE angular-tutorial/src/index.html (301 bytes)
CREATE angular-tutorial/src/styles.css (80 bytes)
CREATE angular-tutorial/src/app/app.module.ts (314 bytes)
CREATE angular-tutorial/src/app/app.component.css (0 bytes)
CREATE angular-tutorial/src/app/app.component.html (23083 bytes)
CREATE angular-tutorial/src/app/app.component.spec.ts (922 bytes)
CREATE angular-tutorial/src/app/app.component.ts (220 bytes)
CREATE angular-tutorial/src/assets/.gitkeep (0 bytes)
✔ Packages installed successfully.
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint: 	git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint: 	git branch -m <name>
    Successfully initialized git.

質問は2つありますので、1つずつ見ていきましょう。

Would you like to add Angular routing?

ルーティングモジュールを追加するかについて訪ねられています。ルーティングモジュールを使用しないことはほとんどないので、yを回答しても構いませんが、後で追加するのでここではデフォルト値のままにしておきます。

Which stylesheet format would you like to use?

スタイルシートのフォーマットを選択することができます。デフォルトはCSSですが、SCSSやSASS、LESSなどを選択できます。UIをどのように開発するか決まっていればそれに合わせて選択していただくのがよいと思います。本記事執筆時点ではいくつかのCSSフレームワークを試そうと思っていますので、デフォルトのCSSのままにしておきます。

hint: Using ‘master’ as the name for the initial branch. This default branch name

プロジェクトの作成が成功したあと、いくつかhint:で始まるメッセージが表示されています。

私の環境では最初に作成されるGitのブランチがmasterになっているため、このメッセージが表示されています。変更しなくても動作には支障はありませんが、不適切なブランチ名であるというのが業界における共通認識になっていると思いますので、GitHubの方針に合わせてmainブランチに変更しておきましょう。

また、git config --global init.defaultBranch <name>でデフォルトのブランチ名を変更しておくことも可能です。毎回変更するのが面倒な場合はこちらでデフォルトブランチを変更してください。ただし、次回以降の作成で有効になる点にはご注意ください。(すでに作成済みのGitリポジトリは変更されません)

これ以降ではVisual Studio Codeを使って開発を進めていきますので、作成したプロジェクトをcodeコマンドで表示し、ターミナルを開きます。

$ code angular-tutorial

Visual Studio CodeでAngularプロジェクトを開くと、拡張機能をインストールを勧めるメッセージが表示されますが、あとでインストールするので、ここでは閉じておいてください。

$ git branch
* master

ブランチを確認すると、masterブランチになっています。これをgit branch -mコマンドでmainに変更します。

$ git branch -m main
$ git branch
* main

ブランチ名がmainに変わったことを確認できました。

まとめ

Angular CLIのインストールとAngularプロジェクトの作成までを行いました。

次回はVisual Studio CodeでのAngularの開発を円滑に進めるための拡張機能をインストールしていきます。

AutoMLの現在と今後

機械学習と言えば、人工知能技術の中でも特に注目されている分野です。機械学習は、私たちが日常的に利用している様々なサービスやアプリケーションの背後にある技術として存在しています。しかし、機械学習は専門知識が必要な分野であり、特にモデルの選択や最適化のプロセスは非常に難解です。そこで、最近注目を集めているのが「AutoML」という分野です。本記事では、AutoMLについておさらいしつつ、自動化によってどのような利益をもたらすのか、そして今後の展開について考えます。

AutoMLとは

AutoML(Automated Machine Learning)とは、機械学習モデルの設計や最適化の工程までを自動化する技術の総称です。データの前処理、特徴量選択、モデル選択、ハイパーパラメータのチューニングなどを効率化し、専門知識がないユーザーでも高品質なモデルを作成可能にします。

「AutoML」という言葉が初めて使われた正確な時期を特定するのは難しいですが、その概念自体は2000年代後半から存在し、2010年代に入ってから注目を集め始めました。

機械学習とは

機械学習についておさらいしてきましょう。

機械学習は、人工知能の一分野で、アルゴリズムと統計を使用して、コンピュータがタスクを自動的に改善する能力を持つように設計されています。つまり、明示的なプログラミングなしにコンピュータが学習し、新しいデータに対する予測や決定を行う能力を持ちます。

機械学習の手法は大きく分けて、教師あり学習、教師なし学習、強化学習の3つのカテゴリーに分けられます。教師あり学習では、既知のデータ(ラベル付きデータ)を使ってモデルを訓練し、新たなデータに対する予測を行います。教師なし学習では、ラベルが付けられていないデータからパターンや構造を見つけ出します。強化学習は、試行錯誤を通じて最適な行動を学習する方法で、特定の目標を達成するために最適な行動を選択する能力を獲得します。

これらの手法は、画像認識、自然言語処理、医療診断、株価予測など、様々な分野で広く応用されています。

また、昨今注目されているディープラーニングは機械学習の一部となります。

AutoMLに対する懸念や疑問

AutoMLに触れたことの方にとっては様々な懸念や疑問があると思います。よく聞くものをいくつか挙げておきます。これらの懸念や疑問はすでに解決しているものもありますが、これから解決していくものもあります。

AutoMLがどのように動作しているのかがよくわからない

AutoMLツールはデータに基づいて最適なモデルを選択し、そのパラメータを調整します。しかし、その内部のプロセスは専門的な知識がなければ理解するのが難しい場合があります。これは、「ブラックボックス問題」とも呼ばれ、どのようにそれが最終的な結果を生成したのか明確ではないことを意味します。

数式や内部の理論まで必要になることは少ないと思いますが、少なくとも手法について概要やメリット・デメリット、適している問題領域などについては理解しておくことが必要となると思います。

データ品質の影響を強く受けるのではないか

AutoMLは提供されたデータに基づいて学習と予測を行います。したがって、データの品質や適切な前処理がモデルのパフォーマンスに大きく影響します。データが不適切または偏っていると、モデルの結果も偏るか、不正確になる可能性があります。

現実の問題においてはデータの品質が常に高いとは限りません。AutoMLは先に触れたように「ブラックボックス問題」があるため、データの品質がモデルや予測にどの程度影響があるのかがわからないという不安を感じやすいといえます。

ただ、AutoMLではデータやモデルの学習結果を可視化できるものも多く存在するため、そういった機能を活用してしっかり確認していくことが重要です。

モデルの適用範囲が十分ではない

すべての問題がAutoMLに適しているわけではありません。特に複雑な問題や専門的な知識を必要とする問題では、手動で設計された特定のモデルの方が適している場合があります。

また、AutoMLにも種類があり、色々な問題に適用できるAutoMLもあれば、特定の問題に特化しているAutoMLもあります。具体的な製品については後ほど説明します。

コストと時間がかかる

AutoMLの多くはサービス、特にクラウドサービスとして提供されており、大規模なデータセットや複雑なモデルを扱う場合、AutoMLの訓練と評価にはかなりの時間とコンピューティングリソースが必要になることがあります。

直感的にはマニュアルよりもオートの方が余計な処理を行っているような気がしてしまうので、余計なコスト・料金がかかっているのではないかという懸念は当然かもしれません。

とはいえ、AutoMLの製品・サービスは日々アップデートを繰り返して高性能化していますし、クラウドサービスは規模の経済性によって利用者が増えることによってコストが安くなっていくので、こういった不安は今後解消してくものと思います。

プライバシーとセキュリティに不安がある

機密性の高いデータを扱う際には特定のセキュリティリスクをもたらす可能性があります。データが適切に保護されているかどうか、またそのデータがどのように使用されるかについて明確な理解が必要です。

これはAutoMLに限ったことではなく、機密性の高いデータが学習に使われて何かの拍子に流出するようなことがないか、という懸念はAutoMLに限らず話題になることでしょう。

AutoMLはどのような問題を解決するのか

AutoMLはどのような問題を解決するのでしょうか。

AutoMLによって、モデルの選択や最適化のプロセスを自動化できます。これによって、専門的な知識やスキルを持つ人材がいなくても、機械学習を導入することができます。また、自動化によって、機械学習の開発時間やコストを削減することができます。その結果、機械学習がビジネスや研究に利用される可能性が高まります。

AutoMLの製品・サービス

以下に、AutoMLの具体的な製品とサービスを汎用的な問題に対応した製品・サービス、特定の領域に特化したいくつか挙げておきます。

汎用的な問題に対応した製品・サービス

  1. Google Cloud AutoML:Googleが提供しているサービスで、非専門家でも高品質なカスタムモデルを構築できるようにすることを目指しています。データのアップロードからモデルの訓練、評価、デプロイまでの一連のプロセスを自動化しています。
  2. AutoML in Microsoft Azure:Microsoft Azure内のMachine Learningサービスの一部として提供されています。AzureのAutoMLは、データの前処理、モデル選択、ハイパーパラメータ調整を自動化し、ビジネス上の問題に対する最適なモデルを導き出すことを可能にします。
  3. Amazon SageMaker Autopilot:機械学習モデルの全体的なプロセスを自動化するサービスです。このサービスは、まずデータセットを自動的に解析し、データの前処理や適切なアルゴリズムの選択を自動で行います。次に、様々なモデルとパラメータを試し、最も性能の良いモデルを見つけ出します。
  4. H2O.ai’s AutoML:H2O.aiが提供しているオープンソースのAutoMLフレームワークで、複数の機械学習モデルの訓練と評価を自動化します。

特定の領域に特化した製品・サービス

  1. Vertex AI:機械学習(ML)モデルの開発、デプロイメント、管理を統合的に行うためのフルマネージド型プラットフォームです。機械学習のライフサイクル全体をカバーし、データサイエンティストやMLエンジニアが独自のモデルを効率的に開発、訓練、デプロイするためのツールとサービスを提供します。
  2. DataRobot:データサイエンティストとビジネスアナリストが効率的にデータから洞察を得るために設計されたAutoMLプラットフォームです。特定の業界や業務に適用可能なモデルを自動的に生成します。

AutoMLの動作原理

AutoML(Automated Machine Learning)の動作原理は、一連の機械学習の工程を自動化することにあります。主な工程は、データの前処理、特徴選択、モデル選択、そしてハイパーパラメータの最適化となります。以下にそれぞれ詳しく説明します。

  1. データ前処理: これは、欠損値の補完、カテゴリ変数のエンコーディング、スケーリングや正規化など、モデルがデータを効率的に処理できる形に変換する工程です。AutoMLはこれらのタスクを自動的に行います。
  2. 特徴選択: これは、モデルが最も重要で意味のある情報に集中できるように、不要または冗長な特徴を取り除く工程です。AutoMLは、特徴の重要度を評価し、最も意味のある特徴だけを選択します。
  3. モデル選択: 機械学習にはさまざまな種類のモデルがあります。AutoMLは、問題のタイプ(分類、回帰、クラスタリングなど)とデータに基づいて最適なモデルを自動的に選択します。
  4. ハイパーパラメータ最適化: ハイパーパラメータはモデルの性能に大きな影響を与える設定値です。AutoMLは、さまざまなハイパーパラメータの組み合わせを試し、最良の性能を出す組み合わせを見つけ出します。

これらのプロセスを通じて、AutoMLはデータから予測モデルを自動的に生成します。その結果、非専門家でも効率的に高品質な機械学習モデルを構築することが可能になります。

AutoMLの限界について

しかし、AutoMLには限界もあります。

例えば、AutoMLによって自動化されるプロセスは限定されており、データ前処理や特徴量エンジニアリングなどの一部のタスクには対応していない場合があります。また、自動化が進んだ場合、技術的なノウハウが失われてしまう可能性もあります。このため、AutoMLの導入には慎重な検討が必要です。

  1. 一部のカスタマイズが難しい:AutoMLは、データの前処理やモデルの訓練といった工程を自動化しますが、その自動化により一部の細かな調整やカスタマイズが難しくなる場合があります。特に、特定の問題に特化した独自のモデルを作りたい場合、AutoMLだけでは不十分な場合があります。
  2. 解釈可能性と透明性に疑問がある:AutoMLは最適なモデルを自動的に選択しますが、その選択プロセスはユーザーにとって不透明で、選択されたモデルがどのように機能しているか、なぜそのモデルが選ばれたのかを理解するのが難しい場合があります。
  3. データの質を保つための準備が大変:AutoMLは、高品質なモデルを構築するためには、クリーンで整形されたデータが必要となります。つまり、データが不完全であったり、欠損値や異常値が含まれていると、モデルの性能に影響を与える可能性があります。
  4. コストがかかる:AutoMLサービスは、大量の計算リソースを使用することがあり、それによりコストが高くなる可能性があります。また、AutoMLが行うモデルの探索やハイパーパラメータチューニングのプロセスは、長時間にわたることが多く、これがさらにコストを増加させる要因となります。
  5. 一般的なソリューションに過ぎない: AutoMLは非常に有用なツールですが、特定の問題に対して最適化されたソリューションを提供するわけではありません。それは一般的なソリューションを提供するツールであり、特定の問題に最適なモデルを作成するためには、専門的な知識と手動のチューニングが依然として必要な場合があります。

これらの問題は現時点での問題で、今後改善していくことが期待されています。

最後に

本記事では、AutoMLについて説明しました。

AutoMLは、機械学習におけるモデルの選択や最適化のプロセスを自動化する技術であり、専門的な知識を持たないユーザーでも機械学習を利用することができます。自動化によって、機械学習の開発時間やコストを削減することができ、機械学習がビジネスや研究に利用される可能性が高まります。しかしながら、AutoMLには限界や懸念もあり、その導入には慎重な検討が必要です。

AutoMLは進歩の目覚ましい分野でもあります。現在抱えている疑問や不安、AutoML自体の限界は徐々に解消されていくことでしょう。また、最近ではRPA、ノーコード・ローコード開発・プロンプトエンジニアリングなど、小さい労力でシステムを開発することに注目が集まっています。この傾向は今後も続くと考えられ、AutoMLもその一つとなると思います。

AutoMLの可能性に期待しつつ、今後の展開に注目していきましょう。

AIベースのサイバーセキュリティは私たちに何をもたらすのか

現在のインターネット時代において、オンライン上には多大な情報が流通しているため、多くの企業や個人がサイバーセキュリティに関心を持っています。

しかし、発展したテクノロジーにより、サイバー攻撃も進化し続けているのが現状です。そのため、AIベースのサイバーセキュリティが注目されています。この記事では、AIベースのサイバーセキュリティについて、概要と具体的な製品・事例を説明し、今後どのような製品が発表されいくのか、我々を取り巻くセキュリティ事情がどう変わっていくのかについて展望します。

AIによってサイバーセキュリティはどう変わるのか


AIベースのサイバーセキュリティは、従来のセキュリティプログラムに比べ、より高度な脅威を特定し、より速く対処することができます。それは、AIが大量のデータを学習し、自己のアルゴリズムを改善することができるためです。具体的には、マルウェアやスパムフィルター、フィッシング攻撃などのサイバー攻撃を特定し、AIが自動的に対処することができます。また、AIは攻撃を予測することも可能で、攻撃を未然に防ぐための情報提供も行えます。

AIベースのサイバーセキュリティ製品

AIベースのサイバーセキュリティ製品としては、国内外の多くの企業がこれに注力しています。いくつか具体的な製品をご紹介します。

CylancePROTECT (開発企業: Cylance Inc. / BlackBerry Limited)

CylancePROTECTは、AIと機械学習を使用してマルウェアやランサムウェアを検出し、予防するセキュリティソフトウェアです。マルウェアの特徴や挙動を学習し、新たな脅威をリアルタイムで検知します。Cylance Inc.が開発し、後にBlackBerry Limitedによって買収されました。

Darktrace (開発企業: Darktrace Limited)

Darktraceは、自己学習型のサイバーセキュリティプラットフォームであり、AIアルゴリズムを使用してネットワークの異常な挙動や攻撃を検出します。ネットワーク全体を監視し、内部および外部の脅威から組織を保護します。

Palo Alto Networks Cortex XDR (開発企業: Palo Alto Networks)

Palo Alto Networks Cortex XDRは、エンドポイント、ネットワーク、クラウド上のセキュリティイベントを統合的に分析し、高度な脅威を検出するプラットフォームです。AIと機械学習による挙動分析や自動化により、リアルタイムの脅威インテリジェンスを提供します。

Symantec Endpoint Protection (開発企業: Broadcom Inc.)

Symantec Endpoint Protectionは、AIとマシンラーニングを活用したエンドポイントセキュリティソリューションです。悪意のあるファイルや不正な挙動をリアルタイムで検知し、防御します。また、攻撃者の手法やパターンを学習し、未知の脅威にも対応します。

残されている課題

AIを採用したサイバーセキュリティでは、予測不可能な脅威に対応することができるため、今後ますます多くの企業がこの技術に着目することが予想されます。例えば、既存の情報セキュリティプログラムを補完することで、企業がより高度な防御を実現できるようになります。また、AIによって攻撃が未然に防止された場合、企業は慎重な情報管理やプライバシー保護についても評価されます。

しかし、良いことばかりではなく、いくつかの課題が残されています。これらの課題は裏を返せば、サイバーセキュリティ製品を導入する個人や企業が製品を選定する際に注意しなければならない点であり、運用する上で知っておかなければならないことであるとも言えます。

偽陽性と偽陰性

AIはデータとパターンを学習して予測や判断を行いますが、完全な正確性を保証することは難しい場合があります。AIモデルは誤って正常な活動を異常と判断する「偽陽性」や、悪意のある活動を見逃す「偽陰性」の問題を抱えることがあります。

トレーニングデータの品質とバイアス

AIモデルはトレーニングに使用されるデータの品質に大きく依存します。セキュリティ分野では、正確なラベル付けされたトレーニングデータを収集することが困難な場合があります。また、トレーニングデータに偏りがある場合、モデルにバイアスが生じ、誤った判断をする可能性があります。

進化する攻撃手法への適応

サイバー攻撃者は常に新たな手法やテクニックを開発しています。AIモデルは過去のデータに基づいて学習するため、未知の攻撃に対しては対応が難しい場合があります。攻撃者がAIを迂回する手法を開発することもあります。

プライバシーと倫理の問題

AIを活用したセキュリティ製品は、ユーザーのデータを収集・分析する場合があります。個人のプライバシーや倫理的な観点から、データの収集や使用に関する懸念が存在します。適切なデータ保護措置や透明性が求められます。

誤った学習や攻撃への悪用のリスク

AIモデルは、誤った学習や故意に攻撃される可能性があります。攻撃者がモデルを欺くために誤ったデータを提供したり、モデル自体に対して攻撃を行ったりすることがあります。また、AIを悪用して攻撃を行う可能性もあります。

最後に

企業や個人がサイバー攻撃を受けるリスクが高まる中、AI技術を採用したセキュリティシステムが注目を集め、目覚ましい進化を遂げています。AIが予測と反応を行うことで、未知の脅威に対して高度な防御が可能になっていきます。しかしその一方で、AIを使ったセキュリティシステムは高い技術水準が求められるだけでなく、学習するという性質を逆手に取った攻撃のリスクもあります。

課題は多く残っていますが、今後、それらの課題が解決されていくことを期待しています。

AIがプログラマの仕事を奪うのか?

近年、AI技術が急速に発展しているため、懸念される一部の人がいます。彼らは、AI技術がプログラマの仕事を奪うことを心配しています。確かに、AI技術はコンピューターの処理能力を高めたため、プログラマの役割が変わりつつあることは確かです。しかし、AI技術はプログラマを置き換えるものではありません。今回の記事では、AI技術がプログラマに及ぼす影響について説明します。

生成AIの登場

近年、人工知能(AI)の高度化が進み、プログラマーを含む職の奪い合いが懸念されています。プログラミング界隈で話題を呼んだAIソフトウェアの例として、ChatGPTやGithub Copilotなどの生成AIがあります。

ChatGPTは、テキストベースの会話に対して人間のような応答を生成することができるAI搭載のチャットボットです。OpenAIのGPT(Generative Pre-trained Transformer)モデルをベースにしており、ディープラーニングを用いて大量のテキストデータのパターンを分析することができます。ChatGPTは、自然な会話を行い、ユーザーに役立つ回答を提供することができると評価されています。

一方、Copilotは、MicrosoftとGitHubが開発したAI搭載のコーディングアシスタントです。開発者がコードを入力する際に、コードスニペットを提案したり、コード行を補完したりすることで、より速く、より効率的にコードを書くことを支援するよう設計されています。Copilotは、機械学習を利用して既存のコードリポジトリを分析し、新しいコードの提案を行う。著作権侵害の可能性をめぐる批判もありますが、多くの開発者は便利なツールとして歓迎しています。

ChatGPTやCopilotのようなツールは、プログラミングのプロセスをより速く、より効率的にするのに役立ちますが、プログラマーを完全に置き換えることを目的としているわけではありません。むしろ、人間のプログラマーのアシスト役として、より速くコードを書き、仕事の質を向上させる手助けをするものです。しかし、あくまでも人間のスキルを補うためのものであり、人間の代わりになるものではないことを忘れてはいけません。

AIはプログラミングに変革をもたらした

AIは、あくまで人間が設計してプログラムしているものです。つまり、AIはプログラマがいなければ存在しません。プログラマがAI技術に向かい、それを構築し、設計し、アルゴリズムを開発しています。プログラマはAI技術の精度を向上させるため、自動機能の設定、AI技術の維持や世話など、AI技術に関する維持管理業務をしています。AIがプログラマを補完するものであり、置き換えるものではないのです。

専門家たちはAI技術に対する懸念を持っていますが、彼らはAI技術が必ずしも仕事を奪うものではないと言います。むしろ、AI技術を利用することで、プログラマは効率と正確性を向上させることができます。AI技術は書き物を検査し、曝露を見つけることができます。それによって、プログラマはより正確なコードを作成し、プログラムの品質を向上させることができます。

創造的な作業は人間にしかできない

プログラミングは、創造的なプロセスでもあります。人間が設計し、人間がアートワークを制作するのか、機械が機能を制作するのか、この選択には適切な答えはありません。新しいプログラマーのスキルが必要になることもあります。今のプログラマには創造的で高度なスキルが求められます。技術が進化するにつれて、プログラマーは適応しなければなりません。ここでAI技術はプログラムの効率を向上することで、人が創造的で革新的なアプローチに時間を費やすことを可能にします。

記憶することができないAIは、特定のプログラマが持つ専門知識や知識を持ちません。AIは、あくまで人間による制御下にあります。コンピュータに保持されていないノウハウ、知識、スキル、経験は、人間であるプログラマにのみ保持されています。

補完的な役割がAIに与えられることで、プログラマーよりも多くの時間と機会が提供されます。自動化されたプログラマーを持つ利点は、時間の節約、一貫した品質、より正確なプログラムであるため、ニーズが非常に高いです。学問に従事しているプログラマーにとって、AIは改善効果が得られます。

私はAIとどう向き合っているか

私自身も普段の開発では、 ChatGPTやGithub Copilot、Amazon CodeWhispererなどを使用しています。

ChatGPTにはうろ覚えのプログラムの書き方を聞いたり、DB設計の草案を考えてもらったり、トラブルの解決方法を聞いたりしています。とはいえ、情報の少ないニッチな内容については正確な答えが返ってこないことが多いですし、うまく伝わらないこともあります。最終的には真偽を確かめて、修正が必要な場合もあります。

Github Copilot、Amazon CodeWhispererを使ってお決まりのコードを生成してもらったり、単純なコピペではない似たようなコードを生成してもらったり、コメントを書いてそれに沿ったプログラムを生成してもらったりしています。よりインテリジェントでより柔軟なスニペットな使い方が中心になっています。

以前と比べると、同じものを作るにしてもタイピング量は格段に減りましたし、習熟度の低いプログラミング言語でもサクサク作れるようになりました。自身に合う/合わないもありますが、生産性を高めるツールはできるだけ試して、取り込んでいくことが大切だと思います。

最後に

技術が進化していくにつれて、プログラマーの役割が変化していくことは確かです。しかし、AI技術によって人間のプログラマーが排除されることはないことがわかりました。AI技術は、プログラマーが自分たちの能力を最大限に発揮するのに役立つ補完的な役割を持っていることがわかりました。AI技術をオプションとして導入することで、さまざまな業界でのプログラマーにとって、より生産的で成功したキャリアを築くことができます。プログラマは、AI技術を活用して、より正確なコード、益を上げるプログラム、より高い生産性などを目指すことができます。AI技術がいかにプログラマーに貢献するかを理解することが重要です。

楽観的排他制御と悲観的排他制御

楽観的排他制御(optimistic exclusive control)と悲観的排他制御(pessimistic exclusive control)は、両方の方法が大規模なスケーラビリティを持つため、システムの排他制御において非常に一般的な方法論です。

近年のWebシステムでは、特に楽観的排他制御が用いられることがほとんどです。本記事では、楽観的排他制御と悲観的排他制御のどちらが適しているのかを考察し、それらがシステム設計に及ぼす影響について説明します。

排他制御とは

排他制御は、同時に複数のスレッド/プロセス/ユーザーがデータを更新しようとする問題を解決するために使用されます。排他制御方法には悲観的排他制御と楽観的排他制御の2つの主要な方法があります。

悲観的排他制御(pessimistic exclusive control)

悲観的排他制御は、データ更新リクエストについての楽観的な予測を行わない方法です。つまり、一度に1人のユーザーしかデータを操作できないように、予約をすることで競合を回避しようとします。悲観的排他制御は、ロック機構やトランザクションを使用することが一般的です。

楽観的排他制御(optimistic exclusive control)

一方、楽観的排他制御は、データを更新する前に、競合を検出して回避するための最適な方法論を提供することにフォーカスしています。これは、別のユーザーがデータを更新したときに、その影響を判断することができるデータバージョン管理を含むことがよくあります。ユーザー操作中に競合が発生した場合、変更をロールバックするか、サーバから新しいデータを取得してマージするか選択できます。

楽観的排他制御では、競合を検出するためにバージョンまたは更新日時を使用することが一般的です。更新日時を使用する場合には日付の精度に注意が必要で、精度が低いと他ユーザーの更新を検出できない場合があります。

排他制御がシステムに与える影響

悲観的排他制御は、大規模なWebシステムのパフォーマンスに深刻な影響を与える可能性があります。つまり、トランザクションロックが長時間存在する場合には、他のトランザクションによってコミットされたデータに対して待たなければならず、それによって生じる遅延リスクがあります。一方、楽観的排他制御には、ユーザーが変更したい場合には非常に簡単であるため、パフォーマンスの限界に到達する前に、他のタイプの制御よりも多数のデータ更新を許容できます。

また、トランザクションが複数のデータをロックする場合、ロック順番に気をつけないとデッドロックが発生するリスクがある点についても注意が必要です。

楽観的排他制御は、特にオンラインWebアプリケーションにおいて、より親和性があると考えられています。ただし、その欠点が存在する場合があります。書き込みの競合性が高い場合、やはり悲観的排他制御で処理することが必要であるかもしれません。そしてこの場合、楽観的排他制御は厳密ではありません。Webアプリケーションには、トランザクション処理スレッド特有の問題も存在するため、慎重に検討する必要があります。

ただし、頻繁にデータ競合が発生する場合は、ユーザーの生産性やパフォーマンスに悪影響を与える場合があります。

アクセス制御による排他制御

楽観的排他制御とも悲観的排他制御とも異なるアプローチで排他制御を行うという選択肢もあります。

例えば、以下のような方法があります。

  • データを更新する前にデータに対するロックを獲得し、ロックしているユーザーのみがデータを更新できるようにする
  • ワークフローによってデータをユーザーに割り当て、割り当てられているユーザーのみが更新できるようにする

いずれの方法においても、ユーザーに与えて、アクセス権限を有しているユーザーだけが更新を行うことができるようにシステムを設計しています。これにより、更新時に排他制御を行わなくても安全にデータを更新することができるようになりますし、楽観的排他制御で安全に排他制御することができます。

結論

楽観的排他制御と悲観的排他制御は、両方とも重要な排他制御手法です。また、これら以外にも排他制御の手法はあります。システム環境、データ同期の頻度、書き込みの競合状況に応じて、適切な排他制御方法を選択し、最適な手法を使用することが重要です。

楽観的排他制御を使用することで、システムのパフォーマンスが向上し、ユーザーエクスペリエンスを向上させることができます。悲観的排他制御を使用することで、強力なトランザクション管理と競合回避制御を実現できます。

したがって、基本的には、両者が制御戦略の好みに応じて異なる選択肢であり、特定のシナリオと目的に基づいて使用されるべきです。

モバイルバージョンを終了