diff --git a/README.md b/README.md
new file mode 100644
index 00000000..7c4e9fe4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,152 @@
+## β° Timo οΈ
+
+
+
+## βοΈ Features
+
+
+**π ν¬λμ μκ°μ μ
λ ₯ν΄ νμλ°μ€ μμ±**
+
+ν΄μΌ ν μΌκ³Ό μμ μμ μκ°μ μ
λ ₯νλ©΄ ν루 κ³νμ΄ νμλ°μ€ ννλ‘ μλ μμ±λ©λλ€.
+
+
+
+**β±οΈ νμ΄λ¨Έλ₯Ό μ€νν΄ κ³ν μν**
+
+κ° νμλ°μ€λ₯Ό μ€ννλ©΄ μ€μ ν μκ°λ§νΌ νμ΄λ¨Έκ° μμλκ³ μ€μ μν μκ°μ΄ μΈ‘μ λ©λλ€.
+
+
+
+**π νμλΌμΈμ ν΅ν΄μ κΈ°λ‘**
+
+νμ΄λ¨Έλ‘ μνν μκ°λ§νΌ νμλΌμΈμ μ€μκ°μΌλ‘ κΈ°λ‘λμ΄ ν루 λμ 무μμ νλμ§ νλμ νμ
ν μ μμ΅λλ€.
+
+
+
+**π€ AI νΌλλ°±**
+
+κ³νν μμ μκ°κ³Ό μ€μ μμ μκ°μ λΉκ΅ν΄ AIκ° μ¬μ©μμκ² λ§λ μκ° μΆμ²κ³Ό νΌλλ°±μ μ 곡ν©λλ€.
+
+
+
+**βΆοΈ μλ£νλ©΄ λ€μ νμλ°μ€λ₯Ό μμ**
+
+νλμ ν¬λλ₯Ό μλ£νλ©΄ μ΄μ΄μ λ€μ νμλ°μ€λ‘ μμ°μ€λ½κ² λμ΄κ° κ³νν ν루λ₯Ό λκΉ μμ΄ μνν μ μμ΅λλ€.
+
+
+
+**π ν΅κ³**
+
+ν루 λμ κ³νν μκ°κ³Ό μ€μ μν μκ°μ μ°¨μ΄λ₯Ό μΊλ¦°λ λ·°μμ νλμ νμΈν μ μμ΅λλ€.
+
+
+
+**π― μ§μ€ λͺ¨λ**
+
+νλμ ν¬λμλ§ μ§μ€ν΄μ μνν μ μλ λͺ°μ
μ μ© νλ©΄μ μ 곡ν©λλ€.
+
+#### ν μΌμ μκ° λ¨μλ‘ κ΄λ¦¬νλ νμλ°μ± ν¬λ
+
+Timoλ μμ
μ μ€ν κ°λ₯ν μκ° λΈλ‘μΌλ‘ λ³νν΄
+κ³νν μΌμ μ€μ λ‘ μλ£ν μ μλλ‘ λλ νμλ°μ€ κΈ°λ° ν μΌ κ΄λ¦¬ μλΉμ€μ
λλ€.
+
+κΈ°μ‘΄μ ν¬λ μλΉμ€μ λ¬λ¦¬, Timoλ κ° μμ
μ μΆκ°ν λ μμ μμ μκ°μ μ
λ ₯νλλ‘ νλ©°
+μ΄λ₯Ό λ°νμΌλ‘ ν루 μΌμ μ μκ° λΈλ‘ μ€μ¬μΌλ‘ ꡬμ±ν©λλ€.
+
+λν μ¬μ©μμ μμ
λ° μκ° μ¬μ© ν¨ν΄μ μ§μμ μΌλ‘ νμ΅νμ¬ AI κΈ°λ° μμ μκ° μΆμ²μ μ 곡ν©λλ€.
+μ΄λ₯Ό ν΅ν΄ κ³Όλν κ³νμ μ€μ΄κ³ λ³΄λ€ νμ€μ μ΄κ³ μ€ν κ°λ₯ν μΌμ μ μΈμΈ μ μλλ‘ μ§μν©λλ€.
+
+
+
+## β¨ Backend Developers
+
+
+ | μ μ€μ |
+ λ°μλ |
+ μ₯μμ€ |
+
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ | Backend Leader |
+ Backend Dev |
+ Backend Dev |
+
+
+ | @laura-jung |
+ @anykrap |
+ @Jy000n |
+
+
+
+
+
+## π οΈ Tech Stack
+#### Backend & Auth
+
+
+#### Database & Cache
+
+
+#### Infra & Deploy
+
+
+#### Monitoring
+
+
+#### AI
+
+
+
+
+## π₯οΈ System Architecture
+
+
+
+
+## π ERD
+
+
+
+
+
+## π¦οΈ Directory
+```
+πcom.Timo.Timo
+βββπdomain
+β βββπtimer
+β β βββπcontroller # HTTP μμ²/μλ΅ μ²λ¦¬
+β β βββπdocs # Swagger λ¬Έμ (Controllerμ λΆλ¦¬)
+β β βββπdto # μμ²/μλ΅ DTO
+β β βββπentity # JPA μν°ν°
+β β βββπenums # λλ©μΈ μ μ© enum
+β β βββπexception # μλ¬/μ±κ³΅ μ½λ
+β β βββπrepository # DB μ κ·Ό
+β β βββπservice # λΉμ¦λμ€ λ‘μ§
+β βββπtodo
+β βββπuser
+β βββ ...
+βββπglobal
+ βββπauth # μΈμ¦/μΈκ° κ³΅ν΅ λ‘μ§
+ βββπcommon # κ³΅ν΅ λ² μ΄μ€ ν΄λμ€ (BaseTimeEntity λ±)
+ βββπconfig # Swagger, Security λ± μ€μ
+ βββπexception # κ³΅ν΅ μμΈ μ²λ¦¬
+ βββπjwt # JWT μ νΈ (λ°κΈ/κ²μ¦)
+ βββπlogging # μμ²/μλ΅ λ° μμΈ λ‘κΉ
+ βββπresponse # κ³΅ν΅ μλ΅ ν¬λ§· (BaseResponse)
+```
+
+
+
+## π Convention
+[π κΉ μ»¨λ²€μ
보λ¬κ°κΈ°](https://app.notion.com/p/Timo-Git-Convention-3877196f19708068b15fe97386a80e01)
+[π μ½λ 컨벀μ
보λ¬κ°κΈ°](https://app.notion.com/p/Timo-spring-code-convention-3877196f19708031b1bcd531a93119fd)
diff --git a/src/main/java/com/Timo/Timo/domain/home/dto/response/HomeResponse.java b/src/main/java/com/Timo/Timo/domain/home/dto/response/HomeResponse.java
index 8db3d142..773b726f 100644
--- a/src/main/java/com/Timo/Timo/domain/home/dto/response/HomeResponse.java
+++ b/src/main/java/com/Timo/Timo/domain/home/dto/response/HomeResponse.java
@@ -74,7 +74,7 @@ public record TodoResponse(
String priority,
TagResponse tag,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
- boolean hasSubtask,
+ boolean hasMemo,
@JsonProperty("isRepeated")
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
boolean isRepeated,
diff --git a/src/main/java/com/Timo/Timo/domain/home/dto/response/TodayResponse.java b/src/main/java/com/Timo/Timo/domain/home/dto/response/TodayResponse.java
index 6668220d..32a95529 100644
--- a/src/main/java/com/Timo/Timo/domain/home/dto/response/TodayResponse.java
+++ b/src/main/java/com/Timo/Timo/domain/home/dto/response/TodayResponse.java
@@ -60,7 +60,7 @@ public record TodayTodoResponse(
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
boolean isRepeated,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
- boolean hasSubtask,
+ boolean hasMemo,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
TodoTimerStatus timerStatus,
Integer sortOrder,
@@ -78,7 +78,7 @@ public static TodayTodoResponse from(LocalDate date, TodoResponse todo) {
todo.priority(),
todo.tag(),
todo.isRepeated(),
- todo.hasSubtask(),
+ todo.hasMemo(),
todo.timerStatus(),
todo.sortOrder(),
todo.subtasks()
diff --git a/src/main/java/com/Timo/Timo/domain/home/mapper/HomeTodoMapper.java b/src/main/java/com/Timo/Timo/domain/home/mapper/HomeTodoMapper.java
index 2260b82c..a647a0d1 100644
--- a/src/main/java/com/Timo/Timo/domain/home/mapper/HomeTodoMapper.java
+++ b/src/main/java/com/Timo/Timo/domain/home/mapper/HomeTodoMapper.java
@@ -30,7 +30,7 @@ public TodoResponse toResponse(TodoContext context) {
todo.getDurationSeconds(),
todo.getPriority() != null ? todo.getPriority().name() : null,
mapTag(context.tag()),
- !todo.getSubtasks().isEmpty(),
+ hasMemo(todo, instance),
todo.getRepeatType() != RepeatType.NONE,
resolveTimerStatus(instance),
instance != null ? instance.getSortOrder() : context.defaultSortOrder(),
@@ -38,6 +38,11 @@ public TodoResponse toResponse(TodoContext context) {
);
}
+ private boolean hasMemo(Todo todo, TodoInstance instance) {
+ String memo = instance != null ? instance.resolveMemo() : todo.getMemo();
+ return memo != null && !memo.isBlank();
+ }
+
private TagResponse mapTag(Tag tag) {
return tag != null ? TagResponse.from(tag) : null;
}
diff --git a/src/main/java/com/Timo/Timo/domain/timer/repository/TimerRecordRepository.java b/src/main/java/com/Timo/Timo/domain/timer/repository/TimerRecordRepository.java
index f2959c24..dcb13d4f 100644
--- a/src/main/java/com/Timo/Timo/domain/timer/repository/TimerRecordRepository.java
+++ b/src/main/java/com/Timo/Timo/domain/timer/repository/TimerRecordRepository.java
@@ -3,6 +3,7 @@
import com.Timo.Timo.domain.timer.entity.TimerRecord;
import com.Timo.Timo.domain.timer.enums.TimerStatus;
import jakarta.persistence.LockModeType;
+import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@@ -18,6 +19,9 @@ public interface TimerRecordRepository extends JpaRepository
boolean existsByTodo_IdAndStatusIn(Long todoId, List statuses);
+ boolean existsByTodo_IdAndTargetDateAndStatusIn(
+ Long todoId, LocalDate targetDate, List statuses);
+
@Modifying(clearAutomatically = true)
@Query("delete from TimerRecord r where r.todo.id = :todoId")
void deleteByTodoId(@Param("todoId") Long todoId);
diff --git a/src/main/java/com/Timo/Timo/domain/timer/service/TimerService.java b/src/main/java/com/Timo/Timo/domain/timer/service/TimerService.java
index 8eea3fb4..778984a6 100644
--- a/src/main/java/com/Timo/Timo/domain/timer/service/TimerService.java
+++ b/src/main/java/com/Timo/Timo/domain/timer/service/TimerService.java
@@ -184,6 +184,10 @@ public boolean hasActiveTimer(Long todoId) {
return timerRecordRepository.existsByTodo_IdAndStatusIn(todoId, ACTIVE_STATUS);
}
+ public boolean hasActiveTimerOn(Long todoId, LocalDate date) {
+ return timerRecordRepository.existsByTodo_IdAndTargetDateAndStatusIn(todoId, date, ACTIVE_STATUS);
+ }
+
@Transactional
public void deleteTimersByTodo(Long todoId) {
timerSessionRepository.deleteByTodoId(todoId);
diff --git a/src/main/java/com/Timo/Timo/domain/todo/service/TodoService.java b/src/main/java/com/Timo/Timo/domain/todo/service/TodoService.java
index c9cad042..f3446eed 100644
--- a/src/main/java/com/Timo/Timo/domain/todo/service/TodoService.java
+++ b/src/main/java/com/Timo/Timo/domain/todo/service/TodoService.java
@@ -137,7 +137,7 @@ public TodoStatusChangeResponse changeCompletion(Long userId, Long todoId, TodoS
throw new CustomException(TodoErrorCode.TODO_NOT_FOUND);
}
- if (timerService.hasActiveTimer(todoId)) {
+ if (timerService.hasActiveTimerOn(todoId, date)) {
throw new CustomException(TodoErrorCode.TIMER_RUNNING);
}