[{"data":1,"prerenderedAt":486},["ShallowReactive",2],{"docs:\u002Fko\u002Fguide\u002Foverview":3},{"page":4,"untranslated":485,"translated":187},{"id":5,"title":6,"body":7,"description":476,"extension":477,"meta":478,"navigation":187,"path":479,"publishedAt":480,"seo":481,"stem":482,"updatedAt":483,"__hash__":484},"docs_ko\u002Fko\u002Fguide\u002Foverview.md","개요",{"type":8,"value":9,"toc":465},"minimark",[10,14,28,33,36,41,47,57,61,67,80,84,90,102,106,109,128,132,135,389,412,416,438,442,455,461],[11,12,6],"h1",{"id":13},"概要",[15,16,17,18,21,22,24,25,27],"p",{},"Tsumugi는 Cloudflare Workers용으로 설계된 잡 관리 시스템입니다.",[19,20],"br",{},"\n사용자는 잡의 처리를 performer로 정의하고, enqueue로 잡을 투입합니다.",[19,23],{},"\n잡의 전달, 실행, 상태 기록은 Tsumugi가 담당합니다.",[19,26],{},"\n관리용 대시보드와 REST API도 제공합니다.",[29,30,32],"h2",{"id":31},"何を解決するの","무엇을 해결하나요",[15,34,35],{},"Cloudflare Queues를 잡 큐로 직접 사용하면 몇 가지 문제가 발생합니다.",[37,38,40],"h3",{"id":39},"ジョブの状態を管理しづらく成功したジョブが残らない","잡의 상태를 관리하기 어렵고, 성공한 잡이 남지 않는다",[15,42,43,44,46],{},"Queues는 투입한 메시지의 상태를 조회하는 API를 제공하지 않습니다.",[19,45],{},"\n성공한 메시지도 보관되지 않으며, 과거의 실행을 나중에 확인할 수단이 마련되어 있지 않습니다.",[15,48,49,50,52,56],{},"Tsumugi의 대시보드와 REST API는 실행 중인 잡과 종료된 잡 모두에 대해 목록 표시, 검색, 집계를 제공합니다.",[19,51],{},[53,54,55],"code",{},"TSUMUGI_METRICS"," binding을 설정하면 실패율과 실행 시간이 메트릭으로 기록됩니다.",[37,58,60],{"id":59},"排他制御と重複排除を実装しづらい","배타 제어와 중복 제거를 구현하기 어렵다",[15,62,63,64,66],{},"Queues는 메시지 간의 의존 관계나 연관성을 관리하지 않습니다.",[19,65],{},"\n따라서 동일 고객에 대한 동시 실행 방지나 중복 투입 제거는 사용자가 개별적으로 구현해야 합니다.",[15,68,69,70,73,74,76,79],{},"Tsumugi에서는 ",[53,71,72],{},"concurrencyKey","를 지정한 잡은 같은 키를 가진 잡끼리 한 건씩 순서대로 실행됩니다.",[19,75],{},[53,77,78],{},"uniqueKey","를 지정하면 일정 기간 내에 같은 키로 투입된 잡은 중복으로 간주되며, 새 잡을 만들지 않고 기존 잡 ID를 반환값으로 돌려줍니다.",[37,81,83],{"id":82},"ジョブの種類が増えるほど分岐が増える","잡의 종류가 늘어날수록 분기가 늘어난다",[15,85,86,87,89],{},"Queues에서는 하나의 큐에 consumer를 하나만 설정할 수 있습니다.",[19,88],{},"\n따라서 잡의 종류가 늘어나면 큐의 수를 늘리거나 consumer 내부의 분기 처리가 복잡해집니다.",[15,91,92,93,95,96,101],{},"Tsumugi는 performer의 export 이름을 binding 이름으로 사용해 잡의 종류를 구분하므로, consumer에 분기를 작성할 필요가 없습니다.",[19,94],{},"\nbinding 이름과 payload 타입의 관계는 ",[97,98,100],"a",{"href":99},"\u002Fko\u002Fguide\u002Fperformer#binding%E5%90%8D","binding名","에서 설명합니다.",[37,103,105],{"id":104},"まれにメッセージが失われる","드물게 메시지가 유실된다",[15,107,108],{},"Queues는 처리되지 않고 유실된 메시지를 감지할 수단을 제공하지 않습니다.",[15,110,111,112,114,117,118,120,123,124,127],{},"Tsumugi는 실행을 시작한 잡에서 결과가 보고되지 않으면 해당 잡을 무응답으로 판정합니다.",[19,113],{},[53,115,116],{},"at-least-once"," 시맨틱스의 경우, 무응답 잡은 다시 투입됩니다.",[19,119],{},[53,121,122],{},"at-most-once"," 시맨틱스의 경우, 무응답 잡은 ",[53,125,126],{},"STALLED"," 상태로 전이하며 수동으로 재시도하기 전까지 실행되지 않습니다.",[29,129,131],{"id":130},"利用者が記述するコード","사용자가 작성하는 코드",[15,133,134],{},"작성할 부분은 잡 처리의 정의와 투입, 단 두 곳뿐입니다.",[136,137,142],"pre",{"className":138,"code":139,"language":140,"meta":141,"style":141},"language-ts shiki shiki-themes github-light github-dark","import { defineTsumugi } from 'tsumugi';\nimport { Performer } from 'tsumugi\u002Fperformer';\n\n\u002F\u002F 1. 잡의 처리를 performer로 정의하고 export한다\nexport class SendMail extends Performer\u003C{ to: string }, void, {}, Env> {\n  async perform(payload: { to: string }): Promise\u003Cvoid> {\n    await this.env.MAILER.send(payload.to);\n  }\n}\n\nconst tsumugi = defineTsumugi({ performers: { SendMail } });\n\n\u002F\u002F 2. 투입한다. binding 이름은 export한 이름이다\nconst id = await tsumugi.enqueue(env, { binding: 'SendMail', payload: { to: 'a@example.com' } });\n","ts","",[53,143,144,167,182,189,196,244,285,309,315,321,326,344,349,355],{"__ignoreMap":141},[145,146,149,153,157,160,164],"span",{"class":147,"line":148},"line",1,[145,150,152],{"class":151},"szBVR","import",[145,154,156],{"class":155},"sVt8B"," { defineTsumugi } ",[145,158,159],{"class":151},"from",[145,161,163],{"class":162},"sZZnC"," 'tsumugi'",[145,165,166],{"class":155},";\n",[145,168,170,172,175,177,180],{"class":147,"line":169},2,[145,171,152],{"class":151},[145,173,174],{"class":155}," { Performer } ",[145,176,159],{"class":151},[145,178,179],{"class":162}," 'tsumugi\u002Fperformer'",[145,181,166],{"class":155},[145,183,185],{"class":147,"line":184},3,[145,186,188],{"emptyLinePlaceholder":187},true,"\n",[145,190,192],{"class":147,"line":191},4,[145,193,195],{"class":194},"sJ8bj","\u002F\u002F 1. 잡의 처리를 performer로 정의하고 export한다\n",[145,197,199,202,205,209,212,215,218,222,225,229,232,235,238,241],{"class":147,"line":198},5,[145,200,201],{"class":151},"export",[145,203,204],{"class":151}," class",[145,206,208],{"class":207},"sScJk"," SendMail",[145,210,211],{"class":151}," extends",[145,213,214],{"class":207}," Performer",[145,216,217],{"class":155},"\u003C{ ",[145,219,221],{"class":220},"s4XuR","to",[145,223,224],{"class":151},":",[145,226,228],{"class":227},"sj4cs"," string",[145,230,231],{"class":155}," }, ",[145,233,234],{"class":227},"void",[145,236,237],{"class":155},", {}, ",[145,239,240],{"class":207},"Env",[145,242,243],{"class":155},"> {\n",[145,245,247,250,253,256,259,261,264,266,268,270,273,275,278,281,283],{"class":147,"line":246},6,[145,248,249],{"class":151},"  async",[145,251,252],{"class":207}," perform",[145,254,255],{"class":155},"(",[145,257,258],{"class":220},"payload",[145,260,224],{"class":151},[145,262,263],{"class":155}," { ",[145,265,221],{"class":220},[145,267,224],{"class":151},[145,269,228],{"class":227},[145,271,272],{"class":155}," })",[145,274,224],{"class":151},[145,276,277],{"class":207}," Promise",[145,279,280],{"class":155},"\u003C",[145,282,234],{"class":227},[145,284,243],{"class":155},[145,286,288,291,294,297,300,303,306],{"class":147,"line":287},7,[145,289,290],{"class":151},"    await",[145,292,293],{"class":227}," this",[145,295,296],{"class":155},".env.",[145,298,299],{"class":227},"MAILER",[145,301,302],{"class":155},".",[145,304,305],{"class":207},"send",[145,307,308],{"class":155},"(payload.to);\n",[145,310,312],{"class":147,"line":311},8,[145,313,314],{"class":155},"  }\n",[145,316,318],{"class":147,"line":317},9,[145,319,320],{"class":155},"}\n",[145,322,324],{"class":147,"line":323},10,[145,325,188],{"emptyLinePlaceholder":187},[145,327,329,332,335,338,341],{"class":147,"line":328},11,[145,330,331],{"class":151},"const",[145,333,334],{"class":227}," tsumugi",[145,336,337],{"class":151}," =",[145,339,340],{"class":207}," defineTsumugi",[145,342,343],{"class":155},"({ performers: { SendMail } });\n",[145,345,347],{"class":147,"line":346},12,[145,348,188],{"emptyLinePlaceholder":187},[145,350,352],{"class":147,"line":351},13,[145,353,354],{"class":194},"\u002F\u002F 2. 투입한다. binding 이름은 export한 이름이다\n",[145,356,358,360,363,365,368,371,374,377,380,383,386],{"class":147,"line":357},14,[145,359,331],{"class":151},[145,361,362],{"class":227}," id",[145,364,337],{"class":151},[145,366,367],{"class":151}," await",[145,369,370],{"class":155}," tsumugi.",[145,372,373],{"class":207},"enqueue",[145,375,376],{"class":155},"(env, { binding: ",[145,378,379],{"class":162},"'SendMail'",[145,381,382],{"class":155},", payload: { to: ",[145,384,385],{"class":162},"'a@example.com'",[145,387,388],{"class":155}," } });\n",[15,390,391,392,394,395,399,400,399,404,399,408,101],{},"큐의 구성, consumer의 분기, 상태 기록을 작성할 필요는 없습니다.",[19,393],{},"\n각 기능은 ",[97,396,398],{"href":397},"\u002Fko\u002Fguide\u002Fenqueue","ジョブの投入",", ",[97,401,403],{"href":402},"\u002Fko\u002Fguide\u002Fexecution","実行の制御",[97,405,407],{"href":406},"\u002Fko\u002Fguide\u002Fschedule","定期実行",[97,409,411],{"href":410},"\u002Fko\u002Fguide\u002Fdashboard","ダッシュボードと認証",[29,413,415],{"id":414},"前提条件","전제 조건",[15,417,418,419,422,423,425,426,429,430,432,433,437],{},"Workers Paid 플랜과 2025-11-17 이후의 ",[53,420,421],{},"compatibility_date","가 필요합니다.",[19,424],{},"\n필요한 리소스와 설정은 ",[53,427,428],{},"npx tsumugi init","으로 생성합니다.",[19,431],{},"\n절차는 ",[97,434,436],{"href":435},"\u002Fko\u002Fguide\u002Fgetting-started","Getting Started","를 참조하십시오.",[29,439,441],{"id":440},"着想","착안",[15,443,444,445,451,452,454],{},"Cloudflare 서비스만으로 완결되는 잡 큐에 대시보드를 함께 제공하는 구성은 ",[97,446,450],{"href":447,"rel":448},"https:\u002F\u002Fkiribi.pages.dev\u002F",[449],"nofollow","Kiribi","에서 착안했습니다.",[19,453],{},"\n도입 절차를 최소한으로 줄이고 사용자가 내부 구성을 의식하지 않게 한다는 방침도 Kiribi와 같습니다.",[15,456,457,458,460],{},"Tsumugi는 실행량 제한, 잡별 실행 보장, 재시도 제어, Flow를 통한 다단계 실행, 정기 실행을 갖추고 있습니다.",[19,459],{},"\n이러한 기능 때문에 사용자가 준비해야 하는 리소스와 설정은 Kiribi보다 많아집니다.",[462,463,464],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":141,"searchDepth":184,"depth":184,"links":466},[467,473,474,475],{"id":31,"depth":169,"text":32,"children":468},[469,470,471,472],{"id":39,"depth":184,"text":40},{"id":59,"depth":184,"text":60},{"id":82,"depth":184,"text":83},{"id":104,"depth":184,"text":105},{"id":130,"depth":169,"text":131},{"id":414,"depth":169,"text":415},{"id":440,"depth":169,"text":441},"Cloudflare Workers용 잡 관리 시스템 Tsumugi가 해결하는 문제, 사용자가 작성하는 코드, 전제 조건","md",{},"\u002Fko\u002Fguide\u002Foverview","2026-07-22",{"title":6,"description":476},"ko\u002Fguide\u002Foverview","2026-10-25","wLnIoTzih0YqXpLdJwMfB7ntYB5pUATHkkoBxS9tu64",false,1794027568505]