Skip to content

[FEAT] 개인 Todo 기간(endDate) 지원 추가 - #459

Merged
jongjunn merged 2 commits into
developfrom
feat/mnppi-calendar-todo-end-date
Aug 13, 2026
Merged

[FEAT] 개인 Todo 기간(endDate) 지원 추가#459
jongjunn merged 2 commits into
developfrom
feat/mnppi-calendar-todo-end-date

Conversation

@MNPPI223

@MNPPI223 MNPPI223 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

📌 연관 이슈


📝 작업 내용

  • personal_todoend_date 컬럼 추가(V6.5.1~V6.5.3, 3파일 분리)
  • PersonalTodo·CreateTodoRequest·CreateTodoCommand·TodoResponseendDate 배선
  • endDate 미지정 시 서비스 계층에서 date와 동일값으로 채움(하위호환), endDate < date는 400(CAL-002)으로 거부
  • GET /api/calendar 월별 조회 쿼리를 overlap 조건으로 변경 — 기존엔 Todo의 date(시작일)만 봐서 이전 달에 시작해 이번 달로 걸치는 Todo를 놓쳤음(findAllByMemberIdAndDateBetweenfindAllByMemberIdOverlappingPeriod)

🖥️ 프론트엔드 연동 가이드 (API 명세)

1. 주요 엔드포인트

  • POST /api/todos : 개인 Todo 생성 — endDate 선택 필드 추가됨
  • GET /api/calendar?month=yyyy-MM : TODO 항목의 endDate가 이제 실제 종료일

2. 요청 파라미터 (Request) — POST /api/todos

파라미터명 위치 필수 여부 설명
title Body Y 200자 이내
date Body Y 시작일
endDate Body N 종료일, 생략 시 date와 동일(단일 날짜)

3. 정상 응답 예시 (201 Created) — POST /api/todos

응답 JSON 보기 (클릭)
{
  "success": true,
  "message": "Todo를 추가했습니다.",
  "data": {
    "id": 10,
    "title": "여행",
    "date": "2026-08-20",
    "endDate": "2026-08-25",
    "isDone": false
  }
}

4. ⚠️ 프론트엔드 참고 및 주의사항

  • endDate 생략하면 서버가 date와 동일값으로 채워서 응답에 실어 보냄 — 응답의 endDate는 항상 값이 있음(null 아님).
  • endDatedate보다 이전이면 400 CAL-002 — "종료일은 시작일보다 빠를 수 없습니다."
  • GET /api/calendar의 TODO 항목 endDate도 이제 실제 종료일로 나감(이전엔 date와 항상 같은 값이었음).

🚨 주요 에러 코드 및 예외 (Exceptions)

  • CAL-002 (400) : endDatedate보다 이전인 경우

💡 백엔드 리뷰 포인트 (Backend Review)

  • 아키텍처 및 도메인: 검증 위치를 PersonalTodoService(서비스 계층)에 뒀다 — Action.start()류 도메인 메서드는 방어용 IllegalStateException만 던지고 실제 비즈니스 검증은 서비스 계층에서 BusinessException으로 하는 이 레포 컨벤션을 그대로 따름. PersonalTodo 팩토리엔 추가 검증 안 둠(클래스 자체 주석의 YAGNI 원칙 유지).
  • 우려되는 부분이나 고민: overlap 쿼리(findAllByMemberIdAndDateLessThanEqualAndEndDateGreaterThanEqual)를 신규 인덱스 없이 기존 (member_id, date) 인덱스로 처리하기로 함 — 인당 Todo 건수가 적어 충분하다고 판단했는데, 이 판단이 맞는지 리뷰 부탁드립니다. 마이그레이션은 ALTER 하나 = 파일 하나 원칙 때문에 3개로 쪼갰습니다(V2.6.7~9 선례 참고).

✅ 체크리스트

  • 코드 컨벤션을 준수했습니다.
  • 불필요한 주석 및 콘솔 로그를 제거했습니다.
  • API 기능이 정상 동작하는지 테스트했습니다.
  • 예외(잘못된 값) 상황에 대한 검증 및 테스트를 통과했습니다.

Summary by CodeRabbit

  • 새 기능

    • Todo에 종료일을 설정할 수 있습니다.
    • 종료일을 지정하지 않으면 시작일과 동일하게 처리됩니다.
    • 조회 기간과 겹치는 Todo가 캘린더에 표시됩니다.
    • Todo 응답에 종료일 정보가 포함됩니다.
  • 버그 수정

    • 종료일이 시작일보다 빠른 경우 오류 메시지를 표시합니다.
    • 기존 Todo의 종료일은 시작일을 기준으로 유지됩니다.

CreateTodoRequest.date 단일 필드만 지원하던 개인 Todo에 endDate를 추가해
기간 입력을 지원한다. endDate 미지정 시 date와 동일값으로 채워 기존 단일
날짜 Todo와 하위호환을 유지하고, endDate < date는 CAL-002로 거부한다.

월간 캘린더 조회(GET /api/calendar)도 함께 고쳤다 — 기존 findAllByMemberIdAndDateBetween은
Todo의 date(시작일)만 봐서 이전 달에 시작해 이번 달로 넘어오는 Todo를 놓쳤다.
findAllByMemberIdOverlappingPeriod로 개명하고 [date, endDate] 구간이 조회
월과 겹치는지로 판정하도록 수정.

DB는 V6.5.1(컬럼 추가)~V6.5.3(NOT NULL 전환) 3개 파일로 나눠 진행
(ALTER 하나 = 파일 하나 원칙, V2.6.7~9 선례 따름).
@MNPPI223 MNPPI223 added this to Z Aug 13, 2026
@github-project-automation github-project-automation Bot moved this to Todo in Z Aug 13, 2026
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 04592558-11e8-4231-bfae-b79d0240b5f9

📥 Commits

Reviewing files that changed from the base of the PR and between 64a19cf and e1191b3.

📒 Files selected for processing (1)
  • src/main/java/com/module06/backend/calendar/infrastructure/persistence/SpringDataPersonalTodoRepository.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/module06/backend/calendar/infrastructure/persistence/SpringDataPersonalTodoRepository.java

📝 Walkthrough

Walkthrough

개인 Todo가 시작일과 종료일을 갖는 기간 Todo로 확장되었습니다. 생성 요청은 종료일을 전달하며, 미지정 시 시작일을 사용합니다. 저장소는 기간 겹침 조건으로 조회하고, 캘린더 응답은 실제 종료일을 반환합니다.

Changes

개인 Todo 기간 지원

Layer / File(s) Summary
Todo 기간 계약과 생성 처리
src/main/java/com/module06/backend/calendar/presentation/api/request/CreateTodoRequest.java, src/main/java/com/module06/backend/calendar/application/command/CreateTodoCommand.java, src/main/java/com/module06/backend/calendar/application/service/PersonalTodoService.java, src/main/java/com/module06/backend/calendar/domain/model/PersonalTodo.java, src/main/java/com/module06/backend/calendar/exception/CalendarErrorCode.java, src/main/java/com/module06/backend/calendar/presentation/api/TodoController.java, src/main/java/com/module06/backend/calendar/presentation/api/response/TodoResponse.java, src/test/java/com/module06/backend/calendar/application/service/PersonalTodoServiceTest.java, src/test/java/com/module06/backend/calendar/presentation/api/TodoControllerTest.java
endDate가 생성 요청, 커맨드, 도메인 모델, 응답에 추가되었습니다. 종료일이 없으면 시작일을 사용하고, 시작일보다 빠르면 TODO_INVALID_DATE_RANGE를 발생시킵니다. 생성 및 API 테스트가 갱신되었습니다.
Todo 기간 저장과 겹침 조회
src/main/resources/db/migration/V6.5.*.sql, src/main/java/com/module06/backend/calendar/infrastructure/persistence/PersonalTodoJpaEntity.java, src/main/java/com/module06/backend/calendar/infrastructure/persistence/PersonalTodoPersistenceAdapter.java, src/main/java/com/module06/backend/calendar/infrastructure/persistence/SpringDataPersonalTodoRepository.java, src/main/java/com/module06/backend/calendar/domain/repository/PersonalTodoRepository.java, src/test/java/com/module06/backend/calendar/infrastructure/persistence/PersonalTodoPersistenceAdapterTest.java
personal_todo.end_date 컬럼을 추가하고 기존 데이터를 백필한 뒤 NOT NULL로 변경합니다. 영속성 변환과 조회 메서드는 종료일을 사용하며, 기간 겹침 조건을 테스트합니다.
캘린더 겹침 조회와 종료일 매핑
src/main/java/com/module06/backend/calendar/application/service/CalendarQueryService.java, src/test/java/com/module06/backend/calendar/application/service/CalendarQueryServiceTest.java
월별 개인 Todo 조회가 조회 월과 겹치는 기간을 반환하도록 변경되었습니다. 캘린더 항목의 종료일은 todo.getEndDate()에서 매핑됩니다. 관련 mock 설정이 갱신되었습니다.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Mergeability Score: ⚪ Minimal · up to e1191

This change adds Todo end dates and overlap-based calendar queries without any identified actionable merge-blocking risk remaining.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant TodoController
  participant PersonalTodoService
  participant PersonalTodoPersistenceAdapter
  participant CalendarQueryService

  Client->>TodoController: endDate 포함 Todo 생성 요청
  TodoController->>PersonalTodoService: CreateTodoCommand 전달
  PersonalTodoService->>PersonalTodoPersistenceAdapter: 종료일이 반영된 Todo 저장
  PersonalTodoPersistenceAdapter-->>PersonalTodoService: 저장 결과 반환
  Client->>CalendarQueryService: 월별 캘린더 조회
  CalendarQueryService->>PersonalTodoPersistenceAdapter: 월 기간 겹침 조회
  PersonalTodoPersistenceAdapter-->>CalendarQueryService: 겹치는 Todo 반환
  CalendarQueryService-->>Client: endDate 포함 캘린더 응답
Loading

Possibly related issues

  • Z-Groupware/BACKEND#456: CalendarQueryService의 Todo 캘린더 매핑과 연결되지만, 이 변경은 idisDone 필드를 추가하지 않습니다.

Possibly related PRs

Suggested reviewers: mosungjin

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 개인 Todo의 기간(endDate) 지원이라는 주요 변경 사항을 정확하고 간결하게 설명합니다.
Linked Issues check ✅ Passed [458]의 endDate, DB 마이그레이션, 기본값 처리, 날짜 검증, 응답 및 기간 겹침 조회 요구 사항을 모두 반영합니다.
Out of Scope Changes check ✅ Passed 변경 사항과 테스트가 개인 Todo 기간 지원 및 관련 데이터 조회 요구 사항에 한정되어 있습니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mnppi-calendar-todo-end-date

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
src/main/resources/db/migration/V6.5.3__set_end_date_not_null.sql (1)

1-4: 🗄️ Data Integrity & Integration | 🔵 Trivial

구버전 애플리케이션의 쓰기 가능 시점을 확인해 주세요.

롤링 배포에서 구버전 인스턴스가 이 마이그레이션 이후에도 Todo를 생성할 수 있다면, end_date 기본값이 없으므로 end_date를 전달하지 않는 INSERT가 실패합니다. 구버전 쓰기가 남을 수 있으면 새 애플리케이션을 먼저 배포해 end_date를 기록한 뒤 구버전 인스턴스를 종료하고 이 제약을 적용하세요. 구버전 쓰기가 없는 배포 방식이면 그 전제를 배포 절차에 명시하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/db/migration/V6.5.3__set_end_date_not_null.sql` around
lines 1 - 4, Ensure the deployment sequence prevents legacy application
instances from writing after this migration: deploy the version that always
populates end_date, wait for older instances to stop, then apply the NOT NULL
change in the migration. If the deployment model guarantees no legacy writes,
document that prerequisite in the release procedure before applying this
migration.
src/main/resources/db/migration/V6.5.1__add_end_date_to_personal_todo.sql (1)

4-5: 🚀 Performance & Scalability | 🔵 Trivial

새 기간 겹침 조회를 위한 인덱스 계획을 확인해 주세요.

기존 src/main/resources/db/migration/V6.1.1__create_personal_todo.sql Line 31의 인덱스는 (member_id, date)입니다. 새 조회는 date <= periodEnd에 하한이 없고 end_date >= periodStart를 추가합니다. 따라서 회원의 과거 Todo를 넓게 스캔한 뒤 종료일을 필터링할 수 있습니다. 데이터가 계속 누적된다면 실제 데이터량으로 EXPLAIN을 실행하고, 실행 계획이 악화될 때 별도 마이그레이션에서 복합 인덱스를 추가하거나 조정하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/db/migration/V6.5.1__add_end_date_to_personal_todo.sql`
around lines 4 - 5, 기존 `(member_id, date)` 인덱스를 유지한 채 새 기간 겹침 조회의 실행 계획을 실제 데이터
규모로 `EXPLAIN`하여 확인하세요. 회원별 과거 Todo 스캔 후 `end_date` 필터링으로 성능이 악화될 경우에만 별도
마이그레이션에서 적절한 복합 인덱스를 추가하거나 조정하고, 현재 마이그레이션에는 인덱스 변경을 포함하지 마세요.
src/test/java/com/module06/backend/calendar/infrastructure/persistence/PersonalTodoPersistenceAdapterTest.java (1)

41-67: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

겹침 조건의 양쪽 방향과 경계값을 추가해 주세요.

현재 테스트는 기간 안의 Todo와 2026년 7월 28일부터 2026년 8월 3일까지의 Todo만 확인합니다. 다음 사례를 추가하세요.

  • 2026년 7월 1일부터 2026년 7월 31일까지: 제외되어야 합니다. endDate >= periodStart를 검증합니다.
  • 2026년 8월 31일부터 2026년 9월 5일까지: 포함되어야 합니다. 시작일 조건을 검증합니다.
  • 2026년 7월 31일부터 2026년 8월 1일까지: 종료일 경계를 포함해야 합니다.
  • 다기간 Todo 결과의 getEndDate()2026년 8월 3일인지 확인합니다.

현재 사례만으로는 두 겹침 조건 중 하나를 제거한 잘못된 쿼리도 통과할 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/module06/backend/calendar/infrastructure/persistence/PersonalTodoPersistenceAdapterTest.java`
around lines 41 - 67, 보완 테스트 메서드인 findsAllByMemberIdOverlappingPeriodOnly와
findsTodoThatStartsBeforeMonthButEndsInsideIt에 양방향 겹침 및 경계 사례를 추가하세요. 2026년 7월
1일~7월 31일 Todo는 제외하고, 8월 31일~9월 5일과 7월 31일~8월 1일 Todo는 포함되는지 검증하며, 다기간 Todo의
getEndDate()가 2026년 8월 3일인지도 확인하세요.
src/test/java/com/module06/backend/calendar/application/service/CalendarQueryServiceTest.java (1)

80-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

기간 Todo의 종료일 매핑을 구분해서 검증하세요.

현재 fixture는 dateendDate가 같습니다. 따라서 CalendarQueryService.toTodoItem()이 다시 todo.getDate()를 종료일로 매핑해도 이 테스트는 통과합니다. 종료일을 다른 날짜로 설정하고 CalendarItem.endDate()를 검증하세요.

테스트 보강 예시
 PersonalTodo todo = PersonalTodo.reconstitute(
-        10L, COMPANY, MEMBER, "우유 사기", LocalDate.of(2026, 8, 20), LocalDate.of(2026, 8, 20),
+        10L, COMPANY, MEMBER, "우유 사기", LocalDate.of(2026, 8, 20), LocalDate.of(2026, 8, 25),
         false, null, null);
 ...
 assertThat(todoItem.id()).isEqualTo(10L);
+assertThat(todoItem.startDate()).isEqualTo(LocalDate.of(2026, 8, 20));
+assertThat(todoItem.endDate()).isEqualTo(LocalDate.of(2026, 8, 25));
 assertThat(todoItem.isDone()).isFalse();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/module06/backend/calendar/application/service/CalendarQueryServiceTest.java`
around lines 80 - 95, Update the period Todo fixture in CalendarQueryServiceTest
so its endDate differs from date, then assert the resulting
CalendarItem.endDate() matches that distinct end date. Keep the existing type,
id, and completion assertions unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/main/resources/db/migration/V6.5.1__add_end_date_to_personal_todo.sql`:
- Around line 4-5: 기존 `(member_id, date)` 인덱스를 유지한 채 새 기간 겹침 조회의 실행 계획을 실제 데이터
규모로 `EXPLAIN`하여 확인하세요. 회원별 과거 Todo 스캔 후 `end_date` 필터링으로 성능이 악화될 경우에만 별도
마이그레이션에서 적절한 복합 인덱스를 추가하거나 조정하고, 현재 마이그레이션에는 인덱스 변경을 포함하지 마세요.

In `@src/main/resources/db/migration/V6.5.3__set_end_date_not_null.sql`:
- Around line 1-4: Ensure the deployment sequence prevents legacy application
instances from writing after this migration: deploy the version that always
populates end_date, wait for older instances to stop, then apply the NOT NULL
change in the migration. If the deployment model guarantees no legacy writes,
document that prerequisite in the release procedure before applying this
migration.

In
`@src/test/java/com/module06/backend/calendar/application/service/CalendarQueryServiceTest.java`:
- Around line 80-95: Update the period Todo fixture in CalendarQueryServiceTest
so its endDate differs from date, then assert the resulting
CalendarItem.endDate() matches that distinct end date. Keep the existing type,
id, and completion assertions unchanged.

In
`@src/test/java/com/module06/backend/calendar/infrastructure/persistence/PersonalTodoPersistenceAdapterTest.java`:
- Around line 41-67: 보완 테스트 메서드인 findsAllByMemberIdOverlappingPeriodOnly와
findsTodoThatStartsBeforeMonthButEndsInsideIt에 양방향 겹침 및 경계 사례를 추가하세요. 2026년 7월
1일~7월 31일 Todo는 제외하고, 8월 31일~9월 5일과 7월 31일~8월 1일 Todo는 포함되는지 검증하며, 다기간 Todo의
getEndDate()가 2026년 8월 3일인지도 확인하세요.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e3e84b7c-dd1f-4ba3-ae30-4e1b8edfe80c

📥 Commits

Reviewing files that changed from the base of the PR and between aeefb5e and 64a19cf.

📒 Files selected for processing (19)
  • src/main/java/com/module06/backend/calendar/application/command/CreateTodoCommand.java
  • src/main/java/com/module06/backend/calendar/application/service/CalendarQueryService.java
  • src/main/java/com/module06/backend/calendar/application/service/PersonalTodoService.java
  • src/main/java/com/module06/backend/calendar/domain/model/PersonalTodo.java
  • src/main/java/com/module06/backend/calendar/domain/repository/PersonalTodoRepository.java
  • src/main/java/com/module06/backend/calendar/exception/CalendarErrorCode.java
  • src/main/java/com/module06/backend/calendar/infrastructure/persistence/PersonalTodoJpaEntity.java
  • src/main/java/com/module06/backend/calendar/infrastructure/persistence/PersonalTodoPersistenceAdapter.java
  • src/main/java/com/module06/backend/calendar/infrastructure/persistence/SpringDataPersonalTodoRepository.java
  • src/main/java/com/module06/backend/calendar/presentation/api/TodoController.java
  • src/main/java/com/module06/backend/calendar/presentation/api/request/CreateTodoRequest.java
  • src/main/java/com/module06/backend/calendar/presentation/api/response/TodoResponse.java
  • src/main/resources/db/migration/V6.5.1__add_end_date_to_personal_todo.sql
  • src/main/resources/db/migration/V6.5.2__backfill_end_date_from_date.sql
  • src/main/resources/db/migration/V6.5.3__set_end_date_not_null.sql
  • src/test/java/com/module06/backend/calendar/application/service/CalendarQueryServiceTest.java
  • src/test/java/com/module06/backend/calendar/application/service/PersonalTodoServiceTest.java
  • src/test/java/com/module06/backend/calendar/infrastructure/persistence/PersonalTodoPersistenceAdapterTest.java
  • src/test/java/com/module06/backend/calendar/presentation/api/TodoControllerTest.java

@MNPPI223 MNPPI223 self-assigned this Aug 13, 2026
@MNPPI223 MNPPI223 added the enhancement New feature or request label Aug 13, 2026
findAllByMemberIdAndDateLessThanEqualAndEndDateGreaterThanEqual가 CompanyId
없이 memberId만으로 조회해서 tenant-derived-query-without-company-scope에
걸림. member_id가 전사 유일 PK라 실제 크로스테넌트 위험은 없음(기존
ActionRepository.findAllByAssigneeMemberId와 동일 근거) — nosemgrep 예외
처리, 근거 주석 추가. 로컬에서 baseline-commit 기준 동일 스캔으로 재확인함.
@jongjunn
jongjunn merged commit 6eeae12 into develop Aug 13, 2026
15 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in Z Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[FEAT] 개인 Todo 기간(endDate) 지원 추가

2 participants