์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿฆ• Deno์™€ TypeScript, ๊ฐœ๋ฐœ ์”ฌ์˜ ํŒ๋„๋ฅผ ๋ฐ”๊พธ๋Š” ๋ฏธ์นœ ์กฐํ•ฉ!

๐Ÿฆ• Deno์™€ TypeScript, ๊ฐœ๋ฐœ ์”ฌ์˜ ํŒ๋„๋ฅผ ๋ฐ”๊พธ๋Š” ๋ฏธ์นœ ์กฐํ•ฉ!

์•ˆ๋…•, ์ฝ”๋”ฉ ๋ฉ”์ดํŠธ! ๐Ÿ‘‹ ์˜ค๋Š˜ ์ข€ ํฅ๋ฏธ๋กœ์šด ์ด์•ผ๊ธฐ๋ฅผ ํ•ด๋ณผ๊นŒ ํ•ด.
์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์”ฌ์— ํ˜œ์„ฑ์ฒ˜๋Ÿผ ๋“ฑ์žฅํ•ด์„œ "ํ˜•๋‹˜, ์ด์ œ ์‹œ๋Œ€๊ฐ€ ๋ณ€ํ–ˆ์Šต๋‹ˆ๋‹ค"๋ฅผ ์™ธ์น˜๊ณ  ์žˆ๋Š” ๋…€์„, ๋ฐ”๋กœ Deno(๋ฐ๋…ธ)์— ๋Œ€ํ•œ ์ด์•ผ๊ธฐ์•ผ.

์•„๋งˆ ๋งŽ์€ ์นœ๊ตฌ๋“ค์ด Node.js์˜ ํ’ˆ์—์„œ ์•ˆ๋ฝํ•˜๊ฒŒ ์„œ๋ฒ„๋ฅผ ๋งŒ๋“ค๊ณ , ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋Œ๋ฆฌ๊ณ  ์žˆ์—ˆ์„ ๊ฑฐ์•ผ. ๋‚˜๋„ ๊ทธ๋žฌ๊ฑฐ๋“ . Node.js๋Š” ์šฐ๋ฆฌ์—๊ฒŒ ์„œ๋ฒ„์‚ฌ์ด๋“œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ผ๋Š” ์‹ ์„ธ๊ณ„๋ฅผ ์—ด์–ด์ค€ ๊ณ ๋งˆ์šด ์กด์žฌ์ง€. ๊ทธ๋Ÿฐ๋ฐ ๋ง์ด์•ผ, ๊ทธ Node.js๋ฅผ ๋งŒ๋“ค์—ˆ๋˜ ์ฒœ์žฌ ๊ฐœ๋ฐœ์ž, ๋ผ์ด์–ธ ๋‹ฌ(Ryan Dahl)์ด "์•„... ๋‚ด๊ฐ€ Node.js ๋งŒ๋“ค ๋•Œ ๋ช‡ ๊ฐ€์ง€ ํ›„ํšŒ๋˜๋Š” ๊ฒŒ ์žˆ์–ด"๋ผ๋ฉด์„œ ์™„์ „ํžˆ ์ƒˆ๋กœ์šด ๋Ÿฐํƒ€์ž„์„ ๋“ค๊ณ  ๋‚˜ํƒ€๋‚ฌ์–ด. ๊ทธ๊ฒŒ ๋ฐ”๋กœ Deno์•ผ. ๐Ÿฆ–

Deno๋Š” ๊ทธ๋ƒฅ 'Node.js 2.0' ๊ฐ™์€ ๊ฒŒ ์•„๋‹ˆ์•ผ.
ํƒœ์ƒ๋ถ€ํ„ฐ ๋‹ค๋ฅธ ์ฒ ํ•™์„ ๊ฐ€์ง€๊ณ  ํƒœ์–ด๋‚ฌ์–ด. ๋ณด์•ˆ์€ ๊ธฐ๋ณธ! ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๋„ค์ดํ‹ฐ๋ธŒ! ์ด ๋‘ ๊ฐ€์ง€๋งŒ ๋“ค์–ด๋„ ๊ฐ€์Šด์ด ๋›ฐ์ง€ ์•Š์•„? ๋งˆ์น˜ ์ตœ์‹ ํ˜• ์Šค๋งˆํŠธํฐ์ด ๋ฐฉ์ˆ˜๋ฐฉ์ง„๊ณผ ์ตœ๊ณ ์˜ ์นด๋ฉ”๋ผ๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ๋‹ฌ๊ณ  ๋‚˜์˜ค๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, Deno๋Š” ๊ฐœ๋ฐœ์ž์—๊ฒŒ ๊ฐ€์žฅ ํ•„์š”ํ–ˆ๋˜ ๊ธฐ๋Šฅ๋“ค์„ ๊ธฐ๋ณธ์œผ๋กœ ํ’ˆ๊ณ  ์„ธ์ƒ์— ๋‚˜์™”์–ด.

์ด ๊ธ€์—์„œ๋Š” Deno๊ฐ€ ๋„๋Œ€์ฒด ๋ญ๊ณ , Node.js๋ž‘์€ ๋ญ๊ฐ€ ๋‹ค๋ฅธ์ง€, ๊ทธ๋ฆฌ๊ณ  Deno์˜ ์˜ํ˜ผ์˜ ํŒŒํŠธ๋„ˆ์ธ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ(TypeScript)๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ์–ด๋–ค ์‹œ๋„ˆ์ง€๊ฐ€ ํญ๋ฐœํ•˜๋Š”์ง€ ์•„์ฃผ ํƒˆํƒˆ ํ„ธ์–ด๋ณผ ๊ฑฐ์•ผ. "์•„, Deno... ์ด๋ฆ„์€ ๋“ค์–ด๋ดค๋Š”๋ฐ..." ํ–ˆ๋˜ ์นœ๊ตฌ๋“ค์ด๋ผ๋ฉด ์˜ค๋Š˜ ์ด ๊ธ€์„ ๋‹ค ์ฝ๊ณ  ๋‚˜์„œ "์™€, ๋‹น์žฅ ์จ๋ณด๊ณ  ์‹ถ๋‹ค!"๋ผ๊ณ  ์™ธ์น˜๊ฒŒ ๋ ์ง€๋„ ๋ชฐ๋ผ. ๐Ÿ˜‰

Node.js์˜ ํ›„ํšŒ, ๊ทธ๋ฆฌ๊ณ  Deno์˜ ํƒ„์ƒ ๋ผ์ด์–ธ ๋‹ฌ์ด ๋‹ค์‹œ ์“ด ์„œ๋ฒ„์‚ฌ์ด๋“œ JavaScript/TypeScript ๋Ÿฐํƒ€์ž„ Node.js - npm & package.json - require() (CommonJS) - ๋ณด์•ˆ์€ '์•Œ์•„์„œ' - TS๋Š” ์ถ”๊ฐ€ ์„ค์ • ํ•„์š” โ†’ "10 Things I Regret About Node.js" Deno ๐Ÿฆ• - URL ๊ธฐ๋ฐ˜ ๋ชจ๋“ˆ - import/export (ESM) - ๋ณด์•ˆ์€ '๊ธฐ๋ณธ' - TS ๋„ค์ดํ‹ฐ๋ธŒ ์ง€์›

์ž, ๊ทธ๋Ÿผ ๋ณธ๊ฒฉ์ ์œผ๋กœ Deno์˜ ๋งค๋ ฅ์— ๋น ์ ธ๋ณผ ์ค€๋น„ ๋์–ด?
Let's dive in! ๐Ÿš€

Deno์˜ ์ฒ ํ•™: ๋ญ๊ฐ€ ๊ทธ๋ ‡๊ฒŒ ๋‹ค๋ฅธ๋ฐ? (feat. ํž™์Šคํ„ฐ ๊ฐ์„ฑ)

Deno๋ฅผ ๊ทธ๋ƒฅ '์ข€ ๋” ๋‚˜์€ Node.js'๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ์„ญ์„ญํ•ด.
Deno๋Š” ์™„์ „ํžˆ ๋‹ค๋ฅธ ํŒ์„ ์งœ๋ ค๊ณ  ๋‚˜์™”๊ฑฐ๋“ . ๋งˆ์น˜ MP3 ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์ง€๋ฐฐํ•˜๋˜ ์‹œ์žฅ์— ์•„์ดํŒŸ์ด ๋“ฑ์žฅํ•œ ๊ฒƒ์ฒ˜๋Ÿผ, ๊ธฐ์กด์˜ ๋ถˆํŽธํ•จ์„ ๊ทผ๋ณธ์ ์œผ๋กœ ํ•ด๊ฒฐํ•˜๋ ค๋Š” ์‹œ๋„์•ผ. Deno์˜ ํ•ต์‹ฌ ์ฒ ํ•™, ๋”ฑ 4๊ฐ€์ง€๋งŒ ์งš์–ด๋ณด์ž.

1. ๐Ÿ‘‘ ๋ณด์•ˆ์€ ์™•์ด๋‹ค: "ํ—ˆ๋ฝ ์—†์ด๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋ชป ํ•ด!"

Node.js๋ฅผ ์จ๋ณธ ์นœ๊ตฌ๋“ค์€ ์•Œ ๊ฑฐ์•ผ. `npm install`๋กœ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜๊ณ  ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด, ๊ทธ ์Šคํฌ๋ฆฝํŠธ๋Š” ๋‚ด ์ปดํ“จํ„ฐ์˜ ํŒŒ์ผ ์‹œ์Šคํ…œ, ๋„คํŠธ์›Œํฌ, ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋“ฑ ๊ฑฐ์˜ ๋ชจ๋“  ๊ฒƒ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด. ์ด๊ฑด ๋งˆ์น˜ ์ฒ˜์Œ ๋ณด๋Š” ์‚ฌ๋žŒ์—๊ฒŒ ์šฐ๋ฆฌ ์ง‘ ํ˜„๊ด€๋ฌธ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์•„. ๋ฌผ๋ก  ๋Œ€๋ถ€๋ถ„์˜ ํŒจํ‚ค์ง€๋Š” ์ฐฉํ•˜์ง€๋งŒ, ์•…์˜์ ์ธ ์ฝ”๋“œ๊ฐ€ ์ˆจ์–ด์žˆ๋‹ค๋ฉด? ๋”์ฐํ•˜์ง€. ๐Ÿ˜ฑ

Deno๋Š” ์—ฌ๊ธฐ์„œ ๋ฐœ์ƒ์˜ ์ „ํ™˜์„ ํ•ด. "๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋ชป ํ•˜๊ฒŒ ๋ง‰๊ณ , ํ•„์š”ํ•œ ๊ถŒํ•œ๋งŒ ๋ช…์‹œ์ ์œผ๋กœ ํ—ˆ์šฉํ•ด์ฃผ์ž!" ์ด๊ฒŒ ๋ฐ”๋กœ Deno์˜ 'Secure by Default' ์ฒ ํ•™์ด์•ผ.

์˜ˆ๋ฅผ ๋“ค์–ด, ์–ด๋–ค ์ฝ”๋“œ๊ฐ€ ๋„คํŠธ์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•˜๋ฉด, ์šฐ๋ฆฌ๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ --allow-net ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ถ™์—ฌ์ค˜์•ผ ํ•ด. ํŒŒ์ผ์„ ์ฝ๊ณ  ์‹ถ์œผ๋ฉด --allow-read, ์“ฐ๊ณ  ์‹ถ์œผ๋ฉด --allow-write ํ”Œ๋ž˜๊ทธ๊ฐ€ ํ•„์š”ํ•˜์ง€.


# ์ด ์ฝ”๋“œ๋Š” ๋„คํŠธ์›Œํฌ ์ ‘๊ทผ ๊ถŒํ•œ์ด ์—†์–ด์„œ ์—๋Ÿฌ ๋ฐœ์ƒ!
deno run my_server.ts

# "my_server.ts์•ผ, ๋„ˆ๋Š” ๋„คํŠธ์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•ด๋„ ์ข‹์•„!" ๋ผ๊ณ  ํ—ˆ๋ฝํ•ด์ฃผ๋Š” ๊ฒƒ
deno run --allow-net my_server.ts
    

์ด๊ฑด ๋งˆ์น˜ ํด๋Ÿฝ ์•ž์„ ์ง€ํ‚ค๋Š” ๋ฉ์น˜ ํฐ ๊ฐ€๋“œ ๊ฐ™์•„. "์‹ ๋ถ„์ฆ ๋ณด์—ฌ์ฃผ์„ธ์š”. ๋ชฉ์ ์ด ๋ญ์ฃ ?"๋ผ๊ณ  ์ผ์ผ์ด ํ™•์ธํ•˜๋Š” ๊ฑฐ์ง€. ์ฒ˜์Œ์—” ์ข€ ๊ท€์ฐฎ์„ ์ˆ˜ ์žˆ์ง€๋งŒ, ๋‚ด ์‹œ์Šคํ…œ์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ง€์ผœ์ค€๋‹ค๋‹ˆ ์–ผ๋งˆ๋‚˜ ๋“ ๋“ ํ•ด? ์ด ๋ณด์•ˆ ๋ชจ๋ธ ํ•˜๋‚˜๋งŒ์œผ๋กœ๋„ Deno๋ฅผ ์จ์•ผ ํ•  ์ด์œ ๋Š” ์ถฉ๋ถ„ํ•˜๋‹ค๊ณ  ๋ด.

๐Ÿ’ก ์ƒŒ๋“œ๋ฐ•์Šค(Sandbox) ๊ฐœ๋…!

Deno์˜ ์ด๋Ÿฐ ๋ณด์•ˆ ๋ชจ๋ธ์„ '์ƒŒ๋“œ๋ฐ•์Šค' ํ™˜๊ฒฝ์—์„œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค๊ณ  ํ‘œํ˜„ํ•ด. ์•„์ด๋“ค์ด ์•ˆ์ „ํ•œ ๋ชจ๋ž˜ ์ƒ์ž(sandbox) ์•ˆ์—์„œ๋งŒ ๋…ธ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ์ฝ”๋“œ๋„ ๊ฒฉ๋ฆฌ๋œ ํ™˜๊ฒฝ์—์„œ ์ •ํ•ด์ง„ ๊ถŒํ•œ ๋‚ด์—์„œ๋งŒ ์‹คํ–‰๋˜๋„๋ก ํ•˜๋Š” ๊ฑฐ์ง€. ๋•๋ถ„์— ์šฐ๋ฆฌ๋Š” ํ›จ์”ฌ ๋” ์•ˆ์‹ฌํ•˜๊ณ  ์™ธ๋ถ€ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ผ.

2. ๐Ÿ’™ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๊ตญ๋ฃฐ์ด์ง€: "์„ค์ •? ๊ทธ๊ฒŒ ๋ญ”๋ฐ ๋จน๋Š” ๊ฑด๊ฐ€?"

์š”์ฆ˜ ์›น ๊ฐœ๋ฐœ ์”ฌ์—์„œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ์„ ํƒ์ด ์•„๋‹Œ ํ•„์ˆ˜๊ฐ€ ๋˜์–ด๊ฐ€๊ณ  ์žˆ์–ด. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ์œ ์—ฐํ•จ์ด ๋•Œ๋กœ๋Š” ๋ฐœ๋ชฉ์„ ์žก๋Š” '๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ'์˜ ์ฃผ๋ฒ”์ด ๋˜๋‹ˆ๊นŒ. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ํƒ€์ž…์„ ์ฒดํฌํ•ด์„œ ์ด๋Ÿฐ ์‹ค์ˆ˜๋ฅผ ๋ฏธ๋ฆฌ ๋ง‰์•„์ฃผ๋Š” ๋“ ๋“ ํ•œ ์ง€์›๊ตฐ์ด์•ผ.

๊ทธ๋Ÿฐ๋ฐ Node.js์—์„œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์“ฐ๋ ค๋ฉด ์–ด๋• ์–ด?
์ผ๋‹จ `typescript`, `ts-node`, `@types/node` ๊ฐ™์€ ํŒจํ‚ค์ง€๋“ค์„ ์„ค์น˜ํ•˜๊ณ , `tsconfig.json`์ด๋ผ๋Š” ๋ณต์žกํ•œ ์„ค์ • ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์„œ ์ปดํŒŒ์ผ ์˜ต์…˜์„ ํ•˜๋‚˜ํ•˜๋‚˜ ์ง€์ •ํ•ด์ค˜์•ผ ํ–ˆ์ง€. ์ดˆ์‹ฌ์ž์—๊ฒŒ๋Š” ์ด ๊ณผ์ • ์ž์ฒด๊ฐ€ ํ•˜๋‚˜์˜ ๊ฑฐ๋Œ€ํ•œ ์žฅ๋ฒฝ์ด์—ˆ์–ด.

Deno๋Š” ์ด ๋ชจ๋“  ๊ณผ์ •์„ ์—†์• ๋ฒ„๋ ธ์–ด. ๊ทธ๋ƒฅ `.ts` ํ™•์žฅ์ž๋กœ ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  ์ฝ”๋“œ๋ฅผ ์งœ๋ฉด ๋. Deno ๋Ÿฐํƒ€์ž„ ์ž์ฒด๊ฐ€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ปดํŒŒ์ผ๋Ÿฌ(์ •ํ™•ํžˆ๋Š” Rust๋กœ ๋งŒ๋“ค์–ด์ง„ SWC)๋ฅผ ๋‚ด์žฅํ•˜๊ณ  ์žˆ์–ด์„œ, ์šฐ๋ฆฌ๊ฐ€ `deno run` ๋ช…๋ น์„ ๋‚ด๋ฆฌ๋ฉด ์•Œ์•„์„œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ๋ณ€ํ™˜ํ•ด์„œ ์‹คํ–‰ํ•ด์ค˜.


// my_script.ts
function greet(name: string): string {
  return `Hello, ${name}!`;
}

console.log(greet("Deno"));

// ๊ทธ๋ƒฅ ์‹คํ–‰ํ•˜๋ฉด ๋! ๋ณ„๋„์˜ ์ปดํŒŒ์ผ ๊ณผ์ •์ด ํ•„์š” ์—†๋‹ค.
// $ deno run my_script.ts
// Output: Hello, Deno!
    

์ด๊ฑด ์ •๋ง ํ˜๋ช…์ด์•ผ. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์“ฐ๊ธฐ ์œ„ํ•œ ํ—ˆ๋“ค์„ ์ œ๋กœ๋กœ ๋งŒ๋“ค์–ด๋ฒ„๋ฆฐ ๊ฑฐ์ง€. ๋•๋ถ„์— ์šฐ๋ฆฌ๋Š” ์˜ค๋กฏ์ด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์–ด. Deno์™€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ์กฐํ•ฉ์€ ๋งˆ์น˜ ์•„๋ฉ”๋ฆฌ์นด๋…ธ์™€ ์น˜์ฆˆ์ผ€์ดํฌ, ์‚ผ๊ฒน์‚ด๊ณผ ์†Œ์ฃผ์ฒ˜๋Ÿผ ๋–ผ๋ ค์•ผ ๋—„ ์ˆ˜ ์—†๋Š” ์™„๋ฒฝํ•œ ํŽ˜์–ด๋ง์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์–ด. ์ฐฐ๋–ก๊ถํ•ฉ ๊ทธ ์ž์ฒด! โœจ

3. ๐Ÿ“ฆ ๋ชจ๋“ˆ ์‹œ์Šคํ…œ์˜ ํ˜„๋Œ€ํ™”: "npm๊ณผ package.json์€ ์ด์ œ ์•ˆ๋…•"

Node.js ์ƒํƒœ๊ณ„์˜ ์‹ฌ์žฅ์€ `npm`๊ณผ `package.json`์ด์—ˆ์–ด. ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ `npm`์œผ๋กœ ์„ค์น˜ํ•˜๋ฉด `node_modules`๋ผ๋Š” ๊ฑฐ๋Œ€ํ•œ ๋ธ”๋ž™ํ™€์ด ์ƒ๊ธฐ๊ณ , `package.json`์— ์˜์กด์„ฑ์ด ๊ธฐ๋ก๋์ง€. ๊ทธ๋ฆฌ๊ณ  ์ฝ”๋“œ์—์„œ๋Š” `require('express')` ๊ฐ™์€ CommonJS ๋ฐฉ์‹์œผ๋กœ ๋ชจ๋“ˆ์„ ๋ถˆ๋Ÿฌ์™”์–ด.

Deno๋Š” ์ด ๋ฐฉ์‹์— ๊ณผ๊ฐํžˆ ์ž‘๋ณ„์„ ๊ณ ํ–ˆ์–ด. ๋Œ€์‹  ์›น ํ‘œ์ค€์ธ ES Modules(ESM)์„ ์ „๋ฉด์ ์œผ๋กœ ์ฑ„ํƒํ–ˆ์ง€. ๊ทธ๋ฆฌ๊ณ  ๋ชจ๋“ˆ์„ `npm` ๊ฐ™์€ ์ค‘์•™ ์ €์žฅ์†Œ์—์„œ ๋‹ค์šด๋ฐ›๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, URL์„ ํ†ตํ•ด ์ง์ ‘ ์ž„ํฌํŠธํ•ด.


// Node.js (CommonJS)
const express = require('express');

// Deno (ES Modules)
import { Application } from "https://deno.land/std@0.224.0/http/mod.ts";
    

URL๋กœ ๋ชจ๋“ˆ์„ ๊ฐ€์ ธ์˜จ๋‹ค๋‹ˆ, ์ฒ˜์Œ์—” ์ข€ ์–ด์ƒ‰ํ•  ์ˆ˜ ์žˆ์–ด. "์ €๋Ÿฌ๋‹ค ์ € URL ์ฃผ์†Œ ๋ฐ”๋€Œ๊ฑฐ๋‚˜ ์‚ฌ๋ผ์ง€๋ฉด ๋‚ด ํ”„๋กœ์ ํŠธ๋Š” ๋งํ•˜๋Š” ๊ฑฐ ์•„๋ƒ?" ๋ผ๋Š” ๊ฑฑ์ •๋„ ๋“ค ์ˆ˜ ์žˆ์ง€. ํ•˜์ง€๋งŒ Deno๋Š” ํ•œ๋ฒˆ ์ž„ํฌํŠธํ•œ ๋ชจ๋“ˆ์„ ๋กœ์ปฌ์— ์บ์‹ฑํ•ด๋‘๊ธฐ ๋•Œ๋ฌธ์— ๋งค๋ฒˆ ๋‹ค์šด๋กœ๋“œํ•˜์ง€ ์•Š์•„. ๊ทธ๋ฆฌ๊ณ  `deno.json`(๋˜๋Š” `deno.jsonc`) ํŒŒ์ผ์˜ `imports` ๋งต์„ ์‚ฌ์šฉํ•˜๋ฉด ์˜์กด์„ฑ์„ `package.json`์ฒ˜๋Ÿผ ๊น”๋”ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๊ณ  ๋ฒ„์ „๋„ ๊ณ ์ •ํ•  ์ˆ˜ ์žˆ์–ด.

์ด ๋ฐฉ์‹์˜ ๊ฐ€์žฅ ํฐ ์žฅ์ ์€ ํƒˆ์ค‘์•™ํ™”์•ผ. ๋ˆ„๊ตฌ๋‚˜ ์ž์‹ ์˜ ์„œ๋ฒ„๋‚˜ GitHub ์ €์žฅ์†Œ์— ์ฝ”๋“œ๋ฅผ ์˜ฌ๋ ค๋‘๊ณ  URL๋กœ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์–ด. `npm`์ด๋ผ๋Š” ํ•˜๋‚˜์˜ ๊ฑฐ๋Œ€ํ•œ ํ”Œ๋žซํผ์— ์˜์กดํ•  ํ•„์š”๊ฐ€ ์—†๋Š” ๊ฑฐ์ง€. ์›น์ด ๋™์ž‘ํ•˜๋Š” ๋ฐฉ์‹๊ณผ ๋˜‘๊ฐ™์ด, ํ•˜์ดํผ๋งํฌ๋กœ ์ฝ”๋“œ๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฑฐ์•ผ. ์ •๋ง '์›น'์Šค๋Ÿฌ์šด ๋ฐฉ์‹์ด์ง€ ์•Š์•„?

4. ๐Ÿ› ๏ธ ์˜ฌ์ธ์› ํˆด์ฒด์ธ: "๊ฐœ๋ฐœ์— ํ•„์š”ํ•œ ๊ฑด ๋‹ค ์ค€๋น„ํ•ด๋†จ์–ด"

Node.js๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋‚˜ ์‹œ์ž‘ํ•˜๋ ค๋ฉด ๋ญ๊ฐ€ ํ•„์š”ํ–ˆ์ง€?
์ฝ”๋“œ ํฌ๋งทํ„ฐ๋กœ๋Š” `Prettier`, ์ฝ”๋“œ ํ’ˆ์งˆ ๊ฒ€์‚ฌ ๋„๊ตฌ๋กœ๋Š” `ESLint`, ํ…Œ์ŠคํŠธ ๋Ÿฌ๋„ˆ๋กœ๋Š” `Jest`๋‚˜ `Mocha`, ๋ฒˆ๋“ค๋Ÿฌ๋กœ๋Š” `Webpack`์ด๋‚˜ `Rollup`... ๊ฐ๊ธฐ ๋‹ค๋ฅธ ๋„๊ตฌ๋“ค์„ ์กฐํ•ฉํ•ด์„œ '๋‚˜๋งŒ์˜ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ'์„ ๊ตฌ์ถ•ํ•ด์•ผ ํ–ˆ์–ด. ๋ฌผ๋ก  ์ž์œ ๋„๊ฐ€ ๋†’๋‹ค๋Š” ์žฅ์ ๋„ ์žˆ์ง€๋งŒ, ๋งค๋ฒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•  ๋•Œ๋งˆ๋‹ค ์ด ์„ค์ • ์ง€์˜ฅ์— ๋น ์ง€๋Š” ๊ฑด ๊ฝค๋‚˜ ํ”ผ๊ณคํ•œ ์ผ์ด์•ผ.

Deno๋Š” "๊ทธ๋Ÿฐ ๊ท€์ฐฎ์€ ๊ฑด ๋‚ด๊ฐ€ ๋‹ค ํ•ด์ค„๊ฒŒ!"๋ผ๊ณ  ๋งํ•ด. Deno๋Š” ๋Ÿฐํƒ€์ž„ ์ž์ฒด์— ํ•„์ˆ˜์ ์ธ ๊ฐœ๋ฐœ ๋„๊ตฌ๋“ค์„ ๋ชจ๋‘ ๋‚ด์žฅํ•˜๊ณ  ์žˆ์–ด. ์ด๊ฑธ 'ํˆด์ฒด์ธ(Toolchain)'์ด๋ผ๊ณ  ๋ถˆ๋Ÿฌ.

  • ์ฝ”๋“œ ํฌ๋งทํ„ฐ: deno fmt - Prettier์ฒ˜๋Ÿผ ์ฝ”๋“œ๋ฅผ ์˜ˆ์˜๊ฒŒ ์ •๋ ฌํ•ด์ค˜.
  • ๋ฆฐํ„ฐ: deno lint - ESLint์ฒ˜๋Ÿผ ์ฝ”๋“œ์˜ ์ž ์žฌ์ ์ธ ๋ฌธ์ œ์ ์„ ์ฐพ์•„์ค˜.
  • ํ…Œ์ŠคํŠธ ๋Ÿฌ๋„ˆ: deno test - Jest์ฒ˜๋Ÿผ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋ณด์—ฌ์ค˜.
  • ์˜์กด์„ฑ ๊ฒ€์‚ฌ๊ธฐ: deno info - ๋‚ด ์ฝ”๋“œ์˜ ์˜์กด์„ฑ ํŠธ๋ฆฌ๋ฅผ ํ•œ๋ˆˆ์— ๋ณด์—ฌ์ค˜.
  • ๋ฌธ์„œ ์ƒ์„ฑ๊ธฐ: deno doc - ์ฝ”๋“œ ์ฃผ์„์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฉ‹์ง„ ๋ฌธ์„œ๋ฅผ ๋งŒ๋“ค์–ด์ค˜.

์ด ๋ชจ๋“  ๊ฒŒ Deno๋ฅผ ์„ค์น˜ํ•˜๋Š” ์ˆœ๊ฐ„ ๋ฐ”๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ด. ๋ณ„๋„์˜ ์„ค์น˜๋‚˜ ๋ณต์žกํ•œ ์„ค์ • ํŒŒ์ผ ์—†์ด, ๋ช…๋ น์–ด ํ•˜๋‚˜๋กœ ๋ชจ๋“  ๊ฑธ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฑฐ์ง€. ๊ฐœ๋ฐœ ๊ฒฝํ—˜(DX, Developer Experience)์ด ์ˆ˜์ง ์ƒ์Šนํ•˜๋Š” ์†Œ๋ฆฌ๊ฐ€ ๋“ค๋ฆฌ์ง€ ์•Š์•„? ๐Ÿš€

๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ •: Node.js vs Deno Deno์˜ ์˜ฌ์ธ์› ํˆด์ฒด์ธ์ด ๊ฐ€์ ธ๋‹ค์ฃผ๋Š” ํŽธ๋ฆฌํ•จ Node.js Ecosystem Runtime (Node.js) + Prettier (Formatter) ESLint (Linter) Jest (Tester) TypeScript (Compiler) ... and more Deno ๐Ÿฆ• All-in-One Runtime Secure Sandbox TypeScript Native Built-in Formatter Built-in Linter Built-in Tester

์–ด๋•Œ, Deno์˜ ์ฒ ํ•™์ด ์ข€ ์™€๋‹ฟ์•„?
์ •๋ฆฌํ•˜์ž๋ฉด Deno๋Š” ๋” ์•ˆ์ „ํ•˜๊ณ , ๋” ํ˜„๋Œ€์ ์ด๊ณ , ๋” ํŽธ๋ฆฌํ•œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ์ง€ํ–ฅํ•ด. ๊ธฐ์กด์˜ ๋ณต์žก์„ฑ์„ ๊ฑท์–ด๋‚ด๊ณ  ๋ณธ์งˆ์— ์ง‘์ค‘ํ•˜์ž๋Š” Deno์˜ ๋ฏธ๋‹ˆ๋ฉ€๋ฆฌ์ฆ˜ ์ฒ ํ•™์€ ์š”์ฆ˜ ํŠธ๋ Œ๋“œ์™€๋„ ์ž˜ ๋งž๋Š” ๊ฒƒ ๊ฐ™์•„. ์ž, ๊ทธ๋Ÿผ ์ด์ œ ์ด๋ก ์€ ๊ทธ๋งŒํ•˜๊ณ  ์ง์ ‘ Deno์˜ ์„ธ๊ณ„๋กœ ๋“ค์–ด๊ฐ€ ๋ณด์ž! ๐Ÿ’ป

๐Ÿ› ๏ธ ์ž, ์ด์ œ ์ง์ ‘ ๋งŒ์ ธ๋ณด์ž: Deno ์„ค์น˜์™€ ์ฒซ๊ฑธ์Œ

๋ฐฑ๋ฌธ์ด ๋ถˆ์—ฌ์ผ๊ฒฌ! Deno๊ฐ€ ์•„๋ฌด๋ฆฌ ์ข‹๋‹ค๊ณ  ๋ง๋กœ๋งŒ ๋“ค์–ด๋ด์•ผ ๊ฐ์ด ์•ˆ ์˜ค์ง€.
์ง์ ‘ ๋‚ด ์ปดํ“จํ„ฐ์— ์„ค์น˜ํ•˜๊ณ  "Hello, Deno!"๋ฅผ ์™ธ์ณ๋ณด๋Š” ๊ฒƒ๋งŒํผ ํ™•์‹คํ•œ ๊ฑด ์—†์–ด. ์„ค์น˜ ๊ณผ์ •์€ ๋†€๋ผ์šธ ์ •๋„๋กœ ๊ฐ„๋‹จํ•˜๋‹ˆ ๊ฒ๋จน์ง€ ๋ง๊ณ  ๋”ฐ๋ผ์™€ ๋ด. ๋ ›์ธ ๊ณ ! ๐Ÿ’จ

1. Deno ์„ค์น˜ํ•˜๊ธฐ: ๋ช…๋ น์–ด ํ•œ ์ค„์ด๋ฉด ๋!

Deno๋Š” ๋ณต์žกํ•œ ์„ค์น˜ ํ”„๋กœ๊ทธ๋žจ ์—†์ด, ํ„ฐ๋ฏธ๋„(๋˜๋Š” PowerShell)์—์„œ ๋ช…๋ น์–ด ํ•œ ์ค„๋งŒ ์ž…๋ ฅํ•˜๋ฉด ์„ค์น˜๊ฐ€ ์™„๋ฃŒ๋ผ. ์ •๋ง์ด์•ผ!

macOS ๋˜๋Š” Linux ์‚ฌ์šฉ์ž (Shell):

ํ„ฐ๋ฏธ๋„์„ ์—ด๊ณ  ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ๋ณต์‚ฌํ•ด์„œ ๋ถ™์—ฌ๋„ฃ์–ด ๋ด.

curl -fsSL https://deno.land/x/install/install.sh | sh

์ด ๋ช…๋ น์–ด๋Š” Deno ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€์—์„œ ์„ค์น˜ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋‹ค์šด๋กœ๋“œํ•ด์„œ ๋ฐ”๋กœ ์‹คํ–‰ํ•˜๋Š” ๊ฑฐ์•ผ. ์„ค์น˜๊ฐ€ ๋๋‚˜๋ฉด ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •์— ๋Œ€ํ•œ ์•ˆ๋‚ด๊ฐ€ ๋‚˜์˜ฌ ํ…๋ฐ, ํ„ฐ๋ฏธ๋„์— ํ‘œ์‹œ๋˜๋Š” ๊ฒฝ๋กœ๋ฅผ `~/.zshrc`๋‚˜ `~/.bash_profile` ๊ฐ™์€ ์‰˜ ์„ค์ • ํŒŒ์ผ์— ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋ผ. (๋ณดํ†ต์€ ์•Œ์•„์„œ ์ž˜ ํ•ด์ค˜!)

Windows ์‚ฌ์šฉ์ž (PowerShell):

PowerShell์„ ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์œผ๋กœ ์‹คํ–‰ํ•˜๊ณ  ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•ด ์ค˜.

irm https://deno.land/x/install/install.ps1 | iex

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์•Œ์•„์„œ Deno๋ฅผ ์„ค์น˜ํ•˜๊ณ  ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊นŒ์ง€ ์„ค์ •ํ•ด์ค„ ๊ฑฐ์•ผ. ์ฐธ ์‰ฝ์ง€? ๐Ÿ˜‰

์„ค์น˜๊ฐ€ ์ž˜ ๋๋Š”์ง€ ํ™•์ธํ•ด๋ณผ๊นŒ?
ํ„ฐ๋ฏธ๋„์„ ์ƒˆ๋กœ ์—ด๊ณ  ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•ด ๋ด.

deno --version

์•„๋ž˜์ฒ˜๋Ÿผ Deno, V8, TypeScript์˜ ๋ฒ„์ „ ์ •๋ณด๊ฐ€ ๋ฉ‹์ง€๊ฒŒ ์ถœ๋ ฅ๋œ๋‹ค๋ฉด ์„ฑ๊ณต์ ์œผ๋กœ ์„ค์น˜๋œ ๊ฑฐ์•ผ! ๐ŸŽ‰


deno 1.44.2 (release, x86_64-apple-darwin)
v8 12.5.202.6
typescript 5.4.5
    
๐Ÿค” ์ž ๊น, V8์ด ๋ญ์•ผ?

V8์€ ๊ตฌ๊ธ€์—์„œ ๋งŒ๋“  ์ดˆ๊ณ ์† ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„์ด์•ผ. ์šฐ๋ฆฌ๊ฐ€ ์“ฐ๋Š” ํฌ๋กฌ ๋ธŒ๋ผ์šฐ์ €์™€ Node.js์˜ ์‹ฌ์žฅ์ด ๋ฐ”๋กœ ์ด V8 ์—”์ง„์ด์ง€. Deno ์—ญ์‹œ V8 ์—”์ง„์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค์–ด์กŒ๊ธฐ ๋•Œ๋ฌธ์— ์•„์ฃผ ๋น ๋ฅธ ์†๋„๋กœ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์–ด. Deno๋Š” Node.js์™€ ์‹ฌ์žฅ์€ ๊ณต์œ ํ•˜์ง€๋งŒ, ๊ทธ ์‹ฌ์žฅ์„ ๋‘˜๋Ÿฌ์‹ผ ํ™˜๊ฒฝ(๋Ÿฐํƒ€์ž„)์€ ์™„์ „ํžˆ ์ƒˆ๋กญ๊ฒŒ ์„ค๊ณ„ํ•œ ๊ฑฐ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ์‰ฌ์›Œ.

2. ์ฒซ ๋ฒˆ์งธ Deno ์Šคํฌ๋ฆฝํŠธ: Welcome, TypeScript!

์ด์ œ ์ง„์งœ ์ฝ”๋“œ๋ฅผ ์งœ๋ณผ ์‹œ๊ฐ„์ด์•ผ.
์ข‹์•„ํ•˜๋Š” ์ฝ”๋“œ ์—๋””ํ„ฐ(VS Code๋ฅผ ๊ฐ•๋ ฅ ์ถ”์ฒœํ•ด!)๋ฅผ ์—ด๊ณ , `welcome.ts`๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ์ƒˆ ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด๋ด. ํ™•์žฅ์ž๊ฐ€ `.js`๊ฐ€ ์•„๋‹ˆ๋ผ `.ts`์ธ ๊ฑฐ ๋ณด์ด์ง€? ์šฐ๋ฆฐ ์ฒ˜์Œ๋ถ€ํ„ฐ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋กœ ์‹œ์ž‘ํ•  ๊ฑฐ์•ผ!

ํŒŒ์ผ์— ์•„๋ž˜ ๋‚ด์šฉ์„ ์ž…๋ ฅํ•ด ๋ณด์ž.


// welcome.ts

// ๊ฐ„๋‹จํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜ํ•ด์„œ ๊ฐ์ฒด์˜ ๋ชจ์–‘์„ ๊ฐ•์ œํ•ด๋ณด์ž.
interface Person {
  name: string;
  age: number;
}

const me: Person = {
  name: "Jaenung Talent",
  age: 10
};

function createGreeting(person: Person): string {
  // ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ๋•๋ถ„์— person.name๊ณผ person.age๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด.
  return `์•ˆ๋…•! ๋‚ด ์ด๋ฆ„์€ ${person.name}์ด๊ณ , ${person.age}์‚ด์ด์•ผ. Deno์˜ ์„ธ๊ณ„์— ์˜จ ๊ฑธ ํ™˜์˜ํ•ด!`;
}

// Deno์˜ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ `colors` ๋ชจ๋“ˆ์„ ๊ฐ€์ ธ์™€๋ณด์ž.
// URL๋กœ ์ง์ ‘ ์ž„ํฌํŠธํ•˜๋Š” Deno์˜ ๋ฐฉ์‹!
import { bold, yellow } from "https://deno.land/std@0.224.0/fmt/colors.ts";

const greetingMessage = createGreeting(me);

// ์ฝ˜์†”์— ์ถœ๋ ฅํ•  ๋•Œ ์ƒ‰๊น”๋„ ์ž…ํ˜€๋ณด์ž!
console.log(bold(yellow(greetingMessage)));
    

์ฝ”๋“œ๊ฐ€ ์–ด๋–ค ์ผ์„ ํ•˜๋Š”์ง€ ๊ฐ„๋‹จํžˆ ์‚ดํŽด๋ณผ๊นŒ?

  1. `Person`์ด๋ผ๋Š” `interface`๋ฅผ ๋งŒ๋“ค์–ด์„œ `name`์€ ๋ฌธ์ž์—ด, `age`๋Š” ์ˆซ์ž์—ฌ์•ผ ํ•œ๋‹ค๊ณ  ํƒ€์ž…์„ ์ •ํ•ด์คฌ์–ด.
  2. `me`๋ผ๋Š” ์ƒ์ˆ˜๋ฅผ ๋งŒ๋“ค ๋•Œ `Person` ํƒ€์ž…์„ ์ง€์ •ํ•ด์„œ, ๋งŒ์•ฝ `age`์— ๋ฌธ์ž์—ด์„ ๋„ฃ์œผ๋ ค๊ณ  ํ•˜๋ฉด ์—๋””ํ„ฐ์—์„œ ๋ฐ”๋กœ ์—๋Ÿฌ๋ฅผ ํ‘œ์‹œํ•ด์ค„ ๊ฑฐ์•ผ. ์ด๊ฒŒ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ํž˜์ด์ง€! ๐Ÿ’ช
  3. `createGreeting` ํ•จ์ˆ˜๋Š” `Person` ํƒ€์ž…์˜ ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„์„œ ์ธ์‚ฌ๋ง ๋ฌธ์ž์—ด์„ ๋งŒ๋“ค์–ด์ค˜.
  4. `import` ๊ตฌ๋ฌธ์„ ๋ด๋ด. `https://deno.land/std...` ๋กœ ์‹œ์ž‘ํ•˜๋Š” URL์—์„œ `colors` ๋ชจ๋“ˆ์„ ์ง์ ‘ ๊ฐ€์ ธ์˜ค๊ณ  ์žˆ์–ด. `npm install` ๊ฐ™์€ ๊ฑด ํ•„์š” ์—†์–ด!
  5. ๋งˆ์ง€๋ง‰์œผ๋กœ `colors` ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•ด์„œ ์ฝ˜์†”์— ์ถœ๋ ฅ๋˜๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ๊ตต๊ณ  ๋…ธ๋ž€์ƒ‰์œผ๋กœ ๊พธ๋ฉฐ์คฌ์–ด.

์ž, ์ด์ œ ์ด ํŒŒ์ผ์„ ์‹คํ–‰ํ•ด๋ณผ๊นŒ?
ํ„ฐ๋ฏธ๋„์—์„œ `welcome.ts` ํŒŒ์ผ์ด ์žˆ๋Š” ๊ฒฝ๋กœ๋กœ ์ด๋™ํ•œ ๋‹ค์Œ, ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ด ๋ด.

deno run welcome.ts

์ฒ˜์Œ ์‹คํ–‰ํ•˜๋ฉด Deno๊ฐ€ `https://deno.land/...` ์—์„œ ๋ชจ๋“ˆ์„ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ์ปดํŒŒ์ผํ•˜๋А๋ผ ์•ฝ๊ฐ„์˜ ๋ฉ”์‹œ์ง€๊ฐ€ ๋œฐ ๊ฑฐ์•ผ. ๊ทธ๋ฆฌ๊ณ  ์ž ์‹œ ํ›„, ํ„ฐ๋ฏธ๋„์— ์ด๋ ‡๊ฒŒ ์˜ˆ์œ ๋ฉ”์‹œ์ง€๊ฐ€ ๋‚˜ํƒ€๋‚  ๊ฑฐ์•ผ.


Download https://deno.land/std@0.224.0/fmt/colors.ts
...
Check file:///path/to/your/project/welcome.ts
<b><span style="color: yellow;">์•ˆ๋…•! ๋‚ด ์ด๋ฆ„์€ Jaenung Talent์ด๊ณ , 10์‚ด์ด์•ผ. Deno์˜ ์„ธ๊ณ„์— ์˜จ ๊ฑธ ํ™˜์˜ํ•ด!</span></b>
    

์–ด๋•Œ? ๋ณ„๋„์˜ ์„ค์ • ํ•˜๋‚˜ ์—†์ด ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ์ด ๊ทธ๋ƒฅ ๋ฟ… ํ•˜๊ณ  ์‹คํ–‰๋์–ด.
URL์—์„œ ๋ชจ๋“ˆ๋„ ์•Œ์•„์„œ ์ฐฉ์ฐฉ ๊ฐ€์ ธ์˜ค๊ณ  ๋ง์ด์•ผ. ์ด๊ฒŒ ๋ฐ”๋กœ Deno์˜ ๊ธฐ๋ณธ ํด๋ž˜์Šค์•ผ. ๐Ÿ˜Ž

3. ๋ณด์•ˆ ์ƒŒ๋“œ๋ฐ•์Šค ์ฒดํ—˜ํ•˜๊ธฐ: ๊ฐ„๋‹จํ•œ ์›น ์„œ๋ฒ„ ๋„์šฐ๊ธฐ

Deno์˜ ํ•ต์‹ฌ ์ฒ ํ•™ ์ค‘ ํ•˜๋‚˜์ธ '๋ณด์•ˆ'์„ ์ง์ ‘ ์ฒดํ—˜ํ•ด๋ณด์ž.
์ด๋ฒˆ์—๋Š” ์•„์ฃผ ๊ฐ„๋‹จํ•œ ์›น ์„œ๋ฒ„๋ฅผ ๋งŒ๋“œ๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณผ ๊ฑฐ์•ผ. `server.ts`๋ผ๋Š” ์ƒˆ ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋„ฃ์–ด๋ด.


// server.ts
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";

console.log("์›น ์„œ๋ฒ„๊ฐ€ http://localhost:8000 ์—์„œ ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค...");

// Deno.serve๋Š” ๋“ค์–ด์˜ค๋Š” HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ด.
serve((_req: Request) => {
  // ๋ชจ๋“  ์š”์ฒญ์— ๋Œ€ํ•ด "Hello from Deno Server!" ๋ผ๋Š” ์‘๋‹ต์„ ๋ณด๋‚ธ๋‹ค.
  return new Response("Hello from Deno Server!", {
    headers: { "content-type": "text/plain" },
  });
});
    

์ด ์ฝ”๋“œ๋Š” Deno ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ `http/server` ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•ด์„œ 8000๋ฒˆ ํฌํŠธ์—์„œ ์›น ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์ฝ”๋“œ์•ผ. ์ด์ œ ์ด ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•ด๋ณผ๊นŒ?

deno run server.ts

์–ด๋ผ? ์ด๋ฒˆ์—๋Š” ์ฝ”๋“œ๊ฐ€ ๋ฐ”๋กœ ์‹คํ–‰๋˜์ง€ ์•Š๊ณ , ๋ญ”๊ฐ€ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๊ฐ€ ๋œฐ ๊ฑฐ์•ผ.


error: Uncaught (in promise) PermissionDenied: Requires net access to "0.0.0.0:8000", run again with the --allow-net flag.
    at ...
    

๋ฉ”์‹œ์ง€๋ฅผ ์ž˜ ์ฝ์–ด๋ด. `PermissionDenied`, ์ฆ‰ ๊ถŒํ•œ์ด ๊ฑฐ๋ถ€๋˜์—ˆ๋‹ค๋Š” ๋œป์ด์•ผ.
๋„คํŠธ์›Œํฌ์— ์ ‘๊ทผํ•˜๋ ค๋ฉด --allow-net ํ”Œ๋ž˜๊ทธ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๊ณ  ์นœ์ ˆํ•˜๊ฒŒ ์•Œ๋ ค์ฃผ๋„ค. ์ด๊ฒŒ ๋ฐ”๋กœ Deno์˜ ๋ณด์•ˆ ์ƒŒ๋“œ๋ฐ•์Šค๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์ด์•ผ. ์ฝ”๋“œ๊ฐ€ ๋งˆ์Œ๋Œ€๋กœ ๋„คํŠธ์›Œํฌ์— ์ ‘์†ํ•˜๋Š” ๊ฑธ ๋ง‰์•„์ค€ ๊ฑฐ์ง€.

์ด์ œ Deno๊ฐ€ ์‹œํ‚ค๋Š” ๋Œ€๋กœ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•ด์„œ ๋‹ค์‹œ ์‹คํ–‰ํ•ด๋ณด์ž.

deno run --allow-net server.ts

์ด์ œ ํ„ฐ๋ฏธ๋„์— "์›น ์„œ๋ฒ„๊ฐ€ http://localhost:8000 ์—์„œ ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค..." ๋ผ๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ๋œจ๊ณ  ๋ฉˆ์ถฐ์žˆ์„ ๊ฑฐ์•ผ. ์„ฑ๊ณต์ ์œผ๋กœ ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰๋œ ๊ฑฐ์•ผ! ์›น ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์—ด๊ณ  ์ฃผ์†Œ์ฐฝ์— `http://localhost:8000`์„ ์ž…๋ ฅํ•ด๋ด. ํ™”๋ฉด์— "Hello from Deno Server!"๊ฐ€ ๋ณด์ธ๋‹ค๋ฉด, ๋„Œ ๋ฐฉ๊ธˆ Deno๋กœ ์ฒซ ์›น ์„œ๋ฒ„๋ฅผ ๋„์šด ๊ฑฐ์•ผ! ์ถ•ํ•˜ํ•ด! ๐Ÿฅณ

๐Ÿšจ ๊ถŒํ•œ์€ ์ตœ์†Œํ•œ์œผ๋กœ!

--allow-net ์ฒ˜๋Ÿผ ๋ชจ๋“  ๋„คํŠธ์›Œํฌ๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ๋Œ€์‹ , --allow-net=google.com ์ฒ˜๋Ÿผ ํŠน์ • ๋„๋ฉ”์ธ๋งŒ ํ—ˆ์šฉํ•  ์ˆ˜๋„ ์žˆ์–ด. ํŒŒ์ผ์„ ์ฝ์„ ๋•Œ๋„ --allow-read=/path/to/my_data ์ฒ˜๋Ÿผ ํŠน์ • ๋””๋ ‰ํ† ๋ฆฌ๋งŒ ํ—ˆ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์•„. ํ•„์š”ํ•œ ๋งŒํผ๋งŒ, ์ตœ์†Œํ•œ์˜ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๋Š” ๊ฒƒ์ด Deno์˜ ๋ณด์•ˆ ์ฒ ํ•™์„ ์ œ๋Œ€๋กœ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด์•ผ!

์—ฌ๊ธฐ๊นŒ์ง€ ์ž˜ ๋”ฐ๋ผ์™”๋‹ค๋ฉด, ๋„Œ ์ด๋ฏธ Deno์™€ ํ•œ์ธต ๊ฐ€๊นŒ์›Œ์ง„ ๊ฑฐ์•ผ.
์„ค์น˜๋ถ€ํ„ฐ ์‹คํ–‰, ๊ทธ๋ฆฌ๊ณ  Deno์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์ธ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ๋„ค์ดํ‹ฐ๋ธŒ ์ง€์›๊ณผ ๋ณด์•ˆ ์ƒŒ๋“œ๋ฐ•์Šค๊นŒ์ง€ ๋ชจ๋‘ ๊ฒฝํ—˜ํ•ด๋ดค์–ด. ๋‹ค์Œ ์ฑ•ํ„ฐ์—์„œ๋Š” Deno์˜ ์˜ํ˜ผ์˜ ๋‹จ์ง, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์–ด๋–ป๊ฒŒ ๋” ๊ธฐ๊น”๋‚˜๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๊นŠ์ด ํŒŒ๊ณ ๋“ค์–ด ๋ณผ๊ฒŒ. ๊ธฐ๋Œ€ํ•ด๋„ ์ข‹์•„! ๐Ÿ˜‰

๐Ÿ’– ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์™€ Deno: ์ด๊ฑด ์šด๋ช…์ด์•ผ! (ํ™˜์ƒ์˜ ์‹œ๋„ˆ์ง€)

์•ž์—์„œ Deno๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋„ค์ดํ‹ฐ๋ธŒ๋กœ ์ง€์›ํ•œ๋‹ค๊ณ  ํ–ˆ์ง€?
์ด๊ฒŒ ๊ทธ๋ƒฅ "ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค" ์ •๋„์˜ ์˜๋ฏธ๊ฐ€ ์•„๋‹ˆ์•ผ. Deno์˜ ๋ชจ๋“  ์„ค๊ณ„๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ตœ์šฐ์„ ์œผ๋กœ ๊ณ ๋ คํ•ด์„œ ๋งŒ๋“ค์–ด์กŒ์–ด. Deno์™€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋งŒ๋‚˜๋ฉด ์–ด๋–ค ๋งˆ๋ฒ• ๊ฐ™์€ ์ผ์ด ๋ฒŒ์–ด์ง€๋Š”์ง€, ์™œ ์ด ์กฐํ•ฉ์ด '์šด๋ช…'์ด๋ผ๊ณ ๊นŒ์ง€ ๋ถˆ๋ฆฌ๋Š”์ง€ ํ•œ๋ฒˆ ์ œ๋Œ€๋กœ ์•Œ์•„๋ณด์ž.

1. ์„ค์ • ์ง€์˜ฅ์—์„œ์˜ ํ•ด๋ฐฉ: `tsconfig.json`์ด ํ•„์š” ์—†๋‹ค!

Node.js์—์„œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•  ๋•Œ ๊ฐ€์žฅ ๋จผ์ € ํ•˜๋Š” ์ผ์ด ๋ญ์•ผ?
๋ฐ”๋กœ `tsconfig.json` ํŒŒ์ผ์„ ๋งŒ๋“œ๋Š” ๊ฑฐ์ง€. ์ด ํŒŒ์ผ ์•ˆ์—๋Š” `target`, `module`, `strict`, `esModuleInterop`, `outDir` ๋“ฑ๋“ฑ... ์ˆ˜๋งŽ์€ ์˜ต์…˜๋“ค์ด ์šฐ๋ฆฌ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์žˆ์–ด. ๊ฐ ์˜ต์…˜์ด ๋ญ˜ ์˜๋ฏธํ•˜๋Š”์ง€, ๋‚ด ํ”„๋กœ์ ํŠธ์—๋Š” ์–ด๋–ค ๊ฐ’์„ ์„ค์ •ํ•ด์•ผ ํ•˜๋Š”์ง€ ์•Œ์•„๋‚ด๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ํ•˜๋ฃจ๊ฐ€ ๋‹ค ๊ฐ€. ๐Ÿคฏ

Deno๋Š” ์ด ๊ณ ํ†ต์„ ์™„์ „ํžˆ ์—†์• ๋ฒ„๋ ธ์–ด.
Deno๋Š” ์ด๋ฏธ ์›น ํ‘œ์ค€๊ณผ ์ตœ์‹  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ(ECMAScript) ๊ธฐ๋Šฅ์— ์ตœ์ ํ™”๋œ ๊ธฐ๋ณธ ์ปดํŒŒ์ผ๋Ÿฌ ์˜ต์…˜์„ ๋‚ด์žฅํ•˜๊ณ  ์žˆ์–ด. ๊ทธ๋ž˜์„œ ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ `tsconfig.json` ํŒŒ์ผ์ด ์•„์˜ˆ ํ•„์š”๊ฐ€ ์—†์–ด.

๊ทธ๋ƒฅ `.ts` ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ , ์ตœ์‹  ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ๋ฌธ๋ฒ•์œผ๋กœ ๋งˆ์Œ๊ป ์ฝ”๋”ฉํ•˜๋ฉด ๋ผ. Deno๊ฐ€ ์•Œ์•„์„œ ๋‹ค ์ฒ˜๋ฆฌํ•ด์ค„ ํ…Œ๋‹ˆ๊นŒ. ์ด๊ฑด ๋งˆ์น˜ ์šด์ „๋ฉดํ—ˆ๋ฅผ ๋”ฐ์ž๋งˆ์ž ํ’€์˜ต์…˜ ์ž์œจ์ฃผํ–‰ ์ž๋™์ฐจ๋ฅผ ๋ฐ›์€ ๊ธฐ๋ถ„์ด์•ผ. ์šฐ๋ฆฌ๋Š” ๊ทธ๋ƒฅ ๋ชฉ์ ์ง€๋งŒ ์ž…๋ ฅํ•˜๋ฉด ๋˜๋Š” ๊ฑฐ์ง€.

๐Ÿ’ก ๊ทธ๋ž˜๋„ ์ปค์Šคํ…€ ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค๋ฉด?

๋ฌผ๋ก  ์•„์ฃผ ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ, ์˜ˆ๋ฅผ ๋“ค์–ด JSX(React ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์‚ฌ์šฉํ•˜๋Š”)์˜ ๋™์ž‘ ๋ฐฉ์‹์„ ๋ฐ”๊พธ๊ฑฐ๋‚˜ ์‹คํ—˜์ ์ธ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๊ธฐ๋Šฅ์„ ์“ฐ๊ณ  ์‹ถ์„ ๋•Œ๋Š” ์ปดํŒŒ์ผ๋Ÿฌ ์˜ต์…˜์„ ์ˆ˜์ •ํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ์–ด. ๊ทธ๋Ÿด ๋• ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์— `deno.json` ๋˜๋Š” `deno.jsonc` ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  `compilerOptions` ํ‚ค๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ์›ํ•˜๋Š” ์„ค์ •์„ ๋ฎ์–ด์“ธ ์ˆ˜ ์žˆ์–ด. ํ•˜์ง€๋งŒ ์ด๊ฑด ์ •๋ง ์˜ˆ์™ธ์ ์ธ ๊ฒฝ์šฐ๊ณ , 95% ์ด์ƒ์˜ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ํ•„์š” ์—†๋‹ค๋Š” ์‚ฌ์‹ค!


// deno.jsonc
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "react"
  }
}
        

2. ์™„๋ฒฝํ•˜๊ฒŒ ํƒ€์ดํ•‘๋œ Deno API: ์ž๋™์™„์„ฑ์˜ ์ถ•๋ณต

Deno์˜ ๋˜ ๋‹ค๋ฅธ ๊ฐ•๋ ฅํ•จ์€ ๋Ÿฐํƒ€์ž„ ์ž์ฒด์˜ ๋ชจ๋“  API๊ฐ€ ์ฒ˜์Œ๋ถ€ํ„ฐ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋กœ ์ž‘์„ฑ๋˜๊ณ  ์™„๋ฒฝํ•˜๊ฒŒ ํƒ€์ดํ•‘(Typing)๋˜์–ด ์žˆ๋‹ค๋Š” ์ ์ด์•ผ. ์ด๊ฒŒ ์™œ ์ค‘์š”ํ•˜๋ƒ๋ฉด, ์šฐ๋ฆฌ๊ฐ€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ๊ฐœ๋ฐœ ๋„๊ตฌ(ํŠนํžˆ VS Code)์˜ ์—„์ฒญ๋‚œ ๋„์›€์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด์•ผ.

์˜ˆ๋ฅผ ๋“ค์–ด, Deno๋กœ ํŒŒ์ผ์„ ์ฝ๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค๊ณ  ์ƒ์ƒํ•ด๋ด.
์—๋””ํ„ฐ์— `Deno.` ์ด๋ผ๊ณ  ์ž…๋ ฅํ•˜๋Š” ์ˆœ๊ฐ„, Deno๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ชจ๋“  ๊ธฐ๋Šฅ๋“ค(`readFile`, `writeFile`, `serve`, `test` ๋“ฑ)์ด ๋ชฉ๋ก์œผ๋กœ ์ซ™ ๋– . ๊ทธ๋ฆฌ๊ณ  `Deno.readFile`์„ ์„ ํƒํ•˜๋ฉด, ์ด ํ•จ์ˆ˜๊ฐ€ ์–ด๋–ค ์ธ์ž(ํŒŒ์ผ ๊ฒฝ๋กœ)๋ฅผ ํ•„์š”๋กœ ํ•˜๊ณ , ์–ด๋–ค ํƒ€์ž…(Promise<Uint8Array>)์˜ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ณด์—ฌ์ค˜.

// Deno API๋Š” ์™„๋ฒฝํ•˜๊ฒŒ ํƒ€์ดํ•‘๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. const filePath = './readme.md'; const data = await Deno.readFile(filePath); // 'Deno.'์„ ์ž…๋ ฅํ•˜๋Š” ์ˆœ๊ฐ„... (property) Deno.readFile: (path: string | URL) => Promise<Uint8Array> readFile Reads the entire contents of a file. @param path The path of the file to read. @returns A promise that resolves with the file contents as a Uint8Array. Deno.readFile(path: string | URL): Promise<Uint8Array>

์ด๊ฑด ๊ทธ๋ƒฅ ํŽธ์˜ ๊ธฐ๋Šฅ์ด ์•„๋‹ˆ์•ผ. ๊ฐœ๋ฐœ์˜ ํŒจ๋Ÿฌ๋‹ค์ž„์„ ๋ฐ”๊พธ๋Š” ๊ฑฐ์•ผ.
๋” ์ด์ƒ ๋ฌธ์„œ๋ฅผ ์ฐพ์•„ ํ—ค๋งฌ ํ•„์š”๊ฐ€ ์—†๊ณ , ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ๊ฐ’์ด ๋ญ”์ง€ ๋ชฐ๋ผ์„œ `console.log`๋ฅผ ์ฐ์–ด๋ณผ ํ•„์š”๋„ ์—†์–ด. ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋™์‹œ์— ์‹ค์ˆ˜๊ฐ€ ์ค„์–ด๋“ค๊ณ , ์ƒ์‚ฐ์„ฑ์€ ํญ๋ฐœ์ ์œผ๋กœ ์ฆ๊ฐ€ํ•ด. ๋งˆ์น˜ ๋‚ด๋น„๊ฒŒ์ด์…˜์ด ์‹ค์‹œ๊ฐ„ ๊ตํ†ต ์ •๋ณด๋ฅผ ๋ฐ˜์˜ํ•ด์„œ ์ตœ์ ์˜ ๊ฒฝ๋กœ๋ฅผ ์•ˆ๋‚ดํ•ด์ฃผ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์•„.

Node.js์—์„œ๋Š” `@types/node`๋ผ๋Š” ๋ณ„๋„์˜ ํƒ€์ž… ์ •์˜ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ด์•ผ ์ด๋Ÿฐ ๊ธฐ๋Šฅ์„ ์˜จ์ „ํžˆ ๋ˆ„๋ฆด ์ˆ˜ ์žˆ์—ˆ์ง€๋งŒ, Deno์—์„œ๋Š” ์ด ๋ชจ๋“  ๊ฒƒ์ด ๊ธฐ๋ณธ์ด์•ผ. Deno๋Š” ํƒœ์–ด๋‚  ๋•Œ๋ถ€ํ„ฐ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ DNA๋ฅผ ๊ฐ€์ง€๊ณ  ํƒœ์–ด๋‚œ ๊ฑฐ์ง€.

3. ์›๊ฒฉ ํƒ€์ž… ์ฒดํฌ: URL๋กœ ์ž„ํฌํŠธํ•œ ๋ชจ๋“ˆ๋„ ์•ˆ์ „ํ•˜๊ฒŒ

Deno์˜ ๊ฐ€์žฅ ํฐ ํŠน์ง• ์ค‘ ํ•˜๋‚˜๊ฐ€ URL๋กœ ๋ชจ๋“ˆ์„ ์ž„ํฌํŠธํ•˜๋Š” ๊ฑฐ๋ผ๊ณ  ํ–ˆ์ง€?
์—ฌ๊ธฐ์„œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์™€์˜ ์‹œ๋„ˆ์ง€๊ฐ€ ๋˜ ํ•œ ๋ฒˆ ํญ๋ฐœํ•ด. Deno๋Š” URL๋กœ ๊ฐ€์ ธ์˜จ ์›๊ฒฉ ๋ชจ๋“ˆ์˜ ํƒ€์ž… ์ •๋ณด๊นŒ์ง€ ์™„๋ฒฝํ•˜๊ฒŒ ๋ถ„์„ํ•˜๊ณ  ์ฒดํฌํ•ด์ค˜.

์˜ˆ๋ฅผ ๋“ค์–ด, ์œ ๋ช…ํ•œ Deno ์›น ํ”„๋ ˆ์ž„์›Œํฌ์ธ `Oak`๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ํ•ด๋ณด์ž.


import { Application, Router } from "https://deno.land/x/oak/mod.ts";

const app = new Application();
const router = new Router();

router.get("/", (ctx) => {
  // ctx ๊ฐ์ฒด์— ๋ญ๊ฐ€ ๋“ค์–ด์žˆ๋Š”์ง€ ์–ด๋–ป๊ฒŒ ์•Œ์ง€?
  // ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ๋•๋ถ„์— ctx์˜ ํƒ€์ž…์„ ์™„๋ฒฝํ•˜๊ฒŒ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค!
  ctx.response.body = "Hello Oak!";
});

// ๋งŒ์•ฝ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์†์„ฑ์— ์ ‘๊ทผํ•˜๋ ค๊ณ  ํ•˜๋ฉด?
router.get("/error", (ctx) => {
  // ctx.response.boddy = "Oops!"; // 'boddy'๋ผ๋Š” ์†์„ฑ์€ ์—†์–ด์š”! -> ํƒ€์ž… ์—๋Ÿฌ ๋ฐœ์ƒ!
});

app.use(router.routes());
app.use(router.allowedMethods());

await app.listen({ port: 8000 });
    

์œ„ ์ฝ”๋“œ์—์„œ `ctx`๋ผ๋Š” ์ปจํ…์ŠคํŠธ ๊ฐ์ฒด๋ฅผ ๋ด๋ด.
์šฐ๋ฆฌ๋Š” `oak` ๋ชจ๋“ˆ์˜ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ๋ณด์ง€ ์•Š์•˜์ง€๋งŒ, Deno์™€ VS Code๋Š” `https://deno.land/x/oak/mod.ts` ํŒŒ์ผ์„ ๋ถ„์„ํ•ด์„œ `ctx` ๊ฐ์ฒด๊ฐ€ ์–ด๋–ค ์†์„ฑ(`request`, `response` ๋“ฑ)๊ณผ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€ ์ •ํ™•ํžˆ ์•Œ๊ณ  ์žˆ์–ด. ๊ทธ๋ž˜์„œ `ctx.response.body`๋Š” ์ž๋™์™„์„ฑ์ด ๋˜์ง€๋งŒ, ๋งŒ์•ฝ ์šฐ๋ฆฌ๊ฐ€ `ctx.response.boddy`๋ผ๊ณ  ์˜คํƒ€๋ฅผ ๋‚ด๋ฉด ์ฆ‰์‹œ "๊ทธ๋Ÿฐ ์†์„ฑ์€ `response` ๊ฐ์ฒด์— ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."๋ผ๊ณ  ๋นจ๊ฐ„ ์ค„์„ ๊ทธ์–ด์ค˜.

์ด๊ฑด ์ •๋ง ๋Œ€๋‹จํ•œ ๊ธฐ๋Šฅ์ด์•ผ. ์ธํ„ฐ๋„ท ๋„ˆ๋จธ์— ์žˆ๋Š” ์ฝ”๋“œ๊นŒ์ง€ ๋งˆ์น˜ ๋‚ด ๋กœ์ปฌ ํ”„๋กœ์ ํŠธ์— ์žˆ๋Š” ์ฝ”๋“œ์ฒ˜๋Ÿผ ํƒ€์ž…์„ ์™„๋ฒฝํ•˜๊ฒŒ ๊ฒ€์‚ฌํ•ด์ฃผ๋Š” ๊ฑฐ์ง€. ๋•๋ถ„์— ์šฐ๋ฆฌ๋Š” `npm`์—์„œ ๋‹ค์šด๋กœ๋“œํ•œ ํŒจํ‚ค์ง€๋“ , URL๋กœ ๊ฐ€์ ธ์˜จ ๋ชจ๋“ˆ์ด๋“  ์ƒ๊ด€์—†์ด ์ผ๊ด€๋˜๊ณ  ์•ˆ์ •์ ์ธ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์–ด. ์žฌ๋Šฅ์„ ๊ณต์œ ํ•˜๋Š” ํ”Œ๋žซํผ **์žฌ๋Šฅ๋„ท**์—์„œ ๋‹ค์–‘ํ•œ ์ „๋ฌธ๊ฐ€๋“ค์˜ ์Šคํ‚ฌ์„ ๋ฏฟ๊ณ  ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, Deno์—์„œ๋Š” ์ „ ์„ธ๊ณ„ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋งŒ๋“  ๋ชจ๋“ˆ์„ ํƒ€์ž… ๊ฑฑ์ • ์—†์ด ์•ˆ์ „ํ•˜๊ฒŒ ๊ฐ€์ ธ๋‹ค ์“ธ ์ˆ˜ ์žˆ๋Š” ๊ฑฐ์•ผ.

4. `deno test`์™€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ: ๋” ๊ฒฌ๊ณ ํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ

Deno์— ๋‚ด์žฅ๋œ ํ…Œ์ŠคํŠธ ๋Ÿฌ๋„ˆ `deno test` ์—ญ์‹œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์™€ ํ™˜์ƒ์ ์ธ ๊ถํ•ฉ์„ ์ž๋ž‘ํ•ด.
ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋„ ๋‹น์—ฐํžˆ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ณ , ์ด๋ฅผ ํ†ตํ•ด ํ›จ์”ฌ ๋” ๋ช…ํ™•ํ•˜๊ณ  ๊ฒฌ๊ณ ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด.

์˜ˆ๋ฅผ ๋“ค์–ด, ๋‘ ์ˆซ์ž๋ฅผ ๋”ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ํ•จ์ˆ˜๋ฅผ ํ…Œ์ŠคํŠธํ•œ๋‹ค๊ณ  ํ•ด๋ณด์ž.


// add.ts
export function add(a: number, b: number): number {
  return a + b;
}

// add.test.ts
import { assertEquals } from "https://deno.land/std@0.224.0/assert/mod.ts";
import { add } from "./add.ts";

Deno.test("add ํ•จ์ˆ˜๋Š” ๋‘ ์ˆซ์ž๋ฅผ ์ •ํ™•ํžˆ ๋”ํ•ด์•ผ ํ•œ๋‹ค", () => {
  const result = add(2, 3);
  assertEquals(result, 5);
});

Deno.test("add ํ•จ์ˆ˜์— ๋ฌธ์ž๋ฅผ ๋„ฃ์œผ๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?", () => {
  // const result = add(2, "3"); // ์ด ์ค„์€ ํƒ€์ž… ์—๋Ÿฌ ๋•Œ๋ฌธ์— ์ปดํŒŒ์ผ์กฐ์ฐจ ๋˜์ง€ ์•Š๋Š”๋‹ค!
  // ๋•๋ถ„์— ์ž˜๋ชป๋œ ํ…Œ์ŠคํŠธ๋ฅผ ์‚ฌ์ „์— ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
});
    

ํ„ฐ๋ฏธ๋„์—์„œ `deno test`๋ฅผ ์‹คํ–‰ํ•˜๋ฉด Deno๋Š” `*.test.ts`, `*_test.ts` ๊ฐ™์€ ํŒจํ„ด์˜ ํŒŒ์ผ์„ ์ฐพ์•„์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ด์ค˜. ์œ„ ์ฝ”๋“œ์—์„œ ์ค‘์š”ํ•œ ์ ์€ `add` ํ•จ์ˆ˜๋Š” `number` ํƒ€์ž…์˜ ์ธ์ž๋งŒ ๋ฐ›๋„๋ก ์ •์˜๋˜์–ด ์žˆ๋‹ค๋Š” ๊ฑฐ์•ผ. ๋งŒ์•ฝ ์šฐ๋ฆฌ๊ฐ€ ์‹ค์ˆ˜๋กœ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ `add(2, "3")`์ฒ˜๋Ÿผ ์ž˜๋ชป๋œ ํƒ€์ž…์˜ ์ธ์ž๋ฅผ ์ „๋‹ฌํ•˜๋ ค๊ณ  ํ•˜๋ฉด, ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ๋„ ์ „์— ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ "์ด๋ด, `add` ํ•จ์ˆ˜๋Š” ์ˆซ์ž๋ฅผ ๊ธฐ๋Œ€ํ•˜๊ณ  ์žˆ๋Š”๋ฐ ์™œ ๋ฌธ์ž๋ฅผ ๋„ฃ๋Š” ๊ฑฐ์•ผ?"๋ผ๋ฉฐ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ.

์ด์ฒ˜๋Ÿผ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ์‹ ๋ขฐ๋„๋ฅผ ๋†’์—ฌ์ฃผ๊ณ , ์ž˜๋ชป๋œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์‹ค์ˆ˜๋ฅผ ์›์ฒœ์ ์œผ๋กœ ์ฐจ๋‹จํ•ด์ค˜. ๋ฒ„๊ทธ๋ฅผ ์žก๊ธฐ ์œ„ํ•ด ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๋Š” ๊ฑด๋ฐ, ๊ทธ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž์ฒด์— ๋ฒ„๊ทธ๊ฐ€ ์žˆ๋‹ค๋ฉด ์ •๋ง ๋‚œ๊ฐํ•˜๊ฒ ์ง€? Deno์™€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ์ด๋Ÿฐ ์ƒํ™ฉ์„ ๋ฏธ์—ฐ์— ๋ฐฉ์ง€ํ•ด์ฃผ๋Š” ์ตœ๊ณ ์˜ ์•ˆ์ „์žฅ์น˜์•ผ.

๊ฒฐ๋ก ์ ์œผ๋กœ, Deno์—๊ฒŒ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๊ทธ์ € '์ง€์›ํ•˜๋Š” ์–ธ์–ด' ์ค‘ ํ•˜๋‚˜๊ฐ€ ์•„๋‹ˆ์•ผ.
Deno์˜ ๋ณด์•ˆ, ๋ชจ๋“ˆ ์‹œ์Šคํ…œ, ๋‚ด์žฅ ๋„๊ตฌ ๋“ฑ ๋ชจ๋“  ํ•ต์‹ฌ ๊ธฐ๋Šฅ์ด ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์™€ ์œ ๊ธฐ์ ์œผ๋กœ ๊ฒฐํ•ฉํ•˜์—ฌ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์ตœ๊ณ ์˜ ๊ฒฝํ—˜์„ ์„ ์‚ฌํ•˜๋„๋ก ์„ค๊ณ„๋˜์—ˆ์–ด. Deno๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์€ ๊ณง ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ํž˜์„ 200% ํ™œ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ์˜๋ฏธ์•ผ. ์ด ํ™˜์ƒ์ ์ธ ์กฐํ•ฉ, ์•ˆ ์จ๋ณผ ์ด์œ ๊ฐ€ ์—†๊ฒ ์ง€? ๐Ÿ˜‰

๐Ÿ“š Deno ์ƒํƒœ๊ณ„ ํƒํ—˜: ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ์„œ๋“œํŒŒํ‹ฐ ๋ชจ๋“ˆ

์ข‹์€ ๋Ÿฐํƒ€์ž„์€ ๊ทธ ์ž์ฒด๋กœ๋„ ํ›Œ๋ฅญํ•ด์•ผ ํ•˜์ง€๋งŒ, ๊ฒฐ๊ตญ ์–ผ๋งˆ๋‚˜ ํ’๋ถ€ํ•˜๊ณ  ์“ธ๋งŒํ•œ '๋ถ€ํ’ˆ(๋ชจ๋“ˆ)'๋“ค์„ ์ œ๊ณตํ•˜๋А๋ƒ์— ๋”ฐ๋ผ ์„ฑํŒจ๊ฐ€ ๊ฐˆ๋ ค. Node.js๊ฐ€ ์„ฑ๊ณตํ•  ์ˆ˜ ์žˆ์—ˆ๋˜ ๊ฐ€์žฅ ํฐ ์ด์œ  ์ค‘ ํ•˜๋‚˜๋Š” `npm`์ด๋ผ๋Š” ๊ฑฐ๋Œ€ํ•œ ์ƒํƒœ๊ณ„ ๋•๋ถ„์ด์—ˆ์ง€. Deno๋Š” ์ด ์ƒํƒœ๊ณ„ ๋ฌธ์ œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ’€๊ณ  ์žˆ์„๊นŒ? Deno์˜ ๋ฌด๊ธฐ๊ณ , ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ์„œ๋“œํŒŒํ‹ฐ ๋ชจ๋“ˆ์˜ ์„ธ๊ณ„๋กœ ๋– ๋‚˜๋ณด์ž. ๐Ÿ—บ๏ธ

1. Deno์˜ ๊ณต์‹ ๋ณด๋ฌผ์ฐฝ๊ณ : ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (Deno Standard Library)

Deno๋Š” `deno.land/std` ๋ผ๋Š” ํŠน๋ณ„ํ•œ ์ฃผ์†Œ๋ฅผ ํ†ตํ•ด ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(Standard Library)๋ฅผ ์ œ๊ณตํ•ด. ์ด๊ฑด Deno ํŒ€์ด ์ง์ ‘ ๋งŒ๋“ค๊ณ  ๊ด€๋ฆฌํ•˜๋Š”, ํ’ˆ์งˆ์ด ๋ณด์žฅ๋œ ๊ณต์‹ ๋ชจ๋“ˆ ๋ชจ์Œ์ง‘์ด์•ผ. Node.js์˜ `fs`, `path`, `http` ๊ฐ™์€ ๋‚ด์žฅ ๋ชจ๋“ˆ๊ณผ ๋น„์Šทํ•œ ์—ญํ• ์„ ํ•˜์ง€๋งŒ, ๋ช‡ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ์ฐจ์ด์ ์ด ์žˆ์–ด.

  • ์™ธ๋ถ€ ๋ชจ๋“ˆ ํ˜•ํƒœ: Node.js์˜ ๋‚ด์žฅ ๋ชจ๋“ˆ์€ ๋Ÿฐํƒ€์ž„์— ํฌํ•จ๋˜์–ด ์žˆ์ง€๋งŒ, Deno์˜ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์™ธ๋ถ€ ๋ชจ๋“ˆ์ฒ˜๋Ÿผ URL๋กœ ์ž„ํฌํŠธํ•ด์„œ ์‚ฌ์šฉํ•ด. ์ด๋Š” Deno ๋Ÿฐํƒ€์ž„ ์ž์ฒด์˜ ํฌ๊ธฐ๋ฅผ ์ž‘๊ณ  ๊ฐ€๋ณ๊ฒŒ ์œ ์ง€ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ผ.
  • ๋ฒ„์ „ ๋ช…์‹œ: URL์— @0.224.0 ์ฒ˜๋Ÿผ ๋ฒ„์ „์„ ๋ช…์‹œํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•ด. ์ด๋ฅผ ํ†ตํ•ด ๋‚ด ํ”„๋กœ์ ํŠธ๊ฐ€ ํŠน์ • ๋ฒ„์ „์˜ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์˜์กดํ•˜๋„๋ก ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜์–ด๋„ ๋‚ด ์ฝ”๋“œ๊ฐ€ ๊ฐ‘์ž๊ธฐ ๋ง๊ฐ€์ง€๋Š” ์ผ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์ง€. (๋ฒ„์ „ ๊ด€๋ฆฌ๊ฐ€ ํ›จ์”ฌ ๋ช…ํ™•ํ•ด!)
  • ์—„๊ฒฉํ•œ ์ฝ”๋“œ ๋ฆฌ๋ทฐ: Deno์˜ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ํฌํ•จ๋˜๋Š” ๋ชจ๋“  ์ฝ”๋“œ๋Š” Deno ํ•ต์‹ฌ ํŒ€์˜ ์ฒ ์ €ํ•œ ๋ฆฌ๋ทฐ๋ฅผ ๊ฑฐ์ณ. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” ์ด ๋ชจ๋“ˆ๋“ค์„ ์•ˆ์‹ฌํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด.

ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—๋Š” ์ •๋ง ์œ ์šฉํ•œ ๊ฒƒ๋“ค์ด ๋งŽ์•„. ๋ช‡ ๊ฐ€์ง€๋งŒ ๋ง›๋ณด๊ธฐ๋กœ ๋ณผ๊นŒ?


// ํŒŒ์ผ ์‹œ์Šคํ…œ ๋‹ค๋ฃจ๊ธฐ (fs)
import { ensureDir } from "https://deno.land/std@0.224.0/fs/ensure_dir.ts";
await ensureDir("./my_dir"); // my_dir ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์—†์œผ๋ฉด ๋งŒ๋“ค์–ด์ค˜!

// ๊ฒฝ๋กœ ๋‹ค๋ฃจ๊ธฐ (path)
import { join } from "https://deno.land/std@0.224.0/path/mod.ts";
const fullPath = join("/users/deno", "project", "file.ts"); // OS์— ๋งž์ถฐ ๊ฒฝ๋กœ๋ฅผ ํ•ฉ์ณ์ค˜.

// HTTP ์„œ๋ฒ„ ๋งŒ๋“ค๊ธฐ (http)
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
// ์ด๊ฑด ์•ž์—์„œ ์ด๋ฏธ ์จ๋ดค์ง€?

// ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋‹ค๋ฃจ๊ธฐ (dotenv)
import { load } from "https://deno.land/std@0.224.0/dotenv/mod.ts";
const env = await load(); // .env ํŒŒ์ผ์˜ ๋ณ€์ˆ˜๋“ค์„ ๊ฐ์ฒด๋กœ ๋ถˆ๋Ÿฌ์™€์ค˜.
const apiKey = env["API_KEY"];

// Assertion (ํ…Œ์ŠคํŠธํ•  ๋•Œ ์œ ์šฉ)
import { assertEquals } from "https://deno.land/std@0.224.0/assert/mod.ts";
assertEquals(1 + 1, 2);
    

์ด ์™ธ์—๋„ CSV ํŒŒ์‹ฑ, ์›น์†Œ์ผ“, UUID ์ƒ์„ฑ ๋“ฑ ๊ฐœ๋ฐœ์— ๊ผญ ํ•„์š”ํ•œ ๋Œ€๋ถ€๋ถ„์˜ ๊ธฐ๋Šฅ๋“ค์ด ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ํฌํ•จ๋˜์–ด ์žˆ์–ด. ์›ฌ๋งŒํ•œ ๊ธฐ๋Šฅ์€ ์™ธ๋ถ€ ๋ชจ๋“ˆ์„ ์ฐพ๊ธฐ ์ „์— ๋จผ์ € ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์žˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณด๋Š” ์Šต๊ด€์„ ๋“ค์ด๋Š” ๊ฒŒ ์ข‹์•„. ๋งˆ์น˜ ์š”๋ฆฌ๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ๋ƒ‰์žฅ๊ณ ์— ์žˆ๋Š” ๊ธฐ๋ณธ ์žฌ๋ฃŒ๋ถ€ํ„ฐ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด์•ผ. ๐Ÿ˜‰

2. ์ „ ์„ธ๊ณ„ ๊ฐœ๋ฐœ์ž๋“ค์˜ ๋†€์ดํ„ฐ: ์„œ๋“œํŒŒํ‹ฐ ๋ชจ๋“ˆ (deno.land/x)

ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๊ณต์‹ ๋ ˆ์‹œํ”ผ๋ผ๋ฉด, ์„œ๋“œํŒŒํ‹ฐ ๋ชจ๋“ˆ์€ ์ „ ์„ธ๊ณ„ ์…ฐํ”„๋“ค์ด ๊ฐ์ž์˜ ๋น„๋ฒ•์„ ๊ณต์œ ํ•˜๋Š” ๊ฑฐ๋Œ€ํ•œ ๋ ˆ์‹œํ”ผ ๋ถ๊ณผ ๊ฐ™์•„. Deno๋Š” `deno.land/x` ๋ผ๋Š” ๊ณต์‹ ์„œ๋“œํŒŒํ‹ฐ ๋ชจ๋“ˆ ํ˜ธ์ŠคํŒ… ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•ด. `npm`๊ณผ ๋น„์Šทํ•œ ์—ญํ• ์„ ํ•˜์ง€๋งŒ, ํŒจํ‚ค์ง€๋ฅผ ์ง์ ‘ ์ €์žฅํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ GitHub ๊ฐ™์€ ๊ณณ์— ์žˆ๋Š” ์ฝ”๋“œ ์ €์žฅ์†Œ์— ๋Œ€ํ•œ '๋ฐ”๋กœ๊ฐ€๊ธฐ'๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ์‹์ด์•ผ.

`deno.land/x`์— ์˜ฌ๋ผ์˜จ ๋ชจ๋“ˆ๋“ค์€ ๋ˆ„๊ตฌ๋‚˜ ์‰ฝ๊ฒŒ URL๋กœ ๊ฐ€์ ธ๋‹ค ์“ธ ์ˆ˜ ์žˆ์–ด.


// Express.js์™€ ๋น„์Šทํ•œ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๋Š” ์›น ํ”„๋ ˆ์ž„์›Œํฌ, Oak
import { Application } from "https://deno.land/x/oak@v16.0.0/mod.ts";

// ORM(๊ฐ์ฒด ๊ด€๊ณ„ ๋งคํ•‘) ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, DenoDB
import { Database, Model, DataTypes } from "https://deno.land/x/denodb@v1.4.0/mod.ts";
    

`deno.land/x`์˜ ์žฅ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•„.

  • ์‹ ๋ขฐ์„ฑ: GitHub ์ €์žฅ์†Œ์˜ ํƒœ๊ทธ(๋ฒ„์ „)๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ํ•œ๋ฒˆ ๋“ฑ๋ก๋œ ๋ฒ„์ „์˜ ์ฝ”๋“œ๋Š” ์ ˆ๋Œ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•„. (Immutable)
  • ๊ฐ€๋…์„ฑ: `deno.land/x/oak` ์ฒ˜๋Ÿผ ์งง๊ณ  ๊ธฐ์–ตํ•˜๊ธฐ ์‰ฌ์šด URL์„ ์ œ๊ณตํ•ด.
  • ๊ฒ€์ƒ‰ ๋ฐ ๋ฌธ์„œ: ์›น์‚ฌ์ดํŠธ์—์„œ ๋ชจ๋“ˆ์„ ์‰ฝ๊ฒŒ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๊ณ , ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ ๋ฌธ์„œ๋ฅผ ๋ฐ”๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด.
๐Ÿค” ์ž ๊น, ๊ทธ๋Ÿผ npm ํŒจํ‚ค์ง€๋Š” ๋ชป ์“ฐ๋Š” ๊ฑฐ์•ผ?

์˜ˆ์ „์—๋Š” ๊ทธ๋žฌ์–ด. ์ด๊ฒŒ Deno์˜ ๊ฐ€์žฅ ํฐ ์•ฝ์  ์ค‘ ํ•˜๋‚˜์˜€์ง€. ํ•˜์ง€๋งŒ Deno ํŒ€๋„ ์ด ๋ฌธ์ œ๋ฅผ ์•Œ๊ณ  ์žˆ์—ˆ๊ณ , ์•„์ฃผ ๋ฉ‹์ง„ ํ•ด๊ฒฐ์ฑ…์„ ๋‚ด๋†จ์–ด! ๋ฐ”๋กœ npm specifier์•ผ. ์ด์ œ Deno ์ฝ”๋“œ์—์„œ `npm:` ์ ‘๋‘์‚ฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ˆ˜๋งŽ์€ npm ํŒจํ‚ค์ง€๋ฅผ ์ง์ ‘ ์ž„ํฌํŠธํ•  ์ˆ˜ ์žˆ์–ด!


// npm์— ์žˆ๋Š” ์œ ๋ช…ํ•œ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, lodash๋ฅผ Deno์—์„œ ๋ฐ”๋กœ ์‚ฌ์šฉํ•˜๊ธฐ
import lodash from "npm:lodash";

console.log(lodash.capitalize("hello world")); // -> "Hello world"

// React๋„ Deno์—์„œ ๋ฐ”๋กœ ์“ธ ์ˆ˜ ์žˆ์–ด!
import React from "npm:react";
import ReactDOM from "npm:react-dom/client";
        

์ด ๊ธฐ๋Šฅ ๋•๋ถ„์— Deno๋Š” ๊ฑฐ๋Œ€ํ•œ Node.js/npm ์ƒํƒœ๊ณ„๋ฅผ ํ’ˆ์— ์•ˆ๊ฒŒ ๋˜์—ˆ์–ด. Deno์˜ ์žฅ์ ์€ ๊ทธ๋Œ€๋กœ ๋ˆ„๋ฆฌ๋ฉด์„œ, ํ•„์š”ํ•  ๋• ๊ฒ€์ฆ๋œ npm ํŒจํ‚ค์ง€๋ฅผ ๊ฐ€์ ธ๋‹ค ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ๋œ ๊ฑฐ์ง€. ์ด๊ฑด ์ •๋ง '๊ฒŒ์ž„ ์ฒด์ธ์ €'๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์–ด. ๐Ÿš€

3. ํ”„๋กœ์ ํŠธ์˜ ์ฒญ์‚ฌ์ง„: `deno.json`๊ณผ Import Maps

URL๋กœ ๋ชจ๋“ˆ์„ ์ž„ํฌํŠธํ•˜๋Š” ๋ฐฉ์‹์€ ์ง๊ด€์ ์ด์ง€๋งŒ, ํ”„๋กœ์ ํŠธ๊ฐ€ ์ปค์ง€๋ฉด ๋ช‡ ๊ฐ€์ง€ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ์–ด.
๋ชจ๋“  ํŒŒ์ผ์— ๊ธธ๊ณ  ๋ณต์žกํ•œ URL์„ ๋ฐ˜๋ณตํ•ด์„œ ์จ์•ผ ํ•˜๊ณ , ์˜์กดํ•˜๋Š” ๋ชจ๋“ˆ์˜ ๋ฒ„์ „์„ ํ•œ๊บผ๋ฒˆ์— ์—…๋ฐ์ดํŠธํ•˜๊ธฐ๋„ ๊นŒ๋‹ค๋กญ์ง€. `package.json`์ด ๊ทธ๋ฆฌ์›Œ์ง€๋Š” ์ˆœ๊ฐ„์ด์•ผ.

Deno๋Š” ์ด ๋ฌธ์ œ๋ฅผ Import Maps๋ผ๋Š” ๊ธฐ๋Šฅ์œผ๋กœ ํ•ด๊ฒฐํ•ด.
ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์— `deno.json` ๋˜๋Š” `deno.jsonc` ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ , ๊ทธ ์•ˆ์— `imports`๋ผ๋Š” ๊ฐ์ฒด๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฑฐ์•ผ. ์—ฌ๊ธฐ์— ๋ชจ๋“ˆ์˜ '๋ณ„๋ช…'๊ณผ ์‹ค์ œ URL์„ ๋งคํ•‘ํ•ด๋‘๋ฉด, ์ฝ”๋“œ์—์„œ๋Š” ์งง์€ ๋ณ„๋ช…์œผ๋กœ ๋ชจ๋“ˆ์„ ์ž„ํฌํŠธํ•  ์ˆ˜ ์žˆ์–ด.

์˜ˆ๋ฅผ ๋“ค์–ด, `deno.jsonc` ํŒŒ์ผ์„ ์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๋Š” ๊ฑฐ์•ผ.


// deno.jsonc
{
  "imports": {
    "std/": "https://deno.land/std@0.224.0/",
    "oak": "https://deno.land/x/oak@v16.0.0/mod.ts",
    "react": "npm:react@18.2.0"
  }
}
    

์ด๋ ‡๊ฒŒ ์„ค์ •ํ•ด๋‘๋ฉด, ์ด์ œ ์šฐ๋ฆฌ ์ฝ”๋“œ์—์„œ๋Š” ์ด๋ ‡๊ฒŒ ๊น”๋”ํ•˜๊ฒŒ ๋ชจ๋“ˆ์„ ์ž„ํฌํŠธํ•  ์ˆ˜ ์žˆ์–ด.


// main.ts
import { Application } from "oak"; // "oak"๋Š” deno.jsonc์— ์ •์˜๋œ URL๋กœ ํ•ด์„๋œ๋‹ค.
import { join } from "std/path/mod.ts"; // "std/" ์—ญ์‹œ ๋งˆ์ฐฌ๊ฐ€์ง€!
import React from "react";

const app = new Application();
// ...
    

์–ด๋•Œ? ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๊นจ๋—ํ•ด์กŒ์ง€?
๋งŒ์•ฝ `oak` ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋ฒ„์ „์„ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, `deno.jsonc` ํŒŒ์ผ์˜ URL ํ•œ ์ค„๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ํ”„๋กœ์ ํŠธ ์ „์ฒด์— ์ ์šฉ๋ผ. ์˜์กด์„ฑ ๊ด€๋ฆฌ๊ฐ€ ํ›จ์”ฌ ์‰ฌ์›Œ์ง€๋Š” ๊ฑฐ์ง€. `deno.json` ํŒŒ์ผ์€ `package.json`์ฒ˜๋Ÿผ ํ”„๋กœ์ ํŠธ์˜ ์„ค์ •, ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ (`deno task`), ๋ฆฐํ„ฐ/ํฌ๋งทํ„ฐ ์„ค์ • ๋“ฑ ๋‹ค์–‘ํ•œ ์—ญํ• ์„ ํ•˜๋Š” Deno ํ”„๋กœ์ ํŠธ์˜ ์‹ฌ์žฅ๊ณผ๋„ ๊ฐ™์€ ํŒŒ์ผ์ด์•ผ.

Deno์˜ ์ƒํƒœ๊ณ„๋Š” Node.js๋งŒํผ ์˜ค๋ž˜๋˜์ง€๋Š” ์•Š์•˜์ง€๋งŒ, ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ผ๋Š” ๋“ ๋“ ํ•œ ๊ธฐ๋ฐ˜ ์œ„์— `deno.land/x`๋ผ๋Š” ํ™œ๋ฐœํ•œ ๋†€์ดํ„ฐ, ๊ทธ๋ฆฌ๊ณ  `npm:` ํ˜ธํ™˜์„ฑ์ด๋ผ๋Š” ๊ฐ•๋ ฅํ•œ ๋ฌด๊ธฐ๊นŒ์ง€ ๊ฐ–์ถ”๋ฉฐ ๋น ๋ฅด๊ฒŒ ์„ฑ์žฅํ•˜๊ณ  ์žˆ์–ด. ์ด์ œ "Deno๋Š” ์“ธ๋งŒํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์—†์–ด์„œ..."๋ผ๋Š” ๋ง์€ ์˜›๋ง์ด ๋œ ๊ฑฐ์ง€. ์ด์ œ ์ด ์žฌ๋ฃŒ๋“ค์„ ๊ฐ€์ง€๊ณ  ์ง„์งœ ์š”๋ฆฌ๋ฅผ ๋งŒ๋“ค์–ด ๋ณผ ์‹œ๊ฐ„์ด์•ผ! ๋‹ค์Œ ์ฑ•ํ„ฐ์—์„œ๋Š” ์‹ค์ œ๋กœ ๊ฐ„๋‹จํ•œ REST API ์„œ๋ฒ„๋ฅผ ๋งŒ๋“ค์–ด๋ณด๋ฉด์„œ Deno์™€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ์‹ค์ „ ํ™œ์šฉ๋ฒ•์„ ์ตํ˜€๋ณผ๊ฒŒ. ๐Ÿ‘จโ€๐Ÿณ

๐Ÿš€ ์‹ค์ „! Deno์™€ TypeScript๋กœ REST API ๋งŒ๋“ค๊ธฐ (feat. Oak)

์ด๋ก  ๊ณต๋ถ€๋Š” ์ถฉ๋ถ„ํžˆ ํ–ˆ์–ด. ์ด์ œ ์ง„์งœ ์‹ค๋ ฅ์„ ๋ณด์—ฌ์ค„ ์‹œ๊ฐ„์ด์•ผ! ๐Ÿง‘โ€๐Ÿ’ป
์ง€๊ธˆ๊นŒ์ง€ ๋ฐฐ์šด Deno์™€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ง€์‹์„ ์ด๋™์›ํ•ด์„œ ๊ฐ„๋‹จํ•œ 'ํ•  ์ผ ๋ชฉ๋ก(Todo List)'์„ ๊ด€๋ฆฌํ•˜๋Š” REST API ์„œ๋ฒ„๋ฅผ ๋งŒ๋“ค์–ด ๋ณผ ๊ฑฐ์•ผ. ์ด ๊ณผ์ •์„ ๋”ฐ๋ผ์˜ค๋ฉด Deno ํ”„๋กœ์ ํŠธ์˜ ๊ตฌ์กฐ๋Š” ์–ด๋–ป๊ฒŒ ์žก๋Š”์ง€, ์›น ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์–ด๋–ป๊ฒŒ ์“ฐ๋Š”์ง€, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋กœ ์–ด๋–ป๊ฒŒ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ์„ ์ •์˜ํ•˜๋Š”์ง€ ํ™•์‹คํ•˜๊ฒŒ ๊ฐ์„ ์žก์„ ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ.

์šฐ๋ฆฌ๋Š” ์›น ํ”„๋ ˆ์ž„์›Œํฌ๋กœ Oak๋ฅผ ์‚ฌ์šฉํ•  ๊ฑฐ์•ผ. Oak๋Š” Node.js์˜ Express.js์— ์˜๊ฐ์„ ๋ฐ›์•„ ๋งŒ๋“ค์–ด์ง„ Deno์šฉ ๋ฏธ๋“ค์›จ์–ด ํ”„๋ ˆ์ž„์›Œํฌ์•ผ. Express๋ฅผ ์จ๋ณธ ์นœ๊ตฌ๋“ค์ด๋ผ๋ฉด ์•„์ฃผ ์นœ์ˆ™ํ•˜๊ฒŒ ๋А๊ปด์งˆ ๊ฑฐ์•ผ.

1. ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ ์žก๊ธฐ ๋ฐ ์˜์กด์„ฑ ์„ค์ •

๋จผ์ € ํ”„๋กœ์ ํŠธ ํด๋”๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค๊ณ , ๊ทธ ์•ˆ์— ํ•„์š”ํ•œ ํŒŒ์ผ๋“ค์„ ์ƒ์„ฑํ•˜์ž.
ํ„ฐ๋ฏธ๋„์—์„œ ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์ฐจ๋ก€๋Œ€๋กœ ์ž…๋ ฅํ•ด ๋ด.


mkdir deno-todo-api
cd deno-todo-api
touch main.ts routes.ts controllers.ts types.ts deno.jsonc
    

๊ฐ ํŒŒ์ผ์˜ ์—ญํ• ์€ ๋Œ€๋žต ์ด๋ž˜:

  • deno.jsonc: ํ”„๋กœ์ ํŠธ ์„ค์ • ๋ฐ ์˜์กด์„ฑ ๊ด€๋ฆฌ (Import Maps)
  • types.ts: ์šฐ๋ฆฌ ์•ฑ์—์„œ ์‚ฌ์šฉํ•  ๋ฐ์ดํ„ฐ ํƒ€์ž… ์ •์˜ (e.g., Todo)
  • controllers.ts: ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฒ˜๋ฆฌ (e.g., ํ•  ์ผ ์ถ”๊ฐ€, ์‚ญ์ œ)
  • routes.ts: URL ๊ฒฝ๋กœ์™€ ์ปจํŠธ๋กค๋Ÿฌ ํ•จ์ˆ˜ ์—ฐ๊ฒฐ (๋ผ์šฐํŒ…)
  • main.ts: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‹œ์ž‘์ . ์„œ๋ฒ„๋ฅผ ์„ค์ •ํ•˜๊ณ  ์‹คํ–‰.

์ด์ œ `deno.jsonc` ํŒŒ์ผ์„ ์—ด๊ณ , ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ์— ํ•„์š”ํ•œ ์˜์กด์„ฑ์„ Import Maps๋กœ ์ •์˜ํ•˜์ž.


// deno.jsonc
{
  "imports": {
    "oak": "https://deno.land/x/oak@v16.0.0/mod.ts",
    "uuid": "https://deno.land/std@0.224.0/uuid/mod.ts"
  }
}
    

์šฐ๋ฆฌ๋Š” ์›น ํ”„๋ ˆ์ž„์›Œํฌ์ธ `oak`์™€, ๊ฐ ํ•  ์ผ ํ•ญ๋ชฉ์— ๊ณ ์œ ํ•œ ID๋ฅผ ๋ถ€์—ฌํ•˜๊ธฐ ์œ„ํ•œ `uuid` ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•  ๊ฑฐ์•ผ. ์ด๋ ‡๊ฒŒ ๋ฏธ๋ฆฌ ์ •์˜ํ•ด๋‘๋ฉด ์ฝ”๋“œ์—์„œ๋Š” `import { ... } from "oak";` ์ฒ˜๋Ÿผ ์งง๊ฒŒ ์“ธ ์ˆ˜ ์žˆ์ง€.

2. ํƒ€์ž… ์ •์˜ํ•˜๊ธฐ (`types.ts`)

๊ฐ€์žฅ ๋จผ์ € ํ•  ์ผ์€ ์šฐ๋ฆฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋‹ค๋ฃฐ ๋ฐ์ดํ„ฐ์˜ '๋ชจ์–‘'์„ ์ •์˜ํ•˜๋Š” ๊ฑฐ์•ผ.
์ด๊ฒŒ ๋ฐ”๋กœ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ํ•ต์‹ฌ์ด์ง€! `types.ts` ํŒŒ์ผ์„ ์—ด๊ณ  `Todo` ํƒ€์ž…์„ ์ •์˜ํ•˜์ž.


// types.ts

// ํ•  ์ผ(Todo) ํ•ญ๋ชฉ ํ•˜๋‚˜์˜ ๋ชจ์–‘์„ ์ •์˜ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค
export interface Todo {
  id: string;       // ๊ณ ์œ  ์‹๋ณ„์ž
  title: string;    // ํ•  ์ผ ๋‚ด์šฉ
  isCompleted: boolean; // ์™„๋ฃŒ ์—ฌ๋ถ€
}
    

์ด๋ ‡๊ฒŒ `interface`๋กœ ํƒ€์ž…์„ ์ •์˜ํ•ด๋‘๋ฉด, ์•ž์œผ๋กœ ์šฐ๋ฆฌ ์ฝ”๋“œ์—์„œ `Todo` ๊ฐ์ฒด๋ฅผ ๋‹ค๋ฃฐ ๋•Œ `id`, `title`, `isCompleted` ์†์„ฑ์ด ์˜ฌ๋ฐ”๋ฅธ ํƒ€์ž…์œผ๋กœ ์กด์žฌํ•˜๋Š”์ง€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ํ•ญ์ƒ ๊ฐ์‹œํ•ด์ค„ ๊ฑฐ์•ผ. ์˜คํƒ€๋‚˜ ์ž˜๋ชป๋œ ๋ฐ์ดํ„ฐ ํƒ€์ž…์œผ๋กœ ์ธํ•œ ๋ฒ„๊ทธ๋ฅผ ์›์ฒœ ๋ด‰์‡„ํ•  ์ˆ˜ ์žˆ์–ด.

3. ์ปจํŠธ๋กค๋Ÿฌ ๋กœ์ง ์ž‘์„ฑํ•˜๊ธฐ (`controllers.ts`)

์ด์ œ ์‹ค์ œ ๊ธฐ๋Šฅ, ์ฆ‰ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋‹ด๋‹นํ•  ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋งŒ๋“ค ์ฐจ๋ก€์•ผ.
์ปจํŠธ๋กค๋Ÿฌ๋Š” "๋ชจ๋“  ํ•  ์ผ ๋ชฉ๋ก์„ ๋ณด์—ฌ์ค˜", "์ƒˆ๋กœ์šด ํ•  ์ผ์„ ์ถ”๊ฐ€ํ•ด์ค˜" ๊ฐ™์€ ์š”์ฒญ์„ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜๋“ค์˜ ๋ชจ์Œ์ด์•ผ. ์ง€๊ธˆ์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋Œ€์‹  ๊ฐ„๋‹จํ•œ ๋ฐฐ์—ด์„ ์‚ฌ์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ• ๊ฒŒ.


// controllers.ts

import { Todo } from "./types.ts";
import { v1 as uuidV1 } from "uuid"; // ๊ณ ์œ  ID ์ƒ์„ฑ์„ ์œ„ํ•ด uuid ๋ชจ๋“ˆ ์‚ฌ์šฉ

// ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋Œ€์‹  ์‚ฌ์šฉํ•  ๋ฉ”๋ชจ๋ฆฌ ๋‚ด ๋ฐฐ์—ด
let todos: Todo[] = [
  {
    id: "1",
    title: "Deno ๊ณต๋ถ€ํ•˜๊ธฐ",
    isCompleted: true,
  },
  {
    id: "2",
    title: "REST API ๋งŒ๋“ค๊ธฐ",
    isCompleted: false,
  },
];

// GET /todos - ๋ชจ๋“  ํ•  ์ผ ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ
export const getTodos = ({ response }: { response: any }) => {
  response.status = 200;
  response.body = {
    success: true,
    data: todos,
  };
};

// GET /todos/:id - ํŠน์ • ํ•  ์ผ ๊ฐ€์ ธ์˜ค๊ธฐ
export const getTodo = ({ params, response }: { params: { id: string }; response: any }) => {
  const todo = todos.find((t) => t.id === params.id);

  if (todo) {
    response.status = 200;
    response.body = {
      success: true,
      data: todo,
    };
  } else {
    response.status = 404;
    response.body = {
      success: false,
      message: "ํ•ด๋‹น ID์˜ ํ•  ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.",
    };
  }
};

// POST /todos - ์ƒˆ๋กœ์šด ํ•  ์ผ ์ถ”๊ฐ€ํ•˜๊ธฐ
export const addTodo = async ({ request, response }: { request: any; response: any }) => {
  if (!request.hasBody) {
    response.status = 400;
    response.body = { success: false, message: "์š”์ฒญ ๋ณธ๋ฌธ์ด ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค." };
    return;
  }
  
  const { title } = await request.body.json();
  const newTodo: Todo = {
    id: uuidV1.generate() as string, // ๊ณ ์œ  ID ์ƒ์„ฑ
    title,
    isCompleted: false,
  };

  todos.push(newTodo);
  response.status = 201;
  response.body = {
    success: true,
    data: newTodo,
  };
};

// PUT /todos/:id - ํ•  ์ผ ์ˆ˜์ •ํ•˜๊ธฐ
export const updateTodo = async ({ params, request, response }: { params: { id: string }; request: any; response: any }) => {
  const todo = todos.find((t) => t.id === params.id);

  if (todo) {
    const { title, isCompleted } = await request.body.json();
    const updatedTodos = todos.map((t) =>
      t.id === params.id ? { ...t, title: title ?? t.title, isCompleted: isCompleted ?? t.isCompleted } : t
    );
    todos = updatedTodos;
    response.status = 200;
    response.body = {
      success: true,
      data: todos.find(t => t.id === params.id),
    };
  } else {
    response.status = 404;
    response.body = {
      success: false,
      message: "ํ•ด๋‹น ID์˜ ํ•  ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.",
    };
  }
};

// DELETE /todos/:id - ํ•  ์ผ ์‚ญ์ œํ•˜๊ธฐ
export const deleteTodo = ({ params, response }: { params: { id: string }; response: any }) => {
  const initialLength = todos.length;
  todos = todos.filter((t) => t.id !== params.id);

  if (todos.length < initialLength) {
    response.status = 200;
    response.body = {
      success: true,
      message: "ํ•  ์ผ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.",
    };
  } else {
    response.status = 404;
    response.body = {
      success: false,
      message: "ํ•ด๋‹น ID์˜ ํ•  ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.",
    };
  }
};
    

์ฝ”๋“œ๊ฐ€ ์ข€ ๊ธธ์ง€๋งŒ, ์ž์„ธํžˆ ๋ณด๋ฉด ๊ฐ ํ•จ์ˆ˜๊ฐ€ ํ•˜๋Š” ์ผ์€ ๋ช…ํ™•ํ•ด.
`getTodos`๋Š” ์ „์ฒด `todos` ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , `addTodo`๋Š” ์š”์ฒญ ๋ณธ๋ฌธ์—์„œ `title`์„ ๋ฐ›์•„ ์ƒˆ `Todo` ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•˜๋Š” ์‹์ด์•ผ. ์—ฌ๊ธฐ์„œ๋„ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ํž˜์ด ๋“œ๋Ÿฌ๋‚˜. `newTodo`๋ฅผ ๋งŒ๋“ค ๋•Œ ์šฐ๋ฆฌ๊ฐ€ `types.ts`์—์„œ ์ •์˜ํ•œ `Todo` ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•ด์„œ, ๊ฐ์ฒด๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ๋ชจ์–‘์„ ๊ฐ–์ถ”๋„๋ก ๊ฐ•์ œํ•˜๊ณ  ์žˆ์–ด.

4. ๋ผ์šฐํ„ฐ ์„ค์ •ํ•˜๊ธฐ (`routes.ts`)

์ด์ œ ์ปจํŠธ๋กค๋Ÿฌ ํ•จ์ˆ˜๋“ค์„ ๋งŒ๋“ค์—ˆ์œผ๋‹ˆ, ์–ด๋–ค URL ๊ฒฝ๋กœ๋กœ ์š”์ฒญ์ด ์™”์„ ๋•Œ ์–ด๋–ค ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ• ์ง€ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” '๊ตํ†ต๊ฒฝ์ฐฐ' ์—ญํ• ์ด ํ•„์š”ํ•ด. ์ด๊ฒŒ ๋ฐ”๋กœ ๋ผ์šฐํ„ฐ(Router)์•ผ. `routes.ts` ํŒŒ์ผ์„ ์—ด๊ณ  Oak์˜ ๋ผ์šฐํ„ฐ๋ฅผ ์„ค์ •ํ•˜์ž.


// routes.ts

import { Router } from "oak";
import {
  getTodos,
  getTodo,
  addTodo,
  updateTodo,
  deleteTodo,
} from "./controllers.ts";

const router = new Router();

// RESTful API ์—”๋“œํฌ์ธํŠธ ์„ค์ •
router
  .get("/todos", getTodos)          // ๋ชจ๋“  ํ•  ์ผ ์กฐํšŒ
  .get("/todos/:id", getTodo)       // ํŠน์ • ํ•  ์ผ ์กฐํšŒ
  .post("/todos", addTodo)          // ์ƒˆ ํ•  ์ผ ์ถ”๊ฐ€
  .put("/todos/:id", updateTodo)    // ํ•  ์ผ ์ˆ˜์ •
  .delete("/todos/:id", deleteTodo); // ํ•  ์ผ ์‚ญ์ œ

export default router;
    

์ฝ”๋“œ๊ฐ€ ์ •๋ง ์ง๊ด€์ ์ด์ง€?
GET ์š”์ฒญ์ด `/todos`๋กœ ์˜ค๋ฉด `getTodos` ํ•จ์ˆ˜๋ฅผ, POST ์š”์ฒญ์ด `/todos`๋กœ ์˜ค๋ฉด `addTodo` ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋ผ๊ณ  ๋ช…์‹œ์ ์œผ๋กœ ์•Œ๋ ค์ฃผ๋Š” ๊ฑฐ์•ผ. ์ด๋ ‡๊ฒŒ ๋ผ์šฐํŒ… ๋กœ์ง์„ ๋ณ„๋„์˜ ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๊น”๋”ํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฌ์›Œ์ ธ.

5. ์„œ๋ฒ„ ์‹คํ–‰ํ•˜๊ธฐ (`main.ts`)

๋“œ๋””์–ด ๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„์•ผ!
๋ชจ๋“  ์กฐ๊ฐ๋“ค์„ ๋ชจ์•„์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์™„์„ฑํ•˜๊ณ  ์„œ๋ฒ„๋ฅผ ์‹คํ–‰์‹œํ‚ค๋Š” `main.ts` ํŒŒ์ผ์„ ์ž‘์„ฑํ•˜์ž.


// main.ts

import { Application } from "oak";
import router from "./routes.ts";

const PORT = 8000;
const app = new Application();

// ๋กœ๊น… ๋ฏธ๋“ค์›จ์–ด ์ถ”๊ฐ€ (๋ชจ๋“  ์š”์ฒญ์„ ์ฝ˜์†”์— ๊ธฐ๋ก)
app.use(async (ctx, next) => {
  await next();
  const rt = ctx.response.headers.get("X-Response-Time");
  console.log(`${ctx.request.method} ${ctx.request.url} - ${rt}`);
});

// ์‘๋‹ต ์‹œ๊ฐ„ ์ธก์ • ๋ฏธ๋“ค์›จ์–ด
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  ctx.response.headers.set("X-Response-Time", `${ms}ms`);
});

// ๋ผ์šฐํ„ฐ ๋ฏธ๋“ค์›จ์–ด ๋“ฑ๋ก
app.use(router.routes());
app.use(router.allowedMethods()); // OPTIONS, 405 Method Not Allowed ๋“ฑ ์ž๋™ ์ฒ˜๋ฆฌ

console.log(`๐Ÿฆ• Deno ์„œ๋ฒ„๊ฐ€ http://localhost:${PORT} ์—์„œ ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค...`);

await app.listen({ port: PORT });
    

`main.ts`์—์„œ๋Š” Oak ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๊ณ , ์šฐ๋ฆฌ๊ฐ€ ์ž‘์„ฑํ•œ ๋ผ์šฐํ„ฐ๋ฅผ ๋ฏธ๋“ค์›จ์–ด๋กœ ๋“ฑ๋กํ–ˆ์–ด. ์ถ”๊ฐ€๋กœ ๋ชจ๋“  ์š”์ฒญ์— ๋Œ€ํ•œ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๊ณ  ์‘๋‹ต ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ๋ฏธ๋“ค์›จ์–ด๋„ ๋„ฃ์–ด๋ดค์–ด. ์ด๋Ÿฐ ๊ฒŒ ๋ฐ”๋กœ ๋ฏธ๋“ค์›จ์–ด์˜ ํž˜์ด์•ผ. ์š”์ฒญ๊ณผ ์‘๋‹ต ์‚ฌ์ด์—์„œ ๋‹ค์–‘ํ•œ ๊ณตํ†ต ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์ง€.

6. API ์„œ๋ฒ„ ์‹คํ–‰ ๋ฐ ํ…Œ์ŠคํŠธ

์ž, ๋ชจ๋“  ์ค€๋น„๊ฐ€ ๋๋‚ฌ์–ด. ํ„ฐ๋ฏธ๋„์—์„œ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•ด๋ณด์ž!
๋„คํŠธ์›Œํฌ ์ ‘๊ทผ ๊ถŒํ•œ์„ ์ฃผ๋Š” `โ€“allow-net` ํ”Œ๋ž˜๊ทธ๋ฅผ ์žŠ์ง€ ๋งˆ.

deno run --allow-net main.ts

ํ„ฐ๋ฏธ๋„์— "๐Ÿฆ• Deno ์„œ๋ฒ„๊ฐ€ http://localhost:8000 ์—์„œ ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค..." ๋ฉ”์‹œ์ง€๊ฐ€ ๋œจ๋ฉด ์„ฑ๊ณต!
์ด์ œ API๊ฐ€ ์ž˜ ์ž‘๋™ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ•ด๋ด์•ผ์ง€. ์›น ๋ธŒ๋ผ์šฐ์ €๋‚˜ Postman, ํ˜น์€ ํ„ฐ๋ฏธ๋„์˜ `curl` ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์–ด.

1. ๋ชจ๋“  ํ•  ์ผ ๋ชฉ๋ก ์กฐํšŒ (GET)

curl http://localhost:8000/todos

2. ์ƒˆ๋กœ์šด ํ•  ์ผ ์ถ”๊ฐ€ (POST)

curl -X POST -H "Content-Type: application/json" -d '{"title": "Deno ํ”„๋กœ์ ํŠธ ๋ฐฐํฌํ•˜๊ธฐ"}' http://localhost:8000/todos

3. ํŠน์ • ํ•  ์ผ ์ˆ˜์ • (PUT) (ID๊ฐ€ 2์ธ ํ•ญ๋ชฉ์„ ์™„๋ฃŒ ์ฒ˜๋ฆฌ)

curl -X PUT -H "Content-Type: application/json" -d '{"isCompleted": true}' http://localhost:8000/todos/2

4. ํ•  ์ผ ์‚ญ์ œ (DELETE) (ID๊ฐ€ 1์ธ ํ•ญ๋ชฉ ์‚ญ์ œ)

curl -X DELETE http://localhost:8000/todos/1

์–ด๋•Œ? ์šฐ๋ฆฌ๊ฐ€ ์ •์˜ํ•œ ๋Œ€๋กœ API๊ฐ€ ์™„๋ฒฝํ•˜๊ฒŒ ๋™์ž‘ํ•˜๋Š” ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ.
์ด ๊ณผ์ •์—์„œ ์šฐ๋ฆฌ๋Š” Deno ํ”„๋กœ์ ํŠธ์˜ ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•˜๊ณ , ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋กœ ์•ˆ์ „ํ•œ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ์„ ๋งŒ๋“ค๊ณ , Oak ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•ด ๊น”๋”ํ•˜๊ฒŒ ๋ผ์šฐํŒ…๊ณผ ์ปจํŠธ๋กค๋Ÿฌ ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์› ์–ด. ์ด ์ •๋„๋ฉด Deno๋กœ ์–ด๋–ค ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด๋“  ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋Š” ์ž์‹ ๊ฐ์ด ์ƒ๊ธฐ์ง€ ์•Š์•„? ๐Ÿ˜‰

๋ฌผ๋ก  ์‹ค์ œ ํ”„๋กœ๋•์…˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๋™, ์—๋Ÿฌ ์ฒ˜๋ฆฌ, ์‚ฌ์šฉ์ž ์ธ์ฆ ๋“ฑ ๋” ๋งŽ์€ ๊ฒƒ๋“ค์ด ํ•„์š”ํ•˜๊ฒ ์ง€๋งŒ, ์˜ค๋Š˜ ๋งŒ๋“  ์ด ์ž‘์€ API ์„œ๋ฒ„๋Š” Deno์™€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ๊ฐ•๋ ฅํ•จ์„ ๊ฒฝํ—˜ํ•˜๊ธฐ์— ์ถฉ๋ถ„ํ•œ, ์•„์ฃผ ํ›Œ๋ฅญํ•œ ์ฒซ๊ฑธ์Œ์ด์•ผ. ์ด ๊ฒฝํ—˜์„ ๋ฐ”ํƒ•์œผ๋กœ ๋” ๋ณต์žกํ•˜๊ณ  ๋ฉ‹์ง„ ํ”„๋กœ์ ํŠธ์— ๋„์ „ํ•ด๋ณด๊ธธ ๋ฐ”๋ผ! ํ˜น์‹œ ๊ฐœ๋ฐœํ•˜๋‹ค ๋ง‰ํžˆ๊ฑฐ๋‚˜ ๋” ๊นŠ์€ ์ง€์‹์ด ํ•„์š”ํ•˜๋‹ค๋ฉด, **์žฌ๋Šฅ๋„ท** ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ Deno ์ „๋ฌธ๊ฐ€์˜ ๋„์›€์„ ๋ฐ›๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด ๋  ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ. ๐Ÿ‘

๐ŸŒŒ Deno, ๊ทธ ๋„ˆ๋จธ์˜ ์„ธ๊ณ„: ์ƒํƒœ๊ณ„์™€ ๋ฏธ๋ž˜

์šฐ๋ฆฌ๋Š” Deno์˜ ๊ธฐ๋ณธ ์ฒ ํ•™๋ถ€ํ„ฐ ์‹ค์ „ API ๊ฐœ๋ฐœ๊นŒ์ง€, ๊ฝค๋‚˜ ๊ธด ์—ฌ์ •์„ ํ•จ๊ป˜ํ–ˆ์–ด.
ํ•˜์ง€๋งŒ Deno์˜ ์„ธ๊ณ„๋Š” ์—ฌ๊ธฐ์„œ ๋๋‚˜์ง€ ์•Š์•„. Deno๋ฅผ ๋‘˜๋Ÿฌ์‹ผ ์ƒํƒœ๊ณ„๋Š” ์ง€๊ธˆ ์ด ์ˆœ๊ฐ„์—๋„ ํ™œ๋ฐœํ•˜๊ฒŒ ์„ฑ์žฅํ•˜๋ฉฐ ์ƒˆ๋กœ์šด ๊ฐ€๋Šฅ์„ฑ์„ ์—ด์–ด๊ฐ€๊ณ  ์žˆ์–ด. ๋งˆ์ง€๋ง‰์œผ๋กœ Deno์˜ ๋ฏธ๋ž˜๋ฅผ ์—ฟ๋ณผ ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ํฅ๋ฏธ๋กœ์šด ์ฃผ์ œ๋“ค์„ ์‚ดํŽด๋ณด๋ฉฐ ์ด์•ผ๊ธฐ๋ฅผ ๋งˆ๋ฌด๋ฆฌํ•˜๋ ค๊ณ  ํ•ด.

1. Deno Deploy: ์„œ๋ฒ„ ์—†๋Š” ์„œ๋ฒ„, ์—ฃ์ง€ ์ปดํ“จํŒ…์˜ ์‹œ๋Œ€

์„œ๋ฒ„๋ฅผ ๋งŒ๋“ค์—ˆ์œผ๋ฉด ์ด์ œ ๋ฐฐํฌ๋ฅผ ํ•ด์•ผ์ง€.
๋ณดํ†ต์€ AWS EC2๋‚˜ DigitalOcean ๊ฐ™์€ ํด๋ผ์šฐ๋“œ ์„œ๋ฒ„๋ฅผ ๋นŒ๋ ค์„œ, ๊ทธ ์•ˆ์— Deno๋ฅผ ์„ค์น˜ํ•˜๊ณ , ์ฝ”๋“œ๋ฅผ ์˜ฌ๋ฆฌ๊ณ , ํ”„๋กœ์„ธ์Šค ๋งค๋‹ˆ์ €(pm2 ๊ฐ™์€)๋กœ ์‹คํ–‰ํ•˜๋Š” ๋ณต์žกํ•œ ๊ณผ์ •์„ ๊ฑฐ์ณ์•ผ ํ–ˆ์–ด.

Deno ํŒ€์€ ์ด ๋ฐฐํฌ ๊ณผ์ •๋งˆ์ € ํ˜์‹ ํ•˜๊ณ  ์‹ถ์—ˆ๋‚˜ ๋ด. ๊ทธ๋ž˜์„œ Deno Deploy๋ผ๋Š” ์ž์ฒด ์„œ๋ฒ„๋ฆฌ์Šค ํ”Œ๋žซํผ์„ ๋งŒ๋“ค์—ˆ์–ด. ์ด๊ฑด ๊ทธ๋ƒฅ ํ‰๋ฒ”ํ•œ ์„œ๋ฒ„๋ฆฌ์Šค๊ฐ€ ์•„๋‹ˆ์•ผ. ์ „ ์„ธ๊ณ„ ์ˆ˜์‹ญ ๊ฐœ์˜ ๋ฐ์ดํ„ฐ ์„ผํ„ฐ์— ๋‚ด ์ฝ”๋“œ๋ฅผ ๋ถ„์‚ฐ์‹œ์ผœ ๋†“๊ณ , ์‚ฌ์šฉ์ž์™€ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์œ„์น˜์—์„œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” '์—ฃ์ง€ ์ปดํ“จํŒ…(Edge Computing)' ํ™˜๊ฒฝ์„ ์ œ๊ณตํ•ด.

Deno Deploy: ๊ธ€๋กœ๋ฒŒ ์—ฃ์ง€ ๋„คํŠธ์›Œํฌ ์‚ฌ์šฉ์ž์™€ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๊ณณ์—์„œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ์ง€์—ฐ ์‹œ๊ฐ„์„ ์ตœ์†Œํ™” Edge EU Edge Asia Edge US-W Edge US-E Edge SA ์œ ๋Ÿฝ ์‚ฌ์šฉ์ž ์•„์‹œ์•„ ์‚ฌ์šฉ์ž ~20ms ~15ms ๊ธฐ์กด ์ค‘์•™ ์„œ๋ฒ„ ~200ms ๋ฒ”๋ก€ ์‚ฌ์šฉ์ž ์—ฃ์ง€ ์„œ๋ฒ„ ์ค‘์•™ ์„œ๋ฒ„

์ด๊ฒŒ ๋ฌด์Šจ ๋ง์ด๋ƒ๋ฉด, ๋ฏธ๊ตญ์— ์žˆ๋Š” ์‚ฌ์šฉ์ž๋Š” ๋ฏธ๊ตญ ์„œ๋ฒ„์—์„œ, ์œ ๋Ÿฝ์— ์žˆ๋Š” ์‚ฌ์šฉ์ž๋Š” ์œ ๋Ÿฝ ์„œ๋ฒ„์—์„œ ๋‚ด ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค๋Š” ๊ฑฐ์•ผ. ๋ฌผ๋ฆฌ์ ์ธ ๊ฑฐ๋ฆฌ๊ฐ€ ๊ฐ€๊นŒ์›Œ์ง€๋‹ˆ ๋‹น์—ฐํžˆ ์‘๋‹ต ์†๋„๋Š” ์—„์ฒญ๋‚˜๊ฒŒ ๋นจ๋ผ์ง€๊ฒ ์ง€? โšก

Deno Deploy์˜ ๊ฐ€์žฅ ๋ฉ‹์ง„ ์ ์€ ์‚ฌ์šฉ๋ฒ•์ด ๊ธฐ๊ฐ€ ๋ง‰ํžˆ๊ฒŒ ๊ฐ„๋‹จํ•˜๋‹ค๋Š” ๊ฑฐ์•ผ. GitHub ์ €์žฅ์†Œ์— ์ฝ”๋“œ๋ฅผ ํ‘ธ์‹œํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด, Deno Deploy๊ฐ€ ์•Œ์•„์„œ ์ „ ์„ธ๊ณ„์— ์ฝ”๋“œ๋ฅผ ๋ฐฐํฌํ•ด์ค˜. ๋ณ„๋„์˜ ์„ค์ •? ๋นŒ๋“œ ๊ณผ์ •? ๊ฑฐ์˜ ํ•„์š” ์—†์–ด. Deno๊ฐ€ ์ถ”๊ตฌํ•˜๋Š” '๋‹จ์ˆœํ•จ์˜ ๋ฏธํ•™'์ด ๋ฐฐํฌ ๊ณผ์ •๊นŒ์ง€ ์ด์–ด์ง„ ๊ฑฐ์ง€.

2. Deno์˜ ์ƒˆ๋กœ์šด ๋‚ ๊ฐœ, Fresh ํ”„๋ ˆ์ž„์›Œํฌ

Oak๊ฐ€ Express.js ๊ฐ™์€ ์ „ํ†ต์ ์ธ ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋ Œ๋”๋ง(SSR) ํ”„๋ ˆ์ž„์›Œํฌ๋ผ๋ฉด, Deno ์ƒํƒœ๊ณ„์—๋Š” Fresh๋ผ๋Š” ์•„์ฃผ ์‹ ์„ ํ•˜๊ณ  ํ˜์‹ ์ ์ธ ํ”„๋ ˆ์ž„์›Œํฌ๋„ ์žˆ์–ด. Fresh๋Š” React์˜ ์ฐฝ์‹œ์ž ์ค‘ ํ•œ ๋ช…์ธ Jordan Walke์˜ ์˜ํ–ฅ์„ ๋ฐ›์€ '์•„์ผ๋žœ๋“œ ์•„ํ‚คํ…์ฒ˜(Islands Architecture)'๋ผ๋Š” ๊ฐœ๋…์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ด.

์ด๊ฒŒ ๋ญ๋ƒ๋ฉด, ์›น ํŽ˜์ด์ง€๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” ์ •์ ์ธ HTML๋กœ ๋งŒ๋“ค์–ด์„œ ์•„์ฃผ ๋น ๋ฅด๊ฒŒ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ฃผ๊ณ , ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๊ผญ ํ•„์š”ํ•œ ๋ถ€๋ถ„(์˜ˆ: ์ด๋ฏธ์ง€ ์บ๋Ÿฌ์…€, ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•œ ๋ฒ„ํŠผ)๋งŒ '์„ฌ(Island)'์ฒ˜๋Ÿผ ๋…๋ฆฝ์ ์œผ๋กœ ๋กœ๋“œํ•ด์„œ ์ƒํ˜ธ์ž‘์šฉ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ๋ฐฉ์‹์ด์•ผ.

๊ฒฐ๊ณผ๋Š”? ์—„์ฒญ๋‚˜๊ฒŒ ๋น ๋ฅธ ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„! ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ถˆํ•„์š”ํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ณด๋‚ด์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์›น์‚ฌ์ดํŠธ ์„ฑ๋Šฅ์ด ๊ทน์ ์œผ๋กœ ํ–ฅ์ƒ๋ผ. ๋นŒ๋“œ ๊ณผ์ •๋„ ํ•„์š” ์—†๊ณ , ๋ชจ๋“  ๋ Œ๋”๋ง์€ ์„œ๋ฒ„(์—ฃ์ง€)์—์„œ ์ฆ‰์‹œ ์ผ์–ด๋‚˜. Deno Deploy์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ๊ทธ์•ผ๋ง๋กœ ํ™˜์ƒ์˜ ๊ถํ•ฉ์„ ์ž๋ž‘ํ•˜์ง€.

๋ธ”๋กœ๊ทธ๋‚˜ ๋ฌธ์„œ ์‚ฌ์ดํŠธ, ๊ฐ„๋‹จํ•œ ์‡ผํ•‘๋ชฐ์ฒ˜๋Ÿผ ์ฝ˜ํ…์ธ  ์ค‘์‹ฌ์˜ ์›น์‚ฌ์ดํŠธ๋ฅผ ๋งŒ๋“ ๋‹ค๋ฉด Fresh๋Š” ์ •๋ง ๋งค๋ ฅ์ ์ธ ์„ ํƒ์ง€๊ฐ€ ๋  ๊ฑฐ์•ผ.

3. WebAssembly(WASM)์™€์˜ ๋งŒ๋‚จ: ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๋„˜์–ด

Deno๋Š” WebAssembly(WASM)๋ฅผ ์ผ๊ธ‰ ์‹œ๋ฏผ(First-class citizen)์œผ๋กœ ์ง€์›ํ•ด. WebAssembly๋Š” C++, Rust, Go ๊ฐ™์€ ๊ณ ์„ฑ๋Šฅ ์–ธ์–ด๋กœ ์ž‘์„ฑ๋œ ์ฝ”๋“œ๋ฅผ ์›น ๋ธŒ๋ผ์šฐ์ €๋‚˜ Deno ๊ฐ™์€ ํ™˜๊ฒฝ์—์„œ ๋„ค์ดํ‹ฐ๋ธŒ์— ๊ฐ€๊นŒ์šด ์†๋„๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ธฐ์ˆ ์ด์•ผ.

์ด๊ฒŒ ์™œ ์ค‘์š”ํ•˜๋ƒ๋ฉด, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ฒ˜๋ฆฌํ•˜๊ธฐ์— ๋„ˆ๋ฌด ๋ฌด๊ฑฐ์šด ์ž‘์—…๋“ค, ์˜ˆ๋ฅผ ๋“ค์–ด ๋น„๋””์˜ค ์ธ์ฝ”๋”ฉ, ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ, ๋ณต์žกํ•œ ๊ณผํ•™ ๊ณ„์‚ฐ ๊ฐ™์€ ๊ฒƒ๋“ค์„ ๊ณ ์„ฑ๋Šฅ ์–ธ์–ด์— ๋งก๊ธฐ๊ณ , Deno๋Š” ์ด WASM ๋ชจ๋“ˆ์„ ์‰ฝ๊ฒŒ ๋ถˆ๋Ÿฌ์™€์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด์•ผ. Deno๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ํŽธ๋ฆฌํ•จ๊ณผ Rust ๊ฐ™์€ ์‹œ์Šคํ…œ ์–ธ์–ด์˜ ์„ฑ๋Šฅ์„ ๋ชจ๋‘ ์ทจํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ•๋ ฅํ•œ ํ”Œ๋žซํผ์œผ๋กœ ์ง„ํ™”ํ•˜๊ณ  ์žˆ๋Š” ๊ฑฐ์ง€.

4. ๊ทธ๋ž˜์„œ, Deno๋Š” Node.js๋ฅผ ๋Œ€์ฒดํ• ๊นŒ?

์ด ๊ธ€์„ ๋‹ค ์ฝ๊ณ  ๋‚˜๋ฉด ์ด๋Ÿฐ ์งˆ๋ฌธ์ด ์ƒ๊ธธ ๊ฑฐ์•ผ. "์™€, Deno ์ •๋ง ์ข‹๋„ค. ๊ทธ๋Ÿผ ์ด์ œ Node.js๋Š” ๋ฒ„๋ฆฌ๊ณ  Deno๋กœ ๊ฐˆ์•„ํƒ€์•ผ ํ•˜๋‚˜?"

๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜์ž๋ฉด, "์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๋‹ค"๊ฐ€ ์ •๋‹ต์ด์•ผ. ๐Ÿค”

Node.js๊ฐ€ ์—ฌ์ „ํžˆ ๊ฐ•๋ ฅํ•œ ์ด์œ 

Node.js๋Š” 2009๋…„๋ถ€ํ„ฐ ์ง€๊ธˆ๊นŒ์ง€ 10๋…„์ด ๋„˜๋Š” ์‹œ๊ฐ„ ๋™์•ˆ ๊ฑฐ๋Œ€ํ•˜๊ณ  ์„ฑ์ˆ™ํ•œ ์ƒํƒœ๊ณ„๋ฅผ ๊ตฌ์ถ•ํ–ˆ์–ด. ๊ฒ€์ฆ๋œ ์ˆ˜๋งŽ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, ๋ฐฉ๋Œ€ํ•œ ์ปค๋ฎค๋‹ˆํ‹ฐ ์ž๋ฃŒ, ์ฑ„์šฉ ์‹œ์žฅ์—์„œ์˜ ์••๋„์ ์ธ ์ˆ˜์š” ๋“ฑ์€ ์ ˆ๋Œ€ ๋ฌด์‹œํ•  ์ˆ˜ ์—†๋Š” ์žฅ์ ์ด์•ผ. ๋Œ€๊ทœ๋ชจ์˜ ๋ณต์žกํ•œ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด๋‚˜, ์ด๋ฏธ Node.js ๊ธฐ๋ฐ˜์œผ๋กœ ์ž˜ ๋Œ์•„๊ฐ€๊ณ  ์žˆ๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ๊ตณ์ด Deno๋กœ ์˜ฎ๊ธธ ํ•„์š”๋Š” ์—†์„ ์ˆ˜๋„ ์žˆ์–ด.

Deno๊ฐ€ ๋น›์„ ๋ฐœํ•˜๋Š” ์ˆœ๊ฐ„

๋ฐ˜๋ฉด์—, ์ƒˆ๋กœ์šด ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ๋‚˜ ์Šคํƒ€ํŠธ์—…์˜ MVP(์ตœ์†Œ ๊ธฐ๋Šฅ ์ œํ’ˆ)๋ฅผ ๋งŒ๋“ค ๋•Œ, ํ˜น์€ ์Šคํฌ๋ฆฝํŒ…, ์ปค๋งจ๋“œ๋ผ์ธ ๋„๊ตฌ(CLI)๋ฅผ ๊ฐœ๋ฐœํ•  ๋•Œ๋Š” Deno๊ฐ€ ์ •๋ง ๊ฐ•๋ ฅํ•œ ๋ฌด๊ธฐ๊ฐ€ ๋  ์ˆ˜ ์žˆ์–ด. ๋ณ„๋„์˜ ์„ค์ • ์—†์ด ๋ฐ”๋กœ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋กœ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๊ณ , ๋‚ด์žฅ ํˆด์ฒด์ธ์œผ๋กœ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์ด ํ›จ์”ฌ ์พŒ์ ํ•˜๋ฉฐ, ๋ณด์•ˆ ๋ชจ๋ธ ๋•๋ถ„์— ๋” ์•ˆ์ „ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์ง€. ํŠนํžˆ Deno Deploy๋‚˜ Fresh ๊ฐ™์€ ์ตœ์‹  ๊ธฐ์ˆ ์„ ํ™œ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด Deno๋Š” ์ตœ๊ณ ์˜ ์„ ํƒ์ด์•ผ.

Deno๋Š” Node.js๋ฅผ ์ฃฝ์ด๊ธฐ ์œ„ํ•ด ๋‚˜์˜จ 'ํ‚ฌ๋Ÿฌ'๊ฐ€ ์•„๋‹ˆ์•ผ.
Node.js๋ฅผ ๋งŒ๋“ค๋ฉด์„œ ๋А๊ผˆ๋˜ ์•„์‰ฌ์›€์„ ๊ตํ›ˆ ์‚ผ์•„, ๋” ํ˜„๋Œ€์ ์ด๊ณ  ์•ˆ์ „ํ•˜๋ฉฐ ํŽธ๋ฆฌํ•œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ์ œ๊ณตํ•˜๋ ค๋Š” '๋Œ€์•ˆ'์ด์ž '์ง„ํ™”'์— ๊ฐ€๊น๋‹ค๊ณ  ์ƒ๊ฐํ•ด. ๋‘˜์€ ๊ฒฝ์Ÿ ๊ด€๊ณ„์ด๋ฉด์„œ๋„, `npm:` ํ˜ธํ™˜์„ฑ์ฒ˜๋Ÿผ ์„œ๋กœ๋ฅผ ๋ณด์™„ํ•˜๋ฉฐ ํ•จ๊ป˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์„œ๋ฒ„์‚ฌ์ด๋“œ ์ƒํƒœ๊ณ„๋ฅผ ๋”์šฑ ํ’์„ฑํ•˜๊ฒŒ ๋งŒ๋“ค์–ด๊ฐ€๊ณ  ์žˆ์–ด.

์˜ค๋Š˜ ์šฐ๋ฆฌ๋Š” Deno๋ผ๋Š” ์ƒˆ๋กœ์šด ๊ณต๋ฃก์˜ ๋“ฑ์— ์˜ฌ๋ผํƒ€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ผ๋Š” ๋‚ ๊ฐœ๋ฅผ ๋‹ฌ๊ณ  ๋ฉ‹์ง„ ๋น„ํ–‰์„ ํ•ด๋ดค์–ด.
์ด ๊ฒฝํ—˜์ด ๋„ˆ์˜ ๊ฐœ๋ฐœ์ž๋กœ์„œ์˜ ์—ฌ์ •์— ์ƒˆ๋กœ์šด ์˜๊ฐ๊ณผ ์ฆ๊ฑฐ์›€์„ ์ฃผ์—ˆ๊ธฐ๋ฅผ ๋ฐ”๋ผ. ์„ธ์ƒ์€ ๋น ๋ฅด๊ฒŒ ๋ณ€ํ•˜๊ณ , ๊ฐœ๋ฐœ์ž์—๊ฒŒ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋ฌด๊ธฐ๋Š” ์ƒˆ๋กœ์šด ๊ธฐ์ˆ ์„ ๋‘๋ ค์›Œํ•˜์ง€ ์•Š๊ณ  ๋ฐฐ์šฐ๋ ค๋Š” ํ˜ธ๊ธฐ์‹ฌ์ผ ๊ฑฐ์•ผ.

์ง€๊ธˆ ๋‹น์žฅ ๊ฑฐ์ฐฝํ•œ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•˜์ง€ ์•Š์•„๋„ ์ข‹์•„.
๊ฐ„๋‹จํ•œ ์Šคํฌ๋ฆฝํŠธ ํ•˜๋‚˜๋ผ๋„ Deno๋กœ ์งœ๋ณด๋Š” ๊ฑด ์–ด๋–จ๊นŒ? ๊ทธ ์ž‘์€ ํ•œ ๊ฑธ์Œ์ด ๋„ˆ๋ฅผ ๋” ๋„“์€ ์„ธ๊ณ„๋กœ ์ด๋Œ์–ด ์ค„ ๊ฑฐ์•ผ. Happy coding! ๐Ÿฅณ

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.