【Android】アプリとローカルサーバーの両方を自作してみた感想

# はじめに
こんにちは。
今回はサーバー側とアプリ側のHTTPリクエスト処理を作成した話になります。
APIはスマホアプリ開発において必須の要素ですが、HTTPリクエストの実装はしばしば敷居が高く感じられます。
私もそうでした。技術的な知識はあったものの実際に1から実装したことがないので勉強がてら動かしたいなと思いながらも以下の敷居が高いと思われる理由で私も躊躇していました。

* セキュリティ問題
    * 無料で使える外部のAPIが本当にセキュリティ上問題ないか不安で、できれば使いたくない
* コストの問題
    * 興味を持ったサービスのAPIが有料だったり、コストがかかるリスクを避けたい
* ローカルサーバーの実装
    * 実装にかかる時間と、自分のスキルで作れるかの不安

# ローカルサーバー実装を選んだ理由
導入部分で触れましたが、敷居が高いなと考えながらもローカルサーバーの実装でやることにしました。
まずローカルサーバーを選んだ理由はHTTPリクエストを1から実装してみたいなと思ったのと、ちょうどプロジェクトでサーバーサイドの理解が不足していると感じたためです。
またTypeScriptを選んだ理由としてはTypeScriptを使用したことがあるためTypeScriptとExpressでローカルサーバーの作成としました。

# 開発時に勉強になったこと
予想以上にサーバーサイドやアプリ実装で学ぶことが多かったので箇条書きにしてみました。
## サーバー側
* 　データベースについて
* 　非同期処理の重要性とPromise、async/awaitの実践的な使用法
* 　リクエスト、レスポンス実装の方法
* 　GET,POST,PUT,DELETEの理解（リクエストボディとか）
* 　エンドポイント、エントリーポイントなどの文言の理解

サーバーサイド側は結構ブラックボックスなところがあったので色々知れてよかったです。

## Android側
* ネットワーク接続時のパーミッション
* network_security_config.xmlの存在
* OkHttpの実装方法

Android側も改めて1からAPI実装をしてみるとやはり新しく学ぶことが多かったです

https://developer.android.com/training/articles/security-config?hl=ja

https://square.github.io/okhttp/

# アプリを触ってみる方へ
ここからは実際にアプリを触ってみたいという方へちょっとした技術面の話です。
今回のアプリは自分用の検証アプリのようなもので特にサービスとして展開しているわけではありません。
そのため使ってみたい人は自分でクローンやフォークが必要です。
その時にGitHubにはアップしていないファイルもあるため、ここではそれらの詳細について説明します。

## サーバー側
### .envの作成
環境設定ファイル(.env)はデータベースやサーバーの設定に不可欠です。以下のテンプレートを使って作成し、必要な値を入力してください。
ポートは設定を入力しないと3000番を使用します。
```conf:.env
# 以下例です

# ホスト名を入力する
DB_HOST=localhost
#　データベースのユーザー名を入力する
DB_USER=root
#　データベースのパスワードを入力する（データベースを使用するときに使うパスワード）
DB_PASS=aaa
# 使用しているデータベース名
DB_NAME=sample_database
# ポート番号を入力　http://127.0.0.1:8000の8000の部分
PORT=8000
```
### URLの確認
使用するURLはstartServer.tsの以下のログで出力で表示されるようになっています。
なのでnpm startを実行後に接続できるURLがログで表示されます。
``` ts:startServer.ts
export async function startServer(app: Express, port: number) {
  const IP = await networkInterface();
  app.listen(port, IP, () => {
  　　　　// URLログ出力
    console.info(`Server started: http://${IP}:${port}`);
  });
}
```
### データベースに値を入れておきたいとき
余談ですが仮にデータベースに値を入れておきたいときの方法も書いておきます。
方法１
app.tsに以下のコードを追加する方法があります。
ただ注意しなければいけないのは起動後はすぐにコードのコメントアウトまたは削除してください。
更新がかかってしまい何度も実行されてしまうので。
```ts:app.ts
// 追加する
import { insertData } from './db/insertData';

・・・
startServer(app, PORT).catch(err => console.error("Failed to start server:", err));
// 追加する
insertData('test', 'test@example.com');
```
方法２
.sqlファイルを作成しファイル実行して値を入れる
```sql:test.sql
INSERT INTO users (name, email) VALUES ('テストさん', 'testsan@sample.com');
INSERT INTO users (name, email) VALUES ('テストにいさん', 'testniisan@sample.com');
INSERT INTO users (name, email) VALUES ('テストねえさん', 'testneesan@sample.com');
```
```
実行手順
データベースにアクセスする

mysql> use <データベース名>;
mysql> source <ファイルパス>/test.sql;
```
https://github.com/kazu09/express-sample-api

## Android側
### wi-fiを同じものを繋ぐ
ローカルサーバーと同じwi-fiを繋ぐようにしないとサーバーに接続できません。
### network_security_config.xmlを作成する
なぜこのファイルを作成しているかと言いますと開発途中で以下のエラーが出たからです。
「CLEARTEXT communication to XXX.XXX.XX.X not permitted by network security policy」
原因は、アプリが暗号化されていない通信を行おうとした際に発生します。つまり、安全でないHTTP通信を試みたときに、Androidのセキュリティポリシーがこれをブロックしたというわけです。

https://imanengineer.net/android-emulator-cleartext-http-traffic-not-permitted/

```xml:network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">XXX.XXX.XX.X</domain>
    </domain-config>
</network-security-config>
```
※XXX.XXX.XX.Xの部分はサーバーで出力したIP部分のみの記載をしてください。（ポート番号の記載は不要）

### 画面の説明
一応説明が必要そうなことだけ書いておこうかと思います。
画面の「url:」の入力はすべてGET、POSTなどに関わらず必ず入力が必要なものです。
入力する値はExpressのローカルサーバー起動時のログに表示されたURLを記載してください。
上記を入力しないということはドメインを入力していないのと同じ状況になります。
あとはreadmeに書いてある通りですかね。
ちなみに現時点ではメッセージのデータベース対応はサーバー側アプリ側ともにしていないで意味のない項目になります。。
今後時間があるときにぼちぼち進めていこうかなと思います。

https://github.com/kazu09/android-api-sample

# 最後に
いかがでしたでしょうか？
知っている技術でも1から作ってみると色々対応が必要なんだなと改めて気付かされました。
ご不明点やご質問があれば、コメントでお知らせください。
最後までご覧いただきありがとうございました。