本記事の目的
Spring Boot アプリ開発で TypeScript が使えるようになる
Spring Boot に TypeScript を導入する手順は、次の順番で説明します。
- 開発環境
- 導入の準備
- 具体的な導入手順《本編》
- TypeScript が反映されないときの対処法
「開発環境」では、本記事で解説する内容が正しく挙動したことを実際に視認できた条件・環境を整理しました。「導入の準備」では、本格的な導入の前までに実装しておきたいことを整理しました。「具体的な導入手順《本編》」では、実際にSpring Boot へ TypeScript を組み込む方法を整理しました。「TypeScriptが反映されないときの対処法」では、ボク自身が初めてSpring Boot アプリ開発で TypeScript を使った際、困った点とその解決法をまとめました。
ムダな前置きは省いて、早速解説します
ここから先の解説内容は、Spring Boot 以外のアプリ開発(Ex:Rails とか)でも、ほぼ同じ原理・方法で導入できるので、気になった方はチラ見・参照・ブクマしてください。
◆ 開発環境
Category | Value,Version |
---|---|
OS | MacOS Sonoma 14.3.1 |
Project | Gradle Groovy |
Language | Java 17 |
Framework | Spring Boot 3.1.9 |
Group | com.project |
Artifact | sprigbootproto |
Name | SprigBootProto |
Package name | com.project.sprigbootproto |
Packaging | Jar |
Dependencies | Spring Web, Thymeleaf |
Editor | IntelliJ IDEA CE |
ここから先は「↑の内容に沿って spring initializr で作成したプロジェクトを、IntelliJで開いている」という状況を想定/前提として、解説を進めます。
◆ 導入の準備
ここでは「Spring Boot アプリへの TypeScript 導入《準備》」を、次の手順で済ませます。
- トップページの表示
- package.json のインストール
各ステップの詳細は、実際のコードを見せながら解説します
準備①:トップページの表示
ここでは、Spring Bootアプリのトップページに index.html
を表示させていきます。
まず、src/main/resources/templates/
内に index.html
ファイルを作成し、中身をカスタマイズします。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Top</title>
</head>
<body>
<h1>Welcome!</h1>
<p>Lorem Ipsum</p>
</body>
</html>
次に、src/main/java/com.project.sprigbootproto/
へRequestCntroller
(任意の名前)というコントローラー用のJavaクラスを作成します。
そして、「index.html
= トップページを表示して 🥺 」というリクエストに対応する処理を、次のように記述します。
package com.project.sprigbootproto;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class RequestController {
@GetMapping /* ("/") はアリ/ナシどっちでもOK */
public String index(){
return "index";
}
}
ここで一度、アプリを実行して localhost:8080
へアクセスし、トップページが正しく表示されるかどうかを確認しましょう。
正しく表示されていることを確認できたら、アプリの実行を停止します。
これで、準備①「トップページの表示」は完了です
準備②: package.json のインストール
ここでは、プロジェクトに package.json
を追加します。
まず、ターミナルで次のコマンドを実行します。
(アプリのディレクトリへ移動)
% npm init -y # -y オプションを付ければ、単に `npm init` を実行したときに答えなきゃいけない ”yes問答” を全スキップできる
プロジェクトに次のファイルが追加されれば、成功です。
-
package.json
「
Node.js
?npm
?? 何やそれ」という状態のとき、ひとまずのザックリ理解に役立った記事を紹介しておきますね。
これで、準備②「package.json のインストール」は完了です
◆ 具体的な導入手順《本編》
ここでは、「Spring Boot アプリへの TypeScript 導入《本編》」を、次の3つの構成で解説します。
- TypeScript・webpack のインストール
- webpack のカスタマイズ
- TypeScriptの挙動確認
各ステップの詳細は、実際のコードを見せながら解説します
STEP①:TypeScript・webpack のインストール
ここでは、プロジェクトへtypescript
と webpack
( + 関連パッケージ)をインストールします。
先に、typescript
だけインストールします。
具体的には、ターミナルで次のコマンドを実行します。
% npm install -D typescript # `-D` は `--save-dev` でもOK
プロジェクトに次のファイル追加/編集がされれば、成功です。
-
プロジェクトのルートディレクトリに
node_modules
-
package.json
>devDependencies
にtypescript:"(ver)"
-
package-lock.json
「
devDependencies
? 何やそれ」という状態のとき、ひとまずのザックリ理解に役立った記事を紹介しておきますね。
続いて、webpack
をはじめとした次のパッケージをインストールします。
-
webpack
:モジュールバンドラー -
webpack-cli
:ターミナル上で webpack を実行するためのツール -
ts-loader
:TypeScript 用ローダ
具体的には、ターミナルで次のコマンドを実行します。
% npm install -D webpack webpack-cli ts-loader
これで、STEP①「TypeScript・webpack のインストール」は完了です
STEP②:webpack のカスタマイズ
ここでは、プロジェクトで TypeScript が使えるように webpack
を細かく設定します。
まず、ルートディレクトリに webpack.config.js
というファイルを作成します。
続けて、webpack.config.js
を次のように編集します。
const path = require('path') // Node.js の `path` モジュール(ファイルパス操作の関数を提供)を読み込み
module.exports = {
// ココにカスタマイズしたい内容を記述していく
}
これで、webpack をカスタマイズしていくための雛形が完成したので、中身を埋めていきます。
具体的には、次の設定が必要です。
- エントリポイントの指定
- ローダの設定
- インポートの効率化
- 出力先の設定
- 環境の設定
STEP②-1:エントリポイントの指定
ここでは、entry
:エントリポイント(wecpack がモジュールをまとめてバンドルをつくるとき、そのモジュールの依存関係の解析を開始するファイル)を指定・作成します。
具体的には、次の記述をします。
const path = require('path')
module.exports = {
entry: './src/main/typescript/application.ts'
}
続けて、↑で指定したエントリポイントに合うように、main ディレクトリへ typescript
(任意の名前)ディレクトリを、そしてそのtypescript
ディレクトリに application.ts
(任意の名前)ファイルを作成します。
これで、STEP②-1「エントリポイントの指定」は完了です
STEP②-2:ローダの設定
ここでは、ローダ(wecpack が TypeScript のファイルをすべてロードするとき、そのロード対象の拡張子 .ts
や、使うローダの指定など)を設定します。
具体的には、次の記述をします。
const path = require('path')
module.exports = {
entry: './src/main/typescript/application.ts',
module: {
rules: [
{
test: /\.ts$/,
use: 'ts_loader', // package.json にインストール済み
exclude: /node_modules/, // `exclude`の理由:パフォーマンスの最適化・・・通常、`node_modules`内のファイルはトランスパイルの必要がないため、`exclude`処理でビルドプロセスから除外
},
],
}
}
これで、STEP②-2「ローダの設定」は完了です
STEP②-3:インポートの効率化
ここでは、インポートの効率化として、拡張子を省略できる(Ex:エントリポイントへのインポート時)ようにします。
具体的には、次の記述をします。
const path = require('path')
module.exports = {
entry: './src/main/typescript/application.ts',
module: {
rules: [
{
test: /\.ts$/,
use: 'ts_loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.ts', '.js']
}
}
これで、STEP②-3「インポートの効率化」は完了です
STEP②-4:出力先の設定
ここでは、wecpack が作成したバンドルの出力先を設定します。
具体的には、次の記述をします。
const path = require('path')
module.exports = {
entry: './src/main/typescript/application.ts',
module: {
rules: [
{
test: /\.ts$/,
use: 'ts_loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.ts', '.js']
},
output: {
filename: 'bundle.js'
path: path.resolve(__dirname, 'src/main/resources/static/javascript'),
},
}
output
の各要素は、次の意味を持ちます。
-
bundle.js
:出力先となるJavaScriptファイル*** -
path
:1行目で読み込んだ、Node.js のpath
モジュール -
resolve
:path
モジュールが持つresolve
関数(引数に渡されたパスから絶対パスを生成する)の呼び出し -
__dirname
:Node.jsのグローバル変数で、現在実行中のスクリプト(今回はwebpack.config.js
)が存在するディレクトリの絶対パスを表す。 -
'src/main/resources/static/javascript'
:出力先となるJavaScriptファイル、つまり、bundle.js
が存在するディレクトリ***の相対パスを表す。
*** bundle.js
ファイルと javascript/
ディレクトリは、あとでおこなうコマンド操作時に自動で生成されるので、自作しなくてOK
まとめると、output
のfilename
とpath
では、path.resolve(__dirname, 'src/main/resources/static/js')
によって、webpack.config.js
が存在するディレクトリの絶対パス __dirname
と'src/main/resources/static/javascript'
という相対パスを結合して、 TypeScript ファイルからトランスパイルされた JavaScript ファイルが出力されるディレクトリの絶対パスを生成されています(この絶対パスは、webpackの設定である output.path に設定され、そこにファイルが出力されるようになります)
これで、STEP②-4「出力先の設定」は完了です
STEP②-5:環境設定
ここでは、環境を設定します。
環境設定はしておきましょう。というのも、何かしら設定しておかないと、コマンド実行時に以下の警告文が表示されます。
TerminalWARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
今回は「開発環境」を想定し、次の記述をします。
const path = require('path')
module.exports = {
entry: './src/main/typescript/application.ts',
module: {
rules: [
{
test: /\.ts$/,
use: 'ts_loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.ts', '.js']
},
output: {
filename: 'bundle.js'
path: path.resolve(__dirname, 'src/main/resources/static/javascript'),
},
mode: 'development',
}
mode
をproduction
に設定すると、出力先となるbuilde.js
の記述量が本番環境向けに短く圧縮されます。
これで、STEP②-5「環境の設定」は完了です
そして、STEP②「webpack のカスタマイズ」全体も完了です
STEP③:TypeScriptの挙動確認
ここでは、TypeScript をプロジェクトへ本格的に組み込んでいきます。
具体的には、次の実装が必要です。
- TypeScriptの設定
- TypeScriptの記述
- Webページでの描画確認
STEP③-1:TypeScriptの設定
ここでは、具体的なTypeScriptを書いていく前に、TypeScript 自体の設定をします。
具体的には、ターミナルで次のコマンドを実行します。
% npx tsc --init # `init` だとエラーが出るので注意!
次の表示/追加/編集があれば、成功です。
- ターミナルに↓の表示
-
プロジェクトのルートディレクトリに
tscinfig.json
追加
% npx tsc --init
Created a new tsconfig.json with:
target: es2016
module: commonjs
strict: true
esModuleInterop: true
skipLibCheck: true
forceConsistentCasingInFileNames: true
You can learn more at https://aka.ms/tsconfig
ここで、追加された tscinfig.json
の中身を見てください。
そして、次の"compilerOptions"
というハッシュ配列の中で、以下をコメントアウトしてください(その他デフォルトでコメントアウトが外れていたキー/バリューは、基本的に外しっぱなしでOK)
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"sourceMap": true,
"outDir": "./dist",
"esModuleInterop": true
"strict": true
}
}
続けて、次の"include"
と"exclude"
という配列を追加
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"sourceMap": true,
"outDir": "./dist",
"esModuleInterop": true
"strict": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"] /* パフォーマンスの最適化:通常、`node_modules`内のファイルはトランスパイルの必要がないため、`exclude`によりビルドプロセスから除外 */
}
これで、STEP③-1「TypeScriptの設定」は完了です
STEP③-2:TypeScriptの記述
ここでは、実際に簡単なTypeScriptを書いていきます。
具体的には、導入の準備で用意したindex.html
上のdiv
タグに、”Hello World”といった好きな文字列を表示させるためのTypeScriptを記述します。
まず、/src/main/typescript/
ディレクトリ、つまり、application.ts
と同じディレクトリにdemoCheck.ts
(任意の名前)を作成します。
続けて、作成した.ts
ファイルを次のように編集します。
exports default class Greeting {
phrase: string;
constructor(phrase: string) {
this.phrase = phrase
}
public sayPhrase(element: HTMLElement | null){
if(element) {
element.innerText = this.phrase
}
}
}
次に、index.html
を次のように編集します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Top</title>
</head>
<body>
<h1>Welcome!</h1>
<p>Lorem Ipsum</p>
<div id="demo"></div> <!-- 追加 -->
</body>
</html>
次に、application.ts
(エントリポイント)を次のように編集します。
import Greeting from "./demoCheck";
const demo: HTMLElement | null = document.getElementById("demo");
const phrase = new Greeting("Hello world");
phrase.sayPhrase(demo);
これで、STEP③-2「TypeScriptの記述」は完了です
STEP③-3: Webページでの描画確認
それでは、TypeScriptが思い通りに処理できているかどうかをブラウザで確かめます。
まず、TypeScriptをトランスパイルした結果をstatic
ディレクトリにJavaScriptファイル bundle.js
として出力するために、package.json
の"scripts"
を次のように編集します。
{
"name": "sprigbootproto",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack" /// 編集
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"ts-loader": "^9.5.1",
"typescript": "^5.3.3",
"webpack": "^5.90.3",
"webpack-cli": "^5.1.4"
}
}
次に、ターミナルで次のコマンドを実行します。
% npm run build
プロジェクトのstatic
ディレクトリに、以下のディレクトリ/ファイルが追加されれば、成功です。
-
javascript/
-
bundle.js
この操作によって、作成したTypeScriptファイルがコンパイルされて、それらがバンドルされたものがbundle.js
に出力されるようになりました。
続けて、index.html
の<body>
内を次のように編集します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Top</title>
</head>
<body>
<h1>Welcome!</h1>
<p>Lorem Ipsum</p>
<div id="demo"></div>
<script src="/javascript/bundle.js"></script> <!-- 追加 -->
</body>
</html>
この追記によって、DOMに変換されたHTMLにJavaScriptが装飾されるようになりました。
ここまで来たら、Spring Bootアプリを起動し、localhost:8080
にアクセスします。
そして、STEP③-2で指定した文字列が表示されていれば成功です
これでSTEP③完了、つまり「Spring BootへのTypeScript導入手順」は終了です
お疲れ様でした。
◆ TypeScriptが反映されないときの対処法
ここでは「Spring BootアプリにTypeScriptが反映されない... 」と困ったときに当てはまるかもしれない、未反映の原因とその解決策を紹介します。
- 再ビルド忘れ
- bundle.js へのパス指定ミス
各原因の具体的な解決策は、以降で提案しますね。
原因①:再ビルド忘れ
「再ビルド(コンパイル)がおこなわれていない」ことが原因で TypeScriptの変更が反映されていない可能性があります...
そんなときは、次を試してみてください。
解決策①:CLIからビルドする
% npm run build
繰り返しになりますが、Spring Bootでは静的リソース、つまり resources/static/
ディレクトリ内の .css
や .js
などのファイルの「コンパイル→再ビルド」というプロセスが、デフォルトだと自動でおこなわれません。
したがって、↑のコマンドを打たないままTypeScriptを編集→アプリを実行(サーバーを起動)しても、編集内容はビルドされない、つまりTypeScriptが反映されない... となるので、注意しましょう。
原因②:bundle.js へのパス指定ミス
「HTMLファイル上でbuild.js
へのパスが正しく設定されていない」ことが原因で TypeScriptが反映されていない可能性があります...
そんなときは、次を試してみてください。
プログラミング学習のアウトプット、以上になります。
ご指摘あれば、お気軽にコメントください。
参考 + 感謝:【Typescript入門】本当の初心者からTypescript×Webpackの開発環境構築までをハンズオン形式で学ぼう!
参考 + 感謝:TypeScript《webpack公式》