Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TypeScript エンジニアが Android 開発の世界に飛び込んだ話

TypeScript エンジニアが Android 開発の世界に飛び込んだ話

Webフロントエンド開発とAndroidネイティブ開発は一見まったく異なる世界に思えますが、TypeScriptとKotlin、Web FrontendにとAndroid Appには驚くほど多くの共通点があります。本セッションでは、バックエンドおよびフロントエンド開発を15年経験し複数の言語環境を経験してきたエンジニアが、Androidアプリ開発に挑戦した経験を共有します。複数の言語パラダイムを渡り歩いてきた視点から、両言語の類似パターン、設計思想の共通点、そして相違点から学んだ貴重な教訓を紹介し、Web Frontend領域のエンジニアにAndroidアプリ開発の敷居を下げられるようなお話をします。
https://2025.tskaigi.org/talks/yui_tang

Avatar for Yui Sakamoto

Yui Sakamoto

May 23, 2025
Tweet

More Decks by Yui Sakamoto

Other Decks in Programming

Transcript

  1. アジェンダ 1. はじめに(自己紹介と背景) 2. Android × Web:UI 設計の歴史比較 3. 言語の比較:Kotlin

    と TypeScript 4. UI 構築:Jetpack Compose と React の共通点と違い 5. アーキテクチャ設計:MVVM 文化とそのギャップ 6. 実践でのつまずきと気づき 7. Web エンジニアが貢献できることと未来展望 8. まとめとクロージング 2
  2. Android UI の進化 年代 UI 記述 構造管理 アーキテクチャ 2008 年

    XML + Java Activity ほぼ MVC 的 2013 年 XML + Java Fragment(画面の分割) 無理やり責務分離 2017 年 XML + Kotlin ViewModel + LiveData MVVM(公式に推奨) 2020 年〜 Jetpack Compose ViewModel または Composable 単体 柔軟(MVVM, MVI な ど) 参考:Android UI History, Jetpack Architecture Guide 2. Android × Web:UI 設計の歴史比較 6
  3. Web UI の進化(ざっくり) 年代 主な技術 特徴 2006 jQuery DOM 操作中心、非構造的

    2012 Backbone/AngularJS MVC/MVVM ライクな構成 2015 React 登場 関数型 UI、状態の局所化 2019 Hooks 関数と状態の統合的な構成 参考:A Brief History of React, MDN - Web Frameworks 2. Android × Web:UI 設計の歴史比較 7
  4. 共通課題:状態と UI の整合性 Android も Web も「状態と UI の同期」 「関心の分離」で苦しんできた

    Android:ライフサイクルと画面構成の複雑さ Web:状態と DOM の手動同期、グローバル変数地獄 これらの課題から、MVVM(Android)と Hooks/Context(Web)が登場した 2. Android × Web:UI 設計の歴史比較 8
  5. Jetpack Compose と React が似ている理由 どちらも宣言的 UI + 関数ベース構成 状態

    = UI の再現を最小構成で可能にする ただし、Compose はMVVM との併用が標準化されている点で文化的に異なる この違いが、開発者体験や責務設計における「ギャップ」になる 2. Android × Web:UI 設計の歴史比較 9
  6. 比較する観点 1. 型システムと null 安全 2. データ構造定義(type/interface/data class) 3. 非同期処理(Promise

    vs Coroutine) 4. パターンマッチ(Discriminated Union vs sealed class) 3. 言語の比較:Kotlin と TypeScript 11
  7. 1. 型システムと null 安全 類似点 静的型付き言語(型推論、ユニオン型) コンパイル時の型チェックによる品質担保 相違点 TypeScript: string

    | null | undefined として union 型で表現 Kotlin:nullable 型は String? として明示的に区別され、厳格に扱われる 3. 言語の比較:Kotlin と TypeScript 12
  8. null 安全:コード比較 function greet(name?: string) { return `Hello, ${name ??

    "Guest"}`; } fun greet(name: String?) = "Hello, ${name ?: "Guest"}" Kotlin は null 安全性が言語仕様として明文化されている。 参考:Kotlin Docs - Null Safety 3. 言語の比較:Kotlin と TypeScript 13
  9. 2. データ構造定義:type / interface / data class 類似点 データ構造を型定義し、安全に受け渡し イミュータブル設計が基本

    相違点 TypeScript は構造的型付け(プロパティが一致すれば OK) Kotlin は名義的型付け(定義された型の一致が必要) 3. 言語の比較:Kotlin と TypeScript 14
  10. データ構造:コード比較 type User = { id: string; name: string; };

    data class User(val id: String, val name: String) Kotlin は`equals`や`hashCode`も自動生成され、値オブジェクトとしての特性が強い 参考:Kotlin Docs - Data Classes 3. 言語の比較:Kotlin と TypeScript 15
  11. 3. 非同期処理:Promise と Coroutine 類似点 処理の非同期化による UI スレッドのブロック回避 async/await 構文により直感的に記述可能

    相違点 TypeScript:Promise ベース、スレッド制御は明示されない Kotlin:Coroutine ベース、スレッド切り替えとキャンセル制御が可能 3. 言語の比較:Kotlin と TypeScript 16
  12. 非同期処理:コード比較 async function fetchData() { const res = await fetch(url);

    return await res.json(); } suspend fun fetchData(): Data = withContext(Dispatchers.IO) { api.get(url) } Kotlin は非同期処理において Dispatcher などのコンテキスト指定ができる 参考:Kotlin Coroutines Guide 3. 言語の比較:Kotlin と TypeScript 17
  13. 4. パターンマッチと制御構文 類似点 条件分岐と型による処理分離 安全な網羅性チェック(TypeScript は switch、Kotlin は when) 相違点

    TypeScript:Discriminated Union + switch 文 Kotlin:sealed class + when 式(else 不要で網羅性担保) 3. 言語の比較:Kotlin と TypeScript 18
  14. 分岐のコード比較 TypeScript type State = { type: "loading" } |

    { type: "success"; data: string }; function render(s: State) { switch (s.type) { case "loading": return "Loading..."; case "success": return s.data; } } 3. 言語の比較:Kotlin と TypeScript 19
  15. 分岐のコード比較 Kotlin sealed class State object Loading : State() data

    class Success(val data: String) : State() fun render(s: State): String = when(s) { is Loading -> "Loading..." is Success -> s.data } sealed class の活用で型安全かつ網羅的な分岐が可能 参考:Kotlin Docs - Sealed Classes 3. 言語の比較:Kotlin と TypeScript 20
  16. 1. UI の記述構文とデータバインディング 類似点 JSX と Compose DSL はどちらも「関数で UI

    を構築」 宣言的記述スタイル、再利用性が高い 相違点 JSX は HTML ライクな構文、DSL は Kotlin そのもの Compose は「式ベース」、React は「JSX ツリー」 4. UI 構築:Jetpack Compose と React 23
  17. 1. UI の記述構文とデータバインディング // tsx function Greeting({ name }: {

    name: string }) { return <Text>Hello, {name}</Text>; } // compose @Composable fun Greeting(name: String) { Text("Hello, $name") } 4. UI 構築:Jetpack Compose と React 24
  18. 2. 状態管理のモデル // tsx const [count, setCount] = useState(0); //

    compose var count by remember { mutableStateOf(0) } 4. UI 構築:Jetpack Compose と React 26
  19. 状態とイベントの統合的構成 // tsx <button onClick={() => setCount(count + 1)}>Click</button> //

    compose Button(onClick = { count++ }) { Text("Click") } React は Props + 関数、Compose はラムダによる記述 4. UI 構築:Jetpack Compose と React 27
  20. 3. コンポジションと再描画 類似点 状態の変化に応じて UI を差分再描画 Component / Composable 単位で再構成される

    相違点 React:Virtual DOM による差分検出 Compose:Recomposition による実行時最適化 Compose は状態追跡により必要最小限の再評価を行う 参考:Compose Recomposition Guide 4. UI 構築:Jetpack Compose と React 28
  21. 4. 開発支援機能:Preview vs Storybook Compose の Preview @Preview(showBackground = true)

    @Composable fun PreviewGreeting() { Greeting("Android") } Theme や画面サイズごとの確認も可能 Android Studio と統合、実機プレビューに強い 4. UI 構築:Jetpack Compose と React 29
  22. 4. 開発支援機能:Preview vs Storybook React の Storybook export const Primary

    = () => <Greeting name="React" />; コンポーネント単位のバリエーション管理に最適 開発中のスタイル確認や UI ライブラリ化に有効 4. UI 構築:Jetpack Compose と React 30
  23. 宣言的 UI の収束 項目 React Jetpack Compose 記述スタイル JSX DSL(Kotlin)

    再描画方式 Virtual DOM Recomposition 状態管理 useState, Context remember, ViewModel 開発支援 Storybook @Preview(Studio 統合) 両者は目的が近づきつつも、設計文化とプラットフォーム特性により差異が存在する 4. UI 構築:Jetpack Compose と React 31
  24. なぜ ViewModel が重要なのか? Android の UI はライフサイクルの影響を強く受ける 状態とロジックを UI から切り離し、再利用・テスト性を高めるために

    ViewModel を導入 Jetpack の公式アーキテクチャでは ViewModel 中心の MVVM を推奨 参考:Guide to app architecture - Android Developers 5. アーキテクチャ設計:MVVM 文化とそのギャップ 33
  25. ViewModel の構成例 class MainViewModel : ViewModel() { private val _uiState

    = MutableStateFlow(UiState()) val uiState: StateFlow<UiState> = _uiState fun onEvent(event: UiEvent) { // 状態更新とビジネスロジック } } 状態を StateFlow で保持し、Composable 側で collectAsState() イベント処理は ViewModel で集約 5. アーキテクチャ設計:MVVM 文化とそのギャップ 35
  26. React との責務設計の違い 項目 Compose + ViewModel React + Hooks 状態保持

    ViewModel useState / useReducer UI イベント処理 ViewModel 側で記述 関数内で直接記述 状態反映 Flow → Composable 値変化で再レンダリング データ流れ 外部 → 内部(UDF) 親 → 子 + 状態リフトアップ ViewModel による一方向データフローが標準文化になっている 5. アーキテクチャ設計:MVVM 文化とそのギャップ 36
  27. なぜ React のようにできないのか? Android は Activity/Fragment のライフサイクルが複雑 ViewModel による状態のサバイバルが設計上求められる UI/状態/副作用の責務分離が「前提」になっている

    React では UI と状態の局所管理が柔軟に許容されているが、Android では明示的な管理が文 化として定着 5. アーキテクチャ設計:MVVM 文化とそのギャップ 37
  28. ViewModel を使わない構成は可能か? @Composable fun Counter() { var count by rememberSaveable

    { mutableStateOf(0) } Button(onClick = { count++ }) { Text("Count: $count") } } 小規模アプリでは ViewModel なしでも完結可能 状態の永続化や再利用には限界がある Jetpack Compose の柔軟性により、用途に応じた選択が可能 5. アーキテクチャ設計:MVVM 文化とそのギャップ 38
  29. Web エンジニアが持ち込める視点 状態のスコープ設計と責務分離のバランス感覚 ViewModel の「設計しすぎ」問題への問い直し 状態 = UI という自然な発想 Web

    の知見は、Compose の今後の進化において必ず活きる 5. アーキテクチャ設計:MVVM 文化とそのギャップ 40
  30. Coroutine と非同期の違い suspend 関数は UI スレッドで呼べないことがある CoroutineScope , Dispatcher ,

    withContext など構成要素が多い launch {} と async {} の使い分け、キャンセル設計に戸惑う viewModelScope.launch { val result = withContext(Dispatchers.IO) { repository.fetch() } _state.value = result } async/await の延長では捉えきれない「並行性とスレッド」の設計が求められる 6. 実践でのつまずきと気づき 43
  31. Compose Preview の落とし穴 @Preview が効かないことがある(条件:Composable の構造や Theme) 再ビルド地獄:Preview 表示に必要な Rebuild

    が多く発生 Preview で完結させるには UI 単位の分割とテーマ切り替えの工夫が必要 @Preview @Composable fun ButtonPreview() { MyTheme { MyButton(onClick = {}) { Text("Click me") } } } Storybook 的な使い方をするには設計の工夫が必要 6. 実践でのつまずきと気づき 44
  32. 状態の保持と再描画のズレ remember で状態を保持するも、意図しない Recomposition が発生することがある LaunchedEffect , SideEffect , derivedStateOf

    など副作用との付き合いが必要 状態が「意図せず更新される」ことに違和感を覚える var name by remember { mutableStateOf("yui") } LaunchedEffect(Unit) { println("This runs once") } React の useEffect に似ているが、「再コンポジション = 再評価」の前提が強い 6. 実践でのつまずきと気づき 45
  33. 気づき:構造の自由度が高いからこそ「設計」が問われる React では eslint + hooks rule によりある程度制約されていた Compose は

    ViewModel 有無・状態の粒度・UI/非 UI の分離まで開発者次第 「書けてしまう」ことで責務設計と状態の一貫性維持が難しくなる 設計力と責任が問われる世界。それゆえに Web エンジニアの経験が活きる場面も多い 6. 実践でのつまずきと気づき 46
  34. 宣言的 UI 経験の移植可能性 React / Vue / SolidJS などで培った宣言的構築の経験は Compose

    に直結する 状態と UI の一貫性、再描画の制御感覚は共通 「状態の粒度設計」 「責務の分離と再利用」への感度が高い Compose での状態設計・再利用パターンで非常に重要 設計フェーズで活きる視点 Storybook 的 UI 分割・Preview 活用の発想 Props/State vs ViewModel/StateFlow の違いに対する抽象化能力 "Composable な責務分離"を前提にした開発スタイルの提案 Jetpack Compose よりも以前からUI実装をしていた皆さんの外部視点が活きる場面は多い 7. Web エンジニアが貢献できることと未来展望 48
  35. Kotlin Multiplatform(KMP)という未来 Kotlin Multiplatform Mobile による ViewModel やビジネスロジックの共有 JetBrains による

    Compose for Web / Desktop の進化 Web / Android を跨いだ UI 戦略が現実的に見えてきた expect class PlatformViewModel() { val state: StateFlow<UIState> } モバイル開発と Web 開発が「Kotlin」という言語で交わる可能性が広がっている 参考:[Kotlin Multiplatform Overview](https://kotlinlang.org/lp/multiplatform/) 7. Web エンジニアが貢献できることと未来展望 49
  36. このトークで伝えたかったこと Kotlin と TypeScript は文法だけでなく思想にも通じる部分が多い Jetpack Compose は React に近いが、設計文化が異なるという発見

    MVVM を中心とした Android の現在地は、「歴史の帰結」でもある 異なる世界に"親しさ"を見出すことは、学びと越境の第一歩になる これから始める人に伝えたいこと Compose の世界は柔軟で、Web エンジニアにも理解しやすい構造を持つ ただし、設計や状態の責任は "開発者に委ねられている" それゆえに、Web の経験が本当に活きる場面が多い 8. まとめとクロージング 52