HTML & CSSとWebデザイン入門講座、実践講座を読んだ

HTML/CSS は今まで雰囲気で書いてたので本でも読んだらちゃんと身につくかなと思って読んでみた。結論として、今後も雰囲気でHTML/CSSを書いていく。

入門講座

入門講座の方は本当に入門という感じで、HTMLとCSSの基本的な解説がされていた。流石にそこはわかっていたのでパラパラ〜っと読み進めて、Flexbox と Grid がよくわかってなかったのでそこはちゃんと読んだ。

後半の実際にウェブサイトを作ってみる章はなかなか良くて、しっかりした解説とともにコードが記載されていて、写経しながらこういうデザインはこうやって組めばいいのかと理解できてよかった。

実践講座

実践講座はWebページごとのデザインとコードが載っている感じで、こっちは写経せずパラパラ〜と読むだけにした。今後自分でウェブサイトを作るときにレイアウトの参考書として使えそう。

おわり

HTML/CSSの入門書としては良さそうだけど、中級者くらいの人にとっては易しすぎる本だった。とはいえ体系的に説明してくれているのでリファレンス的に利用できそう。

当初の目的であった、HTML/CSS を雰囲気で書いているという状況からは脱出できなかった。

プロダクトマネジメントのすべて を読んだ

プロダクトマネージャになりたいわけではないが、エンジニアとしてプロダクトマネジメントについてもっと知っておくかと思って読んでみた。

内容は薄く広くという感じでプロダクトマネジメントに必要ことが述べられていて、プロダクトマネージャめちゃくちゃやることあるじゃん!ってなった。プロダクトマネジメントという点において深い知識を持ちつつ、デザインやエンジニアリング、法や制度、マネジメントなど、幅広い知識と業務をこなす必要がある。(だが実際、この本に述べられていることをちゃんと出来ている人はほとんどいないと思う)

本を読みながら、どんな人が優秀なプロダクトマネージャなのかなあと考えたら「信念と根拠をちゃんと持っており、周りに流されず周りを巻き込んでプロダクトを成長させる人」と思った。手法とか知識量も大事だけど、それをもとにロードマップ示しを推し進めていける人が優秀と思う。

Fit & Refine、プロダクトマーケットフィットをちゃんとしよう、というのが何回か述べられていてここが特に重要そうに思った。まあ当然といえば当然なんだけど、プロダクトの根幹がしっかりしてないと余計な機能を作ったりブレてしまいそう。

プロダクト開発の手法等色々述べられいて、これらを深く学ぼうとはあまり思わなかったけど、統計については今まで雰囲気でしかやってないのでちゃんとやったほうがいいかもと思った。どれくらいのユーザ数が入れば有意に差があると言えるのかなどは全然わからないのでちゃんと勉強が必要そう。

たくさんのキーワードや手法が出てきて、じゃあそれを深く学ぼうと思ったらどうしたらええんや... って思ってたら巻末に推薦図書を記載してくれててありがたかった。

やっぱりプロダクトマネージャになりたいとは本を読んだ後も思っていないが、業務で働くときに「これ本で読んだな」と出てくることがありそう。また、個人開発では全てのことを一人でやるしそこでも活きてくることはありそう。

読み終わった後に著者の及川さんが Podcast に出てて、これを聞くと理解が深まって面白い。

fukabori.fm

プロを目指す人のためのTypeScript入門 を読んだ

TypeScript を雰囲気で使っている感覚があったのでもうちょい深く知るかと思って読んでみた。

プロを目指すとあったので実践入門的な書籍かと思ったけど、入門とあって結構基本的なことの解説も多くて、それなりに TypeScript を書いているので知ってる内容も多かった。でもいくつか知らないことを知れたり、TypeScript の奥深さや高度な型など理解できたのはよかった。また、TypeScript は歴史的経緯から複数の書き方などがあるが「著者ならこうしてます〜」みたいなのがあって表面的な情報だけでなく意見が知れて良かった。

JS/TS 書いてて null と undefined どっち使ったらいいのかいつも迷ってたけど本では「言語使用上 undefined の方がサポートが厚いので筆者は undefined を推奨します」と書かれていたり、りあクト!では interface が推奨されてて疑問に思ってたけどこの本では type の方を推奨してて、やっぱそうだよねって思えて良かった。

TypeScript には馴染みのない型が多くて、 keyof とか typeof とか lookup 型とか mapped types らへんは読んでる時はなんとなくわかったけど、どういう場面で使うのか、実際に使いこなせるかというと微妙で、そういうのを頭に入れつつコードを書きながら理解を深めていったり、型に悩んだらこの本をもう一度参照するなどができたらいいかなと思った。ひとまずは TypeScript: Documentation - Utility Types をちゃんと使いこなしていきたい。

Promise は普通に使ってたけど race, allSettled, any は使ったことがなく初めて見た。

関数の返り値の型は、今では雰囲気で書いてたり、返り値の型を明示するのが面倒なので推論に任せていたけど、この本を通して意図して返り値の型を推論されたり明示して考えるべきというのを知れて良かった。「関数の中身が真実である」場合は省略し、「この関数はこの型の値を返すべきである」なら型を明示するべき(ただし全て書くべき派など様々な人間がいる)。

以下雑メモ


  • enum や namespace は現在は推奨されてない
  • プリミティブ型とオブジェクト型の2種類しかない
    • プリミティブ方は文字列、数値、審議地、BigInt、null、undefined、シンボルの7つ
  • TypeScript の言語使用上 undefined の方がサポートが熱ので undefined を推奨
  • == は異なる型の比較を行った場合、暗黙の型変換を行なってから両者を比較するので意図せず true となってしまうことがあり === を使うべき
  • 変数 ||= は 変数 = 変数 || 式 と同じ
  • スプレッド構文でオブジェクトをコピーする場合、ネストしたオプジェクトは同じオブジェクトを参照するので注意
  • 関数の返り値を省略するか明示するか意図して考えた方が良い
  • private と同じく # という書き方もある
  • lookup 型は Human["age"] のように指定できる
    • 型情報を再利用できるのが lookup 型の基本的な使い方
  • keyof 型はオブジェクトの方からそのオブジェクトのプロパティ名の型を得る機能
    • keyof T と書く
  • as const TypeScript 4.9のas const satisfiesが便利。型チェックとwidening防止を同時に行う
  • mapped types や conditional types で有用な機能は標準ライブラリに組み込まれており、Readonly や Partial などがある
  • default エクスポートは default という名前でエクスポートする機能
  • export type {} を使うと型としてのみ使用可能となる
  • import type {} もあり、こちらは型として import できる
  • Promise.race は複数の Promise から最初に成功or失敗したものを結果とする
  • Promise.allSettled は全ての結果が出るのを待ち {status: 'fulfilled'|'rejected|, value: 結果} を返してくれる
  • Promise.any はどれかが成功したら返す
  • dynamic import でモジュールを遅延読み込みできる

りあクト! TypeScriptで始めるつらくないReact開発 を読んだ #りあクト

booth.pm

React の入門書として良さそうなので読んでみたらめっちゃ良かった。
3冊構成で、Webフロントエンドの歴史から JavaScript / TypeScript の入門、tsconfig / ESLint / Prettier などの設定方法、 React そのものの説明、状態管理フレームワークの解説などがされており、他言語から React を始める人向けの内容ということで自分がそのターゲットなのでとてもためになる内容だった。

特に② React基礎編 の 5-1-2. なぜ React ではデザインとロジックを混在させるのか が良かった。
Rails 的な考えだったり Swift でも VIPER みたいに View と Model を分離しておくみたいにやってたけど、そうではなくコンポーネント単位で分割していくという考え方が推奨されている。Flutter でも Riverpod を触っているとそんな感じになっていって、宣言的UIはそうなのかなと思った。

また、業務で副作用を持つ Component とスタイルだけを当てている純粋な PureComponent と分けて実装しているが、Container Component と Prentational Component と呼ばれていてちゃんと言語化されてしっくりきた。
lazy 関数や Suspense などはまだちゃんと理解できてないけど、開発していく中で理解できたらいいなと思っている。

個人で執筆しているようでたいぽが見受けられて、 useContextuserContext になっていたりして混乱することがあった。また、自社の技術スタックと違う点も多く(Nextjs は特に解説がなかったり Redux の説明が多かったり)、3冊目はためになる内容もあればそうでもない内容もあったが、これから React 始めるって人にはおすすめの本だと思います。

WEB+DB PRESS Vol.132 を読んだ

最近本を色々読んでいるので、WEB+DB PRESS を読んでブログ書くほどでもないけど、せっかく読んだので雑にでもメモを残しておく。

自分の守備範囲外の内容も多いので、すごくためになるかというとそうではないけど勉強にはなった。
まあ雑誌なのでペラペラめくって興味あるとこ読むって感じ。

以下雑メモ

サバンナだより

テストについて。今まではモックと呼んでいたけど正確にはテストダブルを使った方が良さそう。

テストダブルを使うことで忠実性を下げる代わりに速度と決定性を向上させる。テストダブルを使うのは再現の難しい条件のみにして、本物を使えるなら使った方が良い。

オブジェクト指向神話からの脱却

オブジェクト指向の復習みたいな内容だった。

オブジェクト指向が形骸化してきて hoge.method() みたいにモデルに対してメソッドはやすだけみたいに最近はなってきてるよねみたいに書いてあってせやなと思うし、既存のデザインパターンをそのまま使うより今ではラムダ式を使った方が良いよねみたいに言っててせやなってなった。デザインパターンについて否定的に書かれていたように思うが、既存のデザインパターンは現代では適切でないにしてもデザインパターンを知っておくことは良いことなのでそういうふうに書いた方が良いのではと思った。

iOS16

Swift 5.6/5.7のアップデートは最近全然Swift追ってないのでへえ〜ってなった。Generics で some が使えるのは良さそう。

iOS16ネタはすぐには業務で使わないけど頭の片隅に置いておくのは便利そう。

フロントエンドコンポーネント駆動開発

ChakraUI + storybook + Figma を連携していい感じにやる。Figma に storybook を表示できてて、そんなことできるんだ〜ってなった。

https://www.chromatic.com/ は知らなかった。まずは個人開発とかで使ってみたら良さそう。

Web Twitter の初期選択タブを「フォロー中」にする Chrome Extension を作りました

chrome.google.com

最近の変更で https://twitter.com/home を開くと初期選択タブが「おすすめ」になってしまい、毎回手動で「フォロー中」に切り替えるのが面倒で Chrome 拡張を作りました。ソースコードGitHub で公開しています。

Tweak New Twitterという拡張もあって、これでも同じようなことはできるんですが、おすすめタブが消えてしまいます。おすすめタブは残しておきたいし、その他の機能は不要なので起動時にタブをフォロー中にするだけの拡張を作りました。

挙動としては単純で、https://twitter.com/home にアクセスした時に自動でフォロー中タブになっています。おすすめタブも残っているので、おすすめタブが見たくなったらおすすめタブをクリックして普通に閲覧ができます。

ガッと作ったのでバグがあるかもしれませんが、手元では動作しているのでよければ使ってみてください。

ソースコード

github.com

コードは GitHub に公開しています。

最初実装しようとした時は document_end でおすすめタブに切り替えたらいいかな〜と思ったんですが、 Twitter が SPA なためか? document_end や DOMContentLoaded ではうまく動作せず body を observe して div[role="tablist"] を検知したらフォロー中タブをクリックしています。

var tablistObserver;
const bodyCallback = () => {
  const tablist = document.querySelector('div[role="tablist"]');
  if (!tablist) {
    return;
  }

  const followingTab = [...tablist.children].find(c => c.textContent === 'フォロー中' || c.textContent === 'Following');
  const aTag = followingTab.getElementsByTagName('a')[0];
  aTag.click();
  tablistObserver.disconnect();
  tablistObserver = undefined;
};

const observeBody = () => {
  if (!tablistObserver) {
    observing = true;
    tablistObserver = new MutationObserver(bodyCallback);
    const body = document.querySelector('body')
    const config = { childList: true, subtree: true };
    tablistObserver.observe(body, config);
  }
}

また、これだけだと他のページから遷移してきた時にうまく動作しません。ページ遷移を検知するために head を監視して画面遷移を検知しています (本当は URL の変更を検知したかったのだが Chrome extension でそれをやるには面倒臭そうな感じがして head の監視で妥協した)

var firstHome = true;
const headCallback = () => {
  const isHome = location.pathname == '/home';
  if (firstHome && isHome) {
    observeBody();
    firstHome = false;
  }
}

const head = document.querySelector('head');
const observer = new MutationObserver(headCallback);
const config = { subtree: true, characterData: true, childList: true };
observer.observe(head, config);

このあたりの処理のせいで無駄に observe が発生してしまい重くなる懸念があり、また冪等性がない実装になっているのがあまり良くない。

審査について

Chrome 拡張には審査が必要で、一度 Reject された。

提供されている説明が、アイテムの機能を理解するのに十分でない。

とのことで、確かに説明が薄かったので説明を追加したところ審査が通りリリースされた。初回の審査は半日くらいかかったけど、アップデートの審査時間は数分でできているのでアプデでは機械的な審査しかしてなさそう。

おわり

Webフロントエンドハイパフォーマンスチューニングを読んだ

2017年の本で、パフォーマンスを意識したコードをかけた方が良いと思って、若干古いんだけど良さそうなので読んでみた。

ブラウザがHTMLを取得してからCSSで描画されるまでの流れを説明したり、Chrome Dev Tools の使い方を説明してくれたり、キャッシュの仕組みやパフォーマンス改善方法などが記載されていて良かった。ためになることも多かったけど、ひとまず今までの実装の考え方は特に変えずに、アニメーションをまじめに実装するときやウェブサイトが遅くなったりしたらこの書籍を思い出すくらいで良いかなと思った。

2017年の書籍ということもあり、古い情報も多く3割くらいは読み飛ばした。React を使っていたら自力で DOM を書き換えたりなどは基本発生しないし、asm.js を利用した最適化なども現代では古そう。Chromeでしか実装されていませんみたいなテクニックも記載されてて、それは2023年になってもSafariに実装されてないようだった。

以下メモ

  • TSLハンドシェイクはTCP接続の3ウェイハンドシェイクに加えてさらにパケットを最低1往復半やりとりする
  • 計測はCIとかで日時でスコアを確認すると良さそう
  • 計測便利ツールとして NewRelicBrowser が紹介されていたけど、現代だと Firebase Performance でもいい感じに見れるのだろうか?
  • CSSスプライトとかは現代でも使われるのだろうか、使われなさそう
  • リンクは http://example.com/foo へのリクエストに対し http://example.com/foo/ へリダイレクトすることがあるので最初から / つけた方がいいかも
  • requestIdleCallback - Web API | MDN 現代でも Safari で使えない
  • css セレクタは右から解釈するのでセレクタは単純な方が良い
    • body > dig.logo { ... } の場合、class に logo が含まれ、その要素が div であり、その親要素が body である、となる
  • class属性を変更してアニメーションするのは避けた方が良い
  • アニメーションでは transform, opacity を使用するのが良い

DNSの名前解決に時間がかかることがある、みたいなのが書かれてた時にふと「じゃあIPアドレス直書きしたらいいじゃん」って思って ChatGPT に聞いたらちゃんと答えてくれてすごい。

next lint の結果を Reviewdog でプルリクエストに反映する

next lint の結果を Reviewdog に食わせて PR にコメントとして出したかったんだけど結構ハマったのでメモ。

普通に next lint を動かしてもだめ

  "scripts": {
    "lint": "next lint"
  }
      - uses: reviewdog/action-setup@v1
        with:
          reviewdog_version: latest
      - name: Run reviewdog
        env:
          REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: npm run lint | reviewdog -f=eslint -reporter=github-pr-review -filter-mode=diff_context -fail-on-error=true

こんな感じでやってみたんだけど、lint ではエラーが出てるけど reviewdog が PR に反映してくれなくてなんで? ってなっていた。

next lint はフォーマッタが eslint デフォルトと違う

next lint の warning メッセージ:

$ npm run lint:next

./src/hoge/index.tsx
39:6  Warning: React Hook useEffect has an unnecessary dependency: 'ref.current'. Either exclude it or remove the dependency array. Mutable values like 'ref.current' aren't valid dependencies because mutating them doesn't re-render the component.  react-hooks/exhaustive-deps

info  - Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rules

eslint の warning メッセージ:

& npm run lint:next

/Users/hoge/index.tsx
  39:6  warning  React Hook useEffect has an unnecessary dependency: 'ref.current'. Either exclude it or remove the dependency array. Mutable values like 'ref.current' aren't valid dependencies because mutating them doesn't re-render the component  react-hooks/exhaustive-deps

✖ 1 problems (0 errors, 1 warnings)

info  - Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rules

となっており、若干の違いがある。

next lint -f stylish

next lint にフォーマットを指定できるので、 -f stylish をすると reviewdog が解釈できるようになる。

https://eslint.org/docs/latest/use/formatters/#stylish にあるように、eslint は stylish がデフォルトフォーマットとなっている。

  "scripts": {
    "lint": "next lint -f stylish"
  }

これでPRにコメントしてくれるようになった。

おわり

Reviewdog で試行錯誤したPRは reviewdog by starhoshi · Pull Request #3 · starhoshi/romoco · GitHub で、最終的な workflow yml はこれです

name: Test
on: [pull_request]

jobs:
  build:
    name: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'npm'
      - run: npm ci
      - uses: reviewdog/action-setup@v1
        with:
          reviewdog_version: latest
      - name: Run reviewdog
        env:
          REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: npm run lint | reviewdog -f=eslint -reporter=github-pr-review -filter-mode=diff_context -fail-on-error=true

https://github.com/starhoshi/romoco/blob/main/.github/workflows/pr.yml

追記

Reviewdog を使わないで next lint -f stylish するだけで PR には反映できるのでそっちでも良さそう

App Store で言語が EN 英語 になってしまう時の対処法

App Store でリリースしたら対応言語が EN 英語になってて、これを日本語に修正する。

Xcode 側で修正を行う

これは App Store Connect の設定ではなく、Xcode でビルドする時の設定。

PROJECT の Localization に Japanese を追加する

対応としてはこれだけでも言語は日本語になるのだが、英語と日本語 の2つ対応となってしまう。英語に対応していなければ英語も消したい。

xcodeproj を直接いじって English を消す

English も消すことができるが、 Xcode 上では English のマイナスボタンが押せないようになっており、直接プロジェクトファイルをいじる必要がある。

vi hoge.xcodeproj/project.pbxproj

                        buildConfigurationList = E391B24F1F14570100301160 /* Build configuration list for PBXProject "DamageSV" */;
                        compatibilityVersion = "Xcode 3.2";
-                       developmentRegion = en;
+                       developmentRegion = ja;
                        hasScannedForEncodings = 0;
                        knownRegions = (
-                               en,
                                Base,
+                               ja,
                        );
                        mainGroup = E391B24B1F14570100301160;
                        packageReferences = (

このように変更すると、 Development Localization が Japanese になる。

おわり

Flutter でアプリをリリースしたらこうなっていたので Flutter だけなのかなと思ったが、普通に Xcode でリリースしたアプリもこうなっていたのでみんななってるのかも。

Apple に A functional link to the Terms of Use (EULA) でリジェクトされた対応方法

Apple に新規アプリで審査を出したらこのような Reject が返ってきた。

Hello,

Thank you for your resubmission. Upon further review, we identified an additional issue that needs your attention. See below for more information.

If you have any questions, we are here to help. Reply to this message in App Store Connect and let us know. 

Guideline 3.1.2 - Business - Payments - Subscriptions

We noticed that your app did not meet all the terms and conditions for auto-renewing subscriptions, as specified in Schedule 2, section 3.8(b) of the Paid Applications agreement.

We were unable to find the following required item(s) in your app's metadata:

– A functional link to the Terms of Use (EULA)

Next Steps

To resolve this issue, please add this missing information. If the above information is present, please reply to this message in App Store Connect to provide details on where to locate it. 

If you are using the standard Apple Terms of Use (EULA), you will need to include a link to the Terms of Use in your App Description. If you are using a custom EULA, add it in App Store Connect.

Resources

- Learn more about offering auto-renewable subscriptions on the App Store.

- Review the Paid Applications agreement (App Store Connect login required).

Reject された時のアプリの状況は以下の状態だった。

対応

主に https://gosyuin-map.seesaa.net/article/481968762.html を参考にした。
このサイトでは以下のうち①と④に対応したとあり、同様の対応をした。 (私の場合は②③⑤は対応済みだった)

利用規約とプライバシーポリシーは別々のWEBページを用意する
② アプリ内に利用規約へのリンクを実装する
③ アプリ内にプライバシーポリシーへのリンクを実装する
App Store Connect のEULA利用規約を記入する
App Store Connect にプライバシーポリシーへのリンクを掲載する

利用規約とプライバシーポリシーを分ける

当初は terms-privacy.html みたいに利用規約とプライバシーポリシーを混ぜていたが、ちゃんと terms.html, privacypolicy.html に分離してアプリ内や App Store Connect に記述した。

EULA にカスタム使用許諾契約を記載

これはどうしたもんかと思っていたが、クックパッドというアプリをAppStoreでみたら「アプリインストール前にこのURLの利用規約を読んでね」という旨の使用許諾契約が書かれていたので、これを丸っとパクって自分のアプリもこの内容を入れた。

おわり

これで審査無事通った。