사람과 에이전트가 함께 참고하는 협업 규칙 문서.
| 브랜치 | 용도 |
|---|---|
main |
배포 기준 브랜치. 직접 push 금지 |
feature/<topic> |
기능 개발, 버그 수정, 설정 변경 등 모든 작업 |
main에 직접 push하지 않는다.- 브랜치는 작업 단위로 짧게 유지한다.
Conventional Commits 형식을 따른다.
<type>: <한 줄 요약>
[본문 - 선택사항]
- 커밋 메시지의 한 줄 요약(topic)은 한국어로 작성한다.
- AI 에이전트가 커밋할 때
Co-Authored-By:트레일러를 추가하지 않는다.
| type | 사용 시점 |
|---|---|
feat |
새 기능 추가 |
fix |
버그 수정 |
refactor |
동작 변경 없는 코드 개선 |
docs |
문서만 변경 |
chore |
빌드, 설정, 의존성 변경 |
test |
테스트 추가 또는 수정 |
- PR 제목은 커밋 컨벤션과 동일한 형식(
feat: ...)을 따른다. main으로 직접 merge 전 최소 1명의 리뷰가 필요하다.- PR 본문은
.github/pull_request_template.md형식을 그대로 따른다. - PR 본문에 변경 이유와 테스트 방법을 간략히 적는다.
- 리뷰 전 로컬에서
./gradlew build가 통과해야 한다.
- PR에 SonarQube Cloud 봇 코멘트가 달리면, 코멘트 안의
14 New issues같은 이슈 링크를 눌러 상세 목록을 확인한다. - 봇 코멘트가 보이지 않으면 PR 상단의
Checks탭에서SonarCloud Code Analysis체크 실행 결과를 열고, Summary에 있는 이슈 링크를 따라간다. - 개별 이슈를 볼 때는
TODO관련 항목인지 먼저 구분하고, 실제 수정 대상과 단순 메모성 항목을 섞지 않는다. - 수정이 필요한 항목은 파일 경로, 라인, 규칙 제목을 함께 확인한 뒤 대응한다.
- SonarQube 링크 예시:
- 대시보드:
https://sonarcloud.io/dashboard?id=<project-key>&pullRequest=<pr-number> - 이슈 목록:
https://sonarcloud.io/project/issues?id=<project-key>&pullRequest=<pr-number>&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true
- 대시보드:
- Lombok을 적극적으로 활용한다.
- 일반적인 생성자 주입은
@RequiredArgsConstructor를 우선 사용한다. - 구현체가 하나인 경우에는 인터페이스를 만들지 않고 클래스로 직접 정의한다.
- 삼항 연산자는 사용하지 않는다.
if,for,while등 제어문의 본문은 한 줄이어도 반드시{}로 감싼다.- 코드 depth는 2 이하를 원칙으로 하고, 이를 넘길 경우
guard clause나 메서드 추출로 우선 단순화한다. - 코드에서는 FQCN을 직접 쓰지 않고 import를 사용한다.
주요 기능은 도메인별 패키지로 나누고, 설정, 외부 API 연동, 공통 예외 처리처럼 특정 도메인에 속하지 않는 요소는 common 아래에 둔다.
com.howaboutus.backend.
├── common/
│ ├── config/ ← Security, Jackson, Redis 등 공통 설정
│ ├── error/ ← 공통 예외, 에러 응답, 예외 핸들러
│ └── integration/ ← Google 등 외부 API 연동
└── <domain>/
├── controller/ ← Controller, Request/Response DTO
├── service/ ← Service, 비즈니스 로직
├── repository/ ← Repository 인터페이스
└── entity/ ← JPA Entity
- Markdown 문서를 새로 추가하거나 디렉토리를 정리할 때는 도메인 기준이 아니라 aggregate 기준으로 나눈다.
- 하나의 aggregate에서 함께 변경되고 함께 이해해야 하는 문서들은 같은 디렉토리 아래에 둔다.
- aggregate 경계가 명확하지 않으면 임의로 디렉토리를 만들거나 이동하지 말고, 사용자에게 어떤 기준으로 디렉토리를 구성할지 먼저 물어본다.
- aggregate와 무관한 공통 규칙, 인덱스, 결정 기록 가이드는 최상위 공통 문서나
docs/ai/decisions/처럼 목적이 분명한 공용 디렉토리에 둔다.
- 비즈니스 예외는 커스텀 예외 클래스로 정의하고
GlobalExceptionHandler에서 처리한다. - 별도의 상태 코드와 에러 메시지가 필요한 경우
CustomException클래스를 정의하고, 에러 스펙은 enum으로 관리한다. - 임의로
try-catch를 추가하지 않는다. 처리 방식이 불명확하면 먼저 보고한다.
- API 요청의 기본 형식 검증은 컨트롤러 요청 DTO에서
@Valid와 Bean Validation 어노테이션으로 처리한다. - 서비스 레이어에서는
null, blank, 문자열 패턴 같은 기본 형식 검증을 중복해서 수행하지 않는다. - 서비스 레이어는 비즈니스 규칙 검증에 집중한다. 예를 들어 소속 확인, 중복 여부, 상태 전이 가능 여부처럼 도메인 의미가 있는 검증만 둔다.
- 컨트롤러를 거치지 않는 별도 진입점이 생기면 그 경계에서 같은 수준의 입력값 검증 책임을 명시적으로 추가한다.
- 서비스가 REST 컨트롤러 외부에서도 직접 재사용된다면, 그 서비스는 "컨트롤러 전용 서비스"로 가정하지 말고 각 호출 경계 또는 서비스 자체 중 한 곳을 검증 경계로 명확히 정한다.
- 비즈니스 로직(서비스 레이어)은 단위 테스트를 작성한다.
- API 엔드포인트는 기본적으로 통합 테스트(
@SpringBootTest)로 검증한다. - 단순한 컨트롤러 레이어 검증은
@WebMvcTest를 사용할 수 있다. @WebMvcTest에서는 모킹된 응답 전체를 세세하게 검증하기보다 상태 코드와 핵심 필드 중심으로 간결하게 검증한다.- 테스트 메서드에는 반드시
@DisplayName을 사용해 테스트 의미를 짧고 명확하게 적는다. - 테스트 코드의 중복은 공통 필드,
@BeforeEach, 헬퍼 메서드 등을 활용해 줄인다. - 테스트 없이 PR을 올리지 않는다. 테스트가 어려운 경우 사유를 PR에 명시한다.