【Rails】Rails APIで新規登録、退会処理

前回、Rails APIでUser周りの実装をしました。
[【Rails】JWTを用いたUser周りのAPI実装](https://qiita.com/kay903347/items/3847a5cf4642e122128c)
その記事の新規登録、退会処理で気になったことをまとめます。
先にmodel、controllerを貼り付けておきます。
```ruby:models/usr.rb
class User < ApplicationRecord
  before_save { self.email = email.downcase }
  validates :name, presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX },
                    uniqueness: true
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }
end
```
```ruby:controllers/api/v1/users_controller.rb
module Api
  module V1
    class UsersController < ApiController
      # ユーザー登録
      def create
        user = User.new(user_params)
        if user.save
          render json: { message: "ユーザー登録が完了しました", user: {id: user.id, name: user.name, email: user.email} }, status: :created
        else
          render json: { errors: user.errors.full_messages }, status: :unprocessable_entity
        end
      end

      # プロフィール削除（退会処理）
      def destroy
        user = current_user
        if user && user.destroy
          render json: { message: "アカウントが削除されました。" }, status: :ok
        else
          render json: { error: "アカウントの削除に失敗しました" }, status: :unprocessable_entity
        end
      end

      private

      # ユーザー登録時のパラメータ
      def user_params
        params.require(:user).permit(:name, :email, :password, :password_confirmation)
      end
      wrap_parameters :user, include: [:name, :email, :password, :password_confirmation]
    end
  end
end
```
## has_secure_password
RailsのActiveRecordモデルで、ユーザーのパスワードを安全に管理するためのメソッドです。このメソッドを使用すると、パスワードのハッシュ化、パスワード確認、認証機能などが自動的に提供され、セキュリティ強化が簡単に行えます。
#### has_secure_passwordの前提条件
* bcrypt Gemが必要
bcryptというGemが必要です。このGemは、パスワードのハッシュ化と比較を行うために使用されます。Gemfileに以下を追加し、bundle installでインストールします。
```
gem 'bcrypt', '~> 3.1.7'
```
* password_digestカラムが必要
モデルのデータベーステーブルにpassword_digestという名前のカラムを必要とします。このカラムにハッシュ化されたパスワードが保存されます。
#### 主な機能と動作
has_secure_passwordをモデルに追加するだけで、次の機能が自動的に利用できるようになります。
* パスワードのハッシュ化
    * ユーザーがパスワードを設定（password）すると、bcryptによってハッシュ化され、password_digestカラムに保存されます
    * ハッシュ化されるため、実際のパスワード文字列はデータベースに保存されません。データベースに保存されるのはハッシュ化された文字列のみです
* passwordとpassword_confirmation属性
    * passwordとpassword_confirmationという仮想属性がモデルに追加されます
    * ユーザーが新しいパスワードを設定するときに、password_confirmationに同じパスワードを入力することで、パスワードの一致を確認できます
    * passwordとpassword_confirmationが一致しない場合、バリデーションエラーが発生します
* 認証機能
    * authenticateメソッドが自動的に提供されます。このメソッドにパスワードを渡して、ユーザーの認証を行います
    * authenticateは、渡されたパスワードをハッシュ化し、password_digestと比較します。一致すればユーザーオブジェクトを返し、不一致ならfalseを返します。
```
user = User.find_by(email: params[:email])
if user&.authenticate(params[:password])
  # 認証成功
else
  # 認証失敗
end
```
## user_params
RailsのStrong Parametersという仕組みを使って、ユーザーからのリクエストパラメータを安全にフィルタリングするためのメソッドです。
ユーザーが送信するデータ（params）のうち、許可された属性のみを受け取り、他の属性を除外することで、不正なデータがモデルに渡されないようにします。
#### params.require(:user)
paramsは、コントローラが受け取るリクエストのデータが含まれるオブジェクトです。たとえば、フォーム送信やAPIリクエストから送られた情報はすべてparamsで受け取ります。
* require(:user)は、paramsオブジェクトから:userというキーを必須項目として要求します。この例では、リクエストデータがuserというキーの下に存在することが前提になっています
* require(:user)がない場合、:userキーが存在しないときにエラーを発生させることができず、モデルに想定外のデータが渡ってしまう可能性があります

#### permit(:name, :email, :password, :password_confirmation)
permitは、paramsの中で許可したいキー（属性）を指定します。この例では、:name、:email、:password、:password_confirmationのみを許可しています。

* 許可された属性のみが最終的にuser_paramsメソッドから返され、User.newメソッドに渡されます
* 許可されていない属性は自動的に削除されるため、予期しないパラメータの混入や、悪意のあるデータの挿入を防ぐことができます
## wrap_parameters
Railsのコントローラにおいてリクエストパラメータのラップ（ラッピング）を行うためのメソッドです。これにより、JSONリクエストのデータが適切な形で扱いやすくなります。
## 背景と目的
JSON形式でデータを送信するとき、リクエストボディが特定のキーにラップされていることを期待することがあります。wrap_parametersを使うことで、外部APIやクライアントから受け取るデータの形式に柔軟に対応できます。
クライアントから次のようなJSONデータが送信されるとします：
```json:json
{
  "name": "John Doe",
  "email": "john@example.com",
  "password": "secret123"
}
```
Railsのparamsはデフォルトでデータがモデル名にラップされることを期待するため、次のようにラップされていることが理想です：
```json:json
{
  "user": {
    "name": "John Doe",
    "email": "john@example.com",
    "password": "secret123"
  }
}
```
wrap_parametersを使用すると、クライアントがこのラップされた形式で送信しなくても、Rails側で自動的にラップして処理できるようになります。
#### 使用方法
wrap_parametersは、コントローラ内部やアプリケーション全体の設定で利用できます。コントローラ内で使用する場合は以下のように書きます：
```ruby:ruby
class Api::V1::UsersController < ApplicationController
  wrap_parameters :user, include: [:name, :email, :password, :password_confirmation]
end
```
* wrap_parameters :user
:userの部分は、どのキーでラップするかを指定します。この場合、ラップ後のデータがparams[:user]として扱えるようになります。
* include: [:name, :email, :password, :password_confirmation]
includeオプションで、ラップする際に含める属性を指定します。ここで指定した属性が含まれるJSONデータのみがラップされ、params[:user][:name]といったアクセスが可能になります。