こんにちは、とまだです。
ChatGPT Advent Calendar 2024、1 日目の記事をお届けします!
「コードレビューをしてほしいけど、相談する人がいない...」
そんな経験ありませんか?
私も個人開発をしていた時、いつも悩んでいました。
ただ、最近はChatGPT を使ってコードレビューをしてもらうという選択肢もありますよね。
もちろん、人間のレビューに完全に置き換わるわけではありませんが、最低限の品質を保証するためのセカンドオピニオンとして役立ちます。
ただし、現場で使う前に必ず生成 AI 利用に関するルールを確認してください。
また、機密情報や個人情報などの重要な情報は載せないように注意しましょう。
ChatGPT にレビューを依頼するメリット
ChatGPT にレビューを依頼すると、以下のような嬉しいポイントがあります。
- 24 時間いつでも相談可能
- 初歩的なミスを素早くチェック可能
- リファクタリングの提案をもらえる
- 英語のネーミングやコメントの改善案を得られる
- コードの説明を通じて理解度の確認ができる
レビューの基本的な流れ
品質チェックは以下の流れで行います。
- コードの目的を明確に説明
- レビューしてほしいポイントを具体的に指定
- ChatGPT からのフィードバックを確認
- 提案された修正の妥当性を判断
- 必要な修正を適用
それでは、具体的なやり方を見ていきましょう!
効果的なプロンプトの例
1. 基本的なコードチェック用
まずは基本的なチェックから始めましょう。
プロンプト
# 役割
優秀なプログラマーであり、クリーンコードの原則に基づく技術レビュアー
# 文脈
商品の在庫を管理する機能の実装を行っています。
# 目的
実装の品質を向上させるためのアドバイスを求めます。
# 命令
観点に従って、以下のコードをレビューしてください。
# 観点
- 命名規則
- 関数の単一責任
- エラーハンドリング
- コメントの適切性
# コード
```javascript
function checkAndUpdateStock(itemId, quantity) {
// get stock
const stock = getStock(itemId);
// check if enough stock
if (stock >= quantity) {
// update stock
updateStock(itemId, stock - quantity);
return true;
} else {
return false;
}
}
```
このように、プロンプトを作成して ChatGPT にレビューを依頼します。
プロンプトエンジニアリングの原則に則って役割や文脈、観点を明確に示すことが重要です。
特に、観点には具体的なチェック項目を記載しましょう。
ChatGPT からのフィードバック
それでは、ChatGPT からのフィードバックを見てみましょう。
フィードバック例
以下の点について改善をお勧めします:
- 命名について
- 関数名が具体的でない
→ hasEnoughStockAndUpdate のような、動作を明確にした名前を推奨
- エラーハンドリング
- getStock の失敗時の処理がない
→ try-catch で適切に処理すべき
- コメント
- 単純な処理に不要なコメントがある
→ コードの意図が分かる命名にして、コメントを減らす
改善例:
async function hasEnoughStockAndUpdate(itemId, requestedQuantity) {
try {
const currentStock = await getStock(itemId);
if (currentStock < requestedQuantity) {
return false;
}
await updateStock(itemId, currentStock - requestedQuantity);
return true;
} catch (error) {
// エラーログの記録
console.error(`在庫確認中にエラーが発生: ${error.message}`);
throw error;
}
}
このように、指定した観点に基づいて ChatGPT からのフィードバックを受け取ることができます。
自分の中で見てほしい観点が明確であれば都度カスタマイズしたいところではありますが、慣れないうちはこのような形で進めると良いでしょう。
2. パフォーマンス改善用
次はパフォーマンスに焦点を当ててみましょう。
プロンプト
# 役割
パフォーマンスチューニングの専門家として、アドバイスをお願いします。
# 文脈
ユーザー情報を一覧表示する画面のコードです。
# 目的
クエリの効率性やレンダリングパフォーマンスを向上させるためのアドバイスを求めます。
# 命令
以下のコードをレビューし、パフォーマンスの観点から改善点を指摘してください。
# 観点
- クエリの効率性
- メモリ使用量
- レンダリングパフォーマンス
# コード
```javascript
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
const fetchUsers = async () => {
const response = await fetch("/api/users");
const data = await response.json();
setUsers(data);
};
fetchUsers();
}, []);
return (
<div>
{users.map((user) => (
<UserCard key={user.id} user={user} />
))}
</div>
);
}
```
上記では、React コンポーネントのパフォーマンスに焦点を当てています。
できれば「メモリ使用量」や「レンダリングパフォーマンス」など、具体的な観点を指定するとより効果的です。
ChatGPT からのフィードバック
フィードバック例
パフォーマンスチューニングの観点から、以下の点で改善が可能です。
1. クエリの効率性
API へのクエリ効率化を図るには、以下を検討してみてください:
- 不要なデータの排除: 必要なデータだけを取得するように、API エンドポイントでフィルタリングやペイロードの調整を行うと良いでしょう。特に大規模データの場合、重要です。
- ページネーション: ユーザーリストが膨大な場合、全件を一度に取得せず、ページネーションや無限スクロールを実装して、必要な範囲のデータのみをフェッチするようにするのが効率的です。
2. メモリ使用量
メモリ使用量を最適化するために以下を検討します。
-
ユーザー情報のキャッシュ:
React Query
やSWR
などのライブラリを使い、取得したユーザー情報をキャッシュすることが推奨されます。これにより、再レンダリング時の再フェッチを防げ、メモリ使用量の効率化につながります。 -
Lazy Loading: リストに画像や他のリソースが含まれる場合、
IntersectionObserver
などを使用して表示されていないコンポーネントは遅延読み込みにすると、メモリ使用を抑えることができます。
3. レンダリングパフォーマンス
リストの再レンダリングが不要に行われることを避けるため、以下の方法を検討してください。
-
React.memo
の活用:UserCard
コンポーネントが頻繁に再レンダリングされないように、React.memo
を利用し、ユーザー情報が変更されない限りレンダリングをスキップするようにするとパフォーマンスが向上します。 -
virtualization
:react-window
やreact-virtualized
といったライブラリを使用すると、リストのアイテムを仮想化して、画面内に表示される部分のみをレンダリングできます。特に大量のユーザーがいる場合、レンダリングパフォーマンスが大幅に改善します。
改善例
以下は、上記の改善点を反映させたコード例です。
import React, { useState, useEffect } from "react";
import { FixedSizeList as List } from "react-window";
import { UserCard } from "./UserCard";
import axios from "axios";
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
const fetchUsers = async () => {
const response = await axios.get("/api/users"); // Axiosでのデータフェッチ
setUsers(response.data);
};
fetchUsers();
}, []);
const Row = React.memo(({ index, style }) => (
<div style={style}>
<UserCard key={users[index].id} user={users[index]} />
</div>
));
return (
<List height={600} itemCount={users.length} itemSize={100} width={"100%"}>
{Row}
</List>
);
}
この例では以下を実装しています。
- Axiosを使用して効率的にデータを取得(API クエリの効率化)。
-
react-window
を使用して、リストの仮想化を実装(レンダリングパフォーマンスの向上)。 - React.memoを活用し、再レンダリングの負荷を軽減。
指定した観点から具体的な改善案を提案してくれるので、そのまま実装に移すことができます。
これがあれば、相当開発スピードもアップするのではないでしょうか。
3. セキュリティチェック用
1 人で開発していると、うっかりセキュリティ上の問題を見落としてしまうことがあるかもしれません。
また、セキュリティエキスパートでないと、セキュリティの観点からコードをチェックするのは難しいですよね。
本質となるロジックに集中できるよう、セキュリティチェックを ChatGPT に依頼してみましょう。
プロンプト
# 役割
セキュリティエキスパートとして、脆弱性のチェックをお願いします。
# 文脈
ユーザー認証の処理を行うログイン機能のコードを実装しました。
# 目的
セキュリティ上の問題を特定し、改善すること。
# 命令
以下のコードをレビューし、セキュリティの観点から改善点を指摘してください。
# 観点
- 入力値の検証
- 認証・認可
- データの保護
- セキュアな通信
# コード
```javascript
app.post("/login", (req, res) => {
const { username, password } = req.body;
const user = db.users.find((u) => u.username === username && u.password === password);
if (user) {
req.session.user = user;
res.redirect("/dashboard");
} else {
res.status(401).send("Invalid credentials");
}
});
```
ここでは、役割や観点を明確に示し、セキュリティに関するチェックを依頼しています。
ただし、あえて広い目線でチェックしてほしいときには「観点」を広く設定するのも一つの手です。
(今回は具体的な観点を指定しています)
ChatGPT からのフィードバック
こちらも、ChatGPT からのフィードバック例を見てみましょう。
かなり細かい点まで指摘してくれているので、読むだけでも勉強になりますね。
フィードバック例
以下のコードについて、セキュリティの観点からの改善点を指摘します。
1. 入力値の検証
問題点:
入力値(username
と password
)がそのままデータベース検索に使用されています。SQL インジェクションやその他の攻撃ベクトルのリスクは低いかもしれませんが、XSS などの問題が潜在的にあります。
改善案:
- 入力値を検証し、想定される形式(長さ、文字種など)をチェックする。
- 特に HTML 特殊文字のエスケープなどを適用して、XSS のリスクを軽減する。
const sanitizeInput = (input) => input.replace(/<|>/g, "");
const username = sanitizeInput(req.body.username);
const password = sanitizeInput(req.body.password);
2. 認証・認可
問題点:
- 認証の方法が平文のパスワードを直接比較しています。
- パスワードがハッシュ化されていないため、データベースの侵害時にユーザー情報が漏洩するリスクが高い。
改善案:
- パスワードをハッシュ化して保存し、認証時にもハッシュ値を比較する。
- bcrypt などのライブラリを利用してハッシュ化する。
const bcrypt = require("bcrypt");
// パスワードの比較
const user = db.users.find((u) => u.username === username);
if (user && bcrypt.compareSync(password, user.password)) {
req.session.user = user;
res.redirect("/dashboard");
} else {
res.status(401).send("Invalid credentials");
}
3. データの保護
問題点:
- セッションに
user
オブジェクト全体を格納している。不要な情報(例: パスワード)が含まれている可能性がある。 - セッションの乗っ取りリスクが存在。
改善案:
- セッションには必要最小限の情報(例:
userId
)のみを格納する。 - セッションの保護のために HTTP Only クッキーやセッション管理の強化を行う。
req.session.userId = user.id; // 必要な情報のみを保存
4. セキュアな通信
問題点:
- コード上では HTTPS の使用が確認できないが、セッションや認証情報は HTTPS を使用する場合のみ安全。
改善案:
- HTTPS を使用して通信を保護する。
- セッション ID のセキュリティ強化(Secure 属性、HttpOnly 属性)。
const session = require("express-session");
app.use(
session({
secret: "yourSecretKey",
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true, // クライアント側のJavaScriptからのアクセスを防ぐ
secure: true, // HTTPS経由のみでクッキーを送信
},
})
);
5. エラーメッセージ
問題点:
エラーメッセージが固定(Invalid credentials
)であり、ユーザー名またはパスワードのいずれが間違っているかを特定しやすくしていないのは良いですが、ログイン試行に関する詳細情報を攻撃者に漏らすことを防ぐためにも改善の余地があります。
改善案:
- エラー応答の統一は維持しつつ、ログイン試行回数の制限を設ける(レートリミットや CAPTCHA の導入)。
// レートリミットの例
const rateLimit = require("express-rate-limit");
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分間に
max: 5, // 5回までログイン試行を許可
message: "Too many login attempts, please try again later.",
});
app.post("/login", loginLimiter, (req, res) => {
// ログイン処理
});
まとめ
改善点を以下に整理しました:
- 入力値の検証とサニタイズ
- パスワードのハッシュ化(
bcrypt
の使用) - セッションに保存するデータの最小化
- HTTPS を利用したセキュアな通信
- ログイン試行の制限とエラーメッセージの慎重な設計
これらの改善を実施することで、ログイン機能のセキュリティが大幅に向上します。
より良いレビューを得るための Tips
プロンプトエンジニアリングの基礎ですが、できるだけ文脈を詳細に説明しましょう。
ChatGPT は、できるだけ文脈に基づいてフィードバックを提供しようとしますが、文脈が不足しているあまり良い回答を得られないことがあります。
今回のプロンプトには、以下のようなポイントを含めていました。
- 役割
- 文脈
- 目的
- 命令
- 観点
その他にも、たとえば「動作環境」や「使用しているライブラリ」など、ChatGPT が知っておくべき情報があれば追加するのも効果的ですね。
- 最後に全体を見直す
まとめ
いかがでしたか?
一人で開発していても、ChatGPT を活用することで、少しでも品質を向上させることができます。
特に個人開発をしている方こそ、ぜひ試してみてください!
他にもアドベントカレンダー記事を書いています!
他にも、2024 年のアドベントカレンダーに参加しています。
以下の記事でまとめているので、よければ他の記事も読んでいただけると嬉しいです!