์ด์ ๋
Project ๋๋ฉ์ธ์ ์ถ๊ฐํด์
์์ ๋ค์ ํ๋์ ํ๋ฆ ์์ ๋ฃ์๋ค.
์ด์ ๊ตฌ์กฐ๋ ๊ฐ์ถฐ์ก๋ค.
๊ทผ๋ฐ ์ฌ๊ธฐ์ ๋ ํ๋ ๋ถ์กฑํ ๊ฒ ์์๋ค.
โ ํ๋ก์ ํธ ๋จ์๋ก ํ๋ฆ์ด ์ ๋ณด์ธ๋ค
์ง๊ธ์
Task ํ๋์ ํ๋ฆ์ ๋ณผ ์ ์์ง๋ง
Project ์ ์ฒด ํ๋ฆ์ ๋ณผ ์ ์๋ค
๊ทธ๋์ ์ค๋ ๋ชฉํ๋ ์ด๊ฑฐ์๋ค.
โ ํ๋ก์ ํธ ์์์ ์ด๋ค ์ผ์ด ์์๋์ง ํ๋์ ๋ณด์ด๊ฒ ๋ง๋ค์
์๋ฅผ ๋ค์ด,
์ฌ๋ฌ ์์
์ด ๋์์ ์งํ๋๋ ์ํฉ์์
๊ฐ ์์
์ ํ๋์ฉ ๋ณด๋ ๊ฒ ์๋๋ผ
์ด ํ๋ก์ ํธ์์ ์ค์ ๋ก ์ด๋ค ์ผ๋ค์ด ์๊ฐ ์์ผ๋ก ์ผ์ด๋ฌ๋์ง
ํ ๋ฒ์ ๋ณด๊ณ ์ถ์ ๊ฒฝ์ฐ๊ฐ ์๋ค.
์ด๋ฏธ ์ด๋ฐ API๋ ์๋ค.
GET /tasks/{id}/timeline
์ด๊ฑด ์์ ํ๋์ ํ๋ฆ์ ๋ณด์ฌ์ค๋ค.
๊ทผ๋ฐ ์ค์ ๋ก ๊ถ๊ธํ ๊ฑด ์ด๊ฑฐ๋ค.
โ ์ด ํ๋ก์ ํธ์์ ์ค์ ๋ก ๋ฌด์จ ์ผ์ด ์์๋๊ฐ?
์์ ํ๋๋ง ๋ณด๋ฉด ์ฌ์ ํ ๋ถ๋ถ์ด๋ค.
๊ทธ๋์ ํ์ํ ๊ฑด
โ Project ๊ธฐ์ค ํ๋ฆ
์ฆ,
์์
๋จ์๊ฐ ์๋๋ผ
โ ํ๋ก์ ํธ ์ ์ฒด๊ฐ ์ด๋ป๊ฒ ํ๋ฌ๊ฐ๋์ง๋ฅผ ๋ด์ผ ํ๋ค.
๊ทธ๋์ ๋ง๋ API๊ฐ ์ด๊ฑฐ๋ค.
GET /projects/{id}/timeline
์ด API๋
ํ๋ก์ ํธ์ ์ํ ๋ชจ๋ Task๋ฅผ ๊ฐ์ ธ์ค๊ณ
๊ทธ Task๋ค์ ์ด๋ฒคํธ๋ฅผ ์ ๋ถ ๋ชจ์์
์๊ฐ์์ผ๋ก ์ ๋ ฌํด์ ๋ฐํํ๋ค

ํ๋ก์ ํธ ์์์ ๋ฐ์ํ ์ฌ๋ฌ ์์
์ด๋ฒคํธ๋ค์ด
ํ๋์ ์๊ฐ ํ๋ฆ์ผ๋ก ์ ๋ ฌ๋์ด ๋ด๋ ค์ค๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ด์ ๋
โ Task ํ๋๊ฐ ์๋๋ผ
โ ํ๋ก์ ํธ ์ ์ฒด ํ๋ฆ์ด ๋ณด์ธ๋ค
๊ตฌ์กฐ๋ ์ด๋ ๊ฒ ํ๋ฌ๊ฐ๋ค.
Project โ Tasks โ TaskEvents
์ฝ๋ ํ๋ฆ์ผ๋ก ๋ณด๋ฉด ์ด๋ ๋ค.
List<Task> tasks = taskRepository.findByProject_IdAndStatusNot(projectId, TaskStatus.DELETED);
List<Long> taskIds = tasks.stream()
.map(Task::getId)
.toList();
List<TaskEvent> events = taskEventRepository.findByTaskIdInOrderByCreatedAtAsc(taskIds);
ํ๋ก์ ํธ์ ์ํ Task๋ฅผ ๋จผ์ ์กฐํํ ๋ค,
ํด๋น Task๋ค์ id๋ฅผ ๊ธฐ์ค์ผ๋ก ์ด๋ฒคํธ๋ฅผ ํ ๋ฒ์ ๊ฐ์ ธ์ค๋ ๊ตฌ์กฐ๋ค.
์ฌ๊ธฐ์ ํต์ฌ์ ์ด๊ฑฐ๋ค.
โ ์ฌ๋ฌ Task์ ์ด๋ฒคํธ๋ฅผ ํ๋๋ก ํฉ์น๋ค
์ฆ, Project ๊ธฐ์ค์ผ๋ก ์ง์ ์กฐํํ๋ ๊ฒ์ด ์๋๋ผ
Task๋ฅผ ๊ฑฐ์ณ Event๋ฅผ ์กฐํํ๋ ๊ตฌ์กฐ๋ค.
TaskEvent๋ Task ๊ธฐ์ค์ผ๋ก ๊ธฐ๋ก๋๋ค.
๊ทธ๋์ ๋ฐ๋ก Project๋ก ์กฐํํ ์ ์๋ค.
์ค๊ฐ ๊ณผ์ ์ด ํ์ํ๋ค.
Project๋ก Task๋ฅผ ์ฐพ๊ณ
Task๋ก Event๋ฅผ ์ฐพ๋๋ค
์ฆ
โ ๊ด๊ณ๋ฅผ ํ๊ณ ์ฌ๋ผ๊ฐ๋ ๊ตฌ์กฐ
์ด ๊ธฐ๋ฅ์์ ๊ฐ์ฅ ์ค์ํ ๊ฑด
โ ์ ๋ ฌ
findByTaskIdInOrderByCreatedAtAsc(...)
์ด ํ ์ค๋ก
์ฌ๋ฌ Task์ ์ด๋ฒคํธ๊ฐ
ํ๋์ ์๊ฐ ํ๋ฆ์ผ๋ก ์ ๋ฆฌ๋๋ค
๋จ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ชจ์ผ๋ ๊ฒ ์๋๋ผ
ํฉ์ด์ ธ ์๋ ์ด๋ฒคํธ๋ค์ ํ๋์ ์๊ฐ ํ๋ฆ์ผ๋ก ๋ง๋ค์ด์ฃผ๋ ํต์ฌ ํฌ์ธํธ๋ค.
์ด๊ฒ ์ค์ํ ์ด์ ๋,
โ ์ฌ๋ฌ ์์
์ด ๋์์ ์งํ๋๋ ์ํฉ์์๋
โ ๋ฌด์จ ์ผ์ด ๋จผ์ ์ผ์ด๋ฌ๊ณ , ๊ทธ ๋ค์์ ์ด๋ค ๋ณํ๊ฐ ์์๋์ง๋ฅผ
ํ๋์ ํ๋ฆ์ผ๋ก ๋ณผ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ฌ๊ธฐ์ ํ ๋จ๊ณ ๋ ๊ฐ๋ค.
๊ธฐ์กด์๋
Long projectId;
์ด๋ ๊ฒ ๋จ์ ID๋ก ๊ด๋ฆฌํ๋๋ฐ
์ด๊ฑธ ๋ฐ๊ฟจ๋ค.
@ManyToOne(fetch = FetchType.LAZY)
private Project project;
์ด๋ ๊ฒ ๋ฐ๊พธ๋๊น
Task โ Project ๊ด๊ณ๊ฐ ๋ช
ํํด์ง๊ณ
JPA ๊ธฐ์ค์ผ๋ก ์์ฐ์ค๋ฝ๊ฒ ์กฐํ ๊ฐ๋ฅํด์ก๋ค
์ด ๊ตฌ์กฐ๊ฐ ๋๋ฉด์
๋ฉ์๋๋ ๋ฐ๋๋ค.
๊ธฐ์กด:
findByProjectIdAndStatus(...)
๋ณ๊ฒฝ:
findByProject_IdAndStatus(...)
์ด๊ฑด ๋จ์ ๋ฌธ๋ฒ์ด ์๋๋ผ
โ ๊ฐ์ฒด ๊ด๊ณ ๊ธฐ๋ฐ ์กฐํ๋ก ๋ฐ๋ ๊ฒ
Entity ๊ทธ๋๋ก ๋ด๋ ค์ฃผ๋ ๊ฑด ์ ๋ง์์
TaskResponse๋ ์์ ํ๋ค.
this.projectId = task.getProject().getId();
this.projectName = task.getProject().getName();
โ ํ์ํ ์ ๋ณด๋ง ๋ด๋ ค์ฃผ๋ ๊ตฌ์กฐ
์ค๋ ํ ๊ฑธ ํ ์ค๋ก ์ ๋ฆฌํ๋ฉด ์ด๊ฑฐ๋ค.
์ฌ๋ฌ Task์ ํฉ์ด์ ธ ์๋ ์ด๋ฒคํธ๋ฅผ ๋ชจ์์
ํ๋ก์ ํธ ์ ์ฒด ํ๋ฆ์ผ๋ก ์ฌ๊ตฌ์ฑํ๋ค
Project ๋๋ฉ์ธ โ๏ธ
Task โ Project ๊ด๊ณ โ๏ธ
Default Project โ๏ธ
Task Timeline โ๏ธ
Project Timeline โ๏ธ
์ด์ ๋
โ ํ๋ฆ์ ์กฐํํ๋ ๊ฒ ์๋๋ผ
โ ํ๋ฆ์ ๊ตฌ์ฑํ ์ ์๋ ์ํ๊ฐ ๋๋ค
์ฌ๊ธฐ์ ๋์ด ์๋๋ค.
์ง๊ธ์ ํ๋ฆ์ ๋์ด๋ง ํ๊ณ ์๋ค.
๋ค์ ๋จ๊ณ๋ ์ด๊ฑฐ๋ค.
ํ๋ก์ ํธ ๋จ์ ํต๊ณ (์ผ๋ง๋ ์ค๋ ๊ฑธ๋ ธ๋์ง)
์ํ๋ณ ์ฒด๋ฅ ์๊ฐ ๋ถ์
๊ฐ์ฅ ์ค๋ ๋งํ ๊ตฌ๊ฐ ์ฐพ๊ธฐ
โ ํ๋ฆ์ ํด์ํ๋ ๋จ๊ณ
๊ทธ๋ฆฌ๊ณ ํ ๋จ๊ณ ๋ ๋๊ฐ๋ฉด
โ Redis๋ฅผ ๋ถ์ฌ์
โ ๊ณ์ฐ ๋น์ฉ์ด ํฐ ์กฐํ๋ฅผ ์บ์ฑํ ์์
์ฆ
๋จ์ ์กฐํ โ ํ๋ฆ ์ดํด โ ํ๋ฆ ๋ถ์ โ ์ฑ๋ฅ ์ต์ ํ ๋ก ํ์ฅํด ๋๊ฐ ์์ ์ด๋ค