Knex: Knex๋ฅผ AWS Lambda์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒํ•ด์•ผํ•ฉ๋‹ˆ๊นŒ?

์— ๋งŒ๋“  2017๋…„ 01์›” 20์ผ  ยท  34์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: knex/knex

์ผ๋ถ€ ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š” ๋™์•ˆ ์—ฐ๊ฒฐ ํ’€๋ง์— ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด ๋žŒ๋‹ค ํ•จ์ˆ˜๊ฐ€ ๋ช‡ ์ดˆ์— ๊ฑธ์ณ ์ˆ˜์ฒœ ๋ฒˆ ํ˜ธ์ถœ ๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•˜๊ณ  ๋‚ด db์— ์—ฐ๊ฒฐํ•˜๋Š” ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์„ ์ฐพ๋Š” ๋ฐ ์–ด๋ ค์›€์„ ๊ฒช๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ๋…ธ๋“œ / ํฌ์ŠคํŠธ๊ทธ๋ ˆ์Šค์— ๋Œ€ํ•œ ๋งค์šฐ ์œ ์‚ฌํ•œ ๋ฌธ์ œ ์ž…๋‹ˆ๋‹ค. ๋ณธ์งˆ์ ์œผ๋กœ ๋ฌธ์ œ๋Š” ํ’€์ด์žˆ๋Š” ๊ฒฝ์šฐ ํ’€์—์„œ ์—ฐ๊ฒฐ์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์–ด์•ผํ•˜์ง€๋งŒ AWS๊ฐ€ (๋ถˆ์•ˆ์ •ํ•˜๊ฒŒ) ์žฌ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹ ๋•Œ๋ฌธ์— ๊ธฐ์กด ํ’€์— ์˜์กด ํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋žŒ๋‹ค ์ปจํ…Œ์ด๋„ˆ.

๊ธฐ๋ณธ์ ์œผ๋กœ ๋‚ด๊ฐ€ ์ฐพ๊ณ ์žˆ๋Š” ๊ฒƒ์€ ๋‚ด db์— ๋Œ€ํ•œ ์—ฐ๊ฒฐ์„ ์•ˆ์ •์ ์œผ๋กœ ์–ป๊ฑฐ๋‚˜ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. while(!availableConnections) { tryToGetConnection() } ์™€ ๊ฐ™์€ ์˜ˆ์ œ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. node-pool ์™€ (๊ณผ) ์ƒํ˜ธ ์ž‘์šฉํ•ด์•ผํ•ฉ๋‹ˆ๊นŒ? Knex์—์„œ ์–ด๋–ป๊ฒŒ ์ˆ˜ํ–‰ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

insightful question

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

๋ฌธ์žฅ ์ค‘๊ฐ„์— ์ €์งˆ๋Ÿฌ์„œ ๋ฏธ์•ˆ ํ•ด์š”, ์ œ ์•„์ด๊ฐ€ ๋ฐฉ๊ธˆ 3 ๋ฆฌํ„ฐ์˜ ๋ฌผ์„ ๋ฐ”๋‹ฅ์— ๋˜์กŒ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  34 ๋Œ“๊ธ€

Knex ํ’€๋ง์€ ๋™์ผํ•œ ๋…ธ๋“œ ํ”„๋กœ์„ธ์Šค์—์„œ ์—ฐ๊ฒฐ์ด ์ด๋ฃจ์–ด์ง„ ๊ฒฝ์šฐ์—๋งŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

AWS ๋žŒ๋‹ค ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ณต์œ  ๋…ธ๋“œ ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ๊ฐ ๋žŒ๋‹ค ์ธ์Šคํ„ด์Šค์—์„œ ์ตœ์†Œ / ์ตœ๋Œ€ ์—ฐ๊ฒฐ์ด 1 ์ธ ์ƒˆ ํ’€์„ ์ƒ์„ฑํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ˆ˜๋ฐฑ ๊ฐœ์˜ ๋™์‹œ ์—ฐ๊ฒฐ์„ ํ—ˆ์šฉ ํ•  ์ˆ˜์žˆ๋Š” ์ถฉ๋ถ„ํ•œ ์„ค์ •์ด ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ผ๋ฉด๋ฉ๋‹ˆ๋‹ค (RDS์—์„œ๋Š” ์ธ์Šคํ„ด์Šค ํฌ๊ธฐ์— ๋”ฐ๋ผ ๋‹ค๋ฆ„). ).

์ด https://forums.aws.amazon.com/thread.jspa?threadID=216000์„ ์ฝ์€ ํ›„

๋žŒ๋‹ค๊ฐ€ ์‹ค์ œ๋กœ ์ผ๋ถ€ ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋ฏ€๋กœ ์ƒ์„ฑ ๋œ ๋žŒ๋‹ค ์ปจํ…Œ์ด๋„ˆ์˜ ์ตœ๋Œ€ ๊ฐœ์ˆ˜๋ฅผ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ตœ์ ์˜ ํ’€ ํฌ๊ธฐ๋ฅผ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค (๊ฐ ์ปจํ…Œ์ด๋„ˆ์—๋Š” ๋ณ„๋„์˜ ํ’€์ด ์žˆ์œผ๋ฏ€๋กœ DB์— ๋Œ€ํ•œ ์ด ์—ฐ๊ฒฐ์€ ํ’€์ž…๋‹ˆ๋‹ค. ์ตœ๋Œ€ * ์ตœ๋Œ€ ์ปจํ…Œ์ด๋„ˆ ์ˆ˜).

์–ด์จŒ๋“  ํ’€์—์„œ ์ˆ˜๋™ ์—ฐ๊ฒฐ ์š”์ฒญ์„ ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. knex๋Š” ์ž๋™์œผ๋กœ ์—ฐ๊ฒฐ์„ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฒƒ์ด ๋ช‡ ์ดˆ ์•ˆ์— ์™„๋ฃŒ๋˜๋ฉด ๊ทธ ์‹œ๊ฐ„์— ์–ด๋–ค ํƒ€์ž„ ์•„์›ƒ๋„ ํŠธ๋ฆฌ๊ฑฐ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ตœ๋Œ€ ์—ฐ๊ฒฐ ์ˆ˜์— ๋„๋‹ฌํ–ˆ๋‹ค๋Š” ์˜ค๋ฅ˜๊ฐ€ DB์—์„œ ๋ฐœ์ƒํ•˜๋ฉด ์ตœ๋Œ€ ํ’€ ํฌ๊ธฐ๋ฅผ ์ž‘๊ฒŒ ๋งŒ๋“ค์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋ฌธ์žฅ ์ค‘๊ฐ„์— ์ €์งˆ๋Ÿฌ์„œ ๋ฏธ์•ˆ ํ•ด์š”, ์ œ ์•„์ด๊ฐ€ ๋ฐฉ๊ธˆ 3 ๋ฆฌํ„ฐ์˜ ๋ฌผ์„ ๋ฐ”๋‹ฅ์— ๋˜์กŒ์Šต๋‹ˆ๋‹ค.

๋‹ต์žฅ์„ ๋ณด๋‚ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์—ฐ๊ฒฐ ์ˆ˜๊ฐ€ ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ ํฌ๊ฒŒ ๋‹ฌ๋ผ ์ง€๋”๋ผ๋„ ์ตœ๋Œ€ ํ’€ ํฌ๊ธฐ๋ฅผ ์ถ”์ •ํ•˜๋Š” ๊ฒƒ์ด ์ตœ์„ ์˜ ๋ฐฉ๋ฒ• ์ธ ๊ฒƒ์ฒ˜๋Ÿผ ๋“ค๋ฆฝ๋‹ˆ๋‹ค.

๋ช…ํ™•ํ•˜๊ฒŒ ๋งํ•˜๋ฉด Knex์—์„œ ์—ฐ๊ฒฐ์„ ๋Š์„ ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค. ๋งž์Šต๋‹ˆ๊นŒ? ์—ฐ๊ฒฐ ํ’€์„ ํŒŒ๊ดดํ•˜๋Š” ๊ธฐ๋Šฅ ๋งŒ ์žˆ์Šต๋‹ˆ๊นŒ? Lambda๊ฐ€ ๋•Œ๋•Œ๋กœ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์žฌ์‚ฌ์šฉํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์€ ๋ชจ๋“  ๊ฒƒ์„ ํฌ๊ธฐํ•ฉ๋‹ˆ๋‹ค.

์ปค๋„ฅ์…˜ ํ’€์„ ํŒŒ๊ดดํ•˜๋ฉด ๋ชจ๋“  ์ปค๋„ฅ์…˜๋„ ํŒŒ๊ดด๋˜๊ณ  (๋จผ์ € ์™„๋ฃŒ๋˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค) ๋žŒ๋‹ค๊ฐ€ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํŒŒ๊ดด ํ•  ๋•Œ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ข…๋ฃŒ ๋  ๋•Œ ์—ด๋ ค์žˆ๋Š” ๋ชจ๋“  TCP ์†Œ์ผ“์ด ๋ฌต์‹œ์ ์œผ๋กœ ๋‹ซํž ๊ฒƒ์ด๋ผ๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.

ํ’€๋ง์˜ ์ด์ ์„ ํŒŒ๊ดดํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ ์š”์ฒญ ํ›„์— ๋ช…์‹œ ์ ์œผ๋กœ ์—ฐ๊ฒฐ์„ ๋‹ซ์•„์•ผํ•˜๋Š” ์ด์œ ๋ฅผ ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํฌ๊ธฐ๊ฐ€ 1 ์ธ ํ’€์„ ๋งŒ๋“ค๊ณ  ๋‚˜์ค‘์— ์ œ๊ฑฐํ•˜๋ฉด ๋™์ผํ•œ ํšจ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ํ’€์—์„œ ์ž‘์—…์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ ์—ฐ๊ฒฐ์„ ๋‹ซ๋Š” ํ’€์— ๋Œ€ํ•œ ์œ ํœด ์‹œ๊ฐ„ ์ œํ•œ์„ ๊ตฌ์„ฑ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

Knex๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ COPY ์ฟผ๋ฆฌ๋ฅผ RedShift ํด๋Ÿฌ์Šคํ„ฐ์— ๋ณด๋‚ด๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

pg Pool๋กœ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉด Lambda ํ•จ์ˆ˜ ๋์— ๋„๋‹ฌํ•˜๋Š” ์ฆ‰์‹œ ์ฟผ๋ฆฌ๊ฐ€ ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.

@BardiaAfshin ๋žŒ๋‹ค ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ํŒŒ๊ดด๋˜๊ณ  ๋žŒ๋‹ค ํ•จ์ˆ˜ ๋์— ๋„๋‹ฌํ–ˆ์„ ๋•Œ ๋ชจ๋“  ์†Œ์ผ“์ด ํ•ด์ œ๋˜๋ฉด์ด ๊ฒฝ์šฐ db ์ฟผ๋ฆฌ๋„ ์ข…๋ฃŒ๋˜๊ณ  ์™„๋ฃŒ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ ๊ฐ’์„ ์ฝ๊ธฐ ์ „์— ์•”์‹œ ์  ํŠธ๋žœ์žญ์…˜์ด ์™„๋ฃŒ๋˜์ง€ ์•Š์•„ COPY ์ฟผ๋ฆฌ๊ฐ€ ๋กค๋ฐฑ๋˜๋Š” ๊ฒฝ์šฐ postgresql์ด ํด๋ผ์ด์–ธํŠธ ์ธก ์—ฐ๊ฒฐ ์ข…๋ฃŒ์— ์–ด๋–ป๊ฒŒ ๋ฐ˜์‘ํ•˜๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์–ด์จŒ๋“  ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋Š” knex์— ๋‹ฌ๋ ค ์žˆ์ง€ ์•Š์ง€๋งŒ aws lambda ๋ฐ postgresql์˜ ์ž‘๋™ ๋ฐฉ์‹์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

๋‚ด ๊ด€์ฐฐ์€ ์ฟผ๋ฆฌ๊ฐ€ RedShift์—์„œ ์ข…๋ฃŒ๋˜๊ณ  ๋กค๋ฐฑ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์–ด์จŒ๋“  ํ’€์—์„œ ์ˆ˜๋™ ์—ฐ๊ฒฐ ์š”์ฒญ์„ ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. knex๋Š” ์ž๋™์œผ๋กœ ์—ฐ๊ฒฐ์„ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฒƒ์ด ๋ช‡ ์ดˆ ์•ˆ์— ์™„๋ฃŒ๋˜๋ฉด ๊ทธ ์‹œ๊ฐ„์— ์–ด๋–ค ํƒ€์ž„ ์•„์›ƒ๋„ ํŠธ๋ฆฌ๊ฑฐ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

db๋กœ๋“œ ํ…Œ์ŠคํŠธ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ ์ค‘์ด๋ฉฐ ์ด๊ฒƒ์ด ์‚ฌ์‹ค์ด ์•„๋‹Œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋™์‹œ ์—ฐ๊ฒฐ ์ˆ˜๊ฐ€ 30 ๊ฐœ ์ด์ƒ์ด๋ฉด ์—ฐ๊ฒฐ์ด ์—ด๋ ค์žˆ์„ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ์ฆ‰์‹œ ์‹œ๊ฐ„์ด ์ดˆ๊ณผ๋ฉ๋‹ˆ๋‹ค.

์ฟผ๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ํ˜„์žฌ ์‚ฌ์šฉ์ค‘์ธ ์—ฐ๊ฒฐ์„ ์ˆ˜๋™์œผ๋กœ ํ•ด์ œํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ?

AWS Lambda์— ๋ฐฉ๊ธˆ ๋“ค์–ด๊ฐ„ ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•ด ๊ณต์œ  ํ•  ์ˆ˜์žˆ๋Š” ์˜ˆ์ œ ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? ๋ˆ„๊ตฐ๊ฐ€ knex / postgres / lambda์˜ ํŒจํ„ด ๋ฐ / ๋˜๋Š” ์•ˆํ‹ฐ ํŒจํ„ด์„ ๊ณต์œ  ํ•  ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ์ง€๊ธˆ ์‚ฌ์šฉํ•˜๊ณ ์žˆ๋Š” ๊ฒƒ์€ ์ด๊ฒƒ์ด ๊ฐœ์„  ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ™•์‹ ํ•˜์ง€๋งŒ, ๋‚ด๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ๊ธธ์„ ๊ฐ€๊ณ  ์žˆ๋Š”์ง€์— ๋Œ€ํ•ด ์•ฝ๊ฐ„์˜ ์˜นํ˜ธ๋ฅผ ๊ธฐ๋Œ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

'use strict';
var pg = require('pg');

function initKnex(){
  return require('knex')({
      client: 'pg',
      connection: { ...details... }
  });
}

module.exports.hello = (event, context) =>
{
  var knex = initKnex();

  // Should I be returning knex here or in the final catch?
  knex
  .select('*')
  .from('my_table')
  .then(function (rows) {
    context.succeed('Succeeded: ' + JSON.stringify(rows || []));
  })
  .catch(function (error) {
    context.fail(error);
  })
  .then(function(){
    // is destroy overkill? - is there an option for knex.client.release, etc?
    knex.destroy();
  })
}

๋‚˜๋Š” ๊ฐ™์€ ๋ฐฐ๋ฅผ ํƒ€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŽ์€ ์ธํ„ฐ๋„ท ๊ฒ€์ƒ‰๊ณผ ํ…Œ์ŠคํŠธ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์ด ๋ฌด์—‡์ธ์ง€ ์•„์ง ํŒŒ์•…ํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์ง€๊ธˆํ•˜๊ณ ์žˆ๋Š” ๊ฒƒ์€ :

const dbConfig = require('./db');
const knex = require('knex')(dbConfig);

exports.handler = function (event, context, callback) {
...
connection = {..., pool: { min: 1, max: 1 },

์ด๋ ‡๊ฒŒํ•˜๋ฉด ์—ฐ๊ฒฐ (์ปจํ…Œ์ด๋„ˆ ๋‹น ์ตœ๋Œ€ 1 ๊ฐœ)์ด ์œ ์ง€๋˜์–ด ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‰ฝ๊ฒŒ ์žฌ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ฒฐ๊ตญ ๋‚ด ์—ฐ๊ฒฐ์„ ํŒŒ๊ดดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

http://blog.rowanudell.com/database-connections-in-lambda/

์ด๊ฒƒ์ด ์ตœ์„ ์˜ ๋ฐฉ๋ฒ•์ธ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์ง€๋งŒ ์ง€๊ธˆ๊นŒ์ง€ ๋‚˜๋ฅผ ์œ„ํ•ด ์ผํ–ˆ์Šต๋‹ˆ๋‹ค.

/์–ด๊นจ๋ฅผ ์œผ์“ฑํ•˜๋‹ค

์•ˆ๋…•ํ•˜์„ธ์š”.

const knex ๊ฐ€ ์„ ์–ธ๋˜๋ฉด ์–ด๋–ค ์ผ์ด ๋ฐœ์ƒํ•˜๋Š”์ง€ ์ •ํ™•ํžˆ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ดˆ๊ธฐ ์—ฐ๊ฒฐ์ด ์„ค์ •๋˜๋Š” ๊ณณ์ž…๋‹ˆ๊นŒ? ๋ˆ„๊ตฐ๊ฐ€ ๋ช…ํ™•ํžˆ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? (dbConfig์— ์—ฐ๊ฒฐ ์ •๋ณด๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค)

์ฝ”๋“œ์—์„œ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ํ˜ธ์ถœ ๋  ๋•Œ๋งˆ๋‹ค ์—ฐ๊ฒฐ ์ž์ฒด๋ฅผ ๋ฎ์–ด ์“ฐ๊ณ  ๋‹ค์‹œ ์ƒ์„ฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๊นŒ?

๊ฐ™์€ ๋ณดํŠธ์—์žˆ๋Š” ์‚ฌ๋žŒ์—๊ฒŒ ์ฐจ๋ฅผ ์น˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ์ปจํ…Œ์ด๋„ˆ์™€ ์ˆ˜์˜์žฅ ํฌ๊ธฐ๋ฅผ ์•Œ์•„ ๋‚ด๋ ค๊ณ  ๋…ธ๋ ฅํ•œ ์ ์€ ์šด์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค ( @elhigu ์˜ ์ œ์•ˆ์— ๋”ฐ๋ผ). ๋‚ด ํ•ด๊ฒฐ์ฑ…์€ ๋ชจ๋“  ์—ฐ๊ฒฐ ํ›„ ํ’€์„ ํŒŒ๊ดดํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค (์ตœ์ ์ด ์•„๋‹ˆ๋ผ๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค ๐Ÿ˜’).

const knex = require('knex');

const client = knex(dbConfig);

client(tableName).select('*')
  .then((result) => { 

    return Promise.all([
      result,
      client.destroy(),
    ])  
  })
  .then(([ result ]) => {

    return result;
  });

์š”์•ฝ : ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•˜๊ธฐ ์ „์— context.callbackWaitsForEmptyEventLoop = false ํ•˜๊ธฐ ๋งŒํ•˜๋ฉด๋ฉ๋‹ˆ๋‹ค.

AWS Lambda๋Š” ๋นˆ ์ด๋ฒคํŠธ ๋ฃจํ”„๋ฅผ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค (๊ธฐ๋ณธ์ ์œผ๋กœ). ๊ทธ๋ž˜์„œ ํ•จ์ˆ˜๋Š” ์ฝœ๋ฐฑ์ด ์‹คํ–‰ ๋˜์–ด๋„ Timeout ์˜ค๋ฅ˜๋ฅผ ๋˜์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜ ๋งํฌ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
https://github.com/apex/apex/commit/1fe6e91a46e76c2d5c77877be9ce0c206e9ef9fb

@elhigu @tgriesser์—๊ฒŒ : ์ด๊ฒƒ์€ knex ๋ฌธ์ œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํ™•์‹คํžˆ ๋žŒ๋‹ค ํ™˜๊ฒฝ์˜ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ์งˆ๋ฌธ์— ํƒœ๊ทธํ•˜๊ณ  ์ข…๋ฃŒํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. :)

@mooyoul yep, ํ™•์‹คํžˆ knex ๋ฌธ์ œ๋Š” ์•„๋‹ˆ์ง€๋งŒ ๋ฌธ์„œ ๋ฌธ์ œ ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋งํฌ๋ฅผ ๋ด์ฃผ์„ธ์š”
https://stackoverflow.com/questions/49347210/why-aws-lambda-keeps-timing-out-when-using-knex-js
db ์—ฐ๊ฒฐ์„ ๋‹ซ์•„์•ผํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด Lambda๊ฐ€ ์‹œ๊ฐ„ ์ดˆ๊ณผ ๋  ๋•Œ๊นŒ์ง€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

Lambda์™€ ํ•จ๊ป˜ Knex๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ ์™„๋ฒฝํ•˜๊ฒŒ ์ž‘๋™ํ•˜๋Š” ํŒจํ„ด์„ ์ฐพ์€ ์‚ฌ๋žŒ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์—ฐ๊ฒฐ์„ ์ œ๋Œ€๋กœ ์ข…๋ฃŒํ•˜๋ฉด ๋‹ค๋ฅธ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

ํ”ผํ•˜๋ ค๋Š” ๊ฒƒ์€ ์—ฐ๊ฒฐ์„ ๋‹ซ๊ณ  Lambda ํ˜ธ์ถœ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Lambda๊ฐ€ ํ˜ธ์ถœ๊ฐ„์— ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋„๋ก ํ—ˆ์šฉํ•ฉ๋‹ˆ๊นŒ?

๋‚˜๋Š” ์ด๋ ‡๊ฒŒ ์Šค๋ ˆ๋“œ๋ฅผ ๋ถ€๋”ชํžˆ๋Š” ๊ฒƒ์„ ์ข‹์•„ํ•˜์ง€ ์•Š์ง€๋งŒ ๋Œ“๊ธ€์—์„œ ๊ธธ์„ ์žƒ๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์œ„ ์˜ @mooyoul ์˜ ๋‹ต๋ณ€ ์ด ์šฐ๋ฆฌ์—๊ฒŒ ํฐ ๋„์›€์ด๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๋‹น ํ”Œ๋ž˜๊ทธ๋ฅผ false๋กœ ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด Lambda๋Š” ์ด๋ฒคํŠธ ๋ฃจํ”„๊ฐ€ ๋น„์–ด์žˆ์„ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒํ•˜๋ฉด ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•˜์ž๋งˆ์ž ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰์„ ์™„๋ฃŒํ•ฉ๋‹ˆ๋‹ค.

์ €์—๊ฒŒ๋Š” ๋กœ์ปฌ ์ปดํ“จํ„ฐ์—์„œ ์ž‘๋™ํ–ˆ์ง€๋งŒ ๋ฐฐํฌ ํ›„์—๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์˜คํ•ด๋ฅผ ๋ฐ›์•˜๋‹ค.

RDS ์ธ๋ฐ”์šด๋“œ ์†Œ์Šค๊ฐ€ Lambda ํ•จ์ˆ˜์— ์—ด๋ ค ์žˆ์ง€ ์•Š์€ ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค. Stack Overflow ์—์„œ ์†”๋ฃจ์…˜์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค. RDS ์ธ๋ฐ”์šด๋“œ ์†Œ์Šค๋ฅผ 0.0.0.0/0 ํ•˜๊ฑฐ๋‚˜ VPC๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

RDS ์ธ๋ฐ”์šด๋“œ ์†Œ์Šค๋ฅผ ์—…๋ฐ์ดํŠธ ํ•œ ํ›„ Knex๋กœ Lambda๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ์‚ฌ์šฉ์ค‘์ธ Lambda ๋Ÿฐํƒ€์ž„์€ ํŒจํ‚ค์ง€์™€ ํ•จ๊ป˜ Node.js 8.10 ์ž…๋‹ˆ๋‹ค.

knex: 0.17.0
pg: 7.11.0

๋น„๋™๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์•„๋ž˜ ์ฝ”๋“œ๋„ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

const Knex = require('knex');

const pg = Knex({ ... });

module.exports. submitForm = async (event) => {
  const {
    fields,
  } = event['body-json'] || {};

  return pg('surveys')
    .insert(fields)
    .then(() => {
      return {
        status: 200
      };
    })
    .catch(err => {
      return {
        status: 500
      };
    });
};

์•ž์œผ๋กœ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ๋งŒ๋‚  ์ˆ˜์žˆ๋Š” ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ๋„์›€์ด๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์‚ฌ๋žŒ๋“ค์˜ ๊ด€์‹ฌ์„ ๋Œ๊ณ  ์‹ถ์€ ๊ฒƒ์€ serverless-mysql ํŒจํ‚ค์ง€๋กœ, ํ‘œ์ค€ mysql ๋“œ๋ผ์ด๋ฒ„๋ฅผ ๋ž˜ํ•‘ํ•˜์ง€๋งŒ์ด ์Šค๋ ˆ๋“œ์—์„œ ์„ค๋ช…ํ•˜๋Š” ์—ฐ๊ฒฐ ํ’€ ๊ด€๋ฆฌ์™€ ๊ด€๋ จ๋œ ๋งŽ์€ ๋žŒ๋‹ค ๊ด€๋ จ ๋ฌธ์ œ์ ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๋‹ค๋ฅธ ๋“œ๋ผ์ด๋ฒ„๋กœ ๋ฐ”๊ฟ€ ์ˆ˜์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— Knex๊ฐ€ ํ˜„์žฌ serverless-mysql ์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค ( ์ด ๋ฌธ์ œ ์— ๋”ฐ๋ผ). serverless-mysql ๋Š” ์ฝœ๋ฐฑ ๋Œ€์‹  promise๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋น„ ํ˜ธํ™˜์„ฑ์ด์žˆ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ ์•„๋งˆ๋„ Knex์— ์ƒˆ๋กœ์šด ํด๋ผ์ด์–ธํŠธ ๊ตฌํ˜„์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๊ธฐํšŒ๋ฅผ ์ฃผ์‹œ๋ฉด ๊ธฐ๊บผ์ดํ•˜์ง€๋งŒ Knex์— ๋” ์ต์ˆ™ํ•œ ์‚ฌ๋žŒ์ด ํ•ฉ๋ฆฌ์ ์ด๊ณ  ์‹คํ–‰ ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š”์ง€ ๋งํ•ด ์ฃผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๊นŒ?

๋˜ํ•œ ๊ทธ๋™์•ˆ Knex๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ MySQL ์ฟผ๋ฆฌ๋ฅผ _build_ํ•˜์ง€๋งŒ _not execute_ ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ toSQL() ํ˜ธ์ถœํ•˜๊ณ  ์ถœ๋ ฅ์„ serverless-mysql ํ•˜์—ฌ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

DB ์—ฐ๊ฒฐ์—†์ด Knex๋ฅผ ๊ตฌ์„ฑ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ ์—ฐ๊ฒฐ์„ ์—ฌ๋Š” ๊ฒƒ์€ ์˜๋ฏธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์ด ์ž‘๋™ํ•ฉ๋‹ˆ๊นŒ?

connection = {..., pool: { min: 0, max: 0 ) },

@disbelief ์—ฐ๊ฒฐ์—†์ด knex๋ฅผ ์ดˆ๊ธฐํ™” ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. https://knexjs.org/#Installation -client ๋ฌธ์„œ์—์ด ์„น์…˜์˜ ๋ ๋ถ€๋ถ„์—์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์˜ˆ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

const knex = require('knex')({client: 'mysql'});

const generatedQuery = knex('table').where('id',1).toSQL().toNative();

@elhigu ์•„ ๋ฉ‹์ง€๋‹ค, ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์ค‘๊ฐ„์— ๊ธฐํšŒ๋ฅผ ์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์—…๋ฐ์ดํŠธ : ์œ„์˜ ๋‚ด์šฉ์ด ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. Knex๋Š” ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ์œ„ํ•œ ํ˜ธ์ถœ์ด์—†๋Š” ๊ฒฝ์šฐ์—๋„ db ์—ฐ๊ฒฐ์„ ์„ค์ •ํ•  ์ˆ˜์—†๋Š” ๊ฒฝ์šฐ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

@disbelief ๊ทธ์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์„ ์ฐพ์•˜์Šต๋‹ˆ๊นŒ?

@fdecampredon ์•„๋‹ˆ์—์š” . ์ง€๊ธˆ์€ ๋‹จ์ˆœํžˆ squel์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ toString() ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  serverless-mysql ํด๋ผ์ด์–ธํŠธ์— ์ „๋‹ฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

@disbelief db ์—ฐ๊ฒฐ์—†์ด ์ฟผ๋ฆฌ ์ž‘์„ฑ์ด ์‹คํŒจํ•œ ๊ฒƒ์ด ๋†€๋ž์Šต๋‹ˆ๋‹ค.

knex ํด๋ผ์ด์–ธํŠธ๋ฅผ ๋นŒ๋“œํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ์ค‘์ธ ์ฝ”๋“œ ๋ฐ ๊ตฌ์„ฑ์„ ๊ฒŒ์‹œ ํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ๋˜ํ•œ knex ๋ฒ„์ „.

๋‹ค์Œ์€ ๋‚˜๋ฅผ ์œ„ํ•ด ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.
๋…ธ๋“œ v8.11.1
mysql : 2.13.0
knex : 0.18.3

const k = require('knex')

const client = k({ client: 'mysql' })

console.log('Knex version:', require('knex/package.json').version)
// => 0.18.3
console.log('sql:', client('table').where('id',1).toSQL().toNative())
// => { sql: 'select * from `table` where `id` = ?', bindings: [ 1 ] }

Lambda์˜ ๋ฆฌ์†Œ์Šค ์žฌํ™œ์šฉ ์ž‘๋™ ๋ฐฉ์‹์— ๋”ฐ๋ผ Knex ์ธ์Šคํ„ด์Šค๋Š” ํ•ญ์ƒ ํ•จ์ˆ˜ ๋˜๋Š” ํด๋ž˜์Šค ์™ธ๋ถ€์— ์œ ์ง€๋˜์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ํ’€์— ์ตœ๋Œ€ 1 ๊ฐœ๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ :

const Knex = require('knex');
let instance = null;

module.exports = class DatabaseManager {
  constructor({ host, user, password, database, port = 3306, client = 'mysql', pool = { min: 1, max: 1 }}) {
    this._client = client;
    this._poolOptions = pool;
    this._connectionOptions = {
      host: DB_HOST || host,
      port: DB_PORT || port,
      user: DB_USER || user,
      password: DB_PASSWORD || password,
      database: DB_NAME || database,
    };
  }

  init() {
    if (instance !== null) {
      return;
    }

    instance = Knex({
      client: this._client,
      pool: this._poolOptions,
      connection: this._connectionOptions,
      debug: process.env.DEBUG_DB == true,
      asyncStackTraces: process.env.DEBUG_DB == true,
    });
  }

  get instance() {
    return instance;
  }
}

์ด๋Ÿฌํ•œ ๋ฐฉ์‹์œผ๋กœ Lambda์˜ ์žฌํ™œ์šฉ์„ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ํ™œ์„ฑํ™” ๋œ (๊ณ ์ • ๋œ) ๊ฐ ์ปจํ…Œ์ด๋„ˆ๋Š” ๋™์ผํ•œ ์—ฐ๊ฒฐ ๋งŒ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ ๋™์‹œ ์ปจํ…Œ์ด๋„ˆ ์ˆ˜๋ฅผ ์ œํ•œํ•˜์ง€ ์•Š์œผ๋ฉด Lambda๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ํ™•์žฅ๋œ๋‹ค๋Š” ์ ์— ์œ ์˜ํ•˜์‹ญ์‹œ์˜ค.

์ €๋Š” Knex๋ฅผ Lambda์—์„œ ๊ฑฐ์˜ 1 ๋…„ ๋™์•ˆ ์•„๋ฌด ๋ฌธ์ œ์—†์ด ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด Lambda ํ•จ์ˆ˜ ์™ธ๋ถ€์—์„œ ๋‚ด Knex ์ธ์Šคํ„ด์Šค๋ฅผ ์„ ์–ธํ•˜๊ณ  ๋‹ค๋ฅธ ๊ฒŒ์‹œ๋ฌผ์—์„œ ์–ธ๊ธ‰ ํ•œ๋Œ€๋กœ context.callbackWaitsForEmptyEventLoop = false ๋ฅผ ํ™œ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰, ์ง€๋‚œ ํ•˜๋ฃจ ๋™์•ˆ Postgres์—์„œ ์—„์ฒญ๋‚œ ์—ฐ๊ฒฐ ์ŠคํŒŒ์ดํฌ๋ฅผ๋ณด๊ณ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ Lambda ์ธก์—์„œ ๋ฌด์–ธ๊ฐ€ ๋ณ€๊ฒฝ๋œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์—ฐ๊ฒฐ์ด ๋‹ซํ˜€ ์žˆ์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์•ž์„œ ์–ธ๊ธ‰ ํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋Š” ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์ง€๋‚œ ํ•˜๋ฃจ ๋™์•ˆ ๊ธฐํšŒ๋ฅผ ๋ณธ ์ ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

@jamesdixon ์€ ๋žŒ๋‹ค์— ๋Œ€ํ•œ knex ๊ตฌํ˜„์˜ ์ผ๋ถ€๋ฅผ ๋ฆฌํŒฉํ† ๋งํ•˜๋ฉด์„œ ์ง€๊ธˆ ์ด๊ฒƒ์„ ์ฝ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? context.callbackWaitsForEmptyEventLoop = false ์ž‘๋™์ด ์ค‘์ง€ ๋˜์—ˆ์Šต๋‹ˆ๊นŒ?

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰