작성일 2026-08-09 · 대상: 백엔드 팀 전원
기준 문서: 모아셈 최종 기획안 v1.0
이 문서는 프로젝트 착수 시점에 정한 기준이며, 팀 논의를 거쳐 계속 갱신한다. 변경 시 이 문서를 먼저 고치고 PR로 리뷰받은 뒤 반영한다.
| 영역 | 선택 | 비고 |
|---|---|---|
| 언어 | Kotlin 2.2.x | |
| 프레임워크 | Spring Boot 4.1.x | Spring Framework 7 기반, Java 17+ 필수 |
| 빌드 도구 | Gradle (Kotlin DSL, build.gradle.kts) |
|
| DB | PostgreSQL | 기획안 14장 기준, 팀 논의 후 최종 확정 |
| ORM | Spring Data JPA + Hibernate | |
| 캐시 | Redis | |
| 인증 | OAuth2 (Google) + JWT | 자체 회원가입 없음 |
| 파일 저장소 | AWS S3 (예정) | 증빙 이미지 |
| 배포 | Docker Compose, AWS EC2 | V_O와 동일한 구조 사용 예정 |
Java 8/11 스타일 라이브러리를 그대로 가져오지 않는다. Spring Boot 4.x는 spring-boot-starter-web 대신
spring-boot-starter-webmvc, com.fasterxml.jackson.module 대신 tools.jackson.module 좌표를 쓴다.
의존성 추가 전 Spring Boot 4.x 기준으로 groupId가 바뀌었는지 먼저 확인한다.
엔티티 단위가 아니라 도메인(기능 묶음) 단위로 패키지를 나눈다. 기획안의 5개 핵심 흐름을 그대로 도메인 경계로 사용한다.
com.moasem.backend
├── MoasemApplication.kt
├── domain
│ ├── auth # 구글 로그인, 토큰 발급/재발급
│ ├── group # 모임 생성, 가입 코드, 멤버십
│ ├── event # 행사, 최초예산, 예산추가, 마감
│ ├── spending # 지출 신청, 승인/반려, 증빙
│ └── report # 결산 수치 계산, AI 분석 연동, PDF/CSV 생성
└── global
├── config # Security, JPA, 외부 API 등 전역 설정
├── error # 공통 예외, GlobalExceptionHandler
├── security # JWT 필터, OAuth2 관련 공통 컴포넌트
├── response # 공통 API 응답 포맷
└── util # 공통 유틸리티
각 도메인 내부는 기술 레이어 기준으로 6개 폴더를 고정한다.
domain/{도메인명}
├── controller # Controller (요청 검증 후 service 호출)
├── service # 비즈니스 로직, 트랜잭션 경계
├── dto # Request/Response DTO
├── converter # Entity <-> DTO 변환
├── entity # JPA 엔티티
└── repository # Spring Data JPA Repository 인터페이스
의존 방향: controller → service → repository/entity, converter는 service나 controller에서 필요할 때 사용.
컨트롤러는 요청 검증(@Valid) 후 service 호출만 하고 비즈니스 로직을 직접 두지 않는다.
도메인 간 참조: 다른 도메인의 service를 직접 호출하는 건 허용한다.
단, 다른 도메인의 entity를 직접 수정하지 않고 그 도메인의 service 메서드를 통해서만 접근한다.
예: spending이 event의 잔여 예산을 갱신할 때 EventService를 통해서만 접근한다.
같은 도메인 안에 엔티티가 여러 개 있는 경우(예: event 도메인의 Event, BudgetAddition)에는
entity, repository 폴더 안에서 파일명으로 구분한다 (Event.kt, BudgetAddition.kt 등).
도메인이 지나치게 커지면(엔티티 4개 이상 + 서로 다른 흐름) 그때 팀 논의 후 하위 도메인 분리를 검토한다.
| 유형 | 규칙 | 예시 |
|---|---|---|
| 엔티티 | 단수 명사 | Event, Spending, BudgetAddition |
| Repository 인터페이스 | {엔티티}Repository |
SpendingRepository |
| 유스케이스 서비스 | {도메인}{동작}Service 또는 {도메인}Service |
SpendingApprovalService, EventService |
| Controller | {도메인}Controller |
SpendingController |
| Request DTO | {동작}{도메인}Request |
CreateEventRequest, ApproveSpendingRequest |
| Response DTO | {도메인}Response / {도메인}{용도}Response |
EventDetailResponse, SpendingSummaryResponse |
| 커스텀 예외 | {상황}Exception |
InvalidSpendingStateException |
- 함수명은 동사로 시작:
approve(),calculateRemainingBudget() - Repository 조회 메서드는 Spring Data JPA 네이밍 규칙(
findBy...,existsBy...)을 우선 사용하고, 복잡한 조건은@Query또는 QueryDSL로 분리한다 (QueryDSL 도입 여부는 팀 논의 후 결정). - boolean 반환 함수/필드는
is,has접두사:isClosed,hasPendingSpending
기획안에 정의된 enum은 아래 표기를 그대로 코드에 반영한다. 화면 표시용 한글 라벨과 DB 저장 코드를 분리한다.
enum class SpendingTag(val label: String) {
MEAL("식비"),
ACCOMMODATION("숙박비"),
TRANSPORTATION("교통비"),
VENUE("대관비"),
SUPPLIES("물품비"),
OTHER("기타")
}
enum class SpendingStatus {
PENDING, APPROVED, REJECTED
}
enum class EventStatus {
ACTIVE, CLOSED
}
enum class GroupRole {
OWNER, MEMBER
}main (배포) ← develop (통합) ← feature/*, fix/* (작업 브랜치)
main: 항상 배포 가능한 상태. 직접 push 금지.develop: 기능 통합 브랜치. PR 머지만 허용.feature/{도메인}-{작업명}: 예)feature/spending-approval-apifix/{도메인}-{버그명}: 예)fix/event-budget-calcrefactor/{내용},docs/{내용},chore/{내용}도 동일 규칙
Conventional Commits 기반, 한글 사용.
{type}: {변경 요약}
{본문 (선택, 왜 바꿨는지)}
| type | 용도 |
|---|---|
| feat | 새 기능 |
| fix | 버그 수정 |
| refactor | 동작 변화 없는 구조 개선 |
| test | 테스트 추가/수정 |
| docs | 문서 |
| chore | 빌드, 설정, 의존성 등 |
| style | 포맷팅, 세미콜론 등 (로직 변경 없음) |
예: feat: 지출 승인 시 잔여 예산 재계산 로직 추가
- 하나의 PR은 하나의 기능/이슈 단위로 작게 유지한다.
- PR 제목은 커밋 메시지와 같은 형식.
- 최소 1인 이상 리뷰 승인 후 머지. Squash merge 사용.
.github/PULL_REQUEST_TEMPLATE.md체크리스트 채우기.
- 작업 시작 전 이슈 먼저 생성 (
.github/ISSUE_TEMPLATE사용). - 이슈에 도메인 라벨(
domain:auth,domain:event등) 부착 (라벨은 GitHub 조직 생성 후 설정). - PR 본문에
Closes #이슈번호로 연결.
- Kotlin 공식 스타일 가이드(ktlint 기본 룰) 따름. 들여쓰기 4칸(Gradle 파일은 탭).
- 데이터 클래스는 필요한 곳(DTO)에만 쓰고, JPA 엔티티는
data class로 만들지 않는다 (equals/hashCode가 전체 필드 기준이라 프록시/지연로딩과 충돌 위험). - 엔티티는 기본 생성자를 외부에 노출하지 않고, 정적 팩토리 메서드나 별도 생성자로 생성 규칙을 강제한다.
- 모든 금액 필드는
Long(원 단위 정수), 절대Double/Float사용 금지 — 기획안 8.2 원칙. - API 응답은 공통 포맷(
global/response)으로 통일한다. (형식은 1주차 API 명세 작업 시 확정) - 컨트롤러는 요청 검증(
@Valid) 후 바로 application 서비스 호출만 하고, 비즈니스 로직을 두지 않는다. - 백엔드 권한 검사는 프론트 노출 여부와 무관하게 서버에서 항상 재검증한다 (기획안 9장 원칙).
모임장 전용 API는 서비스 레이어에서
Membership.role == OWNER확인을 빠짐없이 넣는다.
- 신규 API는 최소 unit test(서비스 레이어) 하나 이상 동반한다.
- 상태 전이(PENDING→APPROVED/REJECTED), 예산 계산, 음수 잔액, 마감 조건은 기획안 11장 기준으로 반드시 테스트 케이스를 둔다.
- 테스트 프레임워크: JUnit5 + MockK + SpringMockK (Kotlin 친화적 목킹).