본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 24. · 4 Views
Plan-and-Execute 코드 생성 완벽 가이드
LLM이 복잡한 요구사항을 단계별로 분해하고 순차적으로 코드를 생성하는 Plan-and-Execute 패턴을 실무 중심으로 배웁니다. 초급 개발자도 쉽게 이해할 수 있도록 스토리텔링과 비유로 풀어냈습니다.
목차
1. 복잡한 요구사항 분해하기
어느 날 김개발 씨가 팀장님께 업무를 받았습니다. "Todo 앱을 만들어줘.
백엔드도 있고, 프론트엔드도 있고, 데이터베이스도 연결하고." 김개발 씨는 막막했습니다. 어디서부터 시작해야 할까요?
복잡한 요구사항을 작은 단위로 나누는 것을 요구사항 분해라고 합니다. 마치 큰 퍼즐을 작은 조각으로 나누는 것처럼, 전체 프로젝트를 관리 가능한 작은 태스크로 쪼개는 과정입니다.
이것을 제대로 이해하면 어떤 복잡한 프로젝트도 체계적으로 접근할 수 있습니다.
다음 코드를 살펴봅시다.
# 요구사항을 계층적으로 분해하는 예제
def decompose_requirement(user_request):
# 1단계: 메인 목표 파악
main_goal = extract_main_goal(user_request)
# 2단계: 하위 태스크로 분해
subtasks = []
subtasks.append({"task": "데이터베이스 스키마 설계", "priority": 1})
subtasks.append({"task": "백엔드 API 구현", "priority": 2})
subtasks.append({"task": "프론트엔드 UI 구현", "priority": 3})
# 3단계: 각 태스크의 의존성 파악
dependencies = analyze_dependencies(subtasks)
return {"goal": main_goal, "tasks": subtasks, "dependencies": dependencies}
김개발 씨는 입사 6개월 차 주니어 개발자입니다. 오늘 팀장님께 큰 프로젝트를 맡았습니다.
"Todo 앱을 만들어줘. 사용자 인증도 있어야 하고, 할 일을 추가하고 수정하고 삭제할 수 있어야 해.
데이터베이스도 연결해야 하고." 처음에는 막막했습니다. 어디서부터 손을 대야 할지, 무엇을 먼저 해야 할지 감이 오지 않았습니다.
그때 선배 개발자 박시니어 씨가 다가왔습니다. "요구사항 분해부터 시작해봐요.
큰 문제를 작은 문제로 나누는 거예요." 쉽게 비유하자면, 요구사항 분해는 마치 레고 완성품을 보고 어떤 블록들이 필요한지 리스트를 만드는 것과 같습니다. 전체 그림을 보고, 그것을 만들기 위해 필요한 작은 부품들을 하나씩 정리하는 과정입니다.
이처럼 요구사항 분해도 큰 프로젝트를 관리 가능한 작은 단위로 쪼개는 것을 담당합니다. 요구사항 분해가 없던 시절에는 어땠을까요?
개발자들은 머릿속으로만 계획을 세우고 바로 코딩에 들어갔습니다. 코드를 작성하다 보면 놓친 부분이 생기고, 순서가 꼬이기 일쑤였습니다.
더 큰 문제는 프로젝트 중간에 "아, 이것도 필요한데"라며 계속 추가 요구사항이 생긴다는 것이었습니다. 프로젝트가 커질수록 이런 문제는 눈덩이처럼 불어났습니다.
바로 이런 문제를 해결하기 위해 체계적인 요구사항 분해가 등장했습니다. 요구사항 분해를 사용하면 전체 프로젝트의 범위를 명확하게 파악할 수 있습니다.
또한 어떤 작업을 먼저 해야 하는지 우선순위를 정할 수 있습니다. 무엇보다 팀원들과 협업할 때 누가 무엇을 담당할지 명확하게 나눌 수 있다는 큰 이점이 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 3번째 줄을 보면 사용자 요청에서 메인 목표를 추출하는 것을 알 수 있습니다.
이 부분이 핵심입니다. 다음으로 6-8번째 줄에서는 하위 태스크를 우선순위와 함께 리스트에 추가하는 동작이 일어납니다.
마지막으로 목표, 태스크, 의존성 정보가 딕셔너리 형태로 반환됩니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 쇼핑몰 서비스를 개발한다고 가정해봅시다. "상품 검색 기능을 만들어주세요"라는 요구사항이 들어오면, 이것을 "검색 API 엔드포인트 생성", "검색 알고리즘 구현", "검색 UI 컴포넌트 개발", "검색 결과 캐싱 적용"처럼 구체적인 태스크로 나눕니다.
많은 기업에서 이런 패턴을 적극적으로 사용하고 있습니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 너무 작게 쪼개거나, 너무 크게 남겨두는 것입니다. 너무 작게 쪼개면 관리할 태스크가 수백 개가 되어 오히려 복잡해집니다.
반대로 너무 크게 남겨두면 분해의 의미가 없어집니다. 따라서 하루에서 일주일 정도 작업할 수 있는 크기로 나누는 것이 적절합니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 Todo 앱을 여섯 개의 작은 태스크로 나눴습니다.
"아, 이렇게 하니까 훨씬 명확하네요!" 요구사항 분해를 제대로 이해하면 더 체계적이고 계획적인 개발을 할 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 각 태스크는 1-7일 안에 완료할 수 있는 크기로 나누세요
- 태스크 간 의존성을 반드시 파악하고 순서를 정하세요
- 분해한 태스크를 팀원들과 리뷰하면 놓친 부분을 발견할 수 있습니다
2. 단계별 구현 계획 수립
요구사항을 작은 태스크로 나눈 김개발 씨는 이제 막막함이 조금 사라졌습니다. 하지만 새로운 질문이 생겼습니다.
"이 태스크들을 어떤 순서로 해야 할까요?" 박시니어 씨가 웃으며 답했습니다. "그게 바로 실행 계획이에요."
단계별 구현 계획은 분해한 태스크들을 논리적인 순서로 배열하고, 각 단계에서 무엇을 달성할지 명확히 정의하는 것입니다. 마치 요리할 때 레시피의 순서를 정하는 것처럼, 코드 생성도 올바른 순서가 중요합니다.
이것을 제대로 수립하면 막힘 없이 프로젝트를 진행할 수 있습니다.
다음 코드를 살펴봅시다.
# 태스크들의 실행 계획을 수립하는 예제
class ExecutionPlanner:
def __init__(self, tasks):
self.tasks = tasks
self.plan = []
def create_plan(self):
# 의존성 순서대로 정렬
sorted_tasks = self.sort_by_dependencies(self.tasks)
# 각 단계별 목표와 검증 조건 설정
for task in sorted_tasks:
step = {
"name": task["task"],
"goal": f"{task['task']} 완료",
"verification": f"{task['task']} 테스트 통과"
}
self.plan.append(step)
return self.plan
김개발 씨는 Todo 앱을 여섯 개의 태스크로 나눴습니다. 데이터베이스 스키마, 사용자 인증, CRUD API, 프론트엔드 컴포넌트, 상태 관리, 통합 테스트.
하지만 어떤 순서로 해야 할지 막막했습니다. "데이터베이스부터 할까요?
아니면 프론트엔드부터?" 박시니어 씨가 화이트보드에 화살표를 그리기 시작했습니다. "봐요, 프론트엔드를 만들려면 API가 있어야 하고, API를 만들려면 데이터베이스가 있어야 해요.
이게 바로 의존성입니다." 쉽게 비유하자면, 실행 계획은 마치 건물을 지을 때 공정표를 짜는 것과 같습니다. 기초를 먼저 다지고, 그다음 골조를 세우고, 내부 인테리어는 나중에 합니다.
순서가 바뀌면 건물이 무너지듯, 코드도 순서가 중요합니다. 이처럼 실행 계획도 각 단계의 선후관계를 명확히 하는 역할을 담당합니다.
실행 계획 없이 개발하던 시절에는 어땠을까요? 개발자들은 "일단 되는 대로" 만들었습니다.
프론트엔드를 먼저 만들다가 API가 없어서 멈추고, API를 만들다가 데이터베이스 구조가 안 맞아서 다시 수정하는 일이 반복됐습니다. 더 큰 문제는 나중에 통합할 때 모든 부분이 맞지 않아 처음부터 다시 만들어야 하는 경우도 있었습니다.
시간과 비용이 두 배, 세 배로 늘어났습니다. 바로 이런 문제를 해결하기 위해 체계적인 실행 계획이 등장했습니다.
실행 계획을 사용하면 각 단계에서 무엇을 달성해야 하는지 명확해집니다. 또한 현재 어디까지 진행됐는지 진척도를 쉽게 파악할 수 있습니다.
무엇보다 문제가 생겼을 때 어느 단계로 돌아가야 하는지 알 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 8-9번째 줄을 보면 태스크들을 의존성 순서대로 정렬하는 것을 알 수 있습니다. 이 부분이 핵심입니다.
다음으로 12-16번째 줄에서는 각 태스크마다 이름, 목표, 검증 조건을 딕셔너리로 만들어 계획에 추가하는 동작이 일어납니다. 마지막으로 완성된 계획이 리스트 형태로 반환됩니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 결제 시스템을 개발한다고 가정해봅시다.
먼저 결제 데이터베이스 테이블을 만들고, 결제 API를 구현하고, 보안 인증을 추가하고, 결제 UI를 개발하고, 마지막으로 테스트를 진행합니다. 이렇게 순서를 정하면 각 단계가 다음 단계의 기반이 되어 자연스럽게 완성도 높은 시스템이 만들어집니다.
대부분의 핀테크 회사들이 이런 방식을 사용합니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 계획을 너무 경직되게 만드는 것입니다. 개발 중에 예상치 못한 문제가 생기거나 요구사항이 바뀔 수 있습니다.
따라서 계획은 유연하게 조정할 수 있어야 합니다. 완벽한 계획을 만들려고 일주일을 쓰는 것보다, 적당한 계획으로 빨리 시작하고 필요할 때 수정하는 것이 더 효율적입니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨와 함께 실행 계획을 세운 김개발 씨는 자신감이 생겼습니다.
"순서대로 하나씩 하면 되겠네요!" 단계별 구현 계획을 제대로 수립하면 더 예측 가능하고 안정적인 개발을 할 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 의존성 그래프를 그려보면 순서를 정하기 쉽습니다
- 각 단계마다 검증 조건을 미리 정해두면 품질을 높일 수 있습니다
- 계획은 처음에 80% 완성도로 시작하고 진행하면서 보완하세요
3. 순차적 코드 생성과 통합
계획을 세운 김개발 씨는 드디어 코드를 작성하기 시작했습니다. 첫 번째 단계인 데이터베이스 스키마를 완성하고, 두 번째 단계인 API를 만들고, 세 번째 단계인 프론트엔드를 개발했습니다.
그런데 모든 부분을 합쳤을 때 문제가 생겼습니다. "왜 연결이 안 될까요?"
순차적 코드 생성과 통합은 각 단계에서 만든 코드를 다음 단계와 매끄럽게 연결하는 것을 의미합니다. 마치 퍼즐 조각을 맞추는 것처럼, 각 부분이 정확히 맞아떨어져야 전체가 작동합니다.
이것을 제대로 이해하면 부분적으로는 완벽해도 전체적으로 실패하는 상황을 피할 수 있습니다.
다음 코드를 살펴봅시다.
# 단계별로 생성된 코드를 통합하는 예제
class CodeIntegrator:
def __init__(self):
self.components = {}
self.interfaces = {}
def add_component(self, name, code, interface):
# 각 단계에서 생성된 컴포넌트 등록
self.components[name] = code
self.interfaces[name] = interface
def integrate(self, component_a, component_b):
# 인터페이스 호환성 검증
if self.check_compatibility(component_a, component_b):
# 두 컴포넌트를 연결
connection = self.create_connection(component_a, component_b)
return connection
else:
raise ValueError(f"{component_a}와 {component_b}의 인터페이스가 맞지 않습니다")
김개발 씨는 계획대로 코드를 작성했습니다. 데이터베이스는 완벽하게 작동했고, API도 테스트를 통과했고, 프론트엔드 UI도 예쁘게 나왔습니다.
"이제 다 합치면 되겠다!" 그런데 막상 합쳤을 때 에러가 쏟아졌습니다. API가 반환하는 데이터 형식과 프론트엔드가 기대하는 형식이 달랐습니다.
데이터베이스 컬럼 이름과 API 응답의 필드 이름도 달랐습니다. 박시니어 씨가 코드를 보며 말했습니다.
"각 부분을 만들 때 다음 단계와 어떻게 연결될지 생각하지 않았네요. 통합을 염두에 두고 개발해야 해요." 쉽게 비유하자면, 순차적 통합은 마치 레고 블록을 조립하는 것과 같습니다.
각 블록은 위아래에 연결 부위가 있어서 다른 블록과 정확히 맞물립니다. 만약 연결 부위가 맞지 않으면 블록을 붙일 수 없습니다.
이처럼 코드도 각 부분이 정확한 인터페이스로 연결되어야 전체 시스템이 작동합니다. 통합을 고려하지 않고 개발하던 시절에는 어땠을까요?
개발자들은 각자 맡은 부분만 완벽하게 만들었습니다. "내 코드는 완벽해"라고 자신했지만, 막상 합치려니 연결이 안 됐습니다.
데이터 타입이 다르고, 함수 시그니처가 맞지 않고, 통신 프로토콜이 달랐습니다. 더 큰 문제는 이런 문제를 프로젝트 막바지에 발견해서 모든 부분을 다시 수정해야 했다는 것입니다.
일정이 두 배로 늘어나고, 팀원 간 갈등도 생겼습니다. 바로 이런 문제를 해결하기 위해 순차적 통합 방식이 등장했습니다.
순차적 통합을 사용하면 각 단계를 완성할 때마다 바로 이전 단계와 연결해봅니다. 문제가 생기면 즉시 발견하고 수정할 수 있습니다.
또한 인터페이스를 먼저 정의하고 개발하면 나중에 통합이 매끄럽게 진행됩니다. 무엇보다 프로젝트 마지막까지 불안해하지 않고 안정적으로 진행할 수 있다는 큰 이점이 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 7-10번째 줄을 보면 각 단계에서 생성된 컴포넌트와 그 인터페이스를 등록하는 것을 알 수 있습니다.
이 부분이 핵심입니다. 다음으로 13-14번째 줄에서는 두 컴포넌트의 인터페이스가 호환되는지 검증하는 동작이 일어납니다.
호환되면 연결을 만들고, 호환되지 않으면 명확한 에러 메시지를 반환합니다. 실제 현업에서는 어떨게 활용할까요?
예를 들어 마이크로서비스 아키텍처를 구축한다고 가정해봅시다. 사용자 서비스를 먼저 만들고, 주문 서비스를 만들 때 사용자 서비스의 API 스펙을 확인하며 개발합니다.
각 서비스가 완성될 때마다 API Gateway에 연결해서 통합 테스트를 실행합니다. 넷플릭스나 우버 같은 대규모 서비스들이 이런 방식으로 수백 개의 마이크로서비스를 안정적으로 운영합니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 모든 부분을 다 만들고 마지막에 한 번에 통합하려는 것입니다.
이렇게 하면 문제를 발견했을 때 어디가 잘못됐는지 찾기 어렵습니다. 따라서 작은 단위로 자주 통합하고 테스트하는 것이 중요합니다.
"일찍 통합하고, 자주 통합하라"는 격언을 기억하세요. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 조언을 들은 김개발 씨는 각 부분의 인터페이스를 먼저 정의하고, 단계별로 통합 테스트를 실행했습니다. "아, 이렇게 하니까 문제를 바로 찾을 수 있네요!" 순차적 코드 생성과 통합을 제대로 이해하면 더 안정적이고 예측 가능한 시스템을 만들 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 각 컴포넌트의 인터페이스를 먼저 정의하고 코드를 작성하세요
- 단계마다 통합 테스트를 실행하여 문제를 조기에 발견하세요
- API 스펙 문서를 작성하면 팀원 간 소통이 원활해집니다
4. 중간 검증과 수정
김개발 씨는 이제 단계별로 코드를 만들고 통합하는 것까지 배웠습니다. 하지만 세 번째 단계에서 문제를 발견했습니다.
API 응답 속도가 너무 느렸습니다. 박시니어 씨가 물었습니다.
"각 단계마다 검증을 했나요?" 김개발 씨는 고개를 저었습니다.
중간 검증과 수정은 각 단계를 완성할 때마다 품질, 성능, 정확성을 검사하고 문제가 있으면 즉시 수정하는 과정입니다. 마치 요리할 때 중간중간 맛을 보는 것처럼, 코드도 단계마다 확인해야 합니다.
이것을 제대로 이해하면 완성 직전에 큰 문제를 발견하는 상황을 피할 수 있습니다.
다음 코드를 살펴봅시다.
# 각 단계의 코드를 검증하고 수정하는 예제
class StepValidator:
def __init__(self, criteria):
self.criteria = criteria # 검증 기준
self.issues = []
def validate_step(self, step_code, step_name):
# 1. 기능 테스트
functional_pass = self.run_tests(step_code)
# 2. 성능 검증
performance_pass = self.check_performance(step_code)
# 3. 코드 품질 검증
quality_pass = self.check_code_quality(step_code)
# 검증 실패 시 수정 제안
if not (functional_pass and performance_pass and quality_pass):
suggestion = self.generate_fix_suggestion(step_name)
self.issues.append({"step": step_name, "fix": suggestion})
return False
return True
김개발 씨는 Todo 앱의 세 번째 단계까지 완성했습니다. 데이터베이스도 만들고, API도 구현했습니다.
모든 기능이 작동했습니다. 하지만 실제로 사용해보니 API 응답이 3초나 걸렸습니다.
"이건 너무 느린데..." 박시니어 씨가 코드를 보며 말했습니다. "데이터베이스 쿼리에 인덱스를 안 만들었네요.
각 단계마다 성능도 검증했어야죠." 쉽게 비유하자면, 중간 검증은 마치 건물을 지을 때 층마다 안전 점검을 하는 것과 같습니다. 1층을 짓고 점검하고, 2층을 짓고 점검합니다.
만약 10층을 다 짓고 나서야 1층 기둥에 문제가 있다는 걸 발견하면 어떻게 될까요? 전체를 다시 지어야 할 수도 있습니다.
이처럼 코드도 단계마다 검증하면 큰 재앙을 막을 수 있습니다. 중간 검증 없이 개발하던 시절에는 어땠을까요?
개발자들은 프로젝트를 다 만들고 마지막에 한 번만 테스트했습니다. 그때 발견된 문제들이 첫 단계부터 시작된 것이라면 모든 코드를 뜯어고쳐야 했습니다.
더 큰 문제는 어느 단계에서 문제가 생긴 건지 찾기가 너무 어려웠다는 것입니다. 디버깅에 개발 시간보다 더 많은 시간이 들어갔습니다.
바로 이런 문제를 해결하기 위해 단계별 중간 검증이 등장했습니다. 중간 검증을 사용하면 문제를 조기에 발견할 수 있습니다.
문제가 작을 때 수정하면 비용과 시간이 훨씬 적게 듭니다. 또한 각 단계의 품질이 보장되므로 다음 단계를 자신 있게 진행할 수 있습니다.
무엇보다 프로젝트 막바지에 패닉 상태에 빠지지 않는다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 8-14번째 줄을 보면 기능, 성능, 코드 품질을 각각 검증하는 것을 알 수 있습니다. 이 세 가지가 모두 통과해야 다음 단계로 넘어갈 수 있습니다.
다음으로 17-20번째 줄에서는 검증 실패 시 자동으로 수정 제안을 생성하고 이슈 목록에 추가하는 동작이 일어납니다. 마지막으로 검증 결과를 불린 값으로 반환합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 소셜 미디어 앱을 개발한다고 가정해봅시다.
게시물 작성 기능을 만들 때, 기능 테스트로 글이 잘 저장되는지 확인하고, 성능 테스트로 대용량 이미지 업로드 속도를 측정하고, 코드 품질 검증으로 SQL 인젝션 취약점이 없는지 확인합니다. 각 검증을 통과하면 다음 기능인 게시물 수정으로 넘어갑니다.
페이스북이나 인스타그램 같은 서비스들이 이런 방식으로 안정성을 유지합니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 검증을 너무 완벽하게 하려다 진도가 안 나가는 것입니다. 모든 엣지 케이스를 다 테스트하려 하면 끝이 없습니다.
따라서 핵심적인 부분만 검증하고, 세부적인 부분은 나중에 보완하는 것이 효율적입니다. 완벽함보다 꾸준한 진전이 중요합니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨와 함께 데이터베이스에 인덱스를 추가한 김개발 씨는 API 응답 시간을 0.2초로 줄였습니다.
"각 단계마다 검증하니까 문제를 바로 잡을 수 있네요!" 중간 검증과 수정을 제대로 이해하면 더 안정적이고 품질 높은 코드를 만들 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 기능, 성능, 보안 세 가지는 반드시 매 단계마다 검증하세요
- 자동화된 테스트를 작성하면 검증 시간을 크게 줄일 수 있습니다
- 검증 기준을 미리 정해두고 체크리스트로 관리하세요
5. 실습 멀티 파일 프로젝트 생성기
이론은 충분히 배웠습니다. 이제 실전입니다.
김개발 씨는 박시니어 씨에게 도전 과제를 받았습니다. "LLM을 사용해서 멀티 파일 프로젝트를 자동으로 생성하는 도구를 만들어보세요." 김개발 씨는 지금까지 배운 Plan-and-Execute 패턴을 떠올렸습니다.
멀티 파일 프로젝트 생성기는 사용자의 요청을 받아 여러 파일로 구성된 프로젝트를 자동으로 생성하는 도구입니다. Plan-and-Execute 패턴을 활용하여 파일 구조를 계획하고, 각 파일을 순차적으로 생성하고, 통합하는 과정을 자동화합니다.
이것을 구현하면 실제로 생산성을 크게 높일 수 있는 도구를 만들 수 있습니다.
다음 코드를 살펴봅시다.
# 멀티 파일 프로젝트 자동 생성 예제
class MultiFileProjectGenerator:
def __init__(self, llm_client):
self.llm = llm_client
self.planner = ExecutionPlanner([])
def generate_project(self, user_request):
# 1단계: 요구사항 분해 및 파일 구조 계획
plan = self.create_file_structure_plan(user_request)
# 2단계: 각 파일을 순차적으로 생성
files = {}
for file_plan in plan:
code = self.llm.generate_code(file_plan)
files[file_plan["filename"]] = code
# 3단계: 생성된 파일 검증
if not self.validate_file(code, file_plan):
code = self.llm.fix_code(code, file_plan)
files[file_plan["filename"]] = code
# 4단계: 통합 및 프로젝트 구성
return self.integrate_files(files)
김개발 씨는 멀티 파일 프로젝트 생성기를 만들기 시작했습니다. 먼저 전체 구조를 머릿속으로 그려봤습니다.
"사용자가 'Flask 블로그를 만들어줘'라고 하면, 어떤 파일들이 필요할까?" 박시니어 씨가 힌트를 줬습니다. "먼저 파일 구조를 계획하세요.
메인 앱 파일, 데이터베이스 모델, 라우트, 템플릿... 이런 식으로요." 쉽게 비유하자면, 멀티 파일 프로젝트 생성기는 마치 건축가가 설계도를 보고 각 방을 하나씩 만들어가는 것과 같습니다.
거실을 먼저 만들고, 부엌을 만들고, 침실을 만듭니다. 각 방은 독립적이지만 복도로 연결되어 하나의 집을 이룹니다.
이처럼 프로젝트도 각 파일이 독립적이지만 임포트로 연결되어 하나의 시스템을 이룹니다. 수작업으로 프로젝트를 만들던 시절에는 어땠을까요?
개발자들은 새 프로젝트를 시작할 때마다 폴더를 만들고, 파일을 하나씩 생성하고, 보일러플레이트 코드를 복사 붙여넣기 했습니다. 파일 간 임포트 경로를 맞추고, 설정 파일을 작성하고, 의존성을 추가하는 데만 몇 시간씩 걸렸습니다.
더 큰 문제는 실수로 파일을 빠뜨리거나 경로를 잘못 입력하면 프로젝트가 작동하지 않았다는 것입니다. 바로 이런 문제를 해결하기 위해 자동화된 프로젝트 생성기가 등장했습니다.
프로젝트 생성기를 사용하면 몇 분 만에 완전한 프로젝트 구조를 얻을 수 있습니다. 파일 간 연결도 자동으로 맞춰지고, 베스트 프랙티스에 따라 구조화됩니다.
또한 같은 패턴의 프로젝트를 여러 번 만들 때 일관성을 유지할 수 있습니다. 무엇보다 개발자가 창의적인 부분에 집중할 수 있다는 큰 이점이 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 8-9번째 줄을 보면 사용자 요청을 분석하여 파일 구조 계획을 만드는 것을 알 수 있습니다.
이 부분이 Plan 단계입니다. 다음으로 12-15번째 줄에서는 계획된 각 파일을 LLM을 사용하여 순차적으로 생성하는 Execute 단계가 일어납니다.
18-20번째 줄에서는 생성된 파일을 검증하고 문제가 있으면 수정하는 검증 과정이 진행됩니다. 마지막으로 모든 파일을 통합하여 완성된 프로젝트를 반환합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 스타트업에서 신규 마이크로서비스를 자주 만든다고 가정해봅시다.
매번 같은 구조의 서비스를 손으로 만들면 시간이 오래 걸립니다. 하지만 프로젝트 생성기를 사용하면 "사용자 인증 서비스를 만들어줘"라고 요청만 하면 자동으로 API 엔드포인트, 데이터베이스 모델, 미들웨어, 테스트 파일이 모두 생성됩니다.
구글이나 아마존 같은 대기업들도 내부 도구로 이런 생성기를 활용합니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 생성된 코드를 검토 없이 그대로 사용하는 것입니다. LLM이 만든 코드도 완벽하지 않습니다.
보안 취약점이 있을 수 있고, 비효율적인 부분이 있을 수 있습니다. 따라서 생성된 코드를 반드시 리뷰하고, 필요한 부분은 수정해야 합니다.
자동화는 도구일 뿐, 최종 책임은 개발자에게 있습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
Plan-and-Execute 패턴을 적용한 김개발 씨는 멀티 파일 프로젝트 생성기를 완성했습니다. "Flask 블로그를 만들어줘"라고 입력하니 10개의 파일이 자동으로 생성됐습니다.
"와, 정말 작동하네요!" 멀티 파일 프로젝트 생성기를 만들면 반복적인 작업을 자동화하고 생산성을 크게 높일 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 파일 간 의존성을 그래프로 표현하면 생성 순서를 정하기 쉽습니다
- 템플릿 엔진을 활용하면 더 유연한 코드 생성이 가능합니다
- 생성된 프로젝트에 README와 테스트를 자동으로 포함시키세요
6. 실습 Todo 앱 만들어줘 완전 자동화
김개발 씨는 마지막 도전 과제를 받았습니다. "사용자가 'Todo 앱 만들어줘'라고 하면, 계획부터 구현, 테스트, 배포까지 완전 자동으로 처리하는 시스템을 만들어보세요." 이것은 지금까지 배운 모든 것을 종합하는 과제였습니다.
완전 자동화된 앱 생성기는 사용자의 간단한 요청만으로 전체 개발 프로세스를 자동으로 실행하는 시스템입니다. 요구사항 분해, 계획 수립, 순차적 코드 생성, 검증, 통합, 테스트, 배포까지 모든 단계를 자동화합니다.
이것을 구현하면 개발 시간을 획기적으로 단축할 수 있습니다.
다음 코드를 살펴봅시다.
# Todo 앱 완전 자동 생성 시스템
class FullAutoAppGenerator:
def __init__(self, llm_client):
self.llm = llm_client
self.decomposer = RequirementDecomposer()
self.planner = ExecutionPlanner([])
self.generator = MultiFileProjectGenerator(llm_client)
self.validator = StepValidator(criteria={"performance": 1000, "quality": 80})
def create_app(self, user_request):
print(f"요청 받음: {user_request}")
# 1단계: 요구사항 분해
tasks = self.decomposer.decompose(user_request)
print(f"분해 완료: {len(tasks)}개 태스크")
# 2단계: 실행 계획 수립
plan = self.planner.create_plan_from_tasks(tasks)
# 3단계: 순차적 코드 생성 및 검증
project = {}
for step in plan:
code = self.generator.generate_step(step)
# 중간 검증
if self.validator.validate_step(code, step["name"]):
project[step["name"]] = code
else:
# 수정 후 재검증
fixed_code = self.llm.fix_issues(code, self.validator.issues)
project[step["name"]] = fixed_code
# 4단계: 통합 및 최종 테스트
integrated_app = self.generator.integrate_files(project)
# 5단계: 배포 준비
deployment_config = self.create_deployment_config(integrated_app)
print("앱 생성 완료!")
return {"app": integrated_app, "deployment": deployment_config}
김개발 씨는 지금까지 배운 모든 개념을 떠올렸습니다. 요구사항 분해, 실행 계획, 순차적 생성, 중간 검증, 통합.
"이 모든 걸 하나로 합치면 되겠구나." 박시니어 씨가 격려했습니다. "각 단계를 제대로 구현했으니, 이제 파이프라인으로 연결만 하면 됩니다." 쉽게 비유하자면, 완전 자동화 시스템은 마치 자동차 공장의 조립 라인과 같습니다.
원자재가 들어가면 차체 제작, 엔진 조립, 도색, 내부 인테리어, 품질 검사를 거쳐 완성된 자동차가 나옵니다. 사람이 각 단계를 모니터링하지만, 실제 작업은 자동화된 로봇이 수행합니다.
이처럼 코드 생성도 개발자가 감독하지만, 실제 구현은 LLM이 자동으로 수행합니다. 수작업으로 앱을 만들던 시절에는 어땠을까요?
개발자들은 요구사항을 듣고, 며칠간 계획을 세우고, 몇 주간 코드를 작성하고, 버그를 수정하고, 테스트하고, 배포 설정을 만들었습니다. 간단한 Todo 앱 하나 만드는 데도 일주일 이상 걸렸습니다.
더 큰 문제는 비슷한 앱을 또 만들어야 할 때 처음부터 다시 시작해야 했다는 것입니다. 지식이 재사용되지 않았습니다.
바로 이런 문제를 해결하기 위해 완전 자동화 시스템이 등장했습니다. 완전 자동화를 사용하면 몇 분 만에 배포 가능한 앱을 얻을 수 있습니다.
개발자는 창의적인 요구사항 정의와 최종 검토에만 집중하면 됩니다. 또한 베스트 프랙티스가 자동으로 적용되어 품질이 일정하게 유지됩니다.
무엇보다 반복적인 작업에서 해방되어 더 가치 있는 일에 시간을 쓸 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 14-15번째 줄을 보면 사용자 요청을 태스크로 분해하는 것을 알 수 있습니다. 이것이 첫 번째 Plan 단계입니다.
다음으로 18번째 줄에서는 태스크들을 실행 가능한 계획으로 변환하는 두 번째 Plan 단계가 일어납니다. 22-31번째 줄에서는 각 단계를 순차적으로 생성하고 검증하며, 문제가 있으면 자동으로 수정하는 Execute 단계가 진행됩니다.
34번째 줄에서는 모든 부분을 통합하고, 37번째 줄에서는 배포 설정을 자동으로 생성합니다. 마지막으로 완성된 앱과 배포 설정을 반환합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 컨설팅 회사에서 고객마다 비슷한 CRM 시스템을 만들어준다고 가정해봅시다.
완전 자동화 시스템을 사용하면 "고객 관리 CRM을 만들어줘. 연락처, 상담 이력, 매출 분석 기능이 필요해"라고 입력만 하면 됩니다.
시스템이 자동으로 데이터베이스, 백엔드 API, 프론트엔드 대시보드, 인증 시스템을 생성합니다. 개발 시간이 2주에서 2시간으로 단축됩니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 자동화에만 의존하고 코드를 이해하지 않으려는 것입니다.
자동 생성된 코드도 결국 누군가는 유지보수해야 합니다. 문제가 생겼을 때 코드를 이해하지 못하면 해결할 수 없습니다.
따라서 자동화는 시작점이지 끝이 아닙니다. 생성된 코드를 공부하고, 개선하고, 자신의 것으로 만들어야 합니다.
또 다른 주의사항은 보안입니다. 자동 생성된 코드에 보안 취약점이 있을 수 있습니다.
SQL 인젝션, XSS, CSRF 같은 기본적인 공격에 대한 방어가 누락될 수 있습니다. 반드시 보안 검증 단계를 추가하고, 생성된 코드를 보안 관점에서 리뷰해야 합니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 완전 자동화 시스템을 완성한 김개발 씨는 테스트를 시작했습니다.
"Todo 앱 만들어줘"라고 입력했습니다. 시스템이 작동하기 시작했습니다.
요구사항 분해, 계획 수립, 코드 생성, 검증, 통합... 5분 후, 완전히 작동하는 Todo 앱이 생성됐습니다.
박시니어 씨가 감탄했습니다. "대단한데요!
Plan-and-Execute 패턴을 완벽하게 이해했네요." 김개발 씨는 뿌듯했습니다. 하지만 거기서 멈추지 않았습니다.
생성된 코드를 하나씩 열어보며 어떻게 작동하는지 공부했습니다. 보안 취약점은 없는지, 성능 개선 여지는 없는지 검토했습니다.
"자동화가 시작을 도와줬지만, 완성은 내가 해야 해." 완전 자동화 시스템을 제대로 활용하면 생산성을 획기적으로 높이면서도 코드 품질을 유지할 수 있습니다. 여러분도 오늘 배운 Plan-and-Execute 패턴을 실제 프로젝트에 적용해 보세요.
처음에는 작은 프로젝트부터 시작하세요. 경험이 쌓이면 점점 더 복잡한 시스템도 자동화할 수 있을 것입니다.
중요한 것은 자동화가 개발자를 대체하는 게 아니라는 점입니다. 자동화는 개발자를 반복 작업에서 해방시켜, 더 창의적이고 가치 있는 문제에 집중할 수 있게 도와줍니다.
여러분은 자동화 도구를 만드는 사람이자, 그것을 현명하게 활용하는 사람이 되어야 합니다.
실전 팁
💡 - 처음에는 간단한 앱부터 자동화하고 점차 복잡도를 높이세요
- 생성된 코드에 자동 테스트를 포함시켜 품질을 보장하세요
- 보안 검증 단계를 반드시 추가하고, OWASP Top 10을 체크하세요
- 자동화 시스템의 각 단계를 로깅하여 문제 발생 시 디버깅하세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
ReAct 패턴 마스터 완벽 가이드
LLM이 생각하고 행동하는 ReAct 패턴을 처음부터 끝까지 배웁니다. Thought-Action-Observation 루프로 똑똑한 에이전트를 만들고, 실전 예제로 웹 검색과 계산을 결합한 강력한 AI 시스템을 구축합니다.
AI 에이전트의 모든 것 - 개념부터 실습까지
AI 에이전트란 무엇일까요? 단순한 LLM 호출과 어떻게 다를까요? 초급 개발자를 위해 에이전트의 핵심 개념부터 실제 구현까지 이북처럼 술술 읽히는 스타일로 설명합니다.
프로덕션 RAG 시스템 완벽 가이드
검색 증강 생성(RAG) 시스템을 실제 서비스로 배포하기 위한 확장성, 비용 최적화, 모니터링 전략을 다룹니다. AWS/GCP 배포 실습과 대시보드 구축까지 프로덕션 환경의 모든 것을 담았습니다.
RAG 캐싱 전략 완벽 가이드
RAG 시스템의 성능을 획기적으로 개선하는 캐싱 전략을 배웁니다. 쿼리 캐싱부터 임베딩 캐싱, Redis 통합까지 실무에서 바로 적용할 수 있는 최적화 기법을 다룹니다.
실시간으로 답변하는 RAG 시스템 만들기
사용자가 질문하면 즉시 답변이 스트리밍되는 RAG 시스템을 구축하는 방법을 배웁니다. 실시간 응답 생성부터 청크별 스트리밍, 사용자 경험 최적화까지 실무에서 바로 적용할 수 있는 완전한 가이드입니다.