기부자와 복지기관을 연결하는 물품 기부 매칭 서비스 IT:SSUM의 백엔드 레포지토리입니다.
기부자는 가지고 있는 물품을 올리고, 기관은 필요한 물품을 올립니다. 서로 맞는 상대를 추천받아 제안·요청을 주고받고, 수락되면 채팅으로 전달 일정을 잡아 기관이 수령 확인까지 하는 흐름입니다.
기부자
- 기부 물품 등록 (카테고리, 품목, 수량, 물품 상태 상·중·하, 사진 1장 이상, 구 단위 지역)
- 등록한 물품 기준으로 추천 기관 목록 확인 후 기부 제안
- 기관이 보낸 나눔 요청 수락 / 거절
- 수락된 건은 채팅으로 일정 조율 → "나눔중"으로 변경
복지기관
- 회원가입 시 국세청 사업자등록정보 진위확인 API로 사업자 확인 (운영자 승인 절차 없음)
- 필요 물품 등록 (필요 수량, 희망 최소 상태, 수령 지역)
- 필요 물품 기준으로 추천 기부 물품 확인 후 나눔 요청
- 물품을 받으면 수령 확인 → 전달 완료
공통
- STOMP 기반 1:1 채팅 (매칭 1건당 채팅방 1개)
- 매칭 상태가 바뀔 때마다 알림 (실시간 구독 + 목록 조회, 읽음 처리)
- 이메일 링크로 비밀번호 재설정
| 구분 | 사용 |
|---|---|
| Language | Java 21 |
| Framework | Spring Boot 4.1.1, Spring MVC |
| DB | MySQL 8.0, Spring Data JPA (Hibernate) |
| 인증 | Spring Security (JWT 적용 예정) |
| 실시간 | Spring WebSocket + STOMP |
| 기타 | Validation, Java Mail Sender, Lombok |
| Build | Gradle (Groovy) |
| 예정 | AWS S3 (이미지), FCM (푸시), springdoc (Swagger) |
| 색 | 도메인 | 테이블 |
|---|---|---|
| 파랑 | 회원·인증 | users, terms_agreements, password_reset_tokens, donors, organizations, organization_verifications |
| 노랑 | 기준 데이터 | regions, item_categories |
| 초록 | 물품 | donation_items, donation_item_images, needed_items |
| 빨강 | 매칭 | matches |
| 보라 | AI 추천 | organization_recommendations, donation_item_recommendations |
| 청록 | 채팅 | chat_rooms, chat_participants, chat_messages |
| 주황 | 알림 | notifications, notification_settings, push_tokens |
설계하면서 정한 것들
donors,organizations,notification_settings는users의 PK를 그대로 PK로 씁니다. 엔티티에서는@MapsId로 매핑합니다.- 매칭은 항상 기부 물품 1개와 필요 물품 1개를 잇습니다. 기부자가 먼저 제안한 건지 기관이 먼저 요청한 건지는
requested_by로 구분합니다. - 기부 물품 하나를 여러 기관에 나눠 줄 수 있어서 수량을
quantity,reserved_quantity,completed_quantity세 개로 관리합니다. 필요 물품도 같은 방식입니다. - 매칭 목록 조회가 잦아서
matches에donor_id,organization_id를 중복 저장했습니다. - 회원 탈퇴, 물품 삭제는
deleted_at을 채우는 soft delete입니다.
Flyway는 쓰지 않고 spring.jpa.hibernate.ddl-auto: update로 관리합니다. 그래서 실제 스키마의 기준은 엔티티입니다.
- 엔티티를 고치면
docs/erd/itssum_erd_v3.sql과 ERDCloud도 같이 고쳐주세요. update는 컬럼 추가만 반영하고 타입 변경이나 컬럼 삭제는 반영하지 않습니다. 이런 변경이 있으면 로컬 DB를 지우고 다시 띄우면 됩니다.
| 상태 | 화면 표시 | 다음으로 넘어가는 조건 |
|---|---|---|
REQUESTED |
응답 대기 | 상대가 수락하면 RESERVED, 거절하면 REJECTED |
RESERVED |
예약중 | 기부자가 "나눔중으로 변경"을 누르면 SHARING, 취소하면 CANCELED |
SHARING |
나눔중 | 기관이 수령 확인하면 COMPLETED, 취소하면 CANCELED |
COMPLETED |
전달 완료 | 끝 |
REJECTED |
거절 | 끝 |
CANCELED |
취소 | 끝 |
- 수락하는 순간 기부 물품과 필요 물품 양쪽의
reserved_quantity에 매칭 수량이 더해집니다. - 취소되면 예약했던 수량을 다시 돌려놓습니다.
- 수령 확인 시
reserved_quantity에서 빼고completed_quantity(필요 물품은received_quantity)로 옮깁니다.
도메인별로 패키지를 나누고, 각 도메인 안에 controller / dto / entity / repository / service를 둡니다. 여러 도메인이 같이 쓰는 설정과 외부 연동은 global에 모읍니다.
src/main/java/com/itssum
├── domain
│ ├── auth # 로그인, 회원가입, 토큰, 비밀번호 재설정
│ ├── user # 회원, 기부자, 복지기관, 약관 동의, 사업자 인증 이력
│ ├── item # 기부 물품, 이미지, 필요 물품, 카테고리, 지역
│ ├── recommendation # AI 추천 결과
│ ├── match # 매칭
│ ├── chat # 채팅방, 참여자, 메시지
│ └── notification # 알림, 알림 설정, 푸시 토큰
│
│ (도메인 공통)
│ ├── controller
│ ├── dto
│ │ ├── request
│ │ └── response
│ ├── entity
│ ├── repository
│ └── service
│
└── global
├── common # BaseTimeEntity, ApiResponse
├── config # JPA Auditing, Security, WebSocket 설정
├── exception # ErrorCode, BusinessException, GlobalExceptionHandler
├── security # JWT 필터, 인증 처리
├── infra # S3, 국세청 사업자 API, 메일, FCM
└── ai # AI 추천 연동
MySQL 8.0에 itssum 스키마를 먼저 만들어 두고, DB 접속 정보는 환경변수로 넘깁니다. 접속 정보는 커밋하지 않습니다.
export DB_URL="jdbc:mysql://localhost:3306/itssum?serverTimezone=Asia/Seoul"
export DB_USERNAME=root
export DB_PASSWORD=비밀번호
./gradlew bootRunIntelliJ에서 띄울 때는 Run Configuration의 Environment variables에 같은 값을 넣으면 됩니다.
API 명세는 노션에서 관리하고 있습니다. springdoc을 붙이면 Swagger 주소를 여기에 추가할 예정입니다.
- REST:
/api/{리소스 복수형}형태, 내 정보는/me로 접근 (예:GET /api/donors/me/donation-items) - 상태 변경은
PATCH /api/{리소스}/{id}/{동작}(예:PATCH /api/needed-items/{id}/close) - WebSocket 연결
/ws, 채팅 구독/topic/chat-rooms/{chatRoomId}, 전송/app/chat-rooms/{chatRoomId}/messages, 알림 구독/user/queue/notifications
| 브랜치 | 용도 |
|---|---|
main |
배포용. 직접 푸시하지 않습니다. |
develop |
개발 통합 브랜치. 모든 PR은 여기로 올립니다. |
feature/{이슈번호}-{작업} |
기능 개발 (예: feature/12-donation-item-create) |
fix/{이슈번호}-{작업} |
버그 수정 |
refactor/{이슈번호}-{작업} |
리팩토링 |
작업 전에 이슈부터 만들고, 이슈 번호로 브랜치를 땁니다.
타입: 작업 내용 (#이슈번호)
| 타입 | 언제 쓰는지 |
|---|---|
feat |
새로운 기능 |
fix |
버그 수정 |
refactor |
동작은 그대로 두고 코드 구조만 변경 |
docs |
README, ERD, API 명세 등 문서 |
test |
테스트 코드 추가·수정 |
chore |
빌드 설정, 의존성, 설정 파일 |
style |
포맷팅, import 정리처럼 로직과 상관없는 변경 |
- 제목은 한글로, 50자 이내로 씁니다. 끝에 마침표는 찍지 않습니다.
- 왜 바꿨는지 설명이 필요하면 한 줄 띄우고 본문에 적습니다.
- 한 커밋에는 한 가지 작업만 담습니다.
feat: 기부 물품 등록 API 구현 (#12)
feat: 필요 물품 모집 종료 API 추가 (#18)
fix: 매칭 취소 시 예약 수량이 복원되지 않던 문제 수정 (#31)
refactor: 추천 점수 계산을 RecommendationScorer로 분리 (#40)
docs: ERD v3 반영
chore: Spring Boot 4.1.1로 버전 변경
develop브랜치로 올리고, 제목은 커밋 메시지 형식을 따릅니다.- 본문에는 작업 내용, 관련 이슈(
closes #12), 확인 방법을 적습니다. - 리뷰어 1명이 승인하면 Squash and merge로 합칩니다.
- 머지한 브랜치는 삭제합니다.
DTO
- DTO는 전부
record로 만들고 도메인의dto/request,dto/response에 둡니다. - 이름은
{동작}{대상}Request,{대상}{용도}Response형식입니다. 예:CreateDonationItemRequest,DonationItemDetailResponse - 요청 값 검증은 record 필드에
@NotBlank,@Positive같은 Bean Validation을 붙여서 합니다. - 응답 DTO는
from(Entity)정적 메서드로 만들고, 컨트롤러에서 엔티티를 그대로 반환하지 않습니다.
Entity
- setter를 만들지 않고, 기본 생성자는
protected로 둡니다. - 생성은
@Builder나 정적 팩토리 메서드로, 값 변경은 의미 있는 이름의 메서드로 합니다. 예:match.accept(),neededItem.close() - 연관관계는 전부
FetchType.LAZY로 걸고, 필요한 경우에만 양방향으로 만듭니다. - enum은
@Enumerated(EnumType.STRING)으로 저장합니다.
응답·예외
- 모든 응답은
ApiResponse로 감싸서 내려줍니다. - 예외는
ErrorCode에 코드를 추가하고BusinessException으로 던집니다. 처리는GlobalExceptionHandler한 곳에서 합니다.
| 이름 | 담당 (백엔드) |
|---|---|
| 이창훈 | 인증·인가, 회원, 기부·필요 물품, AI 추천, 기관 사업자 인증, 인프라 |
| 허건우 | 매칭, 채팅, 알림 |
