Devin.KR

AI 개발 · 기본

바이브 코딩의 정석

받은 코드 읽기 - diff·실행·설명 다시 쓰기

변경 diff 를 읽는 순서(무엇이 바뀌었나→왜→어디에 영향), 이해 못 한 줄을 질문으로 바꾸기, 실행해 보고 예상과 비교, 설명을 자기 말로 다시 쓰기, 그럴듯하지만 틀린 코드의 흔한 모습, 완성 코드는 difflib 로 검색 기능의 두 버전을 비교해 바뀐 줄을 출력하고 숨은 버그를 찾아 고친 버전을 함께 실행한다

개발자KR · 원고 갱신

이 장에서 배우는 것

앞 장에서 작업을 작게 나누고 체크포인트를 남겼다면, 이제 각 체크포인트에서 받은 코드를 읽을 차례다. AI가 “검색 기능을 추가했다”고 답해도 그 말만으로 작업이 끝나지는 않는다. 무엇을 바꾸었는지, 그 변경이 요청한 행동과 연결되는지, 직접 실행했을 때 예상대로 움직이는지 확인해야 한다.

코드를 읽는다는 것은 모든 문법을 외우거나 작성자의 생각을 맞히는 일이 아니다. 변경된 부분을 좁히고, 이해하지 못한 조건을 질문으로 바꾸고, 관찰한 결과를 자기 말로 설명하는 일이다. 이 장에서는 혼자 쓰는 메모·할 일 관리 도구의 검색 기능을 검토한다. 그럴듯하게 보이는 검색 코드에서 숨은 버그를 찾아 수정한 뒤, 두 버전의 차이와 실행 결과를 나란히 살펴본다.

  • 변경 비교(diff)를 무엇이 바뀌었나, 왜 바뀌었나, 어디에 영향을 주나의 순서로 읽는다.
  • 이해하지 못한 줄을 구체적인 입력과 결과를 묻는 질문으로 바꾼다.
  • 실행 전에 예상 결과를 적고, 실제 결과와 비교한다.
  • AI의 설명을 반복하는 대신 코드의 행동을 자기 말로 다시 쓴다.
  • Python 표준 라이브러리 difflib로 두 검색 함수의 바뀐 줄을 출력하고, 수정 전후를 함께 실행한다.

문제 상황

메모·할 일 관리 도구에는 제목과 완료 여부가 저장되어 있다. 사용자는 제목에 검색어가 들어 있는 항목을 찾고 싶다. 완료한 항목도 제목이 일치하면 검색 결과에 포함한다. 완료 여부로 검색 결과를 따로 거르는 기능은 아직 요청하지 않았다.

사용자는 AI에게 다음과 같이 작업을 맡겼다.

제목 검색 함수를 추가해 달라. 검색어 앞뒤의 공백을 제거하고, 영문 대소문자를 구분하지 않도록 해 달라. 제목에 검색어가 들어 있는 항목의 제목을 원래 순서대로 반환해 달라. 검색어가 비어 있거나 공백뿐이면 빈 목록을 반환해 달라. 완료 여부는 검색 조건에 넣지 않는다.

AI는 “공백과 대소문자를 처리했고, 완료한 메모도 검색할 수 있다”고 답했다. 얼핏 요청을 반영한 설명처럼 들린다. 코드에도 공백을 제거하는 줄과 소문자로 바꾸는 줄이 있다. 하지만 “완료한 메모도 검색할 수 있다”는 말은 두 가지로 읽힌다. 제목이 일치하는 완료 항목을 포함한다는 뜻일 수도 있고, 완료한 항목이면 제목과 관계없이 포함한다는 뜻일 수도 있다.

이 차이는 실제 결과를 바꾼다. “메모”를 검색했는데 완료한 “장보기”까지 나타난다면, 사용자는 검색 결과를 믿기 어렵다. 검색어가 공백뿐인데 모든 항목이 나타나는 것도 요청과 다르다. 이때 필요한 일은 함수 전체를 다시 만들어 달라고 하는 것이 아니다. 잘못된 행동과 그 행동을 만드는 조건을 연결하는 일이다.

검색 기능을 읽기 전에 고정할 기대 행동
입력 상황기대 결과확인할 이유
제목 일부가 일치한다일치하는 제목만 반환한다검색의 기본 조건이다
제목은 불일치하고 완료 상태다결과에 포함하지 않는다완료 여부가 검색 조건으로 섞였는지 확인한다
검색어가 공백뿐이다빈 목록을 반환한다공백 제거 뒤의 빈 문자열을 확인한다
영문 대소문자만 다르다일치하는 제목을 반환한다양쪽에 같은 변환을 적용했는지 확인한다

변경 비교는 무엇·왜·영향의 순서로 읽는다

변경 비교는 두 버전의 텍스트에서 달라진 줄을 보여 주는 방식이다. 파일 전체를 처음부터 다시 읽는 대신, 이번 작업에서 판단할 부분을 좁혀 준다. 다만 바뀐 줄만으로 모든 것을 알 수는 없다. 함수가 받는 값과 결과를 사용하는 곳까지 필요한 만큼 이어서 읽어야 한다.

먼저 “무엇이 바뀌었나”를 확인한다. 함수가 새로 생겼는지, 조건 하나가 바뀌었는지, 반환하는 값의 모양이 달라졌는지 구분한다. 이번 예제에서는 빈 검색어를 처리하는 두 줄이 추가되고, 제목 일치 조건에 붙어 있던 완료 여부 조건이 제거된다. 비교를 위해 함수 이름도 달라지지만, 이름 변경 자체가 검색 버그를 고치지는 않는다.

다음으로 “왜 바뀌었나”를 요청과 연결한다. 빈 검색어를 처리하는 줄은 “공백뿐이면 빈 목록을 반환한다”는 요구를 구현한다. 완료 여부 조건의 제거는 “완료 여부는 검색 조건에 넣지 않는다”는 요구를 반영한다. 변경 이유를 설명할 때는 “코드가 더 깔끔해졌다”보다 어떤 입력의 결과가 달라지는지 말하는 편이 정확하다.

마지막으로 “어디에 영향을 주나”를 확인한다. 검색 함수가 제목 목록을 반환한다면 그 목록을 화면에 표시하는 부분이 영향을 받는다. 반환값이 제목 목록에서 메모 전체 목록으로 바뀌면 호출하는 코드도 바뀌어야 한다. 반대로 제목을 고르는 조건만 수정하고 반환값의 형태를 유지했다면, 영향을 받는 범위는 주로 검색 결과의 내용이다.

변경 비교는 바뀐 내용에서 요구 사항을 거쳐 영향을 받는 결과로 이어서 읽는다

통합 비교(unified diff)는 바뀐 줄과 그 주변 줄을 함께 보여 주는 형식이다. 맨 앞의 두 줄은 비교 대상의 이름이다. 줄 앞의 빼기 기호는 이전 버전에서 빠진 줄을, 더하기 기호는 이후 버전에 들어간 줄을 뜻한다. 앞에 공백이 붙은 줄은 두 버전에 공통으로 있는 주변 코드다. 기호는 비교 표시이며 Python 코드의 일부가 아니다.

두 개의 골뱅이 기호 사이에 있는 숫자는 비교 구간의 위치와 길이다. 예제의 “-1,7”은 이전 코드의 첫 줄부터 일곱 줄, “+1,9”는 이후 코드의 첫 줄부터 아홉 줄을 보여 준다는 뜻이다. 처음에는 숫자를 외우기보다 추가된 조건과 삭제된 조건을 찾아 읽으면 된다.

변경량이 작다고 영향도 작은 것은 아니다. 조건에 붙은 or 하나는 여러 항목의 포함 여부를 바꿀 수 있다. 반대로 설명이나 함수 이름이 많이 바뀌어도 실제 결과는 같을 수 있다. 줄 수와 행동의 변화는 따로 판단해야 한다.

모르는 줄은 입력을 가진 질문으로 바꾼다

“이 코드를 설명해 달라”는 요청은 시작점으로 쓸 수 있지만, 검토 중인 의문을 충분히 좁혀 주지는 않는다. 어떤 줄이 이해되지 않는지, 어떤 입력에서 판단이 필요한지 함께 적으면 답을 확인하기 쉬워진다.

이번 검색 코드의 핵심 조건에는 제목 일치 여부와 완료 여부가 or로 연결되어 있다. or는 둘 중 하나라도 참이면 전체 조건을 참으로 만든다. 따라서 제목에 검색어가 없어도 완료 여부가 참이면 해당 항목이 결과에 들어간다. 이는 문법 오류가 아니라 요구와 다른 판단 규칙이다.

이 조건에서 제목이 “장보기”, 완료 여부가 True, 검색어가 “메모”라면 항목이 포함되는가. 조건의 왼쪽과 오른쪽을 각각 계산해서 설명해 달라. 완료 여부는 검색 조건에 넣지 않는다는 요구와도 비교해 달라.

이 질문은 답변을 실행으로 확인할 수 있다. 왼쪽의 제목 일치는 거짓이고, 오른쪽의 완료 여부는 참이다. 둘을 or로 연결하면 참이므로 “장보기”가 포함된다. 설명이 길어도 이 계산과 다르면 다시 확인해야 한다.

공백 처리에도 질문이 필요하다. strip()은 문자열 앞뒤의 공백을 제거한다. 공백뿐인 문자열에 적용하면 빈 문자열이 된다. 그런데 Python에서 빈 문자열은 어떤 문자열에 대해서도 포함 관계가 참이다. 그러므로 공백을 제거했다는 사실만으로 빈 검색어 처리가 끝나지 않는다.

검색어가 공백 세 칸이면 strip() 뒤의 값은 무엇인가. 그 값으로 제목에 포함되어 있는지 검사하면 어떤 결과가 나오는가. 빈 목록을 반환한다는 요구를 만족하려면 어디에서 처리를 끝내야 하는가.

질문을 잘 만들려면 모르는 용어를 억지로 사용할 필요가 없다. “이 줄에서 어떤 값이 만들어지는가”, “이 항목이 왜 포함되는가”, “입력이 비었을 때 어디로 가는가”처럼 값과 흐름을 묻는 문장으로 충분하다.

막연한 의문을 확인 가능한 질문으로 바꾸는 방법
읽다가 생긴 의문바꾼 질문확인할 근거
or가 왜 필요한지 모르겠다제목이 불일치하고 완료 상태이면 포함되는가두 조건의 값과 실행 결과
공백 처리가 맞는지 모르겠다공백뿐인 입력이 빈 문자열이 된 다음 어디로 가는가빈 검색어를 처리하는 분기
소문자 변환이 필요한지 모르겠다PYTHON으로 Python 제목을 찾을 수 있는가검색어와 제목 양쪽의 변환
반환값이 달라졌는지 모르겠다결과의 각 원소는 제목인가, 메모 전체인가결과를 만드는 표현과 출력

실행 전에 예상하고 실행 뒤에 다시 설명한다

실행 결과를 먼저 보면, 나온 결과를 자연스럽다고 받아들이기 쉽다. 이를 줄이려면 입력과 기대 결과를 먼저 적는다. “검색이 잘된다”처럼 넓은 판단 대신, “없는말을 검색하면 빈 목록이 나온다”처럼 관찰 가능한 문장을 쓴다.

이번 예제에서는 네 가지 검색어를 사용한다. “메모”는 제목 일부의 일치를 확인한다. “없는말”은 어떤 제목에도 일치하지 않을 때의 행동을 확인한다. 공백 세 칸은 빈 검색어 처리를 확인한다. “PYTHON”은 영문 대소문자 처리의 일관성을 확인한다. 각 입력은 다른 의문을 해결한다.

같은 데이터와 같은 입력으로 수정 전후를 실행해야 비교가 쉽다. 이전 버전에는 완료 항목이 있고 수정 버전에는 완료 항목이 없다면, 결과가 달라진 이유를 코드에서 찾기 어렵다. 프로그램은 두 함수에 같은 메모 목록을 전달하고, 기대 결과도 함께 출력한다.

실행 전에 적은 기대 결과와 실제 결과의 차이가 수정할 조건을 알려 준다

실행 뒤에는 설명을 자기 말로 다시 쓴다. AI가 사용한 문장을 외우는 대신, 입력이 어떤 값으로 바뀌고 어떤 조건으로 항목을 고르는지 설명한다. 예를 들면 다음과 같다.

검색어의 앞뒤 공백을 제거하고 소문자로 바꾼다. 그 결과가 비어 있으면 빈 목록을 반환한다. 비어 있지 않으면 각 제목을 소문자로 바꾸어 검색어가 들어 있는지 확인한다. 일치하는 제목을 원래 순서대로 모으며 완료 여부는 읽지 않는다.

이 설명에는 입력 처리, 빈 값의 처리, 선택 조건, 반환값이 들어 있다. 코드와 설명을 나란히 놓고 각각을 가리킬 수 있어야 한다. 설명에는 있는데 코드에서 찾을 수 없는 행동이 있다면, 그 부분을 다시 질문하거나 실행해야 한다.

이번 실행은 선택한 네 경우의 증거다. 모든 입력에서 맞는다는 뜻은 아니다. 다만 “실행이 끝났다”와 “요청한 결과가 나왔다”를 구분하는 출발점이 된다. 다음 장에서는 이런 기대를 반복해서 확인할 수 있는 테스트로 옮긴다.

완성 코드

다음 프로그램을 main.py로 저장한다. Python 3.12 이상에서 외부 패키지 없이 실행할 수 있다. 파일을 추가로 만들거나 입력을 기다리지 않으며, 고정된 데이터로 실행한 뒤 끝난다.

inspect는 실행 중인 함수의 소스 코드를 읽는 표준 라이브러리다. 여기서는 직접 저장한 main.py 안의 함수를 읽는다. difflib는 그 소스의 줄을 비교한다. 함수 이름을 다르게 두어 수정 전후를 한 프로그램에서 함께 실행할 수 있게 했다.

import difflib
import inspect


def search_before(notes, query):
    needle = query.strip().lower()
    return [
        note["title"]
        for note in notes
        if needle in note["title"].lower() or note["done"]
    ]


def search_after(notes, query):
    needle = query.strip().lower()
    if not needle:
        return []
    return [
        note["title"]
        for note in notes
        if needle in note["title"].lower()
    ]


def show_diff():
    before = inspect.getsource(search_before).splitlines(keepends=True)
    after = inspect.getsource(search_after).splitlines(keepends=True)
    lines = difflib.unified_diff(
        before,
        after,
        fromfile="before.py",
        tofile="after.py",
        n=3,
    )
    print("=== 검색 함수 변경 비교 ===")
    for line in lines:
        print(line, end="")


def main():
    notes = [
        {"title": "회의 메모", "done": False},
        {"title": "장보기", "done": True},
        {"title": "Python 정리", "done": False},
        {"title": "메모 백업", "done": True},
    ]
    cases = [
        ("제목 일부", "메모", ["회의 메모", "메모 백업"]),
        ("일치 없음", "없는말", []),
        ("공백뿐", "   ", []),
        ("영문 대소문자", "PYTHON", ["Python 정리"]),
    ]

    show_diff()
    print()
    print("=== 같은 입력으로 실행 ===")
    for label, query, expected in cases:
        before = search_before(notes, query)
        after = search_after(notes, query)
        verdict = "일치" if after == expected else "불일치"
        print(f"[{label}] 검색어={query!r}")
        print(f"  기대: {expected}")
        print(f"  이전: {before}")
        print(f"  수정: {after}")
        print(f"  수정 결과 확인: {verdict}")


if __name__ == "__main__":
    main()

줄별 해설

위에서부터 읽되, 각 줄을 요청한 행동과 연결한다. 아래 표는 연속된 줄을 역할별로 묶는다. 특히 검색 결과를 결정하는 줄과 비교 화면만 만드는 줄을 구분해서 읽는다.

완성 코드의 각 부분이 담당하는 일
줄 또는 구간하는 일읽을 때 확인할 점
import 두 줄소스 읽기와 줄 비교 기능을 가져온다둘 다 표준 라이브러리다
search_before의 첫 계산검색어 공백을 제거하고 소문자로 만든다빈 문자열이 될 수 있다
search_before의 선택 조건제목 일치 또는 완료 상태로 항목을 고른다제목이 불일치해도 포함될 수 있다
search_after의 if와 return검색어가 비면 즉시 빈 목록을 반환한다제목 순회를 시작하기 전에 끝낸다
search_after의 선택 조건제목에 검색어가 있는 항목만 고른다완료 여부를 사용하지 않는다
getsource와 splitlines실제 함수 소스를 줄 목록으로 바꾼다줄 끝의 줄바꿈을 보존한다
unified_diff 호출두 소스의 차이와 주변 줄을 만든다파일 이름은 표시용 이름이다
notes와 cases고정된 메모와 기대 결과를 준비한다완료·미완료와 일치·불일치가 섞여 있다
반복문 안의 두 함수 호출같은 입력으로 두 버전을 실행한다데이터를 바꾸지 않고 비교한다
verdict와 출력수정 결과를 기대 목록과 비교한다항목뿐 아니라 순서도 비교한다
마지막 if파일을 직접 실행할 때 main을 호출한다프로그램의 실행 시작점을 정한다

두 검색 함수의 대괄호 안에는 목록을 만드는 표현이 들어 있다. 이를 리스트 컴프리헨션(list comprehension)이라고 한다. 여기서는 메모를 하나씩 읽고, if 뒤의 조건이 참인 메모의 제목을 새 목록에 넣는다. 익숙하지 않다면 “빈 목록을 만들고, 반복문에서 조건에 맞는 제목을 추가한다”는 과정으로 풀어 읽으면 된다.

search_before의 문제는 제목을 고르는 조건에 있다. 검색어가 제목에 들어 있다는 조건 뒤에 or note["done"]이 붙어 있다. 완료 여부는 True 또는 False로 저장되어 있으므로, 완료한 항목은 제목 일치 여부와 관계없이 선택된다. 요청에 없는 선택 기준이 들어간 것이다.

search_after의 if not needle은 검색어가 빈 문자열인지 확인한다. 빈 문자열은 조건 판단에서 거짓으로 취급되므로 not을 붙이면 참이 된다. 이때 return []이 실행되어 함수가 끝난다. 검색할 내용이 없는 상황을 제목 검사보다 먼저 처리한다.

splitlines의 keepends=True는 각 줄 끝의 줄바꿈을 남긴다. 비교 결과에는 그 줄바꿈이 들어 있으므로 print의 end를 빈 문자열로 설정한다. print가 줄바꿈을 하나 더 붙이지 않게 하는 것이다. 이 설정은 검색 동작과 관계가 없고, 비교 화면을 읽기 좋게 만드는 역할이다.

fromfile과 tofile의 이름은 출력의 제목이다. before.py와 after.py라는 실제 파일을 읽거나 생성하지 않는다. 비교에 사용한 내용은 getsource로 읽은 두 함수다. 따라서 별도로 적어 둔 코드 설명문이 실제 함수와 달라지는 문제를 줄일 수 있다.

검색어 출력의 !r은 문자열의 표현을 따옴표와 함께 보여 준다. 공백 세 칸도 따옴표 사이에서 확인할 수 있다. 마지막의 목록 비교는 내용과 순서가 모두 같아야 참이다. 이 프로그램은 선택한 사례를 확인하고 출력하며, 자동 검사 도구를 만드는 데까지 범위를 넓히지는 않는다.

실행 결과

main.py를 저장한 디렉터리에서 다음 명령을 실행한다.

python3 main.py

예상 출력은 다음과 같다. 변경 비교의 공통 코드에는 맨 앞에 공백 한 칸이 붙는다.

=== 검색 함수 변경 비교 ===
--- before.py
+++ after.py
@@ -1,7 +1,9 @@
-def search_before(notes, query):
+def search_after(notes, query):
     needle = query.strip().lower()
+    if not needle:
+        return []
     return [
         note["title"]
         for note in notes
-        if needle in note["title"].lower() or note["done"]
+        if needle in note["title"].lower()
     ]

=== 같은 입력으로 실행 ===
[제목 일부] 검색어='메모'
  기대: ['회의 메모', '메모 백업']
  이전: ['회의 메모', '장보기', '메모 백업']
  수정: ['회의 메모', '메모 백업']
  수정 결과 확인: 일치
[일치 없음] 검색어='없는말'
  기대: []
  이전: ['장보기', '메모 백업']
  수정: []
  수정 결과 확인: 일치
[공백뿐] 검색어='   '
  기대: []
  이전: ['회의 메모', '장보기', 'Python 정리', '메모 백업']
  수정: []
  수정 결과 확인: 일치
[영문 대소문자] 검색어='PYTHON'
  기대: ['Python 정리']
  이전: ['장보기', 'Python 정리', '메모 백업']
  수정: ['Python 정리']
  수정 결과 확인: 일치

“없는말”의 이전 결과는 완료 여부가 검색에 끼어들었다는 사실을 뚜렷하게 보여 준다. 일치하는 제목이 하나도 없는데 두 항목이 나온다. 공백뿐인 입력에서는 빈 문자열의 포함 검사 때문에 모든 제목이 나온다. 완료 여부 조건만 제거해도 이 두 번째 문제는 남으므로, 두 수정이 각각 필요하다.

영문 검색 사례도 결과 전체를 읽어야 한다. 이전 버전에서도 “Python 정리”는 나온다. 찾고 싶은 제목이 나온다는 이유만으로 통과시키면, 함께 나온 불필요한 제목을 놓친다. 확인 대상은 원하는 항목의 존재뿐 아니라 원하지 않은 항목의 부재와 결과 순서까지 포함한다.

실무에서 자주 틀리는 것

요청에 없는 조건을 친절한 처리라고 받아들인다

AI는 주변 데이터에 있는 필드를 활용해 조건을 추가할 수 있다. 그러나 완료 상태를 저장한다는 사실은 검색에서 완료 상태를 사용해야 한다는 뜻이 아니다. 다음 두 함수는 그대로 실행해 차이를 볼 수 있다.

def wrong_match(title, done, query):
    return query.lower() in title.lower() or done


def fixed_match(title, done, query):
    return query.lower() in title.lower()


print(wrong_match("장보기", True, "메모"))
print(fixed_match("장보기", True, "메모"))

첫 출력은 True이고 두 번째는 False다. 고친 함수에서는 호출 형태를 비교하기 위해 done 매개변수를 남겼지만 조건에는 쓰지 않는다. 완성 코드에서는 메모를 받는 검색 함수 내부에서 완료 필드를 읽지 않는다. 코드에 조건이 늘어날 때는 어떤 요구를 구현한 것인지 먼저 확인한다.

공백 제거를 빈 입력 처리와 같다고 생각한다

공백을 제거하는 작업과 비어 있는 값을 처리하는 작업은 다르다. 빈 문자열로 제목 포함 검사를 하면 참이 된다. 다음 예제는 그 차이를 직접 보여 준다.

def wrong_match(title, query):
    needle = query.strip().lower()
    return needle in title.lower()


def fixed_match(title, query):
    needle = query.strip().lower()
    if not needle:
        return False
    return needle in title.lower()


print(wrong_match("회의 메모", "   "))
print(fixed_match("회의 메모", "   "))

첫 출력은 True이고 두 번째는 False다. 이 예제는 한 제목의 일치 여부를 반환하므로 False를 사용한다. 완성 코드의 검색 함수는 제목 목록을 반환하므로 빈 목록을 사용한다. 같은 빈 입력 정책이라도 함수의 반환값 형태에 맞게 표현해야 한다.

대소문자를 한쪽에서만 바꾼다

검색어를 소문자로 바꾸어도 제목에 대문자가 남아 있으면 기대한 일치가 나오지 않을 수 있다. 비교하는 양쪽에 같은 규칙을 적용했는지 읽어야 한다.

def wrong_match(title, query):
    return query.strip().lower() in title


def fixed_match(title, query):
    return query.strip().lower() in title.lower()


print(wrong_match("Python 정리", "PYTHON"))
print(fixed_match("Python 정리", "PYTHON"))

첫 출력은 False이고 두 번째는 True다. 여기서는 영문 대소문자 처리만 좁혀 확인한다. lower()를 사용했다는 사실만으로 모든 언어의 검색 규칙을 지원한다고 설명하면 범위가 넓어진다. 코드의 실제 처리와 약속한 검색 범위를 맞춰 써야 한다.

변경한 조건을 읽고 반환값의 변화는 놓친다

검색 조건이 맞더라도 반환하는 값이 달라지면 호출하는 부분에 영향을 줄 수 있다. 제목 목록을 기대하는 코드에 메모 전체가 전달되면 화면에 표시되는 내용도 달라진다.

def wrong_search(notes, query):
    needle = query.strip().lower()
    if not needle:
        return []
    return [note for note in notes if needle in note["title"].lower()]


def fixed_search(notes, query):
    needle = query.strip().lower()
    if not needle:
        return []
    return [
        note["title"]
        for note in notes
        if needle in note["title"].lower()
    ]


notes = [{"title": "회의 메모", "done": False}]
print(wrong_search(notes, "메모"))
print(fixed_search(notes, "메모"))

첫 함수는 사전이 들어 있는 목록을 반환하고, 두 번째 함수는 제목 문자열이 들어 있는 목록을 반환한다. 이 도구의 요청은 제목 목록이므로 두 번째가 맞다. 사전 목록 자체가 잘못된 형태인 것은 아니다. 요청과 호출하는 코드가 기대하는 형태에 맞는지가 판단 기준이다.

한눈에 보기

받은 검색 코드를 검토하는 순서와 남길 기록
순서확인할 질문남길 기록
무엇이 바뀌었나조건, 입력 처리, 반환값 중 무엇이 달라졌는가추가·삭제된 행동
왜 바뀌었나어떤 요구를 구현하는 변경인가요구와 코드의 연결
어디에 영향을 주나어떤 입력과 호출 결과가 달라지는가영향을 받는 사례
직접 실행한다미리 적은 결과와 실제 결과가 같은가입력·기대·실제 결과
자기 말로 설명한다코드의 각 행동을 설명에서 찾을 수 있는가입력부터 반환까지의 짧은 설명

검토가 끝났다는 기준은 AI의 답변이 납득된다는 느낌만으로 정하지 않는다. 바뀐 조건을 요구와 연결할 수 있고, 선택한 입력의 결과를 확인했으며, 함수의 행동을 자기 말로 설명할 수 있어야 한다. 아직 설명하지 못하는 줄은 다음 질문의 출발점이다.

연습 문제

  1. 완성 코드의 사례 목록에 검색어 “ 메모 ”를 추가하라. 실행 전에 기대 결과를 적고, 수정 함수의 결과와 비교하라. 이 사례가 기존 “메모” 사례에 더해 무엇을 확인하는지 설명하라.
  2. 완료 여부 조건만 제거하고 빈 검색어 분기는 추가하지 않았다고 가정하라. 공백 세 칸을 검색하면 어떤 결과가 나오는지 예상하고, 완성 코드의 수정 함수에서 해당 분기를 잠시 빼서 확인하라. 확인 뒤에는 분기를 복구하라.
  3. 제목이 “메모 정리”, 완료 여부가 False인 메모를 데이터 맨 끝에 추가하라. 어떤 기대 결과를 수정해야 하는지 적고 실행하라. 반환 순서가 유지되는지도 확인하라.
  4. 수정 함수를 읽고 네 문장 이내로 동작을 설명하라. 이어서 이해가 덜 된 줄 하나를 골라, 구체적인 입력과 결과를 포함한 AI 질문을 작성하라.

정답과 해설

앞뒤 공백이 있는 검색어

기대 결과는 “회의 메모”와 “메모 백업”의 목록이다. “ 메모 ”는 strip()을 거치면 “메모”가 된다. 기존 사례는 제목 부분 일치를 확인하고, 새 사례는 의미 있는 검색어 앞뒤의 공백이 제거되는지 확인한다. 공백뿐인 입력을 확인하는 사례와도 역할이 다르다.

사례 목록에 다음과 같은 항목을 추가하면 된다. 아래 코드는 추가할 항목을 독립적으로 실행해 값의 형태를 확인하는 예제다.

case = ("앞뒤 공백", "  메모  ", ["회의 메모", "메모 백업"])
print(case)

빈 검색어 분기를 빼면 남는 문제

결과는 네 제목 모두가 들어 있는 목록이다. 완료 여부 조건을 지워도 빈 문자열은 모든 제목에 포함된 것으로 판단된다. 완료 항목이 무조건 선택되는 문제와 빈 검색어가 모든 항목을 선택하는 문제는 원인이 다르다. 하나를 고쳤다고 다른 하나까지 해결되지는 않는다.

분기를 복구한 뒤 공백뿐인 사례의 결과가 다시 빈 목록인지 확인한다. 마지막으로 남길 설명은 “빈 검색어 분기가 제목 검사 전에 실행을 끝낸다”가 된다.

항목 추가와 반환 순서

“메모” 사례의 기대 결과에 “메모 정리”를 마지막으로 추가해야 한다. “없는말”과 공백뿐인 사례는 계속 빈 목록이며, “PYTHON” 사례도 그대로다. 메모를 맨 끝에 추가했고 함수가 원래 목록 순서대로 제목을 모으므로 새 제목은 검색 결과의 맨 끝에 나온다.

기대 결과를 수정하지 않고 실행하면 “제목 일부” 사례의 수정 결과 확인이 불일치로 표시된다. 이는 함수의 오류일 수도 있지만, 이번 상황에서는 데이터 변경을 기대 결과에 반영하지 않은 것이 원인이다. 결과가 다르면 코드와 기대값을 함께 검토해야 한다.

자기 말로 쓴 설명과 후속 질문

설명의 한 예는 다음과 같다.

검색어의 앞뒤 공백을 제거하고 소문자로 바꾼다. 검색어가 비면 빈 목록을 반환한다. 각 제목을 소문자로 바꾸어 검색어가 포함되는지 확인한다. 일치하는 제목을 입력 목록의 순서대로 반환하며 완료 여부는 사용하지 않는다.

후속 질문은 같은 제목이 두 번 저장된 상황을 고를 수 있다.

“회의 메모”라는 제목이 두 번 들어 있고 검색어가 “메모”라면 결과에도 같은 제목이 두 번 들어가는가. 목록을 만드는 부분을 기준으로 설명해 달라.

현재 코드는 각 메모를 개별적으로 처리하므로 같은 제목이 두 번 들어간다. 이 질문은 코드를 바꾸기 위한 요청이 아니라 현재 행동을 이해하기 위한 질문이다. 중복 제거가 필요하다면 별도의 요구를 먼저 정해야 한다. 받은 코드를 읽는 과정에서는 현재 행동을 확인하는 일과 새로운 기능을 요청하는 일을 구분한다.

오탈자·오류 제보 비공개로 접수되어 원고 수정에 반영됩니다

이메일 등 개인정보는 받지 않습니다. 답변이 필요한 질문은 아래 댓글을 이용해 주세요.

READER FEEDBACK

질문·의견

내용에 관한 질문이나 더 나은 설명을 위한 의견을 남겨 주세요. 오탈자는 위의 제보 양식이 더 빨리 반영됩니다. 이 댓글은 원래 게시글과 같은 자리에 쌓입니다.

댓글 0

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

댓글을 남기려면 로그인이 필요합니다.