1. 규칙
할일의 칸은 생성일(created_at)이다. “오늘 할일”(is_my_day)은 그 칸을 옮기는 스위치다.
| 동작 | 결과 |
|---|---|
| 켠다 | 생성일을 오늘로 옮긴다. 원래 칸에는 복사로 남기지 않는다. 떠나기 전 날을 anchor_created에 저장한다. |
| 저장한 뒤 다시 연다 | 생성일은 오늘이다. 되돌릴 날은 anchor_created에 그대로 있다. |
| 끈다 | 생성일을 anchor_created의 날로 되돌리고, 그 필드는 비운다. |
| 떠나기 전 날 = 기한 | 기한을 비운다. 그 칸에 기한 때문에 복사처럼 남지 않게 한다. |
| 이미 오늘인데 다시 켠다 | 저장돼 있는 되돌릴 날을 오늘로 덮지 않는다. |
| 생성일을 오늘이 아닌 날로 직접 고른다 | “오늘 할일”은 꺼지고 anchor_created는 비운다. 고른 날이 새 생성일이다. |
| “오늘 할일”이 켜진 채로 생성일을 오늘로 고른다 | 스위치는 유지하고, 되돌릴 날은 덮지 않는다. |
anchor_created는 사용자가 스위치를 켜서 다른 날에서 오늘로 떠날 때만 채운다.
예. 오늘은 2026-09-23.
- 9월 26일에 할일
111111111을 만들고 저장한다. 생성일 2026-09-26. - 그 할일을 열어 “오늘 할일”을 켠다. 칸은 9월 23일이 되고,
anchor_created는 2026-09-26. - 저장한 뒤 9월 23일에서 다시 열어 스위치를 끈다. 칸은 9월 26일로 돌아간다.
2. 고친 원인
아이폰 수정 전에는 되돌릴 날을 편집 시트의 메모리(_anchorCreatedIso)에만 두고,
저장되는 생성일(created_at)은 오늘로 바뀌었다.
시트를 닫으면 그 메모리가 사라져, 다시 열면 되돌릴 날이 오늘이 되었다.
아이폰 day_plan_appl_app 에는 2026-09-23에 반영됨. DB 버전 12.
기준 파일:
lib/models/todo.dart—anchorCreated,applyTodayMovelib/db/app_db.dart—anchor_created컬럼, 버전 12lib/widgets/todo_edit_sheet.dart— 시트를 다시 열어도 저장된 날로 되돌림test/today_move_test.dart
3. 공통 필드
| 이름 | 형식 | 의미 |
|---|---|---|
anchor_created |
TEXT, NULL 허용. 값 예 2026-09-26 |
“오늘 할일”을 켜기 전 생성일. 끄면 이 날로 되돌리고 NULL로 지운다. |
날짜만 저장한다. 시각은 붙이지 않는다.
업데이트할 때 키가 빠지면 이전 값이 남으므로, 끌 때는 anchor_created: null을 반드시 보낸다.
4. 안드로이드 day_plan_app
윈도우에서 연다. 날짜 규칙(생성일이 칸, “오늘 할일”이 생성일을 오늘로 이동)이
이미 들어가 있는 코드를 기준으로 한다.
스위치가 t.isMyDay = v만 하고 생성일을 옮기지 않는 옛 코드라면,
먼저 docs/2026-09-23/할일일정기록_날짜규칙.html의 “오늘 할일은 이동이다”를 넣은 다음 이 절을 적용한다.
핫 리로드만으로는 컬럼이 생기지 않는다. 앱을 완전히 다시 실행한다.
4.1 lib/db/app_db.dart
DB 버전을 현재 값보다 1 올린다. 아이폰은 11 → 12다. 윈도우 소스가 이미 12 이상이면 그 다음 번호를 쓴다.
onCreate의 tb_todo 끝에 컬럼을 넣는다.
created_at TEXT NOT NULL DEFAULT (datetime('now','localtime')),
anchor_created TEXT
onUpgrade에 추가한다. 12는 실제 올린 버전 번호로 바꾼다.
if (oldV < 12) {
await d.execute('ALTER TABLE tb_todo ADD COLUMN anchor_created TEXT');
}
반복 일정의 과거 회차를 단일 일정으로 분리하는 detachPastRepeatEvent insert 맵에
아래를 넣는다. 분리된 과거 일정은 “오늘 할일” 이동이 아니다.
..['is_my_day'] = 0
..['anchor_created'] = null
4.2 lib/models/todo.dart
필드, 생성자, fromMap, toMap, copy에 anchorCreated를 넣는다.
String? anchorCreated; // yyyy-MM-dd. 오늘 할일을 끄면 이 날로 되돌린다.
anchorCreated: m['anchor_created'] as String?,
// null도 항상 실어, 끌 때 컬럼이 지워지게 한다.
'anchor_created': anchorCreated,
이동 로직. 아이폰과 같은 메서드를 둔다.
void applyTodayMove(bool on, String today) {
isMyDay = on;
final cur = _dateHead(createdAt);
if (on) {
if (cur != null && cur != today) anchorCreated = cur;
if (dueDate != null && dueDate!.isNotEmpty && dueDate == cur) {
dueDate = null;
}
createdAt = '$today 00:00:00';
return;
}
final back = _dateHead(anchorCreated);
if (back != null) createdAt = '$back 00:00:00';
anchorCreated = null;
}
String? _dateHead(String? raw) {
if (raw == null || raw.length < 10) return null;
return raw.substring(0, 10);
}
4.3 lib/widgets/todo_edit_sheet.dart
기존 항목을 열 때, “오늘 할일”이 켜져 있고 anchorCreated가 있으면 그 날을 되돌릴 날로 쓴다. 생성일(오늘)로 되돌릴 날을 초기화하지 않는다.
final created = _dateOnly(t.createdAt);
final saved = _dateOnly(t.anchorCreated);
_anchorCreatedIso = (t.isMyDay && saved.isNotEmpty)
? saved
: (created.isNotEmpty ? created : t.dueDate);
스위치는 모델 메서드를 호출한다.
void _setTodayTask(bool on) {
t.applyTodayMove(on, isoOf(DateTime.now()));
if (on) {
final saved = _dateOnly(t.anchorCreated);
if (saved.isNotEmpty) _anchorCreatedIso = saved;
} else {
final created = _dateOnly(t.createdAt);
if (created.isNotEmpty) _anchorCreatedIso = created;
}
}
생성일을 직접 고를 때.
final iso = isoOf(d);
final today = isoOf(DateTime.now());
t.createdAt = _createdStamp(iso);
if (t.isMyDay && iso == today) return;
_anchorCreatedIso = iso;
t.anchorCreated = null;
if (iso != today) t.isMyDay = false;
스위치 onChanged는 setState(() => _setTodayTask(v))다.
4.4 테스트
test/today_move_test.dart를 아이폰과 같이 둔다. 확인할 경우:
- 9/26 생성 → 오늘 할일 켬 →
toMap/fromMap왕복 → 끄면 생성일 9/26,anchor_created는 null. - 오늘에 만든 할일을 끄면 오늘에 남는다.
- 떠나기 전 날과 기한이 같으면 기한은 null.
- 이미 오늘이고
anchorCreated가 9/26이면, 다시 켜도 9/26이 유지되고 끄면 9/26으로 돌아간다.
5. 웹 app_schedule
서버 tb_todo와 할일 편집 화면의 “오늘”(폰에서는 “오늘 할일”)에 같은 규칙을 넣는다.
칸 위치가 생성일인 날짜 규칙이 웹에 이미 있을 때를 기준으로 한다.
5.1 DB
ALTER TABLE tb_todo ADD COLUMN anchor_created TEXT NULL;
신규 스키마를 만드는 경로에도 같은 컬럼을 넣는다. 기존 행은 NULL이다.
5.2 저장·조회
- 할일 읽기·쓰기 JSON(또는 폼)에
anchor_created를 포함한다. - 스위치를 끌 때 컬럼을 NULL로 갱신한다. 필드를 생략하면 이전 날이 남는다.
- 값은
yyyy-MM-dd만 둔다.
5.3 스위치
오늘은 서버·브라우저의 로컬 날짜 하루 단위다. 비교는 yyyy-MM-dd.
- 켠다. 현재 생성일의 앞 10자가 오늘과 다르면 그 날을
anchor_created에 넣는다. 생성일을 오늘00:00:00으로 바꾼다.is_my_day를 1로 한다. 떠나기 전 날과 기한이 같으면 기한을 비운다. - 끈다.
anchor_created가 있으면 생성일을 그 날00:00:00으로 되돌린다.anchor_created는 NULL,is_my_day는 0. - 편집 화면을 다시 열 때,
is_my_day가 1이고anchor_created가 있으면 화면의 “되돌릴 날”은 그 값이다. 지금 생성일(오늘)로 바꾸지 않는다. - 생성일을 오늘이 아닌 날로 직접 바꾸면 스위치를 끄고
anchor_created를 비운다. - 스위치가 켜진 상태에서 생성일을 오늘로 고르면
anchor_created를 유지한다.
목록·달력은 추가 분기가 필요 없다. 칸은 여전히 생성일이다. 스위치가 켜져 저장되면 생성일이 오늘이라 오늘 칸에 있고, 끄면 생성일이 원래 날이라 그 칸으로 돌아간다.
6. 앱과 웹 동기화
폰과 웹이 tb_todo를 주고받으면 양쪽 스키마와 전송 맵에 anchor_created가 있어야 한다.
한쪽에만 있으면, 동기화 뒤에 되돌릴 날이 사라지거나 모르는 컬럼으로 저장이 거절된다.
- 앱
toMap/ 웹 저장 payload 에anchor_created를 넣는다. 끌 때는 null. - 받는 쪽
fromMap/ 서버 컬럼 매핑에서anchor_created를 읽는다. - 반복 회차를 새 행으로 복제할 때, 원본의
anchor_created를 함께 복사한다. 아이폰toMap이 키를 항상 포함하므로 복제 insert도 따라간다.
7. 이미 옮겨 둔 항목
이 컬럼이 생기기 전에 “오늘 할일”을 켜서 저장한 행은 원래 날이 DB에 없다. 마이그레이션으로 되돌리지 않는다. 그 항목은 생성일을 원하는 날로 한 번 다시 고른다. 그 다음부터 켰다 끄면 그 날로 돌아간다.
아이폰에서 이미 9월 23일로 옮겨 둔 111111111도 같다. 생성일을 9월 26일로 한 번 고른다.
8. 확인
오늘은 2026-09-23으로 두고, 안드로이드와 웹에서 각각 본다.
- 9월 26일에 할일을 만들고 저장한다. 9월 26일 칸에만 있다.
- 열어 “오늘 할일”을 켜고 저장한다. 9월 26일 칸에서 사라지고 9월 23일에 있다.
- 앱 또는 브라우저를 닫았다 연다. 9월 23일 항목의 스위치를 끈다. 9월 26일로 돌아간다.
- 같은 항목을 다시 켜고 저장한 뒤, 다시 끄면 또 9월 26일이다.
- 9월 26일에 만들고, 기한도 9월 26일로 둔 뒤 스위치를 켠다. 기한은 비고, 9월 26일 칸에는 남지 않는다.
- 오늘에 만든 할일의 스위치를 켜고 꺼도 오늘에 남는다.
- 스위치를 켠 상태에서 생성일을 9월 28일로 직접 고르면 스위치가 꺼지고 9월 28일에 있다. 그 뒤 스위치를 켜고 끄면 9월 28일로 돌아간다.
- 폰에서 켠 항목을 웹에서 끄면 원래 날로 돌아가고, 반대로도 같다.