Disclamer: 이 문서는 사내용 세미나 진행을 위해, 사외에서 취득 가능한 정보만를 활용하여 사외에서 작성되었습니다.
◼︎ 우리의 현실은
리팩토링 자체가 어렵거나 힘든 작업은 아닙니다. 하지만 우리가 마주한 환경은 그렇게 쉽지만은 않은 것 같습니다.
우리가 마주하는 어려움은 보통 이런 항목들 입니다.
- 코드가 이미 배포되어서 운영 중이다.
- 그런데 이 코드에 테스트 코드가 없거나 있더라도 매우 부족하다.
- 리팩토링을 하다가 서비스 장애가 발생하면, 곧 바로 유관 부서 혹은 현장에서 피드백이 발생한다.
- 일부 프로젝트에서는 불량 유발이나, 설비 고장으로 이어질 수 있다.
우리가 이런 상황에서 마음편히 LLM에게 "리팩토링 해줘"라고 요청할 수 있을까요?
따라서 운영 중인 코드를 리팩토링하는 것은 "변경 범위를 최소화" + "안정장치 마련"이라는 두 가지 원칙이 필요합니다.
◼︎ 기존 리팩토링 vs. LLM 기반 리팩토링
이번에도 기존 방식과 비교해봅니다.
- 기존 리팩토링
보통 개발자가 함수 또는 클래스 단위로 직접 코드를 리뷰하면서, 변경 후에도 정상 동작하는지 하나하나 검증합니다.
시간이 오래 걸리지만 안정성은 높습니다. 유닛 테스트가 준비되어 있다면 매우 안정감 있는 작업이 되겠지만, 그렇지 못한 경우가 대부분입니다. - LLM 기반 리팩토링
코드를 하나 하나 뜯어보면서 씨름하는 시간을 단축(혹은 제거)할 수 있고, 변경 결과에 대한 초안을 빠르게 얻을 수 있습니다.
하지만 불필요하게 많은 코드베이스를 수정하기도 하고, 너무 많은 변경 사항으로 사용자가 리뷰할 수 없는 경우가 많습니다.
따라서 LLM에게 반드시 "어디까지 건드려도 되는지"와 "어떻게 검증할지"를 명시해줘야 합니다.
◼︎ 안전한 리팩토링을 위한 핵심 전략 3가지
1. 리팩토링 목적을 "좁게" 정의
"전체 프로젝트 리팩토링"같은 추상적인 목적은 피하고, "함수 분리만 해줘", "로깅만 개선해줘" 처럼 목적, 작업 단위를 안전하게 쪼개야 합니다.
2. 변경 범위를 제약하기
"주요 로직은 건드리지 말고, 함수 시그니처만 개선해줘", "출력 형식은 그대로 두고, 내부 코드만 읽기 좋게 바꿔줘"와 같이 변경 범위를 제약해야 합니다.
3. 자동 검증 루틴을 포함시키기
유닛 테스트 코드 입/출력 또는 예상되는 출력 결과 등을 프롬프트에 포함시켜서, LLM이 이 결과를 맞춰야 한다는 것을 강제합니다.
◼︎ 예제: 장애 없는 로그 파서 리팩토링
운영 중인 아래와 같은 로그 바서가 있습니다. 현재 기능 수행에는 문제가 없지만, 코드가 길고 복잡해서 읽기가 힙듭니다.
우리의 목표는 "읽기 좋은 코드로 바뀌되, 출력 결과는 절대 변하지 않아야 한다" 입니다.
def parse_logs(log_lines):
results = []
for line in log_lines:
if "ERROR" in line:
parts = line.split(":", 1)
code = int(parts[0].split()[-1])
message = parts[1].strip()
results.append({
"code": code,
"message": message
})
return results
Step1. 프롬프트를 작성합니다.
앞에서와 같이 구조화된 프롬프트를 작성합니다. 구조화된 프롬프트는 곧 잘 작성된 작업 지시서와도 같습니다.
목적: 출력 결과를 변경하지 않고 코드만 가독성 있게 리팩토링
조건:
(1) 함수 시그니처(parse_logs)는 유지한다
(2) 결과 데이터 형식(list or dict)은 변경 금지
(3) 테스트용 입/출력 예시를 반드시 일치시켜야 함
테스트 예시:
입력: ["ERROR 404: Not Found", "INFO start", "ERROR 500: Internal Error"]
출력: [ {"code": 404, "message": "Not Found"}, {"code": 500, "message": "Internal Error"} ]
현재 코드:
(붙여 넣기)
Step2. LLM에게 리팩토링 요청하기
프롬프트를 LLM에게 입력하면 아래와 같은 결과를 출력합니다.
def parse_logs(log_lines):
def parse_error(line: str):
parts = line.split(":", 1)
return {
"code": int(parts[0].split()[-1]),
"message": parts[1].strip()
}
return [parse_error(line) for line in log_lines if line.startswith("ERROR")]
Step3. 리뷰 & 검증
운영 중인 코드를 리팩토링하는 것이기 때문에, 반드시 리뷰와 검증 과정을 거쳐야 합니다.
이미 프롬프트를 작성할 때, 유닛 테스트 입/출력을 정의했기 때문에 pytest 코드를 작성하여 검증 할 수 있습니다.
이 코드 또한 LLM에게 요청할 수도 있겠지만, 이번에는 직접 작성해봅니다.
def test_parse_logs():
logs = ["ERROR 404: Not Found", "INFO start", "ERROR 500: Internal Error"]
expected = [
{"code": 404, "message": "Not Found"},
{"code": 500, "message": "Internal Error"}
]
assert parse_logs(logs) == expected
만약 테스트가 실패한다면? 실패 메시지를 LLM에게 입력하고 개선을 요청합니다. 피드백 루프를 반복적으로 수행하여 테스트가 통과할 때까지 리팩토링을 수행합니다.
◼︎ Wrap-up
운영 중인 코드를 테스트 없이 리팩토링하다가 코드가 깨지는 순간 바로 사고가 발생합니다.
따라서 LLM을 활용하여 리팩토링을 할 때는 "바꿔도 안전한 부분"만, "검증 가능한 형태"로 리팩토링 해야 합니다.
다음 글에서는 운영 중인 코드를 리팩토링하는 몇 가지 사례에 대해 알아보겠습니다.
좋아요 & 댓글 많이 남겨주세요~~
'Vibe Coding' 카테고리의 다른 글
| [코드리뷰] 리뷰 해줄 사람이 없을 때, LLM 활용하기 (0) | 2025.09.10 |
|---|---|
| [리팩토링] LLM으로 테스트 불가능한 구조를 개선하기 (1) | 2025.09.10 |
| [리팩토링] LLM을 사용하여 목적 기반으로 리팩토링 수행하기 (0) | 2025.09.07 |
| LLM 성능 한계를 개선하기 위한 방법 (prompt 전략) (0) | 2025.09.07 |