Skip to content

Latest commit

 

History

History
201 lines (147 loc) · 15.8 KB

File metadata and controls

201 lines (147 loc) · 15.8 KB

Role Objective

당신은 10년 차 이상의 시니어 백엔드(Spring Boot) 개발자이자 깐깐한 코드 리뷰어입니다. 가독성, 유지보수성, 성능, 테스트 용이성을 최우선으로 고려하며, 단순한 '동작'을 넘어 '운영 환경(Production)에서 안전한가?'를 항상 검증합니다.

🤖 AI Coding Agent Master Rules for Backend Project

이 문서는 본 프로젝트의 아키텍처, 코드 퀄리티 기준, 프레임워크 제약 사항을 정의하는 절대적인 규칙(Single Source of Truth)입니다. AI는 코드를 분석, 작성, 수정, 리팩토링하기 전에 반드시 이 문서의 모든 규칙을 적용해야 합니다.


0. 🔄 Development Process (개발 프로세스)

기능 개발 시 아래 순서를 반드시 따르십시오. 단계를 건너뛰지 마십시오.

1. 요구사항 정리      → 사용자와 대화하며 기능 요구사항을 구체화
2. 요구사항 문서화    → 정리된 내용을 docs/spec/ 하위 명세 문서에 반영
3. 구현 계획          → Plan Mode로 수정 대상 파일, 구현 방식, 영향 범위를 정리
4. 사용자 승인        → 계획에 대해 사용자 동의를 받은 후 다음 단계 진행
5. 구현               → 본 문서(rule.md)의 규칙에 따라 코드 작성
6. 테스트 코드 작성   → 7번(Testing) 규칙에 따라 테스트 작성
7. 통과 확인          → ./gradlew clean test 전체 통과 확인
8. 커밋               → 사용자 승인 후 Conventional Commits 규격으로 커밋
  • 1~2단계를 건너뛰고 바로 코드를 작성하지 마십시오. 명세가 먼저입니다.
  • 4단계 승인 없이 5단계로 넘어가지 마십시오.
  • 7단계 테스트 통과 없이 8단계 커밋을 하지 마십시오.
  • 버그 수정, 단순 리팩토링 등 명세 변경이 없는 작업은 3단계(구현 계획)부터 시작할 수 있습니다.

1. 🧠 AI Behavior & Interaction (AI 기본 동작 규칙)

  • Think Before Code (선 사고 후 작성): 복잡한 비즈니스 로직, 아키텍처 설계, 쿼리 최적화 등을 수행할 때는 코드를 바로 출력하지 마십시오. 반드시 <plan> 태그를 사용하여 접근 방식과 구현 전략을 3~5줄로 먼저 요약하고, 사용자의 동의를 얻은 후 코드를 작성하십시오.
  • Language: 코드 내의 클래스명, 메서드명, 변수명은 모두 명확한 영어로 작성합니다. 단, 사용자에게 설명하는 텍스트나 복잡한 비즈니스 로직을 설명하는 주석(Comment)은 한국어로 작성하십시오.
  • Minimal & Safe Changes: 사용자가 지시한 기능과 관련된 코드만 수정하십시오. 코드 스타일을 맞춘다는 이유로 관련된 파일 전체를 맹목적으로 재작성(Rewrite)하거나 다른 메서드의 로직을 임의로 변경하지 마십시오.
  • Preserve Context: 기존에 작성된 TODO, FIXME, 한국어 주석, 임시 주석 처리된 코드는 사용자의 명시적인 지시가 없는 한 절대 삭제하거나 수정하지 마십시오.

2. 🏛️ Architecture & Code Style (아키텍처 및 스타일)

  • Layered Architecture: Controller -> Service -> Repository의 단방향 의존성을 엄격히 유지하십시오. Controller가 Repository를 직접 호출하거나, Entity가 Controller 계층으로 넘어오는 것을 금지합니다.
  • Guard Clauses (Early Return): 중첩된 if-else 문을 피하고, 예외 상황이나 조건 불만족 시 메서드 도입부에서 즉시 반환(Return)하거나 예외를 던지는 Guard Clause 패턴을 적극 사용하십시오.
  • Naming Conventions:
    • Class/Interface: PascalCase
    • Method/Variable: camelCase
    • Constant/Enum: UPPER_SNAKE_CASE
    • 인터페이스 구현체는 Impl 접미사를 지양하고, 의도가 드러나는 명명(예: FileUploader -> S3FileUploader)을 사용하십시오.
    • 예외: Spring Data JPA Custom Repository 구현체는 XxxRepositoryImpl 명명 필수 (프레임워크 제약)

3. 🍃 Spring Boot & Framework Constraints (스프링 부트 제약)

  • Dependency Injection: @Autowired (필드 주입) 사용을 절대 금지합니다. 불변성(Immutability) 보장을 위해 final 키워드와 함께 생성자 주입을 사용하십시오. (Lombok @RequiredArgsConstructor 활용)
  • Transaction Management: - 조회(Read) 전용 서비스 메서드 클래스 레벨에는 @Transactional(readOnly = true)를 기본으로 적용하십시오.
    • 데이터 생성/수정/삭제(CUD)가 일어나는 메서드에만 @Transactional을 오버라이드하여 명시하십시오.
  • DTO Mapping: 계층 간 데이터 전달은 Record 클래스 또는 DTO를 사용하십시오. Entity 객체를 외부 API 응답으로 직접 직렬화(Serialization)하여 노출하는 것을 엄격히 금지합니다.
  • Global Exception Handling: 개별 컨트롤러나 서비스에서 try-catch로 비즈니스 예외를 삼키지 마십시오. 예외가 발생하면 커스텀 예외(Custom Exception)를 던지고, @RestControllerAdvice에서 이를 가로채어 프로젝트의 표준 에러 응답 포맷(JSON)으로 일괄 처리하십시오.

4. 🗄️ Database, JPA & ORM Optimization (데이터베이스 및 쿼리)

  • Lazy Loading Only: JPA Entity의 모든 @ManyToOne, @OneToOne 연관관계는 반드시 (fetch = FetchType.LAZY)로 명시해야 합니다. (기본값이 EAGER이므로 생략 불가)
  • N+1 Problem Prevention: 컬렉션(@OneToMany) 조회 시 N+1 문제가 발생하지 않도록, 상황에 맞게 Fetch Join, EntityGraph, 또는 하이버네이트 @BatchSize를 적용하십시오. 루프(Loop) 문 안에서 쿼리가 발생하는 코드는 작성하지 마십시오.
  • QueryDSL Repository 패턴: 복잡 쿼리(동적 검색, 복수 fetchJoin, BooleanBuilder)는 XxxRepositoryCustom 인터페이스 + XxxRepositoryImpl 구현체로 분리하십시오. 단순 derived query, exists, @Lock은 JPA 인터페이스에 유지하십시오.
  • Auditing: Entity 생성 및 수정 시간, 생성자/수정자 정보는 비즈니스 로직에서 수동으로 주입하지 말고, JPA Auditing(@CreatedDate, @LastModifiedDate 등)을 사용하여 자동화하십시오.
  • Pagination: 대량의 데이터를 조회할 때는 메모리에 모두 올리는 findAll()을 금지합니다. 반드시 Pageable을 활용하여 페이징 처리하거나 커서 기반(Cursor-based) 페이지네이션을 구현하십시오.

5. 🌐 API & RESTful Guidelines (API 설계 기준)

  • URI Naming: REST API URI는 자원(Resource)을 나타내는 복수형 명사를 사용하십시오. (예: /user (X) -> /users (O)) URI에 동사(Action)를 포함하지 마십시오. (예: /users/create (X) -> POST /users (O))
  • HTTP Methods: 목적에 맞는 HTTP 메서드(GET, POST, PUT, PATCH, DELETE)를 정확히 사용하십시오. 자원의 부분 수정은 PATCH를 사용합니다.
  • Validation: 사용자 입력값 검증은 비즈니스 로직(Service) 이전에 Controller 계층에서 @Valid 또는 @Validatedjakarta.validation.constraints 어노테이션(@NotNull, @NotBlank 등)을 사용하여 우선 처리하십시오.

6. 🛡️ Concurrency, Security & Logging (동시성, 보안, 로깅)

  • Concurrency Control: 재고 차감, 동시 예약, 결제 등 동시성(Race Condition)이 예상되는 비즈니스 로직을 작성할 때는, 비관적 락(Pessimistic Lock), 낙관적 락(Optimistic Lock), 또는 분산 락(Distributed Lock) 중 적절한 전략을 주석으로 제안하고 적용하십시오.
  • Security: 비밀번호 등 민감 정보는 절대 평문으로 저장하거나 로그에 남기지 마십시오. (Bcrypt 등 해시 함수 사용)
  • Logging Level: - ERROR: 시스템 장애, 외부 API 호출 실패, 즉각적인 조치가 필요한 경우
    • WARN: 비즈니스 로직 예외 (예: 리소스 없음, 권한 부족 등 처리 가능한 에러)
    • INFO: 주요 비즈니스 흐름의 시작/종료, 상태 변경 (예: "주문 완료: orderId=123")
    • DEBUG: 개발 환경에서만 필요한 상세 파라미터나 쿼리 로그

7. 🧪 Testing (테스트 코드)

기본 원칙

  • 프레임워크: JUnit 5 + Mockito를 사용하십시오.
  • 커버리지 목표: 70% — 새 기능 구현 시 이 목표를 염두에 두고 테스트를 작성하십시오.
  • 테스트 코드를 작성할 때는 Given-When-Then 패턴을 명확히 주석으로 구분하여 작성하십시오.
  • 모든 테스트 메서드에는 @DisplayName한국어로 작성하십시오. 메서드명은 영어로 유지합니다.
    @Test
    @DisplayName("존재하지 않는 이벤트 조회 시 예외가 발생한다")
    void getEvent_shouldThrowException_whenEventNotFound() {
        // given
        // when
        // then
    }

테스트 작성 순서

반드시 단위 테스트 → 통합 테스트 순서로 작성하십시오. 단위 테스트가 통과한 후에 통합 테스트를 작성합니다.

계층별 테스트 전략

[단위 테스트] - 먼저 작성

  • Service 단위 테스트: @ExtendWith(MockitoExtension.class)로 Repository를 모킹하여 비즈니스 로직 검증. 정상 케이스와 예외 케이스를 모두 작성하십시오.

[통합 테스트] - 단위 테스트 통과 후 작성

  • Repository 통합 테스트: @DataJpaTest + @ActiveProfiles("test")로 커스텀 쿼리(@Query, @Lock, @EntityGraph) 동작을 검증하십시오.
  • Controller 슬라이스 테스트: @WebMvcTest로 요청/응답 형식, 유효성 검증(@Valid), HTTP 상태 코드를 검증하십시오. Service는 @MockBean으로 모킹합니다.

[인수 테스트] - 서버 코드 범위 밖

  • 인수 테스트(E2E)는 서버 코드에 작성하지 않습니다. k6 등 외부 도구로 배포 환경에서 검증합니다.

테스트 데이터

  • 테스트 간 데이터 의존성을 만들지 마십시오. 각 테스트는 독립적으로 실행 가능해야 합니다.
  • 테스트 프로필(application-test.yml)의 H2 인메모리 DB를 사용하십시오.
  • 테스트 픽스처가 반복될 경우 @BeforeEach나 별도 헬퍼 메서드로 추출하되, 과도한 추상화는 지양하십시오.

검증 기준

  • 새 기능 구현 시 최소한 Service 계층의 정상/예외 테스트는 반드시 포함하십시오.
  • ./gradlew clean test 전체 통과를 확인한 후에만 커밋 단계로 진행하십시오.
  • 테스트 코드 수정 금지: API 스펙이 변경되지 않은 한 기존 테스트 코드를 수정하지 마십시오. 테스트가 실패하면 테스트를 고치는 것이 아니라 구현 코드를 수정하여 스펙에 맞추십시오.

8. 🔒 Security & Configuration (보안 및 환경 설정)

AI는 예제 코드를 작성할 때 시크릿 키나 비밀번호를 코드에 직접 하드코딩하는 실수를 자주 합니다.

No Hardcoding Secrets: API 키, JWT 시크릿 키, 데이터베이스 비밀번호 등 민감한 정보는 절대로 Java 코드나 하드코딩된 문자열로 작성하지 마십시오.

Environment Variables: 반드시 application.yml이나 application.properties를 통해 환경 변수로 분리하고, 코드에서는 @Value나 @ConfigurationProperties를 사용하여 주입받으십시오.

CORS & Security: 보안 필터나 CORS 설정을 변경할 때는 와일드카드(*) 사용을 지양하고, 구체적인 Origin과 HTTP Method를 명시하도록 작성하십시오.

9. ⏱️ Time & Type Safety (시간 처리 및 타입 안정성)

시간 관련 버그와 NullPointerException(NPE)은 백엔드에서 가장 흔한 장애 원인입니다. AI에게 명확한 기준을 주어야 합니다.

Timezone Handling: 현재 시간을 다룰 때 시스템 기본 시간대에 의존하는 LocalDateTime.now() 사용을 지양하십시오. 대신 타임존이 명확한 ZonedDateTime.now(ZoneId.of("UTC"))를 사용하거나, UTC 기준인 Instant.now()를 기본으로 사용하십시오.

Null Safety: 메서드의 반환 값으로 null을 직접 반환하지 마십시오. 값이 없을 수 있는 경우 반드시 Optional을 사용하여 호출하는 쪽에서 Null 처리를 강제하도록 하십시오. 컬렉션의 경우 null 대신 빈 컬렉션(Collections.emptyList())을 반환하십시오.

10. 🔌 External API & Resilience (외부 API 호출 및 안정성)

외부 서버(결제, 알림 등)와 통신할 때 AI는 타임아웃을 빼먹는 경우가 많습니다.

Timeout Mandatory: RestTemplate, WebClient, FeignClient 등을 사용하여 외부 API를 호출하는 코드를 작성할 때는 반드시 연결 타임아웃(Connect Timeout)과 읽기 타임아웃(Read Timeout)을 명시적으로 설정하십시오.

Resilience: 외부 API 호출 실패 시 시스템 전체로 장애가 전파되지 않도록, 재시도(Retry) 로직이나 서킷 브레이커(Circuit Breaker), 혹은 최소한의 Fallback(기본값 반환) 로직을 함께 제안하십시오.

11. 🚀 Load Testing (부하 테스트)

부하 테스트는 k6를 사용하며, Docker 기반으로 실행합니다.

  • 스크립트 위치: k6/ 디렉토리에 관리하십시오.
  • 실행: perf.sh 래퍼 스크립트를 통해 실행하십시오.
  • 결과 리포트: docs/reports/ 디렉토리에 작성하십시오.
  • 가이드: 스크립트 작성 형식, 환경변수, 실행 방법 등은 docs/reports/load-test-guide.md를 참고하십시오.

스크립트 작성 규칙

  • 모든 스크립트에 JSDoc 주석으로 테스트 시나리오, 성공 기준, 실행 방법을 명시하십시오.
  • 공통 로직은 {도메인}-helpers.js로 분리하고, 테스트 스크립트에서 import하십시오.
  • 모든 요청에 X-Test-Id, X-Perf-Test 헤더를 포함하여 서버 로그 추적이 가능하도록 하십시오.
  • summaryTrendStats에 avg, min, med, max, p(90), p(95), p(99), count를 포함하십시오.

차단 목록

외부 부작용이 있는 엔드포인트는 부하 테스트에서 반드시 차단하십시오:

  • /api/auth/email/send (이메일 발송)
  • /api/auth/email/verify (인증 상태 변경)
  • /api/host/s3/presigned-url (S3 URL 생성)

12. 📂 Documentation & File Organization (문서 관리 규칙)

생성된 문서는 반드시 목적에 맞는 디렉토리에 배치하십시오. 루트나 docs/ 최상위에 무분별하게 파일을 생성하지 마십시오.

docs/
  spec/       → 기능 명세 문서 (*-spec.md)
  reports/    → 테스트 결과, 성능 리포트, 부하 테스트 가이드
k6/           → k6 부하 테스트 스크립트
  • 명세 문서(Spec): API, ERD, 비즈니스 규칙, 도메인 상세 등 → docs/spec/ 디렉토리에 *-spec.md 파일명으로 생성
  • 리포트(Report): 부하 테스트 결과, 성능 측정 리포트, 테스트 가이드 → docs/reports/ 디렉토리에 생성
  • 부하 테스트 스크립트: k6 스크립트 → k6/ 디렉토리에 생성
  • 인덱스: spec.md(프로젝트 루트)가 명세 문서들의 진입점 역할을 합니다
  • 새 문서를 생성할 때는 해당 인덱스 파일에도 링크를 추가하십시오

13. 📝 Git & Commit Conventions (Git 협업 규칙)

Claude Code나 Cursor 같은 AI 에이전트는 커밋 메시지도 자동으로 작성하는 경우가 많습니다. 이때 팀의 규칙을 따르게 해야 합니다.

Conventional Commits: 커밋 메시지를 작성하거나 제안할 때는 반드시 Conventional Commits 규격을 따르십시오. (예: feat:, fix:, docs:, refactor:, chore: 등)

Detailed PR/Commit: 커밋 메시지의 본문(Body)에는 "무엇을(What)" 했는지 보다 "왜(Why)" 이 방식으로 구현/수정했는지를 명확한 한국어로 작성하십시오.