[{"content":"AWSのセキュリティグループ（Security Group、以下SG）を整理するときに、どのような確認が必要なのかを学習用・検証用の環境を想定して整理する。\nはじめに SGは、EC2などのAWSリソースに対する通信を制御する仮想ファイアウォールです。SGにはインバウンドルールとアウトバウンドルールがあり、どの送信元からどのポートへの通信を許可するかを定義します。\nSGを整理していると、広い範囲からのアクセスを許可するルールや、用途が分からないSGを見つけることがあります。しかし、ルールが広いからといって、すぐに削除してよいとは限りません。\n削除前には、次の2つを分けて考える必要があります。\nその通信ルールは業務上必要なのか そのSGは現在どのリソースに関連付けられているのか 0.0.0.0/0とは 0.0.0.0/0は、すべてのIPv4アドレスを表すCIDRです。SGのインバウンドルールでこの値を指定すると、指定したポートへの通信を、送信元IPアドレスによらず許可することになります。\nたとえば、次のルールは世界中のIPv4アドレスからSSH接続を許可します。\nプロトコル ポート 送信元 意味 TCP 22 0.0.0.0/0 すべてのIPv4アドレスからSSHを許可 このような設定では、インターネット上のスキャンや攻撃の対象になる可能性が高くなります。特にSSH（22番）やRDP（3389番）、データベースのポートなどを全世界に公開するのは危険です。\n一方で、公開WebサイトのHTTP（80番）やHTTPS（443番）では、利用者の送信元IPを事前に限定できないため、0.0.0.0/0を使うことがあります。この場合も、Webサーバーやロードバランサーなど、公開が意図されたリソースにだけ関連付けることが重要です。\nまた、今回問題にしたいのは主にインバウンドルールです。アウトバウンドの0.0.0.0/0は、リソースからインターネット上のサービスへ接続するために使われることがあり、インバウンドと同じ意味で単純に判断できません。\n未使用のSGを整理する理由 未使用のSGを残しておくと、今すぐ通信が発生するわけではありません。しかし、将来そのSGを誤ってEC2やENIなどに関連付けたとき、広い範囲からのアクセスを許可する設定が意図せず有効になる可能性があります。\nまた、不要なSGが多いと、名前やルールを見ただけでは「現在使われている設定なのか」「過去の作業の残りなのか」を判断しにくくなります。これは、設定ミスや変更時の事故につながります。\nそのため、未使用であることを確認できたSGは、削除または廃止候補として整理する価値があります。\n本当に使われていないかを確認する SGを削除する前に、少なくとも次の観点で確認します。\nEC2インスタンスやENIに関連付けられていないか 他のSGのルールから参照されていないか ロードバランサー、RDS、ECSなどのAWSサービスで使われていないか Auto Scaling、起動テンプレート、CloudFormationなどで指定されていないか TerraformやAWS CDKなどのIaCで管理されていないか 今後必要になる予定のSGではないか SGのルールを確認する まず、対象SGの所属VPCやルールを確認します。以下は実際の環境で実行する場合の形式例です。値は自分の確認対象に置き換えます。\n1 2 3 4 aws ec2 describe-security-groups \\ --group-ids sg-xxxxxxxxxxxxxxxxx \\ --profile \u0026lt;profile\u0026gt; \\ --region \u0026lt;region\u0026gt; IpPermissionsがインバウンドルール、IpPermissionsEgressがアウトバウンドルールです。0.0.0.0/0だけでなく、::/0というIPv6の全アドレスを表すルールも確認します。\nENIへの関連付けを確認する SGはEC2インスタンスだけでなく、Elastic Network Interface（ENI）に関連付けられます。ロードバランサーやRDS、ECSなど、AWSサービスが管理するENIに関連付けられていることもあります。\n1 2 3 4 5 aws ec2 describe-network-interfaces \\ --filters Name=group-id,Values=sg-xxxxxxxxxxxxxxxxx \\ --query \u0026#39;NetworkInterfaces[].{ENI:NetworkInterfaceId,Status:Status,Type:InterfaceType,Description:Description,Instance:Attachment.InstanceId}\u0026#39; \\ --profile \u0026lt;profile\u0026gt; \\ --region \u0026lt;region\u0026gt; 結果が空であれば、少なくとも指定したSGを持つENIは見つかりませんでした。ただし、ENIがないことだけで、IaCやAWSサービスの設定から完全に不要だと断定することはできません。\n他のSGから参照されていないか確認する SGは、IPアドレスではなく別のSGを送信元として指定できます。たとえば、アプリケーションサーバーのSGからデータベースのSGへの通信を許可する構成です。\n次のコマンドで、対象SGを参照しているSGを確認します。\n1 2 3 4 5 aws ec2 describe-security-groups \\ --filters Name=ip-permission.group-id,Values=sg-xxxxxxxxxxxxxxxxx \\ --query \u0026#39;SecurityGroups[].{GroupId:GroupId,GroupName:GroupName,VpcId:VpcId}\u0026#39; \\ --profile \u0026lt;profile\u0026gt; \\ --region \u0026lt;region\u0026gt; ここで結果が返る場合、対象SGを削除すると参照元のルールも成立しなくなるため、先に参照関係を確認します。\nIaCや設定ファイルから参照されていないか確認する リポジトリを管理している場合は、SG IDやSG名を検索します。\n1 rg \u0026#39;sg-xxxxxxxxxxxxxxxxx|対象SGの名前\u0026#39; . ただし、SG IDを直接書かずにTerraformのリソース参照やCloudFormationのパラメータで渡している場合もあります。そのため、文字列検索だけでなく、IaCの依存関係やデプロイ設定も確認する必要があります。\n安全に削除する 関連付けや参照がなく、IaCや運用上も不要だと判断できた場合に削除します。実環境で実行する前に、対象のアカウント・リージョン・SG IDが正しいことを確認します。\n1 2 3 4 aws ec2 delete-security-group \\ --group-id sg-xxxxxxxxxxxxxxxxx \\ --profile \u0026lt;profile\u0026gt; \\ --region \u0026lt;region\u0026gt; 削除後は、対象SGが取得できなくなったことを確認します。\n1 2 3 4 aws ec2 describe-security-groups \\ --group-ids sg-xxxxxxxxxxxxxxxxx \\ --profile \u0026lt;profile\u0026gt; \\ --region \u0026lt;region\u0026gt; 削除済みであれば、通常はInvalidGroup.NotFoundのようなエラーになります。\n本番環境での削除に不安がある場合は、まずSGにタグを付けて廃止候補として記録し、一定期間利用されていないことを確認してから削除する方法もあります。\nおまけ：AWS CLIのプロファイル一覧を確認する AWS CLIでは、複数のAWSアカウントや環境をプロファイルとして切り替えて利用できます。登録済みのプロファイル一覧は、次のコマンドで確認できます。\n1 aws configure list-profiles たとえば、次のようなプロファイルが表示されます。\n1 2 3 default dev production コマンドを実行するときは、--profileで使用するプロファイルを指定します。\n1 aws sts get-caller-identity --profile dev 削除や変更を行う前に、意図したAWSアカウントへ接続しているかをaws sts get-caller-identityで確認すると、対象アカウントを間違える事故を防ぎやすくなります。\nDependencyViolationが出た場合 SGがリソースや別のSGから参照されている状態で削除しようとすると、DependencyViolationが発生することがあります。\nこのエラーが出た場合は、削除コマンドを繰り返すのではなく、次の依存関係を再確認します。\nEC2インスタンスやENIへの関連付け ロードバランサーやRDS、ECSなどのサービス管理リソース 別のSGのインバウンドルールからの参照 起動テンプレートやAuto Scalingの設定 CloudFormation、Terraform、CDKなどのIaC AWS側で削除を拒否してくれることは安全装置になりますが、「削除できないから使われている場所が明らか」とは限りません。依存関係を特定してから、必要なものを付け替えるか、不要な参照を削除します。\n今回の整理 SGを整理するときは、0.0.0.0/0があるかどうかだけで、機械的に削除対象を決めないことが重要です。\n次の順番で確認すると、判断しやすくなります。\nインバウンド・アウトバウンドのルールを確認する 公開が意図されたポートか確認する ENIやAWSサービスへの関連付けを確認する 他のSGからの参照を確認する IaCや起動設定からの参照を確認する 廃止候補として記録し、必要なら一定期間観察する 不要と判断できたSGを削除する まとめ 0.0.0.0/0はすべてのIPv4アドレスを表す 0.0.0.0/0のインバウンドルールは、指定ポートをインターネット全体に公開する設定になり得る ただし、HTTPやHTTPSなど、意図的な公開が必要なケースもある SGの削除前には、ENI、他のSG、AWSサービス、IaCを確認する SG IDの文字列検索だけでは不十分で、AWS上の関連付けも確認する必要がある 未使用のSGは、廃止候補として記録してから削除すると安全性を高められる 参考 AWS VPC セキュリティグループの操作 AWS CLI describe-network-interfaces AWS Security Hub EC2セキュリティコントロール ","permalink":"https://makiharu.github.io/persona-blog/tech/03-sg%E3%82%92%E5%89%8A%E9%99%A4%E3%81%97%E3%81%9F%E8%A9%B1/","summary":"\u003cp\u003eAWSのセキュリティグループ（Security Group、以下SG）を整理するときに、どのような確認が必要なのかを学習用・検証用の環境を想定して整理する。\u003c/p\u003e\n\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003eSGは、EC2などのAWSリソースに対する通信を制御する仮想ファイアウォールです。SGにはインバウンドルールとアウトバウンドルールがあり、どの送信元からどのポートへの通信を許可するかを定義します。\u003c/p\u003e\n\u003cp\u003eSGを整理していると、広い範囲からのアクセスを許可するルールや、用途が分からないSGを見つけることがあります。しかし、ルールが広いからといって、すぐに削除してよいとは限りません。\u003c/p\u003e\n\u003cp\u003e削除前には、次の2つを分けて考える必要があります。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eその通信ルールは業務上必要なのか\u003c/li\u003e\n\u003cli\u003eそのSGは現在どのリソースに関連付けられているのか\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"00000とは\"\u003e\u003ccode\u003e0.0.0.0/0\u003c/code\u003eとは\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e0.0.0.0/0\u003c/code\u003eは、すべてのIPv4アドレスを表すCIDRです。SGのインバウンドルールでこの値を指定すると、指定したポートへの通信を、送信元IPアドレスによらず許可することになります。\u003c/p\u003e\n\u003cp\u003eたとえば、次のルールは世界中のIPv4アドレスからSSH接続を許可します。\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eプロトコル\u003c/th\u003e\n          \u003cth style=\"text-align: right\"\u003eポート\u003c/th\u003e\n          \u003cth\u003e送信元\u003c/th\u003e\n          \u003cth\u003e意味\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eTCP\u003c/td\u003e\n          \u003ctd style=\"text-align: right\"\u003e22\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e0.0.0.0/0\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eすべてのIPv4アドレスからSSHを許可\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eこのような設定では、インターネット上のスキャンや攻撃の対象になる可能性が高くなります。特にSSH（22番）やRDP（3389番）、データベースのポートなどを全世界に公開するのは危険です。\u003c/p\u003e\n\u003cp\u003e一方で、公開WebサイトのHTTP（80番）やHTTPS（443番）では、利用者の送信元IPを事前に限定できないため、\u003ccode\u003e0.0.0.0/0\u003c/code\u003eを使うことがあります。この場合も、Webサーバーやロードバランサーなど、公開が意図されたリソースにだけ関連付けることが重要です。\u003c/p\u003e\n\u003cp\u003eまた、今回問題にしたいのは主にインバウンドルールです。アウトバウンドの\u003ccode\u003e0.0.0.0/0\u003c/code\u003eは、リソースからインターネット上のサービスへ接続するために使われることがあり、インバウンドと同じ意味で単純に判断できません。\u003c/p\u003e\n\u003ch2 id=\"未使用のsgを整理する理由\"\u003e未使用のSGを整理する理由\u003c/h2\u003e\n\u003cp\u003e未使用のSGを残しておくと、今すぐ通信が発生するわけではありません。しかし、将来そのSGを誤ってEC2やENIなどに関連付けたとき、広い範囲からのアクセスを許可する設定が意図せず有効になる可能性があります。\u003c/p\u003e\n\u003cp\u003eまた、不要なSGが多いと、名前やルールを見ただけでは「現在使われている設定なのか」「過去の作業の残りなのか」を判断しにくくなります。これは、設定ミスや変更時の事故につながります。\u003c/p\u003e\n\u003cp\u003eそのため、未使用であることを確認できたSGは、削除または廃止候補として整理する価値があります。\u003c/p\u003e\n\u003ch2 id=\"本当に使われていないかを確認する\"\u003e本当に使われていないかを確認する\u003c/h2\u003e\n\u003cp\u003eSGを削除する前に、少なくとも次の観点で確認します。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eEC2インスタンスやENIに関連付けられていないか\u003c/li\u003e\n\u003cli\u003e他のSGのルールから参照されていないか\u003c/li\u003e\n\u003cli\u003eロードバランサー、RDS、ECSなどのAWSサービスで使われていないか\u003c/li\u003e\n\u003cli\u003eAuto Scaling、起動テンプレート、CloudFormationなどで指定されていないか\u003c/li\u003e\n\u003cli\u003eTerraformやAWS CDKなどのIaCで管理されていないか\u003c/li\u003e\n\u003cli\u003e今後必要になる予定のSGではないか\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"sgのルールを確認する\"\u003eSGのルールを確認する\u003c/h3\u003e\n\u003cp\u003eまず、対象SGの所属VPCやルールを確認します。以下は実際の環境で実行する場合の形式例です。値は自分の確認対象に置き換えます。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e3\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e4\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eaws ec2 describe-security-groups \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  --group-ids sg-xxxxxxxxxxxxxxxxx \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  --profile \u0026lt;profile\u0026gt; \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  --region \u0026lt;region\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e\u003ccode\u003eIpPermissions\u003c/code\u003eがインバウンドルール、\u003ccode\u003eIpPermissionsEgress\u003c/code\u003eがアウトバウンドルールです。\u003ccode\u003e0.0.0.0/0\u003c/code\u003eだけでなく、\u003ccode\u003e::/0\u003c/code\u003eというIPv6の全アドレスを表すルールも確認します。\u003c/p\u003e\n\u003ch3 id=\"eniへの関連付けを確認する\"\u003eENIへの関連付けを確認する\u003c/h3\u003e\n\u003cp\u003eSGはEC2インスタンスだけでなく、Elastic Network Interface（ENI）に関連付けられます。ロードバランサーやRDS、ECSなど、AWSサービスが管理するENIに関連付けられていることもあります。\u003c/p\u003e","title":"AWSセキュリティグループの削除前に確認すること"},{"content":"AWSのユーザー管理と、MFAを強制する方法について調べたことのアウトプット。\nはじめに 「各ユーザーにMFAを必須にするにはどうすればよいか」「IAMユーザーとIAM Identity Centerはどう使い分けるのか」が気になったので調べました。\nこの記事では、AWSのベストプラクティスをもとに、次の点を整理します。\nIAMユーザーとIAM Identity Centerの違い IAM Identity CenterでMFAを強制する方法 IAMユーザーを残す場合のMFA対策 MFAとアクセスキーの関係 まず結論 人間がAWSコンソールやAWS CLIを使う場合は、長期的な認証情報を持つIAMユーザーよりも、フェデレーションによる一時的な認証情報を使う構成が推奨されています。複数のAWSアカウントを組織で管理する場合は、IAM Identity Centerを使うのが代表的な方法です。\nただし、IAM Identity Centerを導入すればMFAが自動的に必須になるわけではありません。IAM Identity Centerの設定で、ユーザーにMFAデバイスの登録と利用を要求する必要があります。\nまた、IAMユーザーにMFAを設定しても、アクセスキーによるCLI・API操作が自動的にMFA必須になるわけではありません。MFAとアクセスキーは別の論点として考える必要があります。\nIAMユーザーとIAM Identity Centerの違い IAMユーザー IAMユーザーは、特定のAWSアカウント内に作成するユーザーです。ユーザーごとにコンソールのパスワードやアクセスキーなどの認証情報を持たせることができます。\n一方で、IAMユーザーの認証情報は長期間有効になる可能性があります。退職や異動の際に、ユーザー・パスワード・アクセスキー・MFAデバイスなどを漏れなく無効化・削除する必要があり、運用での管理負担が大きくなります。\nそのため、IAMユーザーは「古いから絶対に使えない」というものではありませんが、人間のユーザー用には原則として第一の選択肢にしない、という位置づけです。特定のツールとの互換性など、フェデレーションを利用できないケースでは、例外的に利用します。\nIAM Identity Center IAM Identity Centerは、組織内のユーザーやグループに対して、複数のAWSアカウントへのアクセスをまとめて割り当てるための仕組みです。\nユーザーの認証元には、次のような選択肢があります。\nIAM Identity Center内のディレクトリ AWS Managed Microsoft ADやAD Connector Microsoft Entra ID、Okta、Google Workspaceなどの外部IdP ユーザーがAWSにサインインすると、割り当てられたPermission SetをもとにAWSアカウント内のIAMロールを利用します。人間が長期的なIAMユーザーのアクセスキーを持ち続けるのではなく、必要な期間だけ使える一時的な認証情報を取得する点が重要です。\nフェデレーションとは フェデレーションは、AWSの外部にある認証基盤でユーザーを認証し、その結果を使ってAWSのロールを利用する仕組みです。\nたとえば、次のような流れになります。\n1 2 3 4 5 6 7 会社の認証基盤（Microsoft Entra ID、Okta、Google Workspaceなど） ↓ ユーザーを認証 IAM Identity Center ↓ Permission Setに対応する権限を割り当て AWSアカウント内のIAMロール ↓ 一時的な認証情報でAWSを操作 図の左側が「認証」、右側が「認可」です。外部IdPで本人確認を行ったあと、Permission SetをもとにIAMロールを利用し、期限付きの一時認証情報でAWSリソースを操作します。\n認証を会社のID基盤に任せ、AWS側ではユーザーやグループへのアクセス割り当てと、ロールに相当する権限を管理します。そのため、退職者のアカウントをAWSアカウントごとに探して削除するよりも、会社のID基盤側でアカウントを無効化しやすくなります。\nただし、実際にどこでユーザーを無効化するかは、採用するアイデンティティソースの構成によって変わります。IAM Identity Center内でユーザーを管理するのか、外部IdP側で管理するのかをあらかじめ決める必要があります。\nIAM Identity CenterでMFAを強制する IAM Identity Centerでは、設定画面のAuthenticationからMFAを設定できます。ユーザーがMFAデバイスを登録していない場合に、サインイン時の登録を必須にする設定が代表的です。\nただし、MFAの設定場所はアイデンティティソースによって異なります。IAM Identity Centerのディレクトリなどを使う場合はIAM Identity Center側で設定しますが、Microsoft Entra IDやOktaなどの外部IdPを使う場合は、基本的に外部IdP側のMFAポリシーで制御します。\n大まかな流れは次のとおりです。\nIAM Identity Centerの設定を開く AuthenticationからMFAの設定を開く 利用可能なMFA方式を設定する MFAデバイス未登録のユーザーに、サインイン時の登録を要求する 必要に応じて、既存のMFAユーザーにもサインイン時の再認証を要求する これにより、IAM Identity Center経由でAWSへアクセスするユーザーに対して、MFAを前提としたログインフローを構成できます。\nIAMユーザーのMFAを強制する場合 すでにIAMユーザーを利用していて、すぐに構成を変更できない場合は、まずIAMユーザーにMFAを設定します。これは短期的な止血として有効です。\nさらに、IAMポリシーで次のような制御を行い、MFAを設定していないユーザーが通常のAWS操作を実行できないようにする方法があります。\nMFAデバイスの登録・変更など、MFA設定に必要な操作だけを許可する MFA未使用の場合は、通常のAWS操作を拒否する ルートユーザーにもMFAを設定する 不要なIAMユーザー、パスワード、アクセスキーを棚卸しして削除する ただし、ここで注意が必要です。コンソールへのログイン時にMFAを要求することと、AWS CLI・APIの操作時にMFAを要求することは別の制御です。\nMFAとアクセスキーは別に考える IAMユーザーにMFAデバイスを登録しても、そのユーザーの長期アクセスキーが残っていれば、アクセスキーを使ったCLI・API操作が可能な場合があります。\nつまり、次の2つは別々に確認する必要があります。\n確認対象 確認すること コンソールログイン パスワードに加えてMFAを要求しているか CLI・API操作 長期アクセスキーを使っていないか。必要ならMFA付きの一時認証情報を要求しているか 人間がCLIやSDKを利用する場合も、IAM Identity Centerから一時的な認証情報を取得する方法があります。長期アクセスキーを個人に配布する必要がなくなるため、アクセスキーの漏洩や退職時の回収漏れといったリスクを下げられます。\n今回の整理 今回調べた内容を、運用の優先順位として整理すると次のようになります。\n既存のIAMユーザーとアクセスキーを棚卸しする すぐに変更できる範囲で、IAMユーザーとルートユーザーにMFAを設定する IAMユーザーのコンソールログインにMFAを強制する 長期アクセスキーを利用しているユーザーやツールを確認する 人間のユーザーはIAM Identity Centerと一時的な認証情報へ移行する IAMユーザーを残す場合は、利用目的を明確にして定期的に見直す まとめ 人間のユーザーには、長期認証情報を持つIAMユーザーより、フェデレーションと一時的な認証情報を使う構成が推奨されている 複数AWSアカウントのユーザーアクセスを一元管理する場合、IAM Identity Centerが有力な選択肢になる IAM Identity Centerでも、設定画面でMFAの登録・利用を要求する必要がある IAMユーザーのMFA設定と、アクセスキーによるCLI・API操作の制御は別に考える IAMユーザーは完全に不要なのではなく、フェデレーションを利用できない場合などに限定して使う 参考 AWS IAMのセキュリティのベストプラクティス IAM Identity CenterでMFAデバイスの強制を設定する IAM Identity CenterユーザーのAWS CLI・SDK用認証情報を取得する ","permalink":"https://makiharu.github.io/persona-blog/tech/02/","summary":"外部ファイルなしでMarkdownに画像を埋め込む方法のメモ。Data URIスキームを使う。","title":"AWSでMFAを強制する方法とIAM Identity Centerの使い分け"},{"content":"MIMEについて学んだことのアウトプット\nはじめに 最近SMTPプロトコルとMIMEについて勉強したので、理解の整理を兼ねてアウトプットします。「メールって普通に日本語も画像も送れるけど、裏側では何が起きているんだろう?」という疑問からスタートしました。\nSMTPはASCII文字しか扱えない SMTP(Simple Mail Transfer Protocol)はメールを送信するためのプロトコルですが、もともとはASCII文字(7bit)しか安全に運べないという制約を持っていました。\nつまり素のSMTPでは、\n日本語などの非ASCII文字 画像やPDFなどのバイナリデータ をそのまま送ることができません。これではメールとして不便すぎるので、この制約を解消するために生まれたのがMIMEです。\nMIMEはメールのフォーマットを拡張する仕組み MIME(Multipurpose Internet Mail Extensions)は、SMTPそのものを拡張するプロトコルというよりは、メールメッセージのフォーマットを拡張する規格です。SMTPは「メールを転送する仕組み」、メール本文の書式は別の規格(RFC 5322)で定義されており、MIMEはこの書式を拡張して、テキスト以外のデータ種別やエンコーディング方式を表現できるようにしています。\nMIMEは主に次のようなヘッダーで構成されます。\nContent-Type:データの種類を示す(text/plain、image/png、application/pdf、複数の要素を含む場合はmultipart/mixedなど) Content-Transfer-Encoding:データをASCII文字だけの形にどう変換したかを示す(Base64、Quoted-Printable、7bitなど) charset:文字コードの指定(UTF-8、ISO-2022-JPなど) 変換の流れ:文字コード変換 → 転送用エンコード 日本語のテキストを送る場合、実は2段階の変換が行われています。\n文字コード変換(charset):日本語の文字を、指定した文字コードのバイト列に変換する 転送用エンコード(Content-Transfer-Encoding):そのバイト列を、SMTPで安全に運べるASCII文字列の形に変換する 例えばcharset=UTF-8と指定した場合は「日本語→UTF-8のバイト列」に変換され、そのバイト列はASCIIの範囲を超える(8bit全部を使う)ため、さらにBase64やQuoted-Printableで包む必要があります。\n一方、charset=ISO-2022-JPの場合は少し特殊です。ISO-2022-JPは、エスケープシーケンスという特殊な合図の文字列を使うことで、日本語も含めてすべて7bit(ASCIIの範囲)のバイト列だけで表現できるように設計された文字コードです。つまりISO-2022-JPに変換した時点で、すでにSMTPの制約をクリアしています。そのため、Content-Transfer-Encodingとして追加のエンコードをせず7bit(そのまま送れる、という意味の指定)が使われることが伝統的に多いです。\nまとめると:\ncharset 文字→バイト列の変換 追加の転送用エンコードは必要? UTF-8 日本語→UTF-8バイト列 必要(Base64やQuoted-Printableが多い) ISO-2022-JP 日本語→ISO-2022-JPバイト列(エスケープシーケンス使用) 基本不要(7bit指定で足りる) 画像やPDFの場合 画像やPDFなどのバイナリデータは、そのままだとASCII文字ではないので、Base64エンコードによってASCII文字列に変換してから本文に埋め込みます。添付ファイルが複数ある場合は、multipart/mixedという指定で「本文」と「添付」を区切り(boundaryという文字列で境界を示す)、一通のメールにまとめます。\n全体の流れ 送信側:\nメールに日本語・画像・PDFなどが含まれる MIMEでそれぞれのデータ種別(Content-Type)を記述 charsetで文字コードを指定し、必要なら転送用エンコード(Base64など)を適用 SMTPで運べる形(ASCII文字列)になったメールを送信 受信側:\nSMTPでメールを受信 MIMEヘッダー(Content-Type、Content-Transfer-Encoding、charsetなど)を確認 その情報をもとに、エンコードされたデータを元の日本語・画像・PDFなどに復元 正しいデータとして閲覧できる おまけ:MIMEはメール以外にも影響を与えている Content-Typeヘッダーはメールだけでなく、HTTP通信でも使われています。Webサーバーがブラウザに「これはHTMLです」「これは画像です」と伝えるあの仕組みも、もとをたどればMIMEで定義された考え方です。メールのために生まれた規格が、Webの基盤の一部にもなっているというのは面白いポイントだと思いました。\nまとめ SMTPはASCII文字しか運べないプロトコル MIMEはメールのフォーマットを拡張し、非ASCIIデータをASCIIの形に変換して運べるようにする規格 変換は「文字コード変換」と「転送用エンコード」の2段階(文字コードによっては後者が省略されることもある) MIMEの考え方はHTTPのContent-Typeなど、Web全体にも影響を与えている ","permalink":"https://makiharu.github.io/persona-blog/tech/01/","summary":"\u003cp\u003eMIMEについて学んだことのアウトプット\u003c/p\u003e\n\u003ch2 id=\"はじめに\"\u003eはじめに\u003c/h2\u003e\n\u003cp\u003e最近SMTPプロトコルとMIMEについて勉強したので、理解の整理を兼ねてアウトプットします。「メールって普通に日本語も画像も送れるけど、裏側では何が起きているんだろう?」という疑問からスタートしました。\u003c/p\u003e\n\u003ch2 id=\"smtpはascii文字しか扱えない\"\u003eSMTPはASCII文字しか扱えない\u003c/h2\u003e\n\u003cp\u003eSMTP(Simple Mail Transfer Protocol)はメールを送信するためのプロトコルですが、もともとは\u003cstrong\u003eASCII文字(7bit)しか安全に運べない\u003c/strong\u003eという制約を持っていました。\u003c/p\u003e\n\u003cp\u003eつまり素のSMTPでは、\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e日本語などの非ASCII文字\u003c/li\u003e\n\u003cli\u003e画像やPDFなどのバイナリデータ\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eをそのまま送ることができません。これではメールとして不便すぎるので、この制約を解消するために生まれたのがMIMEです。\u003c/p\u003e\n\u003ch2 id=\"mimeはメールのフォーマットを拡張する仕組み\"\u003eMIMEはメールのフォーマットを拡張する仕組み\u003c/h2\u003e\n\u003cp\u003eMIME(Multipurpose Internet Mail Extensions)は、\u003cstrong\u003eSMTPそのものを拡張するプロトコル\u003c/strong\u003eというよりは、\u003cstrong\u003eメールメッセージのフォーマットを拡張する規格\u003c/strong\u003eです。SMTPは「メールを転送する仕組み」、メール本文の書式は別の規格(RFC 5322)で定義されており、MIMEはこの書式を拡張して、テキスト以外のデータ種別やエンコーディング方式を表現できるようにしています。\u003c/p\u003e\n\u003cp\u003eMIMEは主に次のようなヘッダーで構成されます。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eContent-Type\u003c/code\u003e:データの種類を示す(\u003ccode\u003etext/plain\u003c/code\u003e、\u003ccode\u003eimage/png\u003c/code\u003e、\u003ccode\u003eapplication/pdf\u003c/code\u003e、複数の要素を含む場合は\u003ccode\u003emultipart/mixed\u003c/code\u003eなど)\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eContent-Transfer-Encoding\u003c/code\u003e:データをASCII文字だけの形にどう変換したかを示す(\u003ccode\u003eBase64\u003c/code\u003e、\u003ccode\u003eQuoted-Printable\u003c/code\u003e、\u003ccode\u003e7bit\u003c/code\u003eなど)\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003echarset\u003c/code\u003e:文字コードの指定(\u003ccode\u003eUTF-8\u003c/code\u003e、\u003ccode\u003eISO-2022-JP\u003c/code\u003eなど)\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"変換の流れ文字コード変換--転送用エンコード\"\u003e変換の流れ:文字コード変換 → 転送用エンコード\u003c/h2\u003e\n\u003cp\u003e日本語のテキストを送る場合、実は2段階の変換が行われています。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e文字コード変換(charset)\u003c/strong\u003e:日本語の文字を、指定した文字コードのバイト列に変換する\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e転送用エンコード(Content-Transfer-Encoding)\u003c/strong\u003e:そのバイト列を、SMTPで安全に運べるASCII文字列の形に変換する\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e例えば\u003ccode\u003echarset=UTF-8\u003c/code\u003eと指定した場合は「日本語→UTF-8のバイト列」に変換され、そのバイト列はASCIIの範囲を超える(8bit全部を使う)ため、さらに\u003ccode\u003eBase64\u003c/code\u003eや\u003ccode\u003eQuoted-Printable\u003c/code\u003eで包む必要があります。\u003c/p\u003e\n\u003cp\u003e一方、\u003ccode\u003echarset=ISO-2022-JP\u003c/code\u003eの場合は少し特殊です。ISO-2022-JPは、エスケープシーケンスという特殊な合図の文字列を使うことで、日本語も含めてすべて7bit(ASCIIの範囲)のバイト列だけで表現できるように設計された文字コードです。つまりISO-2022-JPに変換した時点で、すでにSMTPの制約をクリアしています。そのため、Content-Transfer-Encodingとして追加のエンコードをせず\u003ccode\u003e7bit\u003c/code\u003e(そのまま送れる、という意味の指定)が使われることが伝統的に多いです。\u003c/p\u003e\n\u003cp\u003eまとめると:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003echarset\u003c/th\u003e\n          \u003cth\u003e文字→バイト列の変換\u003c/th\u003e\n          \u003cth\u003e追加の転送用エンコードは必要?\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eUTF-8\u003c/td\u003e\n          \u003ctd\u003e日本語→UTF-8バイト列\u003c/td\u003e\n          \u003ctd\u003e必要(Base64やQuoted-Printableが多い)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eISO-2022-JP\u003c/td\u003e\n          \u003ctd\u003e日本語→ISO-2022-JPバイト列(エスケープシーケンス使用)\u003c/td\u003e\n          \u003ctd\u003e基本不要(\u003ccode\u003e7bit\u003c/code\u003e指定で足りる)\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"画像やpdfの場合\"\u003e画像やPDFの場合\u003c/h2\u003e\n\u003cp\u003e画像やPDFなどのバイナリデータは、そのままだとASCII文字ではないので、Base64エンコードによってASCII文字列に変換してから本文に埋め込みます。添付ファイルが複数ある場合は、\u003ccode\u003emultipart/mixed\u003c/code\u003eという指定で「本文」と「添付」を区切り(\u003ccode\u003eboundary\u003c/code\u003eという文字列で境界を示す)、一通のメールにまとめます。\u003c/p\u003e\n\u003ch2 id=\"全体の流れ\"\u003e全体の流れ\u003c/h2\u003e\n\u003cp\u003e送信側:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eメールに日本語・画像・PDFなどが含まれる\u003c/li\u003e\n\u003cli\u003eMIMEでそれぞれのデータ種別(Content-Type)を記述\u003c/li\u003e\n\u003cli\u003echarsetで文字コードを指定し、必要なら転送用エンコード(Base64など)を適用\u003c/li\u003e\n\u003cli\u003eSMTPで運べる形(ASCII文字列)になったメールを送信\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e受信側:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eSMTPでメールを受信\u003c/li\u003e\n\u003cli\u003eMIMEヘッダー(Content-Type、Content-Transfer-Encoding、charsetなど)を確認\u003c/li\u003e\n\u003cli\u003eその情報をもとに、エンコードされたデータを元の日本語・画像・PDFなどに復元\u003c/li\u003e\n\u003cli\u003e正しいデータとして閲覧できる\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"おまけmimeはメール以外にも影響を与えている\"\u003eおまけ:MIMEはメール以外にも影響を与えている\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eContent-Type\u003c/code\u003eヘッダーはメールだけでなく、HTTP通信でも使われています。Webサーバーがブラウザに「これはHTMLです」「これは画像です」と伝えるあの仕組みも、もとをたどればMIMEで定義された考え方です。メールのために生まれた規格が、Webの基盤の一部にもなっているというのは面白いポイントだと思いました。\u003c/p\u003e\n\u003ch2 id=\"まとめ\"\u003eまとめ\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eSMTPはASCII文字しか運べないプロトコル\u003c/li\u003e\n\u003cli\u003eMIMEはメールのフォーマットを拡張し、非ASCIIデータをASCIIの形に変換して運べるようにする規格\u003c/li\u003e\n\u003cli\u003e変換は「文字コード変換」と「転送用エンコード」の2段階(文字コードによっては後者が省略されることもある)\u003c/li\u003e\n\u003cli\u003eMIMEの考え方はHTTPのContent-Typeなど、Web全体にも影響を与えている\u003c/li\u003e\n\u003c/ul\u003e","title":"SMTPとMIME、メールが日本語や画像を運べる仕組みを整理する"},{"content":"背景 社内のAI活用は人によってやり方がバラバラで、全体の運用ルールも整っていない。AIの実務経験がない新規メンバーが同じプロジェクトに加わったことをきっかけに、仕組みの共通化を考え始めた。\n「MCPについて一から説明するとしたら？」という問いに答える形でまとめた記事。MCPを理解すると「AIが自分でファイルを読み、GitHubを操作し、データベースを参照する」レベルの自動化が現実的になる。MCPを知らない人が「なるほど、そういう仕組みか」と感じられることをゴールとする。\nそもそも何が問題だったか 通常のAIチャット（ChatGPT・Claudeなど）は、会話の中にある情報しか見えない。\n1 2 ユーザー: このコードのバグを直して AI: （コードを貼ってもらわないと何も見えない） 実際の業務では\nリポジトリのコードを読んでほしい Slackのスレッドを確認してほしい データベースの値を参照してほしい ファイルを直接編集してほしい といったことが頻繁にある。これらは「AIに手渡す」のが大変で、結局人間がコピペして橋渡しをしていた。\nAIが外部にアクセスする方法 AIが「会話の外にある情報」にアクセスする方法は、実はMCPだけではない。\n方法 例 イメージ MCP Figma MCP、GitHub MCP 専用の窓口を通して操作 CLI git、gh、aws シェルでコマンドを実行 API Twilio REST API HTTPで直接呼ぶ ローカルファイル ソースコード、JSON ファイルを直接読む たとえば GitHub の Issue を取得したい場合、MCPを使わなくても gh issue list コマンドをAIが実行することで同じことができる。\nではMCPの何がうれしいのかというのが本題になる。\nMCPとは何か MCP（Model Context Protocol）は、Anthropicが2024年に公開した、AIと外部ツールをつなぐための標準的な接続規格（プロトコル）。\nATMを思い浮かべるとイメージしやすい。\nどの銀行のカードでも同じATMで引き出せるのは、銀行とATMが共通のプロトコルで通信しているから。異なるサービス同士でも「規格」さえ合っていればつながれる。\nMCPはその「規格」のAI版。AIがどのツールに対しても同じ方法でアクセスできるよう、共通の接続ルールを定めたのがMCPだ。\n重要なのは「MCPはAPIの代わり」ではなく、**「APIやローカル機能などをAIが扱いやすい共通形式で公開する層」**だという点。MCP Server の裏側では、結局その先のサービスのAPIを呼んでいることも多い。\n1 2 3 4 5 6 7 Claude Code ↓ MCP Figma MCP Server ↓ Figma API ← MCP Serverがここを叩いている ↓ Figma CLIと比べたときのMCPの強み CLIの場合、AIは自分で「このサービスはどのコマンドを使えばいいのか」を考える必要がある。\nMCPの場合、MCP Server側が次の情報をAIに提示する。\n1 2 3 4 利用できるツール: - search_code(query: string) ← コードを検索する - get_file(path: string) ← ファイルを取得する - create_pr(title, body) ← PRを作成する AIはこのツール一覧を受け取り、どのツールを呼べばいいかを判断できる。つまりMCPは**「AIと外部ツールのあいだに共通の会話形式を作った仕組み」**といえる。\nMCPの構成要素 MCPには3つの登場人物がいる。\n役割 説明 例 Host AIを動かすアプリケーション Claude Code Client HostとServerをつなぐ仲介役 Hostに内蔵されている Server 外部ツールやデータを提供する小さなプログラム Figma MCP Server、Redmine MCP Server 1 2 3 4 5 6 7 8 9 10 ユーザー │ ▼ Host（Claude Code） │ ▼（MCP経由） MCP Server（GitHub / ファイルシステム / DB など） │ ▼ 実際のツール・データ MCPサーバーは誰が作るのか MCPサーバーは、大きく3つのパターンがある。\n1. サービス提供企業が公式に作る\nFigmaやNotionなど、企業が自社サービスのMCPサーバーを公式提供している。\n1 Claude Code → Figma MCP Server（Figma公式）→ Figma 2. OSSコミュニティや第三者が作る\n公式サーバーがない場合でも、コミュニティがMCPサーバーを作って公開していることがある。\n3. 自分たちで作る\n社内システムや、MCPサーバーが存在しないサービスに接続したい場合、自作できる。\n1 Claude Code → 自作 Redmine MCP Server → 社内Redmine API → Redmine つまりMCPサーバーは「FigmaやGitHubそのもの」ではなく、**「AIからそのサービスを操作するための橋渡しプログラム」**だと考えるとわかりやすい。\nMCPサーバーで何ができるか MCPサーバーはすでに多数が公開されており、設定するだけで使える。代表的なものをいくつか挙げる。\nMCPサーバー できること Figma デザインデータ・コンポーネント情報の参照 Lychee チケットの参照・更新、進捗確認 Redmine チケット検索・登録・ステータス更新 CodeGraph コードの依存関係・呼び出し元の検索 GitHub Issue・PRの読み書き、コード検索 Slack チャンネル・スレッドの参照、投稿 これらをClaude Codeに設定しておくと、AIが自律的に情報を取りに行けるようになる。\n動作のイメージ たとえば「このチケットの内容を実装して」という依頼をした場合。\nMCPなし:\n1 2 3 ユーザー: このチケットを実装して（チケット内容をコピペ、関連コードもコピペ） AI: ここを修正すればいいと思います（提案のみ） ユーザー: （手動でファイルを書き換える） MCPあり（Redmine + CodeGraph）:\n1 2 3 4 5 ユーザー: チケット #456 を実装してください AI: （Redmine MCPでチケット内容を取得） AI: （CodeGraph MCPで関連コードの依存関係を調査） AI: （ファイルを直接書き換える ※Claude Code組み込み機能） AI: 実装しました。チケット #456 の要件に対応しています 人間がやっていた「チケット確認」「コード調査」「ファイル編集」をAIが代行する。\nMCPサーバーを設定してみる ターミナルで claude mcp add コマンドを使って追加する。--scope オプションで設定の有効範囲を指定できる。\nスコープの使い分け スコープ 書き込まれるファイル 有効範囲 local（デフォルト） ~/.claude.json（プロジェクト紐付き） 自分・このプロジェクトのみ project プロジェクトルートの .mcp.json チーム全員・このプロジェクトのみ（Git管理） user ~/.claude.json 自分・全プロジェクト共通 複数のプロジェクトを横断して使うサーバーは user スコープが適している。たとえばdev配下にA・B・Cプロジェクトがあり、FigmaやRedmineのMCPをどのプロジェクトでも使いたい場合は、user スコープで1回登録するだけでよい。\n1 2 3 # 全プロジェクト共通で使うもの → userスコープ claude mcp add figma --scope user -- npx -y figma-mcp claude mcp add redmine --scope user -- npx -y redmine-mcp 特定プロジェクト専用のサーバー（そのリポジトリ固有のDBなど）をチームで共有したい場合は project スコープを使う。.mcp.json がGitにコミットされるので、チームメンバーが git pull するだけで同じ設定が使えるようになる。\n1 2 # チームで共有する必要があるもの → projectスコープ claude mcp add knowledge-server --scope project -- npx -y knowledge-mcp 同じ名前のサーバーが複数のスコープに存在する場合、.mcp.json（project）が user より優先される。user に登録済みのサーバーが特定プロジェクトの .mcp.json にも重複して存在しても壊れはしないが、冗長になる。横断利用が目的なら user スコープに一本化して、.mcp.json には本当にそのプロジェクト固有のものだけ置くのが整理しやすい。\n設定後、claude mcp list で追加されたサーバーを確認できる。\n自社システム用のMCPサーバーを作る 公開されているサーバー以外に、自社システムに接続するMCPサーバーを自分で作ることもできる。\nNode.jsまたはPythonで、以下のような構成で書く。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import { Server } from \u0026#34;@modelcontextprotocol/sdk/server/index.js\u0026#34;; const server = new Server({ name: \u0026#34;my-server\u0026#34;, version: \u0026#34;1.0.0\u0026#34; }); // AIが呼び出せる「ツール」を定義する server.setRequestHandler(ListToolsRequestSchema, async () =\u0026gt; ({ tools: [ { name: \u0026#34;get_ticket\u0026#34;, description: \u0026#34;チケットIDから社内Redmineのチケット情報を取得する\u0026#34;, inputSchema: { type: \u0026#34;object\u0026#34;, properties: { ticketId: { type: \u0026#34;string\u0026#34; } } } } ] })); // ツールが呼ばれたときの処理 server.setRequestHandler(CallToolRequestSchema, async (request) =\u0026gt; { if (request.params.name === \u0026#34;get_ticket\u0026#34;) { const ticket = await redmineApi.getTicket(request.params.arguments.ticketId); return { content: [{ type: \u0026#34;text\u0026#34;, text: JSON.stringify(ticket) }] }; } }); これを設定すると、「このチケットの内容をまとめて実装してください」といった依頼をAIに投げることができる。\nよくある疑問 Q. セキュリティは大丈夫？\nMCPサーバーはローカルで動かすことが基本なので、外部にデータが送られるわけではない。AIに渡す情報の範囲はMCPサーバーの設定で制御できる。ただしAPIキーの管理など基本的なセキュリティ対策は必要。\nQ. プログラミングの知識がないと使えない？\n設定済みのMCPサーバーを追加するだけなら、JSONファイルを編集できれば問題ない。自作サーバーを作る場合はコーディングが必要になる。\nQ. ChatGPTでも使える？\n現時点ではClaude（Anthropic製）やClaude Code、そして一部のサードパーティツールがMCPに対応している。OpenAIも対応を発表しており、今後は複数のAIツールでMCPが使えるようになる見込み。\nQ. MCPを使えばAIはなんでもできる？\nMCPはあくまで「AIと外部ツールをつなぐ規格」であり、接続できるのはMCPサーバーとして実装された機能だけ。どんな操作でもできるわけではなく、何ができるかはMCPサーバーの設計次第。\nまとめ 項目 内容 MCPとは AIと外部ツールをつなぐ標準規格 なぜ必要か CLIやAPIより「AIが使いやすい共通インターフェース」を提供するため 何ができるか ファイル操作・GitHub・DB・SlackなどをAIが自律的に使える MCPサーバーは誰が作る？ サービス企業・OSSコミュニティ・自社、いずれも可 始め方 claude mcp add コマンドでMCPサーバーを追加する 発展 社内システム向けのMCPサーバーを自作できる MCPを活用すると、「AIに相談して自分で作業する」から「AIに依頼して任せる」という働き方にシフトできる。\nまずは claude mcp add でFigmaやRedmineのMCPサーバーを追加して、AIに情報を取りに行かせるところから試してみるのがおすすめ。\n","permalink":"https://makiharu.github.io/persona-blog/tech/mcp-intro/","summary":"MCPとは何か、なぜ必要か、どう動くかを、社内でAI活用を始めたい人向けにわかりやすく整理した。","title":"MCP超入門 ── AIに「道具」を持たせる仕組みを理解する"},{"content":"あるSDKをNext.jsプロジェクトへ組み込む際、dynamic import と ssr: false が必要になった。\n「なぜSSRを無効化する必要があるのか？」を調べる中で、SSRやHydrationの仕組みについて理解が深まったので整理する。\n一般的なWebアプリの構成 通常のSPA（Reactのみ）の場合は次のような構成になる。\n1 2 3 4 5 6 7 ブラウザ │ HTTP ▼ APIサーバー │ ▼ DB ブラウザ側でReactが動き、必要なデータだけをAPIサーバーから取得する。\nHTMLはブラウザ側で生成される。\nSSRを使うNext.jsの構成 SSRを利用する場合は、Next.jsサーバーが追加される。\n1 2 3 4 5 6 7 8 9 10 ブラウザ │ ▼ Next.jsサーバー(Node.js) │ ▼ APIサーバー │ ▼ DB このとき、\nブラウザはNext.jsサーバーへアクセスする Next.jsサーバーがReactコンポーネントを実行する HTMLを生成する APIサーバーから必要なデータを取得する HTMLをブラウザへ返す という流れになる。\nつまり、\nHTMLを返しているのはAPIサーバーではなく、Next.jsサーバー。\nNext.jsサーバーとは 「Next.jsサーバーって誰がどうやって用意するの？」\n例えば本番サーバーへデプロイして\n1 next start するとNode.js上でNext.jsが起動し、これが「Next.jsサーバー」になる。\n別途Node.js用のプロジェクトを作るわけではない。\nSSRサーバーを持たない構成もある Next.jsは必ずNode.jsサーバーを立てる必要があるわけではなく、next export（あるいはoutput: 'export'）で静的エクスポートし、本番ではNext.jsサーバーを動かさずに配信する構成も取れる。\n1 2 3 4 ブラウザ・任意のランタイム │ ▼ 事前生成されたHTML + JavaScript この場合、アクセス時にNext.jsサーバーがHTMLを生成しているわけではなく、ビルド時に生成済みの静的ファイルがそのまま配信される。\nなぜSDKでエラーになるのか ブラウザ向けのSDKの中には\nwindow document などブラウザのAPIを前提にしているものがある。 例えば、Twilio SDK, Teams SDKとか。\nしかしビルド中はNode.js環境なので、\n1 window は存在しない。\nそのため\n1 ReferenceError: window is not defined のようなエラーになる。\nそこで\n1 2 3 dynamic(() =\u0026gt; import(\u0026#34;./SomeSdkComponent\u0026#34;), { ssr: false, }); として、ブラウザだけでSDKを読み込むようにしている。\nHydrationとは Hydrationは、サーバー（またはビルド時）で作られたHTMLに、Reactがイベントなどを結び付ける処理のこと。\nイメージすると、\n1 2 3 4 5 ① HTML表示 ↓ ② Reactが起動 ↓ ③ ボタンや入力欄が動くようになる という流れになる。\nSSR・SSG・SPAの違い 方式 HTMLを作るタイミング SPA ブラウザ SSR リクエストごとにNext.jsサーバー SSG ビルド時に事前生成 今回理解できたこと SSRではNext.jsサーバーがHTMLを生成する Next.jsサーバーはNext.jsプロジェクト自体が動いているもの next export（output: 'export'）では本番にNext.jsサーバーはいない ブラウザ専用のSDKが落ちる理由はNode.js環境にはwindowが存在しないため dynamic import + ssr: falseでブラウザだけに読み込みを限定できる HydrationはHTMLへReactのイベント処理を結び付ける仕組み 補足すると、next export は現在のNext.jsでは非推奨となっており、新しいプロジェクトでは output: 'export' を設定して next build を実行する方式が推奨されている。ただし、SSR・SSG・Hydrationの考え方自体はこのまとめの内容で問題ない。\n理解度チェック 理解度チェッククイズ（全4問）\n問1\nSSRを利用するNext.jsの構成で、実際にHTMLを生成しているのはAPIサーバーとNext.jsサーバーのどちらでしょうか？\n答え Next.jsサーバー。APIサーバーはデータを返すだけで、そのデータを使ってReactコンポーネントを実行しHTMLへ変換しているのはNext.jsサーバー側の処理。 問2\nブラウザ向けのSDKをそのままNext.jsに組み込むと、ビルド時やnext start実行時にwindow is not definedのようなエラーになることがあります。これはなぜでしょうか？\n答え SSR（サーバーサイドでのReact実行）はNode.js環境で行われ、Node.jsには`window`や`document`といったブラウザAPIが存在しないため。ブラウザAPIを前提にしたSDKのコードがサーバー側でも評価されてしまい、参照エラーになる。 問3\nHydrationとは何かを簡潔に説明してください。\n答え サーバー（またはビルド時）で生成済みのHTMLに対して、Reactがイベントハンドラなどを結び付け、ボタンや入力欄が実際に動くようにする処理のこと。 問4\nSPA・SSR・SSGは最終的にはどれもブラウザにHTML/JSを届けますが、何が本質的に違うのでしょうか？「HTMLがいつ・どこで作られるか」という観点で3つを比較し、それぞれの方式がなぜその選択をしているのか（何を優先しているのか）まで考えて説明してください。\n答え 3つとも「Reactコンポーネントを実行してHTMLに変換する」処理自体は同じだが、それを行うタイミングと場所が異なる。SPAはブラウザが表示するたびに（クライアントで、都度）実行する。SSRはリクエストが来るたびに（サーバーで、都度）実行し、常に最新のデータを反映したHTMLを返せる代わりに毎回サーバーの計算コストがかかる。SSGはビルド時に（サーバー相当の環境で、一度だけ）実行しておき、以降は生成済みのHTMLを配信するだけなので速く安く済むが、データが更新されても再ビルドするまでHTMLには反映されない。つまり3方式の違いは「都度作るか、一度作って使い回すか」「その処理をどこで行うか」というトレードオフの選び方の違いである。 ","permalink":"https://makiharu.github.io/persona-blog/tech/0712-sdk-ssr/","summary":"Next.jsプロジェクトへSDKを組み込む際にdynamic importとssr: falseが必要になったことをきっかけに、SSR・SSG・Hydrationの仕組みを整理した。","title":"Next.jsのSSR・SSG・Hydrationを整理して理解した"},{"content":"やりたいこと ・Shapointでmarkdownファイルをプレビュー表示できるようにする\n・今後、格納先のファイルパスは変わる可能性があり、その場合には都度ファイルパスの変更の必要があるが、正直管理が面倒。\n解決策 base64化して、mdに画像を埋め込んでしまえば、ファイルパス or ファイルリンクを考える必要がなくなる！\n1 ![alt](./image.png) これをファイルなしで、Markdown1ファイルだけで画像を表示させたい。\nそもそもbase64とは バイナリデータ（画像・動画など）をテキストとして表現するためのエンコード方式。\n画像ファイルの中身は人間には読めないバイナリデータ（0と1の羅列）だが、MarkdownやHTMLはテキスト形式なのでバイナリをそのまま書けない。そこで base64 を使い、バイナリをテキストに変換する。\n使う文字は以下の64種類。大文字・小文字は別々に数える。\n種類 文字 数 大文字 A〜Z 26 小文字 a〜z 26 数字 0〜9 10 記号 + / 2 合計 64 この64文字だけで構成されるテキストに変換する。名前の由来は「64種類の文字を使う」から。\n1 2 3 4 image.png の中身（バイナリ） ↓ base64エンコード iVBORw0KGgoAAAANSUhEUgAAAAUA...（テキスト） ↓ Markdown に直接貼り付けられる デコードすれば元のバイナリに戻せる。名前の由来は「64種類の文字を使う」から。\n方法: Data URIスキーム 画像をbase64エンコードしてData URIとして直接埋め込む。\n1 ![alt](data:image/png;base64,\u0026lt;base64文字列\u0026gt;) フォーマットは data:\u0026lt;MIMEタイプ\u0026gt;;base64,\u0026lt;データ\u0026gt; 。\nMIMEタイプはファイルの種類をブラウザに伝えるためのラベル。タイプ/サブタイプ の形式で書く。\nMIMEタイプ ファイルの種類 image/png PNG画像 image/jpeg JPEG画像 image/gif GIF画像 image/webp WebP画像 data:image/png;base64,... と書くことで「これはPNG画像をbase64したデータ」とブラウザに伝えている。\nbase64変換コマンド macOS / Linux 1 base64 -i image.png | tr -d \u0026#39;\\n\u0026#39; base64 コマンドはデフォルトで76文字ごとに改行を入れて出力する。Data URI に改行が混ざると URL として壊れた扱いになり画像が表示されないため、tr -d '\\n' で改行をすべて除去して1行にする。\n| — 左のコマンドの出力を右のコマンドに渡す（パイプ） tr -d '\\n' — tr は文字を変換・削除するコマンド。-d は削除モード、'\\n' は改行文字 Python 1 2 3 4 5 6 import base64 with open(\u0026#34;image.png\u0026#34;, \u0026#34;rb\u0026#34;) as f: encoded = base64.b64encode(f.read()).decode() print(f\u0026#34;data:image/png;base64,{encoded}\u0026#34;) 埋め込み例 1 ![ロゴ](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA...) メリット・デメリット メリット ファイル1つで完結する、画像リンク切れが起きない デメリット ファイルサイズが大きくなる（元サイズの約1.33倍）、diff が読みにくくなる 使いどころ GitHubのREADMEで画像パスの管理が面倒なとき ドキュメントを1ファイルで配布したいとき メールやチャットにMarkdownをそのままペーストするとき 逆に、頻繁に変更する画像や大きい画像は素直に外部ファイルにした方がよい。\nひとこと mdの画像ファイルシェアをどうしようかなーと思っていたら、同僚がサクッとこの方法でやっていたので、素直に感心した。 こういうことは真似してどんどん学んでいくが吉。\n理解度チェック 問1 base64とは何をするための仕組みか？\n答え バイナリデータをテキストとして表現するためのエンコード方式。画像などのバイナリをMarkdownやHTMLに直接書けるテキストに変換できる。 問2 base64が「64」と呼ばれる理由は？使う文字の内訳も答えよ。\n答え 64種類の文字を使うから。内訳は大文字A〜Z（26）＋小文字a〜z（26）＋数字0〜9（10）＋記号「+」「/」（2）＝64。 問3 Markdownに画像をbase64で埋め込むときの書き方は？\n答え 1 ![alt](data:image/png;base64,\u0026lt;base64文字列\u0026gt;) フォーマットは data:\u0026lt;MIMEタイプ\u0026gt;;base64,\u0026lt;データ\u0026gt; 。\n問4 macOSで base64 -i image.png の後に | tr -d '\\n' が必要な理由は？\n答え base64 コマンドはデフォルトで76文字ごとに改行を入れて出力する。Data URI に改行が混ざると URL として壊れた扱いになり画像が表示されないため、tr -d '\\n' で改行をすべて除去して1行にする。\n| — 左のコマンドの出力を右のコマンドに渡す（パイプ） tr -d '\\n' — tr は文字を変換・削除するコマンド。-d は削除モード、'\\n' は改行文字 問5 base64埋め込みが向いていないケースを1つ挙げよ。\n答え 頻繁に変更する画像や大きい画像。base64化するとファイルサイズが約1.33倍になり、差分（diff）も読みにくくなるため。 ","permalink":"https://makiharu.github.io/persona-blog/tech/md-base64-image/","summary":"外部ファイルなしでMarkdownに画像を埋め込む方法のメモ。Data URIスキームを使う。","title":"Markdownに画像をbase64で直接埋め込む"},{"content":"はじめに マジックが趣味なので、時たまどこかで手品をしています。 ここ最近の動きは以下のnoteに書いていたりいなかったりです。 https://note.com/tokishunmagic\n今後、スキを見つけて、手品の道具や書籍、DVDに関してのアウトプットを整理していければと思っています。\n","permalink":"https://makiharu.github.io/persona-blog/magic/2026-06-start-activity-blog/","summary":"都内〜千葉を中心に活動する週末マジシャンとしての備忘録","title":"週末マジシャン的なあれこれ"},{"content":"はじめに cult of the party parrot に自作GIFを投稿し、レビュアーとやり取りしながらPRをマージに持っていくまでの体験記です。\nこの記事では以下をまとめます。\n初OSSコントリビュートの流れ GIF制作でハマった技術的ポイント OSS特有のレビューコミュニケーションで学んだこと 背景 会社メンバーとの雑談で「Party Parrot」を知り、以下の記事を読んで興味を持ちました。\nhttps://qiita.com/ayatothos/items/0c3da3d48fe8ade705e6\nこの記事をきっかけに「自分でもOSSに貢献できるかもしれない」と思い、新しいParrot GIFを作ることにしました。\n作成について 作品の方向性やアイデアは自分の発想ですが、GIF制作の具体的な実装はAIと相談しながら進めました。\nレビュー対応について 最初は「Pizza Parrot（HD版）」としてPRを提出しましたが、レビュアーから以下のコメントがありました。\nLooks great! I wonder if we should disambiguate names with the existing 🍕 Parrot. Generally the HD versions recreate the SD versions without any meaningful changes. I like the parrot eating the whole pizza though, maybe we could rename that to wholepizzaparrot.gif or something?\nこのコメントをもとに修正対応を行いました。\nハマったこと1: GIFの透過処理 問題 よくよく見ると、最初に投稿したGIFは背景が透過されていませんでした。痛恨のミスです。 既存の pirateparrot.gif などと比較すると明らかに違和感がありました。\n原因 フレーム生成時に背景を白で塗りつぶしていたことが原因でした。\n1 2 3 4 5 # NG: 白背景 frame = Image.new(\u0026#34;RGBA\u0026#34;, (CANVAS_SIZE, CANVAS_SIZE), (255, 255, 255, 255)) # OK: 透明背景 frame = Image.new(\u0026#34;RGBA\u0026#34;, (CANVAS_SIZE, CANVAS_SIZE), (0, 0, 0, 0)) GIF透過の仕組みと注意点 GIFは「特定のパレットインデックスを透明として扱う」仕様です。\nそのため、RGBA → GIF変換時に透過情報が失われないように処理が必要になります。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def rgba_to_gif_frame(frame, global_palette): alpha = np.array(frame.getchannel(\u0026#34;A\u0026#34;)) p = frame.convert(\u0026#34;RGB\u0026#34;).quantize(palette=global_palette, dither=0) orig_palette = p.getpalette() arr = np.array(p, dtype=np.uint8) arr = (arr + 1).clip(0, 255).astype(np.uint8) arr[alpha \u0026lt; 128] = 0 new_palette = [0, 0, 0] + orig_palette[:255 * 3] result = p.copy() result.putdata(arr.flatten().tolist()) result.putpalette(new_palette) return result さらに保存時に以下を指定する必要があります。\n1 transparency=0, disposal=2 ハマったこと2: gifsicle最適化で頭が消える 問題 gifsicle -O3 で最適化した結果、アニメーション中にパロットの頭が消える問題が発生しました。\n原因 -O3 は差分フレームのみを保存する最適化を行います。\nしかし disposal=background と組み合わさると、前フレームで消された部分が再描画されないケースが発生します。\n1 2 3 frame 0: 128x128（全体） frame 5: 118x71 at (10, 54) → 頭（row 43）が範囲外で消える 解決策 全フレームをフルサイズに展開しました。\n1 subprocess.run([\u0026#34;gifsicle\u0026#34;, \u0026#34;--unoptimize\u0026#34;, abs_out, \u0026#34;-o\u0026#34;, abs_tmp]) ハマったこと3: ピザがキャンバスからはみ出す 問題 ピザが128×128のキャンバスからはみ出していました。\n1 2 PIZZA_CENTER = (113, 84), PIZZA_SIZE = 58 → 右端: 142px \u0026gt; 128px 解決策 キャンバスを拡張し、位置調整しました。\n1 2 3 CANVAS_SIZE = 160 PARROT_OFFSET = (0, 16) PIZZA_CENTER = (113, 100) OSSコントリビューションで学んだこと 1. レビュアーの意図を正確に読む HD版は既存の再現 → 名前衝突NG 新しい動き → 新パロットとして扱うべき 最初はHDとして配置しましたが、コンセプト的に誤りでした。\n2. PRは最終状態が重要 試行錯誤の履歴はローカルに残しつつ、PRは整理して見やすくすることが重要です。\n今回のリポジトリでは「1PR = 1コミット」が一般的でした。\n3. レビュー返信はシンプルに 対応内容を簡潔に伝えることで、レビュアーの確認コストを下げられます。\nまとめ GIFの透過、gifsicleの最適化、キャンバス設計など技術的な調整は多くありました。\nただ、それ以上に印象に残ったのは「OSS活動の楽しさ」です。\n海外の開発者と直接やり取りできる体験は純粋に楽しく、OSSは「技術力が高い人だけの世界」ではなく、\nルールを理解する 丁寧にコミュニケーションする これだけでも十分に価値があると感じました。\n次はもう少し難しいコントリビュートにも挑戦してみようと思います。\n","permalink":"https://makiharu.github.io/persona-blog/tech/oss-pizzaparrot-retrospective/","summary":"cult of the party parrot に自作GIFを投稿し、レビュアーとやり取りしながらPRをマージするまでの体験記。GIF透過処理やgifsicle最適化のハマりどころと、OSSコミュニケーションで学んだことをまとめます。","title":"初OSSコントリビュート振り返り〜WholePizza ParrotのPRで学んだこと"},{"content":"はじめに 手っ取り早くReactアプリを作りたかったので npm create vite@latest を実行したのですが、VSCode上でエラーが出ました。 ネットで同じエラーの人が見つからず自分の環境固有の問題かと思いましたが、原因を調べると仕組みの話で納得感のある解決ができたので記録しておきます。\nエラー内容 1 npm create vite@latest を実行してプロジェクトを作成し、VSCodeで開くと tsconfig.app.json と App.tsx でエラーが発生していました。\n原因 VSCodeは独自のTypeScriptを持っている そもそも VSCode自体がTypeScriptで開発されています。VSCodeのソースコードはTypeScriptで書かれており、TypeScriptはVSCodeにとって開発言語そのものです。\nさらにVSCodeは、エディタ上で型チェックや補完を動かすために TypeScript Language Service（TypeScriptの型解析エンジン）を内部に同梱しています。これがエディタ機能の核であり、プロジェクトの node_modules とは完全に別物です。\n1 2 3 4 5 6 7 8 VSCode本体 ├── VSCode自身のコード（TypeScriptで書かれている） └── TypeScript Language Service（同梱）← エディタの型チェックに使われる バージョン: 4.9.3 プロジェクト └── node_modules/typescript（npm installで入る） バージョン: 5.x npm install を実行すると package.json に記載のTypeScript（5.x系）は node_modules/typescript にインストールされます。 しかし VSCodeのエディタ機能はデフォルトで同梱のTypeScript Language Serviceを使うため、node_modules のTypeScriptは参照しません。\nこれはVSCodeの意図的な設計で、「プロジェクトにTypeScriptがインストールされていなくてもエディタが動く」ようにするためです。\nなぜエラーになるのか Viteが生成する tsconfig.app.json はTypeScript 5.0以降で追加された設定を使っています。\n1 2 3 4 5 { \u0026#34;compilerOptions\u0026#34;: { \u0026#34;moduleResolution\u0026#34;: \u0026#34;bundler\u0026#34; // TypeScript 5.0で追加されたオプション } } VSCode同梱の古いTypeScript（4.9.3）はこのオプションを知らないため、エラーとして扱ってしまいます。\n解決方法 VSCodeが参照するTypeScriptを、同梱のものからワークスペース（node_modules）のものに切り替えます。\nコマンドパレットを開く（Cmd + Shift + P） 「TypeScript: Select TypeScript Version」を選択 「ワークスペースのバージョンを使用 5.x.x」を選択 これで解決！\n","permalink":"https://makiharu.github.io/persona-blog/tech/npm-create-vite-error/","summary":"npm create vite@latest でReactプロジェクトを作成したところVSCode上でTypeScriptのエラーが発生。原因はVSCode内蔵のTypeScriptバージョンの古さにありました。","title":"npm create vite@latest 実行で出たエラーを解決した"},{"content":"はじめに こんな状況はありませんか？\nNotebookLMで作った動画を友人に共有したい。でも、YouTubeに上げるほどでもないし、誰でも見れる状態にはしたくない\u0026hellip;\nこの記事では、AWS S3の署名付きURL（Presigned URL） を使って、期限付きで安全にファイルを共有する方法を解説します。\n今回のユースケース NotebookLMで作成した動画を友人数人に共有したい アクセス数は少ない（数人程度） 閲覧期間は短めでよい（数日〜1週間） 不特定多数には公開したくない このような場合、署名付きURLがぴったりです。\n署名付きURLとは？ 署名付きURL（Presigned URL） は、S3のプライベートなファイルに一時的にアクセスできる特別なURLです。\n1 2 3 4 5 6 7 8 9 10 11 12 通常のS3 URL（アクセス不可）: https://my-bucket.s3.ap-northeast-1.amazonaws.com/video.mp4 → AccessDenied 署名付きURL（期限内ならアクセス可能）: https://my-bucket.s3.ap-northeast-1.amazonaws.com/video.mp4 ?X-Amz-Algorithm=AWS4-HMAC-SHA256 \u0026amp;X-Amz-Credential=... \u0026amp;X-Amz-Date=20260208T000000Z \u0026amp;X-Amz-Expires=3600 \u0026amp;X-Amz-Signature=abc123... → 動画が再生できる！ URLにアクセス権限の情報（署名）が埋め込まれているため、このURLを知っている人だけがファイルにアクセスできます。\nなぜ署名付きURLを使うのか？ 他の方法との比較 方法 メリット デメリット S3を公開設定にする 簡単 誰でもアクセスできてしまう CloudFront + 署名付きCookie 高機能 設定が複雑 署名付きURL 簡単＆安全 URLが長い、期限がある 少人数への一時的な共有なら、署名付きURLが最もシンプルです。\n署名付きURLのメリット S3バケットは非公開のままでOK 期限を設定できる（1秒〜7日間） AWSアカウント不要で相手に共有できる URLをLINEやメールで送るだけ 仕組み 署名付きURLの仕組みをざっくり説明します。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1. あなた（AWSアカウント所有者） ↓ 署名付きURLを生成 2. 署名付きURL - ファイルのパス - 有効期限 - 署名（あなたの認証情報で作成） ↓ 友人にURLを送る 3. 友人がURLにアクセス ↓ 4. S3が署名を検証 - 署名は正しいか？ - 期限内か？ ↓ OKならファイルを返す 5. 友人が動画を視聴できる 重要なのは、署名はあなたの認証情報で作成されるということ。つまり、あなたがアクセス権を持つファイルだけURLを生成できます。\n実際にやってみよう 前提条件 AWSアカウントを持っている S3バケットにファイルをアップロード済み AWS CLIがインストール済み 方法1: AWS CLIで生成（最も簡単） 1 2 3 4 5 # 基本形 aws s3 presign s3://バケット名/ファイル名 # 有効期限を指定（秒単位、デフォルトは3600秒=1時間） aws s3 presign s3://my-bucket/video.mp4 --expires-in 86400 --expires-in 86400 で24時間（86400秒）有効なURLが生成されます。\n1 2 3 4 # 実行例 $ aws s3 presign s3://my-videos/notebooklm-output.mp4 --expires-in 604800 https://my-videos.s3.ap-northeast-1.amazonaws.com/notebooklm-output.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256\u0026amp;X-Amz-Credential=AKIA... 生成されたURLを友人にLINEやメールで送れば完了です。\n方法2: AWSコンソールから生成 AWSコンソールでS3を開く 対象のファイルを選択 「アクション」→「署名付きURLで共有」 有効期限を設定して「署名付きURLを作成」 GUIで簡単に作成できます。\n方法3: Python（boto3）で生成 プログラムから生成したい場合はSDKを使います。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 import boto3 s3_client = boto3.client(\u0026#39;s3\u0026#39;) url = s3_client.generate_presigned_url( \u0026#39;get_object\u0026#39;, Params={ \u0026#39;Bucket\u0026#39;: \u0026#39;my-bucket\u0026#39;, \u0026#39;Key\u0026#39;: \u0026#39;video.mp4\u0026#39; }, ExpiresIn=604800 # 7日間（秒） ) print(url) 方法4: Node.js（AWS SDK v3）で生成 1 2 3 4 5 6 7 8 9 10 11 12 import { S3Client, GetObjectCommand } from \u0026#34;@aws-sdk/client-s3\u0026#34;; import { getSignedUrl } from \u0026#34;@aws-sdk/s3-request-presigner\u0026#34;; const client = new S3Client({ region: \u0026#34;ap-northeast-1\u0026#34; }); const command = new GetObjectCommand({ Bucket: \u0026#34;my-bucket\u0026#34;, Key: \u0026#34;video.mp4\u0026#34;, }); const url = await getSignedUrl(client, command, { expiresIn: 604800 }); console.log(url); 有効期限の目安 用途に応じて適切な有効期限を設定しましょう。\n用途 推奨期限 秒数 その場で見せる 1時間 3600 当日中に見てほしい 24時間 86400 数日中に見てほしい 3日間 259200 1週間以内に見てほしい 7日間 604800 注意: 署名付きURLの最大有効期限は7日間です（IAMユーザーの認証情報を使う場合）。\n注意点 1. URLを知っていれば誰でもアクセスできる 署名付きURLは「知っている人なら誰でも」アクセスできます。URLが漏れると意図しない人にも見られる可能性があります。\n対策: 信頼できる相手にだけ共有し、有効期限を短めに設定する。\n2. 一度発行したURLは取り消せない URLを発行した後、「やっぱり取り消したい」と思っても、有効期限が切れるまで無効化できません。\n対策: 有効期限を必要最小限にする。どうしても無効化したい場合はファイル自体を削除する。\n3. ダウンロード回数は制限できない 署名付きURLでは「3回までダウンロード可能」のような制限はできません。\n対策: 期限を短くするか、CloudFrontの署名付きURLを検討する。\n4. 大量アクセスには向かない 署名付きURLは一時的な共有向けです。多数のユーザーに配信する場合は、CloudFrontとの組み合わせを検討しましょう。\n料金について 署名付きURLの生成自体は無料です。\nただし、以下の料金は発生します。\nS3ストレージ料金: ファイルの保存容量に応じて データ転送料金: ダウンロードされた分だけ 少人数への共有なら、ほぼ無視できる金額です（数円〜数十円程度）。\nまとめ 署名付きURLのポイントをまとめます。\n期限付きでプライベートファイルを共有できる S3バケットは非公開のままでOK AWS CLIなら aws s3 presign で簡単に生成 最大有効期限は7日間 URLが漏れるとアクセスされるので、信頼できる相手にだけ共有 友人への動画共有など、少人数への一時的な共有にはぴったりの方法です。\n理解度チェック 問1 署名付きURLを使うと、S3バケットの公開設定はどうなりますか？\n答え 非公開のままでOK。署名付きURL自体にアクセス権限の情報が含まれているため、バケットを公開する必要はない。 問2 署名付きURLの最大有効期限は何日間ですか？\n答え 7日間（604800秒）。IAMユーザーの認証情報を使う場合の制限。 問3 発行した署名付きURLを途中で無効化できますか？\n答え できない。有効期限が切れるまで有効。無効化したい場合はファイル自体を削除する必要がある。 問4 AWS CLIで署名付きURLを生成するコマンドは何ですか？\n答え `aws s3 presign s3://バケット名/ファイル名`。有効期限を指定する場合は `--expires-in 秒数` オプションを追加する。 問5 署名付きURLの生成自体に料金はかかりますか？\n答え かからない（無料）。ただし、S3ストレージ料金とダウンロード時のデータ転送料金は発生する。 問6 署名付きURLで「3回までダウンロード可能」のような回数制限はできますか？\n答え できない。署名付きURLでは期限内であれば何度でもアクセス可能。回数制限が必要な場合はCloudFrontの署名付きURLなど別の方法を検討する。 問7 署名付きURLが漏れた場合のリスクを軽減するための対策を2つ挙げてください。\n答え 1. 有効期限を必要最小限に設定する。2. 信頼できる相手にだけURLを共有する。（他にも「どうしても無効化したい場合はファイル自体を削除する」なども可） ","permalink":"https://makiharu.github.io/persona-blog/tech/s3-presigned-url/","summary":"AWS S3の署名付きURL（Presigned URL）を使って、期限付きで安全にファイルを共有する方法を初心者向けに解説します。","title":"S3署名付きURL入門 - 期限付きで安全にファイルを共有する方法"},{"content":"はじめに TypeScriptには、型を便利に変換できるUtility Types（ユーティリティ型） が用意されています。その中でも特によく使われるのが Partial\u0026lt;T\u0026gt; です。\nこの記事では、Partial型の基本から実践的な使い方まで、初心者向けに解説します。\nPartial型とは？ Partial\u0026lt;T\u0026gt; は、オブジェクト型のすべてのプロパティをオプショナル（省略可能） にする型です。\n1 2 3 type Partial\u0026lt;T\u0026gt; = { [P in keyof T]?: T[P]; }; 簡単に言うと、「全部のプロパティに ? をつける」ということです。\n基本的な使い方 Before: Partial なし 1 2 3 4 5 6 7 8 9 10 11 12 type User = { id: number; name: string; email: string; }; // すべてのプロパティが必須 const user: User = { id: 1, name: \u0026#34;田中太郎\u0026#34;, email: \u0026#34;tanaka@example.com\u0026#34; }; After: Partial あり 1 2 3 4 5 6 7 8 9 10 11 type User = { id: number; name: string; email: string; }; // すべてのプロパティがオプショナルになる const partialUser: Partial\u0026lt;User\u0026gt; = { name: \u0026#34;田中太郎\u0026#34; // id と email は省略OK }; Partial\u0026lt;User\u0026gt; は以下と同じ意味になります。\n1 2 3 4 5 type PartialUser = { id?: number; name?: string; email?: string; }; 実践的なユースケース 1. 更新処理（PATCH） APIでデータを部分更新するとき、Partial型が活躍します。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 type User = { id: number; name: string; email: string; age: number; }; // 更新したいフィールドだけ渡せる function updateUser(id: number, updates: Partial\u0026lt;User\u0026gt;) { // データベースを更新する処理 console.log(`Updating user ${id}:`, updates); } // 名前だけ更新 updateUser(1, { name: \u0026#34;新しい名前\u0026#34; }); // メールと年齢を更新 updateUser(2, { email: \u0026#34;new@example.com\u0026#34;, age: 30 }); 2. デフォルト値とのマージ 設定オブジェクトでよく使われるパターンです。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 type Config = { theme: \u0026#34;light\u0026#34; | \u0026#34;dark\u0026#34;; fontSize: number; showSidebar: boolean; }; const defaultConfig: Config = { theme: \u0026#34;light\u0026#34;, fontSize: 14, showSidebar: true }; function createConfig(options: Partial\u0026lt;Config\u0026gt;): Config { return { ...defaultConfig, ...options }; } // 一部だけ指定すればOK const myConfig = createConfig({ theme: \u0026#34;dark\u0026#34; }); // 結果: { theme: \u0026#34;dark\u0026#34;, fontSize: 14, showSidebar: true } 3. フォームの状態管理 フォームの入力状態を管理するときにも便利です。\n1 2 3 4 5 6 7 8 9 10 11 12 type FormData = { username: string; email: string; password: string; }; // 初期状態は空でもOK const formState: Partial\u0026lt;FormData\u0026gt; = {}; // ユーザーが入力するたびに更新 formState.username = \u0026#34;user123\u0026#34;; formState.email = \u0026#34;user@example.com\u0026#34;; 4. テストデータの作成 テストで一部のプロパティだけ指定したいときに役立ちます。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 type Product = { id: number; name: string; price: number; description: string; category: string; }; function createTestProduct(overrides: Partial\u0026lt;Product\u0026gt; = {}): Product { return { id: 1, name: \u0026#34;テスト商品\u0026#34;, price: 1000, description: \u0026#34;テスト用の商品です\u0026#34;, category: \u0026#34;テスト\u0026#34;, ...overrides }; } // 価格だけ変えたテストデータを作成 const expensiveProduct = createTestProduct({ price: 99999 }); Partial型の注意点 1. ネストしたオブジェクトには効かない Partial型は1階層目のプロパティのみオプショナルにします。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type User = { id: number; profile: { name: string; age: number; }; }; type PartialUser = Partial\u0026lt;User\u0026gt;; // 結果: // { // id?: number; // profile?: { // name: string; ← オプショナルにならない // age: number; ← オプショナルにならない // }; // } 深い階層もオプショナルにしたい場合は、DeepPartialを自作する必要があります。\n1 2 3 type DeepPartial\u0026lt;T\u0026gt; = { [P in keyof T]?: T[P] extends object ? DeepPartial\u0026lt;T[P]\u0026gt; : T[P]; }; 2. undefinedが許容される オプショナルプロパティは undefined を許容します。\n1 2 3 const user: Partial\u0026lt;User\u0026gt; = { name: undefined // これもOK }; 関連するUtility Types Partial型と一緒に覚えておくと便利な型です。\n型 説明 Partial\u0026lt;T\u0026gt; すべてのプロパティをオプショナルに Required\u0026lt;T\u0026gt; すべてのプロパティを必須に（Partialの逆） Pick\u0026lt;T, K\u0026gt; 指定したプロパティだけを抽出 Omit\u0026lt;T, K\u0026gt; 指定したプロパティを除外 組み合わせの例 1 2 3 4 5 6 7 8 9 10 11 12 13 type User = { id: number; name: string; email: string; }; // idは必須、他はオプショナル type UserUpdate = Pick\u0026lt;User, \u0026#34;id\u0026#34;\u0026gt; \u0026amp; Partial\u0026lt;Omit\u0026lt;User, \u0026#34;id\u0026#34;\u0026gt;\u0026gt;; const update: UserUpdate = { id: 1, // 必須 name: \u0026#34;新しい名前\u0026#34; // オプショナル }; まとめ Partial型のポイントをまとめます。\nPartial\u0026lt;T\u0026gt; はすべてのプロパティをオプショナルにする 更新処理やデフォルト値のマージで特に便利 ネストしたオブジェクトには効かない（1階層のみ） Required\u0026lt;T\u0026gt; は逆の動作（すべて必須に） Partial型を使いこなせると、より柔軟で型安全なコードが書けるようになります。\n理解度チェック 問1 Partial\u0026lt;T\u0026gt; は何をする型ですか？\n答え オブジェクト型Tのすべてのプロパティをオプショナル（省略可能）にする。 問2 以下のコードで partialUser に代入できる値はどれですか？\n1 2 type User = { id: number; name: string; }; const partialUser: Partial\u0026lt;User\u0026gt; = ???; A. {} B. { id: 1 } C. { name: \u0026quot;太郎\u0026quot; } D. すべて正解\n答え D. すべて正解。Partial型ではすべてのプロパティがオプショナルになるため、空オブジェクトも含めすべて有効。 問3 Partial型がネストしたオブジェクトに対して「浅い」と言われる理由は何ですか？\n答え 1階層目のプロパティのみオプショナルになり、ネストしたオブジェクト内部のプロパティはオプショナルにならないため。 ","permalink":"https://makiharu.github.io/persona-blog/tech/typescript-partial/","summary":"TypeScriptのPartial型について初心者向けに解説します。使い方、実践例、仕組みまでわかりやすく説明します。","title":"TypeScript Partial型入門 - オブジェクトのプロパティをすべてオプショナルにする"},{"content":"はじめに TypeScriptのコードを読んでいると、こんな記法を見かけませんか？\n1 2 3 function example\u0026lt;T\u0026gt;(value: T): T { return value; } この \u0026lt;T\u0026gt; が何なのかわからず、モヤモヤしている人も多いのではないでしょうか。\nこの記事では、ジェネリクス（Generics） の基本を初心者向けに解説します。\nジェネリクスとは？ ジェネリクスを一言で説明すると、「型の変数」 です。\n普通の変数が「値」を入れる箱だとすると、ジェネリクスは「型」を入れる箱です。\n1 2 3 4 5 // 普通の変数：値を入れる let value = 100; // ジェネリクス：型を入れる // \u0026lt;T\u0026gt; の T に string や number などの型が入る なぜジェネリクスが必要なの？ 問題：型ごとに関数を作るのは大変 「渡された値をそのまま返す」関数を作りたいとします。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 // number用 function returnNumber(value: number): number { return value; } // string用 function returnString(value: string): string { return value; } // boolean用 function returnBoolean(value: boolean): boolean { return value; } 型ごとに同じような関数を作るのは面倒ですよね。\n解決策1：any型を使う（ダメな例） 1 2 3 4 5 6 function returnAny(value: any): any { return value; } const result = returnAny(\u0026#34;hello\u0026#34;); // result の型は any... 型安全じゃない！ any を使うと、戻り値の型情報が失われてしまいます。\n解決策2：ジェネリクスを使う（良い例） 1 2 3 4 5 6 7 function returnValue\u0026lt;T\u0026gt;(value: T): T { return value; } const num = returnValue\u0026lt;number\u0026gt;(100); // num は number型 const str = returnValue\u0026lt;string\u0026gt;(\u0026#34;hello\u0026#34;); // str は string型 const bool = returnValue(true); // bool は boolean型（型推論） ジェネリクスを使えば、型安全を保ちながら、様々な型に対応できます。\n基本的な書き方 関数のジェネリクス 1 2 3 4 5 6 7 8 // 基本形 function 関数名\u0026lt;T\u0026gt;(引数: T): T { return 引数; } // 使い方 関数名\u0026lt;string\u0026gt;(\u0026#34;hello\u0026#34;); // 明示的に型を指定 関数名(\u0026#34;hello\u0026#34;); // 型推論に任せる（省略可） T は慣習的に使われる名前ですが、何でもOKです。\n1 2 3 function example\u0026lt;Type\u0026gt;(value: Type): Type { ... } function example\u0026lt;U\u0026gt;(value: U): U { ... } function example\u0026lt;TData\u0026gt;(value: TData): TData { ... } よく使われる名前 名前 意味 使用例 T Type（型） 一般的な型 K Key（キー） オブジェクトのキー V Value（値） オブジェクトの値 E Element（要素） 配列の要素 R Return（戻り値） 関数の戻り値 実践例 例1：配列の最初の要素を取得 1 2 3 4 5 6 7 8 9 function getFirst\u0026lt;T\u0026gt;(arr: T[]): T | undefined { return arr[0]; } const nums = [1, 2, 3]; const first = getFirst(nums); // first は number | undefined const strs = [\u0026#34;a\u0026#34;, \u0026#34;b\u0026#34;, \u0026#34;c\u0026#34;]; const firstStr = getFirst(strs); // firstStr は string | undefined 例2：2つの値をペアにする 1 2 3 4 5 6 function makePair\u0026lt;T, U\u0026gt;(first: T, second: U): [T, U] { return [first, second]; } const pair = makePair(\u0026#34;name\u0026#34;, 100); // pair の型は [string, number] 複数のジェネリクスを使う場合は、カンマで区切ります。\n例3：型に制約をつける（extends） 特定の型だけに限定したい場合は extends を使います。\n1 2 3 4 5 6 7 8 // T は { length: number } を持つ型に限定 function getLength\u0026lt;T extends { length: number }\u0026gt;(value: T): number { return value.length; } getLength(\u0026#34;hello\u0026#34;); // OK: stringはlengthを持つ getLength([1, 2, 3]); // OK: 配列はlengthを持つ getLength(100); // エラー: numberはlengthを持たない 型（インターフェース）のジェネリクス 関数だけでなく、型定義にもジェネリクスが使えます。\n例1：APIレスポンスの型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // 汎用的なAPIレスポンス型 type ApiResponse\u0026lt;T\u0026gt; = { data: T; status: number; message: string; }; // User用のレスポンス type User = { id: number; name: string }; type UserResponse = ApiResponse\u0026lt;User\u0026gt;; // { // data: { id: number; name: string }; // status: number; // message: string; // } // Product用のレスポンス type Product = { id: number; price: number }; type ProductResponse = ApiResponse\u0026lt;Product\u0026gt;; 例2：Reactのステート（useState） Reactを使ったことがある人は、これを見たことがあるはずです。\n1 2 3 const [count, setCount] = useState\u0026lt;number\u0026gt;(0); const [name, setName] = useState\u0026lt;string\u0026gt;(\u0026#34;\u0026#34;); const [user, setUser] = useState\u0026lt;User | null\u0026gt;(null); useState はジェネリクスを使って、ステートの型を指定しています。\nTypeScript標準のジェネリクス型 TypeScriptには便利なジェネリクス型が用意されています。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // Array\u0026lt;T\u0026gt;: 配列 const nums: Array\u0026lt;number\u0026gt; = [1, 2, 3]; // number[] と同じ // Promise\u0026lt;T\u0026gt;: 非同期処理の結果 const promise: Promise\u0026lt;string\u0026gt; = fetch(\u0026#34;/api\u0026#34;).then(r =\u0026gt; r.text()); // Partial\u0026lt;T\u0026gt;: すべてのプロパティをオプショナルに type PartialUser = Partial\u0026lt;User\u0026gt;; // Record\u0026lt;K, V\u0026gt;: キーと値の型を指定したオブジェクト const scores: Record\u0026lt;string, number\u0026gt; = { math: 90, english: 85 }; ジェネリクスの読み方 コードを読むときのコツです。\n1 function map\u0026lt;T, U\u0026gt;(arr: T[], fn: (item: T) =\u0026gt; U): U[] \u0026lt;T, U\u0026gt; → 「TとUという2つの型変数を使う」 arr: T[] → 「arrはT型の配列」 fn: (item: T) =\u0026gt; U → 「fnはTを受け取ってUを返す関数」 : U[] → 「戻り値はU型の配列」 つまり「T型の配列を受け取り、各要素をU型に変換した配列を返す」関数です。\nまとめ ジェネリクスのポイントをまとめます。\nジェネリクス（\u0026lt;T\u0026gt;）は型の変数 型安全を保ちながら、様々な型に対応できる any を使わずに柔軟な関数・型が作れる T は慣習的な名前。Type など好きな名前でOK extends で型に制約をつけられる 最初は難しく感じるかもしれませんが、「型を入れる箱」と考えれば理解しやすくなります。\n理解度チェック 問1 ジェネリクスを一言で説明すると何ですか？\n答え 型の変数。値ではなく「型」を入れる箱のようなもの。 問2 以下のコードで result の型は何になりますか？\n1 2 3 4 function identity\u0026lt;T\u0026gt;(value: T): T { return value; } const result = identity(\u0026#34;hello\u0026#34;); 答え string型。\"hello\"という文字列を渡しているので、Tはstringと推論される。 問3 any ではなくジェネリクスを使うメリットは何ですか？\n答え 型情報が保持され、型安全が保たれる。anyを使うと戻り値などの型情報が失われてしまう。 問4 \u0026lt;T extends string\u0026gt; はどういう意味ですか？\n答え 型Tはstring型（またはstringのサブタイプ）に限定される、という制約。 ","permalink":"https://makiharu.github.io/persona-blog/tech/typescript-generics/","summary":"TypeScriptの\u003cT\u003e（ジェネリクス）が何なのか、初心者向けにわかりやすく解説します。","title":"TypeScript ジェネリクス入門 - \u003cT\u003eって何？を初心者向けに解説"},{"content":"はじめに Webアプリケーションを開発していると、「JWT」という言葉をよく目にします。ログイン機能やAPI認証で使われる重要な技術ですが、最初は少しとっつきにくいかもしれません。\nこの記事では、JWTの基本を初心者向けにわかりやすく解説します。\nJWTとは？ JWT（JSON Web Token） は、情報を安全にやり取りするためのトークン形式です。\n簡単に言うと、「誰が何の権限を持っているか」を証明するデジタルな身分証明書のようなものです。\nなぜJWTが必要なの？ 従来のWebアプリケーションでは、ログイン状態をサーバー側の「セッション」で管理していました。しかし、この方法にはいくつかの課題があります。\n方式 特徴 セッション方式 サーバーがログイン状態を記憶する必要がある JWT方式 トークン自体に情報が含まれ、サーバーは記憶不要 JWTを使うと、サーバーは状態を持たなくて済むため（ステートレス）、複数のサーバーに分散させやすくなります。\nJWTの構造 JWTは3つの部分で構成されています。それぞれ ドット（.） で区切られています。\n1 2 3 xxxxx.yyyyy.zzzzz ↓ ↓ ↓ Header.Payload.Signature 実際のJWTはこのような文字列です。\n1 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30 一見すると暗号のように見えますが、実はBase64URLでエンコードされているだけです。\n1. Header（ヘッダー） トークンのタイプと、署名に使うアルゴリズムを指定します。\n1 2 3 4 { \u0026#34;alg\u0026#34;: \u0026#34;HS256\u0026#34;, \u0026#34;typ\u0026#34;: \u0026#34;JWT\u0026#34; } alg: 署名アルゴリズム（HS256, RS256など） typ: トークンのタイプ（JWT） 2. Payload（ペイロード） 実際に伝えたい情報（クレームと呼ばれます）が含まれます。\n1 2 3 4 5 6 { \u0026#34;sub\u0026#34;: \u0026#34;1234567890\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;John Doe\u0026#34;, \u0026#34;iat\u0026#34;: 1516239022, \u0026#34;exp\u0026#34;: 1516242622 } よく使われるクレーム（予約済み）:\nクレーム 説明 sub Subject（主題）- ユーザーIDなど iat Issued At（発行日時） exp Expiration（有効期限） iss Issuer（発行者） aud Audience（対象者） 注意: Payloadは暗号化されていません。Base64でデコードすれば誰でも中身を見れます。パスワードなどの機密情報は絶対に入れないでください。\n3. Signature（署名） ヘッダーとペイロードを結合し、秘密鍵で署名したものです。\n1 2 3 4 HMACSHA256( base64UrlEncode(header) + \u0026#34;.\u0026#34; + base64UrlEncode(payload), secret ) この署名により、トークンが改ざんされていないことを検証できます。\nJWTの流れ 実際の認証フローを見てみましょう。\n1 2 3 4 5 6 7 8 9 10 1. ユーザー: IDとパスワードでログイン ↓ 2. サーバー: 認証OK → JWTを発行して返す ↓ 3. クライアント: JWTを保存（localStorageやCookieなど） ↓ 4. クライアント: APIリクエスト時にJWTを送信 Authorization: Bearer eyJhbGciOi... ↓ 5. サーバー: JWTの署名を検証 → OKならリクエストを処理 JWTを実際に見てみよう jwt.io というサイトで、JWTをデコードして中身を確認できます。\n試しに以下のトークンをデコードしてみてください。\n1 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c JWTのメリット・デメリット メリット ステートレス: サーバーがセッション情報を保持する必要がない スケーラブル: 複数サーバー間で共有しやすい クロスドメイン: 異なるドメイン間でも使いやすい モバイルフレンドリー: ネイティブアプリでも扱いやすい デメリット トークンサイズ: セッションIDより大きくなりがち 無効化が難しい: 発行したトークンを途中で無効にするのが難しい Payload丸見え: 暗号化されていないので機密情報は入れられない セキュリティの注意点 JWTを安全に使うために、以下の点に注意しましょう。\n1. 秘密鍵は厳重に管理する 秘密鍵が漏れると、誰でもトークンを偽造できてしまいます。\n2. 有効期限を短くする expクレームで有効期限を設定し、できるだけ短くしましょう。長期間有効なトークンはリスクが高くなります。\n3. HTTPSを使う JWTは平文で送信されるため、必ずHTTPSで通信しましょう。\n4. 機密情報をPayloadに入れない Payloadは暗号化されていません。パスワードやクレジットカード番号などは絶対に入れないでください。\n5. アルゴリズムを固定する サーバー側で受け入れるアルゴリズムを明示的に指定しましょう。alg: none攻撃を防ぐためです。\nよくある質問 Q: JWTはどこに保存すべき？ A: 用途によりますが、一般的には以下の選択肢があります。\n保存場所 特徴 HttpOnly Cookie XSS攻撃に強い。CSRF対策が必要 localStorage 使いやすいがXSS攻撃に弱い メモリ（変数） 最も安全だがページリロードで消える セキュリティを重視するならHttpOnly Cookieがおすすめです。\nQ: JWTとセッションどちらを使うべき？ A: どちらが良いかは要件次第です。\nJWT向き: マイクロサービス、SPA、モバイルアプリ、サーバーレス セッション向き: 従来のWebアプリ、即座にログアウトさせたい場合 まとめ JWTの基本をまとめると以下の通りです。\nJWTはHeader.Payload.Signatureの3部構成 Payloadに情報を含み、Signatureで改ざんを検知 ステートレスな認証を実現できる Payloadは暗号化されていないので機密情報は入れない 有効期限とHTTPSでセキュリティを確保 JWTは現代のWeb開発において欠かせない技術です。基本を理解した上で、適切に活用していきましょう。\n参考リンク JWT.IO - JWTのデコード・検証ツール RFC 7519 - JWT の公式仕様 理解度チェック 理解度チェッククイズ（全5問）\n問1\nJWTが「ステートレス」と呼ばれるのはなぜですか？\n答え サーバーがログイン状態を保持せず、トークン自体に必要な情報が含まれているため。 問2\nJWTのPayloadにパスワードなどの機密情報を入れてはいけない理由は何ですか？\n答え Payloadは暗号化されておらず、Base64デコードすれば誰でも中身を見られるため。 問3\nJWTのSignature（署名）は何を保証するためのものですか？\n答え トークンが発行後に改ざんされていないことを保証するため。 問4\nJWTを使った認証で、クライアントはAPIリクエスト時にトークンをどこに送りますか？\n答え HTTPヘッダーの `Authorization: Bearer ` に含めて送信する。 問5\nJWTに exp（有効期限）を設定するのはなぜですか？\n答え トークンが漏洩した場合の被害を最小限に抑えるため。 問6\nJWTは途中で無効化しづらいという欠点があります。 この問題を軽減するために、よく使われる設計は何でしょう？\n答え 有効期限を短くし、リフレッシュトークンと組み合わせて運用する。 ","permalink":"https://makiharu.github.io/persona-blog/tech/jwt/","summary":"JWT（JSON Web Token）の仕組みを初心者向けにわかりやすく解説します。構造、使い方、セキュリティの注意点まで基礎から学べます。","title":"JWT入門 - 初心者のためのJSON Web Token"},{"content":"","permalink":"https://makiharu.github.io/persona-blog/posts/test/","summary":"","title":"Test"},{"content":"About Me kurino\nInterests 技術 手品 読書 etc\u0026hellip; Contact GitHub: github ","permalink":"https://makiharu.github.io/persona-blog/about/","summary":"\u003ch2 id=\"about-me\"\u003eAbout Me\u003c/h2\u003e\n\u003cp\u003ekurino\u003c/p\u003e\n\u003ch2 id=\"interests\"\u003eInterests\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e技術\u003c/li\u003e\n\u003cli\u003e手品\u003c/li\u003e\n\u003cli\u003e読書\u003c/li\u003e\n\u003cli\u003eetc\u0026hellip;\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"contact\"\u003eContact\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eGitHub: \u003ca href=\"https://github.com/makiharu%22\"\u003egithub\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e","title":"Profile"},{"content":"はじめに 内容 日常のことなど、自由に書いていきます。\n","permalink":"https://makiharu.github.io/persona-blog/misc/hello-misc/","summary":"雑記/走り書き ページのintoroduction","title":"はじめに"},{"content":"はじめに これは技術カテゴリのサンプル記事です。\nコードサンプル 1 2 def hello(): print(\u0026#34;Hello, World!\u0026#34;) まとめ 記事の内容をここに書いていきます。\n","permalink":"https://makiharu.github.io/persona-blog/tech/hello-tech/","summary":"技術カテゴリのサンプル記事です。","title":"技術カテゴリのサンプル記事"},{"content":"はじめに これは読んだもの見たもののサンプル記事です。\n書籍/映画タイトル 感想をここに書いていきます。\n評価 おすすめ度など。\n","permalink":"https://makiharu.github.io/persona-blog/reviews/hello-review/","summary":"読んだもの見たもののサンプル記事です。","title":"読んだもの見たもののサンプル記事"},{"content":"WebRTC・WebSocket・シグナリング・呼制御・Socket.ioクライアント、Socket.ioサーバー、TURN、CORN、画面共有の仕組みについて段階的に理解する。\nゴール Twilio(あるいはzoomからリアルタイム通信の仕組みを理解する)\n構成 React │ Socket.io │ WebSocket（TCP） │ シグナリング │ WebRTC │ UDP │ 音声・映像\nQ. 普段Web開発で使うHTTP通信と比べて、WebSocketは何が違うのか——特に「誰が」「いつ」データを送れるのか、という点に注目して調べてみてください。\nHttp通信の場合はライアントからリクエストを投げたらサーバーからレスポンスが入ってくると言う。1方向的な通信のやりとりを行うが、それに対してウェブソケットはリアルタイム通信、つまり双方向通信を行うことができる。\n補足:\nHTTP: クライアントが要求 → サーバーが応答、で1往復が完結。次のやり取りをするには再度リクエストが必要（コネクションはその都度使い捨てに近い）。 WebSocket: 最初の接続確立だけHTTPのハンドシェイク（Upgrade: websocket）を使うが、一度繋がるとコネクションを張りっぱなしにする。だから「クライアントから聞かれてないのにサーバーから送る」ということができる＝双方向・非同期。 Q. では素のWebSocketがあるのに、なぜ実務では「Socket.io」というライブラリを使うのか？ WebSocketだけでは足りない・面倒な点を調べてみてください。\nWebSocketはリアルタイム通信の仕組みそのものですが、\nSocket.ioは、 部屋(Room)管理 自動再接続 イベント管理 接続・切断通知 ブロードキャスト（全員・特定の部屋への送信） などの機能を提供してくれるライブラリのため、 WebSocketで一から自前実装するよりも開発効率が高く、保守しやすいため利用される。\n補足: Socket.ioは「WebSocketの上位互換」ではなく、フォールバック機構を持つ点も大きいです。WebSocketが使えない環境（古いプロキシ等）では自動的にlong-pollingに切り替えます。つまりSocket.io自体が独自のプロトコルで、内部で「使えるならWebSocket、ダメならHTTPポーリング」を切り替えている、という位置づけです。\nQ. Socket.io（サーバー経由の通信）があるのに、なぜビデオ通話や画面共有には別途「WebRTC」という技術が必要なのか？ 音声・映像データがサーバーを経由する場合としない場合で、何が変わるか考えてみてください。\n映像、音声データは通信遅延を避けるためにUDPプロトコルを利用している。サーバーを経由する場合には、TCPプロトコルによる通信となるが、TCPプロトコルの通信の場合は、信頼性の確立のための仕組みが整っている反面、音声・映像のデータはデータサイズが多すぎて、通信をする際に、データ破損などが起こりやすく、通信遅延、通信事故が発生してしまう。そのため、UWPプロトコルを利用するのに、WebRTCという通信技術を使って映像、音声データはやり取りをする必要がある\n良い着眼点です。UDP/TCPの違いは正しいですが、もう一つ重要な観点が抜けています。\n✅ プロトコルの違い: 音声・映像はSRTP（UDPベース）を使う。TCPは再送・順序保証の仕組みがある分、パケットロス時に「待ち」が発生し、リアルタイム性を損なう（多少コマ落ちしても待たされるよりマシ、という発想）。 ⚠️ もう一つの本質: 仮にサーバー経由でUDPを使えたとしても、全員の映像・音声データがサーバーを通ると、参加者が増えるほどサーバーの帯域・処理コストが跳ね上がり、サーバーが1つ余分な中継点＝遅延要因にもなる。WebRTCの一番の価値は「サーバーを介さずピア同士が直接繋がる（P2P）」こと。UDPを使うのは手段の一つで、目的は「サーバーを経由しない直接通信」を実現すること。 Q. ピア同士が「直接」繋がるには、事前にお互いのIPアドレスなどの接続情報を知る必要があります。でも見知らぬ相手同士がいきなり直接通信することはできません。この「最初の情報交換」はどうやって行うのでしょうか？ （ここでSocket.ioが再登場します）\n自分の回答\n1 シグナリングを行う。シグナリングとは、P2P通信のやりとりをするための接続情報を交換する仕組み。 ✅ 仕組みの本質: シグナリングとは「まだP2Pで直接繋がっていない2人が、繋がるために必要な情報を、既存の別の通信路（Socket.io/WebSocketなど）経由でやり取りすること」。 📌 重要な補足: WebRTC自体は「シグナリングをどうやるか」を規定していません。Socket.ioでも、素のWebSocketでも、極端な話メールでコピペしてもいい（実用的ではないですが）。あくまで**サーバーを介した「仲介」**であって、通信方式は自由です。 📌 実際に交換するものは次の2種類です: a. SDP（Session Description Protocol）: 「どんなコーデック・解像度で通信したいか」という能力・条件の記述 b. ICE candidate: 実際の接続先候補（IP:ポートの組み合わせ）のリスト このSDPのやり取りの手順こそが「呼制御」です。\nSDP, ICEがわからない。\n直接説明します。\nSDP（Session Description Protocol） 実際の映像・音声データではなく、「私はこういう通信をしたい/できます」というスペック表のテキストです。中身の例:\n音声/映像どちらを使うか 対応コーデック（映像ならVP8, H264など、音声ならOpusなど） 解像度・ビットレート 送受信の方向（送るだけ/受けるだけ/双方向） これを2人が交換して「じゃあお互いが対応してる形式はこれだね」とすり合わせる。\nICE（Interactive Connectivity Establishment） 「実際にどの経路（IP:ポート）でデータを送れば相手に届くか」を探す仕組みです。NATやファイアウォールがあると自分の本当のグローバルIPが分からないので、候補（ICE candidate）を複数集めます。\n自分のローカルIP STUNサーバーに問い合わせて分かった「外から見た自分のIP:ポート」 （直接繋がらない時の最終手段の）TURNサーバー経由のアドレス これらの候補リストをお互いに交換し、実際に繋がるかテスト（疎通確認）して、一番良い経路を選びます。\n例えるなら: SDPは「私は日本語と英語話せます、ビデオ通話できます」という自己紹介カード。ICEは「直通番号／転送番号／交換手経由、どの電話番号なら実際に繋がるか」を試す作業です。\nでは改めて。\nQ. Offer/Answerのやり取りは、どちらが先に「SDP」を作って送るのでしょうか？発信する側（かける人）と、受ける側（かけられる人）、どちらが Offer を作ると思いますか？\n自分の回答 発信側がSDPを作って送る。\n発信側（Caller）: createOffer() でSDP(Offer)を作る → 自分にセット(setLocalDescription) → シグナリングサーバー（Socket.io）経由で相手に送る 着信側（Callee）: Offerを受け取る → 自分にセット(setRemoteDescription) → createAnswer() でSDP(Answer)を作る → 自分にセット(setLocalDescription) → シグナリング経由で送り返す 発信側: Answerを受け取り setRemoteDescription → お互いの「スペック表」が揃う これと並行して、両者は自分のICE candidateを見つけ次第、随時シグナリング経由で相手に送り続ける（onicecandidateイベント） 双方がSDP交換＋十分なICE candidateの疎通確認を終えると、直接のP2P接続が確立する つまりOffer/AnswerとICE candidateの交換は同時並行で進みます。SDPが「何を話すか」、ICEが「どの経路で話すか」の役割分担です。\nlocalDescription と remoteDescriptionにセットするとはどういうことなのか？ RTCPeerConnectionオブジェクトは内部に localDescription と remoteDescription という**2つのプロパティ（変数のようなもの）**を持っています。それぞれにSDPのテキストデータが1つずつ格納される、という意味で「スロット（入れ物）」と表現しました。\nconst pc = new RTCPeerConnection(); // pc.localDescription ← ここに自分のSDPが入る // pc.remoteDescription ← ここに相手のSDPが入る\nawait pc.setLocalDescription(offer); // pc.localDescription に offer が代入される await pc.setRemoteDescription(answer); // pc.remoteDescription に answer が代入される\nつまり「オブジェクトが持つ2つのプロパティに、それぞれSDPを書き込む」というだけの操作です。特別な仕組みではなく普通のオブジェクトの状態管理だと考えてもらえれば大丈夫です。\n実際のSDPはこんなテキストです（簡略化した例）。\nv=0 o=- 4611731400430051336 2 IN IP4 127.0.0.1 s=- t=0 0 m=audio 9 UDP/TLS/RTP/SAVPF 111 103 a=rtpmap:111 opus/48000/2 a=fmtp:111 minptime=10;useinbandfec=1 a=sendrecv m=video 9 UDP/TLS/RTP/SAVPF 96 97 a=rtpmap:96 VP8/90000 a=rtpmap:97 H264/90000 a=sendrecv\n読み方:\nm=audio \u0026hellip; / m=video \u0026hellip;: 「音声トラックと映像トラックがありますよ」という宣言 111, 103, 96, 97: 対応コーデックの候補番号（複数提示している＝「これらのどれかで話せます」） a=rtpmap:111 opus/48000/2: 番号111は「Opusコーデック、サンプリングレート48000Hz、2チャンネル（ステレオ）」という意味 a=rtpmap:96 VP8/90000: 番号96は「VP8コーデック」 a=sendrecv: 「送信も受信もします」（sendonly/recvonly/inactiveもあり得る） これが両者から届くと、ブラウザは自動で「相手も自分もVP8とOpusに対応してるから、それを使おう」とすり合わせます。人間が読むというより、ブラウザ同士が機械的に照合するためのフォーマットです。\nイメージつきましたか？OKなら、STUN/TURNの問いに進みましょう。\nQ. STUNサーバーとTURNサーバーは何が違うのでしょうか？STUNだけでは繋がらないケースがあるのはなぜでしょうか？\n気になった点 ❯ 発信側コーラーがSD Pを作るのは理解しましたが、自分にセットすると言うのはどういうことでしょうか？また着信側もオファーを受け取った後に自分にセットしていますが、ここのセットと言うのは具体的にどういったこと？リモートとlocalのセットがありそう。\n次の問い、STUN/TURNです。\nQ. ICE candidateを集める際に「STUNサーバー」と「TURNサーバー」という2種類が出てきます。この2つは何が違うのでしょうか？STUNだけでは繋がらないケースがあるのはなぜでしょうか？\nQ. 電話をかける時の「発信→呼び出し中→応答→通話→切断」のような一連の流れを、WebRTCでは「Offer」「Answer」という言葉を使って表現します。この Offer/Answer のやり取りはどういう順番で進むと思いますか？ 調べて図解できそうなら流れを書いてみてください。\n参考 socket.ioライブラリ https://socket.io/docs/v4/tutorial/step-1\n","permalink":"https://makiharu.github.io/persona-blog/tech/0712-webrtc/","summary":"\u003cp\u003eWebRTC・WebSocket・シグナリング・呼制御・Socket.ioクライアント、Socket.ioサーバー、TURN、CORN、画面共有の仕組みについて段階的に理解する。\u003c/p\u003e\n\u003cp\u003eゴール\nTwilio(あるいはzoomからリアルタイム通信の仕組みを理解する)\u003c/p\u003e\n\u003ch2 id=\"構成\"\u003e構成\u003c/h2\u003e\n\u003cp\u003eReact\n│\nSocket.io\n│\nWebSocket（TCP）\n│\nシグナリング\n│\nWebRTC\n│\nUDP\n│\n音声・映像\u003c/p\u003e\n\u003cp\u003eQ.  普段Web開発で使うHTTP通信と比べて、WebSocketは何が違うのか——特に「誰が」「いつ」データを送れるのか、という点に注目して調べてみてください。\u003c/p\u003e\n\u003cp\u003eHttp通信の場合はライアントからリクエストを投げたらサーバーからレスポンスが入ってくると言う。1方向的な通信のやりとりを行うが、それに対してウェブソケットはリアルタイム通信、つまり双方向通信を行うことができる。\u003c/p\u003e\n\u003cp\u003e補足:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eHTTP: クライアントが要求 → サーバーが応答、で1往復が完結。次のやり取りをするには再度リクエストが必要（コネクションはその都度使い捨てに近い）。\u003c/li\u003e\n\u003cli\u003eWebSocket: 最初の接続確立だけHTTPのハンドシェイク（Upgrade: websocket）を使うが、一度繋がるとコネクションを張りっぱなしにする。だから「クライアントから聞かれてないのにサーバーから送る」ということができる＝双方向・非同期。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eQ. では素のWebSocketがあるのに、なぜ実務では「Socket.io」というライブラリを使うのか？ WebSocketだけでは足りない・面倒な点を調べてみてください。\u003c/p\u003e\n\u003cp\u003eWebSocketはリアルタイム通信の仕組みそのものですが、\u003c/p\u003e\n\u003cp\u003eSocket.ioは、\n部屋(Room)管理\n自動再接続\nイベント管理\n接続・切断通知\nブロードキャスト（全員・特定の部屋への送信）\nなどの機能を提供してくれるライブラリのため、\nWebSocketで一から自前実装するよりも開発効率が高く、保守しやすいため利用される。\u003c/p\u003e\n\u003cp\u003e補足: Socket.ioは「WebSocketの上位互換」ではなく、フォールバック機構を持つ点も大きいです。WebSocketが使えない環境（古いプロキシ等）では自動的にlong-pollingに切り替えます。つまりSocket.io自体が独自のプロトコルで、内部で「使えるならWebSocket、ダメならHTTPポーリング」を切り替えている、という位置づけです。\u003c/p\u003e\n\u003cp\u003eQ. Socket.io（サーバー経由の通信）があるのに、なぜビデオ通話や画面共有には別途「WebRTC」という技術が必要なのか？ 音声・映像データがサーバーを経由する場合としない場合で、何が変わるか考えてみてください。\u003c/p\u003e\n\u003cp\u003e映像、音声データは通信遅延を避けるためにUDPプロトコルを利用している。サーバーを経由する場合には、TCPプロトコルによる通信となるが、TCPプロトコルの通信の場合は、信頼性の確立のための仕組みが整っている反面、音声・映像のデータはデータサイズが多すぎて、通信をする際に、データ破損などが起こりやすく、通信遅延、通信事故が発生してしまう。そのため、UWPプロトコルを利用するのに、WebRTCという通信技術を使って映像、音声データはやり取りをする必要がある\u003c/p\u003e\n\u003cp\u003e良い着眼点です。UDP/TCPの違いは正しいですが、もう一つ重要な観点が抜けています。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e✅ プロトコルの違い: 音声・映像はSRTP（UDPベース）を使う。TCPは再送・順序保証の仕組みがある分、パケットロス時に「待ち」が発生し、リアルタイム性を損なう（多少コマ落ちしても待たされるよりマシ、という発想）。\u003c/li\u003e\n\u003cli\u003e⚠️ もう一つの本質: 仮にサーバー経由でUDPを使えたとしても、全員の映像・音声データがサーバーを通ると、参加者が増えるほどサーバーの帯域・処理コストが跳ね上がり、サーバーが1つ余分な中継点＝遅延要因にもなる。WebRTCの一番の価値は「サーバーを介さずピア同士が直接繋がる（P2P）」こと。UDPを使うのは手段の一つで、目的は「サーバーを経由しない直接通信」を実現すること。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eQ. ピア同士が「直接」繋がるには、事前にお互いのIPアドレスなどの接続情報を知る必要があります。でも見知らぬ相手同士がいきなり直接通信することはできません。この「最初の情報交換」はどうやって行うのでしょうか？ （ここでSocket.ioが再登場します）\u003c/p\u003e\n\u003cp\u003e自分の回答\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-fallback\" data-lang=\"fallback\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eシグナリングを行う。シグナリングとは、P2P通信のやりとりをするための接続情報を交換する仕組み。\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cul\u003e\n\u003cli\u003e✅ 仕組みの本質: シグナリングとは「まだP2Pで直接繋がっていない2人が、繋がるために必要な情報を、既存の別の通信路（Socket.io/WebSocketなど）経由でやり取りすること」。\u003c/li\u003e\n\u003cli\u003e📌 重要な補足: WebRTC自体は「シグナリングをどうやるか」を規定していません。Socket.ioでも、素のWebSocketでも、極端な話メールでコピペしてもいい（実用的ではないですが）。あくまで**サーバーを介した「仲介」**であって、通信方式は自由です。\u003c/li\u003e\n\u003cli\u003e📌 実際に交換するものは次の2種類です:\na. SDP（Session Description Protocol）: 「どんなコーデック・解像度で通信したいか」という能力・条件の記述\nb. ICE candidate: 実際の接続先候補（IP:ポートの組み合わせ）のリスト\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eこのSDPのやり取りの手順こそが「呼制御」です。\u003c/p\u003e","title":""},{"content":"Mac開発環境 これは入れておけツール\nNix Docker アルフレッド？ なんかもっと違うのがあったような\nMac開発で一番最初にやること\n隠しファイル・フォルダを表示状態にする Finder上で常に見えるようにしたい場合は、隠しファイルの表示ショートカットを使います。\nFinderで 「Macintosh HD」（またはユーザーのルートフォルダ） を開きます。 キーボードで Command (⌘) + Shift + .（ピリオド） を押します。 うっすら半透明になった隠しフォルダ（usr や opt など）が表示されるようになります。 あとは usr ➔ local ➔ var ➔ www とダブルクリックで順にたどっていけばアクセスできます。 /usr/local/などのファイルパスで\nopen . とすると、finderが開けるが、 code . でVSCodeを開けるようにしたい。\n設定手順（最初の一回だけ）\nVS Code を普通にアプリ一覧から起動します。 キーボードで Command (⌘) + Shift + P を押します（コマンドパレットが開きます）。 入力欄に shell command と入力します。 候補に出てくる 「Shell Command: Install \u0026lsquo;code\u0026rsquo; command in PATH」 を選択してクリックします。 (日本語設定の場合：「シェル コマンド: PATH 内に \u0026lsquo;code\u0026rsquo; コマンドをインストールします」) 「成功しました」という通知（またはダイアログ）が出たら設定完了です！（VS Codeは閉じてOKです） めも\nhttpd -v Apacheのバージョンだけ表示するコマンド\nhttpd -V Apacheが実際にどの設定ファイルを読んで起動しているかを教えてくれる\n1 2 -D HTTPD_ROOT=\u0026#34;/usr/local/Cellar/httpd/2.4.68\u0026#34; -D SERVER_CONFIG_FILE=\u0026#34;/usr/local/etc/httpd/httpd.conf\u0026#34; ","permalink":"https://makiharu.github.io/persona-blog/tech/0730-mac-tips/","summary":"\u003ch2 id=\"mac開発環境\"\u003eMac開発環境\u003c/h2\u003e\n\u003cp\u003eこれは入れておけツール\u003c/p\u003e\n\u003cp\u003eNix\nDocker\nアルフレッド？\nなんかもっと違うのがあったような\u003c/p\u003e\n\u003cp\u003eMac開発で一番最初にやること\u003c/p\u003e\n\u003cp\u003e隠しファイル・フォルダを表示状態にする\nFinder上で常に見えるようにしたい場合は、隠しファイルの表示ショートカットを使います。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eFinderで 「Macintosh HD」（またはユーザーのルートフォルダ） を開きます。\u003c/li\u003e\n\u003cli\u003eキーボードで Command (⌘) + Shift + .（ピリオド） を押します。\u003c/li\u003e\n\u003cli\u003eうっすら半透明になった隠しフォルダ（usr や opt など）が表示されるようになります。\u003c/li\u003e\n\u003cli\u003eあとは usr ➔ local ➔ var ➔ www とダブルクリックで順にたどっていけばアクセスできます。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e/usr/local/などのファイルパスで\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eopen .\nとすると、finderが開けるが、\ncode .\nでVSCodeを開けるようにしたい。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e設定手順（最初の一回だけ）\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eVS Code を普通にアプリ一覧から起動します。\u003c/li\u003e\n\u003cli\u003eキーボードで Command (⌘) + Shift + P を押します（コマンドパレットが開きます）。\u003c/li\u003e\n\u003cli\u003e入力欄に shell command と入力します。\u003c/li\u003e\n\u003cli\u003e候補に出てくる 「Shell Command: Install \u0026lsquo;code\u0026rsquo; command in PATH」 を選択してクリックします。 (日本語設定の場合：「シェル コマンド: PATH 内に \u0026lsquo;code\u0026rsquo; コマンドをインストールします」)\u003c/li\u003e\n\u003cli\u003e「成功しました」という通知（またはダイアログ）が出たら設定完了です！（VS Codeは閉じてOKです）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eめも\u003c/p\u003e","title":""}]