TauriでReact/Vue/Svelteアプリをデスクトップ化する

Webアプリケーションをデスクトップアプリに変換したいと思ったことはありませんか?Electronは重いし、ネイティブアプリの開発は複雑すぎる...そんな悩みを解決してくれるのがTauriです。
Tauriは、Rustで書かれた軽量で高速なデスクトップアプリケーションフレームワークです。Web技術(HTML、CSS、JavaScript)を使ってUIを作成し、Rustのバックエンドでネイティブな機能を提供します。
この記事では、React、Vue、SvelteそれぞれのフレームワークでTauriアプリを作成する方法を、実際のコードとエラー例を交えて詳しく解説します。あなたのWebアプリを、軽量で美しいデスクトップアプリに変身させましょう。
Tauriとは
Tauriは、Web技術とRustを組み合わせた次世代のデスクトップアプリケーションフレームワークです。Electronと比較して、以下のような特徴があります。
Tauriの主な特徴
特徴 | 説明 |
---|---|
軽量性 | 最終的なアプリサイズがElectronの1/10程度 |
高速性 | Rustのバックエンドにより高いパフォーマンス |
セキュリティ | デフォルトでセキュアな設計 |
クロスプラットフォーム | Windows、macOS、Linuxをサポート |
Web技術対応 | 既存のWebアプリを簡単に統合可能 |
Electronとの比較
Electronは確かに便利ですが、Chromiumエンジン全体を含むため、アプリサイズが大きくなりがちです。一方、TauriはシステムのネイティブWebビューを使用するため、非常に軽量です。
実際のサイズ比較を見てみましょう:
makefileElectronアプリ: 約120MB
Tauriアプリ: 約12MB
この差は、ユーザーのダウンロード時間やストレージ使用量に直接影響します。
開発環境の準備
Tauriアプリの開発を始める前に、必要な環境を整えましょう。
必要なツール
Tauriの開発には以下のツールが必要です:
- Node.js (v16以上)
- Rust (最新版)
- システム固有の依存関係
Rustのインストール
まず、Rustをインストールします。ターミナルで以下のコマンドを実行してください:
bashcurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
インストール後、ターミナルを再起動するか、以下のコマンドで環境変数を更新します:
bashsource ~/.cargo/env
Tauri CLIのインストール
Rustがインストールされたら、Tauri CLIをインストールします:
bashcargo install tauri-cli
システム固有の依存関係
macOSの場合
Xcode Command Line Toolsが必要です:
bashxcode-select --install
Windowsの場合
Microsoft Visual Studio C++ Build Toolsが必要です。Visual Studio Installerから「C++によるデスクトップ開発」ワークロードをインストールしてください。
Linuxの場合
以下のパッケージをインストールします:
bash# Ubuntu/Debian
sudo apt update
sudo apt install libwebkit2gtk-4.0-dev \
build-essential \
curl \
wget \
libssl-dev \
libgtk-3-dev \
libayatana-appindicator3-dev \
librsvg2-dev
よくあるエラーと対処法
エラー1: rustc
が見つからない
basherror: command 'rustc' not found
対処法: Rustが正しくインストールされているか確認してください:
bashrustc --version
cargo --version
エラー2: システム依存関係の不足
vbneterror: linking with `cc` failed: exit code: 1
対処法: 上記のシステム固有の依存関係をインストールしてください。
Reactアプリのデスクトップ化
ReactアプリをTauriでデスクトップ化する手順を詳しく見ていきましょう。
新しいReact + Tauriプロジェクトの作成
Tauri CLIを使って、ReactアプリとTauriを統合したプロジェクトを作成します:
bash# プロジェクトディレクトリを作成
mkdir my-tauri-react-app
cd my-tauri-react-app
# Tauriプロジェクトを初期化
cargo tauri init
初期化時に以下の質問が表示されます:
scss? What is your app name? (my-tauri-react-app)
? What should the window title be? (Tauri App)
? Where are your web assets (HTML/CSS/JS) located relative to the tauri.conf.json file? (../dist)
? What is the url of your dev server? (http://localhost:1420)
? What is your frontend dev command? (npm run dev)
? What is your frontend build command? (npm run build)
Reactアプリの作成
次に、Reactアプリを作成します。Viteを使用することをお勧めします:
bash# ViteでReactプロジェクトを作成
yarn create vite frontend --template react-ts
cd frontend
# 依存関係をインストール
yarn install
Tauriの設定ファイル
src-tauri/tauri.conf.json
ファイルを確認しましょう:
json{
"build": {
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn build",
"devPath": "http://localhost:1420",
"distDir": "../frontend/dist"
},
"tauri": {
"allowlist": {
"all": false
},
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.tauri.dev",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
},
"security": {
"csp": null
},
"windows": [
{
"fullscreen": false,
"resizable": true,
"title": "Tauri App",
"width": 800,
"height": 600
}
]
}
}
開発サーバーの起動
フロントエンドとTauriアプリを同時に起動します:
bash# プロジェクトルートで実行
cargo tauri dev
よくあるエラーと対処法
エラー1: ポートが使用中
vbneterror: failed to start frontend dev server
対処法: 別のポートを使用するか、既存のプロセスを終了してください:
bash# ポート1420を使用しているプロセスを確認
lsof -i :1420
# プロセスを終了
kill -9 <PID>
エラー2: ビルドコマンドの失敗
vbneterror: failed to build frontend
対処法: フロントエンドの依存関係が正しくインストールされているか確認してください:
bashcd frontend
yarn install
yarn build
ReactアプリでのTauri API使用例
ReactアプリからTauriのネイティブ機能を使用する例を見てみましょう:
typescript// App.tsx
import { useState } from 'react'
import { invoke } from '@tauri-apps/api/tauri'
import { open } from '@tauri-apps/api/dialog'
function App() {
const [greetMsg, setGreetMsg] = useState("")
const [name, setName] = useState("")
async function greet() {
// Rustの関数を呼び出し
setGreetMsg(await invoke("greet", { name }))
}
async function openFile() {
// ファイル選択ダイアログを開く
const selected = await open({
multiple: false,
filters: [{
name: 'Text',
extensions: ['txt', 'md']
}]
})
console.log(selected)
}
return (
<div className="container">
<h1>Tauri + React</h1>
<div className="row">
<input
onChange={(e) => setName(e.currentTarget.value)}
placeholder="名前を入力..."
/>
<button type="button" onClick={greet}>
挨拶
</button>
</div>
<button type="button" onClick={openFile}>
ファイルを開く
</button>
<p>{greetMsg}</p>
</div>
)
}
export default App
Vueアプリのデスクトップ化
Vue.jsアプリをTauriでデスクトップ化する方法を解説します。
Vue + Tauriプロジェクトの作成
VueアプリとTauriを統合したプロジェクトを作成しましょう:
bash# プロジェクトディレクトリを作成
mkdir my-tauri-vue-app
cd my-tauri-vue-app
# Tauriプロジェクトを初期化
cargo tauri init
Vueアプリの作成
Viteを使用してVueアプリを作成します:
bash# ViteでVueプロジェクトを作成
yarn create vite frontend --template vue-ts
cd frontend
# 依存関係をインストール
yarn install
Tauriの設定
src-tauri/tauri.conf.json
を以下のように設定します:
json{
"build": {
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn build",
"devPath": "http://localhost:1420",
"distDir": "../frontend/dist"
},
"tauri": {
"allowlist": {
"dialog": {
"all": true
},
"fs": {
"all": true,
"scope": ["$APPDATA/*", "$APPDATA/documents/*"]
}
},
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.tauri.vue.dev",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
},
"security": {
"csp": null
},
"windows": [
{
"fullscreen": false,
"resizable": true,
"title": "Vue Tauri App",
"width": 900,
"height": 700
}
]
}
}
VueアプリでのTauri API使用例
VueアプリからTauriの機能を使用する例を示します:
vue<!-- App.vue -->
<template>
<div class="container">
<h1>Tauri + Vue</h1>
<div class="form-group">
<input
v-model="name"
placeholder="名前を入力..."
@keyup.enter="greet"
/>
<button @click="greet">挨拶</button>
</div>
<div class="actions">
<button @click="openFile">ファイルを開く</button>
<button @click="saveFile">ファイルを保存</button>
</div>
<div class="result">
<p v-if="greetMsg">{{ greetMsg }}</p>
<p v-if="selectedFile">選択されたファイル: {{ selectedFile }}</p>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { invoke } from '@tauri-apps/api/tauri'
import { open, save } from '@tauri-apps/api/dialog'
import { readTextFile, writeTextFile } from '@tauri-apps/api/fs'
const name = ref('')
const greetMsg = ref('')
const selectedFile = ref('')
async function greet() {
if (name.value.trim()) {
greetMsg.value = await invoke('greet', { name: name.value })
}
}
async function openFile() {
try {
const selected = await open({
multiple: false,
filters: [{
name: 'Text Files',
extensions: ['txt', 'md', 'json']
}]
})
if (selected && typeof selected === 'string') {
selectedFile.value = selected
const content = await readTextFile(selected)
console.log('ファイル内容:', content)
}
} catch (error) {
console.error('ファイルを開けませんでした:', error)
}
}
async function saveFile() {
try {
const filePath = await save({
filters: [{
name: 'Text Files',
extensions: ['txt']
}]
})
if (filePath) {
await writeTextFile(filePath, `Hello from Tauri + Vue!\n${new Date().toISOString()}`)
alert('ファイルを保存しました!')
}
} catch (error) {
console.error('ファイルを保存できませんでした:', error)
}
}
</script>
<style scoped>
.container {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}
.form-group {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
}
.actions {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
}
button {
padding: 0.5rem 1rem;
background: #646cff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #535bf2;
}
input {
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 4px;
flex: 1;
}
</style>
よくあるエラーと対処法
エラー1: Vueコンポーネントの読み込みエラー
markdown[Vue warn]: Failed to resolve component
対処法: Vueコンポーネントのインポートパスを確認してください:
typescript// 正しいインポート方法
import { ref, reactive } from 'vue'
import MyComponent from './components/MyComponent.vue'
エラー2: Tauri APIの型エラー
bashProperty 'invoke' does not exist on type 'typeof import'
対処法: Tauriの型定義をインストールしてください:
bashyarn add -D @tauri-apps/api
Svelteアプリのデスクトップ化
SvelteアプリをTauriでデスクトップ化する方法を解説します。
Svelte + Tauriプロジェクトの作成
SvelteアプリとTauriを統合したプロジェクトを作成しましょう:
bash# プロジェクトディレクトリを作成
mkdir my-tauri-svelte-app
cd my-tauri-svelte-app
# Tauriプロジェクトを初期化
cargo tauri init
Svelteアプリの作成
Viteを使用してSvelteアプリを作成します:
bash# ViteでSvelteプロジェクトを作成
yarn create vite frontend --template svelte-ts
cd frontend
# 依存関係をインストール
yarn install
Tauriの設定
src-tauri/tauri.conf.json
を以下のように設定します:
json{
"build": {
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn build",
"devPath": "http://localhost:1420",
"distDir": "../frontend/dist"
},
"tauri": {
"allowlist": {
"dialog": {
"all": true
},
"notification": {
"all": true
},
"globalShortcut": {
"all": true
}
},
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.tauri.svelte.dev",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
},
"security": {
"csp": null
},
"windows": [
{
"fullscreen": false,
"resizable": true,
"title": "Svelte Tauri App",
"width": 1000,
"height": 800
}
]
}
}
SvelteアプリでのTauri API使用例
SvelteアプリからTauriの機能を使用する例を示します:
svelte<!-- App.svelte -->
<script lang="ts">
import { onMount } from 'svelte'
import { invoke } from '@tauri-apps/api/tauri'
import { open } from '@tauri-apps/api/dialog'
import { sendNotification } from '@tauri-apps/api/notification'
import { register } from '@tauri-apps/api/globalShortcut'
let name = ''
let greetMsg = ''
let selectedFile = ''
let isRegistered = false
async function greet() {
if (name.trim()) {
greetMsg = await invoke('greet', { name })
}
}
async function openFile() {
try {
const selected = await open({
multiple: false,
filters: [{
name: 'All Files',
extensions: ['*']
}]
})
if (selected && typeof selected === 'string') {
selectedFile = selected
await sendNotification({
title: 'ファイル選択',
body: `選択されたファイル: ${selected}`
})
}
} catch (error) {
console.error('ファイルを開けませんでした:', error)
}
}
async function registerShortcut() {
try {
await register('CommandOrControl+Shift+I', () => {
console.log('ショートカットが押されました!')
greetMsg = 'ショートカットが実行されました!'
})
isRegistered = true
} catch (error) {
console.error('ショートカットの登録に失敗しました:', error)
}
}
onMount(() => {
registerShortcut()
})
</script>
<main>
<div class="container">
<h1>Tauri + Svelte</h1>
<div class="form-group">
<input
bind:value={name}
placeholder="名前を入力..."
on:keydown={(e) => e.key === 'Enter' && greet()}
/>
<button on:click={greet}>挨拶</button>
</div>
<div class="actions">
<button on:click={openFile}>ファイルを開く</button>
<button on:click={registerShortcut} disabled={isRegistered}>
{isRegistered ? 'ショートカット登録済み' : 'ショートカット登録'}
</button>
</div>
<div class="result">
{#if greetMsg}
<p>{greetMsg}</p>
{/if}
{#if selectedFile}
<p>選択されたファイル: {selectedFile}</p>
{/if}
</div>
<div class="info">
<p>💡 ヒント: Cmd+Shift+I (Mac) または Ctrl+Shift+I (Windows/Linux) でショートカットをテストできます</p>
</div>
</div>
</main>
<style>
.container {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.form-group {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
}
.actions {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
}
button {
padding: 0.5rem 1rem;
background: #ff3e00;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
}
button:hover:not(:disabled) {
background: #e63900;
}
button:disabled {
background: #ccc;
cursor: not-allowed;
}
input {
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 4px;
flex: 1;
font-size: 1rem;
}
.result {
margin: 2rem 0;
padding: 1rem;
background: #f5f5f5;
border-radius: 4px;
}
.info {
margin-top: 2rem;
padding: 1rem;
background: #e3f2fd;
border-radius: 4px;
border-left: 4px solid #2196f3;
}
h1 {
color: #ff3e00;
text-align: center;
margin-bottom: 2rem;
}
</style>
よくあるエラーと対処法
エラー1: Svelteのリアクティブ宣言エラー
csharp'name' is not defined
対処法: Svelteのリアクティブ宣言を正しく使用してください:
svelte<script>
let name = '' // 正しい宣言方法
$: greetMsg = name ? `Hello ${name}!` : '' // リアクティブ宣言
</script>
エラー2: Tauri APIのインポートエラー
arduinoCannot resolve module '@tauri-apps/api/tauri'
対処法: Tauriの依存関係を正しくインストールしてください:
bashyarn add @tauri-apps/api
共通の設定とカスタマイズ
どのフレームワークを使用する場合でも、Tauriアプリの共通設定とカスタマイズについて学びましょう。
ウィンドウの設定
tauri.conf.json
でウィンドウの外観と動作をカスタマイズできます:
json{
"tauri": {
"windows": [
{
"label": "main",
"title": "My Tauri App",
"width": 1200,
"height": 800,
"minWidth": 800,
"minHeight": 600,
"maxWidth": 1920,
"maxHeight": 1080,
"resizable": true,
"fullscreen": false,
"decorations": true,
"transparent": false,
"alwaysOnTop": false,
"center": true,
"visible": true,
"closable": true,
"minimizable": true,
"maximizable": true,
"skipTaskbar": false,
"fileDropEnabled": true
}
]
}
}
セキュリティ設定
Tauriのセキュリティ設定を理解しましょう:
json{
"tauri": {
"security": {
"csp": "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'",
"dangerousRemoteDomainIpcAccess": [
{
"domain": "localhost",
"windows": ["main"],
"enableTauriAPI": true
}
]
}
}
}
アイコンの設定
アプリのアイコンを設定する方法:
bash# アイコンファイルを配置
src-tauri/icons/
├── 32x32.png
├── 128x128.png
├── 128x128@2x.png
├── icon.icns # macOS
└── icon.ico # Windows
よくあるエラーと対処法
エラー1: ウィンドウが表示されない
sqlWindow creation failed
対処法: ウィンドウ設定を確認してください:
json{
"windows": [
{
"visible": true,
"center": true
}
]
}
エラー2: ファイルドロップが動作しない
arduinoFile drop not working
対処法: fileDropEnabled
を有効にしてください:
json{
"windows": [
{
"fileDropEnabled": true
}
]
}
ビルドと配布
Tauriアプリのビルドと配布方法を詳しく解説します。
開発ビルド
開発中のテスト用ビルドを作成します:
bash# 開発ビルド
cargo tauri build --debug
本番ビルド
リリース用の最適化されたビルドを作成します:
bash# 本番ビルド
cargo tauri build
プラットフォーム別ビルド
特定のプラットフォーム向けにビルドします:
bash# Windows向け
cargo tauri build --target x86_64-pc-windows-msvc
# macOS向け
cargo tauri build --target x86_64-apple-darwin
# Linux向け
cargo tauri build --target x86_64-unknown-linux-gnu
配布用パッケージの作成
各プラットフォーム向けのインストーラーを作成します:
bash# 全プラットフォーム向け
cargo tauri build --target all
# 特定のプラットフォーム向け
cargo tauri build --target x86_64-pc-windows-msvc
よくあるエラーと対処法
エラー1: ビルド時のメモリ不足
csharpfatal error: out of memory
対処法: ビルド設定を最適化してください:
toml# Cargo.toml
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
panic = 'abort'
エラー2: 署名エラー(macOS)
swiftcode signing is required for distribution
対処法: 開発者証明書を設定してください:
bash# 開発者証明書を確認
security find-identity -v -p codesigning
# 署名付きビルド
cargo tauri build --config tauri.conf.json
パフォーマンス最適化
Tauriアプリのパフォーマンスを最適化する方法を学びましょう。
フロントエンドの最適化
バンドルサイズの削減
javascript// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
utils: ['lodash', 'moment']
}
}
},
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
}
})
遅延読み込みの実装
typescript// Reactでの遅延読み込み例
import { lazy, Suspense } from 'react'
const HeavyComponent = lazy(() => import('./HeavyComponent'))
function App() {
return (
<Suspense fallback={<div>読み込み中...</div>}>
<HeavyComponent />
</Suspense>
)
}
Rustバックエンドの最適化
リリースビルドの設定
toml# Cargo.toml
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
panic = 'abort'
strip = true
非同期処理の最適化
rust// src-tauri/src/main.rs
use tauri::Manager;
#[tauri::command]
async fn heavy_operation() -> Result<String, String> {
// 重い処理を非同期で実行
tokio::spawn(async {
// バックグラウンド処理
}).await.map_err(|e| e.to_string())?;
Ok("完了".to_string())
}
メモリ使用量の最適化
画像の最適化
typescript// 画像の遅延読み込み
const LazyImage = ({ src, alt }: { src: string; alt: string }) => {
const [loaded, setLoaded] = useState(false)
return (
<img
src={loaded ? src : ''}
alt={alt}
loading="lazy"
onLoad={() => setLoaded(true)}
style={{ opacity: loaded ? 1 : 0 }}
/>
)
}
不要なデータのクリーンアップ
typescript// メモリリークを防ぐ
useEffect(() => {
const interval = setInterval(() => {
// 定期的な処理
}, 1000)
return () => {
clearInterval(interval) // クリーンアップ
}
}, [])
よくあるエラーと対処法
エラー1: メモリリーク
Memory usage keeps increasing
対処法: イベントリスナーのクリーンアップを確実に行ってください:
typescriptuseEffect(() => {
const handleResize = () => {
// リサイズ処理
}
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
}, [])
エラー2: ビルド時間が長い
arduinoBuild takes too long
対処法: キャッシュを活用してください:
bash# キャッシュを有効にする
export CARGO_INCREMENTAL=1
export RUSTC_WRAPPER=sccache # または ccache
まとめ
Tauriを使ったデスクトップアプリ開発の旅はいかがでしたでしょうか?
この記事では、React、Vue、SvelteそれぞれのフレームワークでTauriアプリを作成する方法を、実際のコードとエラー例を交えて詳しく解説しました。
学んだことの振り返り
- Tauriの基本概念: Web技術とRustを組み合わせた軽量なデスクトップアプリフレームワーク
- 開発環境の準備: Rust、Node.js、システム依存関係のインストール
- フレームワーク別の実装: React、Vue、SvelteそれぞれでのTauri統合方法
- 共通設定とカスタマイズ: ウィンドウ設定、セキュリティ、アイコン設定
- ビルドと配布: 開発ビルド、本番ビルド、プラットフォーム別ビルド
- パフォーマンス最適化: フロントエンド、バックエンド、メモリ使用量の最適化
Tauriの魅力
Tauriの最大の魅力は、その軽量性とパフォーマンスにあります。Electronと比較して、アプリサイズが1/10程度になり、起動速度も大幅に向上します。
また、Rustのバックエンドにより、セキュリティと安定性も確保されています。Web技術の知識があれば、ネイティブアプリのような機能を持つデスクトップアプリを簡単に作成できるのです。
次のステップ
Tauriの基本をマスターしたら、以下のような発展的な機能に挑戦してみてください:
- ネイティブ機能の活用: ファイルシステム、通知、ショートカット
- プラグインの開発: 独自のTauriプラグインの作成
- CI/CDパイプライン: 自動ビルドと配布の設定
- アップデート機能: 自動アップデート機能の実装
心に響くメッセージ
Webアプリケーションをデスクトップアプリに変換することは、単なる技術的な変換ではありません。それは、ユーザーにより良い体験を提供するための選択です。
Tauriを使えば、軽量で高速、そして美しいデスクトップアプリを作成できます。あなたのWebアプリが、デスクトップアプリとして新たな価値を生み出す瞬間を、ぜひ体験してください。
技術の進歩は、私たちに無限の可能性を与えてくれます。Tauriは、その可能性を現実にするための素晴らしいツールです。
あなたのアイデアが、Tauriによって素晴らしいデスクトップアプリに変わることを心から楽しみにしています。
関連リンク
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来