이공계 대학생을 위한 AI 튜터 서비스 백엔드
- 언어: Java 21
- 프레임워크: Spring Boot 3.x
- 데이터베이스: PostgreSQL
- 캐시: Redis
- 빌드 도구: Gradle
- API 문서: Springdoc OpenAPI (Swagger)
- 아키텍처: DDD (Domain-Driven Design)
| 서비스 | URL |
|---|---|
| 프론트엔드 | https://proovy.ai.kr/ |
| API 게이트웨이 | https://api.proovy.ai.kr/ |
| Spring API | https://api.proovy.ai.kr/api/* |
| AI API | https://api.proovy.ai.kr/ai/* |
| Swagger UI | https://api.proovy.ai.kr/swagger-ui/index.html#/ |
proovy-api/
├── src/
│ ├── main/
│ │ ├── java/com/proovy/
│ │ │ ├── ProovyApiApplication.java
│ │ │ ├── global/ # 전역 설정 및 공통 기능
│ │ │ │ ├── config/ # 설정 클래스
│ │ │ │ ├── response/ # API 공통 응답 포맷
│ │ │ │ ├── exception/ # 전역 예외 처리
│ │ │ │ ├── security/ # 인증/인가 설정 (JWT, OAuth)
│ │ │ │ ├── util/ # 유틸리티 클래스
│ │ │ │ ├── tool/ # 도구 설정 및 관리
│ │ │ │ └── infra/ # 외부 인프라 연동
│ │ │ │ ├── s3/ # AWS S3 연동
│ │ │ │ └── http/ # HTTP 클라이언트
│ │ │ │
│ │ │ └── domain/ # 도메인별 비즈니스 로직 (DDD)
│ │ │ ├── auth/ # 인증 도메인
│ │ │ │ ├── controller/ # 소셜 로그인, 회원가입, 토큰 갱신
│ │ │ │ ├── service/
│ │ │ │ ├── dto/
│ │ │ │ ├── provider/ # OAuth 제공자별 구현
│ │ │ │ └── entity/
│ │ │ │
│ │ │ ├── user/ # 사용자 도메인
│ │ │ │ ├── controller/ # 프로필 조회/수정, 회원 탈퇴
│ │ │ │ ├── service/
│ │ │ │ ├── dto/
│ │ │ │ ├── entity/ # User, Plan 엔티티
│ │ │ │ └── repository/
│ │ │ │
│ │ │ ├── note/ # 노트 도메인
│ │ │ │ ├── controller/ # 노트 CRUD, 목록 조회
│ │ │ │ ├── service/
│ │ │ │ ├── dto/
│ │ │ │ ├── entity/ # Note 엔티티
│ │ │ │ └── repository/
│ │ │ │
│ │ │ ├── asset/ # 자산 도메인 (파일 관리)
│ │ │ │ ├── controller/ # 파일 업로드/다운로드/삭제
│ │ │ │ ├── service/ # S3 연동, 스토리지 관리
│ │ │ │ ├── dto/
│ │ │ │ ├── entity/ # Asset 엔티티
│ │ │ │ └── repository/
│ │ │ │
│ │ │ ├── storage/ # 스토리지 도메인
│ │ │ │ ├── controller/ # 스토리지 현황 조회
│ │ │ │ ├── service/
│ │ │ │ └── dto/
│ │ │ │
│ │ │ ├── ocr/ # OCR 도메인
│ │ │ │ ├── controller/ # OCR 처리 API
│ │ │ │ ├── service/ # AI 서버 연동
│ │ │ │ ├── dto/
│ │ │ │ └── entity/ # OcrResult 엔티티
│ │ │ │
│ │ │ ├── conversation/ # 대화 도메인
│ │ │ │ ├── controller/ # 대화 전송, 목록 조회
│ │ │ │ ├── service/ # AI 응답 생성, 도구 실행
│ │ │ │ ├── dto/
│ │ │ │ ├── entity/ # Conversation, Message 엔티티
│ │ │ │ └── repository/
│ │ │ │
│ │ │ ├── credit/ # 크레딧 도메인
│ │ │ │ ├── controller/ # 크레딧 조회, 사용, 내역
│ │ │ │ ├── service/ # 크레딧 차감/충전 로직
│ │ │ │ ├── dto/
│ │ │ │ │ ├── request/
│ │ │ │ │ └── response/
│ │ │ │ ├── entity/ # CreditBalance, CreditHistory 엔티티
│ │ │ │ └── repository/
│ │ │ │
│ │ │ └── embedding/ # 임베딩 도메인
│ │ │ ├── NoteEmbedding.java
│ │ │ ├── NoteEmbeddingRepository.java
│ │ │ └── service/
│ │ │
│ │ └── resources/
│ │ ├── application.yaml # 애플리케이션 설정
│ │ └── db/migration/ # DB 마이그레이션
│ │
│ └── test/ # 테스트 코드
│ └── java/com/proovy/
│
├── build.gradle # Gradle 빌드 설정
├── settings.gradle
└── README.md
- 소셜 로그인 (OAuth)
- 휴대폰 인증 발송/확인
- 회원가입 완료
- 토큰 갱신 (Access/Refresh Token)
- 로그아웃
- 내 정보 조회 (프로필, 요금제, 크레딧)
- 내 정보 수정 (닉네임, 학과 등)
- 회원 탈퇴
- 노트 목록 조회 (페이징)
- 노트 상세 조회 (채팅방 입장)
- 노트 제목 수정
- 노트 삭제
- 파일 업로드 (Presigned URL 방식)
- 파일 업로드 완료 확인
- 파일 다운로드 URL 발급
- 파일 뷰어 URL 발급
- 파일 목록 조회
- 파일 삭제 (단일/일괄)
- 전체 스토리지 현황 조회
- 이미지/PDF에서 텍스트 추출
- OCR 처리 상태 조회
- AI 서버 연동
- Note가 유일한 대화 단위 (
conversationId = noteId), 메시지는 Note에 직접 연결 - 캔버스 이미지 업로드
- 대화 검색
- 신규 run 접수·SSE·취소는
POST /api/conversations·/api/ai-runs/*(browser-run-v1). 구형/stream/v2·ChatSession 경로는 없다
- 크레딧 잔액 조회
- 크레딧 사용 (차감)
- 크레딧 사용 내역 조회 (페이징, 필터링)
- 크레딧 비용 정보 조회
- 예상 크레딧 비용 조회
ai_runs·ai_run_events·run_asset_dependencies,verified_solutions,request_usage_*ledger- 상태 전이·불변 snapshot·Active Run 제약은 DB baseline에서 강제
- 제품 schema는 빈 DB에서
db/migration/V1__product_baseline.sql로 생성한다. 과거 migration·데이터 이관은 없으며 이후 변경은V2__...부터 versioned migration으로 추가한다. baseline-on-migrate는 꺼져 있어 과거 schema/history가 있는 DB에서는 기동이 실패한다.- Server ↔ Brain DB role 경계는
db/provisioning/role-boundary.sql(Flyway 미실행, 운영자 적용)로 고정한다.
실제 Postgres에 테스트마다 격리 database를 만들어 제약·role·baseline을 검사한다 (superuser 접속 필요).
docker run -d --name proovy-postgres-test -p 127.0.0.1:21432:5432 \
-e POSTGRES_USER=proovy -e POSTGRES_PASSWORD=proovy123 postgres:15-alpine
PROOVY_TEST_PG_URL=jdbc:postgresql://127.0.0.1:21432/postgres \
PROOVY_TEST_PG_USER=proovy PROOVY_TEST_PG_PASSWORD=proovy123 \
PROOVY_REQUIRE_DB_TESTS=true ./gradlew test환경변수가 없으면 DB 테스트는 skip(미실행)되고, PROOVY_REQUIRE_DB_TESTS=true(CI)이면 실패한다.
전용 일회용 Postgres 클러스터에서만 실행한다. role 경계 테스트는 cluster 단위 role(proovy_server, brain_*)을
만들고 비밀번호를 덮어쓰며, database를 지워도 role은 남는다. 공유·운영 클러스터를 PROOVY_TEST_PG_URL로 지정하지 않는다.
com.proovy.integration.brain.ServerBrainIntegrationTest는 proovy-brain testkit/이 띄운 실제 Python 내부 API·runtime과
Server 전체 경로를 연결해 spec 07 Verification을 검사한다. proovy-brain이 private이라 Server CI는 Brain을 checkout할 수 없고,
사용자 결정으로 CI에서는 skip한다. skip은 통과가 아니다 — 테스트 로그에 NOT EXECUTED — real Brain integration … 이유가
남고 JUnit 보고서에는 skipped로 집계된다. 결과는 아래 로컬 명령으로 만들고 이슈·PR에 Server·Brain commit과 함께 기록한다.
# proovy-brain checkout(uv sync --locked 완료)을 원하는 commit에 맞춘 뒤
BRAIN_DIR=../proovy-brain PG_CONTAINER=proovy-postgres-brain-it scripts/brain-integration/run-local.sh
# JDK가 없으면 GRADLE_IN_DOCKER=1 (eclipse-temurin:21-jdk 임시 컨테이너, --network host)스크립트는 격리 Postgres(tmpfs) → Brain testkit supervisor → Gradle(PROOVY_REQUIRE_BRAIN_IT=true라 미구성 시 실패) → 정리 순서로
돌고, Brain fixture sha256이 src/test/resources/contracts/brain/PIN*.json과 같은지·Brain commit이 checkout과 같은지도 검사한다.
모델·Daytona는 offline fake seat이고 일부 결과(자료 부재 안내·scope 밖 출처·변조 envelope)는 testkit scripted다 — 수학 품질·실제
provider·Cloud Run identity는 이 테스트가 인증하지 않는다(S14).
- Swagger UI: https://api.proovy.ai.kr/swagger-ui/index.html#/