@@ -11,12 +11,178 @@ on:
 jobs:
   code_review:
     runs-on: ubuntu-latest
-    name: ChatGPT Code Review
+    name: Gemini Code Review
     steps:
-      - uses: anc95/ChatGPT-CodeReview@main
+      - uses: actions/checkout@v4
+
+      - name: Setup Node.js
+        uses: actions/setup-node@v4
+        with:
+          node-version: "20"
+
+      - name: Install dependencies
+        run: npm install @google/generative-ai parse-diff
+
+      - name: Review Changed Files
+        uses: actions/github-script@v7
         env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
-          # optional
-          LANGUAGE: Korean
-          MODEL: gpt-4o-mini-2024-07-18 # https://platform.openai.com/docs/models
+          GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
+          GEMINI_MODEL: ${{ secrets.GEMINI_MODEL }}
+          MAX_CONCURRENT_REVIEWS: 3 # 동시에 처리할 파일 수
+          MAX_RETRIES: 3
+          RETRY_DELAY: 1000
+
+        with:
+          script: |
+            const { GoogleGenerativeAI } = require("@google/generative-ai");
+            const parseDiff = require('parse-diff');
+            // core는 이미 github-script에서 제공됨
+
+            // 재시도 로직을 포함한 API 호출 함수
+            async function withRetry(fn, retries = process.env.MAX_RETRIES) {
+              for (let i = 0; i < retries; i++) {
+                try {
+                  return await fn();
+
+                } catch (error) {
+                  if (error.message.includes('rate limit') && i < retries - 1) {
+                    const delay = process.env.RETRY_DELAY * Math.pow(2, i);
+                    console.log(`Rate limit hit, waiting ${delay}ms before retry ${i + 1}`);
+
+                    await new Promise(resolve => setTimeout(resolve, delay));
+                    continue;
+                  }
+
+                  throw error;
+                }
+              }
+            }
+
+            // Gemini AI 클라이언트 초기화
+            const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
+            const model = genAI.getGenerativeModel({ model: process.env.GEMINI_MODEL });
+
+            /**
+             * 코드 변경사항을 검토하고 피드백을 생성
+             * 
+             * @param content 리뷰할 코드 내용
+             * @param filename 파일명
+             * @returns 리뷰 코멘트
+             */
+            async function reviewCode(content, filename) {
+              try {
+                // 파일명 sanitize
+                const sanitizedFilename = filename.replace(/[^a-zA-Z0-9.-_\/]/g, '_');
+
+                const prompt = `당신은 시니어 개발자입니다. 아래 ${sanitizedFilename} 파일의 코드를 검토하고 다음 사항들을 한국어로 리뷰해주세요:
+                1. 코드의 품질과 가독성
+                2. 잠재적인 버그나 문제점
+                3. 성능 개선 포인트
+                4. 보안 관련 이슈
+                5. 개선을 위한 구체적인 제안
+
+                코드:
+                ${content}`;
+
+                const result = await withRetry(() => model.generateContent(prompt));
+                const response = await result.response;
+
+                if (!response.text()) {
+                  throw new Error('Gemini API returned empty response');
+                }
+
+                console.log(`Successfully reviewed ${filename}`);
+                return response.text();
+
+              } catch (error) {
+                console.error(`Error reviewing ${filename}:`, error);
+                return `⚠️ 코드 리뷰 중 오류가 발생했습니다: ${error.message}
+                       다시 시도하시거나 관리자에게 문의해주세요.`;
+              }
+            }
+
+            /**
+             * PR의 각 파일을 처리하고 리뷰 코멘트 작성
+             * @param file PR에서 변경된 파일 정보
+             * @param pr PR 정보
+             */
+            async function processFile(file, pr) {
+              if (file.status === 'removed') {
+                console.log(`Skipping removed file: ${file.filename}`);
+                return;
+              }
+
+              try {
+                if (!file.patch) {
+                  console.warn(`No patch found for ${file.filename}, skipping.`);
+                  return;
+                }
+
+                const diff = parseDiff(file.patch)[0];
+                if (!diff || !diff.chunks) {
+                  console.log(`No valid diff found for ${file.filename}`);
+                  return;
+                }
+
+                // 모든 변경사항을 하나로 합치기 (추가된 부분만 필터링)
+                const changes = diff.chunks
+                  .flatMap(chunk => chunk.changes)
+                  .filter(change => change.type === 'add');
+
+                if (changes.length === 0) {
+                  console.log(`No added changes found in ${file.filename}`);
+                  return;
+                }
+
+                // 변경사항을 하나의 문자열로 합치기
+                const content = changes.map(change => change.content).join('\n');
+                const review = await reviewCode(content, file.filename);
+
+                // PR에 리뷰 코멘트 작성 (파일명을 헤더로 추가)
+                return github.rest.issues.createComment({
+                  owner: context.repo.owner,
+                  repo: context.repo.repo,
+                  issue_number: context.issue.number,
+                  body: `## ${file.filename} 리뷰\n\n${review}`
+                });
+
+              } catch (error) {
+                console.error(`Error processing ${file.filename}:`, error);
+                throw error;
+              }
+            }
+
+            try {
+              // PR 정보 조회
+              const { data: pr } = await github.rest.pulls.get({
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+                pull_number: context.issue.number,
+              });
+
+              // 변경된 파일 목록 조회
+              const { data: files } = await github.rest.pulls.listFiles({
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+                pull_number: context.issue.number,
+              });
+
+              console.log(`Starting review of ${files.length} files...`);
+
+              // 모든 파일 처리 완료를 기다림
+              const promises = [];
+              const batchSize = parseInt(process.env.MAX_CONCURRENT_REVIEWS);
+            
+              for (let i = 0; i < files.length; i += batchSize) {
+                const batch = files.slice(i, i + batchSize);
+                const batchPromises = batch.map(file => processFile(file, pr));
+                promises.push(...batchPromises);
+              }
+
+              await Promise.all(promises);
+              console.log('All reviews completed successfully');
+
+            } catch (error) {
+              console.error('Workflow failed:', error);
+              core.setFailed(`Workflow failed: ${error.message}`);
+            }

리뷰: ## Gemini Code Review Workflow 파일 리뷰 (code_review.yml)

다음은 제공된 `code_review.yml` 파일에 대한 상세 리뷰입니다. 시니어 개발자의 관점에서 코드 품질, 잠재적 문제점, 성능 개선, 보안 이슈 및 개선 제안을 다룹니다.

**1. 코드의 품질과 가독성**

*   **전반적으로 양호:** 코드의 구조가 비교적 명확하고, 주석이 적절하게 사용되어 가독성이 높습니다. 함수별 역할 분담도 잘 되어 있습니다.
*   **함수명:** 함수명(`reviewCode`, `processFile`)은 역할을 명확히 설명하며, 코드 이해도를 높입니다.
*   **주석:** 각 함수 및 주요 로직에 대한 설명 주석이 존재하여 코드의 의도를 파악하기 용이합니다. 하지만 더 자세한 설명이 필요한 부분도 있습니다 (예: `chunk` 함수).
*   **변수명:** 변수명은 전반적으로 명확하고 의미를 잘 나타냅니다.

**2. 잠재적인 버그나 문제점**

*   **에러 핸들링:** `reviewCode` 함수에서 Gemini API 호출 실패 시 오류 메시지를 반환하지만, 이후 프로세스에서 해당 오류를 어떻게 처리하는지에 대한 고려가 부족합니다.  예를 들어, 오류가 발생한 파일에 대한 리뷰 코멘트 작성을 중단하고, 다른 파일들을 계속 처리하는 것이 적절한지 확인해야 합니다.
*   **API Rate Limiting:** `MAX_CONCURRENT_REVIEWS`를 사용하여 rate limiting을 방지하는 것은 좋은 전략이지만, Gemini API 자체가 rate limiting을 적용할 수 있으므로, 더 robust한 에러 처리 및 재시도 로직이 필요할 수 있습니다. 특히, 오류 메시지를 파싱하여 rate limit 관련 에러인지 확인하고, 지수 백오프(exponential backoff) 전략을 적용하는 것을 고려해볼 수 있습니다.
*   **diff 파싱 실패:** `parseDiff(file.patch)[0]`에서 `file.patch`가 유효하지 않은 경우 (예: binary 파일), 에러가 발생할 수 있습니다.  `parseDiff` 함수의 결과를 제대로 검사하고, 에러 발생 시 적절한 로깅 또는 스킵 처리를 해야 합니다.
*   **빈 응답 처리:** `response.text()`가 비어있는 경우 에러를 던지지만, 이 경우 GitHub에 어떤 코멘트가 남겨지는지 명확하지 않습니다.  빈 응답에 대한 더 사용자 친화적인 메시지 (예: "Gemini API에서 유효한 응답을 받지 못했습니다.")를 남기는 것이 좋습니다.
*   **`lineNumber` 변수:** `lineNumber`는 청크의 시작 라인을 나타내지만, 리뷰가 특정 코드 라인에 대한 것일 경우 해당 라인을 정확히 가리키지 않을 수 있습니다. 더욱 정밀한 리뷰를 위해 change가 발생한 라인 번호를 특정하는 로직을 추가하는 것을 고려해볼 수 있습니다.
*   **삭제된 파일 처리:** `file.status === 'removed'`인 경우 스킵하지만, 실제로 삭제된 파일에 대한 코멘트를 남기는 것이 유용한 경우도 있을 수 있습니다 (예: 왜 삭제되었는지, 대체 코드는 무엇인지 등). 상황에 따라 삭제된 파일에 대한 간단한 코멘트를 추가하는 것을 고려해볼 수 있습니다.

**3. 성능 개선 포인트**

*   **청크 처리:** 현재 코드는 각 청크별로 `reviewCode` 함수를 호출합니다. 이는 Gemini API 호출 횟수를 늘리고 전체 실행 시간을 증가시킬 수 있습니다. 가능하다면, 여러 청크를 묶어서 한 번에 `reviewCode` 함수를 호출하는 것을 고려해볼 수 있습니다. (프롬프트 크기 제한을 고려해야 함)
*   **병렬 처리:** 현재 파일 목록을 순회하면서 `processFile` 함수를 병렬로 실행하지만, `processFile` 함수 내에서도 청크들을 병렬로 처리할 수 있습니다. `Promise.all`을 사용하여 청크 처리 속도를 개선할 수 있습니다.  다만, Gemini API의 rate limiting을 더욱 주의해야 합니다.
*   **캐싱:** 만약 동일한 코드가 여러 번 리뷰되는 경우, Gemini API의 응답을 캐싱하여 불필요한 API 호출을 줄일 수 있습니다. (캐싱 전략 및 만료 시간을 신중하게 결정해야 함)

**4. 보안 관련 이슈**

*   **API Key 관리:** Gemini API 키(`GEMINI_API_KEY`)를 GitHub Secrets에 안전하게 저장하는 것은 매우 중요합니다.
*   **프롬프트 인젝션:** Gemini API에 전달되는 프롬프트에 사용자 입력 (특히 `filename`)이 포함되어 있습니다. 악의적인 사용자가 파일 이름을 조작하여 의도치 않은 결과를 초래할 수 있는 프롬프트 인젝션 공격에 취약할 수 있습니다. 파일 이름에 대한 유효성 검사 또는 필터링을 추가하는 것을 고려해야 합니다.
*   **코드 실행:** `actions/github-script` 액션은 JavaScript 코드를 실행하므로, 의존성 패키지(`@google/generative-ai`, `parse-diff`)에 대한 보안 취약점 관리가 중요합니다. 정기적으로 의존성 패키지를 업데이트하고, 보안 검사를 수행해야 합니다.

**5. 개선을 위한 구체적인 제안**

*   **상세한 로깅:** 각 단계별로 로그를 추가하여 디버깅 및 모니터링을 용이하게 합니다. 특히, API 호출 성공/실패 여부, rate limiting 발생 여부, 파일 처리 시간 등을 로깅합니다.
*   **에러 처리 개선:** `reviewCode` 함수에서 발생한 오류를 더 자세히 처리하고, GitHub 코멘트를 통해 사용자에게 알립니다. 예를 들어, 오류 유형 (API 오류, rate limiting, 파싱 오류 등)을 명시하고, 문제 해결을 위한 가이드라인을 제공합니다.
*   **테스트 케이스 추가:** 코드 변경 사항을 검증하기 위한 테스트 케이스를 추가합니다. 특히, 다양한 파일 형식 (텍스트, JSON, YAML 등) 및 diff 형식에 대한 테스트를 수행하여 코드의 안정성을 확보합니다.
*   **구성 옵션 추가:** `MAX_CONCURRENT_REVIEWS` 외에도, Gemini 모델 (`GEMINI_MODEL`) 및 프롬프트 템플릿을 구성 옵션으로 제공하여 사용자가 필요에 따라 설정을 변경할 수 있도록 합니다.
*   **코드 리뷰 결과 요약:** 각 파일별 리뷰 결과를 요약하여 PR에 대한 전체적인 코드 품질을 파악하기 쉽게 합니다.  예를 들어, "총 X개의 파일에서 Y개의 문제점이 발견되었습니다. 자세한 내용은 각 파일의 코멘트를 참조하세요."와 같은 요약 정보를 제공할 수 있습니다.
*   **프롬프트 최적화:** 코드 리뷰 품질을 높이기 위해 Gemini API에 전달되는 프롬프트를 지속적으로 개선합니다. 예를 들어, 코드 리뷰 기준을 더욱 명확하게 제시하거나, 특정 프로그래밍 언어 또는 프레임워크에 대한 지침을 추가할 수 있습니다.
*   **PR 템플릿 활용:** PR 작성자가 코드 변경 사항에 대한 정보를 미리 제공하도록 PR 템플릿을 활용합니다. 이는 Gemini API가 더욱 정확하고 관련성 높은 리뷰를 생성하는 데 도움이 될 수 있습니다.

**결론:**

전반적으로 잘 작성된 워크플로우 파일이지만, 에러 처리, API Rate Limiting, 보안 및 성능 측면에서 개선할 여지가 있습니다. 제안된 개선 사항들을 적용하여 코드 리뷰 프로세스를 더욱 안정적이고 효율적으로 만들 수 있습니다. 특히, 실제 운영 환경에서 발생할 수 있는 다양한 예외 상황을 고려하여 코드를 보완하는 것이 중요합니다.