DDD + Clean Architecture 기반으로 외부 환경 변화에 유연하게 대응하는 반려식물 관리 및 커뮤니티 플랫폼 서버
- 2024.12 ~ 현재
- 관심사 분리: DDD + Clean Architecture 적용 후 인프라 변경(JPA -> JPA + jOOQ)에도 도메인 로직 수정 없음
- 비용 최적화: 커뮤니티의 규모에 맞게 AWS가 아닌 Wasabi를 채택하는 등 요구사항과 환경에 맞는 기능 개발
- 데이터 무결성: Bean Validation + 도메인 검증 로직 + 테스트 코드로 데이터 정합성 확보
| 팀원 | 역할 |
|---|---|
| 박준희 | • Spring Security 인증/인가, 전역적 예외 핸들링 • 비속어 필터링, 일반 회원가입/댓글 Rest API |
| 박준혁 | • AWS 및 Vercel 배포/관리 • 회원 프로필/좋아요/신고/북마크/건의 및 버그 제보 Rest API |
| 송유정 | • 소셜 로그인(OAuth 2.0), JWT 생성 및 관리 • 비동기 푸시알림, 클라우드 스토리지 관리 • 게시글/게시글 1차+2차 카테고리/알림함 Rest API |
| 고동혁 | • 이메일 서비스 연동, 로깅, 모니터링 • 개발 및 프로덕션 환경 운영, 회원 약관 REST API |
- 회원가입(email/password), 소셜 로그인, 이메일 인증
- 사용자 프로필 관리, 비밀번호 재설정
- 게시글 및 댓글 작성/조회/수정/삭제
- 게시글 검색 및 대표사진 지정, 게시글 이미지 업로드
- 카테고리/최신순/좋아요/북마크에 따른 게시글 조회
- 게시글/작성자에 따른 댓글 조회
- 좋아요 및 북마크
- 게시글 및 댓글 신고
- 건의사항 및 버그 제보
- Spring Boot 3.x & Java 21: 알림 기능의 신속한 처리를 위한 가상 스레드 도입
- PostgreSQL 17: JSONB, GIN INDEX로 비정형 데이터 검색 성능 개선
- Hybrid DB 접근 방식
- JPA/Hibernate: 영속성 맥락 기반 단순 DML 작업, dirty checking으로 데이터 무결성 확보
- jOOQ: 영속성이 불필요한 대규모 READ, 복잡한 집계 쿼리 최적화
- Flyway: DB 스키마 변경 히스토리 관리 및 환경 간 스키마 동기화 자동화
- Redis: 캐싱을 통한 조회 성능 개선 및 토큰 blacklist 관리로 보안 강화
- LGTM Stack (Grafana, Loki, Prometheus, Tempo): 소규모에 적합한 로그, 매트릭, 트레이싱 통합 관찰 환경 구축
- Spring AOP & Logback: 로깅을 비즈니스와 분리하여 로직의 순수성 유지
- Spring Security & JWT: Stateless 인증 구조로 서버 확장성 확보 및 DB 조회 비용 절감
- JaCoCo: 테스트 커버리지 기반 코드 품질 관리
- MapStruct: boilerplate 매핑 로직 제거, 매핑 과정 단순화
- Mailjet: 인증 코드로 사용자 이메일 정보의 신뢰성 확보
- OAuth 2.0 (Google/Kakao): 사용자 로그인 편의성 제공 및 가입 장벽 감소
- 경량의 도메인 객체: 도메인 객체는 상태와 검증 로직만 지니며, 외부 시스템 호출 및 비즈니스 흐름 제어는 adapter 계층에서 처리
- UseCase 계층 단순화: 프로젝트 복잡도 고려 후 UseCase 시스템 사양(인터페이스) 중심으로 설계하고, 불필요한 클래스 생성을 지양
- 프레임워크 격리: Spring, Redis 등 외부 기술 의존 코드를 framework 계층으로 분리하여 도메인 계층의 순수성 유지
📂 modusplant
│ 📜 ModusplantApplication.java
├─📂 domains # 📋 핵심 비즈니스 로직 및 도메인 모델
│ ├─📂 account # 계정 (Email, Social, Normal)
│ ├─📂 post # 게시글
│ └─ 📂 ... # member, comment, term
├─📂 framework # ✈️ 외부 기술 스택 연동 (JPA, jOOQ, Redis 등)
├─📂 infrastructure # 🔨 애플리케이션 공통 기반 (Security, AOP, Config 등)
└─📂 shared # 🧺 전역 공통 모듈 및 유틸리티 (Kernel, Exception 등)
도메인 내부 구조
- domain -> 외부 계층 의존 ❌
- Use Case -> domain 의존 ⭕
- adapter -> Use Case 의존 ⭕
- framework -> adapter 의존 ⭕
📂 [domain_name]
├─📂 adapter # Interface Adapter: 외부 시스템 연결, 데이터 매핑 및 오케스트레이션
├─📂 domain # Enterprise Business Rules: 핵심 비즈니스 로직 (Aggregate, Entity, VO)
├─📂 framework # Frameworks & Drivers: 기술 구현체 (Service, RestController, Persistence)
└─📂 usecase # Application Business Rules: 앱 사양 정의 (Port, DTO, Model)
- 문제: 기능이 확장됨에 따라 클래스 간 의존성 파악을 위한 불필요한 소통 발생, 불명확한 구조로 유지보수 어려움
- 의사 결정: 비즈니스 로직 + 인프라/프레임워크 + 그 사이를 매개하는 계층형 아키텍처로 리팩토링
- 성과: 도메인들 간 명확한 경계 설정, Jira 백로그 기준 신규 기능 개발 기간 약 30% 단축
Flyway로 DB 형상 관리
- 문제: 수동 DDL 공유로 환경 간 스키마 불일치 발생, 변경 히스토리 추적 어려움
- 의사 결정: Flyway 도입으로 실행 시 스키마 자동 마이그레이션
- 성과: 스키마 정합성 문제 제거 및 롤백 가능 구조 확보
JPA의 N+1 문제 근절 및 대량 데이터 조회 최적화
- 문제: 여러 테이블이 JOIN되는 대량의 데이터 조회 시 JPA의 N+1 발생, 성능 저하
- 해결
- 단순 CRUD는 JPA/Hibernate로 데이터 무결성 확보
- 복잡한 통계와 Read 작업은 jOOQ로 Type-Safe하게 수행 및 성능 최적화
- 성과: JPA의 N+1문제 근절 및 컴파일 타임 에러 확인 가능
- 일관된 응답 구조: status, code, data
- 에러 코드 기반 클라이언트 처리 가능 구조
- 버전 관리 가능한 REST URI (/api/v1/...)
요청
- postId: 댓글이 속한 게시글 식별자 (ULID)
- path: 계층형 댓글 구조를 표현하는 경로 값 (숫자.숫자.숫자...)
- content: 댓글 내용
{
"postId": "string"
"path": "string"
"content": "string"
}응답
- status: HTTP 상태 코드
- code: 클라이언트 분기 처리를 위한 응답 코드
- message: 사용자 또는 디버깅을 위한 메시지
{
"status": 200,
"code": "generic_success",
"message": ""
}{
"status": 400,
"code": "empty_post_id",
"message": "게시글의 식별자 값이 비었습니다"
}- Jira 기반 이슈 및 브랜치 관리 + Confluence 기반 문서 아카이브
- Gitmoji 기반 커밋 메시지 형식 통일 -> 코드 변경 이력 파악 시간 감소
- PR 기반 코드리뷰로 품질 관리 -> 코드 품질 향상
- Jira 이슈 단위로 브랜치 생성 (ex. MP-123)
- 작업 완료 후 PR -> 코드 리뷰 -> develop 병합
Gitmoji 목록
| 이모티콘 | 커밋 유형 | 의미 |
|---|---|---|
✨ :sparkles: |
Feat |
새로운 기능 추가 |
🐛 :bug: |
Fix |
버그 해소 |
🚑 :ambulance: |
HOTFIX! |
긴급 버그 해소 |
💄 :lipstick: |
Style |
CSS 및 UI 스타일 추가 및 수정 |
♻️ :recycle: |
Refactor |
코드 리팩토링 |
🎨 :art: |
Format |
코드 구조 / 서식 변경 (코드 자체 변경 없음) |
💥 :boom: |
BREAKING! |
거대한 변경 |
🔥 :fire: |
Remove |
파일 삭제 |
🥅 :goal_net: |
Catch |
잘못 작성된 코드 수정 |
➕ :heavy_plus_sign: |
Dependency |
의존성 추가 |
➖ :heavy_minus_sign: |
Dependency |
의존성 제거 |
📝 :memo: |
Docs |
문서 추가 및 수정 |
⏪️ :rewind: |
Revert |
변경 철회 |
💬 :speech_balloon: |
Comment |
주석 추가 및 수정 |
🚚 :truck: |
Rename |
파일 또는 폴더명 수정 및 이동 |
✅ :white_check_mark: |
Test |
테스트 코드 추가 및 수정 |
🔧 :wrench: |
Chore |
잡다한 과업 |
🎉 :tada: |
Begin |
프로젝트 시작 |
MP-289 :bug: Fix: 게시글 여러 번 수정 시 발생하는 낙관적 락 에러 해결
- 낙관적 락 충돌 문제를 방지하기 위해 전체 매핑 후 저장 방식을 제거하고 개별 필드 업데이트로 변경
- 엔티티 업데이트 메서드 추가


