Skip to content

Commit 4eb9080

Browse files
committed
chore: CLAUDE.md 파일 추가
1 parent 4693f17 commit 4eb9080

1 file changed

Lines changed: 182 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# Billilge Backend
2+
3+
대학교 학생회 물품 대여 관리 시스템 백엔드
4+
5+
## 기술 스택
6+
7+
- **Spring Boot 3.4.1** / **Kotlin 1.9.25** / **JDK 21**
8+
- **JPA + QueryDSL** (MySQL)
9+
- **Spring Security** (JWT + Google OAuth2)
10+
- **Firebase Cloud Messaging** (푸시 알림)
11+
- **AWS S3** (이미지 업로드)
12+
- **Apache POI** (Excel 생성)
13+
14+
## 빌드 & 실행
15+
16+
```bash
17+
./gradlew compileKotlin # 컴파일 확인
18+
./gradlew build # 전체 빌드
19+
./gradlew bootRun # 실행
20+
```
21+
22+
## 아키텍처
23+
24+
```
25+
Controller → Facade → Service → Repository
26+
```
27+
28+
| 레이어 | 역할 | DTO 참조 |
29+
|--------|------|----------|
30+
| **Controller** | HTTP 요청/응답, `@AuthenticationPrincipal`로 인증 정보 추출 | Request/Response DTO |
31+
| **Facade** | Request DTO 분해, 크로스 도메인 조합, Response DTO 생성 | Request/Response DTO |
32+
| **Service** | 비즈니스 로직, 자기 도메인 Repository만 사용 | Entity/primitives만 |
33+
| **Repository** | 데이터 접근 (JPA + QueryDSL) | Entity/Query DTO만 |
34+
35+
### 핵심 규칙
36+
37+
- **Service는 Request/Response DTO를 참조하지 않는다** — Entity, primitives, 글로벌 DTO(`PageableCondition`, `SearchCondition`)만 사용
38+
- **Service는 타 도메인 Repository를 직접 의존하지 않는다** — 타 도메인 Service를 통해 접근 (예외: `PayerService → MemberRepository` 순환 의존 방지)
39+
- **크로스 도메인 조합은 Facade에서 수행한다** — Facade가 여러 Service를 호출해 엔티티를 조합 후 Service에 전달
40+
- **Facade에서 트랜잭션 필요 시 `@Transactional` 명시** — 여러 서비스 호출을 하나의 persistence context로 묶어야 할 때
41+
42+
### 트랜잭션 패턴
43+
44+
- Service 클래스에 `@Transactional(readOnly = true)` 기본 적용
45+
- 쓰기 메서드만 `@Transactional`로 오버라이드
46+
- JPA dirty checking 활용 — `repository.save()` 없이 엔티티 필드 변경으로 자동 반영
47+
48+
## 도메인 구조
49+
50+
```
51+
domain/
52+
├── item/ # 물품 관리
53+
├── member/ # 회원, 인증
54+
├── notification/ # 알림 (FCM 푸시)
55+
├── payer/ # 회비 납부자 관리
56+
└── rental/ # 대여/반납 관리
57+
```
58+
59+
각 도메인 패키지 구조:
60+
```
61+
domain/{name}/
62+
├── controller/ # API 컨트롤러 + Api 인터페이스 (Swagger)
63+
├── facade/ # Facade (DTO 변환, 크로스 도메인 조합)
64+
├── service/ # 비즈니스 로직
65+
├── repository/ # JPA Repository + Custom(QueryDSL)
66+
├── entity/ # JPA Entity
67+
├── dto/ # request/, response/
68+
├── enums/ # 도메인 열거형
69+
└── exception/ # 도메인 에러 코드
70+
```
71+
72+
## 서비스 의존성
73+
74+
```
75+
ItemService → ItemRepository, S3Service
76+
MemberService → MemberRepository, TokenProvider, PayerService
77+
NotificationService → NotificationRepository, FCMService, MemberService
78+
PayerService → PayerRepository, MemberRepository, ExcelGenerator
79+
RentalService → RentalRepository, NotificationService
80+
```
81+
82+
## 대여 상태 머신
83+
84+
```
85+
[사용자 신청] PENDING → CONFIRMED → RENTAL → RETURN_PENDING → RETURN_CONFIRMED → RETURNED
86+
→ REJECTED
87+
PENDING → CANCEL (사용자 취소)
88+
89+
[관리자 생성] 대여물품: → RENTAL (바로 대여중)
90+
소모품: → RETURNED (즉시 반납 처리)
91+
```
92+
93+
- **CONFIRMED**: 재고 차감, 담당자(worker) 배정
94+
- **RETURNED**: 재고 복원 (소모품 제외)
95+
- **소모품(CONSUMPTION)**: RENTAL 상태 요청 시 자동으로 RETURNED 처리
96+
97+
## 대여 비즈니스 규칙
98+
99+
- 회비 납부자(`isFeePaid`)만 대여 가능
100+
- 동일 물품 중복 대여 불가 (`ignoreDuplicate`로 우회 가능)
101+
- 시험 기간 대여 불가 (`exam-period.start-date` / `end-date`)
102+
- 주말 대여 불가
103+
- 과거 시간 대여 불가
104+
- 10시~17시만 대여 가능
105+
- Dev 모드(`/rentals/dev`): 시간 검증 생략, ADMIN 역할 필요
106+
107+
## API 엔드포인트
108+
109+
### 인증 (Public)
110+
| Method | Path | 설명 |
111+
|--------|------|------|
112+
| POST | `/auth/sign-up` | 회원가입 |
113+
| POST | `/auth/admin-login` | 관리자 로그인 |
114+
115+
### 물품 (Public)
116+
| Method | Path | 설명 |
117+
|--------|------|------|
118+
| GET | `/items` | 물품 검색 |
119+
120+
### 회원 (JWT 필요)
121+
| Method | Path | 설명 |
122+
|--------|------|------|
123+
| POST | `/members/me/fcm-token` | FCM 토큰 등록 |
124+
125+
### 알림 (JWT 필요)
126+
| Method | Path | 설명 |
127+
|--------|------|------|
128+
| GET | `/notifications` | 알림 목록 |
129+
| GET | `/notifications/count` | 안읽은 알림 수 |
130+
| PATCH | `/notifications/{id}` | 알림 읽음 |
131+
| PATCH | `/notifications/all` | 전체 읽음 |
132+
133+
### 대여 (JWT 필요)
134+
| Method | Path | 설명 |
135+
|--------|------|------|
136+
| POST | `/rentals` | 대여 신청 |
137+
| POST | `/rentals/dev` | 개발용 대여 (시간 검증 생략) |
138+
| GET | `/rentals` | 대여 이력 조회 |
139+
| PATCH | `/rentals/{id}` | 대여 취소 |
140+
| PATCH | `/rentals/return/{id}` | 반납 신청 |
141+
| GET | `/rentals/return-required` | 반납 필요 목록 |
142+
143+
### 관리자 (JWT + @OnlyAdmin)
144+
| Method | Path | 설명 |
145+
|--------|------|------|
146+
| GET | `/admin/items` | 물품 목록 (대여자 수 포함) |
147+
| POST | `/admin/items` | 물품 추가 |
148+
| PUT | `/admin/items/{id}` | 물품 수정 |
149+
| GET | `/admin/items/{id}` | 물품 상세 |
150+
| DELETE | `/admin/items/{id}` | 물품 삭제 |
151+
| GET | `/admin/members` | 회원 목록 |
152+
| GET | `/admin/members/admins` | 관리자 목록 |
153+
| POST | `/admin/members/admins` | 관리자 등록 |
154+
| DELETE | `/admin/members/admins` | 관리자 해제 |
155+
| GET | `/admin/members/payers` | 납부자 목록 |
156+
| POST | `/admin/members/payers` | 납부자 등록 |
157+
| DELETE | `/admin/members/payers` | 납부자 삭제 |
158+
| GET | `/admin/members/payers/excel` | 납부자 엑셀 다운로드 |
159+
| GET | `/admin/notifications` | 관리자 알림 |
160+
| GET | `/admin/rentals/dashboard` | 대시보드 |
161+
| GET | `/admin/rentals` | 대여 이력 |
162+
| PATCH | `/admin/rentals/{id}` | 대여 상태 변경 |
163+
| POST | `/admin/rentals` | 관리자 대여 생성 |
164+
| DELETE | `/admin/rentals/{id}` | 대여 이력 삭제 |
165+
166+
## Global 패키지
167+
168+
```
169+
global/
170+
├── annotation/ # @OnlyAdmin
171+
├── config/ # SecurityConfig, CorsConfig, SwaggerConfig, QueryDslConfig, AsyncConfig
172+
├── dto/ # PageableCondition, SearchCondition, PageableResponse
173+
├── exception/ # ApiException, ErrorCode, GlobalExceptionHandler
174+
├── external/
175+
│ ├── fcm/ # FCMConfig, FCMService
176+
│ └── s3/ # S3Config, S3Service
177+
├── logging/ # LoggingFilter
178+
├── security/
179+
│ ├── jwt/ # TokenProvider, TokenAuthenticationFilter
180+
│ └── oauth2/ # Google OAuth2 핸들러, UserAuthInfo
181+
└── utils/ # DateUtils(isWeekend), ExcelGenerator
182+
```

0 commit comments

Comments
 (0)