Devin.KR

AI 개발 · 실무

Claude·Gemini·Codex 실전 백서 - 터미널 AI 코딩 도구를 일에 쓰는 법

지침 파일 - CLAUDE.md·AGENTS.md·GEMINI.md 와 규칙 파일 쓰는 법

지침 파일이 하는 일(매 세션 처음 읽히는 규칙), 들어갈 것(빌드·테스트 명령, 금지 사항, 폴더 구조, 코드 관례)과 빼야 할 것(비밀값·자주 바뀌는 사실), 한 저장소에서 세 파일을 함께 관리하는 방법(공통 내용 하나로 모으기), 폴더별 지침, 완성 코드는 공통 규칙 파일과 도구별 덧붙임을 합쳐 세 지침 파일을 만들고 금지 패턴(키·비밀번호)을 검사하는 생성기

개발자KR · 원고 갱신

이 장에서 배우는 것

앞 장에서 같은 저장소에 세 도구를 실행했다면, 이제 팀의 작업 기준을 저장소 안에 남길 차례다. 사람이 매번 “테스트는 이렇게 실행하고 이 폴더는 건드리지 말라”고 설명하면 세션마다 빠지는 내용이 생긴다. 지침 파일은 반복할 설명을 버전 관리되는 문서로 옮기는 수단이다. 다만 파일에 적었다고 실행이 강제되는 것은 아니다. 지침을 읽은 뒤에도 결과를 실행하고 테스트하며 변경 차이(diff)를 검토해야 한다.

이 장에서는 공통 규칙과 도구별 덧붙임을 따로 작성하고, 이를 합쳐 세 지침 파일을 만드는 프로그램을 구현한다. 프로그램은 실제 AI 도구를 호출하지 않는다. Python 3.12 이상과 표준 라이브러리만으로 임시 저장소를 만들고, 위험한 입력을 거부하고, 생성 결과가 일정한지 확인한다.

  • 지침 파일에 남길 정보와 다른 곳에서 관리할 정보를 구분한다.
  • 공통 규칙 하나를 원본으로 삼아 세 지침 파일의 내용이 어긋나는 일을 줄인다.
  • 저장소 전체 규칙과 폴더별 규칙의 책임을 나눈다.
  • 금지 패턴 검사, 재생성 검사, 변경 차이 검토를 생성 과정에 넣는다.

문제 상황

작은 팀이 주문 내역을 정리하는 Python 프로그램을 함께 만든다. 한 사람은 Claude Code로 기능을 추가하고, 다른 사람은 Gemini 계열 도구로 테스트를 보완하며, 또 다른 사람은 Codex CLI로 변경을 검토한다. 처음에는 각자 요청문 끝에 팀 규칙을 붙였다. 작업이 늘자 같은 저장소에서 서로 다른 설명이 돌아다니기 시작했다.

주문 파일을 읽는 함수를 수정하라. 테스트를 실행하고, 샘플 데이터는 바꾸지 말라.

이 요청만으로는 어떤 테스트 명령을 실행할지, 샘플 데이터가 어느 폴더에 있는지 알기 어렵다. 한 사람은 tests만 실행하고, 다른 사람은 이름이 비슷한 임시 스크립트를 실행한다. 누군가는 입력 예제를 고쳐 테스트를 통과시킨다. 문제는 도구의 우열이 아니라 팀이 같은 작업 기준을 전달하지 않았다는 데 있다.

팀은 루트에 지침 파일 세 개를 두지만, 이번에는 문서가 갈라진다. 테스트 명령을 한 파일에서만 바꾸고 나머지는 그대로 둔다. 시간이 지나면 어떤 도구를 쓰느냐에 따라 지시가 달라진다. 세 파일을 사람이 각각 수정하는 방식 대신, 공통 내용을 한곳에서 고치고 세 파일을 다시 만드는 방식이 필요하다.

또 다른 문제도 생긴다. 작업 중 임시 비밀번호를 설명에 붙인 사람이 그 내용을 지침에 복사한다. 지침 파일은 여러 세션에 반복해서 전달될 수 있으므로, 한번 들어간 비밀값의 노출 범위가 넓어질 수 있다. 따라서 생성기는 문장을 합치기 전에 금지 패턴을 검사해야 한다. 검사에 걸렸을 때는 값 자체를 출력하지 않고 파일명과 줄 번호만 알려 주어야 한다.

지침은 반복해서 읽을 작업 기준이다

지침 파일은 새 세션의 시작에서 도구에 전달할 작업 기준을 담는다. 팀에서는 이를 “매 세션 처음 확인할 규칙”으로 운영하면 이해하기 쉽다. 그러나 이 표현을 모든 파일이 언제나 자동으로 읽힌다는 보장으로 받아들이면 안 된다. 실제 탐색 범위와 읽는 시점은 도구와 실행 위치에 따라 확인해야 한다. 대화에서 전달하는 이번 작업의 목적과 저장소에 남기는 지속적인 기준도 구분해야 한다.

2026년 10월 기준 예시: 도구별 지침 파일과 적용 확인점
도구지침 파일팀에서 확인할 점
Claude CodeCLAUDE.md현재 실행 위치에서 적용되는 지침 범위를 확인한다.
Codex CLIAGENTS.md작업 경로에 적용되는 지침과 충돌 여부를 확인한다.
Gemini CLIGEMINI.md실행 환경의 지침 탐색 범위를 확인한다.
Antigravity CLI공식 문서에서 확인GEMINI.md가 적용된다고 추정하지 않는다.

2026년 10월 기준 예시로, 개인 사용의 중심이 된 Antigravity CLI와 Gemini CLI를 같은 이름의 실행 도구로 취급해서는 안 된다. 이 장의 생성기는 확인된 파일명인 GEMINI.md를 생성한다. 이것은 Gemini CLI용 산출물이며 Antigravity CLI의 규칙 파일명을 확정하는 예제가 아니다. 해당 환경에 적용할 파일과 배치 방법은 Gemini CLI 공식 저장소와 해당 도구의 공식 문서에서 확인한다. 다른 두 도구의 적용 방식도 Claude Code 공식 문서와 Codex 공식 문서에서 확인한다.

지침의 좋은 문장은 관찰 가능한 행동을 지정한다. “품질을 높여라”보다 “변경 뒤에 지정한 테스트 명령을 실행하고 실패한 항목을 보고하라”가 낫다. “조심해서 수정하라”보다 “data/examples의 파일을 바꿔야 한다면 변경 이유를 먼저 설명하라”가 검토하기 쉽다. 규칙의 길이보다 실행과 검토로 연결되는지가 중요하다.

지침에 남길 정보와 별도로 관리할 정보
구분남길 내용피할 내용이유
실행 절차현재 저장소에서 확인한 빌드·테스트 명령실행해 보지 않은 추정 명령재현 가능한 확인 절차가 필요하다.
수정 범위보호할 폴더와 변경 전 설명이 필요한 대상“알아서 안전하게” 같은 표현판단 기준을 구체적으로 남긴다.
구조와 관례폴더 역할, 이름 규칙, 오류 처리 원칙소스 전체의 긴 복사본지침과 실제 코드의 중복을 줄인다.
환경 정보설정 확인 위치와 필요한 항목의 이름키·비밀번호·접속 토큰의 실제 값비밀값은 지침의 재료가 아니다.
진행 상태작업 기록을 찾는 위치오늘의 담당자, 현재 배포 번호자주 바뀌는 사실은 쉽게 낡는다.

빌드 단계가 없는 프로젝트라면 없는 명령을 만들어 넣지 않는다. “별도 빌드 단계 없음”이라고 적고 실제 검사 명령을 남기면 된다. 명령이 바뀌는 경우에는 코드 변경과 같은 검토에서 지침도 수정한다. 지침이 오래 유지될 정보만 담는다는 말은 한번 작성하고 방치한다는 뜻이 아니다.

또한 “비밀 파일을 읽지 말라”는 문장은 권한 설정을 대신하지 못한다. 지침은 행동 기준이고, 실행 가능한 작업의 경계는 별도의 통제가 담당한다. 이 장에서는 기준을 문서로 만들고 검증하는 데 집중한다. 읽기와 쓰기의 실제 허용 범위는 다음 장에서 다룬다.

공통 원본과 도구별 덧붙임을 나눈다

예제에서는 rules/common.md에 공통 규칙을 두고, rules/claude.md, rules/codex.md, rules/gemini.md에 짧은 덧붙임을 둔다. 이 경로들은 팀이 정한 생성기 입력 형식이다. 도구가 이 입력 파일들을 자동으로 읽는다는 의미가 아니다. 생성기가 내용을 직접 합쳐 루트의 세 지침 파일에 넣는다.

공통 원본: 테스트 명령, 폴더 역할, 수정 금지 대상, 코드 관례를 기록한다.
도구별 덧붙임: 결과를 보고할 때 추가로 확인할 항목만 기록한다.
생성 파일: 공통 원본과 해당 덧붙임을 합친 결과이며 직접 수정하지 않는다.

덧붙임에 테스트 명령을 다시 적으면 공통화의 이점이 사라진다. 공통 원본은 팀 전체가 지킬 기준이고, 덧붙임은 중복되지 않는 짧은 요구다. 예제의 “검토 결과를 먼저 보고하라” 같은 문장은 팀이 도구별로 부여한 역할이지 해당 제품만의 기능이나 제약을 뜻하지 않는다.

하나의 공통 원본과 세 덧붙임을 합쳐 세 지침 파일을 생성한다

저장소에는 입력 원본과 생성 파일을 함께 커밋하는 방식을 택할 수 있다. 이 경우 검토자는 원본의 의도와 실제로 전달될 파일을 함께 볼 수 있다. 대신 생성 파일만 고치는 실수를 막아야 한다. 예제에서는 첫 줄에 생성 파일임을 표시하고, 같은 입력으로 두 번 생성했을 때 결과가 같은지 검사한다.

생성 파일의 최신 여부는 재현성과 다른 문제다. 같은 입력에서 같은 결과가 나와도 누군가 재생성을 잊었을 수 있다. 실제 저장소에서는 생성 전의 커밋된 파일과 재생성한 파일을 비교하여 차이가 남는지 확인한다. 이때 원본 수정, 생성 파일 수정, 의도하지 않은 변경을 한 묶음으로 검토한다. 재생성으로 생긴 차이를 곧바로 정답으로 간주해서는 안 된다.

폴더별 지침과 생성 전 검사를 설계한다

루트 지침에는 모든 작업에 필요한 기준을 둔다. 특정 폴더에서만 필요한 상세 규칙까지 넣으면 다른 작업에서도 불필요한 내용을 읽게 된다. 예를 들어 src는 실행 코드의 입력 검증 원칙을, tests는 테스트 자료와 외부 의존성에 관한 원칙을 가질 수 있다. 폴더별 지침은 루트의 공통 기준을 반복하기보다 그 위치에서 추가되는 책임을 설명해야 한다.

루트 규칙: 변경 후 테스트를 실행한다. 샘플 데이터를 임의로 수정하지 않는다.
tests의 추가 규칙: 테스트에서 외부 네트워크를 사용하지 않는다. 실패를 숨기려고 검증 조건을 지우지 않는다.

2026년 10월 기준 예시로, 폴더별 지침의 파일명, 자동 탐색 범위, 충돌 시 적용 방식은 도구별 공식 문서에서 확인해야 한다. 팀이 원하는 “루트 기준에 폴더 기준을 더한다”는 설계와 제품의 실제 읽기 동작은 구분해야 한다. 같은 폴더에 세 파일을 놓았다는 이유만으로 적용 방식까지 같다고 판단하지 않는다.

이 장의 코드는 폴더별 파일을 실제로 생성하지 않는다. 먼저 루트의 세 파일을 안정적으로 만드는 범위로 제한한다. 폴더별 생성으로 확장할 때도 공통 원본과 덧붙임을 분리하고, 대상 경로를 고정 목록으로 관리하는 원리는 그대로 적용할 수 있다. 하위 폴더의 규칙이 루트의 금지 사항을 해제하도록 작성하지 않는 것도 팀의 작성 원칙으로 정한다.

금지 패턴 검사는 세 가지를 찾는다. 키나 비밀번호처럼 보이는 이름에 값을 대입한 줄, 비밀번호가 포함된 URL, 개인 키 시작 표식이다. 여기서 목적은 모든 비밀값을 판별하는 것이 아니라 자주 생기는 복사 실수를 생성 전에 막는 것이다. 예제는 패턴에 맞으면 가짜 값도 거부한다. 허용 예외를 많이 넣어 검사 의미를 흐리는 대신, 지침에서 값 대입 자체를 빼도록 유도한다.

모든 입력을 먼저 검사하고 위반이 없을 때만 세 지침 파일을 쓴다

검사는 파일을 하나씩 쓰면서 수행하지 않는다. 첫 파일을 쓴 뒤 두 번째 입력에서 위반을 발견하면 출력이 서로 다른 상태로 남는다. 모든 입력을 읽고 검사한 다음 모든 출력 문자열을 준비하고 마지막에 기록한다. 이 구조는 검사 실패로 인한 부분 생성을 막는다. 다만 기록 도중 디스크 오류가 발생하는 상황까지 세 파일 전체의 일괄 교체로 보장하지는 않는다.

완성 코드

다음 내용을 main.py로 저장한다. 프로그램은 임시 폴더에 원본 네 개를 만든다. 처음에는 가짜 비밀번호 대입문을 넣어 거부 동작을 확인하고, 이를 제거한 뒤 세 파일을 생성한다. 임시 경로와 비밀값은 출력하지 않으며, 종료 시 임시 폴더는 정리된다.

import re
from pathlib import Path
from tempfile import TemporaryDirectory


TARGETS = (
    ("CLAUDE.md", "claude.md"),
    ("AGENTS.md", "codex.md"),
    ("GEMINI.md", "gemini.md"),
)

COMMON = """# 공통 작업 규칙

## 구조
- src: 실행 코드
- tests: 자동 테스트
- data/examples: 검토용 샘플 데이터

## 실행
- 별도 빌드 단계는 없다.
- 문법 검사: python3 -m compileall -q src tests
- 테스트: python3 -m unittest discover -s tests

## 금지 사항
- 비밀값을 코드, 문서, 로그에 넣지 않는다.
- data/examples 변경이 필요하면 이유를 먼저 설명한다.
- 테스트 실패를 숨기려고 검증 조건을 지우지 않는다.

## 코드 관례
- 공개 함수에는 타입 힌트를 작성한다.
- 입력 오류와 내부 오류를 구분한다.
- 변경 후 테스트 결과와 변경 차이를 검토한다.
"""

EXTRAS = {
    "claude.md": """# 도구별 덧붙임

- 작업 종료 시 변경 이유와 실행한 검사를 보고한다.
""",
    "codex.md": """# 도구별 덧붙임

- 검토 시 동작 오류와 누락된 테스트를 먼저 보고한다.
""",
    "gemini.md": """# 도구별 덧붙임

- 구현 전에 가정한 입력 형식과 예외 조건을 정리한다.
""",
}

PATTERNS = (
    (
        "값 대입",
        re.compile(
            r"""(?ix)
            (?<![a-z0-9_])
            (?:[a-z0-9]+[_-])*
            (?:api[_-]?key|access[_-]?token|secret|password|passwd)
            \b ["']? \s* [=:] \s* \S+
            """
        ),
    ),
    (
        "URL 인증 정보",
        re.compile(r"(?i)\b[a-z][a-z0-9+.-]*://[^/\s:@]+:[^/\s@]+@"),
    ),
    (
        "개인 키 표식",
        re.compile(r"-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY-----"),
    ),
)


def normalize(text: str) -> str:
    text = text.replace("\r\n", "\n").replace("\r", "\n")
    return text.strip() + "\n"


def find_violations(name: str, text: str) -> list[str]:
    found = []
    for number, line in enumerate(text.splitlines(), start=1):
        for label, pattern in PATTERNS:
            if pattern.search(line):
                found.append(f"{name}:{number}: {label}")
    return found


def render(root: Path) -> dict[str, str]:
    names = ("common.md",) + tuple(extra for _, extra in TARGETS)
    sources = {}
    violations = []

    for name in names:
        raw = (root / "rules" / name).read_text(encoding="utf-8")
        sources[name] = normalize(raw)
        violations.extend(find_violations(name, raw))

    if violations:
        raise ValueError("\n".join(violations))

    outputs = {}
    for target, extra in TARGETS:
        header = (
            "<!-- 생성 파일: 직접 수정하지 않는다. "
            f"원본: rules/common.md + rules/{extra} -->\n\n"
        )
        outputs[target] = (
            header + sources["common.md"] + "\n" + sources[extra]
        )
    return outputs


def generate(root: Path) -> dict[str, str]:
    outputs = render(root)
    for name, content in outputs.items():
        (root / name).write_bytes(content.encode("utf-8"))
    return outputs


def snapshot(root: Path) -> dict[str, bytes]:
    return {
        name: (root / name).read_bytes()
        for name, _ in TARGETS
    }


def require(condition: bool, message: str) -> None:
    if not condition:
        raise RuntimeError(message)


def main() -> None:
    with TemporaryDirectory(prefix="instruction-demo-") as folder:
        root = Path(folder)
        rules = root / "rules"
        rules.mkdir()
        (rules / "common.md").write_text(COMMON, encoding="utf-8")
        for name, content in EXTRAS.items():
            (rules / name).write_text(content, encoding="utf-8")

        bad_path = rules / "codex.md"
        bad_text = EXTRAS["codex.md"] + "password = demo-only\n"
        bad_path.write_text(bad_text, encoding="utf-8")

        try:
            generate(root)
        except ValueError as error:
            require(
                str(error) == "codex.md:4: 값 대입",
                "예상하지 못한 검사 결과",
            )
            require(
                all(not (root / name).exists() for name, _ in TARGETS),
                "검사 실패 뒤 출력 파일이 생겼다.",
            )
            print(f"차단: {error}")
            print("검사 실패 후 생성 파일: 0개")
        else:
            raise RuntimeError("금지 패턴을 놓쳤다.")

        bad_path.write_text(EXTRAS["codex.md"], encoding="utf-8")
        outputs = generate(root)
        first = snapshot(root)

        for name, _ in TARGETS:
            content = outputs[name]
            require(COMMON.strip() in content, "공통 규칙 누락")
            require(content.count("# 공통 작업 규칙") == 1, "공통 규칙 중복")
            print(f"생성: {name}")

        generate(root)
        require(first == snapshot(root), "재생성 결과 불일치")

        bad_path.write_text(bad_text, encoding="utf-8")
        try:
            generate(root)
        except ValueError:
            require(first == snapshot(root), "기존 출력이 변경되었다.")
        else:
            raise RuntimeError("재검사에서 금지 패턴을 놓쳤다.")
        bad_path.write_text(EXTRAS["codex.md"], encoding="utf-8")

        samples = (
            ("password = demo-only", True),
            ('"api_key": "demo-only"', True),
            ("https://user:demo-only@example.invalid", True),
            ("-----BEGIN PRIVATE KEY-----", True),
            ("비밀번호는 지침에 기록하지 않는다.", False),
        )
        for text, expected in samples:
            actual = bool(find_violations("sample.md", text))
            require(actual == expected, "패턴 회귀 검사 실패")

        print("검증: 공통 규칙 포함·중복 없음")
        print("검증: 재생성 바이트 일치")
        print("검증: 차단 시 기존 파일 유지")
        print(f"검증: 금지 패턴 사례 {len(samples)}개 통과")


if __name__ == "__main__":
    main()

줄별 해설

가져오기와 TARGETS. re는 금지 패턴을, Path는 파일 경로를, TemporaryDirectory는 실행 후 정리할 작업 공간을 담당한다. TARGETS는 생성할 이름과 덧붙임 파일을 짝지은 고정 목록이다. 사용자 입력을 출력 경로에 연결하지 않으므로 이 예제에서는 경로 이탈을 별도로 처리할 필요가 없다. 목록 순서는 출력 순서도 결정한다.

COMMON과 EXTRAS. 공통 원본에는 폴더 역할, 검사 명령, 수정 기준, 코드 관례를 넣는다. 예제에 적힌 src와 tests는 생성될 지침의 설명 대상이다. 생성기 자체가 그 프로젝트의 코드를 만들거나 해당 명령을 실행하지는 않는다. 실제 팀에 옮길 때는 저장소에서 성공을 확인한 명령으로 바꿔야 한다.

PATTERNS. 각 항목은 진단 이름과 정규 표현식(regular expression)의 쌍이다. 첫 패턴은 password나 api_key 같은 이름 뒤에 대입 구분자와 값이 있는지 찾는다. 따옴표가 있는 간단한 JSON 형태도 검사한다. 두 번째는 사용자명과 비밀번호를 함께 넣은 URL을, 세 번째는 개인 키 표식을 찾는다. 특정 서비스의 키 길이나 접두사에 의존하지 않는다.

normalize. 줄바꿈을 LF로 맞추고 본문 앞뒤의 공백을 정리한 뒤 마지막 줄바꿈 하나를 붙인다. 내용 중간의 들여쓰기는 유지한다. 끝부분의 의도적인 빈 줄은 보존하지 않는 정책이므로 원본 작성자도 이 규칙을 알아야 한다. 정책을 코드로 고정하면 실행 환경에 따른 사소한 차이를 줄일 수 있다.

find_violations. 원본 줄 번호를 유지하기 위해 정규화 전 문자열을 검사한다. 진단에는 파일명, 줄 번호, 위반 종류만 넣는다. 일치한 문자열이나 원문 줄을 출력하지 않는다. 이 설계는 검사기가 발견한 값을 다시 로그에 복사하는 일을 피한다. 한 줄이 여러 패턴과 일치하면 진단도 여러 개가 될 수 있다.

render. 입력 네 개를 모두 읽고 위반 목록을 모은다. 위반이 하나라도 있으면 예외를 발생시키므로 출력 작성 단계에 도달하지 않는다. 문제가 없을 때만 공통 원본과 각 덧붙임을 합쳐 사전으로 반환한다. 생성 헤더에는 임시 폴더의 절대 경로나 실행 시각을 넣지 않아 환경에 따른 차이를 만들지 않는다.

generate와 snapshot. generate는 준비된 문자열을 UTF-8 바이트로 기록한다. 줄바꿈 변환을 기록 단계에 맡기지 않아 생성 바이트를 일정하게 만든다. snapshot은 파일의 바이트를 읽어 비교에 사용한다. 화면에서 같아 보이는지 대신 실제 파일 내용이 같은지 확인하는 것이다.

require. 검사 조건이 거짓이면 실행을 실패시킨다. 이 프로그램은 검증 자체가 주요 동작이므로 assert 대신 명시적인 예외를 사용한다. 검사 조건이 실행 옵션에 따라 빠지는 일을 피하고, 실패 원인을 짧은 메시지로 남긴다.

main의 첫 실패 실험. 가짜 비밀번호를 덧붙인 뒤 생성기를 실행한다. 예상한 줄에서 차단되는지, 출력 세 개가 모두 없는지 확인한다. 예외가 발생했다는 사실만 보지 않고 “검사에 실패했는데 파일 일부가 생기지는 않았는가”까지 확인하는 부분이다.

정상 생성과 두 번째 실패 실험. 입력을 고친 뒤 생성한 결과에 공통 규칙이 들어갔는지 검사한다. 다시 생성한 바이트가 같은지도 확인한다. 이어서 이미 출력이 존재하는 상태에서 금지 입력을 넣고, 거부 뒤에도 기존 출력이 유지되는지 검사한다. 처음 실행과 재실행의 실패 경로를 모두 확인하는 셈이다.

samples. 대입문, 간단한 JSON, URL 인증 정보, 개인 키 표식을 차단하고 일반 문장을 허용하는지 확인한다. 이 다섯 사례가 모든 비밀값 탐지를 보증하지는 않는다. 다중 행 값, 다른 언어의 변수명, 인코딩된 값 등은 별도의 사례와 정책이 필요하다. 검사 통과 뒤에도 원본과 생성 파일을 사람이 검토해야 한다.

실행 결과

macOS와 Linux에서 실행 명령은 다음과 같다. 별도 입력 파일이나 네트워크 연결은 필요하지 않다.

python3 main.py

예상 출력은 다음과 같다. 임시 폴더의 이름은 출력하지 않으므로 실행할 때마다 같은 결과가 나온다.

차단: codex.md:4: 값 대입
검사 실패 후 생성 파일: 0개
생성: CLAUDE.md
생성: AGENTS.md
생성: GEMINI.md
검증: 공통 규칙 포함·중복 없음
검증: 재생성 바이트 일치
검증: 차단 시 기존 파일 유지
검증: 금지 패턴 사례 5개 통과

직접 확인할 때는 먼저 문법을 검사하고 위 출력을 비교한다. 문법 검사 명령은 다음과 같다.

python3 -W error -m py_compile main.py

이 예제에서 생성 파일이 실행 후 남지 않는 것은 임시 폴더를 쓰기 때문이다. 실제 저장소에 적용할 때는 입력 경로와 출력 경로를 명시적으로 정하고, 생성된 세 파일의 변경 차이를 확인한다. main.py가 성공했다는 결과는 생성기의 검사 사례를 통과했다는 뜻이다. 지침에 적힌 프로젝트 테스트가 성공했다는 뜻은 아니다.

검토에서는 특히 테스트 명령의 변경, 금지 사항의 삭제, 덧붙임에 추가된 공통 규칙, 예상하지 못한 값 대입을 살핀다. AI가 생성기를 수정했다면 코드뿐 아니라 위 실패 실험이 여전히 유효한지도 확인한다. 검사를 통과시키려고 금지 패턴이나 실패 조건을 지우는 변경은 요구사항을 다시 따져 보아야 한다.

실무에서 자주 틀리는 것

검사 전에 파일부터 쓴다

다음 코드는 실행 가능한 축약 예제다. 위반을 찾았어도 이미 파일은 기록된 상태다. 실제 저장소에서는 이런 순서가 기존의 정상 지침을 덮을 수 있다.

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as folder:
    target = Path(folder) / "AGENTS.md"
    text = "password = demo-only\n"
    target.write_text(text, encoding="utf-8")
    if "password =" in text:
        print("거부했지만 파일이 이미 존재한다.")

고친 코드는 검사를 먼저 끝낸다. 다음의 단순 문자열 검사는 순서를 설명하기 위한 것이며, 완성 코드에서는 find_violations를 사용한다.

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as folder:
    target = Path(folder) / "AGENTS.md"
    text = "password = demo-only\n"
    if "password =" in text:
        print("기록 전에 거부했다.")
    else:
        target.write_text(text, encoding="utf-8")

검사 실패 로그에 원문을 출력한다

다음 코드는 진단 과정에서 값까지 다시 출력한다. 예제의 값은 가짜지만 실제 입력이었다면 로그에도 남게 된다.

line = "password = demo-only"
print(f"금지된 줄: {line}")

고친 코드는 수정 위치와 위반 종류만 출력한다. 원문은 접근이 통제된 원본에서 확인한다.

name = "codex.md"
number = 4
label = "값 대입"
print(f"{name}:{number}: {label}")

생성 파일만 직접 고친다

생성 결과를 직접 고치면 다음 재생성에서 수정이 사라진다. 다음 축약 예제는 그 문제를 보여 준다.

common = "테스트를 실행한다.\n"
generated = common
generated += "변경 차이를 검토한다.\n"
generated = common
print("변경 차이를 검토한다." in generated)

고친 코드는 원본을 수정한 뒤 다시 생성한다. 도구 하나에만 필요한 문장은 해당 덧붙임으로 옮긴다.

common = "테스트를 실행한다.\n"
common += "변경 차이를 검토한다.\n"
generated = common
print("변경 차이를 검토한다." in generated)

검사 통과를 안전성 판정으로 확대한다

특정 단어가 없다는 사실만으로 비밀값이 없다고 말할 수는 없다. 다음 코드는 이름이 다른 토큰을 검사하지 못한다.

text = "credential = demo-only"
passed = "password" not in text
print(f"비밀값 없음: {passed}")

고친 코드는 검사 범위에 맞게 결과를 표현한다. 탐지 범위는 실제 팀의 누락 사례를 바탕으로 확장하고, 원본과 변경 차이의 검토는 유지한다.

text = "credential = demo-only"
passed = "password" not in text
print(f"password 문자열 검사 통과: {passed}")
print("다른 형식의 값은 원본과 변경 차이에서 검토한다.")

한눈에 보기

지침 파일 생성과 검토에서 지킬 기준
대상작성 원칙확인 방법
공통 원본검사 명령, 폴더 역할, 금지 사항, 코드 관례를 모은다.명령을 실제 저장소에서 실행한다.
도구별 덧붙임공통 규칙과 겹치지 않는 요구만 남긴다.공통 기준을 다시 정의하거나 약화하는지 읽는다.
생성 파일직접 수정하지 않고 원본에서 재생성한다.바이트 비교와 변경 차이 검토를 한다.
폴더별 지침해당 위치에서만 필요한 책임을 추가한다.2026년 10월 기준 예시의 적용 범위는 도구별 공식 문서에서 확인한다.
금지 패턴 검사전체 입력을 검사한 뒤 기록한다.차단 사례와 허용 사례, 기존 파일 유지 여부를 확인한다.
검증의 한계지침을 실행 통제나 비밀값 탐지 보증으로 취급하지 않는다.권한 설정, 실행 결과, 테스트, 변경 차이를 별도로 확인한다.

연습 문제

  1. 공통 원본에 “오류 메시지에는 입력 데이터 전체를 넣지 않는다”를 추가하라. 세 출력에 모두 반영되면서 공통 규칙이 중복되지 않는지 확인하라.
  2. 생성 파일에 실행 시각과 임시 폴더 경로를 넣으면 재현성 검사가 어떻게 달라지는지 설명하라. 생성 이력을 남길 위치도 제안하라.
  3. 금지 입력을 가진 새 덧붙임이 추가되었을 때, 다른 두 출력도 기록되지 않아야 하는 이유를 설명하라. 기존 출력이 있는 경우와 없는 경우를 나누어 확인하라.
  4. tests 전용 규칙을 추가하려 한다. 루트 원본에 남길 내용과 폴더별 원본으로 옮길 내용을 나누고, 도구의 자동 적용 여부를 확인할 절차를 적어라.

정답과 해설

  1. COMMON의 금지 사항에 문장을 추가하고 다시 실행한다. 세 출력 각각에 그 문장이 한 번 있는지 확인하는 조건을 추가할 수 있다. 기존의 공통 제목 중복 검사는 그대로 유지한다. 실제 저장소에서는 생성 파일 세 개의 변경 차이에 같은 문장이 들어갔는지 검토한다.
  2. 실행 시각이나 임시 경로가 달라지면 원본이 같아도 생성 바이트가 달라질 수 있다. 한 실행 안에서 두 번 만드는 검사만으로는 서로 다른 실행 사이의 차이를 충분히 확인하지 못한다. 생성 파일에는 원본 경로와 같은 안정적인 정보만 두고, 실행 이력은 별도의 작업 기록에 남긴다. 비밀값이나 원문 전체를 이력에 넣지 않는다.
  3. 세 파일은 같은 규칙 묶음의 산출물이므로 하나의 입력 오류 때문에 일부만 갱신되면 도구별 작업 기준이 갈라진다. 출력이 없던 경우에는 세 경로가 모두 없는지 확인한다. 기존 출력이 있던 경우에는 생성 전 바이트를 저장하고 차단 뒤와 비교한다. 완성 코드의 두 실패 실험이 각각 이 조건을 확인한다.
  4. 테스트 실행 명령과 실패를 숨기지 말라는 기준은 루트에 둔다. 테스트 자료의 배치 방식이나 외부 네트워크를 쓰지 않는 기준은 tests의 추가 규칙으로 둘 수 있다. 2026년 10월 기준 예시로, 실제 파일명과 탐색 방식은 도구별 공식 문서에서 확인한다. 이어서 선택한 작업 경로에서 적용된 지침을 확인하고, 그 규칙이 반영되는 작은 작업의 변경 차이를 검토한다. 도구의 답변만으로 적용을 확정하지 않는다.
오탈자·오류 제보 비공개로 접수되어 원고 수정에 반영됩니다

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

READER FEEDBACK

질문·의견

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

댓글 0

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

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