OmniAICreator/Qiita-1.07M huggingface dataset:
[IaCを学ぼう第1回] tenvを使ってTerraformとOpenTofuを両方インストールする

## これは何？
IaCを学ぼうの第1回、tenvというツールを解説しながらTerraformとOpenTofuの実行環境を整える記事です。

## tenvとは？
以下のツールのインストールや利用バージョン管理を行えるツールです。

- OpenTofu
- Terraform
- Terragrunt
- Atoms

Terraformに関連したツールの管理に特化したツールという理解で一旦良いと思います。
もう少し記載しておくと、tfenvとほぼ同等の機能を備えています。
以前はtfenvを利用してTerraformのインストールや利用バージョン管理を行なっている方が結構多かった印象ですが
2025/01現在、tfenvのGitHubのmasterブランチを見ると長い期間更新されていません。

https://github.com/tfutils/tfenv

Terraformはもちろん、フォークされたOpenTofuやTerragrunt等の派生ツールが多数生まれているため
そういうツールを一括管理したいとか併用したいとかの要望がほぼ確実に出てくるのと

tfenvはBashスクリプトで書かれているのでWindowsで動作させるのはなかなかお辛かったのですが
tenvはGolangで書かれているためWindows含めて動作するようになりました。

上記背景から基本はtenvを使う形の方が良いのでは？と思っています。

## tenvのインストール

https://tofuutils.github.io/tenv/#:~:text=Linux%3A%20dkpg-,Installation,-Automatic%20Installation

公式のインストール方法を参照しましょう。
筆者はMacOSなので大人しくbrewを実行します。

```Bash
$ brew install tenv
```

## tenvの基本的な使い方

### 書式

```
$ tenv [ツール名] [コマンド]
```

### ツール名
書式の中のツール名はそれぞれ以下のようになります。

- OpenTofu → tofu
- Terraform → tf
- Terragrunt → tg
- Atoms → at

### インストール可能なバージョンの確認

このコマンドの実行結果からどのバージョンをインストールできるか(するか)を選択します

OpenTofu

```
$ tenv tofu list-remote
```

Terraform

```
$ tenv tf list-remote
```

### 特定バージョンのインストール

OpenTofu

```
$ tenv tofu install 1.9.0
$ tofu version
OpenTofu v1.9.0
on darwin_arm64
```

Terraform

```
$ tenv tf install 1.10.4
$terraform version
Terraform v1.10.4
on darwin_arm64
```

### インストール済みバージョンの確認

OpenTofu

```
$ tenv tofu list
  1.8.8 (used 2025-01-10)
  1.9.0 (used 2025-01-17)
```


Terraform

```
$ tenv tf list
  1.10.3 (used 2025-01-10)
  1.10.4 (used 2025-01-17)
```

### バージョン切り替え

OpenTofu

```
$ tenv tofu use 1.8.8
Written 1.8.8 in /Users/user/.tenv/OpenTofu/version

$ tofu version
OpenTofu v1.8.8
on darwin_arm64
```


Terraform

```
$ tenv tf use 1.10.3
Written 1.10.3 in /Users/user/.tenv/Terraform/version

$ terraform version
Terraform v1.10.3
on darwin_arm64

Your version of Terraform is out of date! The latest version
is 1.10.4. You can update by downloading from https://www.terraform.io/downloads.html
```

## まとめ
tenvを有効活用していきましょう。
次回はTerraform(OpenTofu)の基本的なコードの書き方を紹介する記事を書きます。
お楽しみに〜＾＾
ArgumentError、Failure/Error httpリクエスト

```rb
RSpec.describe UsersController,type: :controller do
...
    it "user/newページがレンダリングされる", :new do
      get "new"
      expect(response.body).to render_template(:new)
    end
....
  end
end
```
get "/example"で失敗しました。

exampleグループ（つまり`describe` or `context`ブロック)上で`get`は使用可能ではありません。それぞれのexample(つまり`it`ブロック)またはexampleのスコープ内の実行されるコンストラクトからしか利用できません。
```
Failure/Error: get "/example"
  `get` is not available on an example group (e.g. a `describe` or `context` block). It is only available from within individual examples (e.g. `it` blocks) or from constructs that run in the scope of an example (e.g. `before`, `let`, etc).
```

```rb
RSpec.describe UsersController,type: :controller do
  render_views
    # createアクション
  describe "newアクション" do
    before(:new) do # :newからスコープを決める:exampleへ変更して解決
      get "new"
    end
   
    .....
  end
end
```

引数のエラー

フックに対してメタデータとしてシンボルを使ってスコープ (example, context)またはスコープエイリアス(each, all)を明示的に与えなければならない。 
```
ArgumentError:
  You must explicitly give a scope (example, context) or scope alias (each, all) when using symbols as metadata for a hook.
```

注意：`:example`と`:context`スコープはそれぞれ`:each`と`:all`として利用可能です。好きな方を使って下さい
>Note: the :example and :context scopes are also available as :each and :all, respectively. Use whichever you prefer.

https://rspec.info/features/3-13/rspec-core/hooks/before-and-after-hooks/#:~:text=Note%3A%20the%20%3Aexample%20and%20%3Acontext%20scopes%20are%20also%20available%20as%20%3Aeach%20and%20%3Aall%2C%20respectively.%20Use%20whichever%20you%20prefer
管理対象外のGoogleWorkspaceアカウントを全て移行できなかった話

# これはなに
- 契約更新時の整備の為、現在のGoogleWorkspaceのアカウントと管理対象外のアカウントを精査するときにうまくいったこと、うまくいかなかったことの対処方法を共有のため

昨年12月にやったことの詳細の記事になります。

https://qiita.com/tkazu0806/items/b89200455fe5c254be82

# 実施背景
1. 年間契約更新前にアカウントの棚卸をしている中で、前の管理者の方が「管理対象外のアカウント」を全く整備できていないとの共有をもらった
1. 管理コンソールで確認した所、いくつか自社のドメインで管理されていないことを確認
1. 管理されていない場合、データ移行を引継ぎするのにかなり手間がかかるので、
必要アカウントは管轄内に移行をしてアカウント不要時に、楽にデータ移行をできるようにしたい
（管理者コンソールツールで利用すれば、データ移行が簡単にできるので）

https://support.google.com/a/answer/1041297?hl=ja

# そもそもなぜ起きたのか
- 自社は、別のメールサーバーで運用をしていたため、昔はメールアドレスがあればGoogleのアカウントが自身で作れる状態であった
    - 結果、各自が本来のアカウント申請をするフローが必要であったが、それをすっ飛ばして現場ユーザーがGoogleアカウントを作成⇒メール認証でアカウント開設ができてしまった
- 管理者としては、当時申請しなければ分からないので、これは仕方ない
    - 制御する方法はこちらの方が解説していただいているので、かなり参考になりました

https://blog.cloudnative.co.jp/11308/

# どう対処したか
## うまくいった アカウント
- 管理対象外のアカウントのユーザーに現在利用しているのかを確認
    1. 不要であれば、削除を依頼
        1. データバックアップが必要であれば、データエクスポート機能を使って実施
    1. 移行が必要であれば、管理者機能を使って実施

https://support.google.com/a/answer/11112794?hl=ja

## うまくいかなかった アカウント
### どんなアカウント？？
- 管理対象外のアカウントで「Youtubeチャンネル」を利用しているアカウント移行できませんでした

### なんでできなかったの？？
＜Google Workspaceサポート担当者とのやり取りをまとめ＞

- 「Youtube」が「GoogleWorkspace」のコアサポート外である
- 移行に関して、管理対象外ユーザーの移行についてはGmail.comのユーザーを削除して、GoogleWorkspaceのユーザーを再作成するような内部挙動である
- 基本的にユーザーのアドレス名ではなく、アカウントそのものに紐づいておりますため、無償ユーザーから、GoogleWorkspaceアカウントに移行した場合、利用できなくなる可能性もある

- コミュニティと交流した結果このような例は検証をしないとわからないので「Youtubeチャンネル」が消えるリスクを考えると、実施するリスクが高いので、上長と相談して移行見送り

https://support.google.com/youtube/thread/298741585?hl=ja&sjid=6914462787536521895-AP&authuser=1

### じゃあ管理対象外のアカウントはどうしたの？？
- 「Youtubeチャンネル」の利用もしているので、社用携帯にて「二段階認証」を設定して、何かあったときに復旧やトラブルのやり取りをできる状態にした

https://support.google.com/accounts/answer/185839?hl=ja&co=GENIE.Platform%3DiOS&oco=0

# まとめ
## アカウント精査と整備の必要性

年間契約更新に伴い、Google Workspaceの管理対象外アカウントを精査し、必要なアカウントを管轄内に移行、不要なアカウントを削除することで、データ移行の効率化を取り組みました

## 課題とリスクへの対応

過去に管理外で作成されたアカウントや、YouTubeチャンネルを利用するアカウントの移行が困難であることが判明。
リスクが高い場合は移行を見送り、二段階認証設定などの代替措置を実施しました

## 今後の取り組み

Googleアカウント作成フローの厳格化や定期的な棚卸の実施を通じて、管理対象外アカウントの発生防止と管理体制の強化を進めます。

以上が整備活動の共有内容です。
具体的な改善提案やご質問がございましたら、ぜひコメントでください！
`reverse`メソッドの基本と使用例

# `reverse`メソッド

`reverse`メソッドは、Djangoの`urls.py`で設定した**名前付きURLパターン**をもとにURLを生成するためのメソッドです。ビューやURLパターンに付けた名前を使うことで、ハードコーディングを避けながらURLを簡単に参照できるようになります。

## 使用方法

`reverse`メソッドは、`django.urls`モジュールからインポートします。引数には、URLパターンに付けた名前を渡し、必要に応じてURLの引数やクエリパラメータを追加します。

```python
from django.urls import reverse

url = reverse('books_list')
print(url)  # '/books/' が出力される
```

## 名前付きURLパターン例

`urls.py`に以下のような名前付きURLパターンを設定しているとします。

```python
from django.urls import path
from . import views

urlpatterns = [
    path('books/', views.BookListView.as_view(), name='books_list'),
]
```

このとき、`reverse('books_list')`を呼び出すと、`'/books/'`が返ります。

## 具体例

```python
url = reverse('books_list') + query
```

1. `reverse('books_list')`  
   名前付きURLパターン `'books_list'` をもとに `'/books/'` を生成します。  
2. `+ query`  
   生成したURLにクエリパラメータを追加します。たとえば `query` が `'?name=John&genre=mystery'` の場合、最終的に  
   `'/books/?name=John&genre=mystery'`  
   というURLが完成します。

## まとめ

- **`reverse`メソッド**はDjangoのURL解決機能を利用し、`urls.py`で定義した名前付きURLパターンをもとにURLを生成します。  
- この仕組みにより、URLを変更してもコードの変更箇所が少なく済み、メンテナンス性が高まります。


## 例

```python
from django.urls import reverse
from django.http import HttpResponseRedirect
from django.views.generic import ListView
from django.contrib.auth.mixins import LoginRequiredMixin

class BookListView(LoginRequiredMixin, ListView):
    template_name = 'books_list.html'
    model = Books  # DjangoのモデルBooksを想定
    context_object_name = 'books'
    paginate_by = 20

    def get(self, request, *args, **kwargs):
        # クエリパラメータが存在しない場合の処理
        if not self.request.GET:
            # セッションから検索データを取得
            book_search_data = self.request.session.get('book_search_data')
            # セッションにデータがある場合、そのデータをクエリパラメータとしてリダイレクト
            if book_search_data:
                query_param = f"?{book_search_data}"
                url = reverse('books_list') + query_param
                return HttpResponseRedirect(url)
            # セッションにデータがない場合、デフォルトの検索クエリパラメータを設定してリダイレクト
            else:
                default_search_query = "?genre=fiction&stock=available&sort=asc"
                url = reverse('books_list') + default_search_query
                return HttpResponseRedirect(url)

        # クエリパラメータが存在する場合は、親クラスのgetメソッドを呼び出す
        return super().get(request, *args, **kwargs)
```

- `BookListView`は本の一覧を表示するためのビューを想定しています。  
- `book_search_data` にはセッションに保存されたクエリパラメータ（例：`"genre=mystery&stock=out_of_stock"`)などが入っているイメージです。  
- セッションにデータがない場合は、デフォルトのクエリパラメータ（`genre=fiction`、`stock=available`、`sort=asc`など）を付与してリダイレクトしています。
Next.js の脆弱性 CVE-2024-56332 への対応

# はじめに
Next.js のあるバージョン (>= 13.0.0 < 14.2.21, >= 15.0.0 < 15.1.1) にて影響のある脆弱性が見つかったようです。
以下のリンクは、Next.js の GitHub リポジトリの Security にある、CVE-2024-56332 の脆弱性についてのページへ遷移するリンクです。

https://github.com/vercel/next.js/security/advisories/GHSA-7m27-7ghc-44w9

ポートフォリオサイトで dependabot のアラートがあったので、自動対応してもらおうとしたら失敗しました。

そこで、手動でバージョンを上げたところ、next のバージョンが 15 以上では swcMinify の設定が削除されたとのことだったので、紹介のためまとめることにしました。

# 環境
macOS Sequoia 15.1.1

next: 14.2.13 (バージョンアップ前) -> 15.1.4 (バージョンアップ後)

以下、`npm list` コマンド実行時の出力です。

```sh
├── @biomejs/biome@1.9.4
├── @chromatic-com/storybook@3.2.2
├── @newhighsco/storybook-addon-svgr@2.0.38
├── @react-pdf/renderer@4.1.5
├── @storybook/addon-essentials@8.4.6
├── @storybook/addon-interactions@8.4.6
├── @storybook/addon-links@8.4.6
├── @storybook/addon-onboarding@8.4.6
├── @storybook/addon-styling-webpack@1.0.1
├── @storybook/addon-themes@8.4.6
├── @storybook/blocks@8.4.6
├── @storybook/nextjs@8.4.6
├── @storybook/react@8.4.6
├── @storybook/test@8.4.6
├── @svgr/webpack@8.1.0
├── @types/node@20.17.9
├── @types/react-dom@18.3.1
├── @types/react@18.3.12
├── autoprefixer@10.4.20
├── eslint-config-next@14.0.4
├── eslint-plugin-storybook@0.11.1
├── eslint@8.57.1
├── file-loader@6.2.0
├── next@15.1.4
├── postcss@8.4.49
├── prettier-plugin-tailwindcss@0.5.14
├── prettier@3.4.1
├── raw-loader@4.0.2
├── react-dom@18.3.1
├── react-hook-form@7.53.2
├── react-icons@5.3.0
├── react-pdf@9.1.1
├── react@18.3.1
├── storybook@8.4.6
├── tailwindcss-animated@1.1.2
├── tailwindcss@3.4.15
├── tsconfig-paths-webpack-plugin@4.2.0
├── typescript@5.7.2
└── worker-loader@3.0.8
```

# 解決方法

Next.js のバージョンを最新バージョンである 15.1.2, 14.2.21, 13.5.8 のいずれかに変更することで解決します。

先に記載した公式 GitHub のリンクでは、以下のように書かれています。
"There are no official workarounds for this vulnerability."
とのことで、公式の回避策がないとのことで、バージョンアップ以外に対応策はなさそうです。

# 脆弱性 CVE-2024-56332 について

脆弱性の内容については、先のリンクの他に、以下のページに詳しくまとめられています。

https://iototsecnews.jp/2025/01/03/next-js-patches-denial-of-service-vulnerability-cve-2024-56332-in-server-actions/

まとめると、攻撃者が Server Actions でのリクエストを何度も送ることができ、サービス拒否 (Dos) の攻撃ができてしまうようです。

# 解決までに行ったこと

基本は next のバージョンを上げるだけでよいです。

```sh
npm install next@latest
```

最新版ではなく、バージョンを指定してインストールしたい場合は、以下のように対応した各バージョンを指定してコマンドを実行しましょう。


```sh
npm install next@15.1.2
```

```sh
npm install next@14.2.21
```

```sh
npm install next@13.5.8
```

基本はこれでいいのですが、開発環境を構築する際に、以下のような警告が出ました。

```sh
⚠ Invalid next.config.js options detected:
⚠ Unrecognized key(s) in object: 'swcMinify'
⚠ See more info here: https://nextjs.org/docs/messages/invalid-next-config
```

これは、next.config.js ファイルに、swcMinify のオプションを設定しているためです。
next のバージョンが 15 以上では、swcMinify の設定項目が削除されています。バージョンの方から探した限り見つからず、swcMinify の項目に書いてあったので、そのページを載せます。Good to know という欄にあります。

https://nextjs.org/docs/architecture/nextjs-compiler#minification

私は pdfjs というパッケージを用いようとしており、その際に swcMinify に関する設定をしていました。
警告文の内容と、開発環境は正常に立ち上がるところを見る限り、さほど致命的なものでもなさそうであり、あくまでも警告のようです。

# おわりに

コミットメッセージで swcMinify の設定をいつなんのためにしたのかを追えたため、私の場合での設定理由を添えてアップデートに関する記事を簡単にまとめました。

そもそも、next のバージョンが 15 以下でこの脆弱性を解決するバージョンがあるので、そちらに合わせることでもこの警告を回避できそうです。
request.session.get('hoge')` と `request.session['hoge']` の違い


# `request.session.get('hoge')` と `request.session['hoge']` の違い
**A. 違いは「キーが存在しない場合の挙動」。**

## `self.request.session.get('contract_search_data')`
- **挙動**  
  `'contract_search_data'` というキーがセッションに存在する場合はその値を返し、存在しない場合は `None`（または指定したデフォルト値）を返します。  
- **安全性**  
  キーが存在しない場合でもエラーにならないため、安全に使えます。

```python
contract_search_data = self.request.session.get('contract_search_data')
# キーが存在しない場合、contract_search_data は None になる。
# self.request.session.get('contract_search_data', None) のように書くとより明示的になります。
```

## `self.request.session['contract_search_data']`
- **挙動**  
  `'contract_search_data'` というキーがセッションに存在する場合はその値を返しますが、存在しない場合は `KeyError` という例外が発生します。  
- **リスク**  
  キーが存在しない場合にエラーが発生するので、事前にキーが存在するかどうか確認しておく必要があります。

```python
try:
    contract_search_data = self.request.session['contract_search_data']
except KeyError:
    contract_search_data = None
# キーが存在しない場合、KeyError が発生し、except で捕捉して None を代入する例。
```

## まとめ
- **`get` を使う場合**  
  キーが存在しない場合にデフォルト値を返したい、あるいはエラーを防ぎたいときに使用します。

- **`[]` を使う場合**  
  キーが必ず存在することを前提とした処理を行うとき、あるいは存在しない場合にエラーを発生させたいときに使用します。

一般的には、キーが存在しない場合の動作を定義したいときに `get` を使う方が安全で便利です。

・参考記事↓

https://qiita.com/fukiko2020/items/db103c0a14af2c68fdea
【Kintone】Chromeデベロッパーツールでフィールドコードを簡単に確認する方法🚀

こんにちは！今回は、KintoneのフィールドコードをChromeのデベロッパーツールを使ってサクッと確認する方法をご紹介します。

# 手順📝

1. Kintoneアプリのレコード詳細画面を開く
    - フィールドコードを確認したいアプリのレコード詳細画面にアクセスします
1. Chromeのデベロッパーツールを開く
    - 画面上で右クリックして「検証」を選択するか、F12キーを押してデベロッパーツールを起動します
1. コンソールタブで以下のコードを実行
    - このコードは、現在表示しているアプリのIDを取得し、そのアプリの全フィールド情報を取得するAPIを呼び出します
    ```javascript
    await kintone.api('/k/v1/app/form/fields.json', 'GET', { app: kintone.app.getId() });
    
    ```

# コードの説明🔍

- `kintone.api()`：Kintoneが提供するJavaScript APIで、REST APIを呼び出すための関数です
- `'/k/v1/app/form/fields.json'`：フィールド情報を取得するエンドポイントです
- `'GET'`：HTTPメソッドの指定で、データを取得する際に使用します
- `{ app: kintone.app.getId() }`：リクエストパラメータとして、現在のアプリIDを指定しています

この操作により、コンソール上にフィールドコードやフィールド名などの情報が表示されます。
これを活用して、効率的にフィールドコードを確認し、カスタマイズやAPI連携の際に役立ててください😆
instance_method_already_implemented?

```rb
        def generated_attribute_methods
          @generated_attribute_methods ||= Module.new.tap { |mod| include mod }
        end

        def instance_method_already_implemented?(method_name)
          generated_attribute_methods.method_defined?(method_name)
        end
```
https://github.com/rails/rails/blob/cf6ff17e9a3c6c1139040b519a341f55f0be16cf/activemodel/lib/active_model/attribute_methods.rb#L400C2-L407C1

generated_attribute_methodsでモジュールをインクルードしその中にmethod_name（文字列）を持ったメソッドがあるかを確認している。
#### `@generated_attribute_methods`がnilの可能性がある
```rb
      private
        def inherited(base) # :nodoc:
          super
          base.class_eval do
            @attribute_method_patterns_cache = nil
            @aliases_by_attribute_name = nil
            @generated_attribute_methods = nil
          end
        end
```
https://github.com/rails/rails/blob/cf6ff17e9a3c6c1139040b519a341f55f0be16cf/activemodel/lib/active_model/attribute_methods.rb#L386C1-L395C1

#### method_defined?(method_name)　メソッドが定義されているかを確認
https://qiita.com/masatom86650860/items/1085740d04a983485768
PodmanでDb2を導入するコマンドメモ

### 参考
https://hub.docker.com/r/ibmcom/db2

### コマンド

```
podman pull ibmcom/db2
```

```
podman run -itd --name mydb2 --privileged=true -p 50000:50000 -e LICENSE=accept -e DB2INST1_PASSWORD=<passwordを入力> -e DBNAME=testdb -v C:\workspace\db2:/database ibmcom/db2
```

```
docker exec -ti mydb2 bash -c "su - db2inst1"

```
`request.GET` と `self.request.GET` の使い分けを理解する



# `request.GET`と`self.request.GET`の使い分け
## 関数ベースビュー（Function-Based View, FBV）
関数ベースビューでは、ビュー関数が引数として `request` オブジェクトを受け取ります。クエリパラメータにアクセスする場合は、`request.GET` を使用します。

#### 例
```python
def my_view(request):
    name = request.GET.get('name')  # FBVではrequest.GETを使う
```

## クラスベースビュー（Class-Based View, CBV）
クラスベースビューでは、`self.request` がクラスのインスタンス変数として設定されます。クラス内のメソッドからクエリパラメータにアクセスする際は、`self.request.GET` を使います。

#### 例
```python
from django.views.generic import ListView

class MyListView(ListView):
    def get(self, request, *args, **kwargs):
        name = self.request.GET.get('name')  # CBVではself.request.GETを使う
```

## 一貫性の観点から
クラスベースビューでのコードを一貫性のある形で書くには、`self.request.GET` の使用が望ましいかも？これによりクラス内のどのメソッドからでも同じ方法でリクエストオブジェクトにアクセスでき、コードの可読性やメンテナンス性が向上します。

## 実際の例
私の実務のコードでは、クラスベースビュー内で `request.GET` ではなく `self.request.GET` を使用しています。

```python
class ContractsListView(LoginRequiredMixin, OrderedListMixin, ListView):

    def get(self, request, *args, **kwargs):
        if not self.request.GET:  # ここ！！ if not request.GET とも書けてしまう
            contract_search_data = self.request.session.get('contract_search_data', None)
```

このようにすることで、クラスベースビュー全体でリクエストオブジェクトへのアクセス方法が統一され、コードの可読性と保守性が向上します。
文系未経験エンジニアが「ここで躓いた！」シリーズ～SSH接続って何？編～

# はじめに
このシリーズでは、文系出身未経験入社の開発エンジニア2年目の自分が、
なんとなく分からずにモヤモヤしていた知識や疑問をざっくりと解説していきます。
ざっくりとなので、多少正確性を欠くことがありますが、もし誤った記載がありましたらご指摘いただけますと幸いです。

# SSH接続とは
あるコンピュータから別のコンピュータに安全に接続し、遠隔操作を行うための技術。
導入方法は以下の通りです。
### コンピュータAからコンピュータBにSSH接続する流れ
①コンピュータBをインターネットに接続する。
②コンピュータBにSSHサーバーアプリをインストールし、他コンピュータからのSSH接続を許可する設定を行う。
③コンピュータAでSSH接続用の公開鍵と秘密鍵のペアを作成する。
④作成した公開鍵をコンピュータBに配置する（これでパソコンAから接続できるようになる）。
⑤コンピュータAに秘密鍵の情報を置いて、パソコンBにSSH接続する。

※公開鍵・秘密鍵をどこのディレクトリに置くかは、SSHサーバーがどのディレクトリを参照するかによる。
　大体はデフォルトで以下を参照している。
**公開鍵**
>~/.ssh/authorized_keys

**秘密鍵**
>~/.ssh/id_rsa

~はホームディレクトリの意。

# なぜ接続先に公開鍵/接続元に秘密鍵を置くのか？
当たり前ですが、接続先（開放しているコンピュータ）に秘密鍵を置いてしまうと、
接続したコンピュータの利用者全員に秘密鍵が知られてしまうため、意味がなくなってしまいます。

接続先コンピュータの公開鍵・接続元コンピュータの秘密鍵で、
「このコンピュータはSSH接続してOKだよ～」という認証が完了したら、
あとは通信内容を共通鍵で暗号・復号しながらやり取りを行います。

「この共通鍵が漏れてしまったら通信が傍受されちゃわない？」
と思うかもしれませんが、
共通鍵はセッションごとに生成・破棄されるため、
万が一漏れてしまっても、次のセッションでは安全に通信を行うことができます。

# 補足：主なSSHサーバーアプリ
OpenSSH - 最も広く使用されているオープンソースのSSHサーバー。
Dropbear - 軽量なSSHサーバー、主に組み込みシステム向け。
Bitvise SSH Server - Windows向けの商用SSHサーバー。
Windows OpenSSH - Windows 10以降に組み込まれたOpenSSHサーバー。
TeraTerm - 主にクライアントとして有名だが、サーバー機能も提供。
Djangoフォームのコンストラクタを理解する

## はじめに
Djangoのフォームクラスを作成する際、`forms.Form`や`forms.ModelForm`を継承すると、そのクラスは自動的にDjangoフォームの基本的な動作を引き継ぎます。このとき、コンストラクタ（`__init__`メソッド）も含まれています。

## Djangoのフォームのコンストラクタ
`forms.Form`や`forms.ModelForm`を継承したフォームクラスは、親クラス（`Form`クラス）のコンストラクタを自動的に継承します。つまり、フォームクラスで明示的にコンストラクタを定義しなくても、親クラスのコンストラクタが使用される仕組みです。

### 親クラスのコンストラクタ
`forms.Form`のコンストラクタは、主に以下のような処理を行います。

1. **フォームフィールドの初期化**  
   フィールドの設定やバリデーションの準備を行います。

2. **データの設定**  
   フォームの初期化時に渡されたデータ（通常は`request.GET`や`request.POST`）をフォームフィールドにセットします。



## フォームの定義と使用例

以下の例では、`ClientSearchForm`というフォームクラスを定義しています。

### `ClientSearchForm`の定義

```python
from django import forms

class ClientSearchForm(forms.Form):
    name = forms.CharField(
        max_length=150,
        required=False,
        label='',
        widget=forms.TextInput(
            attrs={
                'class': 'form-control col-lg2',
                'style': (
                    'padding-left: 32px; padding-top: 6px; '
                    'padding-bottom: 6px; padding-right: 12px; '
                    'border-radius: 4px; border: 1px solid var(--gray2, #DDD); '
                    'background: var(--white, #FFF); height: 34px;'
                )
            }
        )
    )
```

### フォームのインスタンス化

```python
form = ClientSearchForm({'name': 'John'})
```



## 親クラスのコンストラクタ

フォームのインスタンスを生成すると、`forms.Form`のコンストラクタが呼び出されます。そのシグネチャは以下のようになっています。

```python
class FormBase:
    def __init__(
        self,
        data=None,
        files=None,
        auto_id='id_%s',
        prefix=None,
        initial=None,
        error_class=ErrorList,
        label_suffix=None,
        empty_permitted=False,
        field_order=None,
        use_required_attribute=None,
        renderer=None
    ):
        # 初期化の処理
```



## フォームのインスタンス生成の流れ

1. **データの受け取り**  
   `ClientSearchForm({'name': 'John'})`の呼び出しにより、`{'name': 'John'}`というデータがコンストラクタに渡されます。

2. **親クラスのコンストラクタ呼び出し**  
   明示的に定義していなくても、`forms.Form`のコンストラクタが呼ばれ、フォームフィールドにデータがセットされます。

3. **フォームフィールドの初期化**  
   `name`フィールドに`'John'`という値がセットされ、フォームインスタンスが生成されます。



## フォームのバリデーションとクリーンデータの取得

フォームのバリデーションを実行し、クリーンデータを取得する流れを示します。

### バリデーションの実行

```python
if form.is_valid():
    cleaned_data_name = form.cleaned_data
    print(cleaned_data_name)
else:
    print(form.errors)
```

- `form.is_valid()`を呼び出すと、フォームに設定されたデータがバリデーションされます。  
- バリデーションが成功すると、`form.cleaned_data`にクリーンなデータ（今回の例では“John”）が格納されます。  
- バリデーションが失敗した場合、`form.errors`にエラーメッセージが格納されます。



## 結論

`forms.Form`を継承したクラスでは、明示的にコンストラクタを定義しなくても、親クラスのコンストラクタが自動的に呼び出されます。これにより、フォームの初期化やデータのセット、バリデーションの準備が行われます。

`ClientSearchForm`クラスを`{'name': 'John'}`で初期化すると、`name`フィールドに`'John'`がセットされ、フォームインスタンスが生成されます。その後、バリデーションを行い、問題がなければクリーンデータを取得できます。
Module#method_defined?

# 引数のもった文字列を持つインスタンスメソッドとして定義されているか確認する
>method_defined?(name, inherit=true) -> bool
>モジュールに`インスタンスメソッド name が定義されており、かつその可視性が public または protected であるときに true を返し`ます。

### パラメータ
>[PARAM] name:
Symbol か String を指定します。
[PARAM] inherit:
真を指定するとスーパークラスや include したモジュールで定義されたメソッドも対象になります。

>[SEE_ALSO] Module#public_method_defined?, Module#private_method_defined?, Module#protected_method_defined?

例
```rb
irb(main):001* module A
irb(main):002*   def method1()  end
irb(main):003*   def protected_method1()  end
irb(main):004*   protected :protected_method1
irb(main):005> end
irb(main):006* class B
irb(main):007*   def method2()  end
irb(main):008*   def private_method2()  end
irb(main):009*   private :private_method2
irb(main):010> end
irb(main):011* class C < B
irb(main):012*   include A
irb(main):013*   def method3()  end
irb(main):014> end
=> :method3

irb(main):023> C.method_defined? "private_method2" # スーパークラスで定義されていてもprivateをにあるとfalseを返す
=> false
irb(main):024> A.method_defined? :method1
=> true
irb(main):025> C.method_defined? "method1"
=> true
irb(main):026> C.method_defined? "method2"
=> true
irb(main):027> C.method_defined? "method2", true
=> true
irb(main):028> C.method_defined? "method2", false # falseにするとスーパークラス、モジュールからの継承は対象外になる
=> false
irb(main):029> C.method_defined? "method3"
=> true
irb(main):030> C.method_defined? "protected_method1"
=> true
irb(main):031> C.method_defined? "method4"
=> false
irb(main):032> C.method_defined? "private_method2"
=> false 
```
https://docs.ruby-lang.org/ja/latest/method/Module/i/method_defined=3f.html
Django ORMでの検索フィルタ：大文字小文字の違いと数値比較


## はじめに

DjangoのORM（オブジェクトリレーショナルマッピング）は、データベース操作を簡素化する強力なツールです。SQLを直接記述する代わりに、Pythonコードを使ってデータベースの検索や操作を行えます。本記事では、Django ORMを使用した検索フィルタリングの中でも、大文字小文字の扱いや数値比較に焦点を当てて解説します。


## 大文字小文字を区別しない検索と区別する検索

Djangoでは、フィールド値の検索において大文字小文字を区別するかどうかを選択できます。以下にそれぞれの方法を解説します。

### 大文字小文字を区別しない検索（`icontains`）

`icontains`を使用することで、大文字小文字を区別せずにフィールド値を検索できます。

```python
queryset = Clients.objects.filter(name__icontains='john')
```

このクエリは、`name` フィールドに「john」を含むすべての`Clients`オブジェクトを返します。たとえば、次のようなデータが一致します：

- `'John'`
- `'john'`
- `'JOHN'`
- `'JoHn'`

### 大文字小文字を区別する検索（`contains`）

`contains`を使用すると、大文字小文字を区別した検索が可能です。

```python
queryset = Clients.objects.filter(name__contains='john')
```

このクエリは、`name`フィールドに「john」を正確に含むレコードのみを返します。したがって、`'John'`や`'JOHN'`は一致しません。

### 補足：`icontains`の`i`の意味

`icontains`の先頭の`i`は「case-insensitive（大文字小文字を区別しない）」の略です。Djangoでは、`icontains`のように`i`を加えた検索フィルタを利用することで、より柔軟な検索が可能になります。


## Djangoでの数値比較検索：`lt` と `gt`

数値や日付などの比較検索を行う場合、Django ORMでは`lt`（未満）や`gt`（より大きい）を使用します。

### `lt`（未満）を使用した検索

以下の例では、商品の価格が1000円未満の商品を検索します。

```python
queryset = Product.objects.filter(price__lt=1000)
```

このクエリは、`price`フィールドの値が1000未満の`Product`オブジェクトを返します。

### `gt`（より大きい）を使用した検索

契約の終了日が今日またはそれ以降であるレコードを取得する例です。

```python
contracts = Contracts.objects.filter(end_date__gte=today)
```

- **`gt`**: 「より大きい」（greater than）
- **`gte`**: 「以上」（greater than or equal to）

### 応用例：除外条件と複合フィルタ

以下は、特定の契約を除外し、さらに終了日が今日以降または未定のものを取得する例です。

```python
contracts = Contracts.objects.exclude(id__in=assigned_contract_ids).filter(
    Q(end_date__gte=today) | Q(end_date__isnull=True)
)
```

- **`exclude`**: 指定条件を満たすレコードを除外します。
- **`Q`オブジェクト**: 複数条件のOR/AND検索を簡潔に記述できます。



## 補足情報

- **日付フィルタリング**: `lt`や`gt`は、数値だけでなく日付や時間などにも使用可能です。
- **複雑な条件**: `icontains`や`lt`などを組み合わせることで、柔軟なクエリ構築が可能になります。


## 実務での活用例

私の実務経験では、これらのフィルタを使用して次のような検索条件を実装しました：

- ユーザー名や会社名の部分一致検索。
- 契約終了日が未設定のレコードの取得。

## まとめ

- 大文字小文字を区別しない検索には`icontains`、区別する検索には`contains`を使用。
- 数値や日付の比較には`lt`（未満）や`gt`（より大きい）を活用。
- DjangoのORMは簡潔なコードで柔軟なクエリを実現可能。
Djangoフォームの`cleaned_data`を理解する



## はじめに

Djangoのフォームクラスには、入力データを効率的に検証し、安全に取り扱うための便利な機能が多数用意されています。その中でも、`cleaned_data`はフォームのバリデーションが通過したデータを扱う際に不可欠なプロパティです。本記事では、`cleaned_data`の仕組みや使用方法を具体例を交えて解説します。


## `cleaned_data`の仕組み

### 基本の動作

- **概要**:  
  `cleaned_data`はDjangoのフォームクラスが提供するプロパティで、フォームのバリデーションが成功した後に利用できます。
- **アクセス方法**:  
  メソッドではなくプロパティとして提供されるため、`cleaned_data`に直接アクセスします（例: `form.cleaned_data`）。
- **データ形式**:  
  フィールド名をキー、バリデーション済みのデータを値とした辞書形式でデータを提供します。


## 使用条件と動作の流れ

1. **バリデーションの実行**:
   - `is_valid()`メソッドを呼び出すことで、フォームのバリデーションが実行されます。
   - すべてのフィールドがバリデーションを通過した場合、`cleaned_data`にデータが格納されます。

2. **バリデーション成功後に利用**:
   - `is_valid()`が`True`を返した場合にのみ、`cleaned_data`が利用可能です。
   - フォームが無効な場合（`is_valid()`が`False`）、`cleaned_data`は空の状態となり、データを取得できません。


## 基本例

以下は、`cleaned_data`を使った基本的な例です。

```python
filters = search_form.cleaned_data
```

このコードでは、フォームでバリデーションが通過したデータを`filters`に格納しています。この辞書にはすべてのフィールド名がキーとして含まれ、値としてそのフィールドのデータが格納されます。


## `cleaned_data`と`is_valid`の関係

- **`is_valid`の役割**:
  - フォームデータのバリデーションを実行。
  - バリデーション結果が`True`であれば、`cleaned_data`が利用可能。

- **`cleaned_data`の役割**:
  - バリデーションが成功した後、各フィールドの安全なデータを提供。

### 使用例

```python
if search_form.is_valid():
    # バリデーションが成功した場合のみ、cleaned_dataを利用
    filters = search_form.cleaned_data
    project_name = filters["project_name"]
    print(project_name)  # フォームで入力された「project_name」の値を出力
```


## 具体例：フィールドデータの取得

以下は、フォームデータから特定のフィールド値を取得する具体例です。

### コード例

```python
# フォームがバリデーションを通過したら
if search_form.is_valid():
    # cleaned_dataからフィルタ条件を取得
    filters = search_form.cleaned_data

    # フィールド値の取得
    project_name = filters["project_name"]
    start_date = filters["start_date"]

    # 取得したデータを利用
    print(f"Project: {project_name}, Start Date: {start_date}")
```

### 動作イメージ

1. フォームの`is_valid()`メソッドを呼び出し、バリデーションを実行。
2. 成功時に、`cleaned_data`から`project_name`や`start_date`の値を取得。
3. 取得した値を利用してクエリの絞り込みや、他の処理に活用。


## 注意点

1. **`is_valid()`を必ず呼び出す**:
   - バリデーションが成功する前に`cleaned_data`にアクセスすると、例外が発生します。

2. **安全なデータのみ格納**:
   - バリデーション済みデータのみが`cleaned_data`に含まれるため、不正データの混入を防ぎます。



## まとめ

Djangoの`cleaned_data`は、フォームデータを安全に取得し、活用するための基本プロパティです。バリデーションが成功した後に利用可能で、辞書形式でデータにアクセスできます。`is_valid()`と組み合わせて使用することで、フォームの入力データを効率的かつ安全に取り扱うことが可能です。
docker imageのリポジトリ名とタグ名を変更する

## このような場合を想定

```
REPOSITORY                                  TAG                  IMAGE ID       CREATED         SIZE
sample_repository                           latest               1a2b3c4d5e6f   2 days ago      366MB
<none>                                      <none>               g7h8i9j0k1l2   2 days ago      113MB
```

### ケース１：sample_repositoryのタグ名を変更したい

```
docker tag sample_repository:new_tag
```

```
docker tag {リポジトリ名}:{新たなタグ名}
```

### ケース２：`<none>`のリポジトリ名とタグ名を変更したい

```
docker tag g7h8i9j0k1l2 new_repository:latest
```

```
docker tag {image ID} {新たなリポジトリ名}:{新たなタグ名}
```

### 補足

* イメージの一覧を確認する方法

```
docker images
```
RailsでAWSにデプロイしたら画面が表示されない？force_ssl設定での解決策（Rails7.1以降）

# はじめに

ポートフォリオ作成のために、Railsで作成したアプリケーションをAWS上にデプロイしました。デプロイには、nginx、AWS、Pumaを使用しました。

しかし、nginxやPumaは正常に動作しているようなのに、独自ドメインでページにアクセスすると画面が表示されないというエラーが発生しました。

![エラー画像](https://storage.googleapis.com/support-forums-api/attachment/thread-290837394-11812210559147903366.png)
# 環境
- **Rails** 7.1.4
- **Ruby** 3.2.3
# 解決法
エラーの原因は、Railsのデフォルト設定である```config.force_ssl = true```にありました。これにより全てのアクセスがHTTPSにリダイレクトされるため、HTTPでの接続ができなくなっていました。

以下の手順で解決しました：

1. `config/environments/production.rb`ファイルを開き、`config.force_ssl`の値を変更します。

    ```ruby:config/environments/production.rb
    # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
    config.force_ssl = false
    ```
    &nbsp;
2. ファイルを保存後、Pumaを再起動します。私はsystemdで管理しているため、以下のコマンドを使用しました。

    ```bash
    sudo systemctl restart puma
    ```
    &nbsp;
    
3. 設定を反映させた後、ブラウザでページを開くと正常に表示されるようになりました。

# 補足
元々設定されていた`config.force_ssl = true`は、アプリケーションへの全てのアクセスをSSL経由にする設定です。私の場合、AWS上でHTTPのみの設定をしていたため、画面が表示されないエラーが発生しました。

:::note info
```config.force_ssl = true```の記述はRails 7.1以降にデフォルトの記述として設定されました。
:::

参考ページ:

https://rubyonrails.org/2023/6/16/this-week-in-rails


# おわりに
今回のエラーは、Rails 7.1以降にデフォルトで有効になった設定が原因で発生しました。ネットで調べても情報が少なかったため、ChatGPTを活用して原因を特定することができました。

デフォルトでSSLが有効になっているので、今後はデプロイ時に最初からSSL化を行うことが重要そうです。

ちなみに、私がRailsアプリをAWSでデプロイする際に参考にしたサイトは以下です。

**Railsのデプロイに関して**

https://zenn.dev/redheadchloe/articles/3441d06b95d70e


https://www.youtube.com/watch?v=mIIFnkJXslg


**AWSに関して** 

https://www.udemy.com/course/aws-and-infra/
【Django】Qオブジェクト



## はじめに

Djangoの`Q`オブジェクトは、複雑なクエリを簡潔かつ柔軟に記述できる強力なツールです。特に、AND条件やOR条件を組み合わせたクエリが必要な場合に役立ちます。本記事では、`Q`オブジェクトを活用した期間フィルタリングの例を中心に解説します。



## Qオブジェクトの基本

### AND検索（デフォルト）

Qオブジェクトを複数組み合わせた場合、デフォルトではAND条件が適用されます。

```python
from django.db.models import Q

query = Q(field1="value1") & Q(field2="value2")
queryset = MyModel.objects.filter(query)
```

### OR検索（パイプ演算子`|`）

OR条件を適用するには、Qオブジェクトを`|`で結合します。

```python
query = Q(field1="value1") | Q(field2="value2")
queryset = MyModel.objects.filter(query)
```


## 実践例：日付に基づくフィルタリング

以下のコードは、特定の期間条件に基づいてクエリセットを絞り込む例です。条件として「期間前」「期間中」「期間後」「期間未定」をサポートします。

### コード例

```python
from django.db.models import Q
import datetime

# 今日の日付
today = datetime.date.today()

# 条件を保持するQオブジェクト
query = Q()

# 条件フィルタリング
if filters.get('period_before'):  # 期間前
    query |= Q(start_date__gt=today)

if filters.get('period_during'):  # 期間中
    query |= Q(start_date__lte=today, end_date__gte=today)

if filters.get('period_after'):  # 期間後
    query |= Q(end_date__lt=today)

if filters.get('period_undefined'):  # 期間未定
    query |= Q(start_date__isnull=True) | Q(end_date__isnull=True)

# クエリセットにフィルタを適用
queryset = MyModel.objects.filter(query).distinct()
```



## 各期間の条件説明

### 1. **期間前 (`period_before`)**
   - 条件: 開始日が今日より後の日付。
   - フィルタ: `Q(start_date__gt=today)`

### 2. **期間中 (`period_during`)**
   - 条件: 開始日が今日以前かつ終了日が今日以降の日付。
   - フィルタ: `Q(start_date__lte=today, end_date__gte=today)`

### 3. **期間後 (`period_after`)**
   - 条件: 終了日が今日より前の日付。
   - フィルタ: `Q(end_date__lt=today)`

### 4. **期間未定 (`period_undefined`)**
   - 条件: 開始日または終了日が設定されていない。
   - フィルタ: `Q(start_date__isnull=True) | Q(end_date__isnull=True)`



## クエリの組み立て方

`Q`オブジェクトを使用すると、複数の条件を簡潔に組み立てることができます。

1. 条件を確認し、該当するフィルタを`query`に追加します。
2. 各条件を`|`（OR条件）で結合して柔軟な検索を実現します。
3. 最終的にクエリセットに適用してフィルタリングします。



## 応用例

`Q`オブジェクトを活用すれば、次のようなクエリも簡単に構築できます。

- **複数フィールドでの部分一致検索**:
  ```python
  query = Q(name__icontains="test") | Q(description__icontains="test")
  queryset = MyModel.objects.filter(query)
  ```

- **複数条件のANDとORの組み合わせ**:
  ```python
  query = (Q(name="A") & Q(status="active")) | Q(name="B")
  queryset = MyModel.objects.filter(query)
  ```



## まとめ

Djangoの`Q`オブジェクトを使うことで、複雑な条件付きクエリをシンプルに記述できます。特に、今回の期間フィルタリングの例では、複数の期間条件を効率的に管理し、必要に応じて柔軟な検索が可能になりました。

ぜひ、`Q`オブジェクトを使ってデータベースクエリの可能性を広げてみてください！
AttributeMethodPattern#method_name

```rb
          def method_name(attr_name)
            @method_name % attr_name
          end
```
https://github.com/rails/rails/blob/cf6ff17e9a3c6c1139040b519a341f55f0be16cf/activemodel/lib/active_model/attribute_methods.rb#L491C1-L493C14

#### `@method_name`はprefixとsuffixが""で"%s"だけの可能性がある。
```rb
        class AttributeMethodPattern # :nodoc:
          attr_reader :prefix, :suffix, :proxy_target, :parameters


          AttributeMethod = Struct.new(:proxy_target, :attr_name)


          def initialize(prefix: "", suffix: "", parameters: nil)
            @prefix = prefix
            @suffix = suffix
            @parameters = parameters.nil? ? "..." : parameters
            @regex = /\A(?:#{Regexp.escape(@prefix)})(.*)(?:#{Regexp.escape(@suffix)})\z/
            @proxy_target = "#{@prefix}attribute#{@suffix}"
            @method_name = "#{prefix}%s#{suffix}"
          end


```
https://github.com/rails/rails/blob/cf6ff17e9a3c6c1139040b519a341f55f0be16cf/activemodel/lib/active_model/attribute_methods.rb#L471C1-L484C1

#### 属性が:faa（シンボル）だと文字列にフォーマットされる
```rb
irb(main):001> "%s" % :faa
=> "faa"
```
#### `%`はフォーマット化する
https://qiita.com/masatom86650860/items/bd34398f2bf405af7b60
【Homebrew】It seems the App source '/Applications/xxx.app' is not there.エラー解消法

## 状況

`brew upgrade`を実行した際に以下のようなエラーが発生しました。

```terminal
$ brew upgrade
...
Error: xxx: It seems the App source '/Applications/xxx.app' is not there.
...
```

## 解消法
エラーが発生しているアプリケーションを再インストールすることで解消します。

```terminal
brew reinstall xxx
```

## 参考

https://rubenerd.com/fixing-app-source-not-there-in-homebrew/
URL長すぎ問題への対処

## 概要

**GETリクエストではデータがURLに表示されるため、データ量に制限があります。** 一方、POSTリクエストはデータをHTTPリクエストのボディに含めるため、より多くのデータを安全に送信できます。

しかし、検索機能などでGETリクエストを行うとURLが長くなりすぎ、一定の長さを超えるとエラーが発生する問題に直面します。検索条件が増えると、URLがどんどん長くなり、エラーの起きるケースが出てきたのです。

このような問題に対して、以下の解決策を検討しました。



## 検討した解決策

1. **URLの長さの上限を引き上げる**
   - URLの許容長を増やす案もありましたが、これは根本的な解決にならないため却下。

2. **POSTリクエストに変更**
   - データ量の制約を回避するためにPOSTリクエストに書き換える案も検討しましたが、工数が大きすぎるため断念。



## 最終的な解決策

### IDを使用してURLを短縮

クエリパラメータに名前や長い文字列ではなく、**対応するID**を使うように変更しました。

#### 変更前の例
```plaintext
/customer_list/?first_name=John&last_name=Tommy&company_name=McKinseyCompanyInc
```

#### 変更後の例
```plaintext
/customer_list/?first_name=1&last_name=4&company_name=2
```

このように修正することで、URLの長さを大幅に短縮しました。これでも200以上の検索条件を重ねればエラーが起きる可能性がありますが、現実的には十分な対応と判断しました。



## 【Web開発初学者向け】 GETリクエストとPOSTリクエストの違い

### GETリクエスト

- 主に情報を「取得」するために使用。
- データはURLのクエリパラメータとして送信。
- データがブラウザのアドレスバーに表示されるため、ブックマークや共有が可能。
- **制約**: URLに表示されるため、データ量に限界があり、機密情報には不向き。

#### 例
```plaintext
https://example.com/news?date=2024-08-07
```
この場合、`date=2024-08-07`がクエリパラメータです。サーバーはこの日付に基づくニュースを返します。



### POSTリクエスト

- 主に情報を「送信」するために使用。
- データはHTTPリクエストのボディに含まれるため、URLには表示されない。
- URLの長さ制限を回避でき、機密情報の送信にも適している。
- 主にフォーム送信やファイルアップロードに利用される。

#### 例
オンラインショッピングで購入手続きをする際、住所やクレジットカード情報をPOSTリクエストでサーバーに送信します。

---
### GETリクエストとPOSTリクエストを例えてみよう

### GETリクエスト
図書館で本を借りるようなものです。  
生徒がカタログシステムに「科学の本」と入力して検索し、その結果を受け取る過程に似ています。検索条件は公開されており、誰でも見ることができます。

### POSTリクエスト
先生のロッカーに、テスト回答用紙を入れるようなものです。  
生徒が試験の答案を封筒（HTTPボディ）に入れ、先生のロッカー（サーバー）に投函します。他の人は内容を知ることができません。



## まとめ

GETリクエストのデータ量制限を回避するため、**名前をIDに変換してURLを短縮する方法**を採用しました。この対応により、エラーを防ぎつつ現実的な工数で問題を解決できました。

GETとPOSTの使い分けや特徴を理解することは、Web開発における基本的なスキルです。特に、データ量やセキュリティ要件に応じた適切なリクエスト選択が重要です。

https://zenn.dev/miya_akari/articles/0ba19ecd916386
circuitjsの作法 その51

# 概要
circuitjsを調査してみた。
練習問題やってみた。

# 練習問題
論理回路でクリア付きDフリップフロップを書け。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/dfdb0268-60f9-109f-1073-7ee238ec4781.png)

# ネットリスト

```
$ 1 0.000005 10.20027730826997 50 5 50 5e-11
w 272 288 272 304 0
w 272 272 272 288 0
w 272 128 272 144 0
w 272 112 272 128 0
w 128 416 128 32 0
w 272 368 272 416 0
w 272 144 272 176 0
w 160 256 160 240 0
w 96 160 96 272 0
M 432 144 496 144 0 2.5
R 32 272 -32 272 1 2 100 2.5 2.5 0 0.5
L 32 384 -32 384 0 1 false 5 0
w 160 384 96 384 0
w 96 160 160 160 0
w 160 272 96 272 0
w 128 416 272 416 0
w 160 32 128 32 0
w 272 176 160 240 0
w 272 288 320 288 0
w 272 128 320 128 0
w 160 336 272 304 0
w 160 352 160 336 0
w 272 336 272 368 0
w 160 304 272 336 0
w 160 288 160 304 0
w 272 80 272 48 0
w 160 112 272 80 0
w 160 128 160 112 0
w 160 80 272 112 0
w 160 64 160 80 0
151 160 368 272 368 0 3 0 5
151 160 272 272 272 0 3 5 5
151 160 48 272 48 0 2 5 5
151 160 144 272 144 0 3 0 5
w 320 240 320 256 0
w 320 176 320 160 0
w 432 240 320 176 0
w 432 272 432 240 0
w 432 176 320 240 0
w 432 144 432 176 0
151 320 144 432 144 0 2 5 5
151 320 272 432 272 0 3 0 5
w 160 368 80 368 0
w 80 368 80 144 0
w 80 144 160 144 0
w 0 144 80 144 0
w 80 368 80 448 0
w 80 448 304 448 0
w 304 272 320 272 0
w 304 272 304 448 0
L 0 144 -32 144 0 1 false 5 0
w 32 384 96 384 0
w 32 272 96 272 0
x -20 104 8 107 4 24 clr
x -20 344 -3 347 4 24 D
x 476 88 494 91 4 24 Q
x -20 232 11 235 4 24 clk

```

以上。
【Python】素数判定法　※随時追加

## はじめに
Pythonで素数判定を行うプログラムを実装してみた。
まだアルゴリズムを学び始めたばかりの初心者のため、学習進度に伴って簡単な手法から順次アウトプットしていくことにする。


## 素数判定とは
定義や概要などはWikipediaを参照。

https://ja.wikipedia.org/wiki/%E7%B4%A0%E6%95%B0%E5%88%A4%E5%AE%9A

素数判定の実用的な利用法の1つとしては、RSA暗号など。

https://ja.wikipedia.org/wiki/RSA%E6%9A%97%E5%8F%B7


## 試し割り法

### 概要
https://ja.wikipedia.org/wiki/%E8%A9%A6%E3%81%97%E5%89%B2%E3%82%8A%E6%B3%95

簡単に言うと、判定する数`n`を小さい整数から順に割ってみて、`n`以外のどれでも割り切れない場合、`n`は素数だとなる。
最も理解しやすいアルゴリズムだが、計算量はどうしても多くなってしまう。

### 実装
``` python
# nは素数であるか判定する
n = int(input("素数判定を行う数を入力："))

# nが1以下の場合　→　素数ではない
if n <= 1:
    print(f"{n}は素数ではありません")

# nが2以上の場合 → 2～(n-1)の数で割ってみて、どれかで割れたらループを抜ける
else:
    for i in range(2, n):
        if n % i == 0:
            print(f"{n}は素数ではありません")
            break
        
    # (n-1)までのどの数でも割れない場合 → 素数である
    else:
        print(f"{n}は素数です")
```

### 出力結果
演算速度を確認したいので、入力する素数は大きめにしてみた。
``` 
素数判定を行う数を入力：9973
9973は素数です
``` 
9973は、4桁以下で最大の素数。
Jupyter Notebook上でテストしてみたところ、この程度の大きさの数であれば即座に結果が出力された。

### 感想
あくまで試し割り法をベースとする場合でも、もっと効率的なコードは多分書ける。

思いついたので言うなら、ループを回す前にあらかじめ偶数を除外するとか。
ループを回す条件も √n までで良いという話らしいが、数学の学習を多少しないと今の自分には難しそうだ。

コードの改良・その他のアルゴリズムで実装などは、追々やっていく予定。
5分でAndroid Studioを使ってWhatsAppのような1対1チャットアプリを構築！（最高のUI）

# **5分でAndroid Studioを使ってWhatsAppのような1対1チャットアプリを構築！（最高のUI）**

# **はじめに**

友人や家族とのつながりを保つこと、同僚と協力することなど、WhatsAppやTelegramのようなチャットアプリは私たちのコミュニケーション方法を革新しました。独自の1対1チャットアプリを作成する方法を知りたいと思ったことはありませんか？この[**チュートリアル**](https://trtc.io/document/45914?platform=android&product=chat&menulabel=uikit)はそのためのものです！Android Studio、Tencent UIKit、Tencent SDKを使用して機能豊富な1対1チャットアプリを構築するプロセスをガイドし、ユーザーにシームレスで魅力的なチャット体験を提供できるようにします。このチュートリアルの終わりには、カスタマイズして展開できる完全に機能する1対1チャットアプリが[完成します](https://trtc.io/products/chat)。

# **環境要件**

- Android Studio-Chipmunk
- Gradle-6.7.1
- Android Gradle Plugin Version-4.2.0
- kotlin-gradle-plugin-1.5.31

# **ステップ1：アプリの作成**

1. [**コンソール**](https://console.trtc.io/)にログインします。すでにアプリがある場合は、そのSDKAppIDとSDKSecretKeyを記録します。

**注意：**

1. Chatアカウントは最大300のChatアプリを作成できます。新しいアプリを作成したい場合は、まず不要なアプリを無効化して削除できます。
2. **アプリ（およびそのSDKAppID）が削除されると、提供するサービスとそのすべてのデータが失われます。慎重に操作してください。**
3. `Create Application`をクリックし、アプリケーション名、製品、地域を入力し、`Create`をクリックします。

# **ステップ2：SDKAppIDとSDKSecretKeyの取得**

作成後、アプリケーションページで新しく作成されたアプリのステータス、SDKAppID、有効期限などを確認できます。アプリケーション情報からSDKAppIDとSDKSecretKeyを記録します。

**危険：**

SDKSecretKeyを適切に保管し、漏洩を防止してください。

# **ステップ3：デモのダウンロードと設定**

1. GithubからAndroid[**デモプロジェクト**](https://github.com/TencentCloud/chat-uikit-android)をダウンロードします。
2. ターミナルディレクトリでプロジェクトを開き、`GenerateTestUserSig.java`ファイルを見つけます。パスはAndroid/Demo/app/src/main/java/com/tencent/qcloud/tim/demo/signature/GenerateTestUserSig.javaで、関連するパラメータを設定します：

SDKAPPID：上記で取得したSDKAppIDに設定します。

SECRETKEY：上記で取得したSDKSecretKeyに設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3981333/f3075990-6307-4908-8d9b-b21f4fdf1222.png)



**警告：**

このチュートリアルでは、`UserSig`を取得する方法は、クライアントコードに`SECRETKEY`を設定することです。この方法では、`SECRETKEY`は逆コンパイルやリバースエンジニアリングに脆弱です。`SECRETKEY`が漏洩すると、攻撃者があなたのTencent Cloudトラフィックを盗む可能性があります。したがって、**この方法はデモプロジェクトをローカルで実行し、機能をデバッグするのに適しています**。

正しい`UserSig`配布方法は、`UserSig`の計算コードをサーバーに統合し、アプリ向けのAPIを提供することです。`UserSig`が必要な場合、アプリはビジネスサーバーにリクエストを送信して動的な`UserSig`を取得できます。

# **ステップ4：デモのコンパイルと実行**

Android Studioでデモプロジェクトをインポートし、コンパイルして実行します。詳細については、上記でクローンしたデモプロジェクトの対応するディレクトリにある`README.md`ファイルを参照してください。

**同様のアプリやプラットフォームを構築したいですか？今すぐ無料の10,000分を入手してください[無料で始める](https://trtc.io/login?s_url=https://console.trtc.io)**

# **結論**

おめでとうございます！Android Studio、Tencent UIKit、Tencent SDKを使用して、完全に機能する1対1チャットアプリを構築しました。1対1チャット、グループチャット、検索、複数のメッセージタイプのサポートなどの機能を備えたアプリは、カスタマイズして展開する準備ができています。

しかし、これは始まりに過ぎません！ユーザー認証、プッシュ通知、エンドツーエンドの暗号化などの[**追加機能を追加して、ユーザーに安全でシームレスなチャット体験を提供するようにアプリをさらに強化できます**](https://trtc.io/document/59441?platform=android&product=chat&menulabel=uikit)。

1対1チャットアプリの開発と改良を続ける際は、ユーザーフィードバックと反復の重要性を忘れないでください。ユーザーの提案に耳を傾け、彼らのアイデアを取り入れて、真にニーズを満たすアプリを作成してください。献身と忍耐力を持って、あなたの1対1チャットアプリがインスタントメッセージングの世界で次の大きな存在になる可能性があります。
RailsのActive Record簡易まとめ

Ruby on RailsのActive Recordについて簡単にまとめました。

# Active Recordsとは
Railsとデータベースの間のやりとりを担当するレイヤーです。
データベースから取ってきたデータを元にインスタンスを作成したり、インスタンスへの操作を通してデータベースを更新したりします。

基本的にデータベースの操作はSQLで実行するものですが、Railsではデータベースの操作にSQLを書く必要はほぼありません。Active RecordがRubyで書かれた記述を解釈し、ユーザの代わりにデータベースに対してクエリを発行してくれるからです。

# 機能
## CRUD
データベースに対してCRUD操作を行う機能が組み込まれており、簡単に利用できます。
たとえば```Users.all```だけで「すべてのユーザを取得する」という操作ができます。

## scope
よく使うクエリを、名前を付けて保存する機能です。
たとえば、「登録日が30日以内であるユーザの一覧を取得する」というクエリを、```registered_in_last_30_days```という名前で登録できます。

## validation
データの値が想定と合っているかを検証する機能です。
「データが空でない」、「文字数がn文字以下である」、「特定の文字列フォーマットにマッチする」などの条件で検証することができます。独自の条件を定義することもできます。

## callback
オブジェクトのライフサイクルの各段階において、指定した関数を実行させる機能です。
オブジェクトのライフサイクルとは、オブジェクトの初期化・作成・保存・更新・削除・バリデーション・読み込みといった一連の流れのことです。
たとえば「オブジェクトの更新」が成功した際に「指定のSlackチャンネルに通知する」という関数を実行させる、というように使います。

# 終わりに
Active Recordsにはさまざまな機能があります。
公式ドキュメントにはより詳しい解説が記載されています。
たくさんの機能を便利に使って効率的に開発していきましょう。
【2025年01月版】macOSで、偽装された容量のデバイスかどうかのチェック【容量偽装、SDカード、USBメモリ、SSD】

# はじめに

最近、USBメモリや、SDカード、SSDの容量偽装多すぎ。。。

`f3write` / `f3read` でチェクしても時間かかるし。。。

そうだ、`dd` で、指定位置に書き込みできるかどうかでチェックしよう

:::note info

f3write / f3read によるチェックの方法は以下の記事を参照

[【2023年8月版】macOS で f3 によるSDカードの容量偽装チェック【f3write/f3read】](https://qiita.com/nouernet/items/dcc3c3709457bab6515b)
:::

# 確認方法

## 前提条件

- デバイスは、`64G`だと公証されているとします。
- デバイスは、`/dev/disk4` にマウントされているものとします。
- デバイスがどこにマウントされているかは、以下のコマンドで確認します。
    ```
    % diskutil list
    ```

## 概要

- 指定の位置に文字列を書き込み
- デバイスを抜く
- デバイスを指す
- 指定の位置の文字列を読み込んで書き込み内容をチェック

### 指定の位置に文字列を書き込み

- ポジションは、`seek=$((63*1024*1024*1024))` で指定しますが、適当に1-2Gぐらい抜いておくと良いでしょう 

```terminal
% diskutil umountDisk /dev/disk4
% echo -n "hello" | sudo dd of=/dev/disk4 bs=1 seek=$(((63-1)*1024*1024*1024)) count=5
5+0 records in
5+0 records out
5 bytes transferred in 0.075170 secs (67 bytes/sec)
```

- 一旦書き込み内容を確認

```terminal
% sudo dd if=/dev/disk4 skip=$(((63-1)*1024*1024*1024)) bs=1 count=5 | hexdump -C
5+0 records in
5+0 records out
5 bytes transferred in 0.002430 secs (2058 bytes/sec)
00000000  68 65 6c 6c 6f                                    |hello|
00000005
```

### デバイスを抜く

- 以下のコマンドを実行した後にデバイスを抜きましょう

```terminal
$ diskutil umountDisk /dev/disk4
```

### デバイスを指す

- ふたたびデバイスを差し込んでください。

### 指定の位置の文字列を読み込んで書き込み内容をチェック

- 再び書き込み内容を確認

```terminal
$ sudo dd if=/dev/disk4 skip=$((63*1024*1024*1024)) bs=1 count=5 | hexdump -C
```

### 容量が偽装されていない場合

- 書き込み内容が確認できます。

```terminal
% sudo dd if=/dev/disk4 skip=$(((63-1)*1024*1024*1024)) bs=1 count=5 | hexdump -C
5+0 records in
5+0 records out
5 bytes transferred in 0.002430 secs (2058 bytes/sec)
00000000  68 65 6c 6c 6f                                    |hello|
00000005
```

### 容量が偽装されている場合

- 書き込み内容が確認できず、0 になります。

```terminal
% sudo dd if=/dev/disk4 skip=$(((63-1)*1024*1024*1024)) bs=1 count=5 | hexdump -C
5+0 records in
5+0 records out
5 bytes transferred in 0.062127 secs (80 bytes/sec)
00000000  00 00 00 00 00                                    |.....|
00000005
```

# さいごに

かんたんでしたね
認証/認可の仕組み

## **認証と認可の定義**

**認証**とは、APIにアクセスするユーザーやアプリケーションの身元を確認するプロセスである。つまり、「アクセスしようとする主体が本人であることを確認する」ということ[^1] [^2] [^3]。

**認可**とは、認証済みのユーザーやアプリケーションが、特定のAPIリソースへのアクセス権限を持っているかを判断するプロセス[^1] [^2]。

## **主な認証/認可仕組み**

1. **HTTP Basic Authentication**[^4]:
    - 認証の仕組みで認可の仕組みは含まない
    - ユーザー名とパスワードをBase64エンコードし、HTTPリクエストヘッダーに含める方式[^3] [^4]
    - 実装は容易だが、セキュリティレベルが低く、必ずHTTPSとの併用が必要[^3] [^5]
2. **APIキー認証**[^5]:
    - 認証の仕組みで認可の仕組みは含まない
    - 各ユーザーに固有のAPIキーを発行し、リクエストに含めて認証する方式[^3]
    - キーへの有効期限設定によるセキュリティ強化が可能[^3]
3. OAuth（**Open Authorization**） **2.0**[^6]:
    - 認可の仕組みであり、場合によって認証のフローも含む
    - サードパーティアプリケーションが、ユーザーの代理としてAPIにアクセスするための権限を取得するための標準プロトコル[^3]
    - ユーザーが明示的にリソースアクセス権限を付与でき、高い安全性を実現可能[^3]

## **認証/認可の基本フロー**

1. クライアントによるAPIの呼び出し、アクセストークン（Authorizationヘッダーなど）の提示[^1]
2. リソースサーバー（API）によるアクセストークンの検証[^1]
3. トークン検証結果に基づくバックエンドAPIリソースへのアクセス許可[^1]

```mermaid
sequenceDiagram
    participant C as クライアント
    participant A as APIサーバー
    participant B as バックエンドリソース

    C->>A: 1. APIリクエスト + アクセストークン提示
    Note over A: 2. トークンの検証
    alt トークン有効
        A->>B: 3. リソースアクセス許可
        B-->>C: レスポンス
    else トークン無効
        A-->>C: エラーレスポンス
    end
```

## **セキュリティの考慮事項**

- 認証と認可の強化のため、TLSによるAPIアクセスの保護、認証・認可用資格情報やトークンの適切な保護が必要[^1]
- APIキー認証採用時は、キー漏洩リスク対策としてのHTTPSによる通信暗号化が不可欠[^3]

適切な認証/認可メカニズムの実装による不正アクセスの防止、データとリソースのセキュリティ確保が可能である。

## 参照
[^1] [^2] [^3] [^4] [^5] [^6]
[^1]: https://learn.microsoft.com/ja-jp/azure/api-management/authentication-authorization-overview
[^2]: https://developers.google.com/workspace/guides/auth-overview?hl=ja
[^3]: https://qiita.com/MaSi1031/items/1b1a3258f119a5d43045
[^4]: [HTTP Basic Authenticationの概要と仕組み](https://qiita.com/SHOT0/items/5802b61c0c5772f64ed8) 
[^5]: [HTTPSの概要と仕組み](https://qiita.com/SHOT0/items/cae08a65be065ba07acd) 
[^6]: [OAuthの概要と仕組み](https://qiita.com/SHOT0/items/18f59b1612959c72ea14)
ポリモーフィズムの和訳は「多態性」ではなく「インターフェース共通性」にすべき

## はじめに
スキルアップのため本格的にプログラミングを学習し始めた保守系ESです。
勉強していて「初めからこう言ってくれれば分かりやすかったのに！」というところをまとめていきたいです。

## 多態性（ポリモーフィズム）とは？

#### ChatGPTに聞いてみた。
~~~
ポリモーフィズム（多態性）とは、オブジェクト指向プログラミングにおいて、
異なる型のオブジェクトが同じインターフェースを通じて共通の操作を実行できる特性のことを指します。

これにより、プログラムを柔軟かつ再利用可能に設計することが可能になります。
~~~

ChatGPTの答えが全てなのですが、ポリモーフィズムの真髄は **「異なるオブジェクトが、同じ操作で動かせること」** です。

## なんで「多態性」なんて名前にしたんだ…

「ポリ（多）モーフ（morph＝異形態）ィズム（性）」

の直訳で「多態性」というのも分かりますし、

「**（共通性を保ちながら）** 様々な形態をとれる」

というのも分かりますが…。

こと、プログラミングにおいてはChatGPTの解答よろしく、単語としても「共通性」の方に着目したほうが、そのありがたみが分かりやすいですよね…。



※多態性の活用方法はGoFのデザインパターンを学びましょう。
[増補改訂版Java言語で学ぶデザインパターン入門](https://www.amazon.co.jp/%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%89%88Java%E8%A8%80%E8%AA%9E%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E5%85%A5%E9%96%80-%E7%B5%90%E5%9F%8E-%E6%B5%A9/dp/4797327030)

以上、ド初心者の愚痴でした。
cocolympic問題

# 問題
cocolym.txtの内容を整形し以下の文字列のみを抽出する。
```
CoCoTo Happy 10th anniversary
Relationship
Think
Act
Check
```
#### step1
cocolym.txtから重複行を削除して下さい。
```
-----重複行を削除した出力例-----
'yrasrevinna ht01 yppaH oToCoC',
'shipRelation',
'T',
' h',
'  i',
'   n',
'    k',
'A.,.c　　t',
'Aheaak'
```

#### Goal
文字列を以下のように整形してreturn answerTxtで結果を返して下さい。
```
CoCoTo Happy 10th anniversary
Relationship
Think
Act
Check
```

#### 記載する箇所
「index.js」の★の部分に処理を記載してください。
```
module.exports = function (txt) {
    //(txt)に元データのcocolym.txt 2026行の文字列を読み込んでいます

　　★ここに重複行を削除、整形して文字列のみ出力する処理を記載する
  
    // return answerString
};
```

#### README.mdに従ってPR作成
https://github.com/cocolympic/benchmark/blob/main/README.md

※PRのマージはしないようにお願いします。
※PR時にbaseブランチを「cocolympic_pr_target」にしてください

#### 禁止事項
こんなことはありえないと思いますが、回答の文字列を直接入力することは当然禁止です:frowning2:
```
module.exports = function (txt) {
    // 処理(txt = resource/cocolym.txt)
    const answerString = CoCoTo Happy 10th anniversary\nRelationship\nThink\nAct\nCheck
    return answerString
};
```
# 結果発表🥇🥈🥉
:::note
### 1位🥇
処理時間：0.12887 ms
コード12行
```
module.exports = function (txt) {
    // 処理(txt = resource/cocolym.txt)
    let answerString = [];
    const remove_str = '\n'+txt.substr(0,txt.indexOf(`\n`));
    const arr = txt.replaceAll(remove_str, '').split(/\n/);
    answerString[0] = arr[0].split("").reverse().join("");
    answerString[1] = arr[1].split(/(?=[A-Z])/).sort().join('').replace(/,/g, '');
    answerString[2] = arr.slice(2, 7).join('').replace(/\s+/g, '');
    answerString[3] = arr[7].replace(/[^a-zA-Z]/g, '');
    answerString[4] = arr[8].replace(/A/g, 'C').replace(/a/g, 'c');
    return answerString.join("\n");
};
```
:::


:::note
### 2位🥈
処理時間：0.18899 ms
コード11行
```
module.exports = function (txt) {
    // 処理(txt = resource/cocolym.txt)
    let answerString = "";
    const tempString = txt.split('\n').filter((x, i, self) => self.indexOf(x) === i);
    answerString += tempString[0].split('').reverse().join('') + '\n';
    answerString += tempString[1].split(/(?=[A-Z])/).reverse().join('').replace(/,/g, '') + '\n';
    answerString += tempString.slice(2, 7).join('').replace(/\s+/g, '') + '\n';
    answerString += tempString[7].replace(/[^a-zA-Z]/g, '') + '\n';
    answerString += tempString[8].replace(/A/g, 'C').replace(/a/g, 'c');
    return answerString
};
```
:::

:::note
### 3位🥉
処理時間：0.19594 ms
コード10行
```
module.exports = function (txt) {
    // 処理(txt = resource/cocolym.txt)
    const tmp1 = txt.split(/\n/).filter((text, index, self) => self.indexOf(text) === index);
    let result = tmp1[0].split("").reverse().join("") + '\n'
      + tmp1[1].substr(4) + tmp1[1].substr(0, 4) + '\n'
      + tmp1.slice(2, 7).join('').replace(/\s+/g, '') + '\n'
      + tmp1[7].replace(/[\s,.]/g, '') + '\n'
      + tmp1[8].replace(/[A]/, 'C').replace(/[a]/, 'c');
    return result;
};
```
:::
eslintでimport文の順序整列を行う

サクッとviteでアプリを作るときに、なんだかんだ欲しいなと思ったりするも「どうやるんだっけ...」となるため、備忘録として残しておこうと思います。

```sh
yarn add -D eslint-plugin-import eslint-import-resolver-typescript
```


```diff
 module.exports = {
   extends: [
     "eslint:recommended",
     "plugin:@typescript-eslint/recommended",
     "plugin:react/recommended",
     "plugin:react/jsx-runtime",
     "plugin:react-hooks/recommended",
+    "plugin:import/recommended",
+    "plugin:import/typescript",
     "plugin:prettier/recommended",
   ],
   rules: {
+    "import/order": [
+      "error",
+      {
+        alphabetize: {
+          order: "asc",
+        },
+        "newlines-between": "always",
+      },
+    ],
   },
 };
```