LLM 코딩 에이전트 작업 관리 방법

LLM 코딩 에이전트 작업 관리 방법

- Task/Plan Harness 적용기 -

1. 들어가며

최근 개발 업무에서 LLM 기반 코딩 도구를 사용하는 비중이 점점 커지고 있습니다. 저도 프로젝트 작업 생산성을 높이기 위해 Codex를 사용하기 시작했습니다. 처음에는 일반적인 방식으로 사용했습니다. 필요한 작업을 프롬프트로 설명하고, LLM이 작성한 결과를 검토한 뒤, 부족한 부분을 다시 요청하는 방식이었습니다. 간단한 수정이나 반복 작업에서는 이 방식만으로도 충분히 효과가 있었습니다.

하지만 작업 규모가 커질수록 단순 프롬프트 방식에는 한계가 있었습니다. 프롬프트를 길게 작성해도 LLM이 작업 전체를 안정적으로 관리하지 못했고, 작업 시간이 길어지면 앞에서 합의한 내용이나 이전 변경 의도를 놓치는 경우가 생겼습니다. 처음에는 제가 프롬프트를 더 자세히 쓰면 해결될 문제라고 생각했습니다. 그러나 실제로는 프롬프트의 문제가 아니라 작업을 관리하는 구조가 부족한 문제에 가까웠습니다.

이 글에서는 제가 LLM 기반 개발 작업에서 겪은 문제와, 이를 해결하기 위해 Task/Plan 하네스를 적용한 과정을 정리합니다. 단순히 하네스의 개념을 설명하기보다, 왜 필요하다고 느꼈는지, 어떤 규칙을 만들었는지, 실제 작업 흐름이 어떻게 달라졌는지에 초점을 맞추었습니다.

2. 프롬프트만으로 작업할 때 겪은 문제

처음 Codex를 사용할 때는 하나의 세션에서 가능한 많은 일을 처리하려고 했습니다. 예를 들어 기능 하나를 구현해야 하면 요구사항을 길게 설명하고, 관련 파일을 찾게 하고, 구현과 테스트까지 이어서 요청했습니다. 짧은 작업에서는 괜찮았지만, 작업이 길어지면 세션 안에 쌓이는 문맥이 너무 많아졌습니다.

가장 먼저 체감한 문제는 컨텍스트 윈도우였습니다. 작업량이 많고 시간이 길어질수록 LLM이 이전 내용을 잊어버리거나, 앞에서 정한 방향과 다른 방식으로 코드를 작성하는 경우가 생겼습니다. 단순히 기억을 못 하는 정도가 아니라 코드 품질도 함께 흔들렸습니다. 초반에는 비교적 정확히 파악하던 구조를 뒤로 갈수록 대충 추정하거나, 이미 결정한 내용을 다시 바꾸는 등의 문제가 있었습니다.

이 상황에서 컨텍스트 윈도우를 비우기 위해 세션을 초기화하면 또 다른 비용이 발생했습니다. 새 세션에서는 지금까지 무엇을 했는지, 어떤 파일을 수정했는지, 어떤 결정을 했는지 다시 설명해야 했습니다. 설명을 줄이면 LLM이 잘못 이해했고, 설명을 길게 하면 다시 컨텍스트가 빨리 찼습니다. 결국 작업을 빠르게 하기 위해 도구를 사용했지만, 어느 순간부터는 작업 흐름을 복원하는 데 시간이 들어갔습니다.

두 번째 문제는 LLM이 모호한 내용을 혼자 결정한다는 점이었습니다. 개발 작업에는 사용자의 결정이 필요한 지점이 많습니다. API 계약을 어떻게 둘 것인지, 기존 구조를 유지할 것인지, 새 추상화를 만들 것인지, 검증은 어디까지 할 것인지 같은 문제입니다. 그런데 프롬프트에 명확히 적지 않은 부분이 있으면 LLM은 질문하지 않고 나름의 논리대로 판단한 뒤 구현을 진행하는 경우가 있었습니다.

문제는 그 판단이 항상 제 의도와 맞지는 않았다는 점입니다. 겉으로 보기에는 그럴듯한 코드가 만들어졌지만, 실제 프로젝트의 방향이나 제가 생각한 범위와 어긋나는 경우가 있었습니다. 이때는 이미 변경된 코드를 되돌리고, 다시 설명하고, 다시 작업해야 했습니다. 작업 자체보다 잘못된 작업을 되돌리는 비용이 더 크게 느껴질 때도 있었습니다.

3. 하네스를 적용하게 된 이유

이 문제를 해결하기 위해 하네스라는 개념을 적용해 보기 시작했습니다. 여기서 하네스는 LLM에게 매번 자유롭게 작업을 맡기는 것이 아니라, 작업을 수행하기 전과 후에 따라야 할 고정된 절차와 문서를 두는 방식입니다. 즉, 프롬프트가 한 번의 요청이라면 하네스는 작업 전체를 관리하는 틀에 가깝습니다.

제가 적용한 방식은 Task/Plan 하네스입니다. 이름 그대로 TASK.md와 PLAN.md라는 두 문서를 중심으로 작업을 관리합니다. TASK.md는 현재 작업의 단위와 상태를 기록하고, PLAN.md는 세부 구현 계획과 결정 사항, 검증 기준을 기록합니다. LLM은 사용자의 요청을 받으면 바로 구현하지 않고, 먼저 작업을 작은 단위로 나누고 계획을 작성합니다.

이 방식의 핵심은 큰 문제를 한 번에 해결하지 않는 것입니다. 작업을 T-01, T-02와 같은 작은 단위로 나누고, 하나의 작업이 끝나면 상태를 기록한 뒤 compilejava세션을 초기화할 수 있도록 했습니다. 세션을 초기화하더라도 TASK.md와 PLAN.md에 이전 작업 내용과 전체 계획이 남아 있기 때문에, 다음 세션에서는 이 문서만 읽고도 작업을 이어갈 수 있습니다.

4. Task/Plan 하네스의 기본 구조

하네스의 파일 구조는 기능별 작업 디렉터리를 기준으로 잡았습니다. 루트에 임시 TASK.md와 PLAN.md를 두면 여러 작업이 섞이기 쉽기 때문에, 기능별로 활성 작업과 완료 작업을 구분하는 구조를 사용했습니다.

.codex/tasks/active/<feature-slug>/TASK.md
.codex/tasks/active/<feature-slug>/PLAN.md

.codex/tasks/completed/<feature-slug>/TASK.md
.codex/tasks/completed/<feature-slug>/PLAN.md

TASK.md는 작업 현황판에 가깝습니다. 현재 어떤 작업을 하고 있는지, 어떤 작업이 완료되었는지, 다음에 무엇을 해야 하는지를 짧게 확인할 수 있어야 합니다. 반대로 PLAN.md는 상세 계획서에 가깝습니다. 작업 범위, 제외 범위, 결정 사항, 구현 방법, 검증 방법, 상태 로그를 포함합니다.

# <Feature Name> Tasks

## Status
- Last updated: <YYYY-MM-DD>
- Current task: T-01

## Tasks
- [x] T-00. Create task baseline documents
  - Plan reference: PLAN.md 0, 1, 3
  - Status: Completed
  - Clear after completion: Not required

- [ ] T-01. Implement first scoped change
  - Plan reference: PLAN.md 2
  - Status: Pending
  - Clear after completion: Required

처음에는 작업을 문서로 나누는 과정이 오히려 번거롭게 느껴질 수 있습니다. 그러나 실제로 써보면 이 문서들이 세션 사이의 연결고리 역할을 합니다. LLM이 현재 세션에서 기억하지 못하는 내용도 문서에 기록되어 있으면 다시 읽고 이어갈 수 있습니다. 사람이 봐도 현재 위치를 빠르게 파악할 수 있다는 장점이 있습니다.

5. 컨텍스트 윈도우 문제 해결

제가 가장 먼저 해결하고 싶었던 문제는 컨텍스트 윈도우였습니다. 이전 방식에서는 하나의 세션에서 분석, 구현, 수정, 검증을 모두 이어가려고 했습니다. 이 방식은 초반에는 빠르지만, 뒤로 갈수록 문맥이 비대해지고 LLM의 응답 품질이 떨어지는 문제가 있었습니다.

Task/Plan 하네스에서는 이 문제를 작업 단위 분리로 해결했습니다. LLM은 요청을 받으면 먼저 전체 작업을 작은 Task로 나눕니다. 각 Task는 한 번의 세션에서 이해하고 처리할 수 있는 크기로 잡습니다. 예를 들어 백엔드 계약 변경, 프론트엔드 호출부 수정, 테스트 및 빌드 검증은 각각 별도의 Task가 될 수 있습니다.

하나의 Task가 끝나면 반드시 TASK.md와 PLAN.md를 업데이트합니다. 그리고 다음 작업으로 넘어가기 전에 세션을 초기화하도록 했습니다. 여기서 중요한 점은 단순히 세션을 끊는 것이 아니라, 끊기 전에 현재 상태를 문서에 남기는 것입니다. 그래야 새 세션이 시작되었을 때 이전 작업을 다시 설명하지 않아도 됩니다.

T-01 완료 후 기록 예시

TASK.md
- [x] T-01. Update backend command contract
  - Status: Completed
  - Clear after completion: Required
  - Result: Command 필드 구조를 변경하고 관련 Flow 호출부를 수정했습니다.

PLAN.md State Log
| Task | Status | Last result | Next start |
| T-01 | Completed | 백엔드 계약 변경 완료, compileJava 통과 | T-02 프론트엔드 호출부 수정 |

이렇게 기록해 두면 새 세션에서는 먼저 TASK.md의 Current task를 보고, 필요한 경우 PLAN.md의 해당 섹션만 확인하면 됩니다. 이전에는 제가 긴 설명을 다시 작성해야 했지만, 이제는 문서가 그 역할을 대신합니다. 결과적으로 세션을 자주 초기화해도 작업 흐름이 끊기지 않았습니다.

또한 큰 문제를 한 번에 해결하려고 할 때보다 작은 문제를 하나씩 해결할 때 LLM의 응답 품질이 더 안정적이었습니다. LLM은 넓은 범위를 한 번에 다룰 때보다, 명확한 범위와 완료 기준이 있는 작은 작업에서 더 좋은 결과를 냈습니다. 이 점은 실제 적용하면서 가장 크게 체감한 변화였습니다.

6. 결정되지 않은 사항은 구현하지 않는 규칙

컨텍스트 문제를 어느 정도 해결한 뒤에는 또 다른 문제가 보였습니다. LLM이 모호한 내용을 혼자 결정하는 문제였습니다. 특히 개발자가 반드시 결정해야 하는 부분을 LLM이 질문하지 않고 임의로 진행하는 경우가 있었습니다. 이 문제는 단순한 코드 오류보다 더 위험했습니다. 방향이 잘못 잡히면 많은 파일을 수정한 뒤에야 문제를 발견하기 때문입니다.

이를 해결하기 위해 PLAN.md에 Decisions Required 섹션을 두었습니다. 구현 전에 결정이 필요한 사항을 먼저 식별하고, 하나라도 Open 상태로 남아 있으면 작업을 시작하지 않는 규칙을 만들었습니다. 이 규칙을 적용한 뒤부터 LLM은 모호한 부분을 임의로 처리하지 않고, 선택지와 장단점을 정리한 뒤 제 결정을 기다리게 되었습니다.

## 1. Decisions Required

| ID | Decision Needed | Options / Notes | Status | Decision |
| --- | --- | --- | --- | --- |
| D-01 | API 응답을 기존 DTO에 추가할지, 별도 DTO를 만들지 결정 필요 | 기존 DTO 확장: 변경 범위 작음 / 별도 DTO: 역할 분리 명확 | Open | |

규칙:
- Status가 Open인 결정이 하나라도 있으면 구현을 시작하지 않습니다.
- 사용자가 선택하면 Status를 Decided로 변경하고 Decision에 근거를 기록합니다.

이 방식은 생각보다 효과가 컸습니다. 이전에는 제가 프롬프트에 미처 적지 않은 부분을 LLM이 추정해서 진행했습니다. 이제는 오히려 LLM이 제가 놓친 결정 사항을 먼저 알려줍니다. 예를 들어 기존 필드를 재사용할지, 새 필드를 추가할지, 검증 위치를 백엔드로 둘지 프론트엔드로 둘지 같은 선택지를 먼저 제시합니다.

이 과정은 단순히 LLM을 통제하는 역할만 하지 않았습니다. 제 지시를 검토하는 데도 도움이 되었습니다. 저는 작업을 요청하면서 모든 결정을 다 생각했다고 느끼지만, 실제로는 빠진 부분이 있을 때가 많습니다. PLAN.md의 결정 목록은 그런 빈틈을 드러내는 장치가 되었습니다. 구현 전에 질문을 받으면 잠깐 멈춰서 방향을 다시 확인할 수 있고, 잘못 구현한 뒤 되돌리는 비용을 줄일 수 있습니다.

7. 작업 범위와 제외 범위 명시

LLM을 사용할 때 자주 생기는 문제 중 하나는 요청하지 않은 개선까지 함께 하려는 경향입니다. 주변 코드의 스타일을 정리하거나, 관련 있어 보이는 구조를 추가로 바꾸거나, 미래 확장성을 고려해 추상화를 만드는 식입니다. 물론 이런 제안이 도움이 될 때도 있지만, 실무에서는 변경 범위가 커질수록 리뷰 부담과 회귀 위험도 커집니다.

그래서 PLAN.md의 Working Rules에는 Scope와 Out-of-scope를 반드시 적도록 했습니다. 이번 작업에서 수정해도 되는 영역과 수정하면 안 되는 영역을 나누는 것입니다. 예를 들어 특정 API 계약을 수정하는 작업이라면 해당 Command, Flow, Resource, 호출부까지만 Scope로 두고, unrelated 리팩터링이나 생성 코드 직접 수정은 Out-of-scope로 기록합니다.

## 0. Working Rules

- Scope is limited to **************Command, *************Cdo, Flow/Resource wiring, and directly affected frontend call sites.
- Out-of-scope files/modules: unrelated track pattern behavior, generated target project files, unrelated UI restyling.
- Verification rule: run focused backend compile and affected frontend build when possible.
- Do not start implementation while any item in Decisions Required is Open.

이 규칙을 두면 LLM이 작업 중에 좋은 개선 아이디어를 발견하더라도 바로 적용하지 않습니다. 필요하면 별도 Task로 분리하거나, 사용자에게 확인합니다. 덕분에 하나의 작업이 불필요하게 커지는 것을 막을 수 있었습니다. 특히 협업 코드에서는 작은 변경 단위가 중요하기 때문에, 이 규칙은 리뷰 가능한 변경을 유지하는 데 도움이 되었습니다.

8. 검증 기준을 먼저 정하는 효과

LLM은 코드 생성을 빠르게 도와주지만, 생성된 코드가 실제로 안전한지는 별개의 문제입니다. 그래서 PLAN.md에는 Verification rule을 포함했습니다. 작업을 시작하기 전에 어떤 방식으로 완료 여부를 확인할지 정해 두는 것입니다.

예를 들어 백엔드 수정이라면 관련 모듈의 compileJava나 테스트를 실행하고, 프론트엔드 수정이라면 해당 패키지의 build나 타입 체크를 실행합니다. 환경 문제로 검증을 실행할 수 없다면, 실행하지 못한 이유와 대신 확인한 내용을 기록합니다. 중요한 것은 완료 판단을 LLM의 느낌에 맡기지 않는 것입니다.

Verification or completion:
- Run .\gradlew.bat :drama-feature:compileJava :drama-facade:compileJava
- Search for stale **********Cdo.get**********Id() references
- If verification is blocked, record the blocker and the smallest successful check

이 기준이 있으면 작업 결과를 리뷰할 때도 편합니다. 단순히 “수정했습니다”가 아니라 어떤 검증을 했는지 함께 남기기 때문입니다. 실제 작업에서는 빌드가 통과했는지, 특정 참조가 더 이상 남아 있지 않은지, 생성 결과가 의도한 이름으로 나오는지 같은 확인이 중요했습니다. 검증 기준을 먼저 정하니 LLM도 작업을 끝낼 시점을 더 명확히 판단했습니다.

9. 실제 작업 흐름의 변화

하네스를 적용하기 전에는 작업 흐름이 비교적 단순했습니다. 제가 프롬프트로 기능을 설명하면 LLM이 구현하고, 저는 결과를 검토했습니다. 문제가 있으면 다시 요청했습니다. 이 방식은 빠르게 시작할 수 있지만, 작업이 복잡해질수록 반복 수정이 많아졌습니다.

하네스를 적용한 뒤에는 흐름이 달라졌습니다. 먼저 작업 목표와 범위를 정리합니다. 그 다음 LLM이 TASK.md와 PLAN.md를 작성합니다. PLAN.md에서 결정이 필요한 사항이 있으면 구현을 멈추고 제가 선택합니다. 모든 결정이 정리되면 하나의 Task만 실행합니다. Task가 끝나면 결과와 검증 내용을 기록하고, 필요하면 세션을 초기화한 뒤 다음 Task로 넘어갑니다.

작업 흐름

1. 사용자 요청 입력
2. LLM이 작업 목표, 범위, 제외 범위, 검증 기준 정리
3. TASK.md / PLAN.md 생성
4. Decisions Required 확인
5. Open 결정이 있으면 구현 중단 후 사용자 결정 대기
6. 하나의 Task만 구현
7. 검증 실행 및 결과 기록
8. 세션 초기화 후 다음 Task 진행

이 흐름은 처음에는 느려 보였지만, 전체 작업 시간은 오히려 줄어드는 경우가 많았습니다. 잘못된 방향으로 구현하고 되돌리는 일이 줄었고, 세션이 길어져 품질이 떨어지는 문제도 줄었습니다. 무엇보다 작업 상태가 문서로 남기 때문에 중간에 멈추거나 다른 작업을 하다가 돌아와도 이어가기 쉬웠습니다.

10. 적용하면서 느낀 장점

첫 번째 장점은 컨텍스트 관리가 쉬워졌다는 점입니다. 세션을 오래 유지하지 않아도 되기 때문에 LLM의 품질 저하를 줄일 수 있었습니다. 이전에는 세션을 초기화하는 것이 부담스러웠지만, 이제는 TASK.md와 PLAN.md가 있으므로 초기화가 작업 흐름의 일부가 되었습니다.

두 번째 장점은 결정 비용이 줄었다는 점입니다. LLM이 임의로 결정하지 않고, 결정이 필요한 사항을 먼저 목록화해 주기 때문에 구현 전에 방향을 잡을 수 있었습니다. 이 과정에서 제가 미처 생각하지 못한 선택지도 확인할 수 있었습니다.

세 번째 장점은 리뷰 가능성이 높아졌다는 점입니다. 작업 범위, 결정 이유, 검증 결과가 문서에 남아 있으므로 나중에 코드를 볼 때 왜 이렇게 변경했는지 추적하기 쉬웠습니다. 특히 여러 파일이 함께 수정되는 작업에서는 이 기록이 작은 설계 문서 역할을 했습니다.

네 번째 장점은 LLM의 역할이 명확해졌다는 점입니다. 하네스를 적용하기 전에는 LLM이 구현자이자 설계자처럼 행동하는 경우가 있었습니다. 하네스를 적용한 뒤에는 LLM은 계획 안에서 구현을 보조하고, 작업 경계와 결정은 개발자가 통제하는 구조가 되었습니다. 이 차이가 실무 적용에서는 중요했습니다.

11. 적용 시 주의할 점

Task/Plan 하네스가 모든 작업에 필요한 것은 아닙니다. 단순한 설명 요청, 코드 읽기, 짧은 수정에는 오히려 과할 수 있습니다. 하네스는 코드 변경, 설정 변경, 테스트 변경, 생성물 변경처럼 실제 저장소에 영향을 주는 작업에서 효과가 컸습니다.

또한 문서를 너무 자세하게 쓰면 관리 비용이 커집니다. TASK.md는 작업 상태만 짧게 기록하고, PLAN.md에 상세 내용을 두는 것이 좋았습니다. TASK.md까지 길어지면 현재 작업을 빠르게 파악하기 어렵습니다. 반대로 PLAN.md가 너무 짧으면 다음 세션에서 판단 근거를 복원하기 어렵습니다.

마지막으로, 하네스가 있다고 해서 검토가 필요 없어지는 것은 아닙니다. LLM은 여전히 잘못된 코드를 만들 수 있고, 검증 명령이 통과해도 요구사항을 완전히 만족하지 못할 수 있습니다. 하네스는 LLM을 대신 믿기 위한 장치가 아니라, 개발자가 LLM을 더 안전하게 사용하기 위한 제어 장치로 보는 것이 맞습니다.

12. 마무리

이번 경험을 통해 LLM을 잘 활용하려면 프롬프트를 잘 쓰는 것만으로는 부족하다는 점을 느꼈습니다. 프롬프트는 순간적인 요청을 전달하는 수단입니다. 반면 Task/Plan 하네스는 작업 전체를 관리하는 구조입니다. 실무에서는 일회성 요청보다 지속 가능한 작업 구조가 더 중요했습니다.

Task/Plan 하네스를 적용한 뒤에는 컨텍스트 윈도우 문제로 인한 품질 저하가 줄었고, 세션 초기화 후에도 작업을 쉽게 이어갈 수 있었습니다. 또한 결정되지 않은 사항을 먼저 식별하고 구현을 멈추는 규칙 덕분에 LLM이 임의로 방향을 정하는 문제도 줄었습니다.

결과적으로 LLM은 더 빠르게 코드를 작성해 주는 도구를 넘어, 명확한 작업 구조 안에서 생산성을 높여 주는 도구가 되었습니다. 다만 그 효과는 LLM에게 모든 것을 맡길 때가 아니라, 개발자가 작업 경계와 결정 기준, 검증 기준을 분명히 세울 때 더 크게 나타났습니다. 앞으로도 LLM을 실무에 적용할 때는 작업을 어떻게 나누고 기록하고 검증할 것인지에 더 집중해야 한다고 생각합니다.

DEVKC

Site footer