htmx で多段階ウィザードフォームを実装するコツ

Web アプリケーション開発において、複雑なフォームを扱う際に避けて通れないのが「多段階ウィザードフォーム」です。従来の SPA(Single Page Application)では、状態管理の複雑さやページ遷移の重さに悩まされてきました。
しかし、htmx(Hypertext Markup Language eXtended)の登場により、この課題は劇的に改善されました。htmx を使えば、JavaScript を最小限に抑えながら、スムーズで直感的なウィザードフォームを実装できるのです。
この記事では、htmx を使った多段階ウィザードフォームの実装コツを、実際のコード例と共に詳しく解説していきます。初心者の方でも理解しやすいよう、段階的に説明していきましょう。
htmx の基本概念とウィザードフォーム
htmx は、HTML に動的な機能を追加するライブラリです。従来の JavaScript フレームワークとは異なり、サーバーサイドの HTML レスポンスを直接 DOM に反映させることで、シンプルで効率的なインタラクションを実現します。
htmx の基本属性
ウィザードフォームで使用する主要な htmx 属性を理解しましょう。
html<!-- 基本的なhtmx属性の例 -->
<div
hx-post="/api/step1"
hx-target="#wizard-content"
hx-swap="innerHTML"
>
<button type="submit">次へ進む</button>
</div>
このコードでは、hx-post
で POST リクエストを送信し、hx-target
で更新対象を指定し、hx-swap
で更新方法を定義しています。
ウィザードフォームとの相性
htmx は特にウィザードフォームと相性が良い理由があります:
- 状態管理の簡素化: サーバーサイドで状態を管理できる
- SEO フレンドリー: 各段階が独立した URL として扱える
- プログレッシブエンハンスメント: JavaScript が無効でも基本機能が動作する
多段階ウィザードの設計思想
多段階ウィザードフォームを設計する際は、以下の 3 つの原則を押さえることが重要です。
1. 段階の独立性
各段階は独立した機能を持つべきです。これにより、保守性と再利用性が向上します。
html<!-- 段階ごとに独立したフォーム -->
<form hx-post="/wizard/step1" hx-target="#wizard-container">
<div class="step-content">
<h3>基本情報入力</h3>
<input
type="text"
name="name"
placeholder="お名前"
required
/>
<input
type="email"
name="email"
placeholder="メールアドレス"
required
/>
</div>
<div class="step-actions">
<button type="submit">次へ進む</button>
</div>
</form>
2. データの永続性
段階間でデータを失わないよう、適切な保存戦略を立てます。
html<!-- 隠しフィールドでデータを保持 -->
<form hx-post="/wizard/step2" hx-target="#wizard-container">
<input
type="hidden"
name="step1_data"
value="{{ step1_data }}"
/>
<div class="step-content">
<h3>詳細情報入力</h3>
<input
type="text"
name="company"
placeholder="会社名"
/>
<input type="text" name="position" placeholder="役職" />
</div>
<div class="step-actions">
<button type="button" hx-get="/wizard/step1">
戻る
</button>
<button type="submit">次へ進む</button>
</div>
</form>
3. ユーザビリティの最優先
ユーザーが迷わないよう、現在位置と進捗を明確に示します。
段階管理の実装方法
段階管理は、ウィザードフォームの核となる機能です。htmx を使った実装方法を見ていきましょう。
基本的な段階管理システム
まず、段階を管理する基本的な HTML 構造を作成します。
html<!-- ウィザードの基本構造 -->
<div id="wizard-container" class="wizard">
<div class="wizard-progress">
<div class="step active" data-step="1">基本情報</div>
<div class="step" data-step="2">詳細情報</div>
<div class="step" data-step="3">確認</div>
<div class="step" data-step="4">完了</div>
</div>
<div id="wizard-content">
<!-- 各段階のコンテンツがここに動的に挿入される -->
</div>
</div>
段階遷移の実装
段階間の遷移を制御する JavaScript コードです。
javascript// 段階遷移の制御
document.addEventListener(
'htmx:afterRequest',
function (event) {
const currentStep = event.detail.xhr.getResponseHeader(
'X-Current-Step'
);
const totalSteps =
event.detail.xhr.getResponseHeader('X-Total-Steps');
if (currentStep && totalSteps) {
updateProgress(
parseInt(currentStep),
parseInt(totalSteps)
);
}
}
);
function updateProgress(current, total) {
// プログレスバーの更新
const progressBar = document.querySelector(
'.wizard-progress'
);
const steps = progressBar.querySelectorAll('.step');
steps.forEach((step, index) => {
const stepNumber = index + 1;
step.classList.remove('active', 'completed');
if (stepNumber < current) {
step.classList.add('completed');
} else if (stepNumber === current) {
step.classList.add('active');
}
});
}
サーバーサイドでの段階管理
Node.js/Express での段階管理の実装例です。
javascript// Express.jsでの段階管理
app.post('/wizard/step1', (req, res) => {
const { name, email } = req.body;
// バリデーション
if (!name || !email) {
return res.status(400).send(`
<div class="error-message">
<p>必須項目を入力してください。</p>
<form hx-post="/wizard/step1" hx-target="#wizard-content">
<input type="text" name="name" value="${
name || ''
}" placeholder="お名前" required>
<input type="email" name="email" value="${
email || ''
}" placeholder="メールアドレス" required>
<button type="submit">次へ進む</button>
</form>
</div>
`);
}
// セッションにデータを保存
req.session.wizardData = {
...req.session.wizardData,
name,
email,
};
// 次の段階のHTMLを返す
res.setHeader('X-Current-Step', '2');
res.setHeader('X-Total-Steps', '4');
res.send(renderStep2());
});
状態保持のテクニック
ウィザードフォームでは、段階間でデータを失わないことが重要です。htmx での状態保持のテクニックを紹介します。
セッション管理による状態保持
サーバーサイドでセッションを使って状態を管理する方法です。
javascript// セッション管理の設定
const session = require('express-session');
app.use(
session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: true,
cookie: { secure: false }, // 本番環境ではtrueに設定
})
);
// 段階データの保存
app.post('/wizard/step2', (req, res) => {
const { company, position } = req.body;
// 既存データと新しいデータをマージ
req.session.wizardData = {
...req.session.wizardData,
company,
position,
};
// 次の段階へ
res.setHeader('X-Current-Step', '3');
res.send(renderStep3(req.session.wizardData));
});
クライアントサイドでの状態保持
htmx のhx-vals
属性を使って、クライアントサイドでも状態を保持できます。
html<!-- クライアントサイドでの状態保持 -->
<form
hx-post="/wizard/step3"
hx-target="#wizard-content"
hx-vals='{"step1_data": "{{ step1_data }}", "step2_data": "{{ step2_data }}"}'
>
<div class="step-content">
<h3>確認画面</h3>
<div class="confirmation-data">
<p><strong>お名前:</strong> {{ name }}</p>
<p><strong>メールアドレス:</strong> {{ email }}</p>
<p><strong>会社名:</strong> {{ company }}</p>
<p><strong>役職:</strong> {{ position }}</p>
</div>
</div>
<div class="step-actions">
<button type="button" hx-get="/wizard/step2">
戻る
</button>
<button type="submit">送信する</button>
</div>
</form>
エラー時の状態復元
エラーが発生した際に、入力済みデータを復元する仕組みも重要です。
javascript// エラー時の状態復元
app.post('/wizard/step1', (req, res) => {
const { name, email } = req.body;
// バリデーションエラー
if (!name || !email) {
const savedData = req.session.wizardData || {};
return res.status(400).send(`
<div class="error-message">
<p>必須項目を入力してください。</p>
</div>
<form hx-post="/wizard/step1" hx-target="#wizard-content">
<input type="text" name="name" value="${
savedData.name || ''
}" placeholder="お名前" required>
<input type="email" name="email" value="${
savedData.email || ''
}" placeholder="メールアドレス" required>
<button type="submit">次へ進む</button>
</form>
`);
}
// 正常処理...
});
バリデーションとエラーハンドリング
適切なバリデーションとエラーハンドリングは、ユーザー体験を大きく左右します。
リアルタイムバリデーション
htmx のhx-trigger
属性を使って、リアルタイムバリデーションを実装できます。
html<!-- リアルタイムバリデーション -->
<form hx-post="/wizard/step1" hx-target="#wizard-content">
<div class="form-group">
<label for="email">メールアドレス</label>
<input
type="email"
id="email"
name="email"
hx-post="/validate/email"
hx-trigger="blur"
hx-target="#email-error"
hx-swap="innerHTML"
/>
<div id="email-error"></div>
</div>
<button type="submit">次へ進む</button>
</form>
サーバーサイドバリデーション
サーバーサイドでの包括的なバリデーション処理です。
javascript// サーバーサイドバリデーション
app.post('/wizard/step1', (req, res) => {
const { name, email } = req.body;
const errors = [];
// 名前のバリデーション
if (!name || name.trim().length === 0) {
errors.push('お名前は必須です');
} else if (name.length > 50) {
errors.push('お名前は50文字以内で入力してください');
}
// メールアドレスのバリデーション
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!email || !emailRegex.test(email)) {
errors.push('有効なメールアドレスを入力してください');
}
// エラーがある場合
if (errors.length > 0) {
return res.status(400).send(`
<div class="error-summary">
<h4>入力内容に問題があります</h4>
<ul>
${errors
.map((error) => `<li>${error}</li>`)
.join('')}
</ul>
</div>
<form hx-post="/wizard/step1" hx-target="#wizard-content">
<input type="text" name="name" value="${
name || ''
}" placeholder="お名前" required>
<input type="email" name="email" value="${
email || ''
}" placeholder="メールアドレス" required>
<button type="submit">次へ進む</button>
</form>
`);
}
// 正常処理...
});
エラーメッセージの表示
ユーザーフレンドリーなエラーメッセージの表示方法です。
html<!-- エラーメッセージの表示 -->
<div class="form-container">
<div id="error-messages" class="error-container"></div>
<form
hx-post="/wizard/step1"
hx-target="#wizard-content"
hx-swap="innerHTML"
>
<div class="form-group">
<label for="name">お名前 *</label>
<input type="text" id="name" name="name" required />
<div class="field-error" id="name-error"></div>
</div>
<div class="form-group">
<label for="email">メールアドレス *</label>
<input
type="email"
id="email"
name="email"
required
/>
<div class="field-error" id="email-error"></div>
</div>
<button type="submit">次へ進む</button>
</form>
</div>
プログレス表示の実装
ユーザーが現在の位置を把握できるよう、プログレス表示を実装しましょう。
プログレスバーの実装
視覚的に分かりやすいプログレスバーを作成します。
html<!-- プログレスバーのHTML -->
<div class="wizard-progress">
<div class="progress-bar">
<div class="progress-fill" id="progress-fill"></div>
</div>
<div class="step-indicators">
<div class="step active" data-step="1">
<div class="step-number">1</div>
<div class="step-label">基本情報</div>
</div>
<div class="step" data-step="2">
<div class="step-number">2</div>
<div class="step-label">詳細情報</div>
</div>
<div class="step" data-step="3">
<div class="step-number">3</div>
<div class="step-label">確認</div>
</div>
<div class="step" data-step="4">
<div class="step-number">4</div>
<div class="step-label">完了</div>
</div>
</div>
</div>
プログレス更新の JavaScript
段階が変わるたびにプログレスを更新する処理です。
javascript// プログレス更新の処理
function updateWizardProgress(currentStep, totalSteps) {
// プログレスバーの更新
const progressFill =
document.getElementById('progress-fill');
const percentage = (currentStep / totalSteps) * 100;
progressFill.style.width = `${percentage}%`;
// ステップインジケーターの更新
const steps = document.querySelectorAll('.step');
steps.forEach((step, index) => {
const stepNumber = index + 1;
step.classList.remove('active', 'completed');
if (stepNumber < currentStep) {
step.classList.add('completed');
} else if (stepNumber === currentStep) {
step.classList.add('active');
}
});
}
// htmxイベントでプログレスを更新
document.addEventListener(
'htmx:afterRequest',
function (event) {
const currentStep = event.detail.xhr.getResponseHeader(
'X-Current-Step'
);
const totalSteps =
event.detail.xhr.getResponseHeader('X-Total-Steps');
if (currentStep && totalSteps) {
updateWizardProgress(
parseInt(currentStep),
parseInt(totalSteps)
);
}
}
);
プログレスバーのスタイル
見た目を整える CSS スタイルです。
css/* プログレスバーのスタイル */
.wizard-progress {
margin-bottom: 2rem;
}
.progress-bar {
width: 100%;
height: 8px;
background-color: #e0e0e0;
border-radius: 4px;
overflow: hidden;
margin-bottom: 1rem;
}
.progress-fill {
height: 100%;
background-color: #007bff;
transition: width 0.3s ease;
}
.step-indicators {
display: flex;
justify-content: space-between;
align-items: center;
}
.step {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.step-number {
width: 32px;
height: 32px;
border-radius: 50%;
background-color: #e0e0e0;
color: #666;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
margin-bottom: 0.5rem;
transition: all 0.3s ease;
}
.step.active .step-number {
background-color: #007bff;
color: white;
}
.step.completed .step-number {
background-color: #28a745;
color: white;
}
.step-label {
font-size: 0.875rem;
color: #666;
text-align: center;
}
.step.active .step-label {
color: #007bff;
font-weight: bold;
}
レスポンシブ対応のポイント
モバイルデバイスでも使いやすいウィザードフォームにするためのポイントを紹介します。
モバイルファーストのレイアウト
小さな画面でも見やすいレイアウトを設計します。
html<!-- レスポンシブ対応のウィザード -->
<div class="wizard-container">
<!-- モバイル用のプログレス表示 -->
<div class="mobile-progress">
<div class="progress-text">
ステップ <span id="current-step">1</span> /
<span id="total-steps">4</span>
</div>
<div class="progress-bar">
<div
class="progress-fill"
id="mobile-progress-fill"
></div>
</div>
</div>
<!-- デスクトップ用のプログレス表示 -->
<div class="desktop-progress">
<!-- 既存のプログレスバー -->
</div>
<div id="wizard-content" class="wizard-content">
<!-- ウィザードのコンテンツ -->
</div>
</div>
レスポンシブ CSS
画面サイズに応じて表示を調整する CSS です。
css/* レスポンシブ対応のCSS */
.wizard-container {
max-width: 800px;
margin: 0 auto;
padding: 1rem;
}
/* モバイル用プログレス(デフォルトで表示) */
.mobile-progress {
display: block;
margin-bottom: 1rem;
}
.desktop-progress {
display: none;
}
.progress-text {
text-align: center;
margin-bottom: 0.5rem;
font-weight: bold;
color: #333;
}
/* タブレット以上でデスクトップ用プログレスを表示 */
@media (min-width: 768px) {
.mobile-progress {
display: none;
}
.desktop-progress {
display: block;
}
.wizard-container {
padding: 2rem;
}
}
/* フォーム要素のレスポンシブ対応 */
.form-group {
margin-bottom: 1rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: bold;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
/* ボタンのレスポンシブ対応 */
.step-actions {
display: flex;
gap: 1rem;
justify-content: space-between;
margin-top: 2rem;
}
.step-actions button {
flex: 1;
padding: 0.75rem 1.5rem;
border: none;
border-radius: 4px;
font-size: 1rem;
cursor: pointer;
transition: background-color 0.3s ease;
}
.step-actions .btn-primary {
background-color: #007bff;
color: white;
}
.step-actions .btn-secondary {
background-color: #6c757d;
color: white;
}
/* モバイルではボタンを縦並びに */
@media (max-width: 480px) {
.step-actions {
flex-direction: column;
}
.step-actions button {
margin-bottom: 0.5rem;
}
}
タッチフレンドリーなインターフェース
モバイルデバイスでの操作性を向上させる工夫です。
css/* タッチフレンドリーなスタイル */
@media (max-width: 768px) {
/* タッチターゲットの最小サイズ */
button,
input[type='submit'],
input[type='button'] {
min-height: 44px;
min-width: 44px;
}
/* フォーム要素のタッチ最適化 */
input,
select,
textarea {
font-size: 16px; /* iOSでズームを防ぐ */
padding: 12px;
}
/* チェックボックスとラジオボタンの拡大 */
input[type='checkbox'],
input[type='radio'] {
transform: scale(1.2);
margin-right: 8px;
}
}
アクセシビリティの考慮
すべてのユーザーが使いやすいウィザードフォームにするためのアクセシビリティ対応です。
ARIA 属性の活用
スクリーンリーダーなどの支援技術に対応する ARIA 属性を追加します。
html<!-- アクセシビリティ対応のウィザード -->
<div
class="wizard"
role="application"
aria-label="多段階フォーム"
>
<div
class="wizard-progress"
role="progressbar"
aria-valuenow="1"
aria-valuemin="1"
aria-valuemax="4"
>
<div class="progress-bar">
<div class="progress-fill" id="progress-fill"></div>
</div>
<div class="step-indicators" role="tablist">
<div
class="step active"
role="tab"
aria-selected="true"
aria-label="ステップ1: 基本情報"
>
<div class="step-number" aria-hidden="true">1</div>
<div class="step-label">基本情報</div>
</div>
<div
class="step"
role="tab"
aria-selected="false"
aria-label="ステップ2: 詳細情報"
>
<div class="step-number" aria-hidden="true">2</div>
<div class="step-label">詳細情報</div>
</div>
<!-- 他のステップ... -->
</div>
</div>
<div
id="wizard-content"
role="tabpanel"
aria-labelledby="current-step"
>
<!-- ウィザードのコンテンツ -->
</div>
</div>
キーボードナビゲーション
キーボードだけで操作できるようにします。
javascript// キーボードナビゲーションの実装
document.addEventListener('keydown', function (event) {
const currentStep =
document.querySelector('.step.active');
const steps = document.querySelectorAll('.step');
const currentIndex =
Array.from(steps).indexOf(currentStep);
switch (event.key) {
case 'ArrowLeft':
if (currentIndex > 0) {
event.preventDefault();
navigateToStep(currentIndex);
}
break;
case 'ArrowRight':
if (currentIndex < steps.length - 1) {
event.preventDefault();
navigateToStep(currentIndex + 2);
}
break;
case 'Enter':
// フォーム送信
const form = document.querySelector(
'#wizard-content form'
);
if (form) {
form.requestSubmit();
}
break;
}
});
function navigateToStep(stepNumber) {
// 段階遷移の処理
htmx.ajax('GET', `/wizard/step${stepNumber}`, {
target: '#wizard-content',
swap: 'innerHTML',
});
}
エラー通知の改善
エラーメッセージを適切に通知する仕組みです。
html<!-- アクセシビリティ対応のエラー表示 -->
<div class="form-container">
<div
id="error-summary"
class="error-summary"
role="alert"
aria-live="polite"
aria-atomic="true"
></div>
<form
hx-post="/wizard/step1"
hx-target="#wizard-content"
hx-swap="innerHTML"
>
<div class="form-group">
<label for="name" id="name-label">お名前 *</label>
<input
type="text"
id="name"
name="name"
aria-labelledby="name-label"
aria-describedby="name-error"
aria-invalid="false"
required
/>
<div
class="field-error"
id="name-error"
role="alert"
aria-live="polite"
></div>
</div>
<button type="submit" aria-describedby="submit-help">
次へ進む
</button>
<div id="submit-help" class="sr-only">
フォームを送信して次の段階に進みます
</div>
</form>
</div>
スクリーンリーダー用のスタイル
視覚的に隠しつつ、スクリーンリーダーには読み上げられる要素のスタイルです。
css/* スクリーンリーダー専用のスタイル */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* フォーカス表示の改善 */
button:focus,
input:focus,
select:focus,
textarea:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
}
/* エラー状態の視覚的表示 */
input[aria-invalid='true'] {
border-color: #dc3545;
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
}
.field-error {
color: #dc3545;
font-size: 0.875rem;
margin-top: 0.25rem;
}
パフォーマンス最適化
htmx ウィザードフォームのパフォーマンスを最適化するテクニックを紹介します。
キャッシュ戦略
適切なキャッシュ設定でパフォーマンスを向上させます。
javascript// キャッシュ戦略の実装
app.get('/wizard/step1', (req, res) => {
// 静的コンテンツのキャッシュ設定
res.setHeader('Cache-Control', 'public, max-age=300'); // 5分間キャッシュ
const html = renderStep1();
res.send(html);
});
app.post('/wizard/step1', (req, res) => {
// 動的コンテンツはキャッシュしない
res.setHeader(
'Cache-Control',
'no-cache, no-store, must-revalidate'
);
res.setHeader('Pragma', 'no-cache');
res.setHeader('Expires', '0');
// 処理...
});
遅延読み込み
必要に応じてコンテンツを遅延読み込みします。
html<!-- 遅延読み込みの実装 -->
<div class="wizard-content">
<div id="step1-content" class="step-content active">
<!-- 最初の段階のコンテンツ -->
</div>
<div
id="step2-content"
class="step-content"
hx-get="/wizard/step2"
hx-trigger="load once"
>
<div class="loading">読み込み中...</div>
</div>
<div
id="step3-content"
class="step-content"
hx-get="/wizard/step3"
hx-trigger="load once"
>
<div class="loading">読み込み中...</div>
</div>
</div>
画像の最適化
画像の読み込みを最適化します。
html<!-- 画像の最適化 -->
<div class="wizard-step">
<img
src="/images/placeholder.jpg"
data-src="/images/actual-image.jpg"
class="lazy-image"
alt="説明画像"
loading="lazy"
/>
</div>
javascript// 遅延読み込みのJavaScript
document.addEventListener('DOMContentLoaded', function () {
const lazyImages =
document.querySelectorAll('.lazy-image');
const imageObserver = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy-image');
observer.unobserve(img);
}
});
}
);
lazyImages.forEach((img) => imageObserver.observe(img));
});
バンドルサイズの最適化
htmx の読み込みを最適化します。
html<!-- htmxの最適化された読み込み -->
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>ウィザードフォーム</title>
<!-- htmxをCDNから読み込み -->
<script
src="https://unpkg.com/htmx.org@1.9.10"
integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC"
crossorigin="anonymous"
></script>
<!-- プリロードでパフォーマンス向上 -->
<link
rel="preload"
href="/wizard/step2"
as="fetch"
crossorigin
/>
<link
rel="preload"
href="/wizard/step3"
as="fetch"
crossorigin
/>
</head>
<body>
<!-- ウィザードのコンテンツ -->
</body>
</html>
まとめ
htmx を使った多段階ウィザードフォームの実装は、従来の JavaScript フレームワークとは異なるアプローチを提供します。サーバーサイドの力を最大限活用しながら、シンプルで保守性の高いコードを書けるのが大きな魅力です。
今回紹介したテクニックを活用することで、以下のようなメリットが得られます:
- 開発効率の向上: 複雑な状態管理が不要
- SEO フレンドリー: 各段階が独立した URL として扱える
- アクセシビリティ: プログレッシブエンハンスメントの実現
- パフォーマンス: 軽量で高速な動作
特に、段階管理の実装方法や状態保持のテクニックは、他のプロジェクトでも応用できる汎用的な知識です。バリデーションとエラーハンドリングの実装は、ユーザー体験を大きく左右する重要な要素ですので、しっかりと理解しておきましょう。
htmx の真の価値は、Web の本来の仕組みを活かしながら、モダンなインタラクションを実現できる点にあります。この記事で紹介したコツを参考に、あなただけの素晴らしいウィザードフォームを作成してください。
関連リンク
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来