|
| 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