제1장: DeepSeek 깨끗한 코드의 철학적 기반
DeepSeek 깨끗한 코드는 단순한 코딩 규칙이 아닌, 소프트웨어 본질과 인지 과학을 기반으로 한 실천 철학입니다. 코드는 먼저 사람이 읽고 그 다음에 기계가 실행해야 한다는 믿음은 Kent Beck의 "독성이 옳다" 원칙과 일치하며, 대형 모델 시대에는 AI가 수백 줄의 로직을 자동 생성할 수 있는 상황에서 인간이 의도, 경계, 진화 경로를 통제하는 능력이 시스템 회복탄력성의 핵심이 되었습니다.
예측 가능성을 우선시하라
깨끗한 코드는 모든 논리가 지역 예측 가능성을 가져야 합니다. 입력, 맥락 및 계약이 주어졌을 때 출력과 부작용이 심리 모델에 따라 정확히 예측되어야 합니다. 예를 들어 상태 관리에서 암시적 의존성을 피하는 것이 중요합니다:
// ❌ 전역 시계에 의존하여 테스트 가능성과 예측 가능성 손실
func processOrder(o Order) { now := time.Now() /* ... */ }
// ✅ 명시적으로 시계 소스 주입, 계약 명확화, 시뮬레이션 및 검증 용이
func processOrder(o Order, clock Clock) { now := clock.Now() /* ... */ }
계약 중심 설계 원칙
함수/모듈은 세 가지 계약으로 고정되어야 합니다:
- 사전 조건: 호출자가 만족해야 할 입력 제약
- 사후 조건: 실행 후 항상 성립하는 상태 주장
- 불변량: 생명주기 전체를 통한 구조적 보장
| 차원 | 통상 주석 | Clean Code 계약 표현 |
|---|---|---|
| 입력 검증 | // expects non-nil user | if user == nil { panic("user required") } |
| 상태 약속 | // returns sorted slice | sort.SliceStable(items, ...); return items |
진화는 설계의 자연스러운 연장
깨끗한 코드는 리팩토링을 설계의 자연스러운 연장으로 간주합니다. 의미 있는 리팩토링은 다음을 동시에 충족해야 합니다:
- 모든 테스트가 통과해야 함 (기계적 보장)
- 최소 하나의 인지 차원 향상 (예: 순환 복잡도 감소, 숨겨진 상태 명시)
- 새로운 추상화 레벨 도입 없어야 함 (실제 결합 문제 해결 시 제외)
제2장: 이름은 계약이다 - 의미 있는 식별자 설계 원칙
2.1 변수와 함수 이름에서 영역 언어 매핑 실천
영역 개념과 식별자의 직접 매핑
기술 용어가 비즈니스 의미를 오염시키지 않도록 하세요. 예를 들어 customerCreditLimit 대신 maxCreditValue를 사용하고, 코드를 실행 가능한 영역 문서로 만듭니다.
| 영역 개념 | 권장 이름 | 비권장 이름 |
|---|---|---|
| 주문 이행 상태 | orderFulfillmentStatus | statusFlag |
| 재고 예약 | reserveInventory | lockStock |
함수 이름에서 비즈니스 의도 표현
func calculateLateFeeForOverdueInvoice(invoice *Invoice, today time.Time) Money {
// invoice: 영역 엔티티, dueDate 및 amount 필드 포함
// today: 현재 비즈니스 시계 시간점, 시스템 타임스탬프가 아님
// 반환: 영역 통화 단위(Money)로 포장된 지연료, 정밀도 및 통화 정보 포함
if today.After(invoice.DueDate) {
return invoice.Amount.Multiply(0.05) // 5% 지연료율, 비즈니스 규칙 하드코딩
}
return ZeroMoney()
}
2.2 클래스 및 모듈 이름의 책임 경계 식별과 리팩토링 사례
책임 혼란의 일반적 징후
UserManager가 데이터베이스 CRUD, 이메일 발송, 권한 검증을 모두 처리utils.py모듈 내에 비즈니스 규칙과 암호화 알고리즘 혼합
리팩토링 전후 이름 비교
| 리팩토링 전 | 리팩토링 후 |
|---|---|
| OrderHelper | OrderValidator + OrderRepository |
| core.py | payment/ + shipping/ 서브모듈 |
리팩토링 예시: 주문 상태 처리자
# 구현 전: 책임 범위 초과
class OrderProcessor:
def validate(self, order): ...
def send_notification(self, order): ... # 단일 책임 위반
def persist(self, order): ... # 계층 간 결합
# 구현 후: 명확한 경계
class OrderValidator: ...
class OrderNotifier: ...
class OrderPersistence: ...
이 리팩토링은 상태 검증, 이벤트 알림, 데이터 지속성을 독립적인 클래스로 분리하여 각 클래스가 직접 협력 객체(예: OrderNotifier는 NotificationService에만 의존)에만 의존하게 함으로써 테스트 가능성과 재사용성을 크게 개선했습니다.
2.3 헝가리 표기법과 약어 트랩 회피 실제 코드 감사
형식적 오용 사례
개발자는 strName, iCount, pConfig 등 형식적 접두사를 사용해 유형 또는 용도를 암시하지만, 현대 IDE의 유형 추론과 정적 분석이 이미 이를 불필요하게 만들었습니다.
감사 중 발견된 고위험 약어
usr→user로 통일 (완전하지 않은 스펙, 가독성 저하)tmp→cachedResult또는fallbackValue로 명확화 (의미 불명확)
리팩토링 전후 비교
| 원 코드 | 리팩토링 후 |
|---|---|
int iLen = strlen(szBuf); | int bufferLength = strlen(inputBuffer); |
func validateUser(u *usr) error { // ❌ 약어+헝가리: u 무의미, *usr 이미 포인터 표시 | func validateUser(user *User) |
논리 분석: u 파라미터 이름이 비즈니스 의미를 전달하지 않으며, usr 유형 약어가 영역 엔티티 본질을 가리킴. 변경 후 func validateUser(user *User)로, User는 완전한 명명 구조체이며, IDE에서 정확한 이동이 가능하고, 라이터가 null 포인터를 확인하며, 팀 협업에 모호함이 없음.
2.4 이름 일관성 검사: ESLint 플러그인에서 커스텀 AST 규칙까지
내장 규칙의 한계
ESLint의 기본 camelcase와 id-match는 비즈니스 전용 이름 계약을 커버하지 못합니다. 예를 들어 API 응답 필드는 snake_case 강제, 내부 변수는 PascalCase 필요.
커스텀 AST 규칙 핵심 로직
module.exports = {
meta: { type: "suggestion", docs: { description: "prop 속성 kebab-case 강제" } },
create(context) {
return {
JSXOpeningElement(node) {
const attrs = node.attributes;
attrs.forEach(attr => {
if (attr.type === "JSXAttribute" && attr.name.name.startsWith("on")) {
const eventName = attr.name.name.slice(2);
if (!/^[A-Z][a-zA-Z0-9]*$/.test(eventName)) {
context.report({ node: attr.name, message: "이벤트 핸들러 이름은 PascalCase여야 합니다" });
}
}
});
}
};
}
};
이 규칙은 JSX 요소 속성을 순회하며 on 접두사 이후 이벤트 이름을 추출하고, PascalCase (첫 글자 대문자, 하이픈/언더바 없음)에 대한 정규식을 통해 검증합니다.
규칙 등록 및 활성화 구성
- 규칙 파일을
lib/rules/prop-naming.js에 위치 index.js에서my-plugin/prop-naming으로 등록.eslintrc.js에서 활성화:"my-plugin/prop-naming": "error"
2.5 팀 이름 규약 적용: PR 템플릿에서 CI 단계 강제 검증까지
PR 템플릿 표준화
GitHub/GitLab PR 템플릿에 이름 검사 팁을 포함하여 개발자를 자가 점검하도록 유도:
## 이름 규약 확인
- [ ] 서비스명은 `team-name-service-type-vN` (예: `auth-core-api-v2`)
- [ ] 브랜치명은 `feat/auth-login-jwt` 또는 `fix/user-profile-nullptr`
이 템플릿은 협업 시작점에서 이름 요구사항을 앞당겨 후기 수정 비용을 낮춥니다.
CI 파이프라인 강제 검증
CI의 pre-build 단계에 검증 스크립트 삽입:
# .gitlab-ci.yml 또는 github-actions step
if ! [[ "$CI_COMMIT_TAG" =~ ^[a-z]+-[a-z]+-[a-z]+-v[0-9]+$ ]]; then
echo "❌ 태그 이름 규약 위반: team-service-type-vN 형식 필요";
exit 1;
fi
정규식을 통해 태그 형식을 통일하고, 실패 시 배포 프로세스를 차단합니다.
검증 규칙 대조표
| 컴포넌트 | 합격 예시 | 거부 패턴 |
|---|---|---|
| 서비스명 | billing-gateway-api-v3 | BillingGatewayV3 |
| 브랜치명 | chore/infra-logging-update | update-logger |