๐Ÿš€ Timeout Tamer: ์ธ๋‚ด๊ฐ€ ์ง„๋ณด๋ฅผ ๋งŒ๋‚ฌ์„ ๋•Œ

๋ฐœํ–‰: (2025๋…„ 12์›” 14์ผ ์˜คํ›„ 02:24 GMT+9)
5 min read
์›๋ฌธ: Dev.to

Source: Dev.to

๋‘๋ ค์šด ๊ต์ฐฉ ์ƒํƒœ

๊ทธ๋ฆฌ๋“œํ”ผ์•„์˜ ๋•…์—์„œ, ๋งˆ๋ฒ•์‚ฌ ๋ฃจ๋‚˜๋Š” ๊ฐ€์žฅ ์œ„๋Œ€ํ•œ ์ฃผ๋ฌธ์„ ์ค€๋น„ํ–ˆ์Šต๋‹ˆ๋‹ค โ€” 427๊ฐœ์˜ ๋งˆ๋ฒ• ๋ฐ์ดํ„ฐ ํ–‰์„ ํผ๋ธ”๋ฆฌ์‹œํ•˜๋Š” ๊ฒƒ. ๊ทธ๋…€๋Š” ๋งˆ๋ฒ• ์ง€ํŒก์ด๋ฅผ ํœ˜๋‘๋ฅด๋ฉฐ(โ€œPublishโ€ ํด๋ฆญ)โ€ฆ ๊ทธ๋ฆฌ๊ณ  TimeOut, ์„ฑ๋‚œ ๋ฌธ์ง€๊ธฐ๊ฐ€ ํฌํ„ธ์„ ๋‹ซ์•„๋ฒ„๋ ธ์Šต๋‹ˆ๋‹ค.

โ€œ๋‹น์‹ ์˜ ์ธ๋‚ด๊ฐ€ ๋งŒ๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.โ€

๊ทธ๋…€์˜ ์ฐฝ์กฐ๋ฌผ์ด ์‚ฌ๋ผ์ง„ ๊ฑธ๊นŒ์š”?

์•…๋‹น: TimeOut

TimeOut โ€“ 30์ดˆ ๊ทœ์น™์„ ์ง€ํ‚ค๋Š” ๋ฌด์ž๋น„ํ•œ ์ˆ˜ํ˜ธ์ž. โ€œ๋จธ๋ฌด๋ฅด์ง€ ๋งˆ๋ผ. ์›€์ง์ด๊ฑฐ๋‚˜ ์‚ฌ๋ผ์ ธ๋ผ.โ€

์˜์›…๋“ค์˜ ์ง‘ํ•ฉ

๋ฃจ๋‚˜๋Š” ์„ธ ๋ช…์˜ ์šฉ๊ฐํ•œ ์บ๋ฆญํ„ฐ๋ฅผ ๋ถ€๋ฆ…๋‹ˆ๋‹ค:

  • @TrackProgress (๋‚ด๋ ˆ์ดํ„ฐ) โ€“ ์ด ์—ฌ์ •์„ ์ด์•ผ๊ธฐํ•ด ์ฃผ๋Š” ๋งˆ๋ฒ• ์ฃผ์„.
  • Async (์‹ ์†ํ•œ ๋‹ฌ๋ฆฌ๊ธฐ) โ€“ ๋ฌด๊ฑฐ์šด ์ž‘์—…์„ ์˜คํ”„๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. ์ฐจ๋‹จ ์—†์Œ!
  • SSE (์†์‚ญ์ด๋Š” ์ž) โ€“ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์—…๋ฐ์ดํŠธ๋ฅผ ์ „ํŒŒํ•ฉ๋‹ˆ๋‹ค.

ํ€˜์ŠคํŠธ: ํƒ€์ž„์•„์›ƒ ๊ธธ๋“ค์ด๊ธฐ

Actโ€ฏ1: ๊ต์ฐฉ ์ƒํƒœ

  • ๋ฌธ์ œ: 427๊ฐœ์˜ ํ–‰์„ ๋™๊ธฐ์ ์œผ๋กœ ํผ๋ธ”๋ฆฌ์‹œํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์ด ๋„ˆ๋ฌด ์˜ค๋ž˜ ๊ฑธ๋ ธ์Šต๋‹ˆ๋‹ค. TimeOut์ด ์œผ๋ฅด๋ ๊ฑฐ๋ ธ์Šต๋‹ˆ๋‹ค.
  • ๊ฒฐ๊ณผ: ๋ฃจ๋‚˜๋Š” ๋ฉˆ์ถฐ ์„œ์„œ, ๋ถˆํ™•์‹คํ•˜๊ณ , ๊ต์ฐฉ ์ƒํƒœ์— ๋น ์กŒ์Šต๋‹ˆ๋‹ค.

Actโ€ฏ2: Async ๊ตฌ์กฐ

๋‹จ๊ณ„๋ณ„ ๊ธฐ์ˆ  ๊ฐ€์ด๋“œ

@TrackProgress๊ฐ€ ๋“ฑ์žฅ (AOP ๋งˆ๋ฒ•)

@TrackProgress("Unleashing the grid gods")
public void publishGrid(List rows) {
    String taskId = generateTaskId();
    progressService.startTask(taskId, rows.size());
    publisher.publishAsync(rows, taskId);
    return taskId;
}
  • @TrackProgress (๋‚ด๋ ˆ์ดํ„ฐ): ์„œ์‚ฌ์ ์ธ ์ž‘์—…์„ ํ‘œ์‹œํ•˜๊ณ  ์‹ฌ์žฅ๋ฐ•๋™์„ ๋ถ€์—ฌํ•ฉ๋‹ˆ๋‹ค.

Async๊ฐ€ ๋ถ€ํ•˜๋ฅผ ๋‹ด๋‹น (๋ณ‘๋ ฌ ์ž์œ )

new Thread(() -> {
    try {
        // Process in chunks. No rush!
        jp.proceed();
        completeTask(id);
    } catch (Exception e) {
        failTask(id, e);
    }
}).start();
  • Async (์‹ ์†ํ•œ ๋‹ฌ๋ฆฌ๊ธฐ): ์ž‘์—…์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋ฃจ๋‚˜๋Š” ๋‹ค์Œ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค:
{
  "taskId": "grid-427-groove",
  "message": "Your magic is brewing!",
  "track": "/pulse"
}

SSE๊ฐ€ ์—ฌ์ •์„ ์†์‚ญ์ž„ (์‹ค์‹œ๊ฐ„ ์—ฐ๊ฒฐ)

// As chunks complete:
progressService.updateProgress(taskId, 42, "Validating dreamsโ€ฆ");
  • SSE (์†์‚ญ์ด๋Š” ์ž): ๊ฐ ์‹ฌ์žฅ๋ฐ•๋™์„ ์ŠคํŠธ๋ฆฌ๋ฐํ•ฉ๋‹ˆ๋‹ค, ์˜ˆ:
Publishingโ€ฆ 42% [โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–’โ–’โ–’]

ํด๋ผ์ด๋งฅ์Šค: ์ง„ํ–‰์ด ์‹œ๊ฐ„์„ ์ •๋ณตํ•œ๋‹ค

  • TimeOut์ด ๋น„์›ƒ์œผ๋ ค ํ•ฉ๋‹ˆ๋‹ค.
  • @TrackProgress๊ฐ€ ์ด์ •ํ‘œ๋ฅผ ๋‚ด๋ ˆ์ด์…˜ํ•ฉ๋‹ˆ๋‹ค.
  • Async๊ฐ€ ๊ทธ๋ฆผ์ž ์†์—์„œ ์ž‘์—…ํ•ฉ๋‹ˆ๋‹ค.
  • SSE๊ฐ€ ๋ฃจ๋‚˜๋ฅผ ๊ณ„์† ๋ชฐ์ž…์‹œํ‚ต๋‹ˆ๋‹ค.

์ตœ์ข… ์™ธ์นจ:

Publishingโ€ฆ 100% [โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ] Published! โœจ

๋ฃจ๋‚˜๋Š” ๋ฏธ์†Œ ์ง“์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๋“œ๊ฐ€ ์‚ด์•„๋‚ฉ๋‹ˆ๋‹ค.

๋“ฑ์žฅ์ธ๋ฌผ ๋ฐ ๊ฐœ๋…

  • @TrackProgress (๋‚ด๋ ˆ์ดํ„ฐ) โ€“ ์ฃผ์„ + AOP; ๊ธด ์—ฌ์ •์„ ๋งฅ๋ฝํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • Async (์‹ ์†ํ•œ ๋‹ฌ๋ฆฌ๊ธฐ) โ€“ Java ์Šค๋ ˆ๋”ฉ, ๋…ผ๋ธ”๋กœํ‚น; ๋ฃจ๋‚˜๊ฐ€ ๋ฉˆ์ถ”์ง€ ์•Š๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • SSE (์†์‚ญ์ด๋Š” ์ž) / WebSocket (์ง€ํœ˜์ž) โ€“ Serverโ€‘Sent Events(๋˜๋Š” WebSocket)๋Š” ๋ฐฑ์—”๋“œ์˜ ๋ฐ•๋™์„ ํ”„๋ก ํŠธ์—”๋“œ ์‹ฌ์žฅ๋ฐ•๋™์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.
    ์ฐธ๊ณ : ์•„ํ‚คํ…์ฒ˜ ์ œ์•ฝ์— ๋งž๋Š” ๊ฒƒ์„ ์‚ฌ์šฉํ•˜์„ธ์š”.
  • TimeOut (์•…๋‹น) โ€“ AWS API Gateway ํƒ€์ž„์•„์›ƒ ์ œ์•ฝ; ํ˜์‹ ์„ ์ด‰๋ฐœํ•˜๋Š” ์›์ธ.

์š”์•ฝ

  • ๋ฌธ์ œ: ๋™๊ธฐ์‹ ๋ฌด๊ฑฐ์šด ์ž‘์—…์ด ๋ฒฝ์— ๋ถ€๋”ชํ˜”์Šต๋‹ˆ๋‹ค.
  • ํ•ด๊ฒฐ์ฑ…: @TrackProgress์™€ Async๋ฅผ ๊ฒฐํ•ฉํ•ด ๋Œ€๊ธฐ๋ฅผ ์ง„ํ–‰ ํŒŒํ‹ฐ๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฒฐ๊ณผ: ์–ด๋‘  ๋Œ€์‹  ๊ตํ–ฅ๊ณก์ด ์šธ๋ฆฝ๋‹ˆ๋‹ค.

Timeout Tamerโ€™s Creed (ํƒ€์ž„์•„์›ƒ ๊ธธ๋“ค์ด๋Š” ์‹ ์กฐ)

์ œ์•ฝ์€ ์„ ๋ฌผ์„ ์ˆจ๊น๋‹ˆ๋‹ค. ์ธ๋‚ด๊ฐ€ ์‹œํ—˜๋  ๋•Œ:

  • ๋…ธ๋ ฅ์„ ๋‚ด๋ ˆ์ด์…˜ํ•œ๋‹ค.
  • ํ”„๋กœ์„ธ์Šค๋ฅผ ํ•ด๋ฐฉํ•œ๋‹ค.
  • ์‹ค์‹œ๊ฐ„์œผ๋กœ ์†์‚ญ์ž„์„ ์ „ํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  TimeOut? ์กด๊ฒฝ์˜ ๊ณ ๊ฐœ๋ฅผ ๋„๋•์ž…๋‹ˆ๋‹ค.

๊ตํ›ˆ

๋‹ค์Œ์— ์ œํ•œ์ด ๋‹ค๊ฐ€์˜ฌ ๋•Œ, ์‚ผ์ธ์กฐ๋ฅผ ๋ฐฐ์น˜ํ•˜์„ธ์š”:

@TrackProgressโ€ฏโž•โ€ฏAsyncโ€ฏโž•โ€ฏSSE/WebSocket

์ด ์ƒํ™ฉ์„ ์—ฌ๋Ÿฌ๋ถ„์€ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ–ˆ์„๊นŒ์š”?

Back to Blog

๊ด€๋ จ ๊ธ€

๋” ๋ณด๊ธฐ ยป

React ์„ฑ๋Šฅ ์•ˆํ‹ฐํŒจํ„ด: ์•ฑ ์†๋„๋ฅผ ์ฃฝ์ด๋Š” 5๊ฐ€์ง€ ์‹ค์ˆ˜!

React ์•ฑ์ด ๋А๋ฆฌ๊ฒŒ ๋™์ž‘ํ•˜์ง€๋งŒ ์›์ธ์„ ์ •ํ™•ํžˆ ์ฐพ์„ ์ˆ˜ ์—†๋‚˜์š”? ์ตœ์„ ์˜ ๋ฐฉ๋ฒ•(best practices)์„ ๋”ฐ๋ฅด๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ์ผ๋ถ€ ์ผ๋ฐ˜์ ์ธ ๊ฐœ๋ฐœ ํŒจํ„ดโ€ฆ

์™œ 'Just Coding'๋งŒ์œผ๋กœ๋Š” ๋” ์ด์ƒ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์€๊ฐ€ โ€“ ํ˜„๋Œ€ ๊ฐœ๋ฐœ์ž ์Šคํƒ์˜ ํ•ด๋ถ€

์ €๋Š” ๊ณ ๊ฐ๊ณผ ํ›„๋ฐฐ ๋™๋ฃŒ๋“ค ๋ชจ๋‘์—๊ฒŒ ์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค: โ€œZsolt, ์™œ ์ด๋ ‡๊ฒŒ ๋งŽ์€ ๊ฒƒ์ด ํ•„์š”ํ•ฉ๋‹ˆ๊นŒ? ์™œ ์ด์ œ๋Š” ๋‹จ์ผ server์™€ HTML ํŒŒ์ผ๋งŒ์œผ๋กœ๋Š” ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์€๊ฐ€์š”?โ€ ๋‹ต์€โ€ฆ

CSS Z-Index ์„ค๋ช…: ์Šคํƒœํ‚น ํ˜ผ๋ž€์„ ๋ฉˆ์ถ”๊ณ  ๋ ˆ์ด์–ด๋ฅผ ํ”„๋กœ์ฒ˜๋Ÿผ ๊ด€๋ฆฌํ•˜๊ธฐ

CSS z-index๋ฅผ ํŒŒํ—ค์น˜๋‹ค: ์›น ๋ ˆ์ด์–ด๋ฅผ ์ œ์–ดํ•˜๋Š” ๋น„๋ฐ€ ํ—ค๋” ์œ„์— dropdown menu๋ฅผ ๋†“์•„๋ณด๋ ค ํ–ˆ๊ฑฐ๋‚˜, modal๊ณผ ์‹ธ์›Œ๋ณธ ์ ์ด ์žˆ๋‹ค๋ฉดโ€ฆ

CSS Max-Width ์„ค๋ช…: ๋ ˆ์ด์•„์›ƒ ๊นจ์ง€๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜์„ธ์š”

๋ ˆ์ด์•„์›ƒ์„ ๊นจ์ง€ ๋งˆ์„ธ์š”! CSS max-width ์™„์ „ ๊ฐ€์ด๋“œ ๋…ธํŠธ๋ถ์—์„œ ์™„๋ฒฝํ•˜๊ฒŒ ๋ณด์ด๋Š” ๋””์ž์ธ์— ๋ช‡ ์‹œ๊ฐ„์„ ํˆฌ์žํ–ˆ์ง€๋งŒ, ํ™”๋ฉด์ด ๋Š˜์–ด๋‚˜๋Š” ๊ฒƒ์„ ๋ณธ ์ ์ด ์žˆ๋‹ค๋ฉดโ€ฆ