CHAPTER 16. Zones and the Angular magic

Intro

AngularJS 1.X๋กœ ๊ฐœ๋ฐœํ•˜๋ฉด "๋งˆ์ˆ "๋Š๋‚Œ์ด ์ƒ๊ธฐ๊ณ  Angular 2๋Š” ์—ฌ์ „ํžˆ ๋™์ผํ•œ ํšจ๊ณผ๋ฅผ ์ค€๋‹ค. ์ž…๋ ฅ์— ๊ฐ’์„ ์ž…๋ ฅํ•˜๋ฉด ๋ชจ๋“  ๊ฒƒ์ด ๋งˆ์ˆ ์ฒ˜๋Ÿผ ์—…๋ฐ์ดํŠธ ๋œ๋‹ค.

๋‚˜๋Š” ๋งˆ์ˆ ์„ ์ข‹์•„ํ•˜์ง€๋งŒ, ๋‚˜๋Š” ๋‚ด๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๋„๊ตฌ๋กœ ์–ด๋–ค ์ผ์ด ๋ฒŒ์–ด์ง€๊ณ  ์žˆ๋Š”์ง€ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•œ๋‹ค. ๋‹น์‹ ์ด ๋‚˜๋ฅผ ์ข‹์•„ํ•œ๋‹ค๋ฉด,์ด ๋ถ€๋ถ„์ด ๋‹น์‹ ์—๊ฒŒ๋„ ํฅ๋ฏธ๋กœ์šธ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค. ์šฐ๋ฆฌ๋Š” Angular 2๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ๋ณด๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค! ๊ทธ๋Ÿฌ๋‚˜ AngularJS 1.x๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ๋จผ์ € ์‚ดํŽด ๋ณด๋„๋ก ํ•˜์ž. AngularJS 1.x๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ์žฌ๋ฏธ์žˆ์„ ๊ฒƒ์ด๋‹ค.

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

๋ฐ˜๋ฉด, React๋Š” ๋ณ€๊ฒฝ ๋  ๋•Œ๋งˆ๋‹ค DOM์„ ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜๊ธฐ๋กœ ํ•˜์˜€๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ „์ฒด DOM์„ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์€ ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ์ž‘์—…์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€์ƒ DOM์— ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ ์šฉํ•œ ๋‹ค์Œ ๊ฐ€์ƒ DOM๊ณผ ์‹ค์ œ DOM๊ฐ„์— ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ ์šฉํ•˜๊ธฐ ๋งŒํ•˜๋ฉด ๋œ๋‹ค. Angular๋Š” setter๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉฐ ๊ฐ€์ƒ DOM๋„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋Ÿฌ๋ฉด DOM์—์„œ ๋ฌด์—‡์„ ๋ณ€๊ฒฝํ•ด์•ผํ•˜๋Š”์ง€ ์–ด๋–ป๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์„๊นŒ?

์ฒซ ๋ฒˆ์งธ ๋‹จ๊ณ„๋Š” ๋ชจ๋ธ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ฐ์ง€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๋ณ€๊ฒฝ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ๋˜๋Š” "์‹œ์Šคํ…œ" ์ด๋ฒคํŠธ (HTTP ์‘๋‹ต, ์‹œ๊ฐ„ ์ดˆ๊ณผ ํ›„ ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ๋“ฑ) ์—์„œ ์˜ค๋Š” ์ด๋ฒคํŠธ์— ์˜ํ•ด ํ•ญ์ƒ ํŠธ๋ฆฌ๊ฑฐ ๋œ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด AngularJS 1.x๋Š” ์–ด๋–ค ์‚ฌ๊ฑด์ด ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ์–ด๋–ป๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์„๊นŒ? ์ด ๋ถ€๋ถ„์€ ์‹ค์ œ๋กœ ๋งค์šฐ ๊ฐ„๋‹จํ•˜๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ng-click์„ ํด๋ฆญ ์ด๋ฒคํŠธ์— ๋ฐ˜์‘ ์‹œํ‚ค๊ฑฐ๋‚˜ ng-model์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž…๋ ฅ์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ด€์ฐฐํ•˜๋Š” ๋“ฑ์˜ ์ง€์‹œ๋ฌธ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ๋˜ํ•œ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๊ฐ•์š”ํ•œ๋‹ค. (์˜ˆ : HTTP ์š”์ฒญ์˜ ๊ฒฝ์šฐ http http ๋˜๋Š” ๋น„๋™๊ธฐ ์ ์œผ๋กœ ์ž‘์—…์„ ์‹คํ–‰ํ•˜๋Š” $ timeout).

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

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

ํ”„๋ ˆ์ž„ ์›Œํฌ๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ๊ฐ์ง€ ํ•  ๋•Œ๋งˆ๋‹ค (์‚ฌ์šฉ์ž๊ฐ€ ng ๋ชจ๋ธ, HTTP ์‘๋‹ต, ์‹œ๊ฐ„ ์ดˆ๊ณผ ์‹คํ–‰ ๋“ฑ์œผ๋กœ ์ž…๋ ฅ์—์„œ ๋ฌด์–ธ๊ฐ€๋ฅผ ์ž…๋ ฅํ•˜๋Š” ๊ฒฝ์šฐ) ๋‹ค์ด์ œ์ŠคํŠธ์ฃผ๊ธฐ๋ฅผ ํŠธ๋ฆฌ๊ฑฐ ํ•œ๋‹ค. ์ด ๋‹ค์ด์ œ์ŠคํŠธ์ฃผ๊ธฐ๋Š” ๊ด€์ฐฐ์ž์— ์ €์žฅ๋œ ๋ชจ๋“  ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•˜๊ณ  ์ƒˆ ๊ฐ’๊ณผ ์ด์ „ ๊ฐ’์„ ๋น„๊ต ํ•œ๋‹ค. ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ํ”„๋ ˆ์ž„ ์›Œํฌ๋Š” DOM์„ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— UI๊ฐ€ ์ด์ „ ๊ฐ’ ๋Œ€์‹  ์ƒˆ ๊ฐ’์„ ํ‘œ์‹œํ•œ๋‹ค. ์ด ๊ธฐ์ˆ ์„ ๋”ํ‹ฐ ๊ฒ€์‚ฌ๋ผ๊ณ  ํ•œ๋‹ค.

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

AngularJS 1.x and the digest cycle

์ตœ์†Œํ•œ์˜ ์˜ˆ๋ฅผ ๋“ค์–ด ๋ณด์ž. ์‚ฌ์šฉ์ž๊ฐ€ ์ฑ„์›Œ์•ผ ํ•˜๋Š” ๋‘ ๊ฐœ์˜ ํ•„๋“œ (์ด๋ฆ„๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ)๊ฐ€์žˆ๋Š” ํŽ˜์ด์ง€ ์ด๋‹ค. ํŽ˜์ด์ง€์— ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ•๋„ ํ‘œ์‹œ๊ธฐ๊ฐ€ ํ‘œ์‹œ๋œ๋‹ค๊ณ  ๊ฐ€์ • ํ•ด ๋ณด์ž. watcher๋Š” ์•”ํ˜ธ ๊ฐ’์„๋ณด๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋ฉฐ ๋ณ€๊ฒฝ ๋  ๋•Œ๋งˆ๋‹ค ๊ฐ•๋„๋ฅผ ๋‹ค์‹œ ๊ณ„์‚ฐํ•œ๋‹ค. ๋‹ค์ด์ œ์ŠคํŠธ์ฃผ๊ธฐ์˜ ์ฒซ ๋ฒˆ์งธ ๋ฐ˜๋ณต ํ›„ ์‚ฌ์šฉ์ž๊ฐ€ ๋น„๋ฐ€๋ฒˆํ˜ธ์˜ ์ฒซ ๊ธ€์ž๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์šฐ๋ฆฌ๋Š” ์ด ๊ด€์ฐฐ์ž ๋ชฉ๋ก์„ ๊ฐ–๊ฒŒ๋œ๋‹ค.

$$watchers (expression -> value)
- "user.name" -> "Cรฉdric"
- "user.password" -> "h"
- "passwordStrength" -> 0

๊ทธ๋Ÿฐ ๋‹ค์Œ user.password ํ‘œํ˜„์‹์„ ๊ด€์ฐฐํ•˜๋Š” ๊ด€์ฐฐ์ž์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ์ƒˆ ์•”ํ˜ธ ๊ฐ•๋„๊ฐ€ ๊ณ„์‚ฐ๋œ๋‹ค. Angular๋Š” ๋ชจ๋ธ์—์„œ ์ˆ˜ํ–‰ ๋œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์•Œ์ง€ ๋ชปํ•˜๋ฏ€๋กœ ๋ชจ๋ธ์ด ์•ˆ์ •์ ์ธ์ง€ ์•Œ์•„๋ณด๊ธฐ ์œ„ํ•ด ๋‘ ๋ฒˆ์งธ ๋‹ค์ด์ œ์ŠคํŠธ ๋ฐ˜๋ณต์„ ์‹œ์ž‘ ํ•œ๋‹ค.

$$watchers
- "user.name" -> "Cรฉdric"
- "user.password" -> "h"
- "passwordStrength" -> 3

๋ชจ๋ธ์ด ์•ˆ์ •์ ์ด์ง€ ์•Š๋Š”๋‹ค. ์ฒซ ๋ฒˆ์งธ ๋ฐ˜๋ณต ์ดํ›„ passwordStrength์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‹ค๋ฅธ ๋‹ค์ด์ œ์ŠคํŠธ ๋ฐ˜๋ณต์„ ์‹œ์ž‘ํ•œ๋‹ค.

$$watchers
- "user.name" -> "Cรฉdric"
- "user.password" -> "h"
- "passwordStrength" -> 3

์ด๋ฒˆ์—๋Š” ๋ชจ๋ธ์ด ์•ˆ์ •์ ์ด๋‹ค. AngularJS 1.x๊ฐ€ ๊ฒฐ๊ณผ๋ฅผ DOM์— ํ”Œ๋Ÿฌ์‹œํ•˜๋Š” ๊ฒƒ์€ ๊ทธ ๋‹น์‹œ ๋ฟ์ด๋‹ค. ๋”ฐ๋ผ์„œ ๋‹ค์ด์ œ์ŠคํŠธ์ฃผ๊ธฐ๋Š” ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ๋ณ€๊ฒฝ ๋  ๋•Œ๋งˆ๋‹ค ์ตœ์†Œ 2 ํšŒ ๋ฐœ์ƒ ํ•œ๋‹ค. ๋‹ค์ด์ œ์ŠคํŠธ๋Š” ์ตœ๋Œ€ 10 ํšŒ๊นŒ์ง€ ๋ฐ˜๋ณต ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋” ์ด์ƒ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ฒฐ๊ณผ๊ฐ€ ์•ˆ์ •์ ์ด์ง€ ์•Š์œผ๋ฉด 10 ํšŒ ๋ฐ˜๋ณต ํ›„ ํ”„๋ ˆ์ž„ ์›Œํฌ๋Š” ๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ•˜๊ณ  ์˜ˆ์™ธ๋ฅผ throwํ•œ๋‹ค.

๊ทธ๋ž˜์„œ ๋‚ด ์ž‘์€ ๋“œ๋กœ์ž‰์€ ํ›จ์”ฌ ๋” ์ข‹์•„ ๋ณด์ธ๋‹ค. ์ด๊ฒƒ์€ ๊ฐ ์‚ฌ๊ฑด ํ›„์— ๊ณ„์†๋˜๊ณ  ์žˆ๋‹ค. ์ฆ‰, ์‚ฌ์šฉ์ž๊ฐ€ ์•”ํ˜ธ ํ•„๋“œ์— 5์ž๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ๋‹ค์ด์ œ์ŠคํŠธ์ฃผ๊ธฐ๊ฐ€ 5 ๋ฒˆ ์‹คํ–‰๋˜๋ฉฐ ๋งค๋ฒˆ 3 ๋ฒˆ ๋ฐ˜๋ณต๋˜๋ฏ€๋กœ ์ด 15 ๋ฒˆ์˜ ๋ฐ˜๋ณต์ด ๋ฐœ์ƒํ•œ๋‹ค.

์‹ค์ œ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์—์„œ๋Š” ์ˆ˜๋ฐฑ ๋ช…์˜ ๊ด€์ฐฐ์ž๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ชจ๋“  ์ด๋ฒคํŠธ ํ›„์— ์ˆ˜์ฒœ ๊ฐœ์˜ ํ‘œํ˜„ ํ‰๊ฐ€๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค. ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ €๋Š” ๋งค์šฐ ๋น ๋ฅด๋ฉฐ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋ช‡ ๊ฐ€์ง€ ์‚ฌํ•ญ์„ ์ตœ์ ํ™” ํ•  ์ˆ˜์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์œผ๋ฏ€๋กœ ์ž˜ ๋™์ž‘ํ•  ๊ฒƒ์ด๋‹ค.

AngularJS 1.x์— ๋Œ€ํ•œ ๋‘ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ์š”์ ์„ ๋‹ค์‹œ ์ •๋ฆฌํ•ด ๋ณด์ž. โ€ข ๋ชจ๋ธ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜์žˆ๋Š” ๋ชจ๋“  ๊ฒƒ์— ๋Œ€ํ•ด ํ”„๋ ˆ์ž„ ์›Œํฌ์˜ ์„œ๋น„์Šค์™€ ์ง€์‹œ๋ฌธ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. โ€ข Angular๋กœ ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š๋Š” ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ ํ›„์— ๋ชจ๋ธ์„ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ (๋‹ค์ด์ œ์ŠคํŠธ์ฃผ๊ธฐ๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ์œ ๋ช…ํ•œ $ scope. $ apply () ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ) ๊ฐ์ง€ ๋ณ€๊ฒฝ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๋ช…์‹œ ์ ์œผ๋กœ ํŠธ๋ฆฌ๊ฑฐ ํ•ด์•ผ ํ•œใ„ท. ์˜ˆ๋ฅผ ๋“ค์–ด $ http ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  HTTP ์š”์ฒญ์„ ๋ณด๋‚ด๋ ค๋ฉด ์‘๋‹ต ์ฝœ๋ฐฑ์—์„œ $ scope. $ apply ()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํ”„๋ ˆ์ž„ ์›Œํฌ์— "์ด๋ด, ๋ชจ๋ธ์— ์ƒˆ ๊ฐ’์„ ์ €์žฅ ํ–ˆ์œผ๋‹ˆ, ๋‹ค์ด์ œ์ŠคํŠธ ์‚ฌ์ดํด์„ ์‹œ์ž‘ ํ•˜๊ฒ ๋Š”๊ฐ€?

ํ”„๋ ˆ์ž„ ์›Œํฌ์˜ ๋งˆ๋ฒ•์€ ๋‘ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋‹ค. โ€ข ๊ฐ ์ด๋ฒคํŠธ๊ฐ€ ๋๋‚˜๋ฉด ๋ณ€๊ฒฝ ๊ฐ์ง€๋ฅผ ํŠธ๋ฆฌ๊ฑฐ ํ•œ๋‹ค. โ€ข ๊ฐ์‹œ์ž ๋ฐ ๋‹ค์ด์ œ์ŠคํŠธ์ฃผ๊ธฐ ๋•๋ถ„์— ๋ณ€๊ฒฝ ๊ฐ์ง€ ํ•œ๋‹ค. ์ด์ œ Angular 2๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹๊ณผ AngularJS์™€๋Š” ๋‹ค๋ฅธ ์ ์„ ์‚ดํŽด ๋ณด์ž.

Angular 2 and zones

Angular 2๋Š” ๋™์ผํ•œ ์›์น™์„ ๊ธฐ๋ฐ˜์œผ๋กœํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ ๋œ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋” ๋˜‘๋˜‘ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ๋งํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

Angular ํŒ€์€ ๋ฌธ์ œ์˜ ์ฒซ ๋ฒˆ์งธ ๋ถ€๋ถ„ ์ธ ๋ณ€๊ฒฝ ๊ฐ์ง€๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•˜๊ธฐ ์œ„ํ•ด Zone.js๋ผ๋Š” ์ž‘์€ ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ตฌ์ถ•ํ–ˆ๋‹ค. ์ด ํ”„๋กœ์ ํŠธ๋Š” ์˜์—ญ์ด ๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ์—์„œ ์œ ์šฉ ํ•  ์ˆ˜์žˆ๋Š” ๋„๊ตฌ์ด๊ธฐ ๋•Œ๋ฌธ์— Angular์— ๋ฌถ์—ฌ ์žˆ์ง€ ์•Š๋Š”๋‹ค. ์˜์—ญ์€ ์ „ํ˜€ ์ƒˆ๋กœ์šด ๊ฐœ๋…์ด ์•„๋‹ˆ๋‹ค. Dart ์–ธ์–ด (๋‹ค๋ฅธ Google ํ”„๋กœ์ ํŠธ)์— ์ด๋ฏธ ์ƒ๋‹น ๋ถ€๋ถ„ ์กด์žฌํ•œ๋‹ค. ๊ทธ๊ฒƒ๋“ค์€ ๋˜ํ•œ Node.js (ํ˜„์žฌ ๋ฒ„๋ ค์ง„)์˜ Domains ๋˜๋Š” java์˜ ThreadLocals์™€ ์œ ์‚ฌํ•˜๋‹ค.

Zones

์˜์—ญ์€ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ์ด๋‹ค. ์ด ์ปจํ…์ŠคํŠธ๋Š” ์‹คํ–‰ํ•  ์ฝ”๋“œ๋ฅผ ์ˆ˜์‹ ํ–ˆ์œผ๋ฉฐ ์ด ์ฝ”๋“œ๋Š” ๋™๊ธฐ์‹ ๋˜๋Š” ๋น„๋™๊ธฐ์‹์ด ๋  ์ˆ˜ ์žˆ๋‹ค. ์˜์—ญ์€ ๋ช‡ ๊ฐ€์ง€ ์ด์ ์„ ์ œ๊ณตํ•œ๋‹ค.

โ€ข ์‹คํ–‰ํ•˜๊ธฐ ์ „ํ›„์— ์‹คํ–‰๋  ์ˆ˜์žˆ๋Š” ํ›„ํฌ โ€ข ์‹คํ–‰ํ•  ์ฝ”๋“œ์˜ ์ž ์žฌ์  ์ธ ์˜ค๋ฅ˜๋ฅผ ๊ฐ€๋กœ ์ฑ„๊ธฐ์œ„ํ•œ ๋ฐฉ๋ฒ• โ€ข ์ด ์ปจํ…์ŠคํŠธ์— ๋ฐ”์ธ๋”ฉ ๋œ ๋ณ€์ˆ˜๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•

์˜ˆ๋ฅผ ๋“ค์–ด ๋ณด์ž. ๋‚ด ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์— ๋‹ค์Œ ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž

// score computation -> synchronous
const score = computeScore();
// player score update -> synchronous
updatePlayer(player, score);

์ด ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

computeScore: new score: 1000
udpatePlayer: player 1 has 1000 points

์ด์ œ๋Š” ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋ฐ ์†Œ์š” ๋œ ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๋ ค๊ณ  ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž.

startTimer();
const score = computeScore();
updatePlayer(player, score);
stopTimer();

๊ทธ๋ฆฌ๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

start
computeScore: new score: 1000
udpatePlayer: player 1 has 1000 points
stop: 12ms

์ด์ œ updatePlayer๊ฐ€ ๋น„๋™๊ธฐ ํ•จ์ˆ˜ ์ธ ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ ๋ ๊นŒ? JavaScript๋Š” ์•„์ฃผ ํŠน๋ณ„ํ•œ ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค. ๋น„๋™๊ธฐ ์ž‘์—…์€ ์‹คํ–‰ ํ์˜ ๋์— ๋ฐฐ์น˜๋˜๋ฏ€๋กœ ๋™๊ธฐ ์ž‘์—… ํ›„์— ์‹คํ–‰ ๋œ๋‹ค.

startTimer();
const score = computeScore();
updatePlayer(player, score); // asynchronous
stopTimer();
start
computeScore: new score: 1000
stop: 5ms
udpatePlayer: player 1 has 1000 points

๋‚ด ์‹คํ–‰ ์‹œ๊ฐ„์€ ๋” ์ด์ƒ ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š๋Š”๋‹ค : ๊ทธ๊ฒƒ์€ ์ฝ”๋“œ์—์„œ ๋™๊ธฐํ™” ์ž‘์—…์ด ์ˆ˜ํ–‰ ํ•œ ์‹œ๊ฐ„ ๋งŒ ์ธก์ •ํ•˜๊ณ  ์‹œ์ž‘๋ถ€ํ„ฐ ์ ์ˆ˜ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋๋‚œ ์‹œ๊ฐ„์€ ์ธก์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค! ๊ทธ๊ฒƒ์ด ์˜์—ญ์ด ์œ ์šฉ ํ•  ์ˆ˜์žˆ๋Š” ๊ณณ์ด๋‹ค. ์ „์šฉ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ์—์„œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

const scoreZone = Zone.current.fork({ name: 'scoreZone' });
scoreZone.run(() => {
    const score = computeScore();
    updatePlayer(player, score); // asynchronous
});

์™œ ์ด๊ฒƒ์ด ๋„์›€์ด ๋ ๊นŒ? zone.js ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์— ์˜ํ•ด๋กœ๋“œ๋˜๋ฉด ์ž๋ฐ” ์Šคํฌ๋ฆฝํŠธ ๋Ÿฐํƒ€์ž„์—์„œ ๋ชจ๋“  ๋น„๋™๊ธฐ ํ•จ์ˆ˜์— ํŒจ์น˜๋ฅผ ์ ์šฉํ•˜์—ฌ ์‹œ์ž‘ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ setTimeout () ๋˜๋Š” setInterval ()์„ ์‚ฌ์šฉํ•  ๋•Œ๋งˆ๋‹ค ๋˜๋Š” Promises, XMLHttpRequest, WebSocket, FileReader, GeoLocation ๋“ฑ๊ณผ ๊ฐ™์€ ๋น„๋™๊ธฐ API๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋งˆ๋‹ค ์šฐ๋ฆฌ๋Š” ์‹ค์ œ๋กœ zone.js์˜ ํŒจ์น˜ ๋ฒ„์ „์„ ํ˜ธ์ถœ ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ Zone.js๋Š” ๋น„๋™๊ธฐ ์ž‘์—…์ด ์™„๋ฃŒ๋œ ์‹œ์ ์„ ์•Œ๊ณ  ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•ด๋‹น ์‹œ์ ์— ์ผ๋ถ€ ํ›„ํฌ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜์—ญ์€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๊ณ ๋ฆฌ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. โ€ข onInvoke๋Š” ์˜์—ญ์—์„œ ๋ž˜ํ•‘ ๋œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ํ˜ธ์ถœ๋œ๋‹ค. โ€ข onHasTask๋Š” ์˜์—ญ์—์„œ ๋žฉํ•‘ ๋œ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰์„ ๋งˆ์นœ ํ›„์— ํ˜ธ์ถœ๋œ๋‹ค. โ€ข onHandleError๋Š” ์˜์—ญ์—์„œ ๋ž˜ํ•‘ ๋œ ์ฝ”๋“œ๊ฐ€ ์˜ˆ์™ธ๋ฅผ throw ํ•  ๋•Œ ํ˜ธ์ถœ๋œ๋‹ค. โ€ข onFork์€ ์˜์—ญ์ด ๋งŒ๋“ค์–ด ์งˆ ๋•Œ ํ˜ธ์ถœ๋œ๋‹ค.

๋”ฐ๋ผ์„œ ์šฐ๋ฆฌ๋Š” ์กด๊ณผ ๊ทธ ํ›„ํฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ์ž‘์—…์˜ ์‹œ์ž‘๋ถ€ํ„ฐ ๋๊นŒ์ง€ ์†Œ์š” ๋œ ์ „์ฒด ์‹œ๊ฐ„์„ ์ธก์ • ํ•  ์ˆ˜ ์žˆ๋‹ค.

const scoreZone = Zone.current.fork({
name: 'scoreZone',
onInvoke(delegate, current, target, task, applyThis, applyArgs, source) {
    // start the timer
    startTimer();
    return delegate.invoke(target, task, applyThis, applyArgs, source);
    },
    onHasTask(delegate, current, target, hasTaskState) {
    delegate.hasTask(target, hasTaskState);
    if (!hasTaskState.macroTask) {
        // if the zone run is done, stop the timer
        stopTimer();
    }
}
});
scoreZone.run(() => {
    const score = computeScore();
    updatePlayer(player, score);
});

๊ทธ๋ฆฌ๊ณ  ์ด๋ฒˆ์—๋Š” ๊ฒฐ๊ณผ๊ฐ€ ์ •ํ™•ํ•˜๋‹ค!

start
computeScore: new score: 1000
udpatePlayer: player 1 has 1000 points
stop: 12ms

์ด์ œ Angular 2๊ฐ€ ์ด ๋ฉ”์ปค๋‹ˆ์ฆ˜์˜ ์ด์ ์„ ๋ˆ„๋ฆด ์ˆ˜์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์ถ”์ธก ํ•  ์ˆ˜ ์žˆ๋‹ค. ์‹ค์ œ๋กœ ํ”„๋ ˆ์ž„ ์›Œํฌ์˜ ์ฒซ ๋ฒˆ์งธ ๋ฌธ์ œ๋Š” ๋ณ€๊ฒฝ ๊ฐ์ง€๊ฐ€ ํŠธ๋ฆฌ๊ฑฐ ๋˜์–ด์•ผ ํ•˜๋Š” ์‹œ๊ธฐ๋ฅผ ํŒŒ์•…ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์˜์—ญ์„ ์‚ฌ์šฉํ•˜๊ณ  ์˜์—ญ์—์„œ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ํ”„๋ ˆ์ž„ ์›Œํฌ์—์„œ ์–ด๋–ค ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š”์ง€ ์ž˜ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•์œผ๋กœ ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋” ์ค‘์š”ํ•œ ๊ฒƒ์€ ๋น„๋™๊ธฐ ์ž‘์—…์ด ์™„๋ฃŒ ๋  ๋•Œ๋งˆ๋‹ค ๋ณ€๊ฒฝ ๊ฐ์ง€๋ฅผ ํŠธ๋ฆฌ๊ฑฐ ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๋‹จ์ˆœํ™”ํ•˜๊ธฐ ์œ„ํ•ด Angular 2๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฒ˜๋ฆฌํ•œ๋‹ค.

const angularZone = Zone.current.fork({
    name: 'angular',
    onHasTask: triggerChangeDetection
});
angularZone.run(() => {
// your application code
});

๊ทธ๋ฆฌ๊ณ  ์ฒซ ๋ฒˆ์งธ ๋ฌธ์ œ๊ฐ€ ์ด๋ ‡๊ฒŒ ํ•ด๊ฒฐ๋˜์—ˆ๋‹ค! ๊ทธ๋ž˜์„œ Angular 2์—์„œ๋Š” AngularJS 1.x์™€ ๋‹ฌ๋ฆฌ ์ž๋™ ๋ณ€๊ฒฝ ๊ฐ์ง€ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํŠน๋ณ„ํ•œ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ์›ํ•˜๋Š” ๊ฒƒ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์˜์—ญ์ด ์ด๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.

์˜์—ญ์€ ํ‘œ์ค€ํ™” ๊ณผ์ •์— ์žˆ์œผ๋ฏ€๋กœ ๊ณต์‹์ ์ธ ECMAScript ์‚ฌ์–‘์˜ ์ผ๋ถ€๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค. ๋‹ค๋ฅธ ํฅ๋ฏธ๋กœ์šด ์ •๋ณด ์ธ zone.js์˜ ํ˜„์žฌ ๊ตฌํ˜„์€ WTF์— ๋Œ€ํ•œ ์ •๋ณด๋„ ์ œ๊ณตํ•œ๋‹ค. (์ด ๋ฌธ๋งฅ์—์„œ Web Tracing Framework). ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐœ๋ฐœ ๋ชจ๋“œ์—์„œ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์„ ํ”„๋กœํŒŒ์ผ๋งํ•˜๊ณ  ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ๋ฐ ํ”„๋ ˆ์ž„ ์›Œํฌ ์ฝ”๋“œ์˜ ๋ชจ๋“  ๋ถ€๋ถ„์—์„œ ์†Œ์š” ๋œ ์‹œ๊ฐ„์„ ์ •ํ™•ํžˆ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ์š”์ปจ๋Œ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์„ฑ๋Šฅ์„ ๋ถ„์„ํ•˜๊ณ  ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜์žˆ๋Š” ์ถฉ๋ถ„ํ•œ ์ •๋ณด์ด๋‹ค.

Change detection in Angular 2

๋ฌธ์ œ์˜ ๋‘ ๋ฒˆ์งธ ๋ถ€๋ถ„์€ ๋ณ€๊ฒฝ ๊ฐ์ง€ ์ž์ฒด์ด๋‹ค. ์‹œ์ž‘ ์‹œ์  ๋ฐ ๋ฐฉ๋ฒ•์„ ํŒŒ์•…ํ•˜๋Š” ๊ฒƒ์ด ํ•˜๋‚˜์˜ ๋ฐฉ๋ฒ•์ด์ง€๋งŒ ์ž‘๋™ ์›๋ฆฌ๋ฅผ ์•„๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜๋‹ค.

์šฐ์„  Angular ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๊ตฌ์„ฑ ์š”์†Œ์˜ ํŠธ๋ฆฌ๋ผ๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•ด์•ผ ํ•œ๋‹ค. ๋ณ€๊ฒฝ ๊ฐ์ง€๊ฐ€ ์‹œ์ž‘๋˜๋ฉด ํ”„๋ ˆ์ž„ ์›Œํฌ๋Š” ํŠธ๋ฆฌ์˜ ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๊ฑฐ์ณ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€, ์ƒˆ ์ƒํƒœ๊ฐ€ ํ•ด๋‹น๋ณด๊ธฐ์— ์˜ํ–ฅ์„ ์ฃผ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. ์ด ๊ฒฝ์šฐ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์˜ ์˜ํ–ฅ์„ ๋ฐ›๋Š” ๊ตฌ์„ฑ ์š”์†Œ์˜ DOM ๋ถ€๋ถ„์ด ์—…๋ฐ์ดํŠธ ๋œ๋‹ค. ํŠธ๋ฆฌ ํƒ์ƒ‰์€ ๋ฃจํŠธ์—์„œ ์žŽ์œผ๋กœ ์ด๋™ํ•˜๋ฉฐ AngularJS 1.x์™€๋Š” ๋‹ฌ๋ฆฌ ํ•œ ๋ฒˆ๋งŒ ์ˆ˜ํ–‰๋œ๋‹ค.

์ด์ œ๋Š” ํฐ ์ฐจ์ด๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ณ€๊ฒฝ ๊ฐ์ง€๋กœ Angular 2์˜ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ๋ชจ๋ธ์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์ง€๋งŒ AngularJS 1.x์˜ ๊ด€์ฐฐ์ž๋Š” ํ•ด๋‹น ๋‹จ๊ณ„์—์„œ ๋ชจ๋ธ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

๋˜ ๋‹ค๋ฅธ ํฐ ์ฐจ์ด์ ์€ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์ƒํƒœ์™€ ์ž์‹ ์ƒํƒœ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์กฐ์ƒ์˜ ์ƒํƒœ๋ฅผ ์ˆ˜์ •ํ•ด์„œ๋Š” ์•ˆ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ๊ณ„๋‹จ์‹ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ์ด์ œ ๋” ์ด์ƒ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ๋ณ€๊ฒฝ ๊ฐ์ง€๋Š” ๋ชจ๋ธ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ํ™•์ธํ•˜๊ณ  ์ด์— ๋”ฐ๋ผ DOM์„ ์ˆ˜์ •ํ•˜๋Š” ๋ฐ์—๋งŒ ์‚ฌ์šฉ๋œ๋‹ค. ๊ทธ๊ฒƒ์€ ๋ฒ„์ „ 1.x์—์„œ์™€ ๊ฐ™์ด ๋ชจ๋ธ์— ๋ถ€์ž‘์šฉ์„ ๊ฐ€์งˆ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋ธ์„ ์ˆœํšŒ (traversal)ํ•˜์—ฌ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ํŠธ๋ฆฌ๋ฅผ ๋‘ ๋ฒˆ ํ†ต๊ณผ ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค!

์ •ํ™•ํ•˜๊ฒŒ ๋งํ•˜์ž๋ฉด ๊ฐœ๋ฐœ ๋ชจ๋“œ์—์„œ ๋‘ ๋ฒˆ ์ •ํ™•ํ•˜๊ฒŒ ํƒ์ƒ‰ํ•˜์—ฌ ๊ทธ๋Ÿฌํ•œ ๋ฐ”๋žŒ์งํ•˜์ง€ ์•Š์€ ๋ถ€์ž‘์šฉ์ด ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ์ฃผ์žฅํ•œ๋‹ค. (์˜ˆ : ๋ถ€๋ชจ ๊ตฌ์„ฑ ์š”์†Œ ๋ชจ๋ธ์„ ์ˆ˜์ •ํ•˜๋Š” ํ•˜์œ„ ๊ตฌ์„ฑ ์š”์†Œ). ๋‘ ๋ฒˆ์งธ ๋‹จ๊ณ„์—์„œ ์ด๋Ÿฌํ•œ ๋ณ€๊ฒฝ์ด ๊ฐ์ง€๋˜๋ฉด ๊ฐœ๋ฐœ์ž์—๊ฒŒ ๋ฌธ์ œ๋ฅผ ๊ฒฝ๊ณ ํ•˜๋Š” ์˜ˆ์™ธ๊ฐ€ throw๋œ๋‹ค. ์ด ์ „๋žต์—๋Š” ๋งŽ์€ ์ด์ ์ด ์žˆ๋‹ค.

โ€ข ์ƒํƒœ ๋ณ€๊ฒฝ์€ ์–‘๋ฐฉํ–ฅ์œผ๋กœ ์ž‘์„ฑ๋˜๋Š” ๋Œ€์‹  ์ƒ์œ„์—์„œ ํ•˜์œ„๋กœ ํ•œ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ์ด๋ฃจ์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์— ๋Œ€ํ•œ ์ด์œ ๋ฅผ ์‰ฝ๊ฒŒ ํŒ๋‹จ ํ•  ์ˆ˜ ์žˆ๋‹ค. โ€ข ๋ณ€๊ฒฝ ๊ฐ์ง€์—๋Š” ๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ๋” ์ด์ƒ์žˆ์„ ์ˆ˜ ์—†๋‹ค. โ€ข ๋ณ€๊ฒฝ ๊ฐ์ง€๊ฐ€ ํ›จ์”ฌ ๋น ๋ฅด๋‹ค.

AngularJS 1.x๋Š” (M ๊ด€์ฐฐ์ž) * (N ์‚ฌ์ดํด) ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰ํ–ˆ์œผ๋‚˜ Angular 2๋Š” M ๊ฒ€์ฆ๋งŒ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Angular 2์˜ ์„ฑ๋Šฅ ํ–ฅ์ƒ์— ๊ณ ๋ คํ•ด์•ผ ํ•  ๋˜ ๋‹ค๋ฅธ ๋งค๊ฐœ ๋ณ€์ˆ˜๊ฐ€ ์žˆ๋‹ค. ํ”„๋ ˆ์ž„ ์›Œํฌ๊ฐ€ ์ด๋Ÿฌํ•œ ํ™•์ธ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐ ๊ฑธ๋ฆฐ ์‹œ๊ฐ„์ด๋‹ค. Google ํŒ€์€ ์ปดํ“จํ„ฐ ๊ณผํ•™ ๋ฐ ๊ฐ€์ƒ ์‹œ์Šคํ…œ์— ๋Œ€ํ•œ ๋ชจ๋“  ์ง€์‹์„ ํ™œ์šฉ ํ•˜์˜€๋‹ค.

์„ฑ๋Šฅ์ด ์–ด๋–ป๊ฒŒ ๊ฐœ์„ ๋˜์—ˆ๋Š”์ง€ ์ดํ•ดํ•˜๋ ค๋ฉด ๋‘ ๋ฒ„์ „์˜ ํ”„๋ ˆ์ž„ ์›Œํฌ์—์„œ ํ‘œํ˜„์‹์ด ํ‰๊ฐ€๋˜๊ณ  ๊ฐ’์ด ๋น„๊ต๋˜๋Š” ๋ฐฉ์‹์„ ๊ฒ€ํ† ํ•ด์•ผํ•œ๋‹ค.

๋ฒ„์ „ 1.x์—์„œ๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด ๋งค์šฐ ์ผ๋ฐ˜์ ์ด๋‹ค. ํ•˜๋‚˜์˜ ์ผ๋ฐ˜ ๋ฉ”์†Œ๋“œ๊ฐ€ ๋ชจ๋“  ์›Œ์ฒ˜์— ๋Œ€ํ•ด ํ˜ธ์ถœ๋˜๋ฉฐ ์ด์ „ ๊ฐ’๊ณผ ์ƒˆ ๊ฐ’์„ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฌธ์ œ๋Š” ๋ธŒ๋ผ์šฐ์ € (์˜ˆ : Google ํฌ๋กฌ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ V8)์—์„œ JavaScript ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฐ€์ƒ ๋จธ์‹ ์ด ์ œ๋„ค๋ฆญ ์ฝ”๋“œ๋ฅผ ์ •๋ง ์ข‹์•„ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

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

์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•ด ๋™์  JavaScript ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฐ€์ƒ ์‹œ์Šคํ…œ๊ณผ ๊ฐ™์€ ๊ฐ€์ƒ ์‹œ์Šคํ…œ์€ ์ธ๋ผ์ธ ์บ์‹ฑ์ด๋ผ๋Š” ์ „๋žต์„ ์‚ฌ์šฉํ•œ๋‹ค. 40 ๋…„ ์ „ (IT์—์„œ์˜ ์˜์›) SmallTalk๋ฅผ ์œ„ํ•ด ๊ณ ์•ˆ๋œ ์•„์ฃผ ์˜ค๋ž˜๋œ ๊ธฐ์ˆ ์ด๋‹ค. ํ”„๋กœ๊ทธ๋žจ์ด ํ•จ์ˆ˜๋ฅผ ์ž์ฃผ ํ˜ธ์ถœํ•˜๊ณ  ๋™์ผํ•œ ๋ชจ์–‘์„ ๊ฐ€์ง„ ๊ฐ์ฒด๋กœ VM์ด ํ‰๊ฐ€ํ•˜๋Š” ๋ฐฉ์‹์„ ๊ธฐ์–ตํ•ด์•ผ ํ•œ๋‹ค.

๋”ฐ๋ผ์„œ ์ด ๊ธฐ์ˆ ์€ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์ด๋ฆ„์ด ์ธ๋ผ์ธ ์บ์‹ฑ๋œ๋‹ค. ๊ฐ์ฒด๋ฅผ ๋ฐ›์œผ๋ฉด ์บ์‹œ์—์„œ ๊ฐ์ฒด์˜ ๋ชจ์–‘์„ ์ธ์‹ํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ๋ ‡๊ฒŒ ๋˜๋ฉด ์บ์‹œ ๋œ ์ตœ์ ํ™” ๋œ ๋ฐฉ๋ฒ•์œผ๋กœ ๊ฐ์ฒด์˜ ์†์„ฑ์— ์•ก์„ธ์Šค ํ•œ๋‹ค.

์ด๋Ÿฌํ•œ ์ข…๋ฅ˜์˜ ์บ์‹œ๋Š” ํ•จ์ˆ˜์˜ ์ธ์ˆ˜๊ฐ€ ๊ฐ™์€ ๋ชจ์–‘์„ ๊ฐ–๋Š”๋‹ค๋ฉด ์ •๋ง ์œ ์šฉํ•˜๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด {name : 'Cรฉdric'}๊ณผ {name : 'Cyril'}์˜ ๋ชจ์–‘์€ ๊ฐ™๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ {์ด๋ฆ„ : 'JB', ์Šคํ‚ฌ : []}์€ ๋‹ค๋ฅธ ๋‘ ๊ฐœ์˜ ์Šคํ‚ฌ๊ณผ ๊ฐ™์€ ๋ชจ์–‘์ด ์•„๋‹ˆ๋‹ค.

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

์ด์ œ AngularJS 1.x์˜ ๋ณ€๊ฒฝ ๊ฐ์ง€๋กœ ๋Œ์•„๊ฐ€ ๋ณด์ž. ๋ชจ๋“  ๊ด€์ฐฐ์ž์— ๋Œ€ํ•ด ํ˜ธ์ถœ ๋œ์ด ๊ณ ์œ  ํ•œ ์ œ๋„ค๋ฆญ ํ•จ์ˆ˜๊ฐ€ ์ธ๋ผ์ธ ์บ์‹ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ตœ์ ํ™” ํ•  ์ˆ˜ ์—†์Œ์„ ์•Œ์•˜๋‹ค. ์šฐ๋ฆฌ๋Š” ์ฝ”๋“œ๊ฐ€ ๊ฐ€์žฅ ๋Š๋ฆฐ ๊ฑฐ๋Œ€ํ•œ ์ƒํƒœ์— ์žˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ์ƒํ™ฉ์—์„œ ์ด๊ฒƒ์ด ๋ฌธ์ œ๊ฐ€ ๋˜์ง€๋Š” ์•Š์ง€๋งŒ ๋งŽ์€ ๊ด€์ฐฐ์ž๊ฐ€ ์žˆ๋Š” ์ผ๋ถ€ ํŽ˜์ด์ง€์—์„œ๋Š” ์„ฑ๋Šฅ์ด ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์€ ํ•œ๊ณ„์— ๋„๋‹ฌํ•˜๊ฒŒ ๋œ๋‹ค.

VM์˜ ์ธ๋ผ์ธ ์บ์‹ฑ ์ตœ์ ํ™”์˜ ์ด์ ์„ ์–ป๊ธฐ ์œ„ํ•ด Angular 2๋Š” ๋‹ค๋ฅธ ์ „๋žต์„ ์ฑ„ํƒํ–ˆ๋‹ค. ๋ชจ๋“  ์œ ํ˜•์˜ ๊ฐœ์ฒด๋ฅผ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๋Š” ๋‹จ์ผ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  ๋ชจ๋“  ์œ ํ˜•์˜ ๋น„๊ต๊ธฐ๋ฅผ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ•˜์˜€๋‹ค. ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์„ ์‹œ์ž‘ํ•  ๋•Œ ํ”„๋ ˆ์ž„ ์›Œํฌ๋Š” ๊ตฌ์„ฑ ์š”์†Œ ํŠธ๋ฆฌ๋ฅผ ๊ฑฐ์ณ ๊ฐ ๊ตฌ์„ฑ ์š”์†Œ์— ํŠน์ •ํ•œ ChangeDetectors ์„ธํŠธ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ๋ทฐ์— ์†์„ฑ ์ด๋ฆ„์ด ํ‘œ์‹œ๋œ ๊ตฌ์„ฑ ์š”์†Œ ์‚ฌ์šฉ์ž๊ฐ€ ์žˆ์œผ๋ฉด ํ”„๋ ˆ์ž„ ์›Œํฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ChangeDetector๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

class User_ChangeDetector {
    detectChanges() {
        if (this.name !== this.previousName) {
        this.previousName = this.name;
        hasChanged = true;
        }
    }
}

์ด ์ฝ”๋“œ๋Š” ์ง์ ‘ ์ž‘์„ฑํ•œ ์ฝ”๋“œ์™€ ์œ ์‚ฌํ•˜๋ฉฐ VM์ด ๋‹จ์ผํ˜•์ด๋ฏ€๋กœ ์ตœ์ ํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ํ›จ์”ฌ ๋” ๋น ๋ฅธ ์ฝ”๋“œ๊ฐ€ ๊ฐ€๋Šฅํ•ด์ง€๋ฏ€๋กœ ๋” ๋ณต์žกํ•œ ํŽ˜์ด์ง€๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค. ๋‹ค์‹œ ๋งํ•˜๋ฉด Angular 2๋Š” AngularJS 1.x๋ณด๋‹ค ์ ์€ ์ˆ˜์‹์„ ํ‰๊ฐ€ํ•˜๊ณ  ๋” ์ ์€ ๊ฐ’์„ ๋น„๊ตํ•ด์•ผํ•˜๋ฉฐ (๋‹จ์ผ ํŒจ์Šค๋กœ ์ถฉ๋ถ„ ํ•จ) ํ‰๊ฐ€์™€ ๋น„๊ต๊ฐ€ ๋” ๋นจ๋ผ์กŒ๋‹ค! Google ํŒ€์€ ์ฒ˜์Œ๋ถ€ํ„ฐ AngularJS 1.x, Angular 2 ๋ฐ Polymer and React๋ฅผ ๋‹ค์–‘ํ•œ ์œ ์Šค ์ผ€์ด์Šค์—์„œ ๋น„๊ต ํ•œ ๋ฒค์น˜ ๋งˆํฌ๋ฅผ ํ†ตํ•ด ์„ฑ๋Šฅ์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜์—ฌ ์ƒˆ๋กœ์šด ๋ฒ„์ „์ด ๋” ๋น ๋ฅด๊ฒŒ ์œ ์ง€๋˜๋Š”์ง€ ํ™•์ธํ–ˆ๋‹ค.

์‹ค์ œ๋กœ ํ•„์š”ํ•˜๋‹ค๋ฉด ํ”„๋ ˆ์ž„ ์›Œํฌ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ž๋™ ์ตœ์ ํ™”๋ณด๋‹ค ๋” ๋‚˜์•„๊ฐˆ ์ˆ˜๋„ ์žˆ๋‹ค. ChangeDetection ์ „๋žต์€ ๊ธฐ๋ณธ๊ฐ’์—์„œ ๋ณ€๊ฒฝ ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ํŠน์ • ์‚ฌ์šฉ ์‚ฌ๋ก€์— ๋งž๊ฒŒ ์กฐ์ • ๋ฐ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

Reference URL

Last updated