OpenCode + Ollama(Gemma4-E4B) で小さな Python スクリプトのリファクタリングを実行してみた
요약
본 기사는 OpenCode 환경에서 Ollama와 Gemma 4 E4B 모델을 사용하여 Python 스크립트 리팩토링 과정을 기록한 내용입니다. 필자는 기존의 함수 기반 `fibonacci` 코드를 클래스 구조를 사용하는 형태로 변경하고, 이에 맞춰 테스트 코드(`test_fibonacci.py`)도 업데이트하는 작업을 수행했습니다. 이 과정에서 LLM이 TDD(Test-Driven Development) 접근 방식을 취하며 계획을 수립하고, 여러 번의 시행착오와 오류 수정 과정을 거치는 것이 관찰되었습니다.
핵심 포인트
- OpenCode 환경은 로컬 LLM(Ollama + Gemma 4 E4B)을 활용하여 실제 코드 리팩토링 작업을 수행할 수 있는 플랫폼임을 보여줍니다.
- LLM이 복잡한 리팩토링 요청에 대해 TDD 사이클(테스트 업데이트 -> 구현 -> 테스트 실행)과 같은 체계적인 계획을 세우는 능력을 시연했습니다.
- 실제 LLM 기반 코딩 과정에서는 초기 프롬프트 해석 및 환경 설정 단계에서 상당한 처리 시간 지연이 발생할 수 있습니다 (예: 2005.0s).
- LLM은 테스트 실패나 구문 오류(IndentationError)가 발생했을 때, 이를 인지하고 스스로 수정하며 작업을 지속하는 능력을 보여주었습니다.
はじめに
前回記事では、OpenCode をローカル LLM で動かせることを確認しました。
今回はその続きで、OpenCode で Python スクリプトのリファクタリングを実行してみました。
ローカル LLM の実行基盤には Ollama を使用してますが、前回の LM Studio から変更した理由は特にありません。単純に試してみたかっただけです。
使用したモデルは Gemma 4 E4B で、コンテキスト長の制限は 32768 に設定しました。
その他の環境は前回から変更ありません。
修正対象のスクリプト
対象のスクリプトとして、フィボナッチ数列を作成する関数 fibonacci()
とそのテストコードを実装しました。
これらのスクリプト自体も OpenCode で生成したものです。
fibonacci.py
def fibonacci(n):
"""
Calculates the nth Fibonacci number iteratively.
...
test_fibonacci.py
import unittest
from fibonacci import fibonacci
class TestFibonacci(unittest.TestCase):
...
プロンプト実行
以下のプロンプトで対象のスクリプトを Fibonacci クラスを使用する形に変更するよう指示しました。
@fibonacci.py を Fibonacch クラスを使用する形にリファクタリングしてください。
__main__ 関数はクラスのインスタンス化と処理実行関数の呼び出しのみのシンプルな構成にしてください。
@test_fibonacci.py のテストコードを先に更新してください。
...
実行結果
処理の完了までの時間は 1h 2m でした。
修正後のスクリプトは以下のようになりました。
ちゃんとクラスを使った形になっているし、テストも成功します。
設計的に気になるところはありますが、プロンプトがざっくりしていたので許容範囲だと思います。
fibonacci.py (修正後)
class Fibonacci:
def calculate_fibonacci(self, n):
"""
...
test_fibonacci.py (修正後)
import unittest
from fibonacci import Fibonacci
class TestFibonacci(unittest.TestCase):
...
処理過程の確認
処理過程で気になった部分をピックアップしてみます。
初回の Assistant の処理に時間がかかっている
プロンプト入力後の最初の処理に 2005.0s かかっていました。
それ以降の Assistant 処理はそれほど時間はかかっていないので、プロンプトの解析に時間がかかっていたのかなぁと推測しています。
Assistant 処理時間
## Assistant (Build · gemma4:e4b-32k · 2005.0s)
## Assistant (Build · gemma4:e4b-32k · 297.9s)
## Assistant (Build · gemma4:e4b-32k · 106.0s)
...
最初に計画を立てている
最初に作業を 3 ステップに分けて進める計画を立てているのが観察できました。
計画立案
## Assistant (Build · gemma4:e4b-32k · 2005.0s)
*Thinking:*
The user wants to refactor `fibonacci.py`
to use a `Fibonacci`
class.
The `__main__`
function should be simplified to just instantiate the class and call a processing function.
Crucially, the user wants to update `test_fibonacci.py`
first, using a Test-Driven Development (TDD) approach.
Steps:
- Update
`test_fibonacci.py`
to reflect changes (class usage) in`fibonacci.py`
that will be written later.
- Write the refactored
`fibonacci.py`
adhering to the class structure and minimal`__main__`
function.
- Run tests and repeat (TDD cycle).
Since I need to update the tests first, and the original function `fibonacci(n)`
is currently used in `test_fibonacci.py:2`, I must anticipate how the class methods will be called.
In the refactored class, the primary functionality `calculate_fibonacci(n)`
실제 실행 시 인스턴스 (예: `Fibonacci().calculate_fibonacci(n)`) 에서 호출될 가능성이 높습니다.
### 테스트 실패 및 시행착오
`test_fibonacci.py`
에서 `IndentationError: unexpected indent`
라는 오류가 발생했습니다. 그 후 처리를 중단하지 않고 자동으로 수정을 시도했습니다.
시행착오 끝에 테스트 코드가 손상되었음을 발견하고 이를 수정하는 과정이 관찰되었습니다.
## 테스트 실패
## Assistant (Build · gemma4:e4b-32k · 106.0s)
**Tool: bash**
**Input:**
{
"command": "python test_fibonacci.py",
"description": "Refactoring 및 로직 업데이트 검증 테스트 실행"
...
}
**Output:**
File "C:\work\opencode_test\test_fibonacci.py", line 3
fibonacci_calculator = Fibonacci()
IndentationError: unexpected indent
## 테스트 코드 손상 감지
## Assistant (Build · gemma4:e4b-32k · 594.8s)
*Thinking:*
...
현재 `test_fibonacci.py`
내용 (마지막 읽기):
1: import unittest
2: # Fibonacci 클래스 인스턴스 생성을 위한 주석
3: fibonacci_calculator = Fibonacci()
...
인덱싱이 명백히 잘못되었습니다 (라인 2-3, 5-20). `unittest.TestCase`
메서드는 올바른 인덱싱을 가져야 합니다.
## 테스트 코드 수정 및 재작성
## Assistant (Build · gemma4:e4b-32k · 594.8s)
*Thinking:*
...
`setUp`
를 사용하고 올바른 인덱싱을 적용하기 위해 테스트 파일 구조를 완전히 다시 작성해야 합니다.
계획:
- `setUp`
및 올바른 인덱싱을 사용하여 `test_fibonacci.py`
를 다시 작성.
- 테스트 재실행.
`test_fibonacci.py`
의 예상 내용:
import unittest
from fibonacci import Fibonacci
class TestFibonacci(unittest.TestCase):
...
전체 파일 내용을 사용하여 편집을 수행하여 `oldString`
매칭 문제를 피합니다.
**Tool: edit**
**Input:**
## まとめ
OpenCode を使ってローカル LLM でスクリプトのリファクタリングを実行する様子を確認しました。
時間はかかりましたが、モバイルノートという貧弱な環境でも動くものが出来上がるのは面白いですね。
今回はテストコード側でバグがありましたが、それを自律的に修正する様子も確認できたのが特に良かったと思います。
いろんなところで言われていることだと思いますが、AI に動くものを書かせるならテストコードも同時に作るのが大切そうなのを感じられました。
## 付録
## 会話履歴全文 (OpenCode の `/export` で出力した生データ)
# Refactor fibonacci function to Fibonacch Class using TDD
**Session ID:** ses_208272eb3ffetVCwSQ8pxjLTrM
**Created:** 2026/5/5 20:14:34
**Updated:** 2026/5/5 21:17:19
## User
@fibonacci.py を Fibonacch クラスを使用する形にリファクタリングしてください。
**main** 関数はクラスのインスタンス化と処理実行関数の呼び出しのみのシンプルな構成にしてください。
@test_fibonacci.py のテストコードを先に更新してください。
修正とテストを繰り返す TDD 方式で進めてください。
## Assistant (Build · gemma4:e4b-32k · 2005.0s)
*Thinking:*
The user wants to refactor `fibonacci.py`
to use a `Fibonacci`
class.
The `__main__`
function should be simplified to just instantiate the class and call a processing function.
Crucially, the user wants to update `test_fibonacci.py`
first, using a Test-Driven Development (TDD) approach.
Steps:
- Update
`test_fibonacci.py`
to reflect changes (class usage) in `fibonacci.py`
that will be written later. - Write the refactored
`fibonacci.py`
adhering to the class structure and minimal `__main__` function. - Run tests and repeat (TDD cycle).
Since I need to update the tests first, and the original function `fibonacci(n)`
현재 `test_fibonacci.py:2` 에서 사용되고 있습니다.
클래스 메서드가 어떻게 호출될지 예상해야 합니다.
리팩토링된 클래스에서 주요 기능인 `calculate_fibonacci(n)` 은 인스턴스 (예: `Fibonacci().calculate_fibonacci(n)`) 에게很可能 호출될 것입니다.
**Step 1: Update test_fibonacci.py**
테스트 클래스 메서드를 업데이트하여 새로운 `Fibonacci` 클래스를 인스턴스화하고 사용하는 것입니다.
초기 계획:
- `test_fibonacci.py` 내용을 읽음. 완료됨 - 임포트와 테스트 메서드를 수정하여 `Fibonacci` 클래스를 사용하도록 합니다.
먼저 `from fibonacci import fibonacci` 를 `from fibonacci import Fibonacci` 로 변경하겠습니다. 그런 다음 테스트 어스텐션을 업데이트하겠습니다.
`test_fibonacci.py` 를 수정하기 시작하겠습니다.
**Tool: edit**
**Input:**
{
"filePath": "C:\work\opencode_test\test_fibonacci.py",
"newString": " # Create an instance of the Fibonacci class to test\n fibonacci_calculator = Fibonacci()\n\n # Test for n=0\n self.assertEqual(fibonacci_calculator.calculate_fibonacci(0), 0)\n\n # Test for n=1\n self.assertEqual(fibonacci_calculator.calculate_fibonacci(1), 1)\n\n # Test for n=6 (Sequence: 0, 1, 1, 2, 3, 5, 8. 6th term is 8)\n self.assertEqual(fibonacci_calculator.calculate_fibonacci(6), 8)\n\n # Test for n=10 (Should return 55)\n self.assertEqual(fibonacci_calculator.calculate_fibonacci(10), 55)\n\n # Test for negative input\n expected_error = "Input must be a non-negative integer."
self.assertEqual(fibonacci_calculator.calculate_fibonacci(-1), expected_error)",
...
**Output:**
Edit applied successfully.
## Assistant (Build · gemma4:e4b-32k · 297.9s)
*Thinking:*
사용자는 `fibonacci.py` 를 `Fibonacci` 클래스로 리팩토링하고, 이를 반영하여 `test_fibonacci.py` 를 업데이트하며 TDD 를 계속 진행하도록 요청했습니다.
실행된 단계:
- `fibonacci.py` 읽음 (이전 턴에서 완료됨) - `test_fibonacci.py` 읽음 (이전 턴에서 완료됨) - `test_fibonacci.py` 를 `Fibonacci` 클래스 인스턴스와 `calculate_fibonacci` 메서드를 사용하여 업데이트했습니다.
TDD 에 기반한 다음 단계:
- **Implement/Refactor**: 독립 함수인 `fibonacci.py` 의 `fibonacci(n)` 을 `Fibonacci` 클래스 구조로 리팩토링하고 `calculate_fibonacci(self, n)` 메서드를 구현합니다. - **Run tests**: 리팩토링된 코드가 업데이트된 `test_fibonacci.py` 를 통과하는지 확인하기 위해 테스트를 실행합니다.
먼저 `fibonacci.py` 를 리팩토링하겠습니다.
**Tool: edit**
**Input:**
이전 버전의 코드를 확인하고 수정했습니다.
파일: C:\work\opencode_test\fibonacci.py
변경 사항:
- 클래스 정의와 계산 로직을 명확히 했습니다.
- 반복적 (iterative) 방식으로 n 번째 피보나치 수를 구하는 함수를 구현했습니다.
코드 예제:
```python
class Fibonacci:
def calculate_fibonacci(self, n):
"""
Calculates the nth Fibonacci number iteratively.
"""
if n < 0:
return "Input must be a non-negative integer."
elif n == 0:
return 0
elif n == 1:
return 1
a, b = 0, 1
for _ in range(2, n + 1):
a, b = b, a + b
return b
if __name__ == "__main__":
# Example usage:
calculator = Fibonacci()
n_terms = int(input("Enter the number of Fibonacci terms: "))
fib_num = calculator.calculate_fibonacci(n_terms)
print(f"\nThe Fibonacci number at index {n_terms} is: {fib_num}")
AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기