| 2026-06-10T06:27:30 |
Aha00a |
51
|
0
|
= [Interpreter]Kanban |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
1
|
|
| 2026-05-22T04:26:35 |
Aha00a |
1
|
2
|
Kanban 인터프리터는 위키 텍스트를 단일 원본으로 사용한다. |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
3
|
보기 화면에서는 보드 UI로 편집하고, 저장 시 Kanban 블록을 다시 위키 텍스트로 직렬화한다. |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
4
|
|
| 2026-06-08T06:32:09 |
Aha00a |
48
|
5
|
== Example |
| 2026-05-22T05:03:13 |
Aha00a |
3
|
6
|
[[[#!Kanban |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
7
|
=== Info |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
8
|
==== 작업 제목 ==== #cardid123 |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
9
|
===== Description |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
10
|
카드에 대한 설명을 여러 줄로 작성할 수 있다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
11
|
새 댓글이 추가되어도 밀리지 않는다. |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
12
|
===== Property |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
13
|
* DueDate: [2027-01-01] |
| 2026-06-08T06:23:57 |
Aha00a |
7
|
14
|
* Assignee: [User:Aha00a] |
| 2026-06-08T06:23:57 |
Aha00a |
7
|
15
|
* Attachment: [[Attachment(path/to/file.png)]] |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
16
|
===== Activity |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
17
|
* [User:Aha00a] [2026-05-04]T10:36:17 |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
18
|
* ["#cardid123" #작업 제목] moved - '''Done''' to '''Info''' |
| 2026-06-08T06:23:57 |
Aha00a |
7
|
19
|
=== ToDo |
| 2026-06-08T06:26:27 |
Aha00a |
24
|
20
|
==== 점심 설거지 ==== #cmq4akx08r020y2 |
| 2026-06-08T06:26:27 |
Aha00a |
24
|
21
|
===== Property |
| 2026-06-08T06:26:27 |
Aha00a |
24
|
22
|
* Creator: [User:Aha00a] |
| 2026-06-08T06:26:27 |
Aha00a |
24
|
23
|
* dateCreated: [2026-06-07]T21:26:27Z |
| 2026-06-08T06:29:15 |
Aha00a |
43
|
24
|
* ParentCard: [#cmq4ak09d89cnyp 점심에 할일] |
| 2026-06-08T06:26:27 |
Aha00a |
24
|
25
|
===== Activity |
| 2026-06-08T06:29:15 |
Aha00a |
43
|
26
|
* [User:Aha00a] [2026-06-07]T21:29:15Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
27
|
* ["#cmq4akx08r020y2" #점심 설거지] updated - ParentCard - [#cmq4ak09d89cnyp 점심에 할일] |
| 2026-06-08T06:26:27 |
Aha00a |
24
|
28
|
* [User:Aha00a] [2026-06-07]T21:26:27Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
29
|
* ["#cmq4akx08r020y2" #점심 설거지] added |
| 2026-06-08T06:24:20 |
Aha00a |
12
|
30
|
=== Pending |
| 2026-06-08T06:30:17 |
Aha00a |
47
|
31
|
==== 점심 식사 ==== #cmq4aksurw4h9b6 |
| 2026-06-08T06:30:17 |
Aha00a |
47
|
32
|
===== Property |
| 2026-06-08T06:30:17 |
Aha00a |
47
|
33
|
* Creator: [User:Aha00a] |
| 2026-06-08T06:30:17 |
Aha00a |
47
|
34
|
* dateCreated: [2026-06-07]T21:26:22Z |
| 2026-06-08T06:30:17 |
Aha00a |
47
|
35
|
* ParentCard: [#cmq4ak09d89cnyp 점심에 할일] |
| 2026-06-08T06:30:17 |
Aha00a |
47
|
36
|
===== Activity |
| 2026-06-08T06:30:17 |
Aha00a |
47
|
37
|
* [User:Aha00a] [2026-06-07]T21:30:17Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
38
|
* ["#cmq4aksurw4h9b6" #점심 식사] moved - '''ToDo''' to '''Pending''' |
| 2026-06-08T06:30:17 |
Aha00a |
47
|
39
|
* [User:Aha00a] [2026-06-07]T21:29:10Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
40
|
* ["#cmq4aksurw4h9b6" #점심 식사] updated - ParentCard - [#cmq4ak09d89cnyp 점심에 할일] |
| 2026-06-08T06:30:17 |
Aha00a |
47
|
41
|
* [User:Aha00a] [2026-06-07]T21:26:22Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
42
|
* ["#cmq4aksurw4h9b6" #점심 식사] added |
| 2026-06-08T06:24:01 |
Aha00a |
8
|
43
|
=== Doing |
| 2026-06-08T06:29:36 |
Aha00a |
45
|
44
|
==== 점심에 할일 ==== #cmq4ak09d89cnyp |
| 2026-06-08T06:29:36 |
Aha00a |
45
|
45
|
===== Property |
| 2026-06-08T06:29:36 |
Aha00a |
45
|
46
|
* Creator: [User:Aha00a] |
| 2026-06-08T06:29:36 |
Aha00a |
45
|
47
|
* dateCreated: [2026-06-07]T21:25:45Z |
| 2026-06-08T06:29:36 |
Aha00a |
45
|
48
|
* BlockedBy: [#cmq4aiz5lnvvyen 일어나기] |
| 2026-06-08T06:29:36 |
Aha00a |
45
|
49
|
===== Activity |
| 2026-06-08T06:29:36 |
Aha00a |
45
|
50
|
* [User:Aha00a] [2026-06-07]T21:29:36Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
51
|
* ["#cmq4ak09d89cnyp" #점심에 할일] moved - '''ToDo''' to '''Doing''' |
| 2026-06-08T06:29:36 |
Aha00a |
45
|
52
|
* [User:Aha00a] [2026-06-07]T21:29:28Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
53
|
* ["#cmq4ak09d89cnyp" #점심에 할일] updated - BlockedBy - [#cmq4aiz5lnvvyen 일어나기] |
| 2026-06-08T06:29:36 |
Aha00a |
45
|
54
|
* [User:Aha00a] [2026-06-07]T21:25:54Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
55
|
* ["#cmq4ak09d89cnyp" #점심에 할일] renamed - '''점심에할일''' |
| 2026-06-08T06:29:36 |
Aha00a |
45
|
56
|
* [User:Aha00a] [2026-06-07]T21:25:45Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
57
|
* ["#cmq4ak09d89cnyp" #점심에할일] added |
| 2026-06-08T06:29:57 |
Aha00a |
46
|
58
|
==== 점심밥 차리기 ==== #cmq4akmkpy6chrx |
| 2026-06-08T06:29:57 |
Aha00a |
46
|
59
|
===== Property |
| 2026-06-08T06:29:57 |
Aha00a |
46
|
60
|
* Creator: [User:Aha00a] |
| 2026-06-08T06:29:57 |
Aha00a |
46
|
61
|
* dateCreated: [2026-06-07]T21:26:14Z |
| 2026-06-08T06:29:57 |
Aha00a |
46
|
62
|
* ParentCard: [#cmq4ak09d89cnyp 점심에 할일] |
| 2026-06-08T06:29:57 |
Aha00a |
46
|
63
|
===== Activity |
| 2026-06-08T06:29:57 |
Aha00a |
46
|
64
|
* [User:Aha00a] [2026-06-07]T21:29:57Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
65
|
* ["#cmq4akmkpy6chrx" #점심밥 차리기] moved - '''ToDo''' to '''Doing''' |
| 2026-06-08T06:29:57 |
Aha00a |
46
|
66
|
* [User:Aha00a] [2026-06-07]T21:29:04Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
67
|
* ["#cmq4akmkpy6chrx" #점심밥 차리기] updated - ParentCard - [#cmq4ak09d89cnyp 점심에 할일] |
| 2026-06-08T06:29:57 |
Aha00a |
46
|
68
|
* [User:Aha00a] [2026-06-07]T21:26:14Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
69
|
* ["#cmq4akmkpy6chrx" #점심밥 차리기] added |
| 2026-06-08T06:24:05 |
Aha00a |
9
|
70
|
=== Done |
| 2026-06-08T06:28:06 |
Aha00a |
35
|
71
|
==== 일어나기 ==== #cmq4aiz5lnvvyen |
| 2026-06-08T06:28:06 |
Aha00a |
35
|
72
|
===== Property |
| 2026-06-08T06:28:06 |
Aha00a |
35
|
73
|
* Creator: [User:Aha00a] |
| 2026-06-08T06:28:06 |
Aha00a |
35
|
74
|
* dateCreated: [2026-06-07]T21:24:57Z |
| 2026-06-08T06:28:06 |
Aha00a |
35
|
75
|
===== Activity |
| 2026-06-08T06:28:06 |
Aha00a |
35
|
76
|
* [User:Aha00a] [2026-06-07]T21:28:07Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
77
|
* ["#cmq4aiz5lnvvyen" #일어나기] moved - '''ToDo''' to '''Done''' |
| 2026-06-08T06:28:06 |
Aha00a |
35
|
78
|
* [User:Aha00a] [2026-06-07]T21:24:57Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
79
|
* ["#cmq4aiz5lnvvyen" #일어나기] added |
| 2026-06-08T06:28:38 |
Aha00a |
37
|
80
|
==== 아침에 할일 ==== #cmq4ajpa7tig43b |
| 2026-06-08T06:28:38 |
Aha00a |
37
|
81
|
===== Property |
| 2026-06-08T06:28:38 |
Aha00a |
37
|
82
|
* Creator: [User:Aha00a] |
| 2026-06-08T06:28:38 |
Aha00a |
37
|
83
|
* dateCreated: [2026-06-07]T21:25:30Z |
| 2026-06-08T06:28:38 |
Aha00a |
37
|
84
|
* BlockedBy: [#cmq4aiz5lnvvyen 일어나기] |
| 2026-06-08T06:28:38 |
Aha00a |
37
|
85
|
===== Activity |
| 2026-06-08T06:28:38 |
Aha00a |
37
|
86
|
* [User:Aha00a] [2026-06-07]T21:28:38Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
87
|
* ["#cmq4ajpa7tig43b" #아침에 할일] moved - '''Doing''' to '''Done''' |
| 2026-06-08T06:28:38 |
Aha00a |
37
|
88
|
* [User:Aha00a] [2026-06-07]T21:28:15Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
89
|
* ["#cmq4ajpa7tig43b" #아침에 할일] updated - BlockedBy - [#cmq4aiz5lnvvyen 일어나기] |
| 2026-06-08T06:28:38 |
Aha00a |
37
|
90
|
* [User:Aha00a] [2026-06-07]T21:27:31Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
91
|
* ["#cmq4ajpa7tig43b" #아침에 할일] moved - '''ToDo''' to '''Doing''' |
| 2026-06-08T06:28:38 |
Aha00a |
37
|
92
|
* [User:Aha00a] [2026-06-07]T21:25:50Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
93
|
* ["#cmq4ajpa7tig43b" #아침에 할일] renamed - '''아침에할일''' |
| 2026-06-08T06:28:38 |
Aha00a |
37
|
94
|
* [User:Aha00a] [2026-06-07]T21:25:30Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
95
|
* ["#cmq4ajpa7tig43b" #아침에할일] added |
| 2026-06-08T06:28:42 |
Aha00a |
38
|
96
|
==== 아침밥 차리기 ==== #cmq4aj3iyvga6ec |
| 2026-06-08T06:28:42 |
Aha00a |
38
|
97
|
===== Property |
| 2026-06-08T06:28:42 |
Aha00a |
38
|
98
|
* Creator: [User:Aha00a] |
| 2026-06-08T06:28:42 |
Aha00a |
38
|
99
|
* dateCreated: [2026-06-07]T21:25:02Z |
| 2026-06-08T06:28:42 |
Aha00a |
38
|
100
|
* ParentCard: [#cmq4ajpa7tig43b 아침에 할일] |
| 2026-06-08T06:28:42 |
Aha00a |
38
|
101
|
===== Activity |
| 2026-06-08T06:28:42 |
Aha00a |
38
|
102
|
* [User:Aha00a] [2026-06-07]T21:28:43Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
103
|
* ["#cmq4aj3iyvga6ec" #아침밥 차리기] moved - '''Doing''' to '''Done''' |
| 2026-06-08T06:28:42 |
Aha00a |
38
|
104
|
* [User:Aha00a] [2026-06-07]T21:27:39Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
105
|
* ["#cmq4aj3iyvga6ec" #아침밥 차리기] moved - '''ToDo''' to '''Doing''' |
| 2026-06-08T06:28:42 |
Aha00a |
38
|
106
|
* [User:Aha00a] [2026-06-07]T21:26:56Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
107
|
* ["#cmq4aj3iyvga6ec" #아침밥 차리기] updated - ParentCard - [#cmq4ajpa7tig43b 아침에 할일] |
| 2026-06-08T06:28:42 |
Aha00a |
38
|
108
|
* [User:Aha00a] [2026-06-07]T21:26:34Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
109
|
* ["#cmq4aj3iyvga6ec" #아침밥 차리기] renamed - '''아침밥차리기''' |
| 2026-06-08T06:28:42 |
Aha00a |
38
|
110
|
* [User:Aha00a] [2026-06-07]T21:25:08Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
111
|
* ["#cmq4aj3iyvga6ec" #아침밥차리기] renamed - '''밥차리기''' |
| 2026-06-08T06:28:42 |
Aha00a |
38
|
112
|
* [User:Aha00a] [2026-06-07]T21:25:02Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
113
|
* ["#cmq4aj3iyvga6ec" #밥차리기] added |
| 2026-06-08T06:28:45 |
Aha00a |
39
|
114
|
==== 아침 식사 ==== #cmq4ajesg43bp1g |
| 2026-06-08T06:28:45 |
Aha00a |
39
|
115
|
===== Property |
| 2026-06-08T06:28:45 |
Aha00a |
39
|
116
|
* Creator: [User:Aha00a] |
| 2026-06-08T06:28:45 |
Aha00a |
39
|
117
|
* dateCreated: [2026-06-07]T21:25:17Z |
| 2026-06-08T06:28:45 |
Aha00a |
39
|
118
|
* ParentCard: [#cmq4ajpa7tig43b 아침에 할일] |
| 2026-06-08T06:28:45 |
Aha00a |
39
|
119
|
===== Activity |
| 2026-06-08T06:28:45 |
Aha00a |
39
|
120
|
* [User:Aha00a] [2026-06-07]T21:28:45Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
121
|
* ["#cmq4ajesg43bp1g" #아침 식사] moved - '''Doing''' to '''Done''' |
| 2026-06-08T06:28:45 |
Aha00a |
39
|
122
|
* [User:Aha00a] [2026-06-07]T21:27:42Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
123
|
* ["#cmq4ajesg43bp1g" #아침 식사] moved - '''ToDo''' to '''Doing''' |
| 2026-06-08T06:28:45 |
Aha00a |
39
|
124
|
* [User:Aha00a] [2026-06-07]T21:27:13Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
125
|
* ["#cmq4ajesg43bp1g" #아침 식사] updated - ParentCard - [#cmq4ajpa7tig43b 아침에 할일] |
| 2026-06-08T06:28:45 |
Aha00a |
39
|
126
|
* [User:Aha00a] [2026-06-07]T21:26:45Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
127
|
* ["#cmq4ajesg43bp1g" #아침 식사] renamed - '''아침식사''' |
| 2026-06-08T06:28:45 |
Aha00a |
39
|
128
|
* [User:Aha00a] [2026-06-07]T21:25:17Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
129
|
* ["#cmq4ajesg43bp1g" #아침식사] added |
| 2026-06-08T06:28:48 |
Aha00a |
40
|
130
|
==== 아침 설거지 ==== #cmq4ajlewxrxne2 |
| 2026-06-08T06:28:48 |
Aha00a |
40
|
131
|
===== Property |
| 2026-06-08T06:28:48 |
Aha00a |
40
|
132
|
* Creator: [User:Aha00a] |
| 2026-06-08T06:28:48 |
Aha00a |
40
|
133
|
* dateCreated: [2026-06-07]T21:25:25Z |
| 2026-06-08T06:28:48 |
Aha00a |
40
|
134
|
* ParentCard: [#cmq4ajpa7tig43b 아침에 할일] |
| 2026-06-08T06:28:48 |
Aha00a |
40
|
135
|
===== Activity |
| 2026-06-08T06:28:48 |
Aha00a |
40
|
136
|
* [User:Aha00a] [2026-06-07]T21:28:48Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
137
|
* ["#cmq4ajlewxrxne2" #아침 설거지] moved - '''Doing''' to '''Done''' |
| 2026-06-08T06:28:48 |
Aha00a |
40
|
138
|
* [User:Aha00a] [2026-06-07]T21:27:44Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
139
|
* ["#cmq4ajlewxrxne2" #아침 설거지] moved - '''ToDo''' to '''Doing''' |
| 2026-06-08T06:28:48 |
Aha00a |
40
|
140
|
* [User:Aha00a] [2026-06-07]T21:27:24Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
141
|
* ["#cmq4ajlewxrxne2" #아침 설거지] updated - ParentCard - [#cmq4ajpa7tig43b 아침에 할일] |
| 2026-06-08T06:28:48 |
Aha00a |
40
|
142
|
* [User:Aha00a] [2026-06-07]T21:26:39Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
143
|
* ["#cmq4ajlewxrxne2" #아침 설거지] renamed - '''아침설거지''' |
| 2026-06-08T06:28:48 |
Aha00a |
40
|
144
|
* [User:Aha00a] [2026-06-07]T21:25:25Z |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
145
|
* ["#cmq4ajlewxrxne2" #아침설거지] added |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
146
|
]]] |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
147
|
|
| 2026-06-08T06:32:09 |
Aha00a |
48
|
148
|
== Syntax |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
149
|
* `=== 리스트명`: 컬럼 |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
150
|
* `==== 카드 제목 ==== #cardId`: 카드 |
| 2026-09-12T09:58:23 |
Aha00a |
54
|
151
|
* `===== Description`: 카드 설명 (선택, 여러 줄, Activity에 밀리지 않음). 다음 `=====` 줄이나 `===` 리스트 줄까지가 설명이다. 설명 바로 뒤에 `====` 카드 줄을 쓰면 그 카드도 설명으로 읽히므로, 손으로 쓸 때는 설명 뒤에 `===== Property`를 둔다. UI로 저장하면 늘 그렇게 쓴다. |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
152
|
* `===== Property`: 카드 속성 |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
153
|
* `===== Activity`: 활동 로그 |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
154
|
|
| 2026-05-22T04:26:35 |
Aha00a |
1
|
155
|
== 규칙 |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
156
|
|
| 2026-05-22T04:26:35 |
Aha00a |
1
|
157
|
* 카드 ID(`#...`)는 생성 후 변경하지 않는다. |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
158
|
* 카드/리스트 순서는 입력 순서를 그대로 유지한다. |
| 2026-09-15T07:49:17 |
Aha00a |
56
|
159
|
* Kanban 블록 외부 텍스트는 저장 시 변경하지 않는다. 저장은 줄 번호로 자리를 찾으므로, 페이지를 연 뒤 첫 저장은 먼저 페이지 원문을 읽어 블록의 자리를 원문에서 잡는다. 화면이 그려진 줄 번호는 맨 위 `#!` 줄을 뺀 번호라서, 2026-09-15 전에는 `#!var` 같은 줄이 있는 페이지에서 원격 업데이트를 한 번 받기 전까지 저장이 그 줄 수만큼 위에 들어갔다. |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
160
|
* 저장 전 현재 리비전이 확인되지 않으면 최신 리비전을 1회 조회한 뒤 저장한다. |
| 2026-09-13T03:22:42 |
Aha00a |
55
|
161
|
* 저장마다 reCAPTCHA token 을 새로 받아 함께 보낸다(409 재시도 포함). 받지 못하면 저장하지 않고 알린다. 규칙은 [Dev Editor] '''저장'''. 2026-09-13 전에는 늘 빈 token 을 보냈다. |
| 2026-09-15T07:49:17 |
Aha00a |
56
|
162
|
* `409 Conflict` 가 오면 최신 원문을 다시 읽고, 이 보드가 아직 저장하지 못한 변경을 서버의 보드 위에 다시 얹어(목록의 추가·삭제·이름·순서, 카드의 추가·삭제·내용·이동) 원문 기준 줄 범위로 다시 보낸다. 최대 3회. 양쪽이 같은 것을 바꿨으면 원격 업데이트처럼 서버 쪽이 남고, 그랬다는 것을 토스트로 알린다. 다시 얹을 것이 남지 않으면 보내지 않는다. 모두 실패하면 화면 하단에 non-blocking 토스트를 표시하며 페이지는 새로고침하지 않는다. 2026-09-15 전에는 리비전 번호만 다시 받아 같은 텍스트를 같은 줄 범위에 보냈고, 그 사이 다른 세션이 보드에 한 변경이 사라졌다. |
| 2026-09-12T09:58:23 |
Aha00a |
54
|
163
|
* 다른 탭/사용자의 저장을 받아 보드를 자동 갱신한다 — 조건과 한계는 아래 '''실시간 동기화'''. |
| 2026-09-25T03:20:54 |
Aha00a |
57
|
164
|
* 카드 글(제목·Description·댓글)은 AhaMark 로 그린다. 백틱으로 감싼 코드도 보드 안에서 코드로 보인다. 2026-09-25 전에는 백틱이 페이지를 그릴 때 이미 코드 태그로 바뀌어 버려서, 보드가 그것을 태그 없이 읽고 '''다음 저장에 백틱을 지운 채''' 문서를 다시 썼다 — 쓴 사람이 지우지 않은 글자가 사라지는 셈이었다. 까닭은 [Interpreter] '''구간 안의 백틱'''. |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
165
|
|
| 2026-06-09T19:56:41 |
Aha00a |
49
|
166
|
== 실시간 동기화 |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
167
|
|
| 2026-09-25T03:20:54 |
Aha00a |
57
|
168
|
다른 탭 또는 다른 사용자가 같은 Kanban 페이지를 저장하면 현재 탭의 보드가 자동으로 갱신된다. 2026-09-15 부터는 저장이 어느 인스턴스를 거쳤든 온다 — `page.updated` 를 인스턴스 사이로 전달한다([Dev WebSocket]). 그 전에는 같은 인스턴스에 붙은 사람에게만 갔다. |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
169
|
|
| 2026-06-26T13:44:16 |
Aha00a |
52
|
170
|
* 다른 세션의 저장을 WebSocket `page.updated` 이벤트로 감지한다. |
| 2026-06-26T13:44:16 |
Aha00a |
52
|
171
|
* 최신 원문을 다시 읽고 Kanban 블록을 파싱한 뒤 현재 보드에 병합한다. |
| 2026-06-26T13:44:16 |
Aha00a |
52
|
172
|
* 저장 중인 변경이 있으면 저장 완료 후 원격 업데이트를 다시 적용한다. |
| 2026-09-10T19:36:45 |
Aha00a |
53
|
173
|
* 병합 상세와 구현 노트는 [Dev Kanban Realtime]에 둔다. |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
174
|
|
| 2026-06-26T13:44:16 |
Aha00a |
52
|
175
|
===> 동작 조건 |
| 2026-06-26T13:44:16 |
Aha00a |
52
|
176
|
|
| 2026-06-09T19:56:41 |
Aha00a |
49
|
177
|
* WebSocket이 연결된 상태에서 다른 세션의 저장이 완료되면 `page.updated` 이벤트를 수신한다. |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
178
|
* 이벤트 수신 후 500ms debounce 처리 후 `GET /w/{page}?action=raw`로 최신 원문을 가져온다. |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
179
|
* 가져온 원문에서 칸반 블록 위치를 재계산하여 파싱한다. |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
180
|
|
| 2026-06-26T13:44:16 |
Aha00a |
52
|
181
|
===> 병합 전략 (3-way merge) |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
182
|
|
| 2026-09-12T09:58:23 |
Aha00a |
54
|
183
|
* `base` — 이 탭과 서버가 마지막으로 같았던 칸반 상태. 처음 읽을 때, 저장에 성공할 때, 원격 업데이트를 적용할 때(방금 받은 서버 상태로) 다시 잡는다. |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
184
|
* `local` — 현재 로컬 in-memory 상태 (저장 중인 변경 포함) |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
185
|
* `server` — 방금 가져온 서버 상태 |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
186
|
|
| 2026-06-09T19:56:41 |
Aha00a |
49
|
187
|
[[[ |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
188
|
로컬만 변경된 카드 → 로컬 버전 유지 |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
189
|
서버만 변경된 카드 → 서버 버전 적용 |
| 2026-09-12T09:58:23 |
Aha00a |
54
|
190
|
양쪽 모두 변경된 카드 → 서버 우선 + 카드 표면에 주황 conflict 배지 |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
191
|
로컬에만 추가된 카드 → 서버 상태에 병합하여 유지 |
| 2026-09-12T09:58:23 |
Aha00a |
54
|
192
|
모달에서 열린 카드 → 편집 중이 아닌 칸(제목·설명·속성)은 서버 값으로 바로 바꾸고, 편집 중인 칸을 서버가 바꿨으면 그 칸은 두고 모달 상단 배너 표시 |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
193
|
]]] |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
194
|
|
| 2026-06-26T13:44:16 |
Aha00a |
52
|
195
|
===> pending 뮤테이션 처리 |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
196
|
|
| 2026-06-09T19:56:41 |
Aha00a |
49
|
197
|
* 저장 중인 뮤테이션이 있으면 업데이트를 즉시 적용하지 않고 뮤테이션 완료 후 재적용한다. |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
198
|
|
| 2026-06-26T13:44:16 |
Aha00a |
52
|
199
|
===> 부수 효과 |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
200
|
|
| 2026-09-12T09:58:23 |
Aha00a |
54
|
201
|
* 원격 업데이트를 적용하면 `interpreterStartLine`과 줄 범위를 서버 원문 기준으로, 현재 리비전을 받은 리비전으로, `base`를 방금 받은 서버 상태로 다시 잡는다. |
| 2026-09-12T09:58:23 |
Aha00a |
54
|
202
|
** 리비전이 맞으니 다음 저장이 409를 받지 않고, 줄 번호가 맞으니 저장이 보드 밖 줄을 덮지 않는다. |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
203
|
|
| 2026-05-22T04:26:35 |
Aha00a |
1
|
204
|
== 속성 |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
205
|
|
| 2026-09-12T09:58:23 |
Aha00a |
54
|
206
|
UI가 쓰는 속성: |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
207
|
|
| 2026-09-12T09:58:23 |
Aha00a |
54
|
208
|
* `Creator` (1) — 카드를 만들 때 쓴다. |
| 2026-09-12T09:58:23 |
Aha00a |
54
|
209
|
* `dateCreated` (1) — 카드를 만들 때 쓴다. |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
210
|
* `DueDate` (1) |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
211
|
* `Assignee` (n) |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
212
|
* `BlockedBy` (n) |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
213
|
* `ParentCard` (n) |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
214
|
* `RelatedCard` (n) |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
215
|
* `Attachment` (n) |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
216
|
|
| 2026-09-12T09:58:23 |
Aha00a |
54
|
217
|
(1)·(n)은 UI가 쓰는 개수일 뿐 강제하지 않는다. 목록에 없는 키도 그대로 읽고 저장하며 모달에 표시한다. |
| 2026-09-12T09:58:23 |
Aha00a |
54
|
218
|
|
| 2026-06-07T12:18:02 |
Aha00a |
4
|
219
|
=== 카드 관계 |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
220
|
|
| 2026-06-07T12:18:02 |
Aha00a |
4
|
221
|
같은 `InterpreterKanban` 블록 안의 카드 관계를 Property로 표현한다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
222
|
관계 대상은 페이지명을 저장하지 않고 카드 ID hash만 저장한다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
223
|
|
| 2026-06-07T12:19:35 |
Aha00a |
5
|
224
|
[[[ |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
225
|
===== Property |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
226
|
* BlockedBy |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
227
|
* [#card-api-spec API spec 확정] |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
228
|
* ParentCard: [#card-login-epic Login Epic] |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
229
|
* RelatedCard |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
230
|
* [#card-design-review Design review] |
| 2026-06-07T12:19:35 |
Aha00a |
5
|
231
|
]]] |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
232
|
|
| 2026-06-07T12:18:02 |
Aha00a |
4
|
233
|
* 저장값은 `[#cardId]` 또는 `[#cardId label]` 형식을 사용한다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
234
|
* 실제 식별자는 `cardId`이고, label은 원문 가독성을 위한 보조 정보로만 사용한다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
235
|
* 관계 해석 범위는 같은 `InterpreterKanban` 블록 내부로 제한한다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
236
|
* `BlockedBy`, `ParentCard`, `RelatedCard`를 직접 저장한다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
237
|
* `Blocks`, `SubCards`는 저장하지 않고 `BlockedBy`, `ParentCard`를 역방향으로 스캔해서 계산 표시한다. |
| 2026-06-08T06:23:41 |
Aha00a |
6
|
238
|
|
| 2026-06-08T06:23:41 |
Aha00a |
6
|
239
|
=== 카드 표면 관계 배지 |
| 2026-06-08T06:23:41 |
Aha00a |
6
|
240
|
|
| 2026-06-08T06:23:41 |
Aha00a |
6
|
241
|
보드 뷰의 카드 표면에 관계가 있는 경우 관계 배지를 표시한다. |
| 2026-06-08T06:23:41 |
Aha00a |
6
|
242
|
|
| 2026-06-08T06:23:41 |
Aha00a |
6
|
243
|
* 직접 저장 관계: `BlockedBy`, `ParentCard`, `RelatedCard` |
| 2026-06-08T06:23:41 |
Aha00a |
6
|
244
|
* 파생 계산 관계: `Blocks`, `SubCards` |
| 2026-06-08T06:23:41 |
Aha00a |
6
|
245
|
* 관계가 없는 카드는 배지 영역을 표시하지 않는다. |
| 2026-06-08T06:23:41 |
Aha00a |
6
|
246
|
* 관계가 2개 이상인 경우 배지에 개수를 함께 표시한다. |
| 2026-06-08T06:23:41 |
Aha00a |
6
|
247
|
* 배지와 카드 하단 메타(담당자·통계)는 `kanban-card-footer` 안에 묶여, 내용이 있을 때만 구분선과 함께 표시된다. |
| 2026-09-12T09:58:23 |
Aha00a |
54
|
248
|
* 내용(관계·담당자·Activity·첨부·기한)이 없으면 footer에 여백과 구분선이 붙지 않는다. Activity는 시스템 기록(added·moved 등)도 세므로, UI에서 만든 카드는 처음부터 댓글 수 1과 함께 footer가 보인다. |
| 2026-06-08T06:23:41 |
Aha00a |
6
|
249
|
|
| 2026-06-08T06:23:41 |
Aha00a |
6
|
250
|
=== 관계 색상 체계 |
| 2026-06-08T06:23:41 |
Aha00a |
6
|
251
|
|
| 2026-06-08T06:23:41 |
Aha00a |
6
|
252
|
카드 표면 배지와 카드 상세 모달의 관계 Property 행에 동일한 색상 체계를 적용한다. |
| 2026-06-08T06:23:41 |
Aha00a |
6
|
253
|
|
| 2026-06-09T19:56:41 |
Aha00a |
49
|
254
|
[[[#!Table tsv 1 |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
255
|
관계 타입 색상 |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
256
|
BlockedBy 빨간색 (error) |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
257
|
Blocks (파생) 주황색 (warning) |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
258
|
ParentCard 파란색 (primary) |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
259
|
SubCards (파생) 파란색 (primary) |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
260
|
RelatedCard 회색 (muted) |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
261
|
]]] |
| 2026-06-08T06:23:41 |
Aha00a |
6
|
262
|
|
| 2026-06-08T06:23:41 |
Aha00a |
6
|
263
|
카드 상세 모달에서는 관계 Property 행의 라벨과 카드 칩 테두리·배경에 위 색상을 적용한다. |
| 2026-06-08T06:23:41 |
Aha00a |
6
|
264
|
|
| 2026-06-08T06:23:41 |
Aha00a |
6
|
265
|
=== 카드 상세 모달 관계 표시 |
| 2026-06-08T06:23:41 |
Aha00a |
6
|
266
|
|
| 2026-06-08T06:23:41 |
Aha00a |
6
|
267
|
* 관계 Property별로 현재 연결된 카드를 카드 칩으로 표시한다. |
| 2026-09-12T09:58:23 |
Aha00a |
54
|
268
|
* 칩에는 최신 카드 제목과 카드 ID(`#` 포함 전체)를 표시한다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
269
|
* 칩 클릭 시 `openCardDetailById(cardId)`로 대상 카드 모달을 연다. |
| 2026-09-12T09:58:23 |
Aha00a |
54
|
270
|
* 연결 추가는 카드 선택 목록으로 한다. 리스트별로 묶여 있고, 자기 자신과 이미 연결한 카드는 빠진다. ID를 직접 입력하는 칸은 없다(2026-06-10에 없앴다). |
| 2026-09-12T09:58:23 |
Aha00a |
54
|
271
|
* 원문에 있는 값이 보드에 없는 카드를 가리키면, 누를 수 없는 칩으로 표시한다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
272
|
* 현재 카드 자신을 관계 대상으로 선택하지 못하게 한다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
273
|
* 관계 삭제 버튼을 제공하고, 삭제 시 `card:property:update`로 저장한다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
274
|
|
| 2026-06-07T12:18:02 |
Aha00a |
4
|
275
|
=== Assignee |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
276
|
|
| 2026-06-07T12:18:02 |
Aha00a |
4
|
277
|
`Assignee`는 기존 Property 형식을 유지하되 카드 상세 모달에서 직접 편집할 수 있게 한다. |
| 2026-06-07T12:19:35 |
Aha00a |
5
|
278
|
[[[ |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
279
|
===== Property |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
280
|
* Assignee |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
281
|
* [User:Aha00a] |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
282
|
* [User:Alice] |
| 2026-06-07T12:19:35 |
Aha00a |
5
|
283
|
]]] |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
284
|
|
| 2026-06-07T12:18:02 |
Aha00a |
4
|
285
|
* Assignee는 복수 값을 허용한다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
286
|
* 입력값은 `User:Aha00a`, `[User:Aha00a]`, `Aha00a`를 받아 `[User:Aha00a]`로 정규화한다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
287
|
* 현재 Assignee는 사용자 칩으로 표시한다. |
| 2026-09-12T09:58:23 |
Aha00a |
54
|
288
|
* Assignee는 선택 목록(현재 사용자와 같은 보드에 등장한 Assignee)으로만 고른다. 목록에 없는 이름은 위키 텍스트를 직접 고친다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
289
|
* Assignee 변경은 `card:property:update`로 저장하고 Activity에 기록한다. |
| 2026-06-08T06:23:41 |
Aha00a |
6
|
290
|
* 카드 목록에는 Assignee 프로필 이미지 아바타를 표시하고, 이름은 mouseover tooltip으로 제공한다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
291
|
|
| 2026-06-10T06:26:19 |
Aha00a |
50
|
292
|
== 모바일 UX |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
293
|
|
| 2026-06-10T06:26:19 |
Aha00a |
50
|
294
|
뷰포트 너비가 767px 이하이면 자동으로 모바일 모드로 전환된다. |
| 2026-09-12T09:58:23 |
Aha00a |
54
|
295
|
`window.matchMedia('(max-width: 767px)')` 이벤트를 구독하여, 리사이즈 시에도 탭 바·단일 컬럼 보기·카드 드래그는 바로 바뀐다. 스와이프와 리스트 드래그는 페이지를 열 때의 너비로 정해진다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
296
|
|
| 2026-06-10T06:26:19 |
Aha00a |
50
|
297
|
=== 탭 바 + 단일 컬럼 뷰 |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
298
|
|
| 2026-06-10T06:26:19 |
Aha00a |
50
|
299
|
* 보드 상단에 컬럼 이름을 탭으로 나열한다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
300
|
* 활성 탭만 해당 컬럼을 표시하고, 나머지 컬럼은 숨긴다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
301
|
* 탭에는 해당 컬럼의 카드 수를 배지로 표시한다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
302
|
* 탭 바 우측 끝의 `+` 버튼으로 리스트를 추가할 수 있다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
303
|
|
| 2026-06-10T06:26:19 |
Aha00a |
50
|
304
|
=== 컬럼 이동 |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
305
|
|
| 2026-06-10T06:26:19 |
Aha00a |
50
|
306
|
* 탭을 탭하여 컬럼을 전환한다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
307
|
* 컬럼 영역을 좌우로 스와이프(50px 이상)하여 인접 컬럼으로 이동한다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
308
|
|
| 2026-06-10T06:26:19 |
Aha00a |
50
|
309
|
=== 드래그 비활성화 |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
310
|
|
| 2026-06-10T06:26:19 |
Aha00a |
50
|
311
|
* 모바일에서는 카드/리스트 드래그가 비활성화된다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
312
|
* 카드 이동은 카드 상세 모달의 "List" 셀렉트로 대신한다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
313
|
|
| 2026-06-10T06:26:19 |
Aha00a |
50
|
314
|
=== List 이동 (카드 상세 모달) |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
315
|
|
| 2026-06-10T06:26:19 |
Aha00a |
50
|
316
|
* 카드 상세 모달 Properties 영역 최상단에 "List" 셀렉트를 표시한다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
317
|
* 현재 카드가 속한 컬럼이 기본 선택된다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
318
|
* 다른 컬럼을 선택하면 즉시 이동하고 모달을 닫는다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
319
|
* Activity에 이동 기록이 남으며 `card:move`로 저장된다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
320
|
* 컬럼이 하나뿐이거나 read-only인 경우 표시하지 않는다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
321
|
* 데스크톱에서도 동일하게 표시된다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
322
|
|
| 2026-06-10T06:26:19 |
Aha00a |
50
|
323
|
=== 바텀시트 모달 |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
324
|
|
| 2026-06-10T06:26:19 |
Aha00a |
50
|
325
|
* 카드 상세 모달이 화면 하단에서 올라오는 바텀시트 형태로 표시된다. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
326
|
* 높이 최대 90dvh, 상단 모서리 border-radius 16px. |
| 2026-06-10T06:26:19 |
Aha00a |
50
|
327
|
|
| 2026-05-22T04:26:35 |
Aha00a |
1
|
328
|
== 카드 모달 |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
329
|
|
| 2026-05-22T04:26:35 |
Aha00a |
1
|
330
|
* 카드 열기 시 URL hash를 `#<cardId>`로 설정한다. |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
331
|
* 카드 닫기 시 해당 hash를 제거한다. |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
332
|
* `#<cardId>`로 직접 진입하면 해당 카드 모달을 자동으로 열고 보드 위치로 이동한다. |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
333
|
* 존재하지 않는 `#<cardId>`는 오류 없이 일반 보드 화면을 유지한다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
334
|
* Property 영역에서 `DueDate`, `Assignee`, 카드 관계를 직접 편집할 수 있다. |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
335
|
* 카드 관계 칩 클릭 시 대상 카드 모달을 연다. |
| 2026-06-08T06:23:41 |
Aha00a |
6
|
336
|
* 카드 목록에는 Assignee 프로필 이미지 아바타가 표시된다. |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
337
|
|
| 2026-05-22T04:26:35 |
Aha00a |
1
|
338
|
== 리비전 코멘트 |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
339
|
|
| 2026-06-09T19:56:41 |
Aha00a |
49
|
340
|
형식: `Kanban - [주체] 동사과거형 - Detail` |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
341
|
|
| 2026-06-09T19:56:41 |
Aha00a |
49
|
342
|
* 카드 이벤트: 카드링크가 주체. ex) `Kanban - ["Kanban#cardId" Kanban#할일] moved - '''ToDo''' to '''Done'''` |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
343
|
* 리스트 이벤트: 리스트명(bold)이 주체. ex) `Kanban - '''ToDo''' added` |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
344
|
* 지원 액션: |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
345
|
* `list:add`, `list:rename`, `list:move`, `list:delete` |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
346
|
* `card:add`, `card:rename`, `card:move`, `card:delete` |
| 2026-06-07T12:18:02 |
Aha00a |
4
|
347
|
* `card:comment:add`, `card:property:update`, `card:description:update` |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
348
|
* `card:comment:add`는 코멘트 첫 줄 기준 최대 80자만 기록한다. |
| 2026-06-09T19:56:41 |
Aha00a |
49
|
349
|
* `card:rename`의 Detail은 이전 제목(fromTitle). 카드링크 자체가 새 제목을 담고 있으므로 redundancy 없이 변경 내역을 표현한다. |
| 2026-05-22T04:26:35 |
Aha00a |
1
|
350
|
* `card:delete`는 카드가 삭제되므로 Activity에는 별도로 기록하지 않는다. |
| 2026-09-12T09:58:23 |
Aha00a |
54
|
351
|
* `list:move`와, 같은 리스트 안의 `card:move`는 minor edit로 저장한다([MacroRecentChanges]). |
| 2026-05-22T05:02:54 |
Aha00a |
2
|
352
|
|
| 2026-05-22T05:02:54 |
Aha00a |
2
|
353
|
== Read Only |
| 2026-05-22T05:02:54 |
Aha00a |
2
|
354
|
|
| 2026-05-22T05:02:54 |
Aha00a |
2
|
355
|
InterpreterKanban은 일반 위키 페이지와 같은 쓰기 권한 정책을 따른다. |
| 2026-05-22T05:02:54 |
Aha00a |
2
|
356
|
페이지 렌더링 시 계산된 `isWritable` 값은 `.wikiContent`의 `data-wiki-writable` 속성으로 전달되고, Kanban UI는 이 값을 기준으로 root별 read-only 모드를 결정한다. |
| 2026-09-26T19:46:29 |
Aha00a |
58
|
357
|
옛 리비전을 보는 화면(`?revision=N` 이 최신이 아닐 때)은 쓰기 권한이 있어도 read-only 이고, 실시간 동기화도 하지 않는다 — [Dev Page] '''옛 리비전 화면'''. |
| 2026-05-22T05:02:54 |
Aha00a |
2
|
358
|
|
| 2026-05-22T05:02:54 |
Aha00a |
2
|
359
|
쓰기 권한이 없는 사용자는 Kanban 보드를 조회할 수 있지만, 내용을 변경하는 UI는 노출되지 않는다. |
| 2026-05-22T05:02:54 |
Aha00a |
2
|
360
|
|
| 2026-05-22T05:02:54 |
Aha00a |
2
|
361
|
* 보드, 컬럼, 카드, 속성, Activity, 첨부 이미지는 조회할 수 있다. |
| 2026-05-22T05:02:54 |
Aha00a |
2
|
362
|
* 카드 상세 모달, 이미지 라이트박스, `#<cardId>` hash navigation은 동작한다. |
| 2026-05-22T05:02:54 |
Aha00a |
2
|
363
|
* 보드 배경 드래그를 이용한 가로 스크롤은 read-only에서도 동작한다. |
| 2026-05-22T05:02:54 |
Aha00a |
2
|
364
|
* list/card 추가, rename, delete, drag sort는 비활성화된다. |
| 2026-05-22T05:02:54 |
Aha00a |
2
|
365
|
* 댓글 입력, DueDate 수정, 첨부 삭제, 파일 drop 업로드는 비활성화된다. |
| 2026-05-22T05:02:54 |
Aha00a |
2
|
366
|
* 보드에는 read-only 상태가 표시된다. |
| 2026-05-22T05:02:54 |
Aha00a |
2
|
367
|
|
| 2026-05-22T05:02:54 |
Aha00a |
2
|
368
|
클라이언트의 read-only 처리는 UX를 위한 방어선이다. |
| 2026-05-22T05:02:54 |
Aha00a |
2
|
369
|
실제 보안 경계는 서버 저장 로직의 `/w/{page}` POST 쓰기 권한 검사이며, 쓰기 권한이 없으면 `403 Permission denied`로 차단된다. |
| 2026-05-22T05:02:54 |
Aha00a |
2
|
370
|
|
| 2026-05-22T05:02:54 |
Aha00a |
2
|
371
|
한 페이지에 여러 Kanban interpreter가 있을 수 있으므로 read-only 상태와 mutation 방어는 interpreter root별로 처리한다. |
| 2026-05-22T05:02:54 |
Aha00a |
2
|
372
|
signed read URL로 페이지를 조회하는 경우에도 쓰기 권한이 없으면 read-only로 동작해야 한다. |