๐Ÿฐ [flowbit] #6. Project Analysis - ์ด๋ฒคํŠธ๋ฅผ ํ•ด์„ํ•˜์—ฌ ํ”„๋กœ์ ํŠธ ์ƒํƒœ๋ฅผ ๋„์ถœํ•˜๋‹ค

bean8080๐Ÿซ›ยท2026๋…„ 5์›” 1์ผ

flowbit ๐Ÿฐโ˜˜๏ธ

๋ชฉ๋ก ๋ณด๊ธฐ
7/15

โ˜˜๏ธ 1. ์˜ค๋Š˜ ๋ชฉํ‘œ

์–ด์ œ๋Š”

Project Timeline์„ ๋งŒ๋“ค์–ด์„œ
ํ”„๋กœ์ ํŠธ ๋‹จ์œ„์˜ ํ๋ฆ„์„ ๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.

์ด์ œ๋Š” ํ๋ฆ„์ด ๋ณด์ธ๋‹ค.

๊ทผ๋ฐ ์—ฌ๊ธฐ์„œ ๋˜ ํ•˜๋‚˜ ๋ถ€์กฑํ•œ ๊ฒŒ ์žˆ์—ˆ๋‹ค.

โ†’ ํ๋ฆ„์€ ๋ณด์ด๋Š”๋ฐ, ํ•ด์„์€ ์•ˆ๋œ๋‹ค

์ง€๊ธˆ์€ ์ด๋ฒคํŠธ๋ฅผ ๋‚˜์—ดํ•˜๋Š” ๋‹จ๊ณ„๋‹ค.
๊ทธ๋ž˜์„œ ์˜ค๋Š˜ ๋ชฉํ‘œ๋Š” ์ด๊ฑฐ์˜€๋‹ค.

โ†’ ํ๋ฆ„์„ ์š”์•ฝํ•ด์„œ ๋ณด์—ฌ์ฃผ์ž

์˜ˆ๋ฅผ ๋“ค์–ด,

ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋‹ค ๋ณด๋ฉด
์ž‘์—…์€ ๊ณ„์† ์ถ”๊ฐ€๋˜๊ณ  ์ƒํƒœ๋„ ๋ฐ”๋€Œ๋Š”๋ฐ,

๋ง‰์ƒ ์ง€๊ธˆ ์ด ํ”„๋กœ์ ํŠธ๊ฐ€ ์–ด๋А ๋‹จ๊ณ„์— ์žˆ๋Š”์ง€
ํ•œ ๋ฒˆ์— ํŒŒ์•…ํ•˜๊ธฐ๋Š” ์–ด๋ ต๋‹ค.

์ด๊ฑธ ํ™•์ธํ•˜๋ ค๋ฉด
์—ฌ๋Ÿฌ Task๋ฅผ ํ•˜๋‚˜์”ฉ ๋ณด๊ฑฐ๋‚˜,
Timeline์„ ์ง์ ‘ ํ•ด์„ํ•ด์•ผ ํ•œ๋‹ค.

๊ทธ๋ž˜์„œ

โ†’ ์ด๋ฒคํŠธ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ
โ†’ ํ๋ฆ„์„ ํ•ด์„ํ•ด์„œ ๋ณด์—ฌ์ฃผ๋Š” ๊ตฌ์กฐ๊ฐ€ ํ•„์š”ํ–ˆ๋‹ค.

โ†’ ๊ฒฐ๊ตญ, ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ
โ†’ ์ƒํƒœ๋ฅผ โ€˜ํŒ๋‹จโ€™ํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ์ด์—ˆ๋‹ค.


โ˜˜๏ธ 2. Timeline์˜ ํ•œ๊ณ„

์ด๋ฏธ ์ด๋Ÿฐ API๊ฐ€ ์žˆ๋‹ค.

GET /api/projects/{id}/timeline

์ด๊ฑด ํ”„๋กœ์ ํŠธ ์•ˆ์—์„œ ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ๋ฅผ
์‹œ๊ฐ„์ˆœ์œผ๋กœ ๋‚˜์—ดํ•ด์ค€๋‹ค.

ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ ๊ถ๊ธˆํ•œ ๊ฑด ์ด๊ฑฐ๋‹ค.

โ†’ ์ง€๊ธˆ ์ด ํ”„๋กœ์ ํŠธ๋Š” ์–ด๋А ์ •๋„ ์ง„ํ–‰๋๋Š”๊ฐ€?
โ†’ ์ž‘์—…์€ ๋ช‡ ๊ฐœ๊ณ , ์ƒํƒœ๋Š” ์–ด๋–ป๊ฒŒ ๋ถ„ํฌ๋˜์–ด ์žˆ๋Š”๊ฐ€?
โ†’ ์ตœ๊ทผ์— ์–ธ์ œ ํ™œ๋™์ด ์žˆ์—ˆ๋Š”๊ฐ€?

๋‹จ์ˆœ ๋‚˜์—ด๋งŒ์œผ๋กœ๋Š”
์ด๊ฑธ ๋ฐ”๋กœ ์•Œ๊ธฐ ์–ด๋ ต๋‹ค.

๊ทธ๋ž˜์„œ ํ•„์š”ํ•œ ๊ฑด

โ†’ ํ๋ฆ„์„ ํ•ด์„ํ•œ ๊ฒฐ๊ณผ

๋‹จ์ˆœํžˆ ์ด๋ฒคํŠธ๋ฅผ ๋ณด๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ
์ด ํ”„๋กœ์ ํŠธ๊ฐ€ ์ง€๊ธˆ ๊ฑด๊ฐ•ํ•˜๊ฒŒ ์ง„ํ–‰๋˜๊ณ  ์žˆ๋Š”์ง€ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

์ฆ‰,

โ†’ ํ๋ฆ„์„ ๋‚˜์—ดํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ
โ†’ ํ๋ฆ„์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒํƒœ๋ฅผ ํ•ด์„ํ•ด์•ผ ํ•œ๋‹ค.


โ˜˜๏ธ 3. Project Analysis API

์ด๊ฑธ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“  API๊ฐ€ ์ด๊ฑฐ๋‹ค.

GET /api/projects/{id}/analysis

์ด API๋Š”

ํ”„๋กœ์ ํŠธ์— ์†ํ•œ Task๋ฅผ ์กฐํšŒํ•˜๊ณ 
TaskEvent๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ
์ƒํƒœ ๋ถ„ํฌ์™€ ํ๋ฆ„ ์ •๋ณด๋ฅผ ๊ณ„์‚ฐํ•ด์„œ ๋ฐ˜ํ™˜ํ•œ๋‹ค

ํ”„๋กœ์ ํŠธ ๋ถ„์„ API ์‘๋‹ต ๊ฒฐ๊ณผ

์ด์ œ๋Š”

โ†’ ๋ฌด์Šจ ์ผ์ด ์žˆ์—ˆ๋Š”์ง€๊ฐ€ ์•„๋‹ˆ๋ผ
โ†’ ์ง€๊ธˆ ์–ด๋–ค ์ƒํƒœ์ธ์ง€๊ฐ€ ๋ณด์ธ๋‹ค

์ด API๋Š” ๋‹จ์ˆœ ์กฐํšŒ๊ฐ€ ์•„๋‹ˆ๋ผ,

โ†’ ์—ฌ๋Ÿฌ ์ด๋ฒคํŠธ๋ฅผ ์ข…ํ•ฉํ•ด์„œ
โ†’ ํ”„๋กœ์ ํŠธ์˜ ํ˜„์žฌ ์ƒํƒœ๋ฅผ ํ•œ ๋ฒˆ์— ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“  API๋‹ค.


โ˜˜๏ธ 4. ๋ถ„์„ ํ๋ฆ„

๊ตฌ์กฐ๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•œ๋‹ค.

Project โ†’ Task โ†’ TaskEvent

์ฝ”๋“œ ํ๋ฆ„์€ ์ด๋ ‡๋‹ค.

List<Task> tasks = taskRepository.findByProject_IdAndStatusNot(projectId, TaskStatus.DELETED);

List<Long> taskIds = tasks.stream()
        .map(Task::getId)
        .toList();

List<TaskEvent> events = taskIds.isEmpty()
        ? List.of()
        : taskEventRepository.findByTaskIdInOrderByCreatedAtAsc(taskIds);

ํ•ต์‹ฌ์€ ์ด๊ฑฐ๋‹ค.

โ†’ Task๋Š” ํ˜„์žฌ ์ƒํƒœ
โ†’ TaskEvent๋Š” ์ƒํƒœ ๋ณ€ํ™” ์ด๋ ฅ

์ด ๋‘˜์„ ํ•ฉ์ณ์•ผ

โ†’ ํ๋ฆ„ ํ•ด์„์ด ๊ฐ€๋Šฅํ•˜๋‹ค


โ˜˜๏ธ 5. ์ƒํƒœ ๊ธฐ๋ฐ˜ ํ†ต๊ณ„

๋‹จ์ˆœ ์กฐํšŒ๋ฅผ ๋„˜์–ด์„œ
์ƒํƒœ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ†ต๊ณ„๋ฅผ ๋งŒ๋“ ๋‹ค.

int todoCount = countByStatus(tasks, TaskStatus.TODO);
int inProgressCount = countByStatus(tasks, TaskStatus.IN_PROGRESS);
int blockedCount = countByStatus(tasks, TaskStatus.BLOCKED);
int doneCount = countByStatus(tasks, TaskStatus.DONE);

์ƒํƒœ๋ณ„ ๋ถ„ํฌ ๊ฒฐ๊ณผ

โ†’ TODO / IN_PROGRESS / DONE ๋น„์œจ ํ™•์ธ

์˜ˆ๋ฅผ ๋“ค์–ด,

IN_PROGRESS๊ฐ€ ๋งŽ๊ณ  DONE์ด ์ ๋‹ค๋ฉด
โ†’ ์ž‘์—…์ด ์‹œ์ž‘์€ ๋˜์—ˆ์ง€๋งŒ ์™„๋ฃŒ๋˜์ง€ ์•Š๊ณ  ์Œ“์ด๊ณ  ์žˆ๋Š” ์ƒํƒœ๋‹ค.

์ฆ‰,

๋ณ‘๋ชฉ ๊ตฌ๊ฐ„์„ ํ•œ๋ˆˆ์— ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค

โ†’ ๋‹จ์ˆœ ๊ฐœ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ
โ†’ ์ƒํƒœ ํ๋ฆ„์˜ ๋ฌธ์ œ๋ฅผ ๋“œ๋Ÿฌ๋‚ด๋Š” ์ง€ํ‘œ๊ฐ€ ๋œ๋‹ค


โ˜˜๏ธ 6. ๋งˆ์ง€๋ง‰ ํ™œ๋™ ์‹œ์ 

LocalDateTime lastEventAt = events.isEmpty()
        ? null
        : events.get(events.size() - 1).getCreatedAt();

์ตœ๊ทผ ํ™œ๋™ ์‹œ์ 

โ†’ ๊ฐ€์žฅ ์ตœ๊ทผ ์ด๋ฒคํŠธ ์‹œ๊ฐ„
โ†’ ์ด ํ”„๋กœ์ ํŠธ๊ฐ€ ๋งˆ์ง€๋ง‰์œผ๋กœ ์›€์ง์ธ ์‹œ๊ฐ„

์ตœ๊ทผ ํ™œ๋™ ์‹œ์ ์ด ์˜ค๋ž˜๋๋‹ค๋ฉด

โ†’ ์ด ํ”„๋กœ์ ํŠธ๋Š” ์‚ฌ์‹ค์ƒ ๋ฉˆ์ถฐ์žˆ๋Š” ์ƒํƒœ๋‹ค.

์ฆ‰, ์ตœ๊ทผ ์ด๋ฒคํŠธ ์‹œ๊ฐ„ ํ•˜๋‚˜๋กœ
ํ”„๋กœ์ ํŠธ์˜ โ€˜ํ™œ์„ฑ๋„โ€™๋ฅผ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๋‹ค


โ˜˜๏ธ 7. ์ƒ์„ฑ API ๊ฐœ์„ 

๊ธฐ์กด์—๋Š”

POST /tasks

void ์ƒํƒœ์˜€๋‹ค.

์ด๊ฑธ ๋ฐ”๊ฟจ๋‹ค.

โ†’ ์ƒ์„ฑํ•˜๋ฉด ๋ฐ”๋กœ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜

์ƒ์„ฑ API ์‘๋‹ต

โ†’ ์ƒ์„ฑ ํ›„ id ํฌํ•จ๋œ JSON ๋ฐ˜ํ™˜

์ด์ œ๋Š”

์ƒ์„ฑ โ†’ ๋ฐ”๋กœ ์กฐํšŒ/์‚ฌ์šฉ ๊ฐ€๋Šฅ


โ˜˜๏ธ 8. ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ํ†ต์ผ

๊ธฐ์กด์—๋Š”

โ†’ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๊ธฐ๋งŒ ํ–ˆ๋‹ค

์ด๊ฑธ ๋ฐ”๊ฟจ๋‹ค.

โ†’ JSON ํ˜•ํƒœ๋กœ ํ†ต์ผ

์—๋Ÿฌ ์‘๋‹ต JSON1

์—๋Ÿฌ ์‘๋‹ต JSON2

์ด์ œ๋Š”

โ†’ ์„ฑ๊ณต/์‹คํŒจ ๋ชจ๋‘ ๊ฐ™์€ ๊ตฌ์กฐ


โ˜˜๏ธ 9. ์บ์‹ฑ ์ ์šฉ (๊ทธ๋ฆฌ๊ณ  ๋ฌธ์ œ ๋ฐœ์ƒ)

๋ถ„์„ API๋Š” ๊ณ„์‚ฐ ๋น„์šฉ์ด ์žˆ๋‹ค.

ํ”„๋กœ์ ํŠธ์— ์†ํ•œ Task๋ฅผ ์กฐํšŒํ•˜๊ณ 
๊ฐ Task์˜ ์ด๋ฒคํŠธ๋ฅผ ๋‹ค์‹œ ์กฐํšŒํ•œ ๋’ค
์ƒํƒœ๋ณ„ ๊ฐœ์ˆ˜์™€ ๋งˆ์ง€๋ง‰ ์ด๋ฒคํŠธ ์‹œ์ ์„ ๊ณ„์‚ฐํ•ด์•ผ ํ•œ๋‹ค.

๊ทธ๋ž˜์„œ ๋ถ„์„ API์— ์บ์‹ฑ์„ ๋ถ™์˜€๋‹ค.

@Cacheable(value = "projectAnalysis", key = "#projectId")

๊ทธ๋ฆฌ๊ณ  Task ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด
๋ถ„์„ ๊ฒฐ๊ณผ๋„ ๋ฐ”๋€Œ์–ด์•ผ ํ•˜๋ฏ€๋กœ ์บ์‹œ๋ฅผ ์ œ๊ฑฐํ•˜๋„๋ก ํ–ˆ๋‹ค.

@CacheEvict(value = "projectAnalysis", key = "#result.projectId")

์ฒซ ์š”์ฒญ์€ 1751ms๊ฐ€ ๊ฑธ๋ ธ๋‹ค.

์ด ์š”์ฒญ์—์„œ๋Š” ์‹ค์ œ๋กœ ๋ถ„์„ ๋กœ์ง์ด ์‹คํ–‰๋˜๊ณ ,
Task ์กฐํšŒ์™€ Event ์กฐํšŒ, ์ƒํƒœ๋ณ„ ์ง‘๊ณ„๊ฐ€ ์ˆ˜ํ–‰๋œ๋‹ค.

๊ฐ™์€ ์š”์ฒญ์„ ๋‹ค์‹œ ๋ณด๋‚ด์ž ์‘๋‹ต ์‹œ๊ฐ„์ด 95ms๋กœ ์ค„์—ˆ๋‹ค.

์ฆ‰,

โ†’ ์ฒซ ์š”์ฒญ: 1751ms
โ†’ ๋‘ ๋ฒˆ์งธ ์š”์ฒญ: 95ms

์•ฝ 18๋ฐฐ ์ •๋„ ๋นจ๋ผ์ง„ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๋‹จ์ˆœํžˆ ๋กœ์ง์„ ๋น ๋ฅด๊ฒŒ ๋งŒ๋“  ๊ฒƒ์ด ์•„๋‹ˆ๋ผ,
๋™์ผํ•œ ์š”์ฒญ์— ๋Œ€ํ•ด์„œ๋Š” ๋ถ„์„ ๋กœ์ง ์ž์ฒด๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ 
์บ์‹œ์— ์ €์žฅ๋œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”๋กœ ๋ฐ˜ํ™˜ํ•œ ๊ฒƒ์ด๋‹ค.

์บ์‹ฑ์€ ์–ด๋…ธํ…Œ์ด์…˜๋งŒ ๋ถ™์ด๋ฉด ๋๋‚˜๋Š” ์ค„ ์•Œ์•˜๋Š”๋ฐ,
์‹ค์ œ๋กœ๋Š” ๋ฌด์—‡์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€๊นŒ์ง€ ๊ฐ™์ด ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฑธ ์•Œ๊ฒŒ ๋๋‹ค.

๊ทผ๋ฐ ์—ฌ๊ธฐ์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

GET http://localhost:8080/api/projects/1/analysis

HTTP/1.1 400 
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json
Transfer-Encoding: chunked
Date: Fri, 01 May 2026 12:47:22 GMT
Connection: close

{
  "code": "BAD_REQUEST",
  "message": "DefaultSerializer requires a Serializable payload but received an object of type [com.ahyeon.flowbit.domain.project.dto.ProjectAnalysisResponse]",
  "timestamp": "2026-05-01T21:47:22.185627213"
}
์‘๋‹ต ํŒŒ์ผ์ด ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
> 2026-05-01T214722.400.json

Response code: 400; Time: 87ms (87 ms); Content length: 223 bytes (223 B)

์บ์‹œ์— ์ €์žฅํ•˜๋ ค๋Š” ๊ฐ์ฒด๊ฐ€
์ง๋ ฌํ™”๊ฐ€ ์•ˆ ๋˜์–ด ์žˆ์–ด์„œ ์—๋Ÿฌ๊ฐ€ ํ„ฐ์กŒ๋‹ค.


โ˜˜๏ธ 10. ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… (Serializable)

๋ฌธ์ œ ์›์ธ:

โ†’ ์บ์‹œ์— ์ €์žฅํ•˜๋ ค๋Š” DTO๊ฐ€ ์ง๋ ฌํ™” ๋ถˆ๊ฐ€

ํ•ด๊ฒฐ:

public class ProjectAnalysisResponse implements Serializable

์ง๋ ฌํ™” ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ํ›„ ์ •์ƒ ์‘๋‹ต

โ†’ ์บ์‹ฑ ์ •์ƒ ๋™์ž‘

์ด๊ฑธ ํ†ตํ•ด ์•Œ๊ฒŒ ๋œ ์ :

์บ์‹ฑ์€ ๋‹จ์ˆœ ์–ด๋…ธํ…Œ์ด์…˜์ด ์•„๋‹ˆ๋ผ
์ €์žฅ ๋ฐฉ์‹๊นŒ์ง€ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค


โ˜˜๏ธ 11. API ๊ตฌ์กฐ ๊ฐœ์„  (/api prefix)

๊ธฐ์กด:

/projects
/tasks

๋ณ€๊ฒฝ:

/api/projects
/api/tasks

API ๊ฒฝ๋กœ ๋ณ€๊ฒฝ ๋น„๊ต1

API ๊ฒฝ๋กœ ๋ณ€๊ฒฝ ๋น„๊ต2

โ†’ ํ”„๋ก ํŠธ / ๋ฐฑ์—”๋“œ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌ


โ˜˜๏ธ 12. ์˜ค๋Š˜ ์ •๋ฆฌ

์˜ค๋Š˜ ํ•œ ๊ฑธ ํ•œ ์ค„๋กœ ์ •๋ฆฌํ•˜๋ฉด ์ด๊ฑฐ๋‹ค.

โ†’ ํ๋ฆ„์„ ๋ณด๋Š” ๋‹จ๊ณ„์—์„œ
โ†’ ํ๋ฆ„์„ ํ•ด์„ํ•˜๊ณ  ํŒ๋‹จํ•˜๋Š” ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ”๋‹ค

์ด์ œ๋Š” ๋‹จ์ˆœํžˆ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚˜์—ดํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ

โ†’ ํ”„๋กœ์ ํŠธ์˜ ์ƒํƒœ๋ฅผ ํ•ด์„ํ•˜๊ณ 
โ†’ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ง€์ ์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ๊ฐ€ ๋˜์—ˆ๋‹ค

๋˜ํ•œ ๋ถ„์„ API์— ์บ์‹ฑ์„ ์ ์šฉํ•˜๋ฉด์„œ
โ†’ ์„ฑ๋Šฅ ๊ฐœ์„ ๊ณผ ์ง๋ ฌํ™” ๋ฌธ์ œ๊นŒ์ง€ ํ•จ๊ป˜ ๊ฒฝํ—˜ํ–ˆ๋‹ค.


โ˜˜๏ธ 13. ํ˜„์žฌ ์ƒํƒœ

Project โœ”๏ธ
Task โœ”๏ธ
TaskEvent โœ”๏ธ
Timeline โœ”๏ธ
Analysis โœ”๏ธ
Exception Handling โœ”๏ธ
Caching โœ”๏ธ
API ๊ตฌ์กฐ โœ”๏ธ

์ด์ œ๋Š”

โ†’ ๋‹จ์ˆœ CRUD๊ฐ€ ์•„๋‹ˆ๋ผ
โ†’ ํ๋ฆ„ ๊ธฐ๋ฐ˜ ์‹œ์Šคํ…œ


โ˜˜๏ธ 14. ๋‹ค์Œ ๋ชฉํ‘œ

์ด์ œ ๋ฐฑ์—”๋“œ๋Š” ์ถฉ๋ถ„ํžˆ ์ค€๋น„๋๋‹ค.

๋‹ค์Œ ๋‹จ๊ณ„๋Š”

โ†’ React๋กœ ํ”„๋ก ํŠธ ๋ถ™์ด๊ธฐ

Timeline + Analysis๋ฅผ
ํ™”๋ฉด์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๋‹จ๊ณ„

๊ทธ๋ฆฌ๊ณ  Redis๋Š”

โ†’ ๊ตฌ์กฐ๋งŒ ์žก์•„๋‘๊ณ 
โ†’ ์‹ค์ œ ์ ์šฉ์€ ์ดํ›„ ๋‹จ๊ณ„์—์„œ ์ง„ํ–‰ํ•  ์˜ˆ์ •

0๊ฐœ์˜ ๋Œ“๊ธ€