OmniAICreator/Qiita-1.07M huggingface dataset:
React 19 の 新機能 useOptimistic を触ってみた

# はじめに
この記事では、React 19 で新たに追加された `useOptimistic` という hook で簡単な実装を行い、その動作を確認してみます。

https://react.dev/reference/react/useOptimistic

# 開発環境
開発環境は以下の通りです。

- Windows 11
- VSCode
- JavaScript
- React 19.0.0-rc-cc1ec60d0d-20240607
- Vite 5.2.0
- Node.js 20.14.0
- npm 10.8.1

また、React 19 RC のインストール方法については、以下の記事に記載しています。

https://qiita.com/Yasushi-Mo/items/027503cbe4b299d99bed

# `useOptimistic` の実装
`useOptimistic` では、楽観的に非同期処理成功後の値を取得できます。ここでいう楽観的とは、非同期処理を開始する際に（処理が完了する前に）処理成功後の値を取得できるということです。
これによって、ユーザーは操作した結果をすぐに受け取ることができます。

```
const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);
```

- 引数
  - `state` : `useOptimistic` の管理対象となる値
  - `updateFn(currentState, optimisticValue)` :  戻り値でのひとつある `addOptimistic` の処理が記載された関数（`optimisticState` を返す）
    - `currentState` : 楽観的更新前の `state`
    - `optimisticValue` : `addOptimistic` の引数
- 戻り値
  - `optimisticState` : 楽観的更新を反映した値で、更新処理中以外は `state` と同じ値になる
  - `addOptimistic` : `optimisticState` の更新実行関数

```App.jsx
import { useActionState, useOptimistic } from "react";

function App() {
  const [likeCount, formAction, isPending] = useActionState(async () => {
    addOptimistic(likeCount + 1);

    await new Promise((res) => setTimeout(res, 3000));

    const response = await fetch("https://example.com/like", {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ likeCount }),
    });
    const jsonResponse = await response.json();
    return jsonResponse.likeCount;
  }, 0);

  const [optimisticLikeCount, addOptimistic] = useOptimistic(
    likeCount,
    (currentLikeCount, optimisticValue) => {
      console.log(
        "🚀 ~ App ~ \ncurrentLikeCount:",
        currentLikeCount,
        "\noptimisticValue:",
        optimisticValue
      );
      return optimisticValue;
    }
  );
  console.log(
    "🚀 ~ App ~ isPending:",
    isPending,
    "\noptimisticLikeCount:",
    optimisticLikeCount,
    "\nlikeCount:",
    likeCount
  );

  return (
    <form action={formActionk}>
      <strong>Like: {optimisticLikeCount}</strong>
      <hr />
      <button type="submit" disabled={isPending}>
        Like
      </button>
    </form>
  );
}

export default App;

```

# 動作確認
Like ボタンをクリックした直後、`optimisticLikeCount` の値が更新されます。一方、`isPending` が `true` の間は、`likeCount` の値は更新されていません。`isPending` が `false` になると、`likeCount` も `optimisticLikeCount` と同じ値になります。

![Vite-React-Google-Chrome-2024-06-14-10-09-14.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/ec9ccf2f-32e5-ba8e-2446-c63500743085.gif)

# 参考
- [React 19 RC](https://react.dev/blog/2024/04/25/react-19)
- [useOptimistic](https://react.dev/reference/react/useOptimistic)
- [useOptimistic](https://zenn.dev/uhyo/books/react-19-new/viewer/use-optimistic)

# 関連記事

https://qiita.com/Yasushi-Mo/items/027503cbe4b299d99bed

https://qiita.com/Yasushi-Mo/items/184d46d7d877df698c9e

https://qiita.com/Yasushi-Mo/items/3bebc244a97534c96382
Compose Multiplatform を Xcode Cloud で使う際にハマったポイント

Xcode Cloud で Compose Multiplatform を使用した iOS アプリのビルドを行う際にハマったことを共有したいと思います。

:::note info
Xcode Cloud 環境
- Xcode 15.4
- macOS Sonoma 14.5
:::

# Java のインストールが必要 :coffee: 
Gradle で Kotlin のコードをビルドするために Java が必要となりますが、mac には Java が入っていないのでインストールする必要があります。

Xcode Cloud で Git リポジトリをクローンした後に実行させたい処理を記載できる `ci_post_clone.sh` を使用して Java をインストールします。

```zsh:ci_post_clone.sh
#!/bin/zsh

brew install openjdk@17
```

ここらへんに関しては DroidKaigi2022 のリポジトリを参考にさせてもらいました 🙏

https://github.com/DroidKaigi/conference-app-2022/tree/main/app-ios/DroidKaigi2022/ci_scripts

ちなみに、CI の度に Java をインストールするのは時間がかかりそうですが、何回か試した感じ **2分30秒** ぐらいなので許容範囲かなと思います。

インストールが完了した後は PATH を通して完了、と思いきや、ここからがハマりました…。

# Java が見つからない ~ The operation couldn’t be completed. Unable to locate a Java Runtime. ~ :innocent:
DroidKaigi のリポジトリを参考に `ci_pre_xcodebuild.sh` を使用し PATH を通す処理を追加しましたが、`xcodebuild` が実行されると Java が見つからないエラーが出続けました。

```zsh:ci_pre_xcodebuild.sh
#!/bin/zsh

export PATH="/usr/local/opt/openjdk@17/bin:$PATH"

# これもダメだった↓
# echo 'export PATH="/usr/local/opt/openjdk@17/bin:$PATH"' >> ~/.zshrc
# source ~/.zshrc
```

`Compile Kotlin Framework` の実行時に Java が見つからないエラー

![スクリーンショット 2024-06-15 午前5.47.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/995/ec8201d6-238d-7cf0-6f22-3fde16e5605d.png)

> Run custom shell script 'Compile Kotlin Framework'
> 
> The operation couldn’t be completed. Unable to locate a Java Runtime.
> 
> Please visit http://www.java.com for information on installing Java.
>
> Command PhaseScriptExecution failed with a nonzero exit code


# 原因 :bomb:

色々と試してみると、 **`ci_post_clone.sh` や `ci_pre_xcodebuild.sh` で PATH を通したりしても、 `xcodebuild` には引き継がれない** っぽいことが判明。

同じような内容の投稿が Stack Overflow にもありました。

https://stackoverflow.com/questions/78156271/xcode-cloud-export-path-and-other-environment-variables-lost

# 解決策と最終的な記述 :white_check_mark:
`xcodebuild` のタイミングで PATH が通っていれば良いと考え、Kotlin のコードをビルドする `Compile Kotlin Framework` で PATH が通るように対応しました。

1： `ci_post_clone.sh` で Java をインストールし、 PATH の記述を `~/.zshrc` に追加

  ```zsh:ci_post_clone.sh
  #!/bin/zsh
  brew install openjdk@17
  echo 'export PATH="/usr/local/opt/openjdk@17/bin:$PATH"' >> ~/.zshrc
  ```

2： `Compile Kotlin Framework` で `source ~/.zshrc` を呼ぶ

![スクリーンショット_2024-06-14_午前8_01_48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/995/db84ccac-0797-2cdf-f921-85cdc476a87f.png)

これで Java が見つかるようになり、ビルドできるようになりました。

# おわりに
結果としては非常にシンプルになりましたが、ここに到達するまでにだいぶハマりました…

結局自分は20回目でやっとビルドが成功したので、この記事の内容が誰かの役に立てば幸いです。

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/995/b5db394d-4d02-fc3b-a308-35b0a90f179b.png width=300 />
docker-composeが使えない

## `docker-compose`が使えない原因
- そもそも、`docker-compose`は非推奨（おそらくv2から）となっており、環境によっては使えない場合がある。
- 使えたとしても、`docker compose`へのエイリアスになっている場合がある。

## 対策
- これからは、`docker compose`を使う。
Javaで「多次元配列を作成する」の動作を確認してみた

# 概要

Javaで「多次元配列を作成する」の動作を確認してみました。
以下のページを参考にしました。

https://www.javadrive.jp/start/array/index7.html

# 実装

以下のファイルを作成しました。
```JSample7_1.java
class JSample7_1{
  public static void main(String[] args){
    int[][] num = new int[2][];

    num[0] = new int[3];
    num[0][0] = 78;
    num[0][1] = 64;
    num[0][2] = 59;

    num[1] = new int[3];
    num[1][0] = 58;
    num[1][1] = 92;
    num[1][2] = 82;
    
    for (int i = 0; i < 2; i++){
      for (int j = 0; j < 3; j++){
        System.out.println("num[" + i + "][" + j + "] = " + num[i][j]);
      }
    }
  }
}
```

以下のコマンドを実行しました。
```
$ javac JSample7_1.java 
$ java JSample7_1 
num[0][0] = 78
num[0][1] = 64
num[0][2] = 59
num[1][0] = 58
num[1][1] = 92
num[1][2] = 82
```

# まとめ

何かの役に立てばと。
paiza.ioでelixir その288

# 概要
paiza.ioでelixirやってみた。
練習問題、見つけたので、やってみた。

# 参考にしたページ

https://qiita.com/kohei-s/items/afd92c6464a5c233ecb5

# サンプルコード

```

defmodule Cipher do
    def conversion(input_text) do
        input_text
        |> String.graphemes()
        |> Enum.map(fn x -> 
            conversion_char(x) 
        end)
        |> Enum.join()
    end
    def conversion_char(x) do
        <<c :: utf8>> = x
        if c >= ?a and c <= ?z do
            <<219 - c :: utf8>>
        else    
            x
        end
    end
end


Cipher.conversion("すずき")
|> IO.inspect
Cipher.conversion("abc")   
|> IO.inspect
Cipher.conversion("0123")
|> IO.inspect
Cipher.conversion("ABC") 
|> IO.inspect
Cipher.conversion("/////")
|> IO.inspect
Cipher.conversion(".......") 
|> IO.inspect





```

# 実行結果

```
"すずき"
"zyx"
"0123"
"ABC"
"/////"
"......."
```

# 成果物

https://paiza.io/projects/z3DjR2V45izksB3TBLxumw?language=elixir

以上。
踏み台サーバ経由の多段SSH接続を設定する

WebサーバやDBサーバをプライベートサブネットに作成した場合、踏み台サーバを用意し、踏み台サーバにSSHで接続し、そこからWebサーバやDBサーバに接続します。
この場合、踏み台サーバが乗っ取られた場合に、他のサーバにも侵入されるリスクが大きくなってしまいます。
また、sshコマンドを2回入力する必要がある、秘密鍵ファイルを踏み台サーバに転送する必要があるといった作業の手間があります。
そこで、ローカル端末にある秘密鍵のみを利用した、多段SSH接続を設定します。

# 多段SSH接続を設定する
ホームディレクトリの.sshフォルダにconfigという名前のファイルを作成し、以下を記述してください。
```
Host bation
Hostname xx.xx.xx.xx (踏み台サーバのパブリックIP)
User ec2-user
IdentityFile ~/.ssh/xxxxx.pem

Host web01
Hostname xx.xx.xx.xx  (WebサーバのプライベートIP)
User ec2-user
IdentityFile ~/.ssh/xxxxx.pem
ProxyCommand ssh bation -W %h:%p

Host db01
Hostname xx.xx.xx.xx  (DBサーバのプライベートIP)
User ec2-user
IdentityFile ~/.ssh/xxxxx.pem
ProxyCommand ssh bation -W %h:%p
```

# sshコマンドで接続する
```
ssh web01
```
　
以上です。
今あるサイトを手軽にアプリ化

## 既存のサイトをアプリのように見せたい、そんな時にPWAは強力
- すでにサイトはあるが、アプリはない。
- アプリを作るまでではないが、あったらあったでうれしい。

## PWAを使えば、ほぼ工数をかけずアプリ化することができる
- 前提条件
  - HTTPSが使える。
     - 簡単にお試ししたいのであれば、[Netlify](https://app.netlify.com)がおすすめ。
- 手順
  1. マニフェストを作成。
      - アプリの説明などを記述 。
  1. サービスワーカーを作成。
      - 既存のサイトをアプリのようにみせるだけであれば、最低限の実装で十分。
      - 作りこむことで、オフラインでの使用・プッシュ通知などが行えるようになる。
  1. アプリ化させたいページで、マニフェストとサービスワーカーを読み込む。 
- サンプル
  - https://github.com/skanno/sample_pwa

## アプリとしてインストールするには？
- ブラウザで該当ページを読み込み、「ホーム画面に追加」を行う。
    - 通常はそのURLへのショートカットが追加されるのみだが、PWA化されたページだとあたかもアプリをインストールしたかのようにみえる。
railsのマイグレーションファイルを削除する方法

# 環境

```
$ ruby -v
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [aarch64-linux]
```
```
$ rails -v
Rails 7.0.6
```

# 削除手順
現在のマイグレーションの状況を確認する。

```
$ rails db:migrate:status

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20230206140511  Create tasks
```
削除対象のIDを指定し、statusをdownに。

```
$ rails db:migrate:down VERSION=20230206140511
```
一応確認。

```
$ rails db:migrate:status

 Status   Migration ID    Migration Name
--------------------------------------------------
  down    20230206140511  Create tasks
```
downさせたマイグレーションファイルは削除。(guiでも可)
```
$ rm db/migrate/20230206140511_create_tasks.rb
```
マイグレーションが削除されているのを確認。
```
$ rails db:migrate:status

 Status   Migration ID    Migration Name
--------------------------------------------------
```
# 先にファイルを削除してしまっていた場合

```
$ rails db:migrate:status

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20230206140511  ********** NO FILE **********
```
ファイルはないけど、マイグレーションからは削除されていないといった状況。
`NO FILE`のマイグレーションに対して`{Migration_ID}_{Migration_Name}`の形で適当なファイルを割り当てる。（Migration Nameは一意に）
```
$ touch db/migrate/20230206140511_hoge.rb
```
反映されていることを確認。
```
$ rails db:migrate:status

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20230206140511  Hoge
```
割り当てたファイルにclassだけ記載。

```rb:20230206140511_hoge.rb
class Hoge < ActiveRecord::Migration[7.0]
end
```
あとは[削除手順](#削除手順)へ


# 不可逆なマイグレーションの削除でエラーになった時
changeメソッドでの`drop_table`や`remove_column`などの不可逆なマーグレーションをdownさせる際はエラーになってしまう。
パワープレイではあるが、マイグレーションファイルを書き換えてしまえばエラーは回避可能。
ただし
```:対応前
class DropTableTasks < ActiveRecord::Migration[7.0]
  def change
    drop_table :tasks
  end
end
```
```:対応後
class DropTableTasks < ActiveRecord::Migration[7.0]
end
```
Supabaseでローカル用にAuthenticationの初期ユーザーを作成する

Supabaseの開発をする際にSupabase CLIを用いることでとても簡単にローカルでの開発が可能になります。

https://supabase.com/docs/guides/cli/local-development

また、supabaseフォルダ直下に`seed.sql`を作成しておけばローカル環境を立ち上げた際にマイグレーションから初期データの投入までセットで行ってくれます。

https://supabase.com/docs/guides/cli/seeding-your-database#what-is-seed-data

アプリケーションで認証機能を用いている場合、Authenticationの初期データも投入したいはずです。

簡易的な記事になってしまいますが、以下のクエリを`supabase/seed.sql`の冒頭に記載することでメール認証のユーザーのテストデータを作成することができます。

```sql:seed.sql
WITH credentials(id, mail, pass) AS (
  -- 5人のユーザーのUUID、メールアドレス、パスワードをここに記載します。
  SELECT * FROM (VALUES 
    ('123e4567-e89b-12d3-a456-426614174000', 'user1@example.com', 'password1'), 
    ('123e4567-e89b-12d3-a456-426614174001', 'user2@example.com', 'password2'), 
    ('123e4567-e89b-12d3-a456-426614174002', 'user3@example.com', 'password3'), 
    ('123e4567-e89b-12d3-a456-426614174003', 'user4@example.com', 'password4'), 
    ('123e4567-e89b-12d3-a456-426614174004', 'user5@example.com', 'password5')
  ) AS users(id, mail, pass)
),
create_user AS (
  INSERT INTO auth.users (id, instance_id, ROLE, aud, email, raw_app_meta_data, raw_user_meta_data, is_super_admin, encrypted_password, created_at, updated_at, last_sign_in_at, email_confirmed_at, confirmation_sent_at, confirmation_token, recovery_token, email_change_token_new, email_change)
    SELECT id::uuid, '00000000-0000-0000-0000-000000000000', 'authenticated', 'authenticated', mail, '{"provider":"email","providers":["email"]}', '{}', FALSE, crypt(pass, gen_salt('bf')), NOW(), NOW(), NOW(), NOW(), NOW(), '', '', '', '' FROM credentials
  RETURNING id
)
INSERT INTO auth.identities (id, provider_id, user_id, identity_data, provider, last_sign_in_at, created_at, updated_at)
  SELECT gen_random_uuid(), id, id, json_build_object('sub', id), 'email', NOW(), NOW(), NOW() FROM create_user;
```
諸々カスタムしたい場合は、こちらの箇所を修正してください。

```sql
WITH credentials(id, mail, pass) AS (
  -- 5人のユーザーのUUID、メールアドレス、パスワードをここに記載します。
  SELECT * FROM (VALUES 
    ('123e4567-e89b-12d3-a456-426614174000', 'user1@example.com', 'password1'), 
    ('123e4567-e89b-12d3-a456-426614174001', 'user2@example.com', 'password2'), 
    ('123e4567-e89b-12d3-a456-426614174002', 'user3@example.com', 'password3'), 
    ('123e4567-e89b-12d3-a456-426614174003', 'user4@example.com', 'password4'), 
    ('123e4567-e89b-12d3-a456-426614174004', 'user5@example.com', 'password5')
  ) AS users(id, mail, pass)
),
```

左から、UUID型、TEXT型、TEXT型のデータを投入してください。
UUID型は`user-id-1234`のようになんでもいいわけではなく、postgreSQLに準拠した形式で指定してください。

https://www.postgresql.jp/docs/9.4/datatype-uuid.html

面倒であれば`11111111-1111-1111-1111-111111111111`のようにしてもいいですが、検索をするとUUID生成ツールのようなものもあるのでそちらを用いても良いかと思います。

（筆者はChatGPTに作ってもらいました）

https://uuid.doratool.com/#google_vignette

あとはusersテーブルのidなどに上記で指定したUUIDをセットしてseederデータの作成を進めてください。
【Unity】シーン間の値受け渡しにグローバル変数を使うのはやめよう

# グローバル変数による値渡し
Unityでシーン間で値の受け渡しをしたいこと、多いですよね。
例えばゲームシーンからリザルトシーンにスコア情報を渡したりなどが挙げられます。

その時に最も簡便かつ危険な方法が、グローバル変数を用いる手法です。
```c#
public static class GlobalVariables
{
    public static int score;
}

public class GameView : MonoBehaviour
{
    private void GoToResult()
    {
        GlobalVariables.score = 100;
        SceneManager.LoadScene("Result");
    }
}

public class ResultView : MonoBehaviour
{
    private void Start()
    {
        Debug.Log($"Score: {GlobalVariables.score}");
    }
}
```
このような書き方をすると、scoreはどこからでも参照・代入できるようになり、
ゲーム規模が大きく・人数が増えると管理し切れなくなり、とても危険です

ここでいうグローバル変数はどこからでもアクセスできるものであり、シングルトンやPlayerPrefsも同様です。
これらを値渡しに使うのは避けましょう。

# シーン間の依存による値渡し
そこで次に考えるのが、下記のようにシーン間で直接値を渡す方法です。

```c#
public class GameView : MonoBehaviour
{
    private async void GoToResult()
    {
        await SceneManager.LoadSceneAsync("Result");
        var result = GameObject.FindObjectOfType<ResultView>();
        result.Initialize(100);
    }
}

public class ResultView : MonoBehaviour
{
    public void Initialize(int score)
    {
        Debug.Log($"Score: {score}");
    }
}
```
これはグローバル変数による授受と比較すると、かなりマシではあります。

ただし、この手のシーンオブジェクト(GameView, ResultView)は肥大化するものであり、GameViewがResultViewそのものに依存するのには依然として危険性があります。
また、シーン同士が相互参照して結局スパゲティになるリスクもあります。

# シーンマネージャーとパラメータに依存した値渡し
上記を踏まえて、直接シーンに依存するのは避けたいです。

ではどうするかというと、シーンマネージャーという、シーン遷移と値渡しを行うクラスを用意して、そこに代わりに行ってもらいます。
```c#
public static class GameSceneManager
{
    public static async void LoadScene(string sceneName, object parameter)
    {
        await SceneManager.LoadSceneAsync(sceneName);
        var scene = GameObject.FindObjectOfType<SceneBase>();
        scene.Initialize(parameter);
    }
}

public abstract class SceneBase : MonoBehaviour
{
    public virtual void Initialize(object param) { }
}

public class GameView : SceneBase
{
    private async void GoToResult()
    {
        GameSceneManager.LoadScene("Result", 100);
    }
}

public class ResultView : SceneBase
{
    public override void Initialize(object param)
    {
        Debug.Log($"Score: {param as int}");
    }
}
```
このような実装にすることで、GameViewはResultViewのパラメータの形だけ知っておけば良いので、
疎結合な状態で安全にパラメータを渡すことが可能になります。

実際にシーンマネージャーを設計する際には、ジェネリクスを用いたりなど、より安全な構成にするのが良いでしょう。
「認証」についてのわかりやすい記事を読んだついでに「認可」についても整理した

# 認証

わかりやすかった記事はこちらです。

https://eng-blog.iij.ad.jp/archives/24620

## 日本語の「認証」

広辞苑での「認証」に関する記載をもとに、以下の分類が行われており、非常にわかりやすい整理だと感じました。

![スクリーンショット 2024-06-14 23.57.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54467/b676dc5a-424c-ceda-7798-73327e4af511.png)

個人的にはマイナンバーの例から、ネットワークの例を想起しました。

|  | 現実の世界 | ネットワークの世界 |
|:-:|:-:|:-:|
| 公の機関による証明（Certification） | 新潟市民が役所でマイナンバーカードを発行してもらう | ドメインの所有者が認証局（CA）からSSL/TLS証明書を取得する |
| 本人確認（Identity Proofing）  | 新潟市民が北陸銀行でマイナンバーカードを提示することで、（役所が発行した身分証を持つ）本人であることを示す  | ドメインの所有者はWebサイト上でDV証明書が表示されていることによってドメインが認証されていることを示す |

## 英語の「Authentication」

* 「ユーザ認証」 とは、「**ユーザの識別**」と「**身元の検証**」
    * システムを利用しようとしているユーザを、システムに登録済みのユーザかどうか（**ユーザ名**等によって）識別し、ユーザが主張する身元を検証する（**パスワード**等によって）プロセス。

※ 実際はユーザ認証のプロセスに「ユーザの有効／無効状態の確認」、「検証に成功した場合の身元の保証（アクセストークンの発行等）」なども含まれる。

### 認証器

身元の検証に必要なデータを保管・生成するためにユーザが所持している具体的な手段。
例えば、パスワード、PINコード、指紋、スマートカード、USBトークン等。

### 多要素認証と単要素認証

1. 記憶情報（Something you know）: パスワード、PINコード等
1. 所持情報（Something you have）：USBトークン、スマートカード等
1. 生体情報（Something you are）：指紋、顔等

※ 「2段階認証」は（おそらく）日本でのみ使われる方言
※ 同分類の認証要素を2つ使う場合は単要素認証となり、セキュリティ強度的にはあまり意味がない

# 認可

ここからはついでで、『「認証」を整理する』記事と同様のフォーマットで、ChatGPTに厳密な定義を求めたところ、以下のリンクを参照して作成してくれたものです。

* [Guide to Attribute Based Access Control (ABAC) Definition and Considerations | NIST​ (NIST)​](https://csrc.nist.gov/publications/detail/sp/800-162/final)
* [Authorization - Glossary | CSRC​ (NIST Computer Security Resource Center)​](https://csrc.nist.gov/glossary/term/authorization)
* [SP 800-37 Rev. 2, Risk Management Framework for Information Systems and Organizations​ (NIST Computer Security Resource Center)​](https://csrc.nist.gov/publications/detail/sp/800-37/rev-2/final)
* [SP 800-53 Rev. 5, Security and Privacy Controls for Information Systems and Organizations​ (NIST Computer Security Resource Center)](https://csrc.nist.gov/publications/detail/sp/800-53/rev-5/final)

## 日本語の「認可」

> １ 適当と認めて、許可すること。
> ２ 公の機関（行政庁）が第三者の行為を補充して、その法律上の効力を完成させる行政行為。「保育所の設立を—する」→許可
> 
> デジタル大辞泉 - 小学館
https://www.weblio.jp/content/%E8%AA%8D%E5%8F%AF


| 「認可」の用法 | 対応する英語 | 具体例 |
|:-:|:-:|:-:|
| 公の機関による許可 | Approval | 建築許可、営業許可など |
| 特定の行為に対する権限の付与 | Authorization | ファイルアクセス権限の付与 |
| リソースへのアクセス制御  | Access Control  |アクセス制御リスト（ACL）による制限  |

そのほかの例:
* プロジェクトの予算を上司が承認する（Approval）
* APIキーを使って、特定のユーザだけがAPIを利用できるように設定する​（Authorization）
* 会社のオフィスに入るために社員証をスキャンし、許可された区域にのみアクセスできる（Access Control）

## 英語の「Authorization」

* 「ユーザ認可」とは、「**アクセス権の確認**」と「**リソースの制限**」
    * システムを利用しようとしているユーザが、システム内の特定のリソースや機能にアクセスする権限を持っているかどうかを確認し、適切なアクセス制限を適用するプロセス。

※ 実際はユーザ認可のプロセスに「アクセスレベルの適用」、「アクセス記録の保持」、「ポリシーの適用」なども含まれる。
※ ISO/IEC 27001:2013 では、認可は情報セキュリティ管理システムの一部として、組織の情報資産を保護するためのアクセス制御の手段と定義されている。

### 認可モデル

#### ロールベースアクセス制御（RBAC: Role-Based Access Control）

ユーザは特定のロール（役割）に割り当てられ、そのロールに基づいてアクセス権が決定される。
例えば、管理者、編集者、閲覧者等。

#### 属性ベースアクセス制御（ABAC: Attribute-Based Access Control）

ユーザやリソースの属性に基づいてアクセス権が決定される。
例えば、ユーザの役職、部門、プロジェクトの状態等。

# 感想

簡単ににまとめると、認証はWebサイトのログイン、認可はAWSやGoogle CloudのIAMで行われることのイメージそのものですね。
Java Silver SE11に92%で合格した学習方法

**※受験日 2024年 6月**
## 筆者のレベル
大学生
プログラミング経験 : JavaScript、Cなど
オブジェクト指向の学習 : 未学習

## まずは、結果から
正答率 92%で合格しました。落としてしまった部分は、隠蔽、モジュールやパッケージの理解が足りていませんでした。オブジェクト指向に学習が行きがちですが、意外とモジュールとパッケージのことも問題として出てきます。
![スクリーンショット 2024-12-06 13.12.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3788663/eabce0fe-d140-bba7-7d16-105b9019350d.png)


## 主な学習方法　学習時間 : 52時間
極論、黒本を完璧にすれば合格できると思います。

私は**黒本を3周**しました。その内訳は

①黒本を1周解く。(最初は結構難しいので、さらっと解いて、間違えた問題については **なぜ** そうなるのかを深く考える)
                    ↓
②間違えた問題のみをもう一周(間違えた問題は再度復習)
                    ↓
③最後に、全ての問題を解く
                    ↓
間違えた問題のみをテスト直前に復習

:::message
黒本の1周目は全く分からないので、正答率が低くてもめげないでください！！私も、1周目は3割程度でした。
:::
## コツ
**暗記して乗り越えることをしない**
→暗記したとしても、問題が変われば解けなくなるので、暗記して乗り越えることは絶対にしないことをおすすめします
**すべての挙動に「なぜ？」を意識**
->なぜここはprivateではだめなのかなど一つ一つの事に対して、時間はかかってしまいますが、「なぜ？」と疑問を持つことで最終的に色々なことが繋がってきます。
**目に見える形で自分の苦手分野、学習率を視覚化**
→モチベにもつながりますし、受験前に何を復習しておくべきか簡単にわかる。
![スクリーンショット 2024-12-06 13.04.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3788663/6ead1528-fa76-0507-dd02-5a65a1926bc0.png)

# 使用した参考書
https://www.sbcr.jp/product/4815606015/

あまり覚えていませんが、ラムダ式などの深い内容はあまり載っていなかったような....


https://book.impress.co.jp/books/1118101186

ほとんど、この黒本を使って勉強しました。大体のオブジェクト指向の概念、考え方が網羅されており、わからない問題に対して、「なぜ？」と全てに疑問を持つことでより深い理解になると感じました。挙動を実際に自分で動かしてみたり、技術系ブログなどで調べるととても効率よく勉強ができます。


# 最後に
最短で合格するなら、黒本だけで挑むことは可能だと思います。しかし、黒本だけでは1周目は全く理解することができないので何か入門書でさらっと学習してから黒本に取り組むとまだマシかと思います。
GitHubActionsまとめ

# 概要
・GitHubリポジトリに対して何らかの操作が行われた時に、予め設定しておいたワークフローが自動で実行される
・ワークフローはymlで記述、.github/workflows配下に置く
# トリガー
ワークフローの実行タイミングを設定
```yml
on:
　# リポジトリにプッシュされた際にワークフローが実行される
  push:
```
[トリガー一覧](https://docs.github.com/ja/actions/using-workflows/events-that-trigger-workflows)
# permissions
ワークフローごとに権限を設定
```yml
permissions:
  # Issueへの書き込み/読み込みを許可
  issues: write
```
[権限一覧](https://docs.github.com/ja/actions/using-jobs/assigning-permissions-to-jobs)
# Secrets、Valiables
リポジトリに環境変数を設定し、ワークフロー内で読み込む
![variables.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368850/5c5f247c-ecc8-2ab4-9f1e-bedfafada703.png)

```yml
- run:
    echo "${{ vars.TEST_ENV }}"
```
# Environments
各環境ごとに環境変数などを個別に設定可能
![environments.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368850/b9a91779-1a8d-3786-9a57-425c7e5be4dc.png)
![environments_secrets.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368850/109b4e6f-c406-11bb-a309-4319ab579109.png)

```yml
jobs:
  test:
    runs-on: ubuntu-latest
    environment:
      name: staging

    steps:
      - run:
          echo "${{ secrets.TEST }}"
```
# 再利用可能ワークフロー
別ワークフローを呼び出す
```yml:call.yml(呼び出し元)
jobs:
  call_workflow:
    uses: ./.github/workflows/called.yml
    with:
      environment_name: staging
    # inheritにすると、呼び出し元のsecretsがそのまま呼び出し先で使用できる
    secrets: inherit
```
```yml:called.yml(呼び出し先)
name: Called

on:
  workflow_call:
    inputs:
      environment_name: 
        description: Environment Name
        required: true
        type: string

jobs:
  called:
    runs-on: ubuntu-latest
    environment:
      name: ${{ inputs.environment_name }}

    steps:
      - run:
          echo "${{ secrets.TEST }}"
```
# Composite Actions
複数stepを1つのワークフローにまとめて再利用できる
```yml:call.yml(呼び出し元)
steps:
  # 呼び出す前にチェックアウトをする必要がある
  - uses: actions/checkout@v4
  - uses: ./.github/actions/test
      with:
        TEST: hoge
```
```yml:.github/actions/test/action.yml(呼び出し先
name: Composite Actio
description: TEST

inputs:
  TEST: 
    required: true
    description: Sample
# outputsも可能

runs:
  using: composite
  steps:
    - run: echo "${{ inputs.TEST }}"
      # シェルの種類をstepごとに記述する必要がある
      shell: bash

```
# self-hosted-runner
独自のGitHubActions実行環境を構築できる
`Setting` -> `Actions` -> `Runners`の`New self-hosted runner`を選択すると、self-hosted-runner設定に必要なコマンドが表示される
![self-hosted-runner.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368850/b2309803-8120-3ab7-6815-d3044f20bf56.png)
![self-hosted-runner2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368850/74f21412-44c6-1f0a-0b67-9275420b4fe3.png)

```yml
jobs:
  test:
    # runs-onに"self-hosted"を選択
    runs-on: self-hosted
```
# artifacts
ジョブの実行結果をzipファイルとしてGitHub上に保存し、他ジョブや他ワークフローに共有できる
```yml
- name: Upload artifacts
  uses: actions/upload-artifact@v4
  with:
    name: job-result
    path: result/
```
ダウンロードには`actions/download-artifact`を使用
```yml
- name: Download artifact
  uses: actions/download-artifact@v4
  with:
    name: job-result
```
# Docker
self-hosted-runnerの場合、Dockerコンテナ内でワークフローを実行させることができる。
```yml
jobs:
  test:
    container:
      image: ubuntu:latest
```
supabaseでローカル作業をリモートに反映するコマンドのメモ

# はじめに

Flutter × Supabaseのアプリ開発を進める中で、なぜか微妙にローカルで作業したものをリモートに反映するのに苦戦をしてしまったのでコマンドのメモを残します。


## Supabaseドキュメント

今回このページを見つけるのに苦労をしてしまいました。

https://supabase.com/docs/reference/cli/supabase-db-diff

結論から言うと、以下のコマンドで解決です

```bash
# プロジェクトとリンク
supabase link
# ローカルの状態からマイグレーションファイルを生成
supabase db diff --use-migra -f {任意の名前}
# (例） supabase db diff --use-migra -f create_users_table

# リモートに反映する
supabase db push
```

## 忘れてはいけないSupabaseのAI機能

supabaseはプロジェクトのサイドバーの一番下に検索アイコンがあります。
この機能をすっかり忘れていました。

![スクリーンショット 2024-06-15 1.52.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/735195/f5892714-8b88-419f-ebf1-c7cb0309d3f1.png)

まず、手元のターミナルで`supabase db --help`をしたところ以下が表示されました。

```bash
% supabase db --help                               
Manage Postgres databases

Usage:
  supabase db [command]

Available Commands:
  diff        Diffs the local database for schema changes
  dump        Dumps data or schemas from the remote database
  lint        Checks local database for typing error
  pull        Pull schema from the remote database
  push        Push new migrations to the remote database
  reset       Resets the local database to current migrations
  start       Starts local Postgres database

Flags:
  -h, --help   help for db

Global Flags:
      --create-ticket                     create a support ticket for any CLI error
      --debug                             output debug logs to stderr
      --dns-resolver [ native | https ]   lookup domain names using the specified resolver (default native)
      --experimental                      enable experimental features
      --workdir string                    path to a Supabase project directory

Use "supabase db [command] --help" for more information about a command.
```

記載の通り、diffコマンドで実現できそうです。
```
diff        Diffs the local database for schema changes
```

と、そこまでわかったら検索するのですが、Supabaseの検索はAI検索のため類似情報をいくつかピックアップしてくれます。しかし、今回は一番先頭にありました（そこまで見抜かれてる...?）

![スクリーンショット 2024-06-15 1.56.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/735195/0666bdbc-1aad-f184-215e-38f84085380b.png)

そして冒頭のドキュメントに到達できて無事ローカルからリモートへ反映ができました。

https://supabase.com/docs/reference/cli/supabase-db-diff

以上、小ネタの備忘録でした。


## 追記

### ローカルのレコードをsqlとして出力する
```bash
supabase db dump --local --data-only -f seed.sql
```
【PHP】boolean型聖書

<h1>はじめに</h1>
<p>こんにちは、まりあ様です^^</p>
<span>PHP触ってて衝撃的だったことをメモがてら、残します。</span>
<h1>echoでのboolean型出力</h1>
<p>例えば、期待値としてtrue、falseが返却される以下のソースコードがあるとします。</p>

```
$testTrue = true;
$testFalse = false;

echo $testTrue;     // 想定している返却値は、true
echo $testFalse;    // 想定している返却値は、false
```

これを実際に出力すると、、、、

```
1   // true
""  // false
```

となるんです。。。。
もうこれ、JavaScriptとかかじったくらいで、console.logでとりあえずなんでも中身見る人なので、衝撃でした。
なんで、1とか空文字で返ってきてんの？boolean型のはずなのに？とか混乱しちゃいましたｗ

<h1>var_dumpでの出力</h1>
<p>var_dumpで出力するとどうなるか？？？</p>

```
$testTrue = true;
$testFalse = false;

echo $testTrue;     // 想定している返却値は、true
echo $testFalse;    // 想定している返却値は、false
```

これを実際に出力すると、、、、

```
bool(true)    // true
bool(false)   // false
```

var_dumpだと、ちゃんとtrue、falseで返ってくるみたいです！

<h1>まとめ</h1>
<p>超～～ざっくりほんとに自分用メモですが、その日悩んだことが解決したすごい発見だったのでまとめておきました。</p>
これ知らずに、echoで中身確認して、

```
if ($testTrue === 1) {
```

みたいなif文書いて、期待通りの動きをしない、なんてことがありました。
今度から同じ失敗はしないように、アウトプットしておきました。
アメリカ大陸発見した並の衝撃でした。
以上、ほんとの自分用メモ（まとめとかの文章ひどい）でしたが、読んでくださった方はお付き合いありがとうございました！
50代ママの経験めくり   子供の意思を尊重し二人とも慶應合格を勝ち取る

みなさんこんにちは。ママブロガーの礼子です。私には長男と次男の二人の息子がおり、二人とも無事慶應義塾大学に現役合格することができました。受験勉強は本当に大変でしたが、適切な塾選びと何より子供たちの意思を尊重したことが、合格への道を開いてくれたと実感しています。
私が心がけた「子供二人の慶應合格を勝ち取るための塾選び」のポイントを、エピソードを交えつつお話ししますね。
ポイント1. 合格実績と指導の質を両立
塾選びで最も重視したのは、実際の合格実績と教育の質の高さでした。口コミも参考にしましたが、最終的にはデータを分析し、志望校合格に確かな実績のある塾を選びました。
しかし、実績がよくても子供が通いづらい雰囲気や指導方針では意味がありません。二人のタイプや性格に合う、寄り添った指導ができる塾を選ぶことに重きを置きました。
実は長男の時は、勉強が苦手で勉強時間が確保できず、一度はブレークする時期がありました。しかし、当時の塾の先生方が親身になって寄り添い、長男の気持ちに耳を傾けてくれたおかげで、前を向いて乗り越えられたのです。
ポイント2. 費用より質を重視
費用について言えば、家計への影響は避けられませんでした。しかし、「子供の可能性を最大限に引き出す質の高い教育」には、できる限り投資するつもりでした。
結果的に、費用は決して安くはありませんでしたが、子供たちが「分かりやすい」「通いやすい」と感じられる環境で、しっかりとした学習をサポートしてもらえました。
ポイント3. 子供の意思を尊重
一番大切にしたのが、子供の意思の尊重でした。二人とも幼い頃から「慶應に行きたい」と言っていました。勉強に対する熱意は本人から出るものです。
そのため、私は強制や叱責はせず、あくまでも子供たちの意思を尊重し、それを側面から最大限になる支えるよう心がけました。時には道に迷うことも…が、最終的には二人とも自分の意志で頑張り抜く素晴らしい姿勢を見せてくれました。
このように、子供の意思を尊重しつつ、適切で質の高い塾選びを行えたことが、二人の慶應合格への最短ルートにつながったと実感しています。
受験は厳しい道のりですが、子供一人ひとりにとっての最適な環境を整え、寄り添うことが何より大切だと私は思います。みなさまの参考になれば幸いです。

参考：https://studychain.jp/how-to-choice-school/
IBM Cloudアカウントの作成

## はじめに

IBM Cloud で s390x インスタンスを使いたいなーと思ってアカウント登録すると，クレジットカードの登録？ではねられてしまいます。私は個人アカウントで作成しています。

https://qiita.com/getcreation/items/158f06ca11cb0d12642b

英語のやり取りを頑張ってなんとかなりましたので，この記事はその忘備録です。
Poor English ですが，そこは突っ込まないでください...  
通じればいいと思って適当です。

## IBM Cloud Trust Enablement へメール
登録画面に表示されているエラーコードを控えて，verify@us.ibm.com へメールします。
```
I recently tried to register to IBM Cloud, but unfortunately registration process always fails.
Error code is this: REG-PAYGO-UPGRADE-XXXXXXXX

How can I complete this process?
```

## IBM Cloud Trust Enablement から電話
メールを読んでくださったようで，私の登録した電話番号に連絡が来ました（番号非通知）。

ただ，日本時間午前2:30で，電話に出たものの寝ぼけていて何も言わずに切ってしまいました。確か Hello! と言ってくれていたような気がしますが，自信がありません。

ここで電話に出て本人確認に応じれば早かったのかもしれません。

## IBM Cloud Trust Enablement からメール・電話（かも）
電話に出れなかったからか，24時間以内で2時間枠を指定してくれ，そうすればアカウント情報を確認するからとのメールが返信で来ました。やさしい...
メールヘッダを見るとタイムゾーンはPDTでした。

自分の都合のいい時間帯を指定してお返事しました。
```
Thank you for your response.
Could you give me a phone call again?

I think there are 17 hour time difference between us (I live in Japan).
I would appreciate it if you could call me today between Xpm and Ypm PDT.
```
その時間には電話はなかったのですが，何度かインドから電話がかかってきました。折り返して通話はできないようです。
電話番号をググると何も出てこないので，もしかするとこれは関係ないのかもしれません。

## IBM Cloud Trust Enablement からメール
電話来ないよーとメールをしてみたのですが，インドからの電話（出れず）がかかってくるのみでした。
そうこうしていると，アカウント閉鎖になりそうなのでその前に本人確認書類と詳細を送れとメールが来ました。

本人確認書類については以下のように指定されました。PDF/JPEGで送る必要があるようです：
* two forms of government-issued photo identification (i.e. Driver's License, Passport, etc.)
* Be sure to scan the front and back of the document

詳細については以下のように指定されました：
* The billing address of the payment method on the IBM Cloud account 
* The billing phone number of the payment method on the IBM Cloud account 
* Business name and phone number (if applicable) 
* The URL for your website (if applicable) 
* A contact phone number where you can be reached for additional information 
* Your reasons for using IBM Cloud 
 
以下のように返信しました。運転免許証の両面とパスポートの顔写真面を写真にとってJPEGで添付します。
```
Thank you for your reply. 

Here is my information:
> The billing address of the payment method on the IBM Cloud account 
X-Y, Z-.... (私の住所)

> The billing phone number of the payment method on the IBM Cloud account 
+81 80....(私の携帯)

> Business name and phone number (if applicable) 
> The URL for your website (if applicable) 
These are not applicable.

> A contact phone number where you can be reached for additional information 
+81 80....(私の携帯)

> Your reasons for using IBM Cloud 
I want to study XYZ...
```

## ログイン成功とその後
次の日にログインしてみると成功しました。メールでの返信は特にありません。
クレジットカードの情報は消えていたので，再登録するとAccount Type が Pay-As-You-Go (Pending) になり200USDのクレジットが付与されます。

一度ログインできると，チャットでagentと入力すれば有人のチャットサポートが受けられます。Pay-As-You-Go の Pending を外すためにどうしたらいいか尋ねると，クレジットカードの情報が承認されるまで最低1日は待ってくれとのことでした。

s390xインスタンスの作成もちゃんとできるようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738861/cf419f4b-65cd-59bd-7096-11e8c58dec5c.png)

## おわりに
ログインできるまで合計で5日程度かかりあきらめようかと思いましたが，個人アカウントを無事作ることができました。
IBM Cloud Trust Enablement のみなさまとチャットでサポートしてくださったエージェントの方，ありがとうございました。
Laravelでの論理削除に関するガイド

## 論理削除の概念
論理削除は、データベースのレコードを物理的に削除する代わりに、削除されたことを示すフラグ（通常は`deleted_at`カラム）を設定する方法です
コレにより、データは実際にはデータベースに残り続けますが、アプリケーション上では削除されたものとして扱われます

## メリット

1. データの復元が容易
    - 誤って削除したデータを復元するのが簡単です
1. 履歴管理
    - 削除の履歴を追跡でき、監査やログ管理が容易になります
1. 参照整合性の維持
    - 削除されたレコードを参照しようとしても有効なデータとして参照できる
    - データを完全に削除（物理削除）した場合は参照できなくなってしまいます


## デメリット

1. ストレージ増加
    - 完全にデータを削除しないため、ストレージが増加します 
1. クエリの複雑化
    - 削除されたレコードを無視するための条件を追加する必要があります
1. パフォーマンスの低下
    - 大量の論理削除データがあると、パフォーマンスが低下する可能性があります

## 論理削除を使う場面
- データの復元が必要な場合
- 監査や履歴管理が必要な場合
- 参照整合性を維持する必要がある場合（論理削除済みデータも取得したい場合）

## 論理削除を使う場面において実際に使う場合と使わない場合の違い

論理削除を使う場合
```php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Post extends Model
{
    use SoftDeletes;

    protected $dates = ['deleted_at'];
}

// データの削除
$post->delete();

// 削除されたデータの取得
$deletedPosts = Post::onlyTrashed()->get();

// データの復元
$post->restore();
```

論理削除を使わない場合

```php
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    // ソフトデリートなし
}

// データの削除
$post->delete();

// 削除されたデータは取得できない
$deletedPosts = Post::whereNotNull('deleted_at')->get(); // 手動で管理する必要がある
```

## テーブルに論理削除を定義し、Laravelで設定する方法
（前項と一部重複する部分があります）
まず、テーブルに`deleted_at`カラムを追加する必要があります

### マイグレーションファイルの作成
```php
Schema::table('posts', function (Blueprint $table) {
    $table->softDeletes();
});
```

### モデルでの設定
```php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Post extends Model
{
    use SoftDeletes;

    protected $dates = ['deleted_at'];
}
```

## カスタムカラムを使った論理削除
`deleted_at`以外のカラムを使って論理削除を実装することもできます
例えば、`deleted`と言うカラム使用します

### マイグレーションファイルでのテーブル定義
```php
Schema::table('posts', function (Blueprint $table) {
    $table->timestamp('deleted')->nullable();
});
```

### モデルでの設定
カスタムカラムを使用するためには、`SoftDeletes`トレイトをカスタマイズします

```php
<?php

namespace App;

use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    // 論理削除をON.
    use SoftDeletes;

    // 論理削除に使うカラム名を指定（ここでは「deleted」）
    const DELETED_AT = 'deleted';

    // 日付として扱うカラムを定義（ここに論理削除に使うカラムも指定する）
    protected $dates = ['deleted'];
}
```

## GoodパターンとNGパターン

### Goodパターン
LarravelのEloquentソフトデリート機能を利用する
```php
$post->delete();
```

### NGパターン
#### 生SQLで論理削除を行う
生SQLクエリを直接使用して論理削除を実行することは避けるべきです。これにより、Eloquentの便利な機能（スコープ、リレーション管理、イベント）が使えなくなり、メンテナンス性が低下します

```php
DB::statement("UPDATE posts SET is_deleted = 1 WHERE id = ?", [$postId]);
```

理由:
- Eloquentのソフトデリート機能を無視している
- クエリ実行が直接的で、コードの可読性が低い
- Eloquentイベント（例えば、削除時イベント）がトリガーされない

## 実際の現場ではどのような場面で使っているのか
- ユーザーデータの削除
    - ユーザーがアカウントを削除した場合でも、後で復元できるように論理削除を使用します
- 製品データの管理
    - 製品が廃止された場合でも、過去のデータを保持しておくために論理削除を使用します

## まとめ
論理削除を使うことで、削除されたレコードを他のレコードが参照している場合でも、
そのレコードの情報を保持し続けることができます
実はこのケースは前述が代表例であって結構あるんですよね

論理削除を使うことで、データベース内の参照整合性を維持し、削除されたデータの復元や履歴の追跡が容易になります
テーブル設計に合わせて設定していきましょう！
SwiftUI 画面遷移(NextView)の課題について

画面遷移にnextViewという変数を用意するパターンはよくある。
nextViewが悪さをしたので備忘録として残そうと考えた。

今回の課題は特定的であり、汎用性の高いものではない。

遷移画面が増えてきた時に、毎回navigationDestinationのModifierとViewで使用する変数（`@State`と`@Binding`の2つ以上）を用意するのは、冗長なのではないだろうか。

という考えに思い至り、簡単になるような処理を実装してみた。

**冗長だという考えに至った状態**
```swift
// View
}
 .navigationDestination(isPresented: $showExhibitView) {
    ExhibitView(showExhibitView: $showExhibitView)
        .navigationTitle("")
        .navigationBarBackButtonHidden(true)
}
.navigationDestination(isPresented: $showArtDetailView) {
    if let artInfo = selectedArtInfo {
        ArtDetailView(showArtDetailView: $showArtDetailView, artInfo: artInfo)
            .navigationTitle("")
            .navigationBarBackButtonHidden(true)
    }
}
```

# 遷移するViewを変数で保持する

以下のように、遷移するViewを変数(transitionNextView)で保持してnavigationDestinationに渡すという処理である。

```swift
//Viewの外
@State var transitionNextView: AnyView?


// View
}
.navigationDestination(isPresented: $showNextView) {
    if let nextView = transitionNextView {
        nextView
            .navigationTitle("")
            .navigationBarBackButtonHidden(true)
    }
}
```


これで画面遷移が必要になった際、簡単に画面遷移できると考えた…
簡単になったとは、表示のためにnavigationDestinationや変数を用意する必要がなくなった状態のことを指している。

しかし、動作を確認してみると、ある課題が発生した。



# 画面遷移(NextView)の課題について
課題とは、画面遷移の速度が遅くなったことである。
ほぼノータイムだった画面遷移が0.7秒かかるようになってしまった。
許容することができそうな数値ではあるが、元に戻すことにした。

# 経緯
### そもそもなぜ一つのViewで、navigationDestinationを複数用意しているのか?

navigationDestinationのmodifireを付与する場所を間違えてしまうと、デバッグ時に以下のようにエラーログがXcode上で表示される。
ログが表示されるだけで動作はするが、審査でリジェクトの対象にもなりかねないため修正したいところである。

:::note alert
Do not put a navigation destination modifier inside a "lazy” container, like `List` or `LazyVStack`. These containers create child views only when needed to render on screen. Add the navigation destination modifier outside these containers so that the navigation stack can always see the destination.
:::

`the navigation stack can always see the destination.`とあるように、
NavigationStackが、遷移先のViewを監視できている状態が望ましいようだ。
そのため、NavigationStackを定義しているView内でnavigationDestinationを付与する必要がある。
結果的に、navigationDestinationを同じ場所で複数用意することになった。

```Swift
NavigationStack {
    VStack {
       
    }
    .navigationDestination(isPresented: $showView) {
      // 表示するView
    }
}
```
いまさらdockerを始めてみる➁

# 前回まで
installerをダウンロードしてdockerをinstall。dockerコマンドが有効になり、DockerDesktopというアプリケーションが使用できるように。途中BIOSのSVMモード?に起因するエラーに阻まれるものも、何とか解消し、GettingStartedの1ページ目を完了

# dockerで展開されているAppを編集する
2ページ目ではアプリケーションの開発を体験できるようです。
1. zipファイルをダウンロード。gitコマンドでもできるようですが、初心者なので使いません(使えません)![1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269297/9d484e75-08b9-325b-c04b-26575f131e81.png)
1. 中身はこのような内容![2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269297/7dcac7d8-22ab-d946-1aed-31ad899d2a0c.png)
1. コマンドプロンプトを起動→先述のディレクトリにcd→'docker compose watch'
1. 最終的にこのような状態に。enterキーをおしても反応なし。docker上のサーバーが起動した？![3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269297/dc7da0c7-d7f3-7782-db5f-56637dd28349.png)
1. localhostにアクセス。成功っぽい![4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269297/6ec41818-01f4-cf0f-4bc9-198c384faa82.png)
1. このページは, React/Node/MySQL/php/Trafikproxy の技術スタックで構成されているけど、君はそれらをconfigureしなくてもいいんだよ！とのこと。なるほど、確かにすごいかも
1. DockerDesktopを起動。backend-1をクリック![5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269297/d6888f33-f293-a523-65d5-5d4a7b9091d1.png)
1. 'backend/src/routes/getGreeting.js'を編集せよとのことだが、一向に見つからない。10分ぐらいさまよってやっと発見(/usr/local/app)![6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269297/88706440-5b39-8a80-16be-ade9167b179c.png)
1. getGreeting.jsにカーソルを合わせ、Open file editorをクリック
1. const GREETINGをよしなに編集
1. ブラウザにあるTodoAppを再読み込みすると、内容が変更されている![8.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269297/ffd33c03-a08f-0976-bf39-833fff82fdf1.png)
1. getting-started-todo-app-main-clientを起動。/usr/local/app/src/components/AddnewItemForm.jsxを編集。FormContorolタグ？のplaceholder属性？を変更
1. 同様に...src/index.scssを編集。bodyのbackground-coloを変更
1. これにて終了。いいんやない![9.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269297/5867526f-6089-a146-e7cc-8d30253f31b2.png)

# Recap
- zero installation effort でコンテナイズドされた環境をゲット！NodeもMySQLおよびdependenciesを自分のパスコン本体にinsatllするひつようがない
- すぐに変更結果が反映されたのは以下の理由
    - 各コンテナのランニングプロセスは監視されている
    - ファイルらはコンテナイズドされた環境で共有されている

とのこと。よくわかんないけど、backedn-1とかmysql-1はコンテナで、todo-app-mainは複数のコンテナをまとめたコンテナってことかな。まあ時がたてばわかるだろう。今日はここまで
【Rails】ログローテーションをする方法

`config/environments/production.rb`などの設定ファルに以下の設定を追記するとログローテーションができます。

```rb
# ファイルサイズ基準
config.logger = Logger.new("log/production.log", 5, 10 * 1024 * 1024)

# 日数基準
config.logger = Logger.new("log/production.log", 'daily')  # weeklyなども可

```

## その他の方法

Linuxコマンドの `logrotate` コマンドを使用してもログローテーションが可能です。

https://hackers-high.com/linux/man-jp-logrotate/
技術書典で話題になった本の感想

私のXのTLを見ていると「技術書典」なるものが存在し、さらにとある本がちょっとした話題になっていました。
ちょうど、Quitaの投稿イベントに参加しているのもあり、その本を買い、学んだことをまとめてみます！

# 技術書典とは？
めちゃめちゃ簡単に言うと技術書版のコミケです！
[技術書典](https://techbookfest.org/)
2016年から開催されているようで様々な技術についての話を安価で読むことができます！
最近Zennなどでも本が読めるようになりましたがこういう技術書が安価で簡単に手に入る、いい時代になりましたね...
しかもオンラインで買うこともでき、電子書籍としてすぐに読むことができます！
読みたいものを読みたいとき読める...しかも送料無料でした！
今回はその中から「読み手につたわる文章テクニカルライティング」（mochikoAsTech 著）という本を購入しましたのでネタバレにならない範囲で紹介します。

# 感想と学んだこと

- ターゲットを決めることの重要性

ライティング本にもよく書かれている「ターゲットを決めること」について、本書では特に技術関係のライティングにおいて、より明確かつ詳細に書くべきと強調されています。私も多くの記事や専門書を読んだ経験から、この点には非常に共感しました。
特に、自分の読んでほしい層以外の人に対しては、早々に「あなたはターゲットではないですよ！」と伝えることが重要です。これにより、読者にとって無駄な時間を使わずに済み、ライターとしても効果的なメッセージを届けることができます。
私も多くのプログラミング本や記事を読む際に、内容が抽象的で自分にとって必要な情報かどうかがすぐに判断できないことが多々ありました。そのため、この本の内容には非常に共感を覚えました。

- 読み手のゴールを想定する

読者が読み終えた際、どのような状態になっているのかを具体的に明示することが大事とありました。これはこれから文章を読む読者のモチベーションにもつながってくる内容であり、ただ単に読んだだけ...にならなず読者が本の内容を理解することにつながる重要な部分でした。

- 早い段階でAIを活用する

初案をAIに考えてもらうことで各スピードを上げることも大事だと書かれていました。
私の知人にもライターの方がいますが、その方もAIを活用したことがあると話されていたため、一から自分で文章を考えるよりもAIと対話しながら書くことでより良いものができるのかもしれません。

# 最後に
技術書典なる素晴らしいものがあると知れたと同時に、自分が記事を書く上でのハードルが少し下がったかのように感じ取れました。特に初めて文章を書いていたということもあり、どう書けばいいのか四苦八苦していましたが、この本を読み学んだことを少しずつ実践していこうと思います。
Gitのアップストリームとは

Gitを使ってチームで開発を行う際、重要な概念の一つが「アップストリーム」です。今回は、初心者が理解しやすいように、アップストリームの役割と使い方を、具体的な例を用いて説明します。

# アップストリームとは何か

アップストリームとは、自分のローカルリポジトリと最新の状態を同期するための基準となるリモートブランチのことです。

ローカルリポジトリのブランチでアップストリーム設定することで、リモートリポジトリの最新の変更を取り込んでローカルリポジトリを最新の状態に保つことができます。
例えば、あるプロジェクトでGitを利用して開発を行っている場合、origin/developというリモートブランチがアップストリームとして設定されていることが多いです。これは、originというリモートリポジトリにあるdevelopブランチを基準にするということです。

# なぜアップストリームを使うか

### 独立したブランチ作業

複数のブランチで作業する際、アップストリームを設定することで、各ブランチが他のブランチの影響を受けずに、必要なときだけリモートリポジトリの最新情報を取り込むことができます。これにより、ブランチごとに異なる機能開発や修正作業が効率的に行えます。

### マージの基準

アップストリームを基準にすることで、マージ時の衝突を最小限に抑えることができます。例えば、リモートブランチに他のメンバーの変更があった場合、それをアップストリームから取り込んでおけば、マージする際に衝突が発生しにくくなります。
本番環境のルーティングを破壊し、決済できなくしてみた....

# 経緯
このシステムではお客様に実際にクレジットカードでチケットを購入し、
そのチケットをもとにシステムの中の商品を購入するというシステムになっています。
お店ごとにサーバーは立っており、gitリポジトリも分かれているため、
何か機能を追加するという場合でも一箇所ソースを変更という形ではなく、
全てのお店に同じソースコードを適応するという形になっていました。
ある日メンテナンス機能を追加してほしいと要望があり、
自分が実装担当になり、張り切って実装しました。
テストはパスしたため後は全てのサーバーに同じコードを適応していくのみとなりました。
IT経験3ヶ月、順風満帆に過ごしていた自分に初めて試練が訪れました。

# 事件発生
自分の方で全てのサーバーに適応し、本番環境にデプロイしました。
メンテナンス機能の実装は楽しかったなと思い、1週間が経った頃、
お客様より怒りの電話が来ました。
「1つのサイトで決済できなくなっている！」
自分は焦りました。
「なぜ他のサイトは大丈夫で、このサイトだけなんだ？」
「直近で変更を加えたのは自分だけだから、原因は自分にある...」
「お客様は凄く怒っていて、賠償等も話に出ている」

初めてビジネスに打撃を与えることの影響というものにびっくりし頭が真っ白になりました。

# なぜ発生したのか
答えはシンプルで、
その1つのシステムだけは他のシステムのルーティングが少し異なっているのが原因でした。
そのシステムだけ独自の機能で決済が行われていたのですが、
自分はそこを失念し、メンテナンス機能に関わる部分を全てコピー＆ペーストで済ませていました。

# 反省点
隅々まで既存のソースコードを確認せず、
前はこれでうまくいったから今回もきっとうまくいくと勝手に判断してしまいました。
エンジニアはテスト要員としてキャリアを始めることが多いというのも、
テストには開発する上で大切なものがたくさん詰まっているからです。
自分はこの失敗から改めてテストの重要性に気づき、
実装する上でも単なる機能実装だけでなく、
テスト工程のことまで考え実装できるようになり、
品質の高いコードを意識して実装するようになりました。

# 最後に
失敗することで学べることはたくさんあります。
デバックログの消し忘れ等のケアレスミスも失敗して怒られることで、
二度と発生させまいと心に誓うものです。
皆さんも失敗を重ねて強いエンジニアになりましょう！
Proxyの有効/無効を切り替えるbatファイル

学校や職場にBYODでPCを持ち込む際、インターネットへの接続にProxyサーバーを経由することがある。
そのような環境と自宅を行き来する度に、Proxyの有効/無効を切り替えなければならず面倒だったためbatファイルを作成した。
デスクトップ上に配置しておけばワンクリックで切り替えることができる。
```bat:proxy_switch.bat
@echo off
set regpath="HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings"

for /f "tokens=3" %%A in ('reg query %regpath% /v "ProxyEnable"') do set enable=%%A

if %enable%==0x0 (
  reg add %regpath% /f /v "ProxyEnable" /t reg_dword /d 1
  echo Turn On Proxy Server
) else (
  reg add %regpath% /f /v "ProxyEnable" /t reg_dword /d 0
  echo Turn Off Proxy Server
)
```
同様のコードを[GitHub](https://github.com/raptech-jp/proxy_switch)にも公開している。
社内勉強会アンチパターン

# 最初に
皆さんは社内で勉強会を開催されたことはありますか？
自分が勤務している会社では週に2回勉強会が開催されており、非常に有意義な勉強会となっております。
では上記の見出しは何かというと、自分が今の会社に入社する前、転職前の会社でのお話です。
ある日PMの方から「勉強会ってよくない？」とこの一言から毎週1回勉強会が開催されることになりました。

# 勉強会の発表者が決まった人になっている
参加人数は20名ほどでしたが、発表者は自分かPMの方の2名のみとなっており、
発表内容に偏りが出てしまいました。
またPMの方が忙しいと開催されないといったデメリットもあるので、
発表者は勉強会に参加する人で順番に回していくのという方法がいいのかもしれません

# 手を動かすのを見ているだけ
勉強会の内容ですが、一つのプロダクトを作ってみようという形式になりました。
その形式はいいと思うのですが、実施内容はPMの方がコーディングするのを、
参加者は見て知識をインプットするという内容になっていました。
一番の問題点が**受動的な勉強会**になってしまうところです。
やはり自分から学びに行こうという姿勢がないと脳への定着率も低いものです。

# そもそも勉強のテーマが興味を持てない内容
発表者を順番で回す形式ではなかったので、PMの方が題材を決めていたのですが、
参加者にアンケートを取って決めたわけではないので、
今学びたい技術を学ぶという題材の決め方になっており、
参加者の中には「それは興味ないよ...」という方もいました。
上述の通り、一つのプロダクトを作るという内容のため、
一週間で終わる内容ではなく最低でも2~3ヶ月かかるため、
興味のない技術だったら終了するまで参加者は減っていく一方でした。

# 伝えたかったこと
自分の身の回りで発生していたことなので、あまり共感を得られる内容ではないのかもしれません。
PMの方もなんとか技術を学べる場を提供したいという思いからなので、
PMの方を悪く思うといったことはなく、自分自身がもっと提案できていたらと反省しております。
参加者全員が主体的に学べる場・雰囲気を作ることができれば有意義な勉強会になると信じています。
せっかく技術が好きでこの仕事に携わっているので、開催するのがワクワクするような勉強会を目指していきたいですね！
ViewBuilderを用いて再利用可能なレイアウトテンプレートを作成する

## 概要

`@ViewBuilder`を用いることで、HeaderがFooterは同じで、メインのコンテンツが違うようなレイアウトテンプレートは以下のように記述できます。

### コード例

```swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        CommonLayout {
            Text("Hello")
            Text("World")
        }
    }
}

struct CommonLayout<Content: View>: View {
    let content: Content
    
    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content()
    }
    
    var body: some View {
        Header()
        
        content
        
        Footer()
    }
}

struct Header: View {
    var body: some View {
        Text("Header")
    }
}

struct Footer: View {
    var body: some View {
        Text("Footer")
    }
}

#Preview {
    ContentView()
}
```
Chainlitをクラウド上で編集・起動してみる

ChainlitをGCP Cloud Shell Editorでプレビューしてみるのが今回やること。

## 準備
### Cloud Shellに入る
Web検索からすぐ入れるページがここ。
ここで「コンソールへ移動」をクリック。
![スクリーンショット 2024-06-14 225804.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68673/5e734e92-db31-3cd0-2d31-9c1ff78555e1.png)

下のような画面が出てくるので、「エディタを開く」を選択。
![スクリーンショット 2024-06-14 225901.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68673/334f9fdc-7cd9-35ab-fef3-dc19d1fe182b.png)

すると、このような画面が出てきて、しばし待つ。
![スクリーンショット 2024-06-14 225908.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68673/49d9cbe6-281f-0cd4-95c7-8e4b4b2eb79d.png)

すると、VScodeそっくりの画面が登場する。配色が違うかもしれないが問題ない。
![スクリーンショット 2024-06-14 230101.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68673/74afd9d5-9055-b38b-512a-12dff4aefa33.png)

ちなみに、Cloud Shellはこのように無料。
![スクリーンショット 2024-06-14 225638.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68673/85834022-9f8f-4f0a-6ba2-b4f341e592ed.png)

Cloud Shell Editorも無料だが、さすがに1週間あたりの利用上限がある。
![スクリーンショット 2024-06-14 230348.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68673/33bafa8a-96b5-3c15-23f3-fe87104256d6.png)

## Python環境を用意する
venvで仮想環境を作り、アクティベートする。

```bash
python3 -m venv .venv
source .venv/bin/acivate
```
Chainlitをインストールする。

```bash
pip install chainlit
```

## Chainlitのファイルを作成する
`app.py`を、[公式](https://docs.chainlit.io/get-started/pure-python)からコピペして作成する。


```py
import chainlit as cl


@cl.on_message
async def main(message: cl.Message):
    # Your custom logic goes here...

    # Send a response back to the user
    await cl.Message(
        content=f"Received: {message.content}",
    ).send()

```
↑ 見た感じ、オウム返ししてくれそう。

# 実行とプレビュー
portを指定して、実行。

```
chainlit run app --port 8080
````

そして、プレビューでアプリケーションを見てみる。
![スクリーンショット 2024-06-14 231705.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68673/d81973c0-0b4e-265a-8c09-44b7d2d41c5c.png)

「ポート8080でプレビュー」をクリックすると、新しいタブで開く。

無事、オウム返ししてくれるのがわかる。
![スクリーンショット 2024-06-14 231831.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68673/1281bcba-a406-47a4-324e-9ee438346200.png)

# ここからが始まり
プレビューができれば、これからはチャットボットを作るなり、エージェントを作るなりしていくことになる。

Langchainを使ったり、ベクターストアを使ったり、いろいろやりたいことが広がる。

クラウド上のエディタとプレビューがあれば、Windows環境に悲しまなくて済む。
Play a catch? はなぜバズったのか

# 事件はおきた

https://x.com/octo_rain_game/status/1776390746188488858

[すいでんさん](https://twitter.com/suidensuiden)と作成したゲーム「play a catch?」を紹介していただいたツイートをきっかけにアクセス数がのびた！

いままで様々なアプリやゲームを作成してきたが、ここまでの反響は初めてだった。
この記事ではどうしてこのゲームがバズったのかその理由を考察していく。

# なぜバズったのか

## 1. シンプルなルール

このゲームは非常にシンプルなルールで遊べる。
よくサービスは一言で説明できるものが成功すると言われるが、このゲームはその典型例だと思う。
「知らない人とキャッチボールをする」でこのゲームの全てを説明できる。

## 2. 差別化ができている

このゲームはネットワークを通じて他のプレイヤーとキャッチボールができる。
UnityRoomというサイトで公開している。

https://unityroom.com/games/playacatch

このサイトでは多くのゲームが投稿されているが多くは一人で遊ぶものである。
しかしネットワークでリアルタイムに遊べることを強みにしているので、他のゲームと差別化ができていると考える。

## 3. 見た目が良い

この体験を通じて一番痛感したことがある。
良いサービスは見た目が良いということだ。
そりゃ当たり前だろと思うかもしれないが、UIは馬鹿にできない。
どんなに素晴らしい機能を持っていても、ユーザーが使いたいと思えるような見た目でなければ使ってもらえない。
最初に見える部分はできるだけ綺麗にすることが大事だと感じた。

# さいごに

このゲームがバズった理由はシンプルなルール、差別化、見た目の良さだと考える。
私は今までバックエンドを中心に開発してきて、見た目にこだわることが少なかったが、この経験を通じて見た目の重要性を再認識した。
ここまでこの作品を育てられたのは[すいでんさん](https://twitter.com/suidensuiden)のおかげだと考えている。
自分はフロントエンドが苦手なので、このような作品を作れる人と協力して作品を作っていきたい。

ここまで読んでいただきありがとうございました！