2년 전부터 가끔 필요한 파이썬 코드가 있을 때는 요구사항이 별로 복잡하지 않아서 ChatGPT 무료 버전에게 간단한 문장으로 만들어 달라고 했습니다.
이번에는 어려운 프로그램은 아닌데 한 문장으로 표현하기에는 애매해서 간단하게 문서를 만들었고, Deepseek 열풍이 분 후 나온 알리바바의 QwQ모델을 4bit 양자화 모델로 로컬PC(RTX3090/Ryzen5600)에서 Ollama를 통해 실행하고 Cline의 계승자인 Roo Code에게 파이썬 코드를 요청해 보았습니다.
보통 코딩 에이전트인 Cline이나 Roo Code는 클로드 소네트 3.7을 쓰라고 추천합니다. 저는 로컬 LLM모델을 써서 몇 번 시도했지만 제대로 작동한 것은 이번이 처음입니다. 아무리 간단한 코딩을 시켜도 워낙 토큰을 많이 사용하는 방식이라서 유료 온라인 서비스는 사용하지 않았습니다.
일단, 자세하게 써줍니다. 어떤 사람들은 readme.md 파일만 github에 넣어두고 처음부터 끝까지 개발한다는 사람도 있었습니다.
간단한 요구 사항 문서 작성
1.개발 언어
- Python 3.11
2.명령 인자
- 없음
3.입력 데이터
- 입력 데이터는 intput 디렉터리 내에 있는 모든 .CSV파일을 사용한다.
- 입력데이터 CSV의 파일명은 "지역_데이터종류_YYYYMM_YYYYMM.CSV"의 규칙을 갖고 있다.
- CSV 파일 내용은 다음과 같다.
format: day,hour,value location:60_124 Start : 20250101
1, 0000, 0.000000
1, 0100, 0.000000
1, 0200, 0.000000
1, 0300, 0.000000
1, 0400, 0.000000
1, 0500, 0.000000
1, 0600, 0.000000
- 두번째 줄부터는 날짜(day), HHMM(time), 데이터값(value) 로 구성되어 있다.
4.출력 데이터
- 출력 데이터는 output 디렉터리에 "YYYYMM_YYYYMM.CSV"로 저장한다.
- 모든 CSV파일을 메모리로 읽어들인 후, 기간별로 하나의 CSV 파일을 만든다.(예를 들면, 202501_202502.CSV)
- CSV의 출력 자료의 예제는 다음과 같다.
date, time, area, temperature, humidity, winddirection, windspeed, rainfall, rainkind
20250101, 00:00:00, 과천동, -5.0, 25.0, 250.0, 3.0, 2.5, 0.0
20250101, 00:01:00, 과천동, -6.0, 23.0, 240.0, 5.0, 3.5, 1.0
5.처리 방법
- 각 CSV파일을 처리할 때는 한 줄을 읽어들인 후에 중간에 있는 문자열 'Start : "를 찾고 이후에 있는 날짜정보(YYYYMMDD)를 파싱하고 다음에 'Start : "를 만날 때까지 날짜 정보 기준으로 처리한다.
- 'Start : YYYYMMDD'는 파일 중간에 반복적으로 나타날 수 있다.
- 파일 1개를 처리할 때마다 print 문을 통하여 진행 중임을 표시한다.
요약하자면, 여러 지역의 여러 기상정보가 CSV파일로 쪼개져 있는 것을 하나의 파일로 합쳐 달라는 내용입니다.
기상청의 기상예보 - 초단기실황에서 파일로 다운로드한 파일들입니다. 물론 API로 구현해 달라고 해도 잘 하겠지만, 자주 쓸 내용이 아니라서 파일로 받았습니다.
기상자료개방포털
data.kma.go.kr
제가 변환한 결과물처럼 보통 CSV는 바로 읽어서 사용할 수 있어야 할텐데, 아마도 과거에 개발할 때 저장소 공간 절약을 생각해서 포맷을 설계한 것 같습니다.
Roo Code의 처리 과정
VSCode에서 시킵니다...

나중에 알고 보니까 QwQ가 뭔가 반복하는 문제가 있었다고 하더군요. 그래서인지 VSCode에 설치된 Roo Code, 코딩 에이전트에게 시킨 후 아래 결과물을 받는데 1시간 40분이 소요되었습니다.
- Roo Code와 qwq:32b Q4(RTX3090, Ollama로 구동) 모델 조합으로 코드 개발 과정입니다.
- Cline(roo code)용으로 qwq모델 변경하기가 필요합니다.(아래 첨부 코드 참조)
ollama create qwq:32b-cline -f modfile-qwq.txt - 42분 동안 도돌이표(wait... 어쩌구 저쩌구)로 생각하더니 Python code를 저장도 하지 않고 실행하겠다고 하더군요. 그래서 해보라고(?) 했더니, "아차 안 만들었지?" 하더니 다시 코드를 리뷰를 해보겠다고 하네요. ㅋㅋㅋ 그 정도 시간이면 내가 해도 벌써 코딩 끝났겠다라고 비웃어 주었습니다. -> 사실 파이썬 경험이 짧아서 쉽지는 않아요.
- 33분간 더 생각하더니 드디어 소스 코드를 만들기 시작… 이번에는 저장할 거냐고 물어보네요.
- 저장하고 실행을 허락하니까, 제대로 결과가 나왔습니다!
- 웃기는 것은 자기가 작성한 print문에 오류 메시지가 있어서 분석한다고 24분을 더 소요한 후에, "오류가 있지만 잘 실행되었으므로 출력을 확인하기 위해 폴더 내용을 dir하겠다고 합니다?
- 정말로 (영어로)말은 많았지만 결과물을 내주네요. 제가 받은 모델이 코딩 전용은 아니어서 그렇다는 이야기도 보았습니다.
- Cline(roo code)용으로 qwq모델 변경하기가 필요합니다.(아래 첨부 코드 참조)
아래는 modfile-qwq.txt의 내용입니다.
FROM qwq:latest
PARAMETER num_ctx 32768
PARAMETER stop "<|im_start|>"
PARAMETER stop "<|im_end|>"
PARAMETER stop "<|endoftext|>"
PARAMETER temperature 0
결과물
한글로 된 코멘트 부분이 있는데 제가 "'Start : YYYYMMDD'는 파일 중간에 반복적으로 나타날 수 있다."는 점을 알려주지 않아서 나중에 코드를 추가했습니다.
import os
import sys
import glob
from datetime import datetime, timedelta
import re
from collections import defaultdict
def main():
input_dir = 'input'
output_dir = 'output'
data_by_period = defaultdict(lambda: defaultdict(dict))
# Process all CSV files in input directory
for filename in glob.glob(os.path.join(input_dir, '*.CSV')):
process_file(filename, data_by_period)
# Write outputs per period
for period, entries in data_by_period.items():
write_output(period, entries, output_dir)
def process_file(filename, data_dict):
print(f"Processing {filename}")
with open(filename, 'r') as f:
lines = [line.strip() for line in f.readlines()]
# Find the Start line
start_line = None
for line in lines:
if 'Start :' in line:
start_line = line
break
else:
raise ValueError(f"Missing 'Start : ' line in {filename}")
# Extract start date from start_line
match = re.search(r'Start\s*:\s*(\d{8})', start_line)
if not match:
raise ValueError("Invalid Start format in file")
start_date_str = match.group(1)
start_date = datetime.strptime(start_date_str, '%Y%m%d')
# Parse filename components
base_name = os.path.basename(filename).replace('.CSV', '')
parts = base_name.split('_')
area = parts[0]
data_type = parts[1]
period_start_end = '_'.join(parts[2:4])
# Process each data line after the start_line
start_index = lines.index(start_line) + 1 # next line is first data row?
for line in lines[start_index:]:
if not line.strip():
continue # skip empty lines
fields = line.split(',')
if len(fields)!=3:
# Start : 20250201 로 나오는 라인이 있다면 start_date를 다시 설정
match = re.search(r'Start\s*:\s*(\d{8})', line)
if match:
start_date_str = match.group(1)
start_date = datetime.strptime(start_date_str, '%Y%m%d')
print(f"Start keyword found: {line}")
continue
day_str, hour_str, value_str = fields
try:
day = int(day_str)
hour_part = hour_str.strip()
hours = int(hour_part[:2])
minutes = int(hour_part[2:4])
# Calculate the date
delta_days = day -1 # since day starts at 1
current_date = start_date + timedelta(days=delta_days)
dt = datetime(
current_date.year,
current_date.month,
current_date.day,
hours,
minutes,
0
)
except Exception as e:
print(f"Error processing line: {line} → {str(e)}")
continue
# Store the data
key = (dt, area)
data_dict[period_start_end][key][data_type] = float(value_str)
def write_output(period, entries, output_dir):
output_filename = os.path.join(output_dir, f"{period}.csv")
with open(output_filename, 'w') as outf:
# Write header
outf.write("date,time,area,temperature,humidity,winddirection,windspeed,rainfall,rainkind\n")
for (dt, area), data in entries.items():
required = ['강수', '강수형태', '기온', '습도', '풍속', '풍향']
missing = [field for field in required if field not in data]
if missing:
print(f"Warning: Missing data types {missing} for entry {dt}, {area}")
continue # skip incomplete entries
row = [
dt.strftime('%Y%m%d'),
dt.strftime('%H:%M:00'),
area,
data['기온'],
data['습도'],
data['풍향'],
data['풍속'],
data['강수'],
data['강수형태']
]
outf.write(','.join(map(str, row)) + '\n')
if __name__ == "__main__":
main()
후기
- 유튜브에서 비교적 큰 프로젝트를 Cline에게 시켜서 코드를 개발하는 영상을 본 적이 있습니다. 일반적으로 작동을 보여주기 위해 쓸모 없는 작은 게임프로그램이나 만들도록 하는데, 그 사람은 소프트웨어
아키텍트엔지니어 수준으로 문서를 작성해 두고 시키더군요. 초중급 개발자들의 자리가 위험한 셈입니다. 코딩은 기본이고 분석 설계 이상의 경험도 보유해야 살아남을 수 있겠습니다. - 이 예제가 비록 간단하기는 하지만, 문서를 잘 이해하고 원하는대로 작동하는 코드를 만들어 주는 것을 알 수 있었습니다.
- 프로그래머는 늘 공부해야 하는 숙명을 갖고 있는데 이제 상세한 코딩 스킬이나 프로그래밍 문법 등에 굳이 시간을 투입할 필요는 없어 보입니다. 물론 코드를 읽고 틀린 것이 없나 정도를 볼 정도 수준은 되어야 실수나 사고를 방지할 수 있습니다.
- 전에는 스택오버플로우와 구글로 씨름하여 해결했는데, 이제는 ChatGPT, Gemini, Copilot등 다양한 LLM들이 아주 빠르게 해결해 주어서 좀 더 여유롭게 일할 수 있지 않나 싶습니다.
- 물론, 그렇다고 해서 생산성이 몇 배로 증가하지는 않을 가능성이 많습니다. 퇴근을 제 때 할 수 있게 된다든지, 여유가 생기면 전에 보지 못했던 부분들에 대해 더 생각하게 되니 프로젝트 전반적으로 품질이 높아질 가능성도 있겠습니다.
- 가끔 귀차니즘으로 코드 리뷰를 자세히 하지 않은 경우 뒤통수를 맞을 수 있으므로 업무상 활용할 때에는 집중력이 필요합니다.
- 코드 일부를 선택해서 고쳐달라고 할 때에는 Github계정이 있는 사람에게 무료로 제공되는 Copilot기능을 사용하고 있습니다. 많이 사용하지 안않았는데도 55%나 이용했군요.

Unsloth의 QwQ 적용해 보기(내용 추가)
먼저 Unsloth에 의해 최적화된 모델을 받았습니다.
ollama pull hf.co/unsloth/QwQ-32B-GGUF:Q4_K_M)
Cline(혹은 Roo Code)에 맞도록 수정한 모델을 생성하기 위해 다른 사람들이 만든 내용을 참고하여 Modfile.txt 를 만듭니다.
FROM hf.co/unsloth/QwQ-32B-GGUF:Q4_K_M
PARAMETER num_ctx 32768
PARAMETER stop "<|begin▁of▁sentence|>"
PARAMETER stop "<|end▁of▁sentence|>"
PARAMETER stop "<|User|>"
PARAMETER stop "<|Assistant|>"
PARAMETER stop "</thinking>"
PARAMETER stop "</tool_call>"
PARAMETER stop "</attempt_completion>"
PARAMETER stop "</write_to_file>"
PARAMETER stop "</execute_command>"
PARAMETER stop "</tool_response>"
PARAMETER temperature 0.6
SYSTEM """
You are very knowledgable an expert. You have unlimited time to think and response with confidence. The user is your boss and you can call them "boss".
"""
ollama create qwq-unsloth-cline -f Modfile.txt
위 작업을 통해 만든 qwq-unsloth-cline 모델을 사용하여 Roo Code에서 코딩을 시켜본 결과 전처럼 "wait..." 어쩌구하면서 반복하던 것은 잘 보이지 않게 되었습니다. 새로 테스트한 것은 좀 더 복잡한 프로그램이어서 그럴 수도 있지만, 코딩에 오래 시간이 걸리는 것이 바뀐 것은 아니고, 버그도 남겼고 결국 수정을 못하고 7시간 만에 종료를 시켰습니다.
RTX3090에서 Roo Code로 작업하는 것은 시간이 너무 소요되는 것 같고, 현실적으로는 유료 API를 사용하는 것이 필요해 보입니다.
'AI' 카테고리의 다른 글
DeepL번역으로 Youtube 유튜브 외국어 영상 자막 보기 (0) | 2023.09.16 |
---|---|
자막 생성 Whisper 미디엄 모델을 2GB VRAM NVIDIA 노트북에서 이용하기 (1) | 2023.08.27 |
DeepL 번역 API 써보기 - RAPID API 허브를 통하면 가능(2024.06.종료) (0) | 2023.07.13 |
stable-ts (Whisper)로 영상 자막을 파이썬 코드로 추출해 보기 및 DeepL 파일 번역 도우미 (73) | 2023.06.06 |