개발·프레임워크

BUILD / 4번째 글

Aider: AI 페어 프로그래머와 Git 통합 개발

오픈소스 AI 코딩 CLI Aider의 레포 맵 생성 방식, 편집 형식이 실패하는 자리, Architect 모드의 비용 계산, 자동 커밋 히스토리 정리까지 실전 기준으로 정리합니다.

PALDYN Team29 MIN READ

지난 글에서 OpenAI의 Codex와 ChatGPT 계열 AI 코딩 도구를 살펴봤다. 이번엔 오픈소스 진영의 Aider를 다룬다. 터미널에서 도는 AI 페어 프로그래밍 도구이고, 모든 수정을 git 커밋으로 남긴다는 점과 어떤 모델이든 백엔드로 꽂을 수 있다는 점이 갈림길이다.

Aider를 오래 쓰다 보면 결과가 좋은 날과 나쁜 날이 갈리는데, 그 차이는 대개 모델이 아니라 무엇을 컨텍스트에 넣었는가와 모델이 수정을 어떤 형식으로 돌려주는가에서 온다. 이 글은 그 두 자리를 중심으로 본다.

설치와 첫 세션

설치와 모델 지정

# pipx로 설치 (권장)
pipx install aider-chat

# API 키 설정
export ANTHROPIC_API_KEY="sk-ant-..."
export OPENAI_API_KEY="sk-..."

# 모델을 지정해 시작
aider --model claude-sonnet-4-6

# 특정 파일로 시작
aider app/main.py app/services/auth.py

Aider는 API 키를 직접 쓰므로 월정액이 아니라 쓴 만큼 요금이 나간다. 처음 며칠은 한 세션이 끝날 때마다 사용량을 확인해 보는 편이 좋다. 파일을 잔뜩 붙인 채로 긴 대화를 이어 가면 같은 코드가 매 요청마다 다시 올라가고, 대화가 길어질수록 한 번의 요청이 비싸진다.

시작할 때 파일을 지정하는 것과 지정하지 않는 것은 뜻이 다르다. 지정하면 그 파일의 내용 전체가 대화에 들어가 모델이 고칠 수 있는 상태가 되고, 지정하지 않으면 뒤에 설명할 레포 맵만 올라간다. 고칠 자리를 이미 알고 있으면 지정해서 시작하는 편이 빠르다.

Aider 워크플로우

세션 커맨드

# 컨텍스트 관리
/add app/services/payment.py   # 파일을 대화에 추가
/drop app/legacy.py            # 파일 제거
/ls                            # 현재 컨텍스트 파일 목록

# 코드 vs 질문 분리
/ask JWT 토큰은 어디서 검증해?    # 수정 없이 질문만
/code 이 함수 리팩토링해줘        # 코드 수정 모드

# 실행과 검증
/run pytest tests/test_auth.py   # 명령 실행
/diff                            # 마지막 변경 diff 확인
/undo                            # 마지막 커밋 되돌리기

/ask와 /code를 가르는 습관이 생각보다 크게 작용한다. 구조를 파악하려고 던진 질문에 모델이 곧바로 파일을 고쳐 버리면, 아직 이해하지 못한 변경이 커밋으로 남는다. 「어디서 검증하지?」 같은 물음은 전부 /ask로 보내고, 무엇을 고칠지 정한 다음에 /code로 넘어가는 순서가 안전하다.

Aider 명령어 참조

설정 파일

매번 같은 옵션을 치는 대신 저장소 루트에 설정 파일을 둔다. 여기에 적은 값은 팀 전체가 같은 조건으로 도구를 쓰게 만드는 규칙이 되므로, 개인 취향에 해당하는 값과 팀 규칙에 해당하는 값을 갈라 두는 것이 좋다. 모델 이름은 사람마다 다를 수 있으니 환경 변수로 빼고, 린트 명령과 테스트 명령처럼 저장소에 딸린 것만 파일에 적는 식이다.

# .aider.conf.yml
model: claude-sonnet-4-6
editor-model: claude-haiku-4-5-20251001

# 컨텍스트에 항상 포함할 파일
read:
  - README.md
  - ARCHITECTURE.md

# 수정 뒤 자동으로 돌릴 명령
lint-cmd: ruff check {files} --fix
test-cmd: pytest tests/ -v

map-tokens: 2048

린트 명령을 적어 두면 수정이 적용된 직후에 자동으로 돌고, 실패하면 그 출력이 모델에 돌아가 스스로 고친다. 사람이 포맷 문제를 지적하는 왕복이 통째로 사라지므로 설정 파일에서 가장 값하는 한 줄이다.

레포 맵

레포 맵은 저장소 전체를 요약해 모델에 보여 주는 목차다. 파일 내용을 다 넣을 수는 없으니 각 파일에 어떤 클래스와 함수가 있는지만 뽑아 올린다. 모델이 「그 함수는 어디 있지」를 묻지 않고 바로 관련 파일을 지목할 수 있는 이유가 이것이다.

레포 맵이 만들어지는 순서

심볼 추출

맵을 만드는 첫 단계는 파싱이다. Aider는 tree-sitter라는 파서를 써서 소스 코드를 구문 트리로 바꾼 뒤, 그 트리에서 정의와 참조를 뽑는다. 정규식으로 함수 이름을 긁는 방식과 달리 주석이나 문자열 안의 글자를 함수로 착각하지 않고, 언어마다 문법 정의만 갈아 끼우면 되므로 파이썬·자바스크립트·고 같은 여러 언어를 같은 방식으로 다룰 수 있다.

여기서 뽑는 것은 이름과 시그니처 수준이다. 함수 본문은 들어가지 않는다. 그래서 맵은 저장소가 수천 개 파일이어도 수천 토큰 안에서 표현된다. 반대로 말하면 모델은 맵만 보고는 함수가 무엇을 하는지 모른다. 이름이 정직하지 않은 코드베이스에서 Aider가 엉뚱한 파일을 고르는 일이 잦은 이유이고, 이름을 고쳐 두는 일이 도구의 성능을 올리는 일이 되는 이유이기도 하다.

토큰 예산과 순위

문제는 저장소가 커지면 이름만 모아도 예산을 넘는다는 것이다. 그래서 Aider는 심볼들 사이의 참조 관계를 그래프로 보고, 여러 곳에서 불리는 심볼과 지금 대화에 올라온 파일에 가까운 심볼에 높은 점수를 준다. 점수 순으로 담다가 예산에 닿으면 자른다. 그래서 맵은 고정된 목록이 아니라 지금 대화에 맞춰 매번 다시 만들어지는 요약이다.

# 레포 맵 크기 조절
aider --map-tokens 2048        # 기본값 근처
aider --map-tokens 4096        # 큰 저장소에서 더 넓게

예산을 키우면 모델이 보는 범위가 넓어지지만 매 요청의 입력 토큰이 그만큼 늘어난다. 「관련 없는 파일을 자꾸 고치려 든다」면 예산을 줄이고, 「분명히 있는 함수를 없다고 한다」면 예산을 늘린다. 이 두 증상이 예산을 조절하는 유일한 신호다. 순위를 매긴다는 말에는 한 가지 함의가 더 있다. 방금 /add한 파일이 바뀌면 그 주변 심볼의 점수가 올라 맵의 내용도 함께 바뀐다는 것이다. 같은 질문을 두 번 던졌는데 답이 달라졌다면 모델이 변덕을 부린 것이 아니라 맵이 달라졌을 수 있다.

/add의 경계

맵이 있어도 실제 수정은 대화에 올라온 파일에서만 일어난다. 그래서 /add를 어디까지 하느냐가 결과를 정한다. 흔한 실수는 관련 있어 보이는 파일을 열 개쯤 한꺼번에 붙이는 것이다. 이렇게 하면 세 가지가 동시에 나빠진다. 요청마다 그 열 개가 전부 올라가 요금이 뛰고, 모델이 고쳐도 되는 파일이 많아져 엉뚱한 파일을 건드리고, 정작 중요한 파일의 비중이 묻힌다.

기준은 단순하다. 이번 수정에서 실제로 줄이 바뀔 파일만 넣는다. 읽기만 하면 되는 파일 — 설정, 타입 정의, 참고할 인터페이스 — 은 읽기 전용으로 붙이는 옵션을 쓴다. 작업이 끝나면 /drop으로 내린다. 한 세션에서 주제가 바뀔 때 파일 목록을 비우지 않는 것이 대화가 점점 느려지고 비싸지는 가장 흔한 원인이다.

파일이 정말 여럿 필요한 작업이라면 작업 자체를 쪼개는 편이 낫다. 인터페이스를 먼저 바꿔 커밋하고, 그것을 쓰는 쪽을 다음 세션에서 고치는 식이다. 한 번에 다섯 파일을 고치라고 시켜 성공하는 비율보다, 두 단계로 나눠 각각 성공하는 비율이 훨씬 높다.

편집 형식

모델은 고친 코드를 글로 돌려줄 뿐이고, 그 글을 파일에 반영하는 것은 Aider다. 그 사이의 약속을 편집 형식이라고 부른다. 형식이 어긋나면 모델이 옳은 수정을 생각해 냈어도 파일에는 아무 일도 일어나지 않는다.

diff

기본 형식은 검색·치환 블록이다. 모델이 「원래 이랬던 부분」과 「이렇게 바꾼다」를 짝으로 내놓으면 Aider가 앞쪽 문자열을 파일에서 찾아 뒤쪽으로 바꾼다. 장점은 바뀌는 부분만 출력하므로 출력 토큰이 적다는 것이고, 단점은 앞쪽 문자열이 파일과 한 글자라도 다르면 적용이 실패한다는 것이다. 공백 하나, 탭과 스페이스의 차이, 모델이 무심코 정리한 따옴표가 실패의 원인이 된다.

실패가 잦다고 해서 이 형식이 나쁜 것은 아니다. 긴 파일에서 세 줄을 고치는 데 파일 전체를 다시 출력하게 하면 출력 요금이 수십 배로 뛰고, 그 긴 출력 어딘가에서 다른 곳이 함께 바뀔 위험도 커진다. 실패하면 다시 시도하면 그만이지만 조용히 망가진 파일은 나중에 발견된다. 결국 형식 선택은 「실패를 자주 보되 눈에 보이게 할 것인가, 실패를 드물게 보되 조용하게 둘 것인가」의 갈림이고, 대부분의 상황에서 앞쪽이 낫다.

whole와 udiff

whole은 파일 전체를 다시 출력하게 하는 형식이다. 찾을 문자열이 없으니 적용은 거의 항상 성공하지만, 파일이 길면 출력 토큰이 그만큼 나가고 모델이 중간의 함수 하나를 조용히 빠뜨리는 사고가 난다. 짧은 파일이나 새로 만드는 파일에 맞다.

udiff는 유닉스 diff 형식에 가까운 방식으로, 줄 번호와 맥락 줄을 함께 쓴다. 일부 모델은 이 형식에서 눈에 띄게 정확해진다. 모델마다 학습한 출력 습관이 달라 형식별 적용 성공률이 갈리기 때문이다. 그래서 형식은 모델을 바꿀 때 함께 다시 정하는 값이다. 새 모델로 갈아탄 뒤 적용 실패가 늘었다면 모델이 나쁜 것이 아니라 형식이 안 맞는 것일 수 있다.

형식 출력 토큰 적용 실패 맞는 자리
diff 적음 문자열 불일치로 자주 긴 파일의 부분 수정
whole 많음 거의 없음 짧은 파일, 새 파일
udiff 중간 모델에 따라 갈림 형식을 잘 지키는 모델

적용 실패를 읽는 법

편집이 실패하면 Aider가 그 사실을 알려 주고 다시 시도하는데, 같은 실패가 두세 번 반복되면 형식을 의심한다. 대화를 끝내고 --edit-format whole로 다시 시작해 같은 요청을 던져 보면 원인이 모델의 이해가 아니라 형식이었는지 바로 드러난다.

실패가 특정 파일에서만 난다면 그 파일 자체를 본다. 탭과 스페이스가 섞여 있거나, 아주 긴 한 줄이 있거나, 같은 코드 조각이 파일 안에 여러 번 나오는 경우다. 마지막 것이 특히 까다롭다 — 찾을 문자열이 두 군데서 걸리면 어느 쪽을 고쳐야 하는지 알 수 없으므로 적용이 막힌다. 이럴 때는 요청에 함수 이름을 못 박아 주어 맥락을 넓히게 한다.

Git 자동 커밋

커밋 단위

Aider는 수정이 적용될 때마다 커밋을 만든다. 이 동작의 진짜 값은 「커밋을 대신 해 준다」가 아니라 모든 변경이 되돌릴 수 있는 단위로 쪼개진다는 데 있다. 사람이 손으로 작업할 때는 두 시간 동안 열 파일을 고친 뒤 한 번에 커밋하는 일이 흔하고, 그러면 중간의 어느 판단이 틀렸는지 되짚을 수 없다.

커밋이 자동으로 생기므로 세션을 시작하기 전에 작업 트리가 깨끗한지 확인하는 습관이 필요하다. 커밋하지 않은 내 변경이 남아 있으면 그것이 모델의 첫 커밋에 함께 딸려 들어간다. 브랜치를 먼저 파는 것도 같은 이유다 — 되돌릴 때 브랜치째 버리는 선택지가 남는다.

$ aider app/auth.py

> JWT 토큰 만료 시간을 15분에서 1시간으로 변경해줘

Applied edit to app/auth.py
Commit a3f1b2c fix: JWT 토큰 만료 시간을 1시간으로 변경

/undo

/undo는 마지막 커밋을 되돌린다. 방금 한 수정이 마음에 들지 않을 때 파일을 직접 편집해 고치는 대신 되돌리고 요청을 다시 쓰는 편이 대체로 낫다. 사람이 손으로 고친 내용은 모델의 대화 기록에 남지 않으므로, 다음 요청에서 모델이 자기가 쓴 옛 코드를 기준으로 다시 고치면서 방금 한 수정을 덮어 버린다.

되돌릴 수 없는 경우가 하나 있다. 커밋 이후에 사람이 그 파일을 또 고쳤다면 Aider는 안전을 위해 되돌리기를 거부한다. 그래서 세션 중에는 에디터에서 같은 파일을 만지지 않는 것이 규칙처럼 굳는다.

되돌리는 것과 다시 시키는 것을 헷갈리지 않는 것도 중요하다. /undo는 파일을 되돌릴 뿐 대화를 되돌리지는 않는다. 모델은 자기가 방금 쓴 코드를 여전히 기억하고 있으므로, 되돌린 뒤에 「다시 해 줘」라고만 하면 같은 코드가 다시 온다. 무엇이 마음에 들지 않았는지를 한 문장 덧붙여야 다른 답이 나온다.

히스토리 정리

자동 커밋이 남기는 히스토리는 작업 중에는 유용하지만 그대로 올릴 것은 아니다. 한 기능을 붙이는 데 열두 개의 커밋이 생기고 그중 셋은 되돌린 것이며 둘은 오타 수정이다. 리뷰하는 사람이 읽을 이야기가 아니다.

정리하는 시점은 브랜치를 올리기 직전이다. 그때까지는 잘게 남겨 두는 편이 안전하고, 올릴 때 의미 단위로 묶는다. 대략 세 덩이로 갈리는 경우가 많다. 구조를 바꾼 커밋, 기능을 더한 커밋, 테스트를 더한 커밋이다. 커밋 메시지는 사람이 다시 쓴다. 모델이 붙인 메시지는 무엇을 바꿨는지는 정확하지만 왜 바꿨는지를 모르기 때문에, 리뷰에서 가장 궁금한 부분이 비어 있다.

정리하기 전에 git log --oneline으로 한 번 훑는 일도 값한다. 되돌린 커밋과 그것을 되돌린 커밋이 나란히 있으면 그 사이에 무엇을 시도했다가 접었는지가 보이고, 그 판단이 기억에서 사라지기 전에 코드 주석이나 설계 문서로 옮길 수 있다.

Architect 모드

두 모델의 분업

Architect 모드는 한 요청을 둘로 나눈다. 앞쪽 모델이 무엇을 어떻게 고칠지 말로 설계하고, 뒤쪽 모델이 그 설계를 편집 형식에 맞는 출력으로 옮긴다.

aider --architect \
  --model claude-opus-4-7 \
  --editor-model claude-sonnet-4-6

이 분업이 이득인 이유는 두 일의 성격이 다르기 때문이다. 설계는 저장소의 구조를 이해하고 판단하는 일이라 모델의 능력이 그대로 결과에 나타나지만, 편집 형식을 지키는 일은 성실함의 문제에 가깝다. 강한 모델에 형식 맞추기까지 시키면 출력 토큰이 비싼 쪽에서 나간다. 설계 단계의 출력이 사람이 읽을 수 있는 말이라는 점도 덤이다. 편집이 적용되기 전에 무엇을 하려는지 읽고 멈출 수 있으므로, 큰 변경에서는 이 모드가 승인 단계 역할을 겸한다.

비용 계산

이득의 크기는 두 수가 정한다. 설계 출력이 짧고 편집 출력이 길수록 이득이 크다. 함수 하나를 고치는 작업은 설계가 세 문장이고 편집이 스무 줄이므로 차이가 작지만, 파일 넷에 걸친 리팩터링은 설계가 열 줄이고 편집이 수백 줄이라 차이가 커진다.

반대로 손해가 나는 자리도 분명하다. 요청을 두 번 부르므로 입력 토큰은 두 배로 든다. 레포 맵과 파일이 크면 입력이 지배적이라 분업의 이득이 그대로 상쇄된다. 짧은 수정을 반복하는 세션에서는 그냥 한 모델로 도는 편이 싸다.

한 번 재 보면 판단이 쉬워진다. 같은 작업을 한 모델로 한 번, Architect 모드로 한 번 돌리고 두 세션의 요금과 걸린 시간을 적어 둔다. 저장소의 크기와 작업의 성격에 따라 답이 달라지므로 남의 수치보다 자기 저장소에서 잰 두 줄이 정확하다. 대체로 파일이 많고 작업 단위가 큰 저장소일수록 분업이 값한다.

로컬 모델

쓸 만한 크기

Aider는 로컬에서 도는 모델도 백엔드로 받는다. 코드가 밖으로 나가면 안 되는 환경에서는 이 선택지가 유일한 답이 되기도 한다.

# Ollama로 로컬 모델 연결
export OLLAMA_API_BASE=http://127.0.0.1:11434
aider --model ollama/qwen2.5-coder:32b

다만 크기에 따라 되는 일이 뚜렷하게 갈린다. 아주 작은 모델은 편집 형식을 지키지 못해 적용이 계속 실패한다. 코드 이해 이전에 「찾을 문자열을 원문 그대로 옮겨 적기」가 안 되기 때문이다. 30B 안팎의 코드 특화 모델쯤 되면 한 파일 안의 수정은 쓸 만해지고, 그 아래는 자동완성 대용으로만 생각하는 편이 실망이 적다.

못 하는 작업

크기와 무관하게 로컬 모델이 약한 자리가 있다. 파일 여럿에 걸친 변경, 긴 맥락을 유지해야 하는 대화, 그리고 저장소의 관행을 따라야 하는 작업이다. 이런 요청은 설계가 틀린 채로 편집만 성공해 그럴듯한 오답이 커밋된다.

현실적인 절충은 갈라 쓰는 것이다. 이름 바꾸기, 테스트 껍데기 만들기, 주석 정리처럼 판단이 거의 필요 없는 작업은 로컬 모델에 맡기고, 설계가 필요한 작업만 외부 API로 보낸다. 무엇을 밖으로 보낼 수 있는지가 팀마다 다르므로, 이 경계는 도구 설정이 아니라 팀 규칙으로 먼저 정해야 한다.

속도도 함께 본다. 로컬 모델은 요금이 들지 않는 대신 사람의 시간을 쓴다. 한 요청에 1분이 걸리고 세 번 실패하면 외부 API로 한 번에 끝낼 일에 5분을 쓴 셈이다. 요금이 0이라는 사실이 비용이 0이라는 뜻은 아니다.

버그 수정 한 사이클

재현

첫 단계는 고치는 일이 아니라 실패를 눈으로 확인하는 일이다. 실패하는 테스트가 없으면 먼저 만든다. /run으로 테스트를 돌리면 그 출력이 그대로 대화에 들어가므로, 스택 트레이스를 복사해 붙일 필요가 없다. 이 단계를 건너뛰면 모델이 「고쳤습니다」라고 말할 때 그것을 확인할 방법이 없다. 통과하는 테스트가 실패로 바뀌는 순간을 먼저 만들어 두면, 그 뒤의 모든 수정은 성공과 실패가 자동으로 판정된다.

/add app/services/order.py tests/test_order.py
/run pytest tests/test_order.py -v

수정과 검증

실패 출력이 컨텍스트에 있는 상태에서 무엇이 잘못됐는지를 말로 적는다. 「고쳐 줘」보다 「order_id가 None일 때 500이 난다. 검증을 넣고 400으로 돌려줘」처럼 원하는 결과를 적는 편이 왕복이 적다. 수정이 적용되면 다시 /run으로 돌린다. 이 두 줄을 번갈아 치는 것이 한 사이클의 전부다.

통과한 뒤에 /diff로 마지막 변경을 확인하는 단계를 빼지 않는다. 테스트가 통과했다고 해서 의도한 것만 바뀌었다는 뜻은 아니다. 함께 정리된 임포트, 조용히 바뀐 기본값, 지워진 주석이 여기서 드러난다.

사이클이 세 번을 넘어가는데도 테스트가 통과하지 않으면 요청을 다시 쓰는 대신 한 번 멈춘다. 같은 자리를 계속 고치고 있다는 것은 대개 원인이 컨텍스트에 없다는 뜻이다. 호출하는 쪽 파일을 붙이지 않았거나, 실패의 진짜 원인이 다른 모듈에 있는 경우다.

도구 비교

항목 Aider Cursor Claude Code
인터페이스 CLI 터미널 GUI IDE CLI 터미널
Git 통합 커밋 자동 생성 수동 수동
모델 선택 제한 없음 제한적 Claude 위주
로컬 모델 지원 지원 미지원
비용 구조 API 사용량 월정액 API 사용량

Aider가 맞는 팀은 둘로 좁혀진다. git 히스토리를 작업의 기록으로 쓰는 팀, 그리고 특정 모델 제공자에 묶이고 싶지 않은 팀이다. 반대로 화면에서 코드를 보며 고치는 흐름이 익숙하다면 터미널로 옮기는 값이 크지 않을 수 있다. 세 도구를 함께 쓰는 것도 이상한 선택이 아니다. 탐색과 이해는 에디터에서, 여러 파일을 한꺼번에 고치는 일은 에이전트에서, 그리고 기록을 남겨야 하는 수정은 Aider에서 하는 식으로 나눠 쓰는 팀이 적지 않다. 도구를 하나로 통일하는 것보다 어떤 작업을 어디서 하는지 팀이 같은 답을 갖는 쪽이 중요하다.


읽어주셔서 감사합니다. 😊

LATEST

개발·프레임워크의 최신 글

개발·프레임워크2026.05.28

Google Gemini SDK 활용 가이드

google-genai 패키지의 Client 하나로 Gemini API를 부르는 법 — 응답 객체와 finish_reason, 생성 설정과 구조화 출력, 인라인 데이터와 Files API, 도구 호출 왕복, 안전 설정과 재시도, 대화 비용과 컨텍스트 캐싱까지 정리한다.

26 MIN
개발·프레임워크2026.05.28

OpenAI SDK 완전 정복

Python openai 패키지로 OpenAI API를 다루는 법 — 클라이언트 설정과 재시도, 메시지와 응답 구조, Responses API 대응, 모델 고르는 축, 도구 호출 루프, 구조화 출력, 임베딩·이미지 입력, 토큰 비용과 한도까지 정리

30 MIN