This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
읽는 순서: 이 파일(전체 맥락) →
src/main/java/com/devpick/CLAUDE.md(도메인/DB 상세)
# 빌드 + 전체 테스트 (PR 전 필수)
./gradlew build --no-daemon
# 테스트만 실행
./gradlew test --no-daemon
# 단일 테스트 클래스 실행
./gradlew test --tests "com.devpick.domain.user.service.AuthServiceTest" --no-daemon
# 단일 테스트 메서드 실행
./gradlew test --tests "com.devpick.domain.user.service.AuthServiceTest.signup_duplicateEmail_throwsException" --no-daemon
# 테스트 커버리지 리포트 생성 (build/reports/jacoco/test/index.html)
./gradlew jacocoTestReport --no-daemon
# QueryDSL Q클래스 재생성
./gradlew compileJava --no-daemon# EC2/운영: PostgreSQL + 앱만, Redis는 ElastiCache (.env에 REDIS_HOST 등, 구성 엔드포인트 clustercfg.*)
# compose가 SPRING_PROFILES_ACTIVE=docker,elasticache 로 클러스터 Redis 연결(application-elasticache.yml)
# 로컬에서 Docker Redis까지 쓸 때: docker-compose.local.yml 병합 (profile docker 만 — standalone Redis)
DB_PASSWORD=... REDIS_HOST=... JWT_SECRET=... \
RESEND_API_KEY=... GITHUB_CLIENT_ID=... GITHUB_CLIENT_SECRET=... \
GOOGLE_CLIENT_ID=... GOOGLE_CLIENT_SECRET=... \
docker compose up -d
# 로컬: Postgres + Docker Redis + 앱 (ElastiCache 없이)
docker compose -f docker-compose.yml -f docker-compose.local.yml up -d --build
# DB/캐시만 (앱은 로컬에서 직접 실행할 때) — Redis 컨테이너 포함
docker compose -f docker-compose.yml -f docker-compose.local.yml up -d postgres redis
# 종료
docker compose down환경변수는 .env.example 참고.
테스트 실패 시 원인 확인: build/reports/tests/test/index.html
EC2에서 Nginx(TLS) 뒤 Spring Boot(8080)로 서비스할 때, 브라우저·프론트 env에 넣을 공개 origin은 아래와 같다. (sslip.io + Elastic IP 기준, 경로 제외.)
https://3-39-96-126.sslip.io
IP·sslip 호스트가 바뀌면 Certbot·Nginx·프론트 설정을 같이 맞춘다. 상세는 AGENTS.md §8.
authored-by claude,co-authored-by claude, session URL, 기타 Claude 관련 내용 절대 포함 금지- 형식:
DP-{티켓번호}: {작업 내용}(예:DP-177: 이메일 회원가입 API 개발)
PR을 올리기 전에 반드시 아래 순서대로 로컬 검증을 완료해야 한다.
./gradlew build --no-daemon- 빌드 실패 또는 테스트 실패 시 PR 생성 금지 — 원인 수정 후 재시도
- 실패 원인은
build/reports/tests/확인
PR 생성 전 아래 항목을 코드에서 직접 눈으로 확인한다:
| 항목 | 확인 방법 |
|---|---|
| 신규 코드 커버리지 ≥ 80% | 새 Service/Controller 메서드마다 테스트 케이스 존재 여부 |
| 코드 중복률 ≤ 3% | 복붙한 코드 없는지, 공통 추상 클래스로 추출 가능한지 |
| Security Hotspot | csrf().disable() 등에 // NOSONAR java:Sxxxx 주석 추가 여부 |
| 버그/취약점 0개 | Null 체크, 하드코딩 비밀번호, SQL 인젝션 없는지 |
위 두 단계 모두 통과한 경우에만 PR을 생성한다.
왜 중요한가: CI가 실패하면 auto-merge가 블로킹되고, SonarCloud Quality Gate 실패 시 PR이 develop에 머지되지 않는다. 로컬 검증 없이 PR을 올리면 반복 수정 커밋이 생겨 히스토리가 지저분해진다.
모든 PR은 SonarCloud Quality Gate를 통과해야 머지 가능. 코드 작성 전/후 반드시 아래 기준 확인.
| 항목 | 기준 | 자주 걸리는 원인 |
|---|---|---|
| 코드 중복률 | ≤ 3% | 유사한 클래스 여러 개 생성, 복붙 코드 |
| Security Hotspot | 미검토 0개 | csrf().disable() → // NOSONAR java:S4502 필요 |
| 버그 | 0개 | Null 체크 누락, 잘못된 타입 사용 |
| 취약점 | 0개 | 하드코딩된 비밀번호, SQL 인젝션 등 |
중복 코드 방지 규칙:
- 이미 있는 베이스 클래스 새로 만들지 말 것 —
global/entity/BaseTimeEntity.java존재,BaseEntity생성 금지 - 테스트 셋업 코드(MockMvc, ObjectMapper 초기화)가 여러 파일에 반복되면 감지됨
- 새 클래스 작성 전
global/,common/디렉토리에 동일 역할 클래스 없는지 확인
Security Hotspot 처리:
.csrf(AbstractHttpConfigurer::disable) // NOSONAR java:S4502auto/feature/DP-* 및 auto/fix/DP-* 브랜치는 아래 두 GitHub Actions job이 모두 초록색이어야 자동 머지됨:
| Job | 파일 | 블로킹 조건 |
|---|---|---|
Build & Test |
ci.yml / build-test job |
빌드 실패 또는 테스트 실패 시 블로킹 |
SonarCloud Quality Gate |
ci.yml / sonar job |
Quality Gate ERROR/WARN 시 exit 1로 블로킹 |
SonarCloud Quality Gate 실패 원인 TOP 3:
- 신규 코드 커버리지 < 80% → 새 Service/Controller에 단위 테스트 반드시 작성
- 코드 중복률 > 3% → 공통 추상 클래스 재사용, 복붙 금지
- Security Hotspot 미검토 →
// NOSONAR java:Sxxxx주석 추가
SonarCloud CI 타임아웃 대응 (TRB-005): 별도 polling 스크립트 대신 scanner 자체 wait 옵션 사용:
./gradlew sonar --no-daemon -Dsonar.qualitygate.wait=trueSTATUS=NONE 타임아웃 오탐 방지 — scanner가 직접 QG 결과를 폴링함.
GitHub branch protection 설정 (수동):
develop 브랜치 보호 규칙에서 Required status checks:
Build & TestSonarCloud Quality Gate두 항목 모두 체크되어 있어야 auto-merge가 Quality Gate를 기다림.
Trace — 개발자 성장형 통합 플랫폼
개발 콘텐츠 탐색 → AI 요약/질문 → 커뮤니티 소통 → 성장 기록/리포트를 하나의 흐름으로 연결
이 레포는 Spring Boot REST API 서버다.
- 담당: 홍근 (백엔드 메인), 하영 (백엔드 서브)
- MVP 데드라인: 2026-04-13
브라우저 → Nginx → Next.js (프론트, :3000)
→ Spring Boot (백엔드, :8080) → PostgreSQL (:5432)
→ DynamoDB (AWS)
→ Redis (:6379)
→ FastAPI AI 서버 (:8000)
| 구분 | 기술 | 버전 |
|---|---|---|
| 언어 | Java | 21 (LTS) |
| 프레임워크 | Spring Boot | 3.5.11 (Spring Framework 6.x) |
| ORM | JPA/Hibernate + QueryDSL | - |
| 빌드 | Gradle | 최신 |
| DB (구조화) | PostgreSQL | 16 (AWS RDS) |
| DB (비정형) | DynamoDB | AWS (AI 요약, 퀴즈, RAG, 이벤트 로그) |
| 캐시 | Redis | 7 |
| 웹서버 | Nginx | 최신 |
| CI/CD | GitHub Actions | - |
| 인프라 | Docker + AWS EC2 | - |
com.devpick
├── domain
│ ├── user # 사용자/프로필/소셜로그인/토큰
│ │ ├── controller
│ │ ├── service
│ │ ├── repository
│ │ ├── entity
│ │ └── dto
│ ├── content # 콘텐츠 피드/스크랩/좋아요/AI요약/AI퀴즈 (구현 완료)
│ │ └── collector/ # CollectedContent, NormalizedContentDto, StackOverflowCollector
│ │ └── document/ # AiSummaryDocument, AiQuizDocument (DynamoDB)
│ │ └── client/ # AiServerClient (퀴즈 등 FastAPI; 요약은 배치→Dynamo, 조회만 Spring)
│ │ └── entity/ # Content, ContentSource, Like, Scrap, QuizAttempt
│ │ └── repository/ # AiQuizRepository, QuizAttemptRepository
│ ├── community # 게시글/답변/AI질문개선 (구현 완료)
│ │ ├── controller/ # PostController, AnswerController, AiQuestionController, CommentController
│ │ └── client/ # AiQuestionClient (FastAPI /refine 호출)
│ ├── report # 주간 리포트 + 학습 히스토리 (구현 완료)
│ │ ├── document/ # ReportInsightDocument (DynamoDB)
│ │ └── repository/ # WeeklyReportRepository, ReportActivityRepository, HistoryRepository, ReportInsightRepository
│ │ # ※ history 패키지는 설계상 분리 예정이나 현재 report 하위에 있음
│ └── point # 포인트 적립/조회 + 배지 시스템 (구현 완료, DP-269)
│ ├── controller/ # PointController ← 신규 (미커밋)
│ ├── service/ # PointService, BadgeService
│ ├── repository/ # PointLogRepository, UserBadgeRepository, BadgeRepository
│ ├── dto/ # PointSummaryResponse, PointHistoryResponse, PointHistoryItem, BadgeResponse
│ └── entity/ # PointLog, UserBadge, Badge, PointAction(enum), BadgeSeeder
├── global
│ ├── common
│ │ ├── exception # DevpickException, ErrorCode enum, GlobalExceptionHandler
│ │ └── response # ApiResponse<T> record
│ ├── entity # BaseTimeEntity, BaseCreatedEntity
│ ├── config # SecurityConfig, CorsConfig, SwaggerConfig, WebClientConfig
│ └── security # JwtTokenProvider, JwtAuthenticationFilter
현재 작업 브랜치:
developV2
develop: MVP 완성본 고정 (건드리지 않음)developV2: MVP 이후 추가 기능 작업 기준 브랜치. 모든 신규 작업은 여기서 시작.
# developV2에서 시작 (develop/main 직접 작업 금지)
git checkout developV2
git pull origin developV2
git checkout -b feature/DP-{티켓번호}-{기능명}
# 예시
git checkout -b feature/DP-177-이메일-회원가입-API| 브랜치 | 용도 |
|---|---|
main |
배포용. 직접 push 절대 금지 |
develop |
MVP 완성본. 직접 작업 금지 |
developV2 |
MVP 이후 추가 기능 통합. PR 머지 대상 |
feature/DP-{번호}-{기능명} |
기능 개발 (사람이 직접 작업) |
auto/feature/DP-{번호}-{기능명} |
Claude Code 신규 기능 자동화. CI 통과 시 developV2에 자동 머지 |
auto/fix/DP-{번호}-{설명} |
Claude Code 버그 수정 자동화. CI 통과 시 developV2에 자동 머지 |
hotfix/DP-{번호}-{설명} |
긴급 버그 수정 |
DP-{티켓번호}: {작업 내용}
예: DP-177: 이메일 회원가입 API 개발
[DP-{티켓번호}] {설명}
예: [DP-177] 이메일 회원가입 API 구현
머지 조건: AC 충족 + 팀원 1명 이상 리뷰 승인 + CI 통과
Base URL: https://api.devpick.kr/v1
인증: Authorization: Bearer {access_token}
{
"success": true,
"data": { }
}
data가 null이면@JsonInclude(NON_NULL)설정으로 필드 자체가 응답에서 제외됨
{
"success": false,
"error": {
"code": "AUTH_001",
"message": "로그인이 필요합니다",
"detail": "Access Token이 만료되었습니다"
}
}| 접두사 | 도메인 |
|---|---|
AUTH_ |
인증 |
USER_ |
사용자 |
CONTENT_ |
콘텐츠 |
AI_ |
AI 기능 |
COMMUNITY_ |
커뮤니티 |
POINT_ |
포인트 |
BADGE_ |
배지 |
| 코드 | 의미 |
|---|---|
| 200 | 성공 |
| 201 | 생성 성공 |
| 400 | 잘못된 요청 |
| 401 | 인증 필요 |
| 403 | 권한 없음 |
| 404 | 찾을 수 없음 |
| 500 | 서버 오류 |
- 스타일: Google Java Style Guide, 줄 길이 120자 이하
- DTO:
record사용 권장 - 예외: 반드시 커스텀 예외 클래스 사용 (ADR-003 기반)
- 공통 응답:
ApiResponse<T>래퍼 클래스 사용
| 대상 | 규칙 | 예시 |
|---|---|---|
| 클래스 | PascalCase | UserService |
| 메서드/변수 | camelCase | getUserById |
| 상수 | UPPER_SNAKE_CASE | MAX_TOKEN_SIZE |
| 테이블/컬럼 | snake_case | user_id |
| URL | kebab-case | /auth/sign-up |
- API 응답 DTO의 날짜-시간 필드는 반드시
Instant타입 사용 - API 요청 파라미터는
OffsetDateTime사용 (@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)) - 직렬화 결과:
2026-04-06T12:00:00Z형식 (ISO 8601, Z suffix = UTC) LocalDateTime은 타임존 정보가 없으므로 응답 DTO에 절대 사용 금지- 엔티티/내부 로직/MongoDB document는
LocalDateTime유지 가능. DTO 변환 시entity.getCreatedAt().toInstant(ZoneOffset.UTC)패턴 사용 - JacksonConfig에 전역 설정됨 — 별도
@JsonFormat불필요
모든 비즈니스 예외는 DevpickException에 ErrorCode enum을 넘겨 던진다. ErrorCode는 HTTP 상태 코드, 에러 코드 문자열, 메시지를 함께 보유한다.
// 새 에러 코드 추가 위치: global/common/exception/ErrorCode.java
AUTH_DUPLICATE_EMAIL(HttpStatus.CONFLICT, "AUTH_004", "이미 사용 중인 이메일입니다."),
// 예외 던지기
throw new DevpickException(ErrorCode.AUTH_DUPLICATE_EMAIL);GlobalExceptionHandler가 DevpickException을 잡아 ApiResponse.fail(code, message)로 변환한다. 새 예외 타입을 추가할 때는 GlobalExceptionHandler도 함께 수정하지 않아도 된다 — DevpickException + ErrorCode 조합으로 처리된다.
// 성공 응답 (data 포함)
return ApiResponse.ok(authService.signup(request));
// 성공 응답 (data 없음)
return ApiResponse.ok(null); // 또는 ApiResponse.ok()컨트롤러에서 HTTP 상태 코드는 @ResponseStatus로 선언한다 (@ResponseStatus(HttpStatus.CREATED)).
JPA 엔티티는 두 베이스 클래스 중 하나를 상속한다. 생성자는 protected로 막고, static factory method 또는 @Builder를 사용한다.
| 베이스 클래스 | 포함 필드 | 사용 대상 |
|---|---|---|
BaseTimeEntity |
id(UUID), createdAt, updatedAt |
수정 가능한 일반 엔티티 (User, Content, Post 등) |
BaseCreatedEntity |
id(UUID), createdAt |
불변 이력 테이블 (Like, Scrap, PostLike, AnswerLike 등) |
BaseEntity는 존재하지 않음 — 절대 새로 생성 금지
테스트 프로파일은 application-test.yml을 사용하며, @ActiveProfiles("test") 없이 src/test/resources에서 자동 로드된다.
@ExtendWith(MockitoExtension.class)
class AuthServiceTest {
@InjectMocks private AuthService authService;
@Mock private UserRepository userRepository;
@Mock private PasswordEncoder passwordEncoder;
@Test
@DisplayName("이메일 중복 - AUTH_DUPLICATE_EMAIL 예외가 발생한다")
void signup_duplicateEmail_throwsException() {
// given
given(userRepository.existsByEmail(any())).willReturn(true);
// when & then
assertThatThrownBy(() -> authService.signup(request))
.isInstanceOf(DevpickException.class)
.satisfies(e -> assertThat(((DevpickException) e).getErrorCode())
.isEqualTo(ErrorCode.AUTH_DUPLICATE_EMAIL));
}
}@WebMvcTest(AuthController.class)
class AuthControllerTest {
@Autowired private MockMvc mockMvc;
@Autowired private ObjectMapper objectMapper;
// 컨트롤러에 주입된 서비스는 모두 @MockitoBean으로 등록 (Spring Boot 3.4+)
@MockitoBean private AuthService authService;
@MockitoBean private EmailVerificationService emailVerificationService;
@Test
@WithMockUser // Spring Security 인증 우회
void signup_success() throws Exception {
mockMvc.perform(post("/auth/signup")
.with(csrf()) // CSRF 토큰 (SecurityConfig에서 disable해도 WebMvcTest는 필요)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.success").value(true));
}
}주의: @WebMvcTest에서 컨트롤러에 주입된 모든 서비스를 @MockitoBean으로 등록하지 않으면 컨텍스트 로딩 실패.
| Method | Endpoint | 설명 | 인증 | 담당 |
|---|---|---|---|---|
| POST | /auth/signup |
이메일 회원가입 | X | 하영 (DP-177) |
| POST | /auth/email/send |
이메일 인증 코드 발송 | X | 하영 (DP-178) |
| POST | /auth/email/verify |
이메일 인증 코드 검증 | X | 하영 (DP-178) |
| POST | /auth/login |
이메일 로그인 | X | 하영 (DP-180) |
| POST | /auth/logout |
로그아웃 | O | 하영 (DP-185) |
| POST | /auth/refresh |
Access Token 재발급 | X | 하영 (DP-181) |
| GET | /auth/github |
GitHub 소셜 로그인 (redirect) | X | 하영 (DP-183) |
| GET | /auth/github/callback |
GitHub OAuth 콜백 | X | 하영 (DP-183) |
| GET | /auth/google |
Google 소셜 로그인 (redirect) | X | 하영 (DP-184) |
| GET | /auth/google/callback |
Google OAuth 콜백 | X | 하영 (DP-184) |
| POST | /auth/recover |
탈퇴 계정 복구 (이메일) | X | 홍근 (DP-189) |
| POST | /auth/social/recover |
탈퇴 계정 복구 (소셜) | X | 홍근 (DP-189) |
| GET | /users/me |
내 프로필 조회 | O | 홍근 (DP-187) |
| PUT | /users/me |
내 프로필 수정 | O | 홍근 (DP-187) |
| DELETE | /users/me |
회원 탈퇴 (soft delete) | O | 홍근 (DP-189) |
| Method | Endpoint | 설명 | 인증 | 담당 |
|---|---|---|---|---|
| GET | /contents |
개인화 피드 (?page=0&size=10) |
O | 홍근 (DP-204) |
| GET | /contents/{contentId} |
글 상세 | O | 홍근 (DP-205) |
| GET | /contents/search |
글 검색 | O | 홍근 (DP-210) |
| POST | /contents/{contentId}/scrap |
스크랩 | O | 홍근 (DP-207) |
| DELETE | /contents/{contentId}/scrap |
스크랩 취소 | O | 홍근 (DP-207) |
| POST | /contents/{contentId}/like |
좋아요 | O | 홍근 (DP-208) |
| DELETE | /contents/{contentId}/like |
좋아요 취소 | O | 홍근 (DP-208) |
| GET | /contents/{contentId}/recommendations |
태그 기반 추천 콘텐츠 | O | 홍근 |
| Method | Endpoint | 설명 | 인증 | 담당 |
|---|---|---|---|---|
| POST | /internal/contents |
AI 레포가 수집한 콘텐츠 일괄 수신 → PostgreSQL 저장 | X(내부) | 홍근 (DP-289) |
| Method | Endpoint | 설명 | 인증 | 담당 |
|---|---|---|---|---|
| GET | /contents/{contentId}/summary |
레벨별 AI 요약 조회 (Redis→DynamoDB, 없으면 202) | O | 홍근 (DP-221) |
| GET | /contents/{contentId}/quiz |
레벨별 AI 퀴즈 조회 (?level=JUNIOR) |
O | 홍근 |
| POST | /contents/{contentId}/quiz/submit |
퀴즈 결과 제출 (통과 시 히스토리 + 포인트) | O | 홍근 |
| Method | Endpoint | 설명 | 인증 | 담당 |
|---|---|---|---|---|
| POST | /posts |
질문 작성 + 커뮤니티 동시 게시 | O | 홍근 (DP-229) |
| GET | /posts |
게시글 목록 | O | 홍근 |
| GET | /posts/{postId} |
게시글 상세 | O | 홍근 |
| PUT | /posts/{postId} |
게시글 수정 | O | 홍근 |
| DELETE | /posts/{postId} |
게시글 삭제 | O | 홍근 |
| POST | /posts/refine |
AI 질문 개선 | O | 홍근 (DP-230) |
| GET | /posts/{postId}/similar |
유사 질문 조회 | O | 수헌 (DP-235) |
| POST | /posts/{postId}/ai-answer |
AI 답변 생성 | O | 홍근/수헌 (DP-233) |
| POST | /posts/{postId}/answers |
답변 작성 | O | 홍근 (DP-239) |
| PUT | /posts/{postId}/answers/{answerId} |
답변 수정 | O | 홍근 |
| DELETE | /posts/{postId}/answers/{answerId} |
답변 삭제 | O | 홍근 |
| POST | /posts/{postId}/answers/{answerId}/adopt |
답변 채택 | O | 홍근 (DP-239) |
| POST | /posts/{postId}/answers/{answerId}/comments |
댓글 작성 | O | 하영 (DP-240) |
| DELETE | /posts/{postId}/answers/{answerId}/comments/{commentId} |
댓글 삭제 | O | 하영 |
| Method | Endpoint | 설명 | 인증 | 담당 |
|---|---|---|---|---|
| GET | /history |
히스토리/활동 통합 조회 (actionTypes, startDate, endDate 필터) |
O | 하영 (DP-248) |
| Method | Endpoint | 설명 | 인증 | 담당 |
|---|---|---|---|---|
| GET | /users/me/points |
누적 포인트, 이번 주 포인트, 연속 로그인 일수 조회 | O | 홍근 |
| GET | /users/me/points/history |
포인트 적립 내역 페이징 조회 | O | 홍근 |
| GET | /users/me/badges |
전체 배지 목록 (획득 여부 포함) | O | 홍근 |
| Method | Endpoint | 설명 | 인증 | 담당 |
|---|---|---|---|---|
| GET | /reports/weekly/list |
내 리포트 목록 (드롭다운용) | O | 홍근 (DP-256) |
| GET | /reports/weekly |
이번 주 리포트 | O | 홍근 (DP-256) |
| GET | /reports/weekly/{reportId} |
특정 주 리포트 | O | 홍근 (DP-256) |
| POST | /reports/weekly/{reportId}/share |
공유 링크 생성 | O | 홍근 (DP-258) |
| GET | /reports/weekly/share/{token} |
공유 링크로 리포트 조회 | X | 홍근 (DP-258) |
| 도메인 | 구현 상태 | 주요 티켓 |
|---|---|---|
| user (인증/프로필) | ✅ 완료 | DP-177~189, DP-196 |
| content (피드/스크랩/좋아요/검색) | ✅ 완료 | DP-204~210, DP-289 |
| content (AI 요약) | ✅ 완료 | DP-221 |
| content (AI 퀴즈) | ✅ 완료 (미커밋) | — |
| community (게시글/답변/AI질문개선) | ✅ 완료 | DP-229~240 |
| report (주간 리포트 + 히스토리) | ✅ 완료 | DP-248 |
| point (포인트/배지) | ✅ 완료 | DP-269 |
티켓별 상세 진행 상황은 Jira (프로젝트: DevPick) 참고
| ADR | 결정 | 상태 |
|---|---|---|
| ADR-001 | PostgreSQL(구조화) + DynamoDB(AI JSON/이벤트/RAG) 분리 | 확정 |
| ADR-002 | JWT (Access + Refresh Token) | 제안됨 |
| ADR-003 | API 에러 포맷: {success, error:{code,message,detail}} |
제안됨 |
| ADR-005 | Feature Flag: dp.{영역}.{기능명} |
미결 |
| ADR-007 | Redis 캐시: summary 7일, feed 10분, report 7일 | 제안됨 |
| Flag | 기능 |
|---|---|
dp.ai.summary |
AI 요약 |
dp.ai.question_refine |
AI 질문 개선 |
dp.ai.quiz |
AI 퀴즈 |
dp.reports.weekly |
주간 리포트 |
- 도구: JUnit 5 + Mockito (단위), Spring Boot Test + MockMvc (API)
- 커버리지 목표: 신규 코드 80% 이상 (SonarCloud Quality Gate 기준)
- CI 트리거: PR → develop / PR → main 시 자동 실행
- 테스트 패턴: given / when / then 구조
| 서비스 | 포트 |
|---|---|
| Spring Boot (이 서버) | 8080 |
| Next.js 프론트엔드 | 3000 |
| FastAPI AI 서버 | 8000 |
| PostgreSQL | 5432 |
| DynamoDB | (AWS) |
| Redis | 6379 |
| 문서 | 내용 |
|---|---|
src/main/java/com/devpick/CLAUDE.md |
도메인/DB 구조 상세 |
TRB.md |
트러블슈팅 로그 전체 (TRB-001 ~ TRB-005) |
docs/통신.md |
FastAPI ↔ Spring 서버 간 통신 스펙 (POST /internal/contents, Spring→FastAPI POST /internal/* + X-Internal-Key) |
docs/proposal.md |
캡스톤 제안서 초안 |
hong.md |
팀원 하영 온보딩 가이드 |
.env.example |
환경변수 목록 |
.github/PULL_REQUEST_TEMPLATE.md |
PR 작성 양식 |
| Confluence ADR | 기술 결정 기록 |
/feature-dev DP-177 입력
→ Jira 티켓 자동 읽기 (제목/설명/AC) + In Progress 전환
→ auto/feature/DP-177-email-signup-api 브랜치 생성 (GitHub API)
→ AC 기반 코드 + JUnit 테스트 작성
→ PR 생성 (Jira 링크 자동 삽입 + 코드 자동 리뷰)
→ CI 실행: 빌드 + 테스트 + SonarCloud 분석
→ CI 통과 시 develop 자동 squash 머지 + Jira Done
| 파일 | 트리거 | 역할 |
|---|---|---|
ci.yml |
PR → develop, PR → main | 빌드 · 테스트 · SonarCloud 분석 |
auto-merge.yml |
PR → develop (auto/ 브랜치만) |
CI 통과 시 자동 squash 머지 |
- PR마다 자동 실행 — 버그 · 취약점 · 코드스멜 · 커버리지 분석
- PR 댓글로 결과 자동 표시 (Quality Gate Pass/Fail)
- 대시보드: https://sonarcloud.io/project/overview?id=Devpick-Org_devpick-backend
| 브랜치 | 생성 주체 | 머지 방식 |
|---|---|---|
auto/feature/DP-{번호}-{기능명} |
Claude Code (신규 기능) | CI 통과 시 자동 squash 머지 |
auto/fix/DP-{번호}-{설명} |
Claude Code (버그 수정) | CI 통과 시 자동 squash 머지 |
feature/DP-{번호}-{기능명} |
개발자 직접 | PR 확인 후 수동 머지 |
hotfix/DP-{번호}-{설명} |
개발자 직접 | PR 확인 후 수동 머지 |
사용법: /feature-dev DP-177
자동으로 하는 것:
- Jira MCP로 티켓 제목 · 설명 · 인수조건(AC) 읽기
- 티켓 상태 자동 전환: To Do → In Progress → Done
auto/feature/DP-{번호}-{기능명}(신규 기능) 또는auto/fix/DP-{번호}-{설명}(버그 수정) 브랜치 생성- AC 항목을 테스트 케이스로 변환해 JUnit 5 테스트 작성
- PR 생성 (Jira 링크 자동 삽입)
- PR diff 기반 코드 자동 리뷰
- CI 통과 후 develop 자동 머지
팀원 참고:
feature/브랜치로 직접 작업한 PR은 CI 통과 후에도 자동 머지되지 않습니다. GitHub에서 리뷰 확인 후 직접 Merge 버튼을 눌러주세요.
규칙: 문제를 직면하면 해결 즉시
TRB.md에 기록한다. 같은 문제를 두 번 겪지 않는다.전체 기록:
TRB.md