AI한테 코드 맡기고 롤백한 이야기 — 앱 개발에서 만난 5가지 함정

S_Soo100·2026년 3월 31일

ai

목록 보기
4/11

AI 코딩 에이전트로 앱을 만들면서 겪은 실패 패턴 5가지. 코드를 대신 짜주는 건 해결됐지만, "어떻게 시키느냐"는 여전히 사람의 몫이었습니다.


배경

AI 코딩 에이전트(Claude Code)로 사이드 프로젝트를 진행하고 있습니다. Flutter 앱과 React 웹앱을 동시에 개발하는데, UI 구현부터 상태 관리, API 연동까지 대부분의 코드를 AI가 작성합니다.

개발 속도는 확실히 빨라졌습니다. 하루 만에 로그인 화면, 리스트 UI, 상세 페이지, 상태 관리까지 돌아가는 프로토타입이 나왔으니까요.

문제는 속도가 아니라 방향이었습니다. 빠르게 잘못된 곳으로 갈 수 있다는 걸, 하루 세션에서 여러 번 확인했습니다.


함정 1: "개선해줘" → 화면 전체 재작성

무슨 일이 있었나

나: "이 주문 목록 화면 좀 개선해줘."
AI: 네, 전체 구조를 개선하겠습니다.
    - StatefulWidget → Riverpod AsyncNotifier로 전환
    - ListView → CustomScrollView + SliverList로 교체
    - 에러/로딩/빈 상태를 별도 위젯으로 분리
    ...

이 한 마디에 AI가 화면 하나를 통째로 재작성했습니다. 상태 관리 방식이 바뀌었고, 스크롤 구조가 달라졌으며, 기존에 맞춰둔 애니메이션 타이밍이 전부 깨졌습니다.

결과? 롤백하는 게 더 빠를 정도의 나쁜 수정.

왜 이렇게 됐나

AI는 "개선"이라는 단어를 "더 좋게 다시 짜기"로 해석했습니다. 하지만 기존 코드에는 수십 번의 디바이스 테스트와 미세 조정이 녹아있었어요.

// AI가 "매직 넘버"라고 판단하고 삭제한 코드
const kCardHeight = 72.0;    // 72px — 터치 타겟 최소 48px + 패딩. 갤럭시 폴드에서 검증
const kScrollThreshold = 0.85; // 85% — 0.9로 하면 느린 네트워크에서 로딩 스피너가 안 보임
const kAnimDuration = 180;    // 180ms — 200ms는 체감상 느림, 150ms는 끊겨 보임 (Galaxy A13 기준)

// AI가 "깔끔하게" 바꾼 코드
const kCardHeight = 56.0;      // Material 기본값
const kScrollThreshold = 0.9;  // 일반적인 값
const kAnimDuration = 300;     // Material 기본 애니메이션

카드 높이, 스크롤 임계값, 애니메이션 시간 — 이것들은 여러 기기에서 테스트하고 나서 조정한 값이지 임의의 숫자가 아니었습니다.

교훈

"개선" 요청 전에, 진짜 문제가 뭔지 먼저 파악하세요.

실제로 급했던 건 목록 하단의 페이지네이션 버그였습니다. 화면 전체 재설계가 아니라 스크롤 리스너 조건문 하나 수정이면 될 일이었어요.

// 이렇게 시켰어야 했다
나: "주문 목록에서 마지막 페이지 로드가 안 돼.
    _onScroll 메서드의 페이지네이션 로직만 봐줘.
    다른 코드는 건드리지 마."

AI에게 "개선해줘"라고 하면, AI는 자기가 할 수 있는 최대치를 합니다. 변경 범위를 먼저 정해주지 않으면, 범위는 끝없이 커집니다.


함정 2: 코드만 보고 에셋/설정은 안 봄

무슨 일이 있었나

앱에서 이미지가 간헐적으로 깨져서 나오는 문제가 있었습니다. AI는 이미지 로딩 코드를 분석하고 캐시 로직을 수정했습니다.

나: "프로필 이미지가 가끔 깨져서 나와."
AI: CachedNetworkImage의 캐시 정책을 분석해보겠습니다.
    → cacheManager의 maxAge가 너무 짧은 것이 원인입니다. 7일 → 30일로 수정했습니다.
나: "아직 깨지는데?"
AI: 추가로 placeholder와 errorWidget의 처리를 개선하겠습니다.
나: "... 여전히 깨지는데?"

캐시 코드를 3번이나 수정했지만 핵심 문제는 그대로였습니다.

왜 이렇게 됐나

원인은 코드가 아니라 pubspec.yaml이미지 에셋이었습니다. flutter_image_compress 패키지 버전이 올라가면서 HEIC 포맷 처리가 바뀌었고, 특정 안드로이드 기기에서 변환이 실패하고 있었어요.

$ git diff --stat
 pubspec.lock                    | 12 +++---  # ← AI가 무시한 변경
 lib/widgets/profile_image.dart  | 25 ++++++---
 lib/services/image_cache.dart   | 18 ++----

git diffpubspec.lock 변경이 찍혀 있었는데, AI는 .dart 파일에만 집중하느라 패키지 버전 변경을 무시했습니다.

교훈

"코드가 맞는데 동작이 이상하다" → 에셋·설정·패키지부터 확인 → 코드는 그 다음.

AI 코딩 에이전트는 본능적으로 코드를 먼저 봅니다. 하지만 앱 개발에서 "특정 기기에서만 이상하다"의 원인은 코드가 아니라 패키지 버전, 에셋 포맷, 빌드 설정인 경우가 많습니다. git diffpubspec.lock, build.gradle, Podfile.lock 변경이 있으면 반드시 확인하세요.


함정 3: AI가 만든 설계안을 그대로 믿을 뻔함

무슨 일이 있었나

앱이 커지면서 상태 관리가 복잡해졌고, AI에게 아키텍처 개선 보고서를 생성시켰습니다. 보고서가 꽤 그럴듯했어요:

## 아키텍처 개선 제안 (AI 생성)

1. Clean Architecture 도입 (Domain/Data/Presentation 3레이어 분리)
2. 모든 API 호출을 UseCase 클래스로 래핑
3. Repository 패턴 + DataSource 추상화
4. DI 컨테이너: get_it + injectable
5. 에러 처리: Either<Failure, Success> (dartz 패키지)
6. 라우팅: go_router + ShellRoute 기반 네비게이션 재설계

화면 8개, 개발자 1명인 사이드 프로젝트에 UseCase 클래스 20개, Repository 인터페이스 10개라니. 이건 대규모 엔터프라이즈 앱이지, 내 프로젝트가 아닙니다.

왜 이렇게 됐나

AI는 "아키텍처 개선"이라는 키워드만 보고 업계 베스트 프랙티스를 제안했습니다. 앱 규모, 팀 크기, 출시 일정 같은 맥락이 프롬프트에 충분히 들어가지 않았어요.

다행히 "비판적으로 검수해봐"라고 다른 모델에게 요청한 덕분에, 이런 피드백이 돌아왔습니다:

검수 결과 (다른 모델):
❌ Clean Architecture 3레이어 — 화면 8개에 파일 수만 3배. 유지보수 비용 > 이득
❌ UseCase 클래스 — API 호출을 그대로 전달하는 패스스루 클래스가 대부분일 것
❌ dartz Either — 러닝커브 대비 효과 미미. try-catch + sealed class로 충분
⚠️ Repository 패턴 — 서버가 1개인데 DataSource 추상화는 과잉. 직접 접근이 더 간단
✅ go_router — 네비게이션 개선은 유효. 단, ShellRoute 전면 재설계는 점진적으로

요청하지 않았으면 그대로 설계에 반영했을 겁니다.

교훈

AI가 생성한 설계안은 "초안"이지 "결정"이 아닙니다.

AI는 "기술적으로 가능한 최선"을 제안하지, "이 규모와 이 상황에서 현실적인 최선"을 제안하지 않습니다. "구현 가능한가"뿐 아니라 "우리 앱 규모에 맞는가"를 반드시 검증하세요. 이 문제를 구조적으로 해결한 방법은 다음 글에서 다룹니다.


함정 4: 문제를 새 패키지로 해결하려는 본능

무슨 일이 있었나

앱의 메인 리스트 화면이 버벅거린다는 피드백이 들어왔습니다.

나: "메인 피드가 스크롤할 때 버벅여. 개선 방안 제안해줘."
AI: 다음 3가지를 제안합니다:
    1. flutter_staggered_animations로 지연 렌더링 도입
    2. 상태 관리를 Provider → Riverpod으로 마이그레이션
    3. 이미지 로딩을 CachedNetworkImage → fast_cached_network_image로 교체

전부 새로운 패키지 도입이나 대규모 마이그레이션이었습니다.

왜 이렇게 됐나

기존 코드를 살펴보니 이미 문제가 보였습니다:

// 문제 1: ListView.builder를 안 쓰고 전체 리스트를 한 번에 빌드
Widget build(BuildContext context) {
  return ListView(
    children: items.map((item) => FeedCard(item: item)).toList(),
    // 아이템 200개면 → 위젯 200개를 한 번에 생성
  );
}

// 문제 2: 매 빌드마다 이미지 URL 파싱
class FeedCard extends StatelessWidget {
  Widget build(BuildContext context) {
    final url = Uri.parse(item.imageUrl);  // 스크롤할 때마다 파싱
    final resized = url.replace(queryParameters: {'w': '300'});  // 매번 새 객체
    return CachedNetworkImage(imageUrl: resized.toString());
  }
}

// 문제 3: setState로 전체 화면 리빌드
void _onLike(String itemId) {
  setState(() {
    items = items.map((i) => i.id == itemId ? i.copyWith(liked: true) : i).toList();
    // 좋아요 1개 누르면 → 아이템 200개 전부 리빌드
  });
}

실제 해결: 새 패키지 0개.

  • ListViewListView.builder (화면에 보이는 것만 빌드)
  • 이미지 URL 파싱을 모델 생성 시 한 번만 수행
  • 좋아요를 개별 아이템의 ValueNotifier로 분리 → 해당 카드만 리빌드

jank이 완전히 사라졌습니다.

교훈

"화면이 버벅인다" ≠ "새 패키지가 필요하다"

앱 개발자(그리고 AI)의 본능은 "더 좋은 패키지로 교체"이지만, 실제 필요한 건 기존 코드의 위젯 빌드 최적화인 경우가 많습니다.

기존 코드에 이미 답이 있는데 새로 도입하려는 건, 사람도 AI도 똑같이 빠지는 함정입니다.


잘 된 것: 병렬 에이전트 대량 투입

실패 사례만 있는 건 아닙니다.

6개의 독립적인 화면을 구현할 때, AI 에이전트 6개를 동시에 투입했습니다.

에이전트 A → 주문 상세 화면   (새 파일: order_detail_screen.dart)
에이전트 B → 결제 화면        (새 파일: payment_screen.dart)
에이전트 C → 리뷰 작성 화면   (새 파일: review_write_screen.dart)
에이전트 D → 알림 목록 화면   (새 파일: notification_list_screen.dart)
에이전트 E → 설정 화면        (새 파일: settings_screen.dart)
에이전트 F → 프로필 편집 화면 (새 파일: profile_edit_screen.dart)

규칙: "각자 새 파일만 생성. 기존 파일 수정 금지. 공통 위젯과 모델만 import."

결과: ~5분 만에 전부 완료. 파일 충돌 0건.

성공 요인은 단순했습니다:

  • 각 에이전트에 "이 파일만 생성, 기존 파일 수정 금지" 규칙
  • 기존 공통 위젯과 모델만 import (에이전트 간 의존성 차단)
  • 화면 스펙 문서를 에이전트에 직접 전달 (자율 구현)

AI에게 자율성을 줄 때, 경계를 명확히 정해주면 병렬 처리의 위력이 폭발합니다.


정리: AI 코딩 에이전트 활용 체크리스트

매 작업 전에 확인하는 목록입니다:

#체크관련 함정
1진짜 문제가 뭔지 파악했는가? (코드? 에셋? 설정? 패키지?)함정 1, 2
2변경 범위를 합의했는가? (전면 교체 vs 최소 수정)함정 1
3git diff에 pubspec.lock·설정 파일 변경이 있는가?함정 2
4AI 생성 설계안을 검수했는가? (앱 규모, 팀 크기, 현실성)함정 3
5기존 코드에 이미 답이 있지 않은가?함정 4
6새 패키지가 정말 필요한가? 기존 위젯 최적화로 해결 가능하지 않은가?함정 4
7병렬 투입 시 에이전트 간 경계가 명확한가?성공 사례

다음 글

함정 3에서 "AI 설계안을 검수해야 한다"고 했는데, 구체적으로 어떻게 검수할까요?

같은 모델에게 "이거 괜찮아?"라고 물으면, "네, 괜찮습니다"라고 답합니다. 자기가 만든 걸 자기가 리뷰하면 결함을 못 잡아요.

다음 글에서는 이 "자기 동의 편향"을 깨는 방법 — 교차 모델 리뷰에 대해 작성해 보겠습니다.

profile
Ai agent 설계를 잘 하고싶은 개발자

0개의 댓글