Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1440f00
feat : add batch deployment scripts and bump version
hgkim-openerd Jan 23, 2026
6aba04e
feat(admin): 테이스팅 태그 CRUD API 구현
Whale0928 Jan 27, 2026
27dbef7
docs(admin): 테이스팅 태그 RestDocs 문서화
Whale0928 Jan 27, 2026
6f5a368
docs(admin): 테이스팅 태그 HTTP 클라이언트 파일 추가
Whale0928 Jan 27, 2026
afd997f
feat: add Base64 image validation annotation and validator
hgkim-openerd Jan 28, 2026
f871697
chore: git ignore
hgkim-openerd Jan 28, 2026
2be01df
refactor: TastingTag DTO and related logic to hierarchical structure
hgkim-openerd Jan 28, 2026
21eea97
docs: simplify titles in tasting tag API documentation
hgkim-openerd Jan 28, 2026
e64cdd7
feat(admin): 큐레이션 관리 Admin API 구현
Whale0928 Feb 2, 2026
17ce185
docs: simplify ADMIN-API-GUIDE to rules and checklists
Whale0928 Feb 2, 2026
9cf5b5b
docs: add GitHub Copilot repository instructions
Whale0928 Feb 2, 2026
07fe515
docs: add Antora documentation migration plan
Whale0928 Feb 2, 2026
e833951
docs: add file management strategy and deployment sections to Antora …
Whale0928 Feb 2, 2026
6cb00a6
refactor: 큐레이션 응답 데이터 구조 변경 및 메타 정보 필드 추가
Whale0928 Feb 3, 2026
877cd4a
docs: Admin API 문서 삭제
Whale0928 Feb 3, 2026
8a75e1e
docs: API 문서 구조 및 초기 설정 파일 추가
Whale0928 Feb 3, 2026
ed8ecc7
docs: Antora 소스 경로 및 시작 경로 수정
Whale0928 Feb 3, 2026
782875c
docs: Antora UI 커스터마이징 설정 및 다크모드 추가
Whale0928 Feb 3, 2026
361efc7
docs: Antora UI에 다크모드 테마 및 헤더/푸터 추가
Whale0928 Feb 3, 2026
7086c8d
docs: 헤더 콘텐츠에 와이드 스크린 레이아웃 스타일 추가
Whale0928 Feb 3, 2026
61180e2
test: 외부 docs 테스트 포함 필터 추가
Whale0928 Feb 3, 2026
20d4e5d
fix: 리뷰 정렬 기준을 수정
Whale0928 Feb 4, 2026
4cf5b3f
fix(batch): Discord 웹훅 설정 문제 수정
Whale0928 Feb 4, 2026
7ec4ebf
chore: 버전 업데이트 및 환경 변수 파일 변경
Whale0928 Feb 4, 2026
8d5838e
refactor: Antora API 문서 시스템 및 @Sql 마이그레이션 파일 제거
Whale0928 Feb 4, 2026
3a31fb7
refactor: HistoryPayload 네이밍 및 아키텍처 룰 수정
Whale0928 Feb 4, 2026
9afff34
chore: 환경변수와 키 파일 삭제
Whale0928 Feb 5, 2026
579785d
fix(history): ViewHistory 동기화 시 중복 키 에러 수정
Whale0928 Feb 5, 2026
071aad7
chore: VERSION 파일 버전 업데이트
Whale0928 Feb 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 169 additions & 0 deletions .claude/docs/ADMIN-API-GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# Admin API 구현 가이드

> Admin API 구현 시 따라야 할 규칙과 체크리스트를 정의합니다.
> 코드 예시는 기존 구현 파일을 참고하세요.

---

## 아키텍처 개요

```
admin-api (Kotlin) → mono (Java)
├── Controller (presentation) ─┬→ Service (비즈니스 로직)
│ ├→ Repository (JPA + QueryDSL)
│ └→ DTO (Request/Response)
```

**핵심 원칙:**
- admin-api는 프레젠테이션 계층만 담당 (Kotlin)
- 비즈니스 로직과 DTO는 mono 모듈에 작성 (Java)
- admin-api는 `spring-data-jpa`를 `testImplementation`으로만 의존

---

## 구현 체크리스트

### Phase 1: 구현 (mono → admin-api 순서)

| 순서 | 작업 | 모듈 | 위치 |
|------|------|------|------|
| 1 | Request/Response DTO | mono | `{domain}/dto/request/`, `{domain}/dto/response/` |
| 2 | ExceptionCode 추가 | mono | `{domain}/exception/{Domain}ExceptionCode.java` |
| 3 | ResultCode 추가 | mono | `global/dto/response/AdminResultResponse.java` |
| 4 | Repository 확장 (필요 시) | mono | `{domain}/repository/` |
| 5 | Service 작성 | mono | `{domain}/service/Admin{Domain}Service.java` |
| 6 | Controller 작성 | admin-api | `{domain}/presentation/Admin{Domain}Controller.kt` |

### Phase 2: 테스트

| 순서 | 작업 | 위치 |
|------|------|------|
| 1 | Test Helper | `app/helper/{domain}/{Domain}Helper.kt` (object 싱글톤) |
| 2 | Integration Test | `app/integration/{domain}/Admin{Domain}IntegrationTest.kt` |

### Phase 3: 문서화 (선택)

| 순서 | 작업 | 위치 |
|------|------|------|
| 1 | RestDocs Test | `app/docs/{domain}/Admin{Domain}ControllerDocsTest.kt` |
| 2 | AsciiDoc | `src/docs/asciidoc/api/{domain}/` |

---

## 핵심 규칙

### DTO 규칙

| 규칙 | 설명 |
|------|------|
| **DTO-Entity 분리** | Response DTO는 Entity를 직접 참조하면 안 됨 (아키텍처 규칙 위반) |
| **팩토리 메서드** | `from(Entity)` 금지 → `of(...)` 사용, 변환 로직은 Service에서 처리 |
| **record 사용** | Java record로 작성, `@Builder` 생성자에서 기본값 설정 |
| **Validation** | `@NotBlank`, `@NotNull` 등 Bean Validation 사용 |

### Service 규칙

| 규칙 | 설명 |
|------|------|
| **목록 조회 반환** | `Page<T>` 직접 반환 금지 → `GlobalResponse.fromPage()` 사용 |
| **상세 조회 반환** | Response DTO 반환, `of()` 팩토리로 변환 |
| **CUD 반환** | `AdminResultResponse.of(ResultCode, targetId)` 통일 |
| **트랜잭션** | 조회는 `@Transactional(readOnly = true)`, CUD는 `@Transactional` |

### Controller 규칙

| 규칙 | 설명 |
|------|------|
| **목록 조회** | `ResponseEntity.ok(service.search(request))` (Service가 GlobalResponse 반환) |
| **그 외 API** | `GlobalResponse.ok(service.xxx())` |
| **매핑** | `@RequestMapping("/{복수형}")` (kebab-case) |
| **검색 파라미터** | `@ModelAttribute` 사용 |
| **Body 파라미터** | `@RequestBody @Valid` 사용 |

### 페이징 방식

| API 유형 | 방식 | 파라미터 |
|----------|------|----------|
| Admin (관리자) | 오프셋 페이징 | `page`, `size` |
| Product (클라이언트) | 커서 페이징 | `cursor`, `pageSize` |

---

## HTTP 메서드 규칙

| 작업 | Method | URL 패턴 | 예시 |
|------|--------|----------|------|
| 목록 조회 | GET | `/{resources}` | `GET /curations` |
| 상세 조회 | GET | `/{resources}/{id}` | `GET /curations/1` |
| 생성 | POST | `/{resources}` | `POST /curations` |
| 전체 수정 | PUT | `/{resources}/{id}` | `PUT /curations/1` |
| 부분 수정 | PATCH | `/{resources}/{id}/{field}` | `PATCH /curations/1/status` |
| 삭제 | DELETE | `/{resources}/{id}` | `DELETE /curations/1` |
| 하위 리소스 추가 | POST | `/{resources}/{id}/{sub}` | `POST /curations/1/alcohols` |
| 하위 리소스 삭제 | DELETE | `/{resources}/{id}/{sub}/{subId}` | `DELETE /curations/1/alcohols/5` |

---

## 테스트 규칙

### Integration Test

| 항목 | 규칙 |
|------|------|
| 상속 | `IntegrationTestSupport` |
| 태그 | `@Tag("admin_integration")` |
| 인증 | `getAccessToken(admin)` → `Authorization: Bearer $token` |
| 검증 | `mockMvcTester.get/post/put/delete()` + AssertJ |

### 필수 테스트 케이스

| API | 필수 테스트 |
|-----|-----------|
| 목록 조회 | 성공, 인증 실패, 필터링 |
| 상세 조회 | 성공, 인증 실패, 존재하지 않는 ID |
| 생성 | 성공, 인증 실패, 필수 필드 누락, 중복 검사 |
| 수정 | 성공, 인증 실패, 존재하지 않는 ID |
| 삭제 | 성공, 인증 실패, 존재하지 않는 ID |

### RestDocs Test

| 항목 | 규칙 |
|------|------|
| 어노테이션 | `@WebMvcTest(excludeAutoConfiguration = [SecurityAutoConfiguration::class])` |
| Mock | `@MockitoBean`으로 Service 목킹 |
| 목록 조회 Mock | `GlobalResponse.fromPage(page)` 반환 |
| 그 외 Mock | Response DTO 또는 `AdminResultResponse` 반환 |

---

## 참고 구현 파일

| 항목 | 파일 경로 |
|------|----------|
| Controller | `admin-api/.../alcohols/presentation/AdminCurationController.kt` |
| Service | `mono/.../alcohols/service/AdminCurationService.java` |
| Response DTO | `mono/.../alcohols/dto/response/AdminCurationDetailResponse.java` |
| Request DTO | `mono/.../alcohols/dto/request/AdminCuration*Request.java` |
| Integration Test | `admin-api/.../integration/curation/AdminCurationIntegrationTest.kt` |
| RestDocs Test | `admin-api/.../docs/curation/AdminCurationControllerDocsTest.kt` |
| Helper | `admin-api/.../helper/curation/CurationHelper.kt` |

---

## 검증 절차

Admin API 구현 완료 후 아래 순서대로 검증:

| 순서 | 검증 항목 | 명령어 | 태그/범위 |
|------|----------|--------|-----------|
| 1 | 컴파일 | `./gradlew :bottlenote-admin-api:compileKotlin` | - |
| 2 | 코드 포맷팅 | `./gradlew :bottlenote-mono:spotlessCheck` | mono만 적용 |
| 3 | 아키텍처 규칙 | `./gradlew :bottlenote-mono:check_rule_test` | `@Tag("rule")` |
| 4 | 단위 테스트 | `./gradlew unit_test` | `@Tag("unit")` |
| 5 | 어드민 통합 테스트 | `./gradlew admin_integration_test` | `@Tag("admin_integration")` |
| 6 | REST Docs 생성 | `./gradlew :bottlenote-admin-api:restDocsTest` | `app.docs.*` |

**전체 검증 (위 1-5 포함):**
```bash
./gradlew :bottlenote-admin-api:build
```
112 changes: 112 additions & 0 deletions .claude/skills/deploy-batch/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
name: deploy-batch
description: |
배치 모듈 배포. "배치 배포", "batch deploy", "배치 이미지 올려줘" 요청 시 사용.
단계별 스크립트로 구성되어 개별 테스트 가능.
disable-model-invocation: true
allowed-tools: Bash, Read, Edit, Write, AskUserQuestion
---

# Batch 모듈 배포

## 현재 상태

### 사전 조건
!`.claude/skills/deploy-batch/scripts/check-prerequisites.sh 2>&1 || true`

### 버전 정보
!`.claude/skills/deploy-batch/scripts/check-version.sh both 2>&1 || true`

## 배포 플로우

```
1. 사전 조건 확인 (check-prerequisites.sh)
2. 레지스트리 인증 복호화 (decrypt-registry.sh)
3. 버전 확인 (check-version.sh)
→ 충돌 시 버전 증가 (bump-version.sh)
4. [사용자 질문] 배포 환경 선택
5. [사용자 질문] cosign 서명 여부
6. 이미지 빌드 (build-image.sh)
7. 이미지 푸시 (push-image.sh)
8. kustomize 업데이트 (update-kustomize.sh)
```

## 실행 가이드

### 1단계: 사전 조건 확인
```bash
.claude/skills/deploy-batch/scripts/check-prerequisites.sh
```
- 실패 시 안내된 설치 명령어 실행 후 재시도

### 2단계: 버전 확인 및 조정
```bash
.claude/skills/deploy-batch/scripts/check-version.sh both
```
- STATUS=CONFLICT 시 버전 증가 필요:
```bash
.claude/skills/deploy-batch/scripts/bump-version.sh --patch
```

### 3단계: 사용자에게 질문 (AskUserQuestion)

**배포 환경 선택:**
- Production만
- Development만
- 둘 다 (권장)

**cosign 서명 여부:**
- 서명 포함 (권장)
- 서명 제외

### 4단계: 이미지 빌드
```bash
# VERSION 생략 시 VERSION 파일에서 자동 읽음
.claude/skills/deploy-batch/scripts/build-image.sh
.claude/skills/deploy-batch/scripts/build-image.sh 1.0.0 # 명시적 버전
```

### 5단계: 이미지 푸시
```bash
# 서명 없이
.claude/skills/deploy-batch/scripts/push-image.sh

# 서명 포함
.claude/skills/deploy-batch/scripts/push-image.sh --sign
```

### 6단계: kustomize 업데이트
```bash
# 환경: production | development | both (기본값: both)
.claude/skills/deploy-batch/scripts/update-kustomize.sh
.claude/skills/deploy-batch/scripts/update-kustomize.sh production
.claude/skills/deploy-batch/scripts/update-kustomize.sh 1.0.0 both # 명시적 버전
```

## 스크립트 목록

| 스크립트 | 용도 |
|---------|------|
| `check-prerequisites.sh` | 필수 도구/키 확인, 미충족 시 즉시 중단 |
| `decrypt-registry.sh` | sops로 레지스트리 인증 정보 복호화 |
| `check-version.sh` | VERSION 파일과 배포 태그 비교 |
| `bump-version.sh` | semver 패치/마이너/메이저 증가 |
| `build-image.sh` | Gradle + Docker 빌드 |
| `push-image.sh` | Docker 푸시 + cosign 서명 (선택) |
| `update-kustomize.sh` | kustomize 태그 업데이트 + 서브모듈 푸시 |

## 테스트 (dry-run)

모든 스크립트는 `--dry-run` 옵션 지원:
```bash
.claude/skills/deploy-batch/scripts/bump-version.sh --dry-run
.claude/skills/deploy-batch/scripts/build-image.sh --dry-run
.claude/skills/deploy-batch/scripts/push-image.sh --dry-run --sign
.claude/skills/deploy-batch/scripts/update-kustomize.sh both --dry-run
```

## 주의사항

- VERSION에 `+` 문자 사용 금지 (Docker 태그 제한)
- 서브모듈 푸시 전 자동으로 `git pull --rebase` 실행
- cosign 서명 시 cosign.key 파일 필요
88 changes: 88 additions & 0 deletions .claude/skills/deploy-batch/scripts/build-image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/usr/bin/env bash
set -euo pipefail

# build-image.sh
# Gradle 빌드 + Docker 이미지 빌드

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'

# 파라미터 파싱
VERSION=""
DRY_RUN=false
SKIP_GRADLE=false

while [[ $# -gt 0 ]]; do
case $1 in
--dry-run)
DRY_RUN=true
shift
;;
--skip-gradle)
SKIP_GRADLE=true
shift
;;
-*)
echo "Usage: $0 [version] [--dry-run] [--skip-gradle]" >&2
exit 1
;;
*)
VERSION="$1"
shift
;;
esac
done

# 버전 파라미터 검증
if [[ -z "$VERSION" ]]; then
VERSION_FILE="$PROJECT_ROOT/bottlenote-batch/VERSION"
if [[ -f "$VERSION_FILE" ]]; then
VERSION=$(cat "$VERSION_FILE" | tr -d '[:space:]')
echo -e "${YELLOW}[INFO]${NC} VERSION 파일에서 읽음: $VERSION"
else
echo -e "${RED}[ERROR]${NC} 버전을 지정하거나 VERSION 파일이 필요합니다" >&2
exit 1
fi
fi

REGISTRY="docker-registry.bottle-note.com"
IMAGE_NAME="bottlenote-batch"
FULL_TAG="${REGISTRY}/${IMAGE_NAME}:batch_${VERSION}"

echo "=== 빌드 시작 ==="
echo "버전: $VERSION"
echo "이미지: $FULL_TAG"
echo ""

# 1. Gradle 빌드
if [[ "$SKIP_GRADLE" == "false" ]]; then
echo -e "${GREEN}[1/2]${NC} Gradle 빌드..."
if [[ "$DRY_RUN" == "true" ]]; then
echo -e "${YELLOW}[DRY-RUN]${NC} ./gradlew :bottlenote-batch:build -x test --build-cache --parallel"
else
cd "$PROJECT_ROOT"
./gradlew :bottlenote-batch:build -x test --build-cache --parallel
echo -e "${GREEN}[OK]${NC} Gradle 빌드 완료"
fi
else
echo -e "${YELLOW}[SKIP]${NC} Gradle 빌드 건너뜀"
fi

# 2. Docker 빌드
echo -e "${GREEN}[2/2]${NC} Docker 이미지 빌드..."
if [[ "$DRY_RUN" == "true" ]]; then
echo -e "${YELLOW}[DRY-RUN]${NC} docker build --platform linux/arm64 -f Dockerfile-batch -t $FULL_TAG ."
else
cd "$PROJECT_ROOT"
docker build --platform linux/arm64 -f Dockerfile-batch -t "$FULL_TAG" .
echo -e "${GREEN}[OK]${NC} Docker 빌드 완료: $FULL_TAG"
fi

echo ""
echo "---"
echo "IMAGE=$FULL_TAG"
Loading