명확한 폴더 경계, 일관된 네이밍, 향후 재작업을 줄이는 단순한 기본값으로 생성된 코드를 유지보수하기 쉽게 만드는 방법을 알아보세요.

생성된 코드는 일상 업무를 바꿉니다. 단지 기능을 만드는 것이 아니라 많은 파일을 빠르게 생성할 수 있는 시스템을 이끌어야 합니다. 속도는 장점이지만, 작은 불일치가 빠르게 누적됩니다.
생성된 출력물은 개별적으로는 괜찮아 보입니다. 비용은 두 번째, 세 번째 변경에서 드러납니다: 해당 코드가 어디에 속하는지 알 수 없고 동일한 동작을 두 군데에서 수정하거나, 다른 영향이 두려워 파일을 건드리지 않게 됩니다.
“영리한” 구조는 예측하기 어렵기 때문에 비용이 큽니다. 처음엔 맞아 보이던 커스텀 패턴, 숨은 마법, 과한 추상화가 시간이 지나면 짐이 됩니다. AI 보조 생성의 경우 이런 영리함이 이후 세대의 혼란을 일으켜 중복 로직이나 쌓이는 레이어로 이어질 수 있습니다.
무난한(boring) 아키텍처는 정반대입니다: 단순한 경계, 단순한 이름, 명백한 기본값. 완벽함이 목표가 아니라, 피곤한 동료(또는 미래의 자신)가 30초 안에 이해할 수 있는 레이아웃을 선택하는 것입니다.
간단한 목표: 다음 변경을 쉽게 만드는 것, 멋있게 보이게 만드는 것이 아닙니다. 보통 UI, API, 데이터, 공유 유틸리티 같은 각 종류의 코드에 한 가지 분명한 위치, 파일이 하는 일과 일치하는 예측 가능한 이름, 자동 와이어링이나 숨은 글로벌, 메타프로그래밍 같은 최소한의 “마법”을 의미합니다.
예: Koder.ai에 “팀 초대”를 추가하라고 하면 UI는 UI 영역에, API 경로는 API 영역에 하나만, 초대 데이터는 데이터 레이어에 저장되게 하길 원합니다. 그 기능을 위해 새로운 폴더나 패턴을 발명하지 않고요. 그런 무난한 일관성이 향후 편집 비용을 낮춥니다.
생성된 코드는 같은 일을 하는 여러 방법을 제공할 때 비용이 커집니다. 무난한 아키텍처 규칙은 단순합니다: 첫 빌드가 덜 영리하게 느껴지더라도 다음 변경이 예측 가능하게 만드세요.
다음 질문에 빠르게 답할 수 있어야 합니다:
하나의 단순한 구조를 골라 어디서나 지키세요. 도구(또는 팀원)가 화려한 패턴을 제안하면, 실제 고통을 없애지 않는 한 기본 답은 “아니오”입니다.
시간이 지나도 버티는 실용적 기본값:
새 개발자가 리포를 열고 “구독 취소” 버튼을 추가해야 한다고 상상해보세요. 커스텀 아키텍처를 먼저 배울 필요가 없어야 합니다. 명확한 기능 영역, 명확한 UI 컴포넌트, 단일 API 클라이언트 위치, 단일 데이터 접근 경로를 찾아야 합니다.
이 규칙은 Koder.ai 같은 빠른 생성 도구와 특히 잘 맞습니다: 빠르게 생성하되 매번 같은 무난한 경계로 출력을 안내하세요.
생성된 코드는 빠르게 커지는 경향이 있습니다. 유지보수를 안전하게 지키는 가장 확실한 방법은 누구나 변경이 어디에 속하는지 추측할 수 있는 무난한 폴더 맵입니다.
많은 웹앱에 맞는 작은 최상위 레이아웃 예시:
app/ 화면, 라우팅, 페이지 레벨 상태components/ 재사용 가능한 UI 조각features/ 기능별 폴더 하나씩 (billing, projects, settings)api/ API 클라이언트 코드와 요청 헬퍼server/ 백엔드 핸들러, 서비스, 비즈니스 규칙이렇게 경계가 명확해집니다: UI는 app/과 components/에 있고, API 호출은 api/에, 백엔드 로직은 server/에 있습니다.
데이터 접근도 단순해야 합니다. SQL 쿼리와 레포지토리 코드는 UI 파일에 흩어지지 않고 백엔드 가까이에 두세요. Go + PostgreSQL 환경에서는 간단한 규칙으로: HTTP 핸들러가 서비스를 호출하고, 서비스가 레포지토리를 호출하고, 레포지토리가 데이터베이스와 대화합니다.
공유 타입과 유틸리티도 명확한 집이 필요하지만 작게 유지하세요. 공통 타입은 types/(DTO, enum, 공유 인터페이스)에, 작은 헬퍼는 utils/(날짜 포맷팅, 간단한 밸리데이터)로 둡니다. 만약 utils/가 또 다른 작은 앱처럼 느껴진다면 해당 코드는 기능 폴더로 옮겨야 할 신호입니다.
생성된 폴더는 교체 가능하다고 취급하세요.
generated/(또는 gen/)에 두고 직접 수정하지 마세요.features/나 server/에 두어 재생성이 덮어쓰지 않게 하세요.예: Koder.ai가 API 클라이언트를 생성하면 generated/api/에 저장하고, api/에 얇은 래퍼를 작성해 재시도, 로깅, 명확한 오류 메시지 등을 추가하세요.
생성된 코드는 만들기 쉽고 쌓이기 쉽습니다. 네이밍이 한 달 뒤에도 읽기 쉽게 유지시켜 줍니다.
하나의 네이밍 스타일을 정하고 섞지 마세요:
kebab-case (user-profile-card.tsx, billing-settings)PascalCase (UserProfileCard)camelCase (getUserProfile)SCREAMING_SNAKE_CASE (MAX_RETRY_COUNT)역할로 이름 지으세요, 현재 구현 방식으로 이름 짓지 마세요. user-repository.ts는 역할입니다. postgres-user-repository.ts는 변경될 수 있는 구현 세부사항입니다. 구현 접미사는 진짜로 여러 구현이 있을 때만 사용하세요.
misc, helpers, 거대한 utils 같은 정크 서랍을 피하세요. 함수가 하나의 기능에서만 사용된다면 그 기능 근처에 두세요. 공유된다면 기능을 설명하는 이름으로 만들고(date-format.ts, money-format.ts, id-generator.ts) 모듈을 작게 유지하세요.
라우트, 핸들러, 컴포넌트가 패턴을 따르면 검색 없이도 찾을 수 있습니다:
routes/users.ts 경로 /users/:userId 같은 패스handlers/users.get.ts, handlers/users.update.tsservices/user-profile-service.tsrepositories/user-repository.tscomponents/user/UserProfileCard.tsxKoder.ai(또는 어떤 생성기)를 사용한다면, 이 규칙들을 프롬프트에 넣고 편집할 때도 일관되게 유지하세요. 파일 이름을 추측할 수 있으면 향후 변경 비용이 낮아집니다.
생성된 코드는 첫날에는 인상적이지만 30일 뒤엔 고통스러울 수 있습니다. 약간 반복적이어도 코드를 명확하게 만드는 기본값을 선택하세요.
먼저 마법을 줄이세요. 동적 로딩, 리플렉션 스타일 트릭, 자동 와이어링은 측정된 필요가 있을 때만 사용하세요. 이런 기능은 요소가 어디서 오는지 숨겨 디버깅과 리팩터링을 느리게 합니다.
명시적 임포트와 명확한 의존성을 선호하세요. 파일에 필요한 것이 있으면 직접 임포트하세요. 모듈 간 와이어링이 필요하면 한 눈에 보이는 곳(예: 단일 조합 파일)에서 하세요. 무엇이 먼저 실행되는지 추측하게 해선 안 됩니다.
설정은 단순하고 중앙화하세요. 환경 변수, 기능 플래그, 앱 전역 설정을 하나의 모듈에 하나의 네이밍 방식으로 두세요. 편리해서 여기저기 흩어놓지 마세요.
팀 일관성을 지키는 경험적 규칙:
오류 처리는 영리함이 가장 해로운 곳입니다. 한 패턴을 선택해 어디서나 사용하세요: 데이터 레이어에서 구조화된 오류를 반환하고, 한 곳에서 HTTP 응답으로 매핑하고, UI 경계에서 사용자용 메시지로 번역하세요. 파일마다 세 가지 다른 오류 타입을 던지지 마세요.
Koder.ai로 앱을 생성한다면, 명시적 모듈 와이어링, 중앙화된 설정, 하나의 오류 패턴을 처음부터 요청하세요.
UI, API, 데이터 사이의 명확한 선은 변경을 국한시킵니다. 대부분의 미스터리 버그는 한 레이어가 다른 레이어 일을 하려 할 때 발생합니다.
UI(보통 React)는 화면을 렌더링하고 UI 전용 상태를 관리하는 장소로 취급하세요: 어떤 탭이 열렸는지, 폼 오류, 로딩 스피너, 기본 입력 처리 등.
서버 상태는 분리하세요: 가져온 목록, 캐시된 프로필, 백엔드와 일치해야 하는 것들. UI 컴포넌트가 합계를 계산하거나 복잡한 규칙을 검증하거나 권한을 판단하면 로직이 화면 전반에 퍼져 수정 비용이 커집니다.
API 레이어는 예측 가능해야 합니다. HTTP 요청을 비즈니스 코드 호출로 번역하고 결과를 안정적인 요청/응답 형태로 다시 번역하세요. 데이터베이스 모델을 그대로 전송하지 마세요. 안정적인 응답은 내부를 리팩터링해도 UI가 깨지지 않게 합니다.
잘 동작하는 단순한 경로:
SQL(또는 ORM 로직)은 레포지토리 경계 뒤에 두어 앱 나머지 부분이 데이터 저장 방식에 “알게” 하지 마세요. Go + PostgreSQL의 경우 보통 UserRepo나 InvoiceRepo 같은 레포지토리가 있고 작은 명확한 메서드(GetByID, ListByAccount, Save)를 제공합니다.
구체적 예: 할인 코드 추가. UI는 입력 필드를 렌더하고 업데이트된 가격을 보여줍니다. API는 code를 받아 {total, discount}를 반환합니다. 서비스가 코드 유효성과 할인 중첩 규칙을 결정하고, 레포지토리가 필요한 행을 조회·저장합니다.
생성된 앱은 빠르게 “완성된” 것처럼 보일 수 있지만, 구조가 향후 변경 비용을 낮춥니다. 먼저 무난한 규칙을 정하고, 그것들을 증명할 만큼만 코드를 생성하세요.
짧은 계획 단계를 먼저 진행하세요. Koder.ai를 사용한다면 Planning Mode에서 폴더 맵과 몇 가지 네이밍 규칙을 작성해 두는 것이 좋습니다.
그다음 다음 순서를 따르세요:
ui/, api/, data/, features/)와 몇 가지 네이밍 규칙을 고르세요.CONVENTIONS.md를 추가하고 계약처럼 다루세요. 코드베이스가 커지면 이름과 폴더 패턴 변경은 비용이 큽니다.현실 점검: 새 사람이 “연락처 편집”을 어디에 넣을지 묻지 않고 추측 못하면 아키텍처가 아직 충분히 무난하지 않은 것입니다.
간단한 CRM을 상상해보세요: 연락처 목록 페이지와 연락처 편집 폼이 있습니다. 첫 버전을 빠르게 만들고 일주일 뒤에 연락처에 “태그”를 추가해야 합니다.
앱을 UI, API, 데이터라는 세 개의 무난한 박스로 취급하세요. 각 박스는 명확한 경계와 문자 그대로의 이름을 가지면 “태그” 변경은 작아집니다.
깔끔한 레이아웃 예시:
web/src/pages/ContactsPage.tsx 와 web/src/components/ContactForm.tsxserver/internal/http/contacts_handlers.goserver/internal/service/contacts_service.goserver/internal/repo/contacts_repo.goserver/migrations/이제 “태그”는 예측 가능합니다. 스키마를 업데이트하고(새 contact_tags 테이블이나 tags 컬럼), 한 레이어씩 건드리세요: 레포는 태그 읽기/쓰기, 서비스는 검증, 핸들러는 필드 노출, UI는 렌더링과 편집. 핸들러에 SQL을 슬쩍 넣거나 React 컴포넌트에 비즈니스 규칙을 숨기지 마세요.
테스트와 픽스처는 작게 유지하고 코드 근처에 두세요:
server/internal/service/contacts_service_test.go(예: “태그 이름은 연락처별로 고유해야 함”)server/internal/repo/testdata/(최소 픽스처)web/src/components/__tests__/ContactForm.test.tsx(폼 동작)Koder.ai로 생성했다면 내보낸 후에도 같은 규칙이 적용됩니다: 폴더를 무난하게 유지하고, 이름을 문자 그대로 유지하면 편집이 고고학 작업처럼 느껴지지 않습니다.
생성된 코드는 첫날 깔끔해 보여도 나중에 비용이 클 수 있습니다. 주된 원인은 “나쁜 코드”가 아니라 일관성 부족입니다.
비싼 습관 중 하나는 생성기가 매번 구조를 발명하게 두는 것입니다. 기능마다 폴더, 네이밍 스타일, 헬퍼가 생기면 같은 일을 하는 방식이 세 가지가 됩니다. 하나의 패턴을 고르고 문서화하세요. 새 패턴은 기본값이 아니라 의식적인 변경으로 다루세요.
또 다른 함정은 레이어를 섞는 것입니다. UI 컴포넌트가 데이터베이스와 통신하거나 API 핸들러가 SQL을 만들면 작은 변경이 앱 전반에 위험한 편집으로 번집니다. 경계를 지키세요: UI는 API를 호출하고, API는 서비스를 호출하고, 서비스는 데이터 접근을 합니다.
초기에 과도한 일반화 추상화도 비용을 더합니다. 범용 BaseService나 Repository 프레임워크는 그럴듯하지만 초기 추상화는 추측입니다. 현실이 바뀌면 프레임워크와 싸우느라 배포를 못 하게 됩니다.
계속된 이름 변경과 재구성도 부채의 한 형태입니다. 파일이 매주 이동하면 사람들은 레이아웃을 신뢰하지 않게 되고 빠른 수정은 임의의 곳에 쌓입니다. 먼저 폴더 맵을 안정화하고 계획된 청크로 리팩터링하세요.
마지막으로, 반복적으로 필요하지 않은 플랫폼 코드에 주의하세요. 공유 라이브러리와 자체 도구는 반복적이고 입증된 필요가 있을 때만 가치를 냅니다. 그 전까지는 기본값을 직접적으로 유지하세요.
새 사람이 리포를 열면 빠르게 답할 수 있어야 합니다: “여기에 어디에 추가하지?”
동료(또는 미래의 당신)에게 작은 기능(예: 가입 폼에 필드 추가)을 추가해 달라고 하고 올바른 위치를 찾을 수 있는지 보세요. 빠르게 찾지 못하면 구조가 제 역할을 못 하고 있는 것입니다.
세 가지 명확한 집을 확인하세요:
플랫폼이 지원한다면 롤백 경로를 유지하세요. 구조를 실험할 때 스냅샷과 롤백은 안전한 복귀 수단이 됩니다.
스타일을 두고 토론하는 시간을 줄이고 몇 가지 결정을 내려 고정시키는 것이 유지보수를 가장 빠르게 개선합니다.
파일 위치, 네이밍, 오류 및 설정 처리 방식 같은 일상적 망설임을 제거하는 짧은 규약을 적으세요. 1분 내에 읽을 수 있을 만큼 짧게 유지하세요.
그다음 한 번 정리 작업을 해서 규칙에 맞추고 매주 재정렬을 중단하세요. 자주 재구성하면 코드가 더 보기 좋아질지는 몰라도 다음 변경을 느리게 만듭니다.
Koder.ai(koder.ai)로 빌드하는 경우, 이 규약을 시작 프롬프트로 저장하면 각 새 생성물이 같은 구조로 나오도록 도와줍니다. 도구는 빠르게 움직일 수 있지만, 변경하기 쉬운 코드를 유지하는 건 무난한 경계입니다.