"난이도가 높아서 오래 걸리는 거야?" — AIFid 개발 기록으로 따져 본 소요 시간
시리즈 CLAUDE.md 규칙으로 보는 PHP 백엔드 기본기 현재 위치 11 / 15
- 직접 JWT 구현 금지! 검증된 라이브러리와 안전 설계
- CodeIgniter 4 + 모던 PHP 8.4: readonly DTO, backed enum, match로 배열·define() 걷어내기
- Redis 캐시 TTL 설계와 N+1 방지 실전 가이드
- CI4에서 얇은 컨트롤러와 서비스 기반 트랜잭션 관리법
- OpenAPI를 PHP 어트리뷰트로: $ref 재사용 컴포넌트와 공통 ErrorResponse
- DB는 UTC, 화면은 KST: TIMESTAMP + 세션 타임존으로 변환 코드 없애기
- 프론트 로그를 큐로 받는 수집 파이프라인: 즉시 202 → Redis → 워커 → 파일 + DB, 실패 시 dead-letter
- 서비스를 쪼갤까? MSA 선택을 위한 실전 체크리스트
- MSA 공통 에러 규약, RFC 9457로 정하기
- 스쿼시 머지 vs 머지 커밋: 1인 개발자의 PR 전략
- "난이도가 높아서 오래 걸리는 거야?" — AIFid 개발 기록으로 따져 본 소요 시간
- CLAUDE.md로 팀 규칙을 AI에게 가르치는 법
- CI4 브랜드 에러 페이지: 설계서 한 장에서 구현까지
- 870줄 CLAUDE.md를 116줄로 줄인 실전 리팩터링
- Claude Code 토큰 절감 실전: 3개월 로그 분석과 5단계 전략
시리즈 Claude Code 워크플로 현재 위치 15 / 29
- git worktree로 AI 세션 여러 개를 동시에 돌리기: 브랜치 충돌과 stash 공유 사고를 피하는 운영 규칙
- AI에게 기억을 심는 법: 메모리 파일로 삽질 교훈을 세션 간에 유지하기
- 스킬로 반복 작업 자동화: 이슈 착수부터 종료까지 한 명령으로 묶은 dev-issue 사례
- 코드 그래프(graft)로 AI의 탐색 비용 줄이기: grep/read 대비 토큰 절감 체감
- AI 코드 리뷰를 규칙으로 고정하기
- 반복되는 Git 워크플로우와 브랜치 정리 자동화
- 같은 요청을 여러 저장소에 반복하는 법: 한 줄 프롬프트 + 기준 문서 하나
- 이슈 하나를 한 줄로 개발 끝내는 법 — 내가 가장 많이 쓰는 Claude Code 스킬들
- 블로그 글을 스무 번 부탁하고 나서, 스킬로 만들었다
- 마라톤 세션이 토큰을 먹는 구조: 로그로 찾고, /compact와 서브에이전트로 줄이기
- 1인 개발자가 클로드코드로 SaaS 11개 만든 기록
- 이슈 하나에 30~60분, 어디서 새고 있었나: skill-creator로 dev-issue 스킬을 만든 과정
- 도면을 AI에게 읽히려다 AI를 뺀 이야기
- 스쿼시 머지 vs 머지 커밋: 1인 개발자의 PR 전략
- "난이도가 높아서 오래 걸리는 거야?" — AIFid 개발 기록으로 따져 본 소요 시간
- 엑셀 간편장부를 웹 ERP로 옮기기 (AICassa)
- Impeccable 스킬 적용한 게시글 상세 화면 구조 개선
- CLAUDE.md로 팀 규칙을 AI에게 가르치는 법
- AI티 나는 디자인 패턴 걷어내기: overused-font 훅 오탐 처리와 접근성 대비 수정
- CI4 브랜드 에러 페이지: 설계서 한 장에서 구현까지
- Claude에 MCP로 디자인 툴 붙이기 — DesignCraft·PhotoCraft 설치부터 명함, 카툰 효과까지
- AI 실수 복구: 메인 체크아웃을 dev 상태로 되돌린 기록
- 870줄 CLAUDE.md를 116줄로 줄인 실전 리팩터링
- AI로 AI 강의안을 만들다 — 실용인문학 5·6강 준비기
- Claude Code 토큰 절감 실전: 3개월 로그 분석과 5단계 전략
- 개발 기록을 재료로 AI가 쓰는 블로그 글 만들기
- Claude Vision으로 영수증·세금계산서 자동 장부 입력 구현
- AI가 만든 실제 실수 4가지 패턴과 교훈
- GitHub 이슈·PR 기반의 월간 작업일지 자동 생성 Claude 스킬 만들기
시리즈 제품 개발기 현재 위치 9 / 20
- 엑셀 간편장부를 웹 ERP로 옮기기 (AICassa)
- 혼자 쓰는 맛집 앱 Pagus 만들기
- 감으로 잡던 견적을 코드로: CNC 목공과 AILign 개발 이야기
- AIPicto: 도연님의 그림책을 위한 온라인 구독 플랫폼
- PHP 개발자, TypeScript와 R3F로 3D 가구 배치 앱 만들기
- 녹화형 AI 화상면접 솔루션 기획 노트 — AILego
- AIvance 제품 이름은 왜 라틴어일까: Cassa에서 Cella까지
- 1인 개발자가 11개 SaaS를 운영하는 비밀: 공통 골격과 MSA 분할 전략
- "난이도가 높아서 오래 걸리는 거야?" — AIFid 개발 기록으로 따져 본 소요 시간
- 개별로는 통과하는데 전체에서만 죽는다 — AIFid 테스트 스위트가 composer test를 멈춘 날들
- 1인 개발자가 클로드코드로 SaaS 11개 만든 기록
- 도면을 AI에게 읽히려다 AI를 뺀 이야기
- 팬리스 우분투로 DC 모터를 인터넷에서 제어하기
- 저장소 아키텍처를 archify로 그려 README에 링크한 날
- Apache 403 해결과 php spark 로 슈퍼관리자 자동 발급하기
- UX와 보안을 동시에 잡는 그림책 게스트 열람 방안
- 혼자 하는 유지보수, 단가와 한도를 어떻게 정할까
- SEO + GEO 동시 적용: 정적 페이지에서 AI 검색 노출까지 한 방법
- 코어를 건드리지않고 확장하는 CodeIgniter 4 플러그인 구조와 핵심 설계 포인트
- GitHub 이슈·PR 기반의 월간 작업일지 자동 생성 Claude 스킬 만들기
프로젝트를 하다가 AI에게 물었다. "개발 시간이 더 걸리는 느낌인데, 난이도가 높아서 그래?" 답은 "난이도보다 프로세스 무게"였다. 그런데 그 뒤 두 주 치 기록을 다시 보니 그 답은 반만 맞았다. 같은 프로세스를 밟는데도 이슈 하나가 20분 만에 닫히기도 하고, 이슈 네 개에 하루가 걸리기도 했다.
0. 질문과 첫 번째 답
2026-07-27 오전, AIFid(멀티테넌트 실시간 상담 시스템) 세션에서 이렇게 물었다.
이 프로젝트 개발할 때 시간이 더 걸리는 느낌인데 난이도가 높아서 그런 거야?
AI의 답을 요약하면 이랬다.
- 커밋 로그를 보면 기능 하나마다
설계 확정 → 구현 계획(N개 태스크) → feat3단계를 거친다. 코드 쓰기 전에 문서화 비용이 먼저 붙는다. CLAUDE.md의 절대 규칙 9개(테넌트 격리, 내부 노트 유출 방지 테스트, PHPStan 6, 커버리지 70%, 마이그레이션down()필수 등)가 매 기능마다 체크리스트로 걸린다.- 스택이 4갈래(CI4 백오피스 / Node 엔진 / React 콘솔 / Preact 위젯)라 도메인 자체도 단순하지는 않다.
- 결론: 난이도와 의도된 프로세스 오버헤드가 둘 다 기여하지만, 체감 지연의 상당 부분은 후자다.
이어서 "프로세스 무게를 줄일 방법이 있냐"고 물었고, 작업 크기별로 풀 사이클 여부를 판단하자는 제안이 나왔다. 그 기준을 CLAUDE.md에 "설계 문서 생략 기준"으로 적었다.
- 생략 가능: 선택지가 하나뿐인 버그 수정, 마이그레이션 없이 컴포넌트 하나 안에서 끝나는 소규모 기능, 3절 절대 규칙에 영향이 없는 변경, 이슈에 수용 기준이 이미 구체적인 백로그.
- 생략 금지: 여러 컴포넌트에 걸친 변경, 새 도메인·테이블·외부 연동, 테넌트 격리·권한에 영향을 주는 변경, 트레이드오프 결정이 필요한 경우.
여기까지가 그날의 이야기다. 이 글은 그 이후, 이슈 #69~#113을 /dev-issue로 연달아 처리한 기록을 가지고 "정말 프로세스 무게 때문이었나"를 다시 따져 본 회고다.
먼저 짚어 둘 점. 첫 답은 커밋 로그를 보고 추정한 것이지 시간을 잰 결과가 아니었다. 아래 숫자는 GitHub 이슈의 생성·종료 시각과 PR 변경량이다. PR은 작업이 끝난 뒤에 올리므로 "PR 생성→머지 시간"은 작업 시간이 아니라서 쓰지 않았다. 이슈 종료 시각에는 잠자는 시간이 섞여 있다.
1. 프로세스 무게는 상수다
첫 답의 약점은 이것이다. 프로세스 무게(설계 → 계획 → TDD → 게이트 통과)는 이슈마다 거의 같다. 그런데 소요 시간은 이슈마다 열 배 넘게 달랐다. 상수로는 변동을 설명할 수 없다. 프로세스는 바닥(기본 비용)을 정할 뿐이고, 그 위에 얹히는 시간은 다른 변수가 정한다.
실제 기록을 두 덩어리로 나눠 보자.
| 모바일 SDK (iOS·Android·Flutter) | 세션 무효화 후속 (#110~#113) | |
|---|---|---|
| 이슈 | #69~#80, #92~#95 (14개) | #110, #111, #112, #113 (4개) |
| 기간 | 07-31 16:26 ~ 08-01 19:59 (KST) | 08-06 20:48 생성 ~ 08-07 17:45 종료 (KST) |
| 관련 PR 변경량 | #83~#99 합계 약 +18,000줄 | #114~#119 합계 약 +4,650줄 |
| 이슈당 간격 | 대개 15~35분 | 이슈 하나에 2시간 이상 (첫 이슈는 밤을 넘김) |
코드량은 SDK 쪽이 네 배 가까이 많은데 시간은 훨씬 짧다. 난이도가 "코드가 많음"이라면 이 표는 말이 안 된다.
2. 빨랐던 쪽: 결정이 끝나 있었다
iOS SDK 이슈 네 개(#72~#75)는 07-31 하루 오후에 16:59, 17:20, 17:55, 18:11로 연달아 닫혔다. 변경량은 +1,766, +837, +1,348, +240줄. Android는 다음 날 12:00, 12:26, 12:57에 세 개가 닫혔고(+2,218, +766, +1,201줄), Flutter는 PR 하나가 +7,057줄·95파일이었는데 이슈 네 개(#92~#95)가 08-01 18:30~19:59, 한 시간 반 사이에 닫혔다.
빨랐던 이유는 코드가 쉬워서가 아니라 결정할 게 남아 있지 않아서였다.
- 07-30에 모바일 SDK 설계 문서를 먼저 확정했고, 백엔드 푸시 API(#71, #81, #82)도 SDK 이슈보다 앞서 끝나 있었다. 계약이 이미 있었다.
- Android는 커밋 메시지에 "iOS 미러링"이라고 적을 만큼 iOS를 번역하는 작업이었다. Flutter는 네이티브 SDK를 감싸는 브릿지였다.
- 이슈에 수용 기준이 구체적으로 적혀 있어서
CLAUDE.md의 생략 기준을 대부분 만족했다. 계획 문서를 별도로 쓰지 않고 TDD로 바로 들어갈 수 있었다.
타자 치는 양과 걸리는 시간은 별개라는 얘기다. 결정이 끝난 작업은 양이 많아도 번역에 가깝다.
3. 느렸던 쪽: 고칠 때마다 다음 구멍이 나왔다
세션 무효화(정지·삭제된 계정의 WebSocket을 즉시 끊기)는 설계 문서가 +2,001줄이었고(#105), 구현(#108)은 +1,566줄이었다. 여기까지는 보통이었다. 시간은 그 뒤에 들어갔다. 이슈 네 개가 서로의 후속이었다.
- #110 — 토큰 재사용(탈취 의심)을 탐지해 세션을 끊는데 계정은 그대로
active라서, 탈취범이 재접속하면valid: true로 다시 붙는다. 끊는 효과가 사실상 없었다. - #111 — 폐기 시 엔진이
connection.error를 보내는데 그걸 처리하는 클라이언트가 하나도 없었다. 폐기된 소켓이 재연결 → 거부 → 재연결을 무한 반복한다. - #112 —
account_disabled폐기가 정지하는 순간이 아니라 정지된 계정이 토큰 갱신을 시도할 때만 발행된다. 정지 기능 자체가 아직 없어서 훅을 달 곳이 없었다. - #113 — bootstrap 경합과 Pub/Sub 메시지 유실로 폐기를 놓친 소켓이 재검증 없이 남는다.
하나를 고치면 그 고침이 전제하던 가정이 깨져 다음 이슈가 됐다. 이슈마다 diff는 작거나 중간인데(#115는 +433줄, #119는 +350줄) 시간은 길었다. 줄 수가 아니라 되돌아간 횟수가 시간을 먹었다.
가장 아팠던 건 #114였다. 로그아웃 시 세션 폐기가 아무 소켓도 못 끊고 있었다. 로그아웃 봉투에 실리는 jti는 API 토큰의 것이고, 소켓이 쥔 건 엔진 토큰의 jti다. 두 토큰은 issue()를 각각 호출해 만들어지고 issue()는 매번 새 uuid를 채우니 구조적으로 일치할 수 없다. 그런데 백오피스 테스트는 "봉투에 값이 실렸는지"만, 엔진 테스트는 목 소켓에 같은 값을 스스로 심어 확인했다. 양쪽 다 초록불이었고, 로그에는 revoke.applied가 정상처럼 찍혔다. 공통 sid를 도입해서 고쳤다.
그날 얻은 교훈은 CLAUDE.md에 규칙 10번으로 들어갔다. "컴포넌트 경계를 넘는 값은 '같은 값인가'를 테스트로 고정한다."
이 구간에는 난이도 말고도 시간을 먹은 것이 더 있었다.
- 병렬 세션이 같은 계획서를 보고 각자 구현해서 PR 하나를 통째로 버렸다. (큐 룸 인가, 세션 무효화)
- 같은 체크아웃에서 테스트를 두 프로세스로 동시에 돌리면 스키마가 깨져
Table doesn't exist,Deadlock found같은 에러가 매번 다르게 나온다. 코드 문제로 오인하기 쉬워서 디버깅에 시간을 썼다. - 콘솔을 두 탭에 띄우면 두 탭이 같은 refresh 토큰으로 동시에 회전해서, 정상 사용자가 "탈취"로 잡혀 로그아웃됐다(#115). 서버 로직이 아니라 브라우저 탭이라는 환경 변수 때문에 생긴 버그였다.
4. 코드가 쉬운데도 오래 걸린 날
난이도가 아닌 이유로 오래 걸린 사례가 두 개 더 있다.
- #104 / #100 — Android Maven groupId를
io.aifid에서kr.aivance.fid로 바꾸는 일. diff는 +132/−128줄, 단순 치환이다. 하지만 48개 파일이고, 이슈 #100은 Sonatype 네임스페이스 검증을 기다리느라 08-01에 열려 08-05에 닫혔다. 코드 난이도는 0에 가깝고 외부 대기가 4일이었다. - CI 전환(#129~#144) — 러너를 옮긴 날 PR이 열여섯 개 쌓였다. 하나하나는 한 줄짜리였다. CI에서 "한 줄 찍고 재실행"이 한 번에 5~10분이라 가설 하나 검증하는 비용이 컸다. 자세한 건 CI 고치다 PR이 15개 쌓인 날에 적었다. 여기서 시간을 정한 건 코드 난이도가 아니라 피드백 루프의 길이였다.
5. 정리: 소요 시간을 가늠하는 식
기록을 놓고 보니 "난이도"라는 한 단어가 서로 다른 네 가지를 뭉쳐 놓고 있었다.
소요 시간 ≈ 프로세스 고정비
+ 작업량 × (결정이 끝났으면 작고, 안 끝났으면 크다)
+ 되돌아간 횟수 × 한 번 되돌아가는 비용
+ 대기 (외부 검증, 느린 피드백 루프)
- 프로세스 고정비는 이슈마다 거의 같다. 첫 답이 말한 부분이다. 줄일 수 있지만(생략 기준) 변동은 설명하지 못한다.
- 작업량은 SDK가 보여 줬듯 결정이 끝난 상태면 많아도 빠르다.
- 되돌아간 횟수가 가장 큰 변수였다. 특히 컴포넌트 경계를 넘는 불변식(
sid, 룸 이름, 봉투 필드)은 한쪽 테스트가 초록불이어도 맞는지 알 수 없다. - 대기는 코드가 쉬워도 시간을 먹는다.
착수 전에 던질 질문을 세 개로 줄이면 이렇다.
- 결정이 이미 끝났나? 설계·계약이 문서로 있고 이슈에 수용 기준이 있으면 빠르다. 없으면 설계부터 한다. 생략 기준이 이걸 가른다.
- 값이나 계약이 컴포넌트 경계를 넘나? 넘으면 "양쪽이 같은 값을 보는지"를 확인하는 테스트를 먼저 하나 둔다. 이 질문에 "예"인 작업은 예상 시간에 두 배를 곱해도 모자란다.
- 한 번 확인하는 데 몇 분 드나, 외부에서 기다릴 게 있나? CI에서만 재현되거나 외부 승인이 걸려 있으면 코드 난이도와 무관하게 길어진다. 로컬 재현부터 만든다.
6. 그래서 처음 질문의 답은
"난이도가 높아서 오래 걸리나?"에는 "난이도 하나로는 설명이 안 된다"가 답이다. 첫 답처럼 프로세스 무게가 바닥을 깔고, 그 위의 변동은 결정이 끝났는지, 경계를 넘는 불변식이 있는지, 피드백이 느린지가 정했다. 프로세스를 줄이는 건 바닥을 낮출 뿐이고, 오래 걸린 이슈들은 대부분 바닥이 아니라 그 위에서 시간을 썼다.
한 가지 더 남은 소득은, 설계 문서 생략 기준이 실제로 작동했다는 점이다. 07-27 이후 IDOR·인가 버그 수정(#101~#103)은 문서 없이 바로 TDD로 갔고, 권한 모델에 영향이 있는 세션 무효화(#105)는 기준대로 설계 문서를 먼저 썼다. 그리고 그 설계가 놓친 구멍(#110~#113)이 바로 그 뒤에 줄줄이 나왔다는 점은, 설계 문서가 있어도 경계를 넘는 불변식은 테스트로 고정하지 않으면 안 잡힌다는 걸 보여 준다.
근거: AIFid 저장소 GitHub 이슈 #69~#113의 생성·종료 시각(UTC를 KST로 환산), PR #83~#119의 변경량, 2026-07-27 세션 로그의 질의응답, CLAUDE.md의 생략 기준·규칙 10번, 커밋 #114·#115 메시지. 시각에는 수면 시간이 포함되어 있어 "순수 작업 시간"이 아니다.

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