[EPIC] 실용적 DDD 리팩토링 v2 - 복잡도 기반 선택적 적용
🔗 Related Issues & PRs
| Phase |
Issue |
PR |
Status |
| 0 |
#147 - [DOCS] 현재 코드베이스 분석 및 복잡도 분류 |
#148 |
✅ |
| 1 |
#149 - [DOCS] 테스트 인프라 표준화 및 작성 가이드 문서화 |
#150 |
✅ |
| 2 |
#151 - [REFACTOR] 패키지 구조 1차 정리 |
#152 |
✅ |
| 3-1 |
#153 - [REFACTOR] Notification 도메인 단순화 (파일럿) |
#154 |
✅ |
| 3-2 |
#155 - [REFACTOR] CoreValue 도메인 단순화 |
#156 |
✅ |
| 3-3 |
#157 - [REFACTOR] FAQ 도메인 단순화 |
#158 |
✅ |
| 3-4 |
#159 - [REFACTOR] Generation 도메인 단순화 |
#160 |
✅ |
| 3-5 |
#161 - [REFACTOR] Member 도메인 단순화 |
#162 |
✅ |
| 3-6 |
#163 - [REFACTOR] Part 도메인 단순화 |
#164 |
✅ |
| 3-7 |
#165 - [REFACTOR] Recruitment 도메인 단순화 |
#166 |
✅ |
| 3-8 |
#167 - [REFACTOR] News 도메인 단순화 |
#168 |
✅ |
| 4 |
Review, SoptStory 테스트 보완 |
- |
✅ (기존 완료) |
| 5 |
Homepage/Admin 조합 서비스 정리 |
- |
🔄 진행중 |
🎯 개요
배경: 왜 v2인가?
v1 리팩토링의 문제점
❌ 모든 도메인에 동일한 Full DDD 패턴을 적용하려 함
❌ 단순한 필드(이메일, 기수)에도 전용 VO + 예외 + 에러코드 생성
❌ 과잉 엔지니어링으로 오히려 복잡성 증가
❌ "왜 이렇게까지 했지?"라는 의문을 후임자가 가질 수 있음
❌ 결론적으로 오히려 인수인계 및 유지보수 측면에서 안좋아짐
구체적 문제 예시 (Notification 도메인):
// Before: v1 - 과잉 엔지니어링
@Entity
public class Notification {
@Embedded
private Email email; // VO - 단순 형식 검증만 하는데 전용 클래스
@Embedded
private Generation generation; // VO - 양수 검증만 하는데 전용 클래스
}
@Embeddable
public class Email {
private String value;
public Email(String value) {
if (!EMAIL_PATTERN.matcher(value).matches()) {
throw NotificationDomainException.emailInvalidFormat(value);
// ↑ 전용 예외 + 에러코드까지...
}
this.value = value;
}
}
// 파일 수: 12개 (Entity, VO 2개, Repository 2개, Service 2개,
// Exception 2개, Controller, DTO 3개...)
v2 리팩토링의 방향
✅ 비즈니스 규칙 복잡도에 따라 선택적 적용
✅ 복잡한 규칙이 있는 도메인 → Full DDD 유지 (Review, SoptStory)
✅ 단순 CRUD 도메인 → Light 패턴으로 단순화 (Notification 등 9개)
✅ 핵심 목표: 인수인계에 도움이 되는 살아있는 테스트 코드
개선된 코드 (Notification 도메인):
// After: v2 - 실용적 접근
@Entity
@Table(name = "\"Notification\"")
public class Notification {
@Email(message = "유효한 이메일 형식이 아닙니다")
@NotBlank(message = "이메일은 필수입니다")
@Column(nullable = false)
private String email; // ✅ @Valid 어노테이션으로 충분
@Min(value = 1, message = "기수는 1 이상이어야 합니다")
@NotNull(message = "기수는 필수입니다")
@Column(nullable = false)
private Integer generation; // ✅ @Valid 어노테이션으로 충분
public static Notification of(String email, Integer generation) {
Notification notification = new Notification();
notification.email = email;
notification.generation = generation;
return notification;
}
}
// 파일 수: 7개 (42% 감소!)
최종 목표
┌─────────────────────────────────────────────────────────────────┐
│ 6개월마다 팀원이 바뀌는 환경에서 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1️⃣ 테스트 코드가 "살아있는 인수인계 문서" 역할 │
│ → 테스트만 읽으면 비즈니스 규칙 이해 가능 │
│ │
│ 2️⃣ 새 팀원 온보딩 시간 단축 │
│ → 코드 구조가 직관적이고 일관성 있음 │
│ │
│ 3️⃣ 리팩토링/기능 추가 시 회귀 버그 방지 │
│ → 통합 테스트가 안전망 역할 │
│ │
│ 4️⃣ 과잉 엔지니어링 제거 │
│ → 유지보수 부담 감소, 코드 이해도 향상 │
│ │
└─────────────────────────────────────────────────────────────────┘
적용 전략
Full vs Light 판단 기준
| 질문 |
Yes → Full 적용 |
예시 |
| 조건부 검증이 있나? |
"A일 때 B가 필수" 같은 규칙 |
Review: 전체활동 → 세부활동 필수 |
| 상태 변화 로직이 있나? |
좋아요 증감, 상태 전이 |
SoptStory: LikeCount 증감 |
| 여러 필드 간 관계가 있나? |
카테고리에 따라 세부항목 결정 |
Review: category ↔ subjects |
| 계산 로직이 있나? |
날짜 계산, 금액 계산 등 |
- |
2개 이상 충족 시 Full DDD, 그 외 Light 패턴
최종 분류 결과
┌─────────────────────────────────────────────────────────────────────────┐
│ 최종 분류 결과 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 🟢 Full DDD 유지 (2개) │
│ ├── Review : 카테고리-세부주제 조건부 검증 │
│ │ └── "전체활동이면 세부활동 필수", "서류/면접이면 세부유형 필수" │
│ └── SoptStory : 좋아요 증감 규칙, IP 중복 체크 │
│ └── LikeCount 불변성, 음수 불가, IP 기반 중복 방지 │
│ │
│ 🟡 Light 단순화 (9개) │
│ ├── Notification : VO 제거, @Valid 전환 │
│ ├── CoreValue : Command/Query 통합 │
│ ├── FAQ : Command/Query 통합 (QuestionAnswer JSON 유지) │
│ ├── Generation : Command/Query 통합 (BrandingColor, MainButton 유지) │
│ ├── Member : Command/Query 통합 (MemberRole, SnsLinks 유지) │
│ ├── Part : Command/Query 통합 (VO 없음) │
│ ├── Recruitment : Command/Query 통합 (Schedule, RecruitType 유지) │
│ ├── RecruitPartIntroduction : 별도 패키지 분리 (PartIntroduction 유지) │
│ └── News : 레거시 정리, Light로 전환 │
│ │
│ 🔵 조합 서비스 (2개) │
│ ├── Homepage : 여러 도메인 Query 조합 → application/ 이동 │
│ └── Admin : 여러 도메인 Command 조합 → application/ 이동 │
│ │
│ ⚪ 외부 연동 (리팩토링 범위 외) │
│ ├── Project : Playground API 호출 위주 │
│ └── infrastructure/external : auth, crew, playground, scrap │
│ │
└─────────────────────────────────────────────────────────────────────────┘
도메인별 테스트 전략
| 도메인 유형 |
단위 테스트 |
통합 테스트 |
이유 |
| 🟢 Full DDD (Review, SoptStory) |
✅ 필수 |
✅ 필수 |
복잡한 비즈니스 규칙 검증 필요 |
| 🟡 Light (나머지 9개) |
❌ 불필요 |
✅ 필수 |
단순 CRUD, 통합 테스트로 충분 |
| 🔵 조합 서비스 (Homepage, Admin) |
❌ 불필요 |
✅ 필수 |
여러 도메인 조합 검증 |
📋 목표 (What)
📊 진행 상황
Phase 0: 분석 및 계획 ✅
산출물: docs/analysis-v2.md
| 분석 항목 |
결과 |
| 전체 도메인 수 |
13개 |
| Full DDD 대상 |
2개 (Review, SoptStory) |
| Light 대상 |
9개 |
| 조합 서비스 |
2개 (Homepage, Admin) |
| 제거 대상 VO |
2개 (Email, Generation) |
| 유지 대상 VO |
8개 (의미있는 필드 묶음) |
Phase 1: 테스트 인프라 구축 ✅
산출물: docs/testing-guide.md
테스트 피라미드:
┌───────────┐
│ E2E │ ← 최소한 (CI/CD에서 API 호출)
─┼───────────┼─
/ │ 통합 │ \ ← 핵심 (모든 도메인)
/ │ 테스트 │ \
─┼───┼───────────┼───┼─
/ │ │ 단위 │ │ \ ← Full DDD 도메인만
/ │ │ 테스트 │ │ \
────┴───┴───────────┴───┴────
테스트 명명 규칙:
| 이모지 |
용도 |
예시 |
| ✅ |
정상/성공 케이스 |
@DisplayName("✅ 정상: 알림 등록 성공") |
| ❌ |
실패/예외 케이스 |
@DisplayName("❌ 실패: 중복 등록 불가") |
| 🔍 |
조회/검색 케이스 |
@DisplayName("🔍 조회: 기수별 필터링") |
| ⚡ |
성능/경계값 테스트 |
@DisplayName("⚡ 대량 데이터 조회") |
Phase 2: 패키지 구조 정리 ✅
산출물: docs/phase2-package-structure.md
Before → After:
Before: After:
sopt.org.homepage/ sopt.org.homepage/
├── admin/ │
├── aws/ ├── global/ 🌐 전역 공통
│ └── s3/ │ ├── common/
├── cache/ │ │ ├── constants/
├── common/ │ │ ├── dto/
│ ├── constants/ │ │ ├── filter/
│ ├── dto/ │ │ ├── type/
│ ├── filter/ │ │ └── util/
│ ├── type/ │ ├── config/
│ └── util/ │ └── exception/
├── config/ │
├── exception/ ├── infrastructure/ 🔧 인프라 계층
├── homepage/ │ ├── aws/s3/
├── internal/ │ ├── cache/
│ ├── auth/ │ └── external/
│ ├── crew/ │ ├── auth/
│ └── playground/ │ ├── crew/
├── notification/ │ ├── playground/
├── review/ │ └── scrap/
├── soptstory/ │
├── corevalue/ ├── application/ 📱 응용 서비스
├── member/ │ ├── admin/
├── part/ │ ├── homepage/
├── generation/ │ └── visitor/
├── recruitment/ │
├── faq/ └── [도메인들]/ 🎯 도메인 계층
├── news/ ├── notification/
├── project/ ├── corevalue/
├── scrap/ ├── faq/
└── visitor/ ├── generation/
├── member/
├── part/
├── recruitment/
├── recruitpartintroduction/
├── news/
├── project/
├── review/ (Full DDD)
└── soptstory/ (Full DDD)
트러블슈팅:
// FeignClient 빈 인식 실패 해결
// Before
@EnableFeignClients(basePackages = "sopt.org.homepage.internal")
// After
@EnableFeignClients(basePackages = "sopt.org.homepage.infrastructure.external")
Phase 3: Light 도메인 단순화 ✅
3-1. Notification (파일럿) ⭐
| 항목 |
Before |
After |
개선 |
| 파일 수 |
12개 |
7개 |
42% 감소 |
| VO 클래스 |
2개 |
0개 |
100% 제거 |
| Service 클래스 |
2개 |
1개 |
50% 감소 |
| Repository |
2개 |
1개 |
50% 감소 |
| 예외 클래스 |
2개 |
1개 |
50% 감소 |
핵심 변경:
// Before: 전용 VO
@Embedded private Email email;
@Embedded private Generation generation;
// After: Bean Validation
@Email @NotBlank private String email;
@Min(1) @NotNull private Integer generation;
3-2. CoreValue
| 항목 |
Before |
After |
개선 |
| 파일 수 |
10개 |
7개 |
30% 감소 |
| QueryDSL 구현체 |
1개 |
0개 |
제거 |
3-3. FAQ
| 항목 |
Before |
After |
개선 |
| 파일 수 |
10개 |
7개 |
30% 감소 |
| VO 유지 |
QuestionAnswer |
✅ |
JSON 저장 |
3-4. Generation
| 항목 |
Before |
After |
개선 |
| 파일 수 |
11개 |
8개 |
27% 감소 |
| VO 유지 |
BrandingColor, MainButton |
✅ |
4개/3개 필드 묶음 |
특이사항: PK가 Integer이고 자동 생성 아닌 기수 번호 직접 사용 (35, 36...)
3-5. Member
| 항목 |
Before |
After |
개선 |
| 파일 수 |
13개 |
10개 |
23% 감소 |
| VO 유지 |
MemberRole, SnsLinks |
✅ |
Enum + 4개 SNS 링크 |
레거시 호환:
// Admin에서 문자열 role로 요청
MemberRole.fromLegacyRole("회장") // → MemberRole.PRESIDENT
3-6. Part
| 항목 |
Before |
After |
개선 |
| 파일 수 |
12개 |
9개 |
25% 감소 |
| VO |
없음 |
- |
가장 단순 |
특이사항: curriculums는 JSON으로 저장 (List<String>)
3-7. Recruitment + RecruitPartIntroduction
| 항목 |
Before |
After |
개선 |
| 파일 수 |
24개 |
16개 |
33% 감소 |
| 패키지 |
1개 |
2개 |
관심사 분리 |
| VO 유지 |
Schedule, RecruitType, PartIntroduction |
✅ |
|
패키지 분리:
recruitment/ # 모집 일정
├── Recruitment.java
├── RecruitmentRepository.java
├── RecruitmentService.java
└── vo/
├── Schedule.java # 6개 일정 필드 묶음
└── RecruitType.java # OB/YB Enum
recruitpartintroduction/ # 파트별 모집 소개 (분리)
├── RecruitPartIntroduction.java
├── RecruitPartIntroductionRepository.java
├── RecruitPartIntroductionService.java
└── vo/
└── PartIntroduction.java # content + preference
3-8. News
| 항목 |
Before |
After |
개선 |
| 파일 수 |
15개 |
12개 |
20% 감소 |
| Entity 이름 |
MainNewsEntity |
News |
정리 |
특이사항:
- Admin DTO 6개를
application/admin/dto/로 이동
- Presigned URL V2 API 유지 (Lambda 10MB 제한 우회)
Phase 4: Full 도메인 검증 ✅
Full DDD 도메인은 v1에서 이미 적절하게 구현되어 있어 테스트 보완만 진행.
Review 도메인
비즈니스 규칙:
// 카테고리-세부주제 조건부 검증
public void validateForCategory(ReviewCategory category) {
if (category.requiresSubActivities() && isEmpty()) {
throw new InvalidReviewSubjectException(
"전체활동 카테고리는 세부 활동이 필수입니다."
);
}
if (category.isRecruitingCategory() && isEmpty()) {
throw new InvalidReviewSubjectException(
"서류/면접 카테고리는 세부 유형이 필수입니다."
);
}
}
테스트 현황:
| 테스트 유형 |
파일 |
상태 |
| 단위 테스트 |
ReviewTest, ReviewSubjectsTest, ReviewCategoryTest, ReviewContentTest, ReviewAuthorTest, ReviewUrlTest |
✅ |
| 통합 테스트 |
ReviewCommandServiceTest, ReviewQueryServiceTest |
✅ |
SoptStory 도메인
비즈니스 규칙:
// 좋아요 증감 규칙 (불변 VO)
public LikeCount increment() {
if (value >= MAX_COUNT) {
throw new IllegalStateException("좋아요 개수가 최대값에 도달했습니다.");
}
return new LikeCount(this.value + 1);
}
public LikeCount decrement() {
if (value <= MIN_COUNT) {
throw new IllegalStateException("좋아요 개수는 음수가 될 수 없습니다.");
}
return new LikeCount(this.value - 1);
}
테스트 현황:
| 테스트 유형 |
파일 |
상태 |
| 단위 테스트 |
SoptStoryTest, SoptStoryLikeTest, LikeCountTest, SoptStoryContentTest, SoptStoryUrlTest, IpAddressTest |
✅ |
| 통합 테스트 |
SoptStoryCommandServiceTest, SoptStoryQueryServiceTest |
✅ |
Phase 5: 조합 서비스 정리 🔄
패키지 이동은 Phase 2에서 완료. 통합 테스트 작성 진행 중.
현재 구조
application/
├── admin/ ✅ 패키지 이동 완료
│ ├── AdminController.java
│ ├── service/
│ │ ├── AdminService.java
│ │ └── AdminServiceImpl.java
│ └── dto/
│ ├── request/
│ └── response/
│
├── homepage/ ✅ 패키지 이동 완료
│ ├── controller/
│ │ └── HomepageController.java
│ ├── service/
│ │ └── HomepageQueryService.java
│ └── dto/
│ ├── MainPageResponse.java
│ ├── AboutPageResponse.java
│ └── RecruitPageResponse.java
│
└── visitor/
├── VisitorController.java
└── VisitorService.java
HomepageQueryService 분석
| 메서드 |
조합 도메인 |
외부 API |
복잡도 |
getMainPageData() |
Generation, Part, News, Recruitment |
❌ |
중 |
getAboutPageData() |
Generation, CoreValue, Part, Member |
✅ Playground, Crew, Auth |
상 |
getRecruitPageData() |
Generation, Recruitment, RecruitPartIntroduction, FAQ |
❌ |
중 |
AdminServiceImpl 분석
| 메서드 |
조합 도메인 |
인프라 |
복잡도 |
addMainData() |
- |
S3, Cache |
상 |
addMainDataConfirm() |
Generation, CoreValue, Member, Part, Recruitment, RecruitPartIntroduction, FAQ |
S3, Cache |
매우 높음 |
getMain() |
Generation, CoreValue, Member, Part, Recruitment, RecruitPartIntroduction, FAQ, News |
- |
상 |
작업 현황
Phase 6: DB 스키마 정리 (예정)
현재 Flyway 현황:
src/main/resources/db/migration/
└── V1__init_notification_table.sql # Notification 테이블만 있음
주요 테이블:
| 테이블 |
도메인 |
상태 |
| Notification |
Notification |
✅ |
| Review |
Review |
✅ |
| SoptStory |
SoptStory |
✅ |
| SoptStoryLike |
SoptStory |
✅ |
| Generation |
Generation |
✅ |
| CoreValue |
CoreValue |
✅ |
| Member |
Member |
✅ |
| Part |
Part |
✅ |
| Recruitment |
Recruitment |
✅ |
| RecruitPartIntroduction |
RecruitPartIntroduction |
✅ |
| FAQ |
FAQ |
✅ |
| MainNews |
News |
✅ |
Phase 7: 문서화 및 마무리 (예정)
📈 성과 요약
정량적 성과
| 항목 |
Before |
After |
개선 |
| Light 도메인 평균 파일 수 |
~12개 |
~8개 |
33% 감소 |
| 불필요한 VO |
2개 |
0개 |
100% 제거 |
| 유지된 VO (의미있는) |
8개 |
8개 |
유지 |
| Application 계층 분리 |
❌ |
✅ |
명확한 책임 분리 |
| 테스트 커버리지 도메인 |
3개 |
11개 |
267% 증가 |
| 테스트 완료율 |
- |
10/11 |
91% |
도메인별 테스트 현황
| 도메인 |
테스트 파일 |
케이스 수 |
상태 |
| Notification |
NotificationServiceTest |
5개 |
✅ |
| CoreValue |
CoreValueServiceTest |
3개 |
✅ |
| FAQ |
FAQServiceTest |
7개 |
✅ |
| Generation |
GenerationServiceTest |
7개 |
✅ |
| Member |
MemberServiceTest |
4개 |
✅ |
| Part |
PartServiceTest |
4개 |
✅ |
| Recruitment |
- |
- |
❌ 누락 |
| RecruitPartIntroduction |
RecruitPartIntroductionServiceTest |
5개 |
✅ |
| News |
NewsServiceTest |
6개 |
✅ |
| Review |
단위 6개 + 통합 2개 |
다수 |
✅ |
| SoptStory |
단위 6개 + 통합 2개 |
다수 |
✅ |
VO 처리 결과
| 처리 |
대상 |
이유 |
| ❌ 제거 |
Email, Generation |
@Valid 어노테이션으로 충분 |
| ✅ 유지 |
BrandingColor |
4개 컬러 필드 묶음 (main, high, low, point) |
| ✅ 유지 |
MainButton |
3개 필드 묶음 (text, keyColor, subColor) |
| ✅ 유지 |
MemberRole |
Enum (회장, 부회장, 총무...) |
| ✅ 유지 |
SnsLinks |
4개 SNS 링크 묶음 |
| ✅ 유지 |
Schedule |
6개 일정 필드 묶음 |
| ✅ 유지 |
RecruitType |
Enum (OB, YB) |
| ✅ 유지 |
PartIntroduction |
content + preference 묶음 |
| ✅ 유지 |
QuestionAnswer |
question + answer 묶음 (JSON) |
🚨 리스크 관리
DB 변경 안전 원칙
❌ 코드 정리 중간에 DB 스키마 변경 금지
❌ 여러 테이블을 한 번에 변경 금지
✅ Phase 5 완료 후에만 DB 작업 시작
✅ 각 마이그레이션마다 롤백 스크립트 준비
체크포인트
| Phase |
체크포인트 |
상태 |
| 2 |
전체 테스트 통과 확인 |
✅ |
| 3 (Notification) |
패턴 리뷰 및 확정 |
✅ |
| 5 |
코드 레벨 리팩토링 완료 |
🔄 |
| 6 시작 전 |
백업 + 롤백 계획 수립 |
⏳ |
📚 참고 자료
프로젝트 문서
| 문서 |
설명 |
위치 |
| 분석 결과 |
전체 도메인 분석 및 복잡도 분류 |
docs/analysis-v2.md |
| 패키지 구조 |
목표 패키지 구조 정의 |
docs/phase2-package-structure.md |
| Notification 파일럿 |
Light 패턴 가이드 |
docs/phase3-notification-light.md |
| 테스트 가이드 |
테스트 작성 기준 |
docs/testing-guide.md |
| v1 리팩토링 |
기존 리팩토링 문서 |
docs/refactoring-v1.md |
외부 참고
🔜 다음 단계
즉시 작업 필요
| 우선순위 |
작업 |
예상 시간 |
필수 여부 |
| 1 |
RecruitmentServiceTest 작성 |
20분 |
⭐ 필수 |
| 2 |
HomepageQueryServiceTest 작성 |
40분 |
권장 |
| 3 |
AdminServiceTest 작성 |
60분 |
권장 |
📝 회고 및 교훈
잘한 점
- 파일럿 접근 - Notification으로 패턴 확립 후 나머지 적용
- 단계적 진행 - Phase별 체크포인트로 안정성 확보
- VO 선별적 유지 - 의미있는 VO는 유지하여 도메인 표현력 보존
- 문서화 병행 - 각 Phase마다 문서 업데이트
개선점
- 테스트 누락 - RecruitmentServiceTest 빠짐
- 조합 서비스 테스트 - 복잡도 높아 우선순위 밀림
향후 적용 시 권장사항
1️⃣ 분석 먼저: 무조건 Full DDD 적용하지 말고 복잡도 분석부터
2️⃣ 파일럿 필수: 가장 단순한 도메인으로 패턴 확립
3️⃣ VO 기준 명확히: "여러 필드 묶음"인가? → 유지, "단순 검증"인가? → 제거
4️⃣ 테스트 = 문서: 비즈니스 규칙을 테스트로 표현
5️⃣ 점진적 개선: 한 번에 다 하려고 하지 말 것
[EPIC] 실용적 DDD 리팩토링 v2 - 복잡도 기반 선택적 적용
🔗 Related Issues & PRs
🎯 개요
배경: 왜 v2인가?
v1 리팩토링의 문제점
구체적 문제 예시 (Notification 도메인):
v2 리팩토링의 방향
개선된 코드 (Notification 도메인):
최종 목표
적용 전략
Full vs Light 판단 기준
최종 분류 결과
도메인별 테스트 전략
📋 목표 (What)
📊 진행 상황
Phase 0: 분석 및 계획 ✅
산출물:
docs/analysis-v2.mdPhase 1: 테스트 인프라 구축 ✅
산출물:
docs/testing-guide.md테스트 피라미드:
테스트 명명 규칙:
@DisplayName("✅ 정상: 알림 등록 성공")@DisplayName("❌ 실패: 중복 등록 불가")@DisplayName("🔍 조회: 기수별 필터링")@DisplayName("⚡ 대량 데이터 조회")Phase 2: 패키지 구조 정리 ✅
산출물:
docs/phase2-package-structure.mdBefore → After:
트러블슈팅:
Phase 3: Light 도메인 단순화 ✅
3-1. Notification (파일럿) ⭐
핵심 변경:
3-2. CoreValue
3-3. FAQ
3-4. Generation
특이사항: PK가
Integer이고 자동 생성 아닌 기수 번호 직접 사용 (35, 36...)3-5. Member
레거시 호환:
3-6. Part
특이사항: curriculums는 JSON으로 저장 (
List<String>)3-7. Recruitment + RecruitPartIntroduction
패키지 분리:
3-8. News
특이사항:
application/admin/dto/로 이동Phase 4: Full 도메인 검증 ✅
Full DDD 도메인은 v1에서 이미 적절하게 구현되어 있어 테스트 보완만 진행.
Review 도메인
비즈니스 규칙:
테스트 현황:
SoptStory 도메인
비즈니스 규칙:
테스트 현황:
Phase 5: 조합 서비스 정리 🔄
패키지 이동은 Phase 2에서 완료. 통합 테스트 작성 진행 중.
현재 구조
HomepageQueryService 분석
getMainPageData()getAboutPageData()getRecruitPageData()AdminServiceImpl 분석
addMainData()addMainDataConfirm()getMain()작업 현황
application/homepage/패키지로 이동application/admin/패키지로 이동Phase 6: DB 스키마 정리 (예정)
현재 Flyway 현황:
주요 테이블:
Phase 7: 문서화 및 마무리 (예정)
📈 성과 요약
정량적 성과
도메인별 테스트 현황
VO 처리 결과
@Valid어노테이션으로 충분🚨 리스크 관리
DB 변경 안전 원칙
체크포인트
📚 참고 자료
프로젝트 문서
docs/analysis-v2.mddocs/phase2-package-structure.mddocs/phase3-notification-light.mddocs/testing-guide.mddocs/refactoring-v1.md외부 참고
🔜 다음 단계
즉시 작업 필요
RecruitmentServiceTest작성HomepageQueryServiceTest작성AdminServiceTest작성📝 회고 및 교훈
잘한 점
개선점
향후 적용 시 권장사항