언어별 세부 규약은 각 레이어
CLAUDE.md에 위임. 본 문서는 모든 언어 공통 규약.
- 변수/함수: 의미 있는 영문 단어, 약어 자제
- 불리언:
is,has,can,should접두 (isCompleted,hasResume) - 컬렉션: 복수형 (
sessions,documentIds) - 상수: 언어별 컨벤션 (Java SCREAMING_SNAKE, JS UPPER_SNAKE, Python UPPER_SNAKE)
- 코드에서는 영어로 통일 (
session,interview,repository) - DB 컬럼은 snake_case
- 한국어 도메인 용어는 glossary.md 매핑 참조
허용: id, url, uri, api, db, pg, mq, ai, llm, rag, stt, tts, pdf
비권장: usr, msg, cfg, tmp → user, message, config, temp 사용
- 단일 책임: 한 함수는 한 가지 일만
- 순수 함수 우선: 입력 → 출력, 사이드이펙트 격리
- 인자 ≤ 4개: 그 이상은 객체로 묶기
- boolean 인자 지양: 호출부에서 의미 불명. enum 또는 함수 분리.
// 나쁨 createSession(true, false); // 좋음 createSession(SessionMode.ONLINE, JobCategory.BACKEND);
기본은 주석 없음. 다음 경우만 주석 작성:
| 케이스 | 예시 |
|---|---|
| 비명백한 제약 | // pgvector ivfflat은 list 수 = sqrt(rows) 권장 |
| 외부 워크어라운드 | // GitHub API 5xx 시 30초 backoff (rate-limit과 구분) |
| 의도적 트레이드오프 | // JPA cascade 사용 안 함: 명시적 삭제 흐름 유지 |
| TODO + 컨텍스트 | // TODO(US-21): STT 청크 단위 처리 |
금지:
- 코드 그대로 풀어쓴 주석 (
// loop over users) - 변경 이력·작성자 (
// 2026-04-27 수정 - 박상우) → git blame 사용 - 죽은 코드 보존 (
// 임시로 주석처리) → 삭제
- 비즈니스 규칙 위반: 도메인 예외 (
SessionNotInProgressException) - 시스템/외부 장애: 일반 예외 (
RuntimeException,IOException) - API 응답: 도메인 예외 → API error code 매핑 (api-conventions.md §5)
- 빈 catch 블록 (
} catch (Exception e) {}) - 일반
Exception을 무차별 catch - 에러 메시지에 비밀 정보 포함
- timeout 명시 (default 무한대 금지)
- retry는 idempotent 작업만, exponential backoff
- circuit breaker는 의존성 down이 전파되는 호출에 적용
| Yes | No |
|---|---|
| 비즈니스 규칙 (상태 전이, 권한) | trivial getter/setter |
| 경계값 (0, max+1, null) | 프레임워크 기능 (Spring DI 동작 등) |
| 외부 의존성 wrap (RabbitMQ publisher) | 외부 의존성 자체 (RabbitMQ 동작) |
| 회귀 케이스 (버그 fix 후) |
{메서드명_혹은_시나리오}_{조건}_{기대결과}
예:
createSession_whenUserNotConsented_throwsConsentRequiredException
analyzeResume_whenS3UploadFails_marksResumeFailed
// given
var user = aUser().build();
var request = aSessionCreateRequest().mode(ONLINE).build();
// when
var session = sessionService.create(user, request);
// then
assertThat(session.status()).isEqualTo(READY);상세 전략은 testing-strategy.md 참조.
- 표준 라이브러리로 해결 가능한지 확인
- 마지막 릴리스 날짜 (1년 이상 ago = 위험)
- 라이선스 (MIT/Apache-2.0/BSD ✓, AGPL/GPL ?)
- 별점·이슈 수
- 대체재 비교 → PR 본문에 근거 작성
- production 의존성은 정확한 버전 (caret/tilde 회피)
- 락 파일(
package-lock.json,uv.lock, gradle lock) 커밋 - Renovate / Dependabot으로 주기적 업데이트
- 한 파일 한 책임
- 줄 수 가이드: 200줄 이내 권장, 400줄 초과 시 분할 검토
- 파일명은 export 하는 핵심 식별자와 일치 (PascalCase 컴포넌트는 PascalCase 파일명)
// 나쁨
if (session.questionCount() >= 10) {...}
// 좋음
private static final int DEFAULT_MAX_QUESTIONS = 10;
if (session.questionCount() >= DEFAULT_MAX_QUESTIONS) {...}상수는 가장 가까운 도메인 클래스에 위치. 전역 Constants는 회피.
// 나쁨
public Session create(User user, boolean online, boolean technical) {...}
// 좋음
public Session create(User user, SessionMode mode, InterviewType type) {...}- 가능한 한 불변 객체 (Java
record, Kotlindata class, TSreadonly) - 컬렉션은 불변 또는 방어적 복사
- 예외: 성능 임계 경로
- 생성자 주입 (필드 주입 금지)
- 인터페이스 + 구현체 분리는 테스트 더블이 필요할 때만. 단일 구현이면 클래스 직접 사용.
- 한국어 로그 메시지 OK (개발 팀 내부 사용)
- 단, 구조화 필드 키는 영어 (
userId,sessionId) - 민감 정보 절대 X — observability.md §9
- 권장: 변경 라인 ≤ 400
- 초과 시 분할 검토 (리팩터 + 기능 분리, 도메인별 분리)
- 단일 commit ≠ 단일 PR. 한 PR에 의미 단위 commit 여러 개 OK.