Studyplus Engineering Blog

スタディプラスの開発者が発信するブログ

Amazon AuroraのMySQLユーザーをTerraformで安全に管理したい

SREの菅原(id:ksugahara08)です。

最近、既存のシステムをAmazon Auroraへ移行させるという作業が頻繁に発生しました。

モテ期かな?と勘違いするくらいAuroraに関しての仕事に恵まれたため、その中でも役に立ったTerraformでAmazon AuroraのMySQLユーザーを管理する方法を今回紹介します。

興味あれば最後まで読んで頂けると幸いです。

目次

Terraformでのパスワード管理の難しさ

どのように設定したかお話する前にTerraformでのパスワード管理の難しさについて少しだけ触れておきたいと思います。

Terraformでパスワードを隠すにはtfファイルだけでなく、tfstateファイルにも気を付けなければいけません。tfstateファイルは外部に漏れないように厳重に管理していても、漏れてしまう可能性は拭いきれません。したがって、tfファイルとtfstateファイルの両方に平文で保存されないことを考えなければいけないという難しさがあります。

鍵の暗号化・復号化

いくつか手段があったのですが今回はTerraformとAWS Key Management Service (KMS)を使った暗号・復号化を選択しました。

具体的にはTerraformのaws_kms_secretsとKMSのaws kms encryptで平文をカスタマーマスターキーから直接暗号化する方法を組み合わせました。この組み合わせであればtfstateファイルにもパスワー ドが平文保存されなかったため採用しました。

GPG鍵を使った方法も実装してみたのですが、マスターキーを自分で保管しなくて良いという点でKMSの方を選びました。またHashiCorp社のVaultをTerraformと組み合わせれば良いかなと調べてみたのですが、tfstateファイルには平文で保存されてしまうという記事を読み、Vaultは選択肢から外しました。

Terraformでの設定

本題のTerraformの実装方法について話していきます。

AWS Key Management Service (KMS) のマスターキーを作成

まず、KMSのCustumer Master Keyを作成します。kms.tfは設定例です。

kms.tf

resource "aws_kms_key" "sample" {
  description             = "Custumer Master Key"
  enable_key_rotation     = true
  is_enabled              = true
  deletion_window_in_days = 30
}

resource "aws_kms_alias" "sample" {
  name          = "alias/sample"
  target_key_id = aws_kms_key.sample.key_id
}

AWSのKMS権限を設定したIAMを作成

AWSのEC2インスタンスを使っているのであればIAMロールを作成して割り当てます。そうでなければIAMユーザーを作成します。

IAMポリシーは以下のようなものを設定していれば暗号化・復号化ができます。(詳しくはAWSの公式ドキュメントに書いてあるので各自調整してください。)

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": [
      "kms:Encrypt",
      "kms:Decrypt"
    ],
    "Resource": [
      "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab",
      "arn:aws:kms:us-west-2:111122223333:key/0987dcba-09fe-87dc-65ba-ab0987654321"
    ]
  }
}

KMSを使ってパスワードを暗号化

aws kmsコマンドで暗号化します。 先程作ったIAMロールかIAMユーザーを使用して以下を実行します。

$ vim secret.txt
# 暗号化したいパスワードや文字列を書き込みます。

$ aws kms encrypt \
    --key-id alias/sample \
    --plaintext fileb:///path/to/secret.txt \
    --query CiphertextBlob \
    --output text
# ここで出力された文字列をTerraformのtfファイルに記載します。

$ rm secret.txt
# 暗号化できたらファイルは廃棄します。

この手順でAuroraのrootユーザー名、rootパスワード、作成したいユーザーのパスワード等を暗号化しておきます。

MySQLユーザーの設定

以下のmysql_users.tfを使ってAmazon Auroraに接続して、MySQLユーザーを作成します。 このときTerraformが使っているIAMにRDSへの接続権限と接続できるNWで実行する必要があります。

mysql_users.tf

data "aws_kms_secrets" "sample_aurora" {
  secret {
    name    = "root_username"
    payload = "暗号化のときに受け取った文字列"
  }
  secret {
    name    = "root_password"
    payload = "暗号化のときに受け取った文字列"
  }
  secret {
    name    = "sample_password"
    payload = "暗号化のときに受け取った文字列"
  }
}

# Amazon Auroraへの接続はここで行っています。
provider "mysql" {
  endpoint = "sample-cluster.cluster-XXXXXXXX.ap-northeast-1.rds.amazonaws.com"
  username = data.aws_kms_secrets.sample_aurora.plaintext["root_username"]
  password = data.aws_kms_secrets.sample_aurora.plaintext["root_password"]
}

resource "mysql_user" "sample" {
  user  = "sample"
  host  = "%"
  plaintext_password = data.aws_kms_secrets.sample_aurora.plaintext["sample_password"]
}

resource "mysql_grant" "sample" {
  user       = mysql_user.sample.user
  host       = mysql_user.sample.host
  database   = "sample"
  table      = "*"
  privileges = ["SELECT", "INSERT", "UPDATE", "DELETE"]
}

plaintext_passwordで設定すればtfstateファイルにはハッシュ化された値だけが入ります。terraform state show mysql_user.sampleterraform state pullコマンドを使えば、パスワードが見えないことを確認できます。

Auroraへの接続はprovider mysqlを使って行うのですが、KMSを組み合わせることでパスワードを平文で書かずに済みます。

KMSを使ってパスワードを復号化

後からMySQLのパスワードを知りたい場合は以下のコマンドで復号して確認することができます。

$ aws kms decrypt \
    --ciphertext-blob fileb://<(echo '暗号化のときに受け取った文字列'|base64 -d) | jq .Plaintext \
    --raw-output |base64 -d

あとがき

今まではAnsible Vault機能を使ってパスワードを保持していたのですが、今回の方法でtfファイルにもtfstateファイルにも平文で持たなくて済みました。また復号するための鍵を手元ではなくKMSに保管することができたのでかなりメリットがありました。

課題としてはKMSの復号化ができるIAMユーザーの取り扱いには注意が必要だということです。IAMユーザーが漏れると復号化ができてしまう可能性があるからです。

まだまだパスワードや鍵の管理には頭を悩ませることもありますが少しずつ改善していきたいと思います。

リモートでのペアプロにはSlack Callが便利

こんにちは。ForSchool事業部の石上です。最近はおやつに杏仁豆腐をよく食べています。甘党ではないのですが、杏仁豆腐はちょうどいい甘さなので好きです。

今回は小ネタです。友だちのエンジニアにリモートでのペアプロのやり方を聞かれたため、せっかくならTechブログ記事にしようという感じです。

スクリーンショット 2020-04-18 17.15.47.png (118.7 kB)

ペアプロに使えそうなツール

ペアプロに使えそうなツールを考えたとき、まず以下のような選択肢が浮かびました。

  • VSCode拡張のLive Share
  • 画面共有ができる各種ビデオチャットツール

最初は、VSCodeのLiveShare拡張ならば相手のエディタに入り込むような体験でペアプロができるので良いと思いました。しかし、あるプロジェクトのペアプロをしているときに、ちょっとあっちのコードも確認しようと言って別のプロジェクトのディレクトリを開くと接続が切れてしまう問題がありました。

Studyplus for Schoolはいくつかのリポジトリに分かれているため、これが頻繁に発生するとつらいので、LiveShareでペアプロをするのはやめました。

どうしてSlack Callにしたか

そこで、他のものを使おうとなったときに、最も導入が手軽なのはSlack Callでした。普段会社で利用しているチャットツールがSlackだからです。利用するには、呼びかけたいチャンネルで通話を開始するだけです。

スクリーンショット 2020-06-24 10.35.09.png (119.2 kB)

Slack Callの良いところ

ふつうに使っていただけなので特筆することもないと思っていましたが、改めて考えてみると、良いところがいくつもありました。

  • 相手のスクリーンにお絵かきができる
  • Slack上で誰と誰が通話しているのかわかる

相手のスクリーンにお絵かきができる

ペアプロの際に嬉しいのはこれです。Slack Callでは、画面共有した相手のスクリーンに、お絵かきができます。これができると何が嬉しいかというと、2つあります。

  • 口頭だけでは説明しにくいことを図示して伝えられる
  • typoの指摘を一瞬で伝えられる

口頭だけでは説明しにくいことを図示して伝えられる

Slack Callを使うと、口頭だけでは説明しにくいことを図示して伝えられます。特にフロントエンドのコードを書いていてレイアウトの説明をするときとか、通信の流れを説明するときなどは、図示できるととても楽です。

スクリーンショット 2020-06-24 12.07.14.png (5.6 MB)

また、自分が説明している部分を囲って強調したり、そのソースコードの登場人物(変数や関数、型定義など)の関係性を伝えるのにも役立ちます。

誤記の指摘を一瞬で伝えられる

地味に嬉しいのが、タイポ(誤記)の指摘です。口頭だとこんなやり取りが発生します。

「あ、そのStudentsのところ、単数形が正しいです。sが多い。あ、ええと、今開いているファイルの真ん中のあたりの....あ、もうちょっと上です。そこそこ。」

お絵かきができれば、その箇所を丸で囲って「あ、ここタイポですね」で済みます。

スクリーンショット 2020-06-24 12.09.09.png (4.0 MB)

Slack上で誰と誰が通話しているのかわかる

また、ペアプロとは少しずれますが、これも嬉しいポイントでした。弊チームは4人のエンジニアが在籍しています。二手に分かれてペアプロをするタイミングがあったのですが、自分がペアプロしている相手以外の2人がペアプロをしているのか、ほかのことをしているのかがわかります。

そうすると、聞きたいことがあったときに、簡単なことであればペアプロしているところに入っていって、ちょっといいですかといって聞いてしまうこともできます。

まとめ

ペアプロをやる理由はチームや状況によって様々でしょうが、私は暗黙知を効率的に伝えるために必要に応じてやるべきものだと考えています。

そういう意味で、わかりづらい概念を図示してわかりやすくしたりタイポのような説明の不要な指摘を一瞬で終わらせられるSlack Callは、とても便利だと思いました。

GitHub Scheduled remindersにPull Pandaからさっそく切り替えてみた

モバイルクライアントグループの若宮(id:D_R_1009)です。 先日、すやすや寝ていたところGitHubから1通のメールが届きました。

f:id:D_R_1009:20200717230443p:plain

私個人の話なのですが、AndroidとiOS、そしてFlutterのコードを書いたりレビューしたりしています。 このためFlutterのコードを書いている時にAndroidのレビュー依頼が来る状況などが発生するため、自らレビュー依頼の一覧を確認しに行くだけでは難しくなっています。 こんな状況のため、Pull Pandaの恩恵を強く感じています。 もはやPull Pandaのおかげでレビューができていると言っても過言ではありません!

DMとしてアサインやコメントがSlackのDMとして送られてくるので、Macの通知に「GitHub上で動きがあったよ!」と表示されます。 またレビュー依頼を見落としていたとしても、Slackのチャンネル上にメンションが来るため、長時間レビューを放置してしまう事件を防ぐことができています。 Androidアプリを個人的に導入してみましたが、GitHub Actionsの実行に関する通知も表示されてしまいます。 このため、少々通知がうるさく感じることもあり、Slackで完結すると大変嬉しい感じです。

そんなわけで、GitHubのScheduled Reminderに移行してみました。 結論として、移行自体はとても簡単です。 しかしいくつかのハマりどころを見つけましたので、去年の続編という形で今回ブログまとめておきたいと思います。

去年のブログはこちら。

tech.studyplus.co.jp

チャンネルの設定を移行する

まずはPull Pandaにログインしてみましょう。 右上の Sign in からGitHubのアカウント連携に進みます。

pullpanda.com

Team RemindersMy DM settings 、そして Add users が表示されていると思います。

f:id:D_R_1009:20200717232440p:plain

まずは、 Team reminders から対応しましょう。 クリックすると次のような表示になっているはずです。

f:id:D_R_1009:20200717224054p:plain

Migrate to Github をクリックすると、移行工程の半分が終了です。 ブラウザはそのままにして、Slackの通知を飛ばしているチャンネルに移動してください。

すると、チャンネルに「どのチームの下にリマインダーを移行するか」という質問が投稿されています。 ここで質問されているのは「GitHub Scheduledとして、どのアカウントの下でこのリマインダーの設定を管理するか」になります。このため、例えばAndroidチームではOrganization Accountである "Studyplus" の下にリマインダーの設定を移行しています。 おそらく、同じような設定にするとPull Pandaの設定をしていた時と同じように、社内の他チームの設定を確認できるようになると思います。

移行が済むと、次のようなコメントがSlackに書き込まれます。

f:id:D_R_1009:20200718000642p:plain

manage this reminder からリマインダー設定を更新してみてください。 UIが真新しいので戸惑うかもしれませんが、Pull Pandaで設定できる項目は全て揃っています。 なお、GitHub Scheduled RemindersにすることでDraft PRを通知の対象外にすることができるようになっています。 チームのPull Requestの運用ルールに合わせて、この機会にぜひ設定してみてください。

個人の設定(Pull PandaからのDM)を移行する

続いて My DM settings を移行します。 この移行をしないと、GitHubのチャンネル通知においてGitHubアカウント名のまま投稿されてしまうので注意してください。

移行はPull Pandaの My DM settings 上部に表示されているリンクからGitHubへ移行すれば間違い無いと思いますが、うまくいかない場合は次の手順を実行してみてください。

まず、SlackとGitHubの連携状態を次のページから確認します。

slack.github.com

こちらの Add to Slack からGitHubの連携状態を更新すると、GitHubからDMが飛んできます。 DM内容は次のような「連携したよ!」というメッセージです。

f:id:D_R_1009:20200718002430p:plain

続いて、自分の設定を確認し更新します。 次のリンクから、GitHubの個人アカウントに紐づくScheduled remindersを確認することができます。

github.com

更新したいworkspaceの設定を変更したら、忘れずに更新します。 これで、Slackのチャンネル上にSlackアカウント宛の通知が飛んでくるようになります! やった!

終わりに

Slackに「移行できるよ!」という通知には気付きつつ来ていて放置していたのですが、移行してたらサクッと完了しました。 移行ツールって大事ですね。

Slack通知は過剰になりすぎると追うのも一苦労となってしまいますが、適切に設定すればチームの開発力を底上げしてくれると思います。 Scheduled RemindersとSlackを組み合わせれば、レビュー待ちの通知をしつつ、Pull Requestに対する全てのコメントをチャンネルに流すこともできます。 逆にOpenしてから数時間たったPull RequestだけをSlackチャンネルに通知し、細かな通知はDMに集約することもできます。

code review assignment については、所属しているチームが小さいこともあり、Pull PandaもGitHubの機能も使うことがしばらくなさそうです。 もし所属されているチームで利用し「便利だ!」となりましたら、ぜひ共有していただければと思っています!

Kotlin Flow+Roomで作るTimer&Stopwatch

こんにちは、モバイルクライアントグループの若宮(id:D_R_1009)です。 2月以降リモートワークで開発を続けております。会社近くのラーメン屋さんが恋しくなってきました。

今回は5月中旬にリリースした、AndroidアプリのTimer&Stopwatch改修について書きます。 Kotlin FlowとRoomを組み合わせ、複数のUIで同時にある値を表示する実装となったので、参考になれば幸いです。

Timer&Stopwatchの改修経緯

Studyplusスアプリには、学習している時間を計測するためのTimerとStopwatch機能があります。 Timerは予め時間を指定して経過時間を記録する機能、Stopwatchは計測開始から終了までの経過時間を記録する機能です。

今年の4月以降、このStopwatch機能について不具合報告が多くなっていました。 不具合を精査したところ、一部「ユーザーの利用環境変化」が理由と考えられるものが発生していることがわかりました。

利用状況の変化の詳細については、弊社の発表や過去のブログ記事をご参照ください。

info.studyplus.co.jp

tech.studyplus.co.jp

今回影響がありそうな点は、スタディプラスのユーザーがより自宅で学習する状況となったことです。 自宅で「動画教材を視聴しその時間を計測する」ユーザーが増加し、「教材の動画を視聴していると計測がストップする」という不具合が起こりやすくなったと推測できます。

開発を検討していた4月半ばごろは緊急事態宣言の話が取り沙汰されていました。 そのためこの問題が起こりやすい状況が続くことも予想され、優先度を高めて対応することとしました。

既存実装について

まず、不具合が報告された既存実装から簡単に説明します。

これまでの実装では、ForegroundService 上で時間計測用のTimerやCountDownTimerを行い、その計測結果をRxBusやIntentを用いてUIに送信する実装となっていました。 これはサービスとスレッドのどちらを選択するかを参考に、バックグラウンドでAndroid OSの許す限り計測を行うという実装方針のもとに選択したものです。 既存実装の設計時には「(物理媒体の)教材を学習中、スマートフォンで時間を計測する」ようなケースを想定していたため、この実装でも問題ないだろうと考えていました。

ただこの実装ではユーザーが例えば動画のストリーミングアプリを立ち上げた時、端末のスペックによってはStudyplusアプリがメモリから破棄されてしまいます。 この破棄時に状態を保持するためにはには SavedInstance への保存処理、そして SavedInstance からの復元処理を記述する必要があります。 しかしながら、既存実装ではこの対応が行えていませんでした。 またServiceの SavedInstance 対応は調査から始める必要があり、導入コストが高いと見積もられ、すぐに着手できる状況ではありませんでした。

不具合の原因まとめ

簡単にまとめると、下記のようになります。

  1. 既存実装は ForegroundService 上で計測処理を実施していた
  2. Activity/Fragmentは Service から送られてくる計測状態を表示していた
  3. メモリ破棄時の SavedInstance が未実装であった
  4. 外出自粛や各種休校に伴い動画視聴と並行して利用されるケースが増加した(と考えられる)

Kotlin Flow+Roomによる計測機能

新たにTimer&Stopwatchの設計をするにあたり2つの方針を立てました。

  1. 不揮発領域に計測状態を保存し、アプリの生存期間に関わりなく時間の計測ができるようにする
    1. アプリ破棄時への対応
    2. アプリ再起動時への対応
  2. Activity/FragmentとServiceを同列に扱い、どちらも"UI"としての役割とする
    1. 計測をUI層ではなくModel層で実施する
    2. DIが可能にすることでテスタビリティを向上する

なお、簡単な実装を下記のリポジトリで公開しています。 動作を見つつコードを確認したい場合は、ご活用ください。

github.com

Roomの設計

以前のエントリで紹介しているように、スタディプラスのAndroidアプリではRoomを採用しています。

tech.studyplus.co.jp

このため、今回の機能開発においてもRoomを利用することとしました。 都合の良いことにRoom 2.2よりKotlin Flowをサポートしているため、今回の用件にマッチした次第です。

medium.com

Roomに保存するテーブルは、下記のようにします。 なお説明を簡単にするため、Stopwatch機能のみの実装としています。 実際にはTimerや計測中の教材に関連する情報などのデータも含む実装です。

@Entity(tableName = "measurement")
data class MeasurementEntity(
    @PrimaryKey
    @ColumnInfo(name = "entity_id")
    val entityId: Int = 0,
    val state: MeasurementState,
    @ColumnInfo(name = "start_date_time")
    val startDateTime: String, // 2020-04-20'T'08:20:00+09:00
    @ColumnInfo(name = "elapsed_sec")
    val elapsedSec: Long = 0L
) {
    companion object {
        val INIT_OBJECT = MeasurementEntity(
            state = MeasurementState.INIT,
            startDateTime = ""
        )
    }
}

MeasurementState は次の通りです。 Stopwatchはユーザーがボタンを操作して状態が変化する仕組みとなるため、シンプルにRUNとSTOPの2つの状態だけを想定します。

enum class MeasurementState {
    INIT,
    STOPWATCH_RUN,
    STOPWATCH_STOP
}

これでStopwatchの状態、計測開始時刻、前回までの計測で加算された時間を保存することができるようになります。 続いて、経過した時間について開発を進めていきます。

Kotlin Flowによる計測

ここからはDaoの定義を行い、ブログタイトルの通りKotlin Flowでデータを取得します。 Flowを使うことで「rowを更新する」処理と「rowが更新された」処理を分離し、特に「rowが更新された」処理をリアクティブな実装とすることができます。

RoomはSELECTメソッドの返り値を Flow<MeasurementEntity?> とすることで、rowが存在しない時に null となるFlowを作ることができます。 後述のRepositoryでnon-nullに変換していますが、 Flow<List<MeasurementEntity>> とすることでnon-nullにすることもできるので、お好みで選択してください。

@Dao
interface MeasurementDao {

    @Query("SELECT * FROM measurement WHERE entity_id=0")
    fun findFlow(): Flow<MeasurementEntity?>

    @Query("SELECT * FROM measurement WHERE entity_id=0")
    suspend fun find(): MeasurementEntity?

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(entity: MeasurementEntity)
}

最後にFlowで受け取った MeasurementEntity に合わせて、描画更新用のイベント clock を作成します。 サンプルでは1つのActivityでしか描画を受け取っていませんが、同期イベントをRepositoryから作成することで、複数のUIで同時に描画を更新することができます。

transformLatest を利用することで MeasurementEntity が変更された時に、rowの更新があったとき clock が更新されるようになります。 このため clock 内で while(true) のループを回す実装を採用しています。

@Singleton
class StopwatchRepository @Inject constructor(private val dao: MeasurementDao) {

    val entity = dao.findFlow().map {
        it ?: MeasurementEntity.INIT_OBJECT
    }

    private val clock = entity.transformLatest {
        emit(OffsetDateTime.now())
        when (it.state) {
            MeasurementState.STOPWATCH_RUN -> {
                while (true) {
                    delay(INTERVAL_MILLI)
                    emit(OffsetDateTime.now())
                }
            }
            else -> {
                // nop
            }
        }
    }

    companion object {
        private const val INTERVAL_MILLI = 500L
    }
}

計測時間の表示

ここまで計測のベースを作成したので、リアルタイムで更新される描画を考えていきます。 経過時間は現在時刻と計測開始時刻の差と前回までの計測で加算された時間の合計と考えることができます。

@Singleton
class StopwatchRepository @Inject constructor(private val dao: MeasurementDao) {

    val elapsedTime = combine(entity, clock) { entity, event ->
        when (entity.state) {
            MeasurementState.INIT -> {
                0L
            }
            MeasurementState.STOPWATCH_RUN -> {
                entity.secUntil(event) + entity.elapsedSec
            }
            MeasurementState.STOPWATCH_STOP -> {
                entity.elapsedSec
            }
        }
    }
}

ViewModelではこの値を表示させるため、LongからStringへの変換を行います。 どのようなStringへ変換するかはUI依存のため、今回はViewModelで変換するのが良さそうと判断しています。

class MainViewModel @Inject constructor(private val repository: StopwatchRepository) : ViewModel() {

    val elapsedTime = repository.elapsedTime.map { formatMeasureTime(it) }.asLiveData()
}

fun formatMeasureTime(duration: Long): String {
    val hour = duration / 3600L
    val minute = duration % 3600L / 60L
    val second = duration % 60L

    return String.format(Locale.US, "%02d:%02d:%02d", hour, minute, second)
}

あとは各ボタンの有効状態を repository.entityMeasurementState を基に調整することで、Stopwatch機能が完成です。 現実装では StopwatchRepository に作成した操作用メソッドをViewModelから操作することで、Roomの状態を更新しています。

不具合の改修状況

新たな実装ではRoomに保存されている状態を見ればいつでも計算を行うことができるようになりました。 このため、既存の Service のライフサイクルに依存することにより発生した問題を解消しています。

また、「Stopwatchを止めずに端末の電源をOFFにした」ケースにも対応することができました。 サンプルのコードを動かしている場合は、ぜひ1度お試しください。*1

時間計測は考慮することが多く大変なのですが、基本的なユースケースにシンプルなコードで対応できたように思います。 なお端末の時間を変更した場合などのエッジケースが存在するのですが、そちらの考慮は割愛させていただきます。

終わりに

5月半ばよりリリースした、RoomとKotlin Flowを組み合わせて時間計測機能を追加する実装を紹介しました。 現時点まで大きな不具合報告もなく、市場で動作しているようで安心しています。

Roomから簡単にKotlin Flowを取得することができるので、Roomを中心とした設計にするとFlowが扱いやすいなと感じました。 ちょうどRxJavaをアプリから削除し終わったので、今後もFlowを活用していきます。

最後までお読みいただきありがとうございました!

*1:もしもビルドがめんどくさければ、是非Studyplusをダウンロードしてみてください!

Studyplus iOS版におけるアプリ内課金時のUI制御

こんにちは、モバイルクライアントグループの明渡です。

先日のGoogle Play定期購入でプランを切り替えるモードの話でも記載の通り、スタディプラスでは今年3月に有料会員サービス Studyplus Pro をiOS/Android両OSでリリースしました。

info.studyplus.co.jp

iOS版でのアプリ内課金の実装自体は、用意されているフレームワークにてアプリ開発者が考慮しなければいけない事項がAndroidより少ないです。そして、Appleの豊富なドキュメント*1や、先人たちが多々残してくれている豊富な知見*2に助けられスムーズに実装を進められました。

そんな中で今回は、アプリ内課金実装そのものと比べれば些事ながらも結果的にリリース直前まで苦しんだ、アプリ内課金操作を行なった際のUI制御についてお話しします。

iOSのアプリ内課金実装の中でもアプリ開発者の裁量に委ねられていてアプリごとに最適解が異なりそうなテーマになりますので、ほんの一例としてご覧いただければ幸いです。

やりたかったこと

  • アプリ内課金を試行中、アプリ画面の操作はできないようにする

    • 購入操作を行うためのApp Storeアラートが表示されるまで数秒かかることがあり、何も制御しないとアプリ画面の操作ができてしまう
  • アプリ内課金に関する操作が終了後、アプリ画面を操作できるようになる

  • アプリ内課金の購入・復元完了後に完了した旨を伝える画面を表示する

実装

最終的に落ち着いた実装が以下の通りになります。

前提

  • Appleのドキュメントからダウンロードできるサンプルコード*3を基に実装したので、フレームワークはStoreKitをそのまま利用

    • App Storeのオブザーバにてストアからのコールバック処理時、画面側へデリゲートによりアプリ内課金のトランザクション処理ステータスを伝えて制御
  • UI制御が絡まない実装の記述は割愛

実行環境

  • Xcode 11.5
  • Swift 5.2.4

App Storeのオブザーバ

protocol AppStoreObserverDelegate: class {
    func storeObserverPurchaseSucceed()
    func storeObserverDidReceiveMessage(message: String)
    func storeObserverDidCancelled()
}
final class AppStoreObserver: NSObject {
    static let shared = AppStoreObserver()
    weak var delegate: AppStoreObserverDelegate?
}

extension AppStoreObserver: SKPaymentTransactionObserver {
    
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch transaction.transactionState {
            case .purchasing:
                break
            case .deferred:
                DispatchQueue.main.async { [weak self] in
                    self?.delegate?.storeObserverDidCancelled()
                }
            case .purchased:
                handlePurchased(transaction, success: {
                    // サーバーでレシート検証して会員ステータス更新が成功した際、購入完了時の処理を行う
                    DispatchQueue.main.async { [weak self] in
                        self?.delegate?.storeObserverPurchaseSucceed()
                    }
                }, failure: { [weak self] message in
                    DispatchQueue.main.async { [weak self] in
                        self?.delegate?.storeObserverDidReceiveMessage(message: message)
                    }
                })
            case .failed:
                if let error = transaction.error as? SKError, error.code != .paymentCancelled {
                    DispatchQueue.main.async { [weak self] in
                        self?.delegate?.storeObserverDidReceiveMessage(message: error.localizedDescription)
                    }
                } else {
                    DispatchQueue.main.async { [weak self] in
                        self?.delegate?.storeObserverDidCancelled()
                    }
                }
                handleFailed(transaction)

            case .restored:
                handleRestored(transaction, success: {
                    // サーバーでレシート検証して会員ステータス更新が成功した際、購入完了時の処理を行う
                    DispatchQueue.main.async { [weak self] in
                        self?.delegate?.storeObserverPurchaseSucceed()
                    }
                }, failure: { [weak self] message in
                    DispatchQueue.main.async { [weak self] message in
                        self?.delegate?.storeObserverDidReceiveMessage(message: error.localizedDescription)
                    }
                })
            @unknown default:
                let message = "Unknown payment transaction case"
                #if DEBUG
                fatalError(message)
                #endif
            }
        }
    }
}

画面

final class ViewController: UIViewController {
    // UIActivityIndicatorViewを含む画面全体を覆うカスタムビュー
    // 購入手続き・復元処理開始時に表示しておく
    private lazy var loadingView: LoadingMaskView = {
        return LoadingMaskView(frame: view.frame)
    }()
}

extension ViewController: AppStoreObserverDelegate {
    func storeObserverPurchaseSucceed() {
        DispatchQueue.main.async { [weak self] in
            self?.loadingView.dismiss()
            // Studyplus Pro 登録完了画面を表示
        }
    }
    
    func storeObserverDidReceiveMessage(message: String) {
        DispatchQueue.main.async { [weak self] in
            self?.loadingView.dismiss()
            // messageのアラート表示
        }
    }
    
    func storeObserverDidCancelled() {
        DispatchQueue.main.async { [weak self] in
            self?.loadingView.dismiss()
        }
    }
}

苦労したこと

ユーザーへアプリから伝えるべき情報とそうでないものの切り分け

購入・復元処理が完了したあと、App Storeのオブザーバ内で弊社サーバーへレシート検証リクエストも立て続けに行なっております。 SKPaymentTransactionによって購入〜課金ステータス更新までを1つトランザクションとして扱える恩恵にあやかるためですね。

苦しんだ要因としては、その後続処理側のエラー時の挙動も並行してきちんと整理せず実装を進めているうちに、無駄にこんがらがってしまっただけという側面が強いです...

App Storeからのレスポンスについては、以下のような対応に落ち着きました。

  • SKPaymentTransactionState
    • .purchased または .restored
      • レシート検証を進める
    • .deferred
      • Ask to Buyによる購入承認リクエストが送信されたがまだ購入が確定していないので、レシート検証は行わずアプリの利用を再開できるようにする
    • .failed のうち、ユーザーが自らキャンセル(SKError.paymentCancelled)
      • ユーザーの意志で購入が行われなかったので、レシート検証は行わずアプリの利用を再開できるようにする
    • .failed のうち、上記以外
      • ユーザーの意志に反して購入・復元完了しなかった恐れがあるため、エラーの内容を表示しつつアプリの利用を再開できるようにする

App Storeでの操作完了後、アプリ側のローディング表示をきちんと終了する

上記のソースコードなのですが、DispatchQueue.main.async のクロージャがくどく感じません?

オブザーバ・画面共にどちらかのクロージャを欠くと、ローディング表示を終了できず永遠にローディング表示のままとなる事象が一定確率で発生するため必須だったのです...

一定確率で発生するのがミソで、リリース版がFIXする直前まで見落として開発を進めてしまい、発覚した際には少々青ざめながら検証して現在の実装に落ち着きました。

自アプリ以外の要素が絡む処理はとりわけ、メインスレッドで実行したい処理を DispatchQueue.main.async のクロージャへ明記しないといけないのだなと理解しました。

さいごに

今回はテーマに関わるソースコードを抜粋しながら記事を書き起こしたのですが、元のソースコードをもう少し整理してリファクタしたい欲に駆られました...

近いうちにアプリ内課金前提の動作確認の伴う機能開発と併せて、今回の記事を見直してリファクタリングに臨もうかなと思います。

少人数で複数のマイクロサービスの開発を行うための弊社の開発事情

こんにちは、サーバーサイドエンジニアの山田です。

昨年の11月に入社して以降、Studyplusアプリのバックエンドとそれに付随するいくつかのマイクロサービスの開発に関わってきました。今回はその中で感じたサーバーサイドチームの開発環境のいいところや改善したい点をいくつか紹介したいと思います。

背景

わたしが所属するStudyplus事業部のサーバーサイドチームでは、10ほどのマイクロサービスの開発を4人のサーバーサイドエンジニアが担当しています。その中で一番改修が多いのはStudyplusアプリのバックエンドとなるRailsアプリケーションになりますが、それ以外のサービスもたまに改修を入れていくことになります。わたしの場合は入社して約半年で7個ほどのサービスの改修に関わりました。

それらのサービス開発に関わった結果、新しく入ったメンバーが早く立ち上がりやすい環境だと感じたので、そのあたりの観点を中心にチームの開発環境について紹介していきます。

ローカル環境編

docker-composeによる環境構築

どのサービスもdocker-composeでローカル環境を構築できるようになっており、環境構築に時間を取られることが少ないです。これによって、あるサービスにちょっとした改修を入れたいだけなのに環境構築に時間が取られるといったことを避けることができています。

開発環境のDBに接続して開発

ローカルでアプリケーションを動かす時に時間を取られやすい作業にデータの準備があると思います。 弊チームでは以下のように開発者個人のローカルのアプリケーションから開発環境のDBに接続して開発を行う方法をとっています。

f:id:yshunske:20200608113144p:plain

例えばRailsの場合はseeds.rbで初期データを準備する方法がありますが、seedのメンテナンスが手間で中途半端になってしまうことも多いと思います。

この方法のメリットとして以下のような点が挙げられます。

  • seedのメンテが不要
  • seedでカバーできない(or 準備コストが高い)ような特定の条件を確認するためのデータのバリエーションについても初めてローカルの環境を起動してすぐに使える
  • ローカル環境での確認後に開発環境で確認するといった場合に2度同じデータを作成する手間が省ける

一方で以下のようなデメリットもありますが、現状は問題にはなっておらずデメリットよりメリットの方が大きいと感じています。

  • DBのスキーマ変更を行う場合に別の開発者と競合してしまう可能性がある
  • みなが使うDBなので試しにスキーマを変更したいという場合にやり辛い

開発環境DBでやりづらい開発の場合は以下のような方法で回避することが多いです。

  • ローカル環境ではテストコード上での確認のみを行い、残りは開発環境へのdeploy後に確認する
  • 開発環境のDBをコピーしてそこに接続して開発

CI/CD編

テストコードとCircleCI

サーバーサイドチームが担当している全てのリポジトリにテストコードがあり、プルリクエストはCircleCI上のテストと二人以上のレビューが通らないとmasterブランチへマージできないようにしています。 これにより初めて修正を行うリポジトリでもテストコードで動作を確認しつつ自信を持って改修をしていくことができています。

デプロイ方法の統一

デプロイは以下のようにslackからデプロイする方法で統一されています。そのため個別の手順を覚えたりする手間が省けています。

f:id:yshunske:20200608113447p:plain

アプリケーション編

開発言語とフレームワーク

バックエンドは基本的にRubyとRailsが使われています(一部はGoなど別の言語を使用)。言語とフレームワークが揃っていることで学習コストを抑えて開発を行えています。 詳しくは後述しますが、フロントエンドはこのあたりに課題を抱えています。

Railsアプリケーションのレイヤー

Railsアプリケーション上で他のマイクロサービスと連携する処理は gateways というレイヤーを設けてそこに集約しています。

下のようなイメージです。

# Railsの app以下のディレクトリ
.
├── assets
├── controllers
├── gateways <- ここに他のマイクロサービスと連携する処理を集約している
├── helpers
・
・
・

他サービスとの連携は処理を追いづらいことが多いですが、異なるRailsアプリでも同じ構成をとることで処理が追いやすくなっています。

インフラ編

AnsibleとTerraformによるIaC

弊社のインフラの多くは、AnsibleとTerraformでコード化して管理しています。新しい仕組みの導入や大きな変更はSREチームが担当しますが、アプリケーションの変更に伴うちょっとした修正はサーバーサイドチームのメンバーも修正を行います。その際、AnsibleやTerraformのコードを修正してSREチームのレビューを受けてから反映しています。

これにより以下のようなメリットがあると感じています。

  • アプリケーションを開発するサーバーサイドのメンバーもインフラ構成や設定を把握しやすくなる
  • 作業がSREチームのみに集中することを避けられている
  • SREチームのコードレビューを受けることで一定の質を保って修正が行える

改善したいところ

ここまでは良いと感じている点を挙げてきましたが、改善したい点もいくつか紹介します。

フロントエンドの技術スタックが統一されていない

弊社の体制上、Webのフロントエンドが得意なエンジニアは、スマホアプリではなくWebのサービスであるStudyplus for Schoolを開発している部署に集まっています。そのためStudyplus事業部が担当するWeb画面を持ったサービスはサーバーサイドチームのメンバーがメンテナンスしています。

そのような体制ではありますが、使っているサービスによってJavaScriptのフレームワークは以下のように各種フレームワークが使われています。

  • Vue.js
  • React
  • AngularJS(Angularではない)
  • jQuery

これらをサーバーサイドエンジニアがメンテナンスしているためコストが上がってしまっており、ある程度は技術スタックを揃えていく必要があると感じています。

Ruby、Railsのバージョン

現状は各サービスで使っているRuby、Railsのバージョンがそれぞれ以下となっており、最新のバージョンに追従できていない状態です。

  • Ruby: 2.5系 or 2.6系
  • Rails: 5.2系

これがすぐに問題となるわけではありませんが、最新のバージョンからの差が大きくなるほどバージョンを上げるコストが上がっていきます。また、最新の機能や改善された機能の恩恵を受けられないというデメリットもあり、近いうちにバージョンを上げていきたいと考えています。

また、バージョンを上げていく作業は今後も発生し続けるため、なるべくコストを抑えつつ新バージョンに追従していく仕組みや体制を考えていきたいです。

まとめ

サーバーサイドチームが担当するサービスで共通することを中心に開発事情を紹介しました。個人的には複数のサービスで技術スタックや開発のフローが統一されている点が現状のチーム体制を考えると効率的だと感じて気に入っている点です。

新しいサービスにJOINして開発を開始するまでに必要な要素は、ドメイン知識など多岐にわたると思います。その中でもサービスを横断して統一されていることが多いとその一部を把握する時間をショートカットできるので立ち上がりが早くなると思います。

一方、そのマイクロサービスに適した個別のやり方を選択することも必要なので、開発の体制とバランスを取りながらより価値を発揮しやすい環境にしていきたいところです。

スタディプラスメンバーのリモートワーク作業環境事情

スタディプラスメンバーの作業環境事情

こんにちは。ForSchool事業部の田口です。
COVID-19の影響で在宅勤務になってしばらく経ちましたが、みなさんいかがおすごしでしょうか。
スタディプラスでは以前からリモートワークが可能でしたが、今回の騒動で原則在宅勤務となりました。
そして、会社の好意により5月の給与振込日に在宅勤務手当が振り込まれました。在宅勤務が続く中でこのような手当がいただけるのはありがたいですね。
元々リモートワークを行っていたメンバーならまだしも、今回の騒動でほとんど経験のない在宅勤務を行う必要が出てきたという方も多いと思うので、今回は在宅勤務に馴染みのある職種以外の方も含めてスタディプラスのいろんな職種の方の在宅作業環境を紹介しようと思います。
この記事では、作業環境を紹介してくれるみなさんに作業環境の写真を提供していただき、各メンバーに

  • (もしあれば)こだわっているところや商品情報など
  • 今の在宅勤務環境の良い点・悪い点
  • 在宅勤務手当で買いたいもの

の各項目に答えていただいた内容を紹介します。

目次

  • ForSchool事業部エンジニア: 田口
  • ForSchool事業部エンジニア: 石上さん
  • ForSchool事業部エンジニア: 冨山さん
  • ForSchool事業部デザイナー: 秋間さん
  • ForSchool事業部長: 宮坂さん
  • Studyplus事業部iOSエンジニア: 明渡さん
  • Studyplus事業部デザイナー: 小松さん
  • 総合広告事業部アカウントプランナー: 伊藤さん
  • ポルト事業部デザイナー: 竹内さん

ForSchool事業部エンジニア: 田口

というわけでまずこの記事を書いている自分の作業環境から紹介させていただきます。

写真

f:id:tagucch:20200601164736j:plain f:id:tagucch:20200601164801j:plain f:id:tagucch:20200601164814j:plain

こだわっているところや商品情報など

  • L字のデスクを使っていて、正面(Lの長い辺)にメインの作業環境、右側の手前(Lの短い辺の部分)にWindowsマシン用のモニターやキーボード、Switchなどを置いています(PC本体は足元にあります)。モニターの左に立ててあるMacBook Proは私物です。
  • 4Kディスプレイが少なくとも1枚はほしいので、LGの27インチ4Kモニターをメインディスプレイにしていますが、もう1枚欲しくなってきました。ゲーム用のモニターは壊してしまったのでこのモニターでゲームもやるのですが、HDMIケーブルをつけ直すのが面倒なのでHDMIセレクターの導入を検討しています。
  • キーボードはPFUのHHKB Professional2 墨/無刻印をかれこれ3年くらい利用しています。気分転換で他のキーボードを使うこともあって、MiSTEL BAROCCOのMD650Lを使ったりErgoDoxを使ったりもします。ただこれらのセパレート型のキーボードは設置にちょっと手間がかかるのでなんだかんだHHKBを使っていることが多いです。やはり静電容量無接点方式は最高ですね。
  • 第二世代のApple Pencilが使えるiPad Pro11インチ(第二世代)を持っていて、macOS CatalinaにアップデートしてSidecarが利用できるようになったのでもう1枚のディスプレイとして使おうと思っていました。が、なんだかんだでYouTubeを垂れ流したり音楽をかけたりするのに便利なためわりと単独で使ってます。
  • マイクは前に誕生日に贈ってもらったSonyのECM-PCV80Uをマイクアームにつけて利用しています。外付けのマイクでUSB接続もでき、値段もお手頃なためおすすめです。(ポップガードはマイクアームについていたものです)

今の在宅勤務環境の良い点・悪い点

良い点

  • デスク自体は大きく奥行きもあるので、わりと広々としていてNintendo TOKYOで買ったマグカップやはてなボックスなどが置けます。
  • 飲み物やお菓子をすぐに取りにいけるのでとにかく楽です。面倒くさがりなので楽な方向に倒れ続けるのはとても素晴らしいです。
  • ANKERのスピーカーとiPad Proがあるので好きな音を流せるのもいいところです。

悪い点

  • 今の椅子も悪くはないんですが、オフィスで使っていたオカムラのBaronが恋しいです。
  • とにかく運動不足になりがちです。やはりスタンディングデスクやステッパーが必要なのかもしれません。
  • 大きめの通りに面しているので車の音がちょびっと気になったりします。

在宅勤務手当で買いたいもの

  • この前発売されたREALFORCE for Mac PFU Limited Editionがとてもほしいです。
  • 持っているイヤホンは前に買った5000円程度のもので特に不自由はしてないのですが、せっかくなのでいいイヤホンを買いたいです。友人に教えてもらったSHUREのSE215が気になっています。

ForSchool事業部エンジニア: 石上さん

続いて、ForSchool事業部エンジニアの石上さんの作業環境を紹介します。

写真

f:id:tagucch:20200601172314j:plain

焦ったときはI'm very busy 猫を見て落ち着くようにしています。

とのことです。

今の在宅勤務環境の良い点・悪い点

良い点

  • コンパクト
  • 集中できる
  • 好きな姿勢で仕事ができる
  • 行き詰まったときに変な声を出せる

悪い点

  • ゆくゆくはもうちょっと机を広げたい

在宅勤務手当で買いたいもの

  • 美味しいせんべいをたくさん買いたいと思います。

ひとこと

石上さんの作業環境はシンプルでコンパクトにまとまっておりきれいですね。I'm very busy猫がかわいい。
画面はMacBook Proの1枚のみですが目線の高さまでスタンドを使って上げているのがいいですね。WebカメラもMacBook Pro内蔵のものが使えてよさそうです。

ForSchool事業部エンジニア: 冨山さん

次はForSchool事業部エンジニアチームのリーダー冨山さんです。

写真

f:id:tagucch:20200601165211j:plain f:id:tagucch:20200601165242j:plain f:id:tagucch:20200601165301j:plain

(もしあれば)こだわっているところや商品情報など

  • 電動昇降デスク/flexispot E3
    • 仕事中にボタン一つで高さを調整できるので立ったり座ったりできて腰痛に悩まされないため重宝しています!立っているときは足元のメディスンボールの上に立って体幹を鍛え上げてます!
  • キーボード(左)/planck rev6
    • ortholinear配列のキーボードで打鍵のときに手をあまり動かさなくて良いのでコーディングするときに重宝しています!
  • キーボード(右)/MISTEL BAROCCO MD650L
    • こちらはロープロファイルのメカニカルキーボードでwindowsマシンに繋いであります.クライアントアプリケーションをwindows環境でデバッグ作業する時くらいにしか使いませんが色合いが好きで気に入ってます!!

今の在宅勤務環境の良い点・悪い点

良い点

  • 空調を自分で好きなように調整できること
  • 電動昇降デスクで自分が一番しっくりくる体勢で仕事ができるので集中力が上がる
  • 音楽をスピーカーでかけても怒られないこと

悪い点

  • MBPをクラムシェルモードで運用したいんですが,ウェブカメラがないので会議の時MBPを机の上に広げておく必要があって机の上が密
  • 話し相手がいないので雑談やちょっとした会話ができずに精神的負荷がかかる
  • 椅子がAmazonで買った安いプラスチック椅子なので座り心地が悪い

在宅勤務手当で買いたいもの

  • 椅子(GTRacingがほしいです!!!)
    • 座り心地が....
  • ウェブカメラ
    • クラムシェルモード...
  • カーテン
    • 背中に窓があるため,会議のときに逆光になってラスボスっぽくなってしまうので
    • 男の夢

ひとこと

冨山さんはなんといってもスタンディングデスクがあるのがポイントですね。電動昇降なので気分や体調によって高さを自由に決められるのは運動不足になりがちなリモートワークでは重要ですよね。
キーボードもこだわりが見られますね。自分もMD650Lを持っているのですが、左右を分離させることもできますし冨山さんの写真のようにくっつけることもできます。

ForSchool事業部デザイナー: 秋間さん

次はForSchool事業部デザイナーの秋間さんです。

写真

f:id:tagucch:20200601165402j:plain f:id:tagucch:20200601165421j:plain

(もしあれば)こだわっているところや商品情報など

  • デスクの横に花を必ず飾っています。FLOWERというお花便のサービスを使って月二回季節の花が届くので、デスク周りが華やかになって気持ちも落ち着きます。
  • リモート勤務になってすぐに、椅子をバランスボールに切り替えました。VLUVのバランスボールは、転がりにくくて、安定感もあってとても気に入っています。姿勢も気にすることができるので、とてもありがたい存在です。
  • iPad proで音楽を流したり、オンライン会の視聴をしたり、ほどよく手軽に使えて、自宅勤務になってからよく使うようになりました。
  • デスクとデスク横のチェストは無印良品です。
  • スピーカーは、BOSEのSoundLink Miniを愛用しています。
  • MacBook Proを置くためのスタンドは、Rain DesignのmStandを使っています。
  • イヤホンは、AirPods Pro。今年買ってよかったランキングの1位かもしれません。

今の在宅勤務環境の良い点・悪い点

良い点

  • 好きな音楽を聴きながら作業できたりリラックスしながら作業ができるところ
  • 家の環境をきれいに保てるので、精神衛生上良いです。(仕事してるときは土日くらいしか片せなかった)

悪い点

  • 部屋の壁紙がちょっとダサいので賃貸でも使える壁紙を貼りたい・・・
  • ライトが暖色系しかなく、夜になると集中しにくくなってしまう
  • 部屋の冷暖房環境があまり整っていないので、夏は暑さに耐えられるのか不安。

在宅勤務手当で買いたいもの

  • philips hueの電球を買ったものの、デスクライトの電球ソケットと合わず(事前に調べてなかった)使っていないので、新しく照明器具を買おうと思っています。
  • キーボードが有線しか自宅にないので、Bluetoothのものを買いたいです。
  • 欲をいえば、電動のスタンディングデスクが欲しいです。

ひとこと

秋間さんの作業環境はバランスボールが目を引きます。そして花が机にあると見た目が華やかになりますね。
全体的にコンパクトにまとまっている印象ですが、物が少ないので窮屈な印象はなく、花があって素敵な環境だと思います。

ForSchool事業部長: 宮坂さん

次はForSchool事業部の部長、宮坂さんです。

写真

f:id:tagucch:20200601165501j:plain

(もしあれば)こだわっているところや商品情報など

  • キーボード / REALFORCE for Mac
    • 出張や会議も多いのでラップトップのキーボードを使っていたのですが、REALFORCEを買いました。新卒で就職した会社(リブセンス)では、社員全員がREALFORCEを会社に用意してもらっていたので久しぶりのREALFORCE(当時はfor Macがなかったのでこれも感動)
  • 椅子 / イトーキ オフィスチェア サリダ YL6
    • アーロンチェアは流石に高いので(当時のリブセンスは社員全員がアーロンチェアでした汗)、ロッキング機構がある椅子でリーズナブルなものということでこれを選びました。 購入してからは疲労感も消えたのでとても良かったです。
  • Google Nest Wifi
    • 以前から自宅でリモートワークしていた際にzoomの接続が不安定なときがあったので、回線の改善のために購入しました。購入してからは一度もzoomが不安定になることはありませんでした。

今の在宅勤務環境の良い点・悪い点

良い点

  • 通勤時間がないのでそれだけ長く働ける点。コロナ禍でオンラインで指導せざるをえない学習塾のご支援が増えたことで、仕事量はどうしても増えてしまうのでこれは良かったです。

悪い点

  • もともと運動不足でしたから、通勤さえもなくなってしまうと、本当に動かなくなってしまったので困りました。しかも椅子に座りっぱなしなので、上記の椅子が到着するまでは、疲労も酷いものでした。

在宅勤務手当で買いたいもの

  • グリーンバック
    • 私のMacBook12inchはグリーンバックがないとバーチャル背景を利用できないためこれは欲しいです。
  • SwitchBot(スマートカーテン)
    • カーテンを閉めないと逆光になってしまうので、オンライン会議のときはいつもカーテンを締めているのですが、せっかくGoogle Homeがあるので、カーテンを音声で締めたいです。

ひとこと

宮坂さんの作業環境もシンプルにまとまっていますね。壁に飾られた植物や照明がおしゃれです。
そしてREALFORCE for Macが目立ちますね。机上が黒でまとまっておりかっこいい。

Studyplus事業部iOSエンジニア: 明渡さん

次は事業部が移り、Studyplus事業部iOSエンジニアの明渡さんの作業環境です。

写真

f:id:tagucch:20200601165757j:plain

(もしあれば)こだわっているところや商品情報など

  • モニタはDMM.makeの55インチのもの
    • 購入当初に電源の入らない初期不良品を引き当て、交換のために再梱包する手間がつらかったので無闇に人におすすめできない気持ち。メーカーに対応はちゃんとしてもらえました
    • 自分はモニタのサイズこの半分くらいで良い派で、家族が大きいモニター使ってみたいということで昨年購入してみたもの
  • 作業部屋が2階、リビングが1階なので手軽に水分補給できるよう電気ポット+急須を常備
    • オフィスにも急須を持ち込んでいるので平常運転

今の在宅勤務環境の良い点・悪い点

良い点

  • 今年2月にお迎えしたおねこさまが床へおもむろに転がっていることがあり、たいへん可愛い。癒し
  • 夕飯を摂る時間が不規則だったのだが、3食とも概ね決まった時間に食べるようになった

悪い点

  • 通勤が多少の運動になっていたようで、意識して体を動かさないとぶくぶく太る
  • 通勤時間を考慮しなくて良いこともあり、うっかり所定時間オーバーして働く頻度が高くなった

在宅勤務手当で買いたいもの

  • 4月時点で手当について発表があり、入ってくるのを当てにして+αでPanasonicの目もとエステなる充電式ホットアイマスク的なものを買いました
    • 昼休憩や寝る直前に使うとすっきりしますが、使い捨てが気にならなければ蒸気でホットアイマスクで十分かもという使用感です

ひとこと

まず大きなモニターが目を引きますね。55インチとなると同時に色々な画面を表示できそうです。そして水分の摂取を怠らないように電気ポットと急須があるのもいいですね。これからの季節は水分不足に注意しなければいけないです。
リモートワークの大きなメリットの一つに、飼っているペットと常に一緒にいられるというのがありますね。飼い猫と一緒に過ごせるのは大変素晴らしいです。

Studyplus事業部デザイナー: 小松さん

次はStudyplus事業部デザイナーの小松さんです。後述されていますが、元々リモートワークの環境作りにはかなり力を入れていたようでとても素敵な作業環境です。

写真

f:id:tagucch:20200601165842j:plain f:id:tagucch:20200601165904j:plain

(もしあれば)こだわっているところや商品情報など

  • フリーランスだった期間が8年くらいあり、たまに副業もするので、もともと家でも作業できるようにはしてました。
  • 昨年夏に引っ越してから、片付けつつじわじわと部屋づくりをしてた。そんなに予算は無いのでコスパにこだわりがち。
  • モニターは LG UltraFine 5K Display 、きれいな中古があったので買った。
  • デュアルのディスプレイアームマウントトレー で2画面をできるだけシームレスに使えるようにしている。
  • デスクは、昔あった無印のユニットシェルフのデスク。フリーランス時代にでっかいデスクを置きたくて2個並べていたのの生き残り。
  • 椅子は WilkhahnSolis 、コレもフリーランスを始める際に勢いで大枚を叩いて購入し、文字通り今まで身体を支えてきてくれたのだけど、10年以上使ってファブリックとか流石にいろいろくたびれてきたので、感謝しつつ買い替えたい気分。
  • ヘッドホンはSONYの WH-1000XM3 。集中するのにとてもいいです。UI検証用に用意したPixel3aとの接続だとより高音質で音楽聞けるコトを発見し、ちょっとうれしい。
  • AmazonのEcho(Plus , Show5 , Dot)とIKEAのTRÅDFRIなどで、だいたいの照明と家電は声で制御できるようにしている、スマート!
  • 目の前のリビングのペンダントライトは IKEA の GRIMSÅS、ちょっとファンシー過ぎるのではないかとも思ったけど、そのシルエットと落とす影とクラフト感で、さっぱりはしてて味気ない無い部屋の中でアクセントになっている。
  • 運動不足解消・健康維持の為に リングフィットアドベンチャー をやる頻度が上がり、自粛前より身体ができあがってきたw

今の在宅勤務環境の良い点・悪い点

良い点

  • 照明・空調を自分の好きなように調整できる、窓全開にしとくのは気持ちいい。
  • スピーカーで音楽が聴ける、歌える。
  • 隙あらばラジオも聴ける。
  • 洗濯物をいつでも取り込める
  • 移動しないでいい

悪い点

  • 線路が近いので電車がうるさい
    • ノイズキャンセリングヘッドホンで緩和できるが、そろそろ暑い。
  • 休憩中も、なんか家事とかやってしまって、厳密には休んでないコトに気づいた。
  • 目を使ってばかりなので、目の疲れはひどい。
    • 目が痛くなったら、ホットアイマスクをして10分横になるといい。
  • リモートの会議やユーザーインタビュー中にAlexaが反応する。
  • 会社で買ってもらった本が無い。
  • お菓子を自前で用意せねばならぬ。

在宅勤務手当で買いたいもの

  • 眼精疲労の緩和の為にブルーライトカットな眼鏡をかけるのだけど、ヘッドホンと干渉するので Short Temple というカタチの眼鏡を購入したい。
  • 朝になるとブラインドが自動的に上がるといいなって思うので、ブラインドを巻き上げる機械が欲しい(ちょっと高い、2個必要)。
  • コーヒーをドリップするようになったので、グラインダーが欲しい。
  • 除湿機も考えている。
  • もしくは植物を増やす。
  • あとアートを飾りたい。
  • 物欲は無限大。

ひとこと

小松さんの作業環境は僕が憧れてしまいました。今揃っているものもそうですし、今後買い足したいものもおしゃれだったりこだわりが強かったりしてかっこいいです。物欲は無限大、本当にそのとおりだと思いますね。
そしてスマートホーム化が進んでいるのもリモートワークだと便利でいいですよね。

総合広告事業部アカウントプランナー: 伊藤さん

続いてアカウントプランナー(営業)の伊藤さんです。リモートワークというとエンジニアやデザイナーの環境を思い浮かべがちなので、営業の方の環境を見せてもらえるのは貴重です。

写真

f:id:tagucch:20200601170020j:plain 退勤後は一式をトレイにまとめています(再現) f:id:tagucch:20200601170057j:plain

(もしあれば)こだわっているところや商品情報など

  • 部屋にデスクを置くスペースがないため、以前はちゃぶ台で頑張っていました。
  • 現在はドレッサー(化粧台)をデスクとして使用しています。
  • デスクにそのままPCを置くと猫背になるため高さや傾斜が変えられるスタンドを使っています。使わないときは折りたためて便利です!
  • 我が家にはディスプレイがないので、欲しい時にはiPadでDuetDisplayを起動してディスプレイにしています。
    • ディスプレイを使わないときはスタンドにスマホを立てています。

今の在宅勤務環境の良い点・悪い点

リモートについて

良い点

  • 独り言を言っても特に問題ない
  • 昼食代が安く済む
  • 休憩時間に洗濯等家事ができる
  • 体調がよくない日は服装や体勢を工夫して仕事ができる
  • MTGや打合せの平均時間が短い

悪い点

  • 画面越しにしか人と話していないため、通常の会話がどのようなものだったかわからなくなってきている。コミュニケーション能力が下がっている気がする
  • 仕事が終わっても気持ちの切り替えがしづらい
  • 運動不足
  • ラーメンが食べられない
  • ラーメンが食べられない

この環境について

良い点

  • 目の高さに画面があるので、背中や腰の痛みが以前より軽減されている
  • 仕事が終わったらコンパクトに片づけられる
  • すぐ左に冷蔵庫があり、いつでも飲み物を飲める

悪い点

  • シンプルに狭い
  • 隣の住人がいる方の壁に向いているため電話やWeb会議の声が聞こえていないかやや心配。
  • ライトがまぶしい
  • ドレッサーの椅子は8時間座るものではない。痛い。
  • ドレッサーを使用しているので、途中で化粧を直したくなった時に少し大変

在宅勤務手当で買いたいもの

  • やはりお尻が痛くなるので、いい感じのクッションが欲しいです。もしくはバランスボールに座りたいです。

番外編(ベランダ勤務)

f:id:tagucch:20200601170137j:plain

暖かい晴れた日にやりました。 大変気分転換になりましたが、電源がない、なんとなく通話がしづらい、 花粉症の症状が強まるなど課題は多そうでした。

ひとこと

伊藤さんはドレッサーを作業環境にして、退勤時に片付けるという形をとっているそうです。ディスプレイをiPad含め2枚にしていますが、視線が下を向かないように高さを合わせていていいですね。
ベランダでの勤務はいい気分転換になりそうですが、花粉なども考えるとたまーにやるくらいがちょうどよさそうですね。
冷蔵庫がすぐ隣にあり、明渡さんと同じく水分補給が手間なくできるのもよさそうです。

ポルト事業部デザイナー: 竹内さん

最後に、昨年9月にリリースされたPortoの開発・運営を行うポルト事業部のデザイナー、竹内さんの環境を紹介します。

写真

f:id:tagucch:20200601170227j:plain f:id:tagucch:20200601170249j:plain

こだわっているところや商品情報など

  • デスクはずっと使っているもので、とても気に入ってます
    • たまにオイル塗るメンテナンスが必要だけど、ピカピカになるので良い
    • が、奥行きが狭い(500mm)ので、ディスプレイアームでディスプレイ・PCを浮かせて使えるエリアを広げています
  • オンラインMTG用にマイクはRODE NT-USB Mini、イヤホンはAirPods Proを使用
  • スピーカーはBose SoundLink Mini Bluetooth speaker II で、音楽聴くのに使っています
    • キャンプとかアウトドアにも持っていけるのでオススメ
  • ワイヤレス充電スタンドは、スマホが対応していれば、立てておくだけで充電できて便利なのでオススメです。AirPods Proも充電できます

今の在宅勤務環境の良い点・悪い点

良い点

  • 家族の理解があり、作業用に一部屋(の一角)使えているので、子どもがMTGに映り込むトラブルはまだ無い
  • 家族と3食いっしょに食事できるので良い

悪い点

  • 北側の部屋なので、あまり日差しを感じられない & 寒い
  • 宝くじが当たったらもっと良い椅子が欲しい...

在宅勤務手当で買いたいもの

  • Wi-Fiルータを新調したい(調子が悪くてインターネットがたまに途切れるので)

ひとこと

竹内さんは元々フルリモートで勤務しているので、作業環境がとても整っていますね。特にデスクがおしゃれで、全体的に白色の部屋に対していいコントラストなように思います。
特にお子さんがいる家庭ではリモートワークでみなさん苦労している方が多いように思いますが、作業部屋があり家族の理解があるというのは素晴らしいですね。

さいごに

今回は弊社スタディプラスのメンバーのリモートワークでの作業環境の紹介でした。
リモートワークではずっと家にいるので、長時間いることを前提に自分の好みの環境を作りたいですよね。みなさんの個性が出ていて、この記事を書いていてとても楽しかったです。
弊社ではコロナウィルスの影響によりリモートワークが続いており、今後も適宜リモートワークを選択していける環境が整っています。ご興味のある方はぜひこちらをご覧の上ぜひご応募ください。