Backend/RubyOnRails

[๋ฒˆ์—ญ] Introduction to Ruby on Rails Patterns and Anti-patterns (Ruby On Rails์˜ ํŒจํ„ด๊ณผ ์•ˆํ‹ฐํŒจํ„ด ์†Œ๊ฐœ)

Seyun(Marco) 2024. 1. 2. 13:43
728x90

๐Ÿ’ก ์›๋ณธ ๊ธ€: https://blog.appsignal.com/2020/08/05/introduction-to-ruby-on-rails-patterns-and-anti-patterns.html

 

Introduction to Ruby on Rails Patterns and Anti-patterns | AppSignal Blog

Dig into the basics of design patterns and anti-patterns.

blog.appsignal.com

 

Ruby On Rails ํŒจํ„ด ๋ฐ ์•ˆํ‹ฐํŒจํ„ด ์‹œ๋ฆฌ์ฆˆ์˜ ์ฒซ ๋ฒˆ์งธ ๊ฒŒ์‹œ๋ฌผ์— ์˜ค์‹  ๊ฒƒ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ์‹œ๋ฆฌ์ฆˆ์—์„œ๋Š” Rails ์•ฑ์„ ๊ฐœ๋ฐœํ•˜๋Š” ๋™์•ˆ ์ ‘ํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ์ข…๋ฅ˜์˜ ํŒจํ„ด์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์˜ค๋Š˜ ์šฐ๋ฆฌ๋Š” (๋””์ž์ธ) ํŒจํ„ด์ด ๋ฌด์—‡์ธ์ง€ ๋ณด์—ฌ์ฃผ๊ณ  ์•ˆํ‹ฐํŒจํ„ด์ด ๋ฌด์—‡์ธ์ง€ ์„ค๋ช…ํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ•  ๊ฒƒ ์ž…๋‹ˆ๋‹ค. ์„ค๋ช…์— ๋Œ€ํ•œ ์ดํ•ด๋ฅผ ๋†’์ด๊ธฐ ์œ„ํ•ด, ์˜ค๋žœ ์—ญ์‚ฌ๋ฅผ ๊ฐ€์ง„ Ruby on Rails ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‹น์‹ ์ด Rails๋ฅผ ์„ ํ˜ธํ•˜์ง€ ์•Š๋”๋ผ๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ ๋‹ค๋ฃจ๋Š” ์•„์ด๋””์–ด(๋˜๋Š” ํŒจํ„ด)๋“ค์€ ๋‹ค๋ฅธ ๊ธฐ์ˆ ์—์„œ๋„ ์œ ์šฉํ•˜๊ฒŒ ํ™œ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ํŒจํ„ด๊ณผ ์•ˆํ‹ฐํŒจํ„ด์— ๋Œ€ํ•ด ์„ค๋ช…ํ•˜๊ธฐ ์ „์—, ์™œ ๋จผ์ € ์šฐ๋ฆฌ๊ฐ€ ์ด๋Ÿฐ ๊ฐœ๋…๋“ค์ด ํ•„์š”ํ•œ ์ด์œ ๊ฐ€ ๋ฌด์—‡์ผ๊นŒ์š”? ์†Œํ”„ํŠธ์›จ์–ด๋Š” ์ด๋Ÿฌํ•œ ๊ฒƒ์ด ํ•„์š”ํ•œ ์ด์œ ๊ฐ€ ๋ฌด์—‡์ผ๊นŒ์š”? ์šฐ๋ฆฌ ์†”๋ฃจ์…˜์€ ์™œ ๋””์ž์ธ(์„ค๊ณ„)๊ฐ€ ํ•„์š”ํ• ๊นŒ์š”?

๋งž์Šต๋‹ˆ๋‹ค. ๋‹น์‹ ์€ ๋””์ž์ด๋„ˆ์ž…๋‹ˆ๋‹ค.

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

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

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

๋””์ž์ธ ํŒจํ„ด์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

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

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

์ด ํŒจํ„ด ์–ธ์–ด์˜ ์•„์ด๋””์–ด๋Š” 1970๋…„๋Œ€ ํ›„๋ฐ˜ Christopher Alexander์˜ ์ €์„œ์ธ A pattern Language์—์„œ ์ฒ˜์Œ ๋“ฑ์žฅํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๋†€๋ž๊ฒŒ๋„ ์ด ์ฑ…์€ ์†Œํ”„ํŠธ์›จ์–ด ์—”์ง€๋‹ˆ์–ด๋ง์ด ์•„๋‹ˆ๋ผ ๊ฑด์ถ•์— ๊ด€ํ•œ ์ฑ…์ž…๋‹ˆ๋‹ค. ํŒจํ„ด ์–ธ์–ด๋Š” ์ฒด๊ณ„์ ์ด๊ณ  ์ผ๊ด€์„ฑ ์žˆ๋Š” ํŒจํ„ด์˜ ์ง‘ํ•ฉ์ด๋ฉฐ, ๊ฐ.ํŒจํ„ด์€ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ์™€ ํ•ด๊ฒฐ์ฑ…์˜ ํ•ต์‹ฌ์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ๋ญ”๊ฐ€ ์ต์ˆ™ํ•˜๊ฒŒ ๋“ค๋ฆฌ์ง€ ์•Š๋‚˜์š”? (์˜ˆ: ํ”„๋ ˆ์ž„์›Œํฌ, Rails)

์ดํ›„ ์†Œํ”„ํŠธ์›จ์–ด ์—”์ง€๋‹ˆ์–ด๋ง์˜ ๋””์ž์ธ ํŒจํ„ด์€ 1994๋…„ ์ถœํŒ๋œ Gang Of Four์˜ ์ „์„ค์ ์ธ ์ฑ… Design Partterns๋ฅผ ํ†ตํ•ด ๋Œ€์ค‘์—๊ฒŒ ๋„๋ฆฌ ์•Œ๋ ค์ง€๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ์ฑ…์—๋Š” ํ˜„์žฌ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š” Factory, Singleton, Decorator ๋“ฑ์˜ ํŒจํ„ด์— ๋Œ€ํ•œ ์„ค๋ช…๊ณผ ์ •์˜๊ฐ€ ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ ๋””์ž์ธ ํŒจํ„ด์— ๋Œ€ํ•œ ์ง€์‹์„ ์ตํžˆ๊ฑฐ๋‚˜ ๋˜์ƒˆ๊ฒผ์œผ๋‹ˆ, ์•ˆํ‹ฐ ํŒจํ„ด์ด ๋ฌด์—‡์ธ์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์•ˆํ‹ฐํŒจํ„ด์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

๋งŒ์•ฝ ํŒจํ„ด์„ ์ข‹์€ ์šฉ์‚ฌ๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค๋ฉด, ์•ˆํ”ผํŒจํ„ด์€ ์•…๋‹น์ด๋ผ๊ณ  ํ•  . ์ˆ˜์žˆ์Šต๋‹ˆ๋‹ค. ์ข€ ๋” ์ •ํ™•ํ•˜๊ฒŒ ๋งํ•˜์ž๋ฉด, ์†Œํ”„ํŠธ์›จ์–ด์˜ ์•ˆํ‹ฐํŒจํ„ด์€ ํ”ํžˆ ์‚ฌ์šฉ๋˜์ง€๋งŒ ๋น„ ํšจ์œจ์ ์ด๋‚˜ ๋น„ ์ƒ์‚ฐ์ ์ธ ํŒจํ„ด์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์•ˆํ‹ฐํŒจํ„ด์˜ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ๋กœ๋Š” ๋งŽ์€ ํ•จ์ˆ˜์™€ ์˜์กด์„ฑ์„ ํฌํ•จํ•˜๊ณ  ์žˆ์–ด ๋‹ค๋ฅธ ๊ฐ์ฒด๋กœ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” God ๊ฐ์ฒด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

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

ํ•˜์ง€๋งŒ, ํ”„๋ก ํŠธ์—”๋“œ ์•ฑ์ด ๊ทธ๋‹ค์ง€ ๋ณต์žกํ•˜์ง€ ์•Š์œผ๋ฉฐ ์ด๋ฅผ ์ปจํ…Œ์ด๋„ˆ์— ๋„ฃ๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ํšจ๊ณผ์ ์ธ ํ•ด๊ฒฐ์ฑ…์ด ์•„๋‹ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒ˜์Œ์—๋Š” ์ข‹์€ ์†Œ์‹œ๋กœ ๋“ค๋ฆด์ˆ˜ ์žˆ์ง€๋งŒ, ๋‚˜์ค‘์—๋Š” ๋น„ ์ƒ์‚ฐ์ ์ธ ๊ฒƒ์œผ๋กœ ํŒ๋ช…๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์•ˆํ‹ฐํŒจํ„ด์—์„œ๋„ Golden Hammer(ํ™ฉ๊ธˆ ๋ง์น˜)๋ผ๊ณ  ๋ถˆ๋ฆฝ๋‹ˆ๋‹ค.

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

์ด๋Ÿฐ ์ผ์€ ์ข…์ข… ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์„ ํ•œ ์šฉ์‚ฌ๊ฐ€ ์•…๋‹น์œผ๋กœ ๋ณ€ํ•˜๊ธฐ๋„ ํ•˜๊ณ , ๊ทธ ๋ฐ˜๋Œ€๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Ruby์™€ Rails๋Š” ์ด ๊ทธ๋ฆผ์—์„œ ์–ด๋–ค ์œ„์น˜์— ์žˆ์„๊นŒ์š”?

๋จผ์ € Ruby, ๊ทธ ๋‹ค์Œ์— Rails

๋Œ€๋ถ€๋ถ„ ์‚ฌ๋žŒ๋“ค์€ ๋น ๋ฅด๊ฒŒ ์›น ์‚ฌ์ดํŠธ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ธฐ ์œ„ํ•ด Ruby On Rails๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Ruby๋ฅผ ๋ฐฐ์šฐ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ €๋„ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ Ruby๋ฅผ ๋ฐฐ์šฐ๊ฒŒ ๋˜์—ˆ๊ณ , ์ด๋Š” ๋‚˜์œ๊ฒŒ ์•„๋‹™๋‹ˆ๋‹ค. Rails๋Š” ์ž˜ ์ •๋ฆฝ๋œ ์†Œํ”„ํŠธ์›จ์–ด ํŒจํ„ด์ธ MVC(Model-View-Controller)๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Rails์—์„œ MVC ํŒจํ„ด์˜ ์„ธ๋ถ€์‚ฌํ•ญ์„ ์‚ดํŽด๋ณด๊ธฐ ์ „์— ํ”ํžˆ ๋ฐœ์ƒํ•˜๋Š” ํฐ ์˜ค๋ฅ˜๋Š” Ruby๋ฅผ ์ œ๋Œ€๋กœ ๋ฐฐ์šฐ์ง€ ์•Š๊ณ  Rails๋งŒ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Rails ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์•„์ด๋””์–ด๊ฐ€ ๋– ์˜ฌ๋ž์„ ๋•Œ ๋น ๋ฅด๊ฒŒ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ์ตœ๊ณ ์˜ ์„ ํƒ ์ค‘ ํ•˜๋‚˜์˜€์Šต๋‹ˆ๋‹ค. ์˜ค๋Š˜๋‚  Rails๋Š” ์—ฌ์ „ํžˆ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์ง€๋งŒ ์ตœ์ „์„ฑ๊ธฐ ๋งŒํผ์˜ ์ธ๊ธฐ๋ฅผ ์œ ์ง€ํ•˜์ง€ ๋ชปํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ๊ณผ ์‹คํ–‰์ด ๋งค์šฐ ๊ฐ„๋‹จํ•˜๋‹ค๋Š” ์  ๋•Œ๋ฌธ์— ๋งŽ์€ ์ดˆ๋ณด์ž๋“ค์ด rails new ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›น์•ฑ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ดˆ๋ณด์ž๋กœ์„œ Rails์˜ ๋น ๋ฅด๊ณ  ๊ฐ„๋‹จํ•œ ๊ฐœ๋ฐœ ์†๋„์— ๋งค๋ฃŒ๋˜๊ณ  ์ฒ˜์Œ์—๋Š” ๋ชจ๋“  ๊ฒƒ์ด ๋งˆ๋ฒ•์ฒ˜๋Ÿผ ์›ํ• ํ•˜๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ‘๋งˆ๋ฒ•’์— ๋„ˆ๋ฌด ์˜์กดํ•˜๊ฒŒ ๋˜๊ณ  ํ”„๋ ˆ์ž„์›Œํฌ ๋‚ด๋ถ€์—์„œ ์–ด๋–ค ์ผ์ด ์ผ์–ด๋‚˜๋Š”์ง€ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ €๋Š” ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ์ง์ ‘ ๊ฒช์—ˆ๊ณ , ๋งŽ์€ ์ดˆ๋ณด์ž์™€ ์ค‘๊ธ‰ ๊ฐœ๋ฐœ์ž๋“ค์ด ์ด๋กœ ์ธํ•ด ๊ณ ํ†ต์„ ๋ฐ›๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์— ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ•˜๊ณ  ๊ทธ ์œ„์— ๊ธฐ๋Šฅ์„ ์Œ“์•„ ๊ฐ€๋‹ค๊ฐ€, ๊ณ ๋„๋กœ ๋งž์ถคํ™”๋œ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋ ค๊ณ  ํ•  ๋•Œ, ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋งˆ๋ฒ•์„ ๋‹ค ์จ๋ฒ„๋ ธ๊ธฐ ๋•Œ๋ฌธ์— ๋” ์ด์ƒ ์ง„ํ–‰ํ•  ์ˆ˜ ์—†๊ฒŒ ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์‹œ์ ์—์„œ ๋˜๋Œ์•„๊ฐ€์„œ ๊ธฐ๋ณธ์„ ๋ฐฐ์šฐ๋Š” ๊ฒƒ์€ ์–ด๋ ค์šด์ผ์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋ˆ„๊ตฌ์—๊ฒŒ๋‚˜ ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋Š” ์ผ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Ruby์™€ ๊ฐ™์€ ํ•„์ˆ˜์ ์ธ ์‚ฌํ•ญ์„ ๋ฐฐ์šฐ์ง€ ์•Š๊ณ  ์•ž์œผ๋กœ ๋‚˜์•„๊ฐ€๋ฉด ๋ฌธ์ œ๊ฐ€ ๋”์šฑ ์‹ฌ๊ฐํ•ด์ง‘๋‹ˆ๋‹ค. ์ด์™€ ๊ด€๋ จํ•˜์—ฌ ์ถ”์ฒœํ•˜๋Š” ์ข‹์€ ์ฑ…์€ “The Well-Grounded Rubyist” ์ž…๋‹ˆ๋‹ค.

์ดˆ๋ณด์ž๋ผ๋ฉด ์ฒ˜์Œ๋ถ€ํ„ฐ ๋๊นŒ์ง€ ๋‹ค ์ฝ์„ ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ถ๊ธˆํ•  ๋•Œ๋งˆ๋‹ค ์‰ฝ๊ฒŒ ์ฐธ๊ณ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ฐ€๊นŒ์ด์— ๋‘๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฒƒ์„ ๋ฉˆ์ถ”๊ณ  ์ฑ… ์ „์ฒด๋ฅผ ์ฝ์œผ๋ผ๊ณ  ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ๊ฐ€๋”์‹ ๋ฉˆ์ถ”์–ด Ruby ๊ธฐ๋ณธ ์ง€์‹์„ ๋˜์ƒˆ๊ธฐ๋Š” ๊ฒƒ์ด ์ƒˆ๋กœ์šด ์ง€ํ‰์„ ์—ด์–ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

MVC: Rails์˜ ๋นต๊ณผ ๋ฒ„ํ„ฐ

์ข‹์•„์š” ๊ทธ๋Ÿผ MVC๋Š” ์–ด๋–ค๊ฐ€์š”? Model-View-Controller ํŒจํ„ด์€ ์˜ค๋žซ๋™์•ˆ ์‚ฌ์šฉ๋˜์–ด ์™”์Šต๋‹ˆ๋‹ค. Python(Djnago), Java(Play, Spring MVC), Ruby(Rails) ๋“ฑ๊ณผ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ๋งŽ์€ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ฑ„ํƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ํŒจํ„ด์˜ ํ•ต์‹ฌ์€ ๊ฐ์ž ๊ณ ์œ ํ•œ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ถ„๋ฆฌ๋œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๊ฐ™์€ ๊ฒƒ์ž…๋‹ˆ๋‹ค.:

  • Model์€ ๋ฐ์ดํ„ฐ์™€ ๋น„์ง€๋‹ˆ์Šค ๋ชจ๋ธ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  • View๋Š” ๋ฐ์ดํ„ฐ์™€ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ‘œํ˜„์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
  • Controller๋Š” Model์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ View๋ฅผ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์คŒ์œผ๋กœ์จ ์ด ๋‘ ๊ฐ€์ง€๋ฅผ ํ•˜๋‚˜๋กœ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

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

MVC๋Š” ์›น ๊ฐœ๋ฐœ ์ปค๋ฎค๋‹ˆํ‹ฐ ์ „๋ฐ˜์— ๊ฑธ์ณ ๊ธ‰์†๋„๋กœ ํผ์ ธ๋‚˜๊ฐ”์Šต๋‹ˆ๋‹ค. ์š”์ฆ˜ ๋ฏธ์นœ๋“ฏ์ด ์ธ๊ธฐ ์žˆ๋Š” React์™€ ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์กฐ์ฐจ๋„ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ View ๊ณ„์ธต์œผ๋กœ ์„ค๋ช…๋ฉ๋‹ˆ๋‹ค. ๊ทธ ์–ด๋–ค ํŒจํ„ด๋„ ๋Œ€์ค‘ํ™”๋˜์–ด ๋–จ์ณ๋ฒ„๋ฆด ์ˆ˜ ์—†๋Š” ํŒจํ„ด์€ ์—†์Šต๋‹ˆ๋‹ค. Rails๋Š” ActionCable์— Publish-Subscribe๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋Š”๋ฐ ์—ฌ๊ธฐ์„œ ์ฑ„๋„์˜ ๊ฐœ๋…์€ MVC ํŒจํ„ด์˜ ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์„ค๋ช…์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ํŒจํ„ด์—์„œ ์•ˆํ‹ฐํŒจํ„ด์€ ๋ฌด์—‡์ผ๊นŒ์š”? MVC ํŒจํ„ด์˜ ๊ฐ ๋ถ€๋ถ€๋„น ๋Œ€ํ•œ ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ์•ˆํ‹ฐ ํŒจํ„ด์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Model ๋ฌธ์ œ

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๊ฑฐ๋Œ€ํ•ด์ง€๊ณ  ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์ด ์ž๋ผ๋‚  ์ˆ˜๋ก ์‚ฌ๋žŒ๋“ค์€ ๋ชจ๋ธ์— ๊ณผํ•˜๊ฒŒ ์ฑ„์›Œ๋„ฃ๋Š” ๊ฒฝํ–ฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ง€์†์ ์ธ ์ฆ๊ฐ€๋Š” ‘Fat Model’์ด๋ผ๋Š” ์•ˆํ‹ฐํŒจํ„ด์œผ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์œ ๋ช…ํ•œ ‘Fat Model, Skinny Controller’ ํŒจํ„ด์€ ์ผ๋ถ€์—์„œ๋Š” ์„ ํ•œ ์šฉ์‚ฌ๋กœ ์ผ๋ถ€์—์„œ๋Š” ์•…๋‹น์œผ๋กœ ์—ฌ๊ฒจ์ง‘๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋šฑ๋šฑํ•จ(fat) ์ž์ฒด๊ฐ€ ์•ˆํ‹ฐํŒจํ„ด์ด๋ผ๊ณ  ๋งํ•  ๊ฒƒ ์ž…๋‹ˆ๋‹ค. ์ดํ•ด๋ฅผ ๋•๊ธฐ ์œ„ํ•ด ์˜ˆ์‹œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. Spotify ๋˜๋Š” Deezer์™€ ๊ฐ™์€ ์ŠคํŠธ๋ ˆ๋ฐ ์„œ๋น„์Šค๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ด…์‹œ๋‹ค. ์ด ์„œ๋น„์Šค์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋…ธ๋ž˜ ๋ชจ๋ธ์ด ์žˆ์Šต๋‹ˆ๋‹ค:

class Song < ApplicationRecord
  belongs_to :album
  belongs_to :artist
  belongs_to :publisher

  has_one :text
  has_many :downloads

  validates :artist_id, presence: true
  validates :publisher_id, presence: true

  after_update :alert_artist_followers
  after_update :alert_publisher

  def alert_artist_followers
    return if unreleased?

    artist.followers.each { |follower| follower.notify(self) }
  end

  def alert_publisher
    PublisherMailer.song_email(publisher, self).deliver_now
  end

  def includes_profanities?
    text.scan_for_profanities.any?
  end

  def user_downloaded?(user)
    user.library.has_song?(self)
  end

  def find_published_from_artist_with_albums
    ...
  end

  def find_published_with_albums
    ...
  end

  def to_wav
    ...
  end

  def to_mp3
    ...
  end

  def to_flac
    ...
  end
end

์ด์™€ ๊ฐ™์€ ๋ชจ๋ธ์˜ ๋ฌธ์ œ์ ์€ ๋…ธ๋ž˜์™€ ๊ด€๋ จ๋œ ๋‹ค์–‘ํ•œ ๋กœ์ง์˜ ์“ฐ๋ ˆ๊ธฐ์žฅ์ด ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ ๋ฉ”์„œ๋“œ๊ฐ€ ํ•˜๋‚˜์”ฉ ์ฒœ์ฒœํžˆ ์ถ”๊ฐ€๋˜๋ฉด์„œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์ „์ฒด ๋ชจ๋ธ์ด ํฌ๊ณ  ๋ณต์žกํ•ด ๋ณด์ด๋ฏ€๋กœ ๋กœ์ง์„ ๋‹ค๋ฅธ ๋ช‡ ๊ตฐ๋ฐ๋กœ ๋ถ„ํ• ํ•˜๋ฉด ๋‚˜์ค‘์— ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ชจ๋ธ์ด ์œ„๋ฐ˜ํ•˜๊ณ  ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ๊ถŒ์žฅ ์‚ฌ๋ก€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ชจ๋ธ์€ ๋‹จ์ผ ์ฑ…์ž„ ์›์น™(SRP)๋ฅผ ์œ„๋ฐ˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

ํŒ”๋กœ์šฐ์™€ ๊ฒŒ์‹œ์ž์—๊ฒŒ ์•Œ๋ฆผ์„ ๋ณด๋‚ด๋Š” ์ž‘์—…์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ํ…์ŠคํŠธ์—์„œ ์š•์„ค์„ ํ™•์ธํ•˜๊ณ , ๋…ธ๋ž˜๋ฅผ ๋‹ค๋ฅธ ์˜ค๋””์˜ค ํ˜•์‹์œผ๋กœ ๋‚ด๋ณด๋‚ด๋Š” ๋ฐฉ๋ฒ• ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ชจ๋“  ๊ฒƒ์ด ๋ชจ๋ธ์˜ ๋ณต์žก์„ฑ์„ ์ฆ๊ฐ€์‹œํ‚ค๋ฉฐ, ์ด ๋ชจ๋ธ์˜ ํ…Œ์ŠคํŠธ ํŒŒ์ผ์€ ์ƒ์ƒ์กฐ์ฐจ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

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

ํŒ”๋กœ์›Œ์™€ ๊ฒŒ์‹œ์ž์—๊ฒŒ ์•Œ๋ฆฌ๋Š” ์ฝœ๋ฐฑ์€ Job์œผ๋กœ ์ถ”์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Job์ด ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€๋˜๊ณ  ๋กœ์ง์ด ๋ชจ๋ธ์—์„œ ์ œ์™ธ๋ฉ๋‹ˆ๋‹ค:

class NotifyFollowers < ApplicationJob
  def perform(followers)
    followers.each { |follower| follower.notify }
  end
end

class NotifyPublisher < ApplicationJob
  def perform(publisher, song)
    PublisherMailer.song_email(publisher, self).deliver_now
  end
end

Job์€ ๋ชจ๋ธ๊ณผ ๋ณ„๊ฐœ๋กœ ๋ณ„๋„์˜ ํ”„๋กœ์„ธ์Šค์—์„œ ์ž์ฒด์ ์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด์ œ Job ๋กœ์ง์„ ๋ณ„๋„๋กœ ํ…Œ์ŠคํŠธํ•˜๊ณ  ๋ชจ๋ธ์—์„œ ์ ์ ˆํ•œ Job์ด ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์š•์„ค ํ™•์ธ๊ณผ ์‚ฌ์šฉ์ž๊ฐ€ ๋…ธ๋ž˜๋ฅผ ๋‹ค์šด๋กœ๋“œ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋Š” ์ž‘์—…์ด ๋ชจ๋‘ ์•ฑ์˜ ๋ทฐ ๋ถ€๋ถ„์—์„œ ๋ฐœ์ƒํ•œ๋‹ค๊ณ  ๊ฐ€์ • ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ Decorator ํŒจํ„ด์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋น ๋ฅด๊ฒŒ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ์ธ๊ธฐ์žˆ๋Š” ํ•ด๊ฒฐ์ฑ…์€ Draper gem์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์Œ๊ณผ ์œ ์‹œํ•œ Decorator๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

class SongDecorator < Draper::Decorator
  delegate_all

  def includes_profanities?
    object.text.scan_for_profanities.any?
  end

  def user_downloaded?(user)
    object.user.library.has_song?(self)
  end
end

๊ทธ ๋‹ค์Œ์—, ์ปจํŠธ๋กค๋Ÿฌ์—์„œ decorate ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ๋“ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค:

def show
  @song = Song.find(params[:id]).decorate
end

๊ทธ๋ฆฌ๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด view์—์„œ ์‚ฌ์šฉํ•ด๋ณด์„ธ์š”:

<%= @song.includes_profanities? %>
<%= @song.user_downloaded?(user) %>

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

module SongFinders
  def find_published_from_artist_with_albums
    ...
  end

  def find_published_with_albums
    ...
  end
end

module SongConverter
  def to_wav
    ...
  end

  def to_mp3
    ...
  end

  def to_flac
    ...
  end
end

Song ๋ชจ๋ธ์€ SongFinders ๋ชจ๋“ˆ์„ ํ™•์žฅํ•˜๋ฏ€๋กœ ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋กœ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Song ๋ชจ๋ธ์—๋Š” SongConverter ๋ชจ๋“ˆ์ด ํฌํ•จ๋˜๋ฏ€๋กœ ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค์—์„œ ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ชจ๋“  ๊ฒƒ์ด ์šฐ๋ฆฌ์˜ Song ๋ชจ๋ธ์„ ๋งค์šฐ ๋‚ ์”ฌํ•˜๊ณ  ์™„๋ฒฝํ•˜๊ฒŒ ๋งŒ๋“ค์–ด ์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค:

class Song < ApplicationRecord
  extend SongFinders
  include SongConverter

  belongs_to :album
  belongs_to :artist
  belongs_to :publisher

  has_one :text
  has_many :downloads

  validates :artist_id, presence: true
  validates :publisher_id, presence: true

  after_update :alert_artist_followers, if: :published?
  after_update :alert_publisher

  def alert_artist_followers
    NotifyFollowers.perform_later(self)
  end

  def alert_publisher
    NotifyPublisher.perform_later(publisher, self)
  end
end

๋” ๋งŽ์€ ์•ˆํ‹ฐํŒจํ„ด๋“ค์ด ์žˆ์œผ๋ฉฐ ์ด๋Š” ๋ชจ๋ธ์„ ํ†ตํ•ด ์–ป์„ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ค‘์— ํ•˜๋‚˜์˜ ์˜ˆ์‹œ์ผ ๋ฟ์ด์˜€์Šต๋‹ˆ๋‹ค. ์ด ์‹œ๋ฆฌ์ฆˆ์˜ ๋‹ค๋ฅธ ๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๋ฌผ์„ ๊ณ„์† ์ง€์ผœ๋ด ์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ๋” ๋งŽ์€ ๋ชจ๋ธ ์•ˆํ‹ฐ ํŒจํ„ด์— ๋Œ€ํ•ด์„œ ์ž์„ธํžˆ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ view์— ์–ด๋–ค ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

View ๋ฌธ์ œ

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

์–ด์จŒ๋“  HTML, CSS, JS, Ruby๋ฅผ ํ•œ ๊ณณ์— ๋‹ค๋ฃจ๋Š” ๊ฒƒ์€ ๋ณต์žกํ•ด ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Rails view๋ฅผ ๋งŒ๋“œ๋Š”๋ฐ ์–ด๋ ค์šด ์ ์€ ๋„๋ฉ”์ธ ๋กœ์ง์ด ๋•Œ๋•Œ๋กœ view ๋‚ด๋ถ€์—์„œ ์ฐพ์„์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด๋Š” MVC ํŒจํ„ด์„ ๊นจ๋œจ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ธˆ์ง€๋œ ํ–‰์œ„์ž…๋‹ˆ๋‹ค.

๋˜ ๋‹ค๋ฅธ ๊ฒฝ์šฐ๋Š” view์™€ ๋ถ€๋ถ„ ํ…œํ”Œ๋ฆฟ์— ๋„ˆ๋ฌด ๋งŽ์€ ์ž„๋ฒ ๋””๋“œ ruby๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ผ๋ถ€ ๋กœ์ง์€ ํ—ฌํผ๋‚˜ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ (view ๋ชจ๋ธ ๋˜๋Š” ํ”„๋ฆฌ์  ํ„ฐ๋ผ๊ณ  ํ•จ) ๋‚ด๋ถ€์— ๋“ค์–ด ๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ์˜ˆ์‹œ๋Š” ์ด ์‹œ๋ฆฌ์ฆˆ ๋‹ค์Œ ๊ฒŒ์‹œ๋ฌผ์—์„œ ๋‹ค๋ฃฐ ์˜ˆ์ •์ด๋‹ˆ ๊ณ„์† ์ฃผ๋ชฉํ•ด์ฃผ์„ธ์š”.

Controller ๋ฌธ์ œ

Rails ์ปจํŠธ๋กค๋Ÿฌ ๋˜ํ•œ ๋‹ค์–‘ํ•œ ๋ฌธ์ œ์— ์‹œ๋‹ฌ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ค‘ ํ•˜๋‚˜๊ฐ€ ‘Fat Controller’ ์•ˆํ‹ฐ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.

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

์–ด๋–ค ์‚ฌ๋žŒ๋“ค์€ Trailblazer๋‚˜ dry-transcation๊ณผ ๊ฐ™์€ ๋ณด์„์„ ์‚ฌ์šฉํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ์˜ ์•„์ด๋””์–ด๋Š” ํŠน์ • ํŠธ๋žœ์žญ์…˜์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฒƒ์„ ์ปจํŠธ๋กค๋Ÿฌ ๋ฐ–์œผ๋กœ ์˜ฎ๊ธฐ๊ณ  ๋ชจ๋ธ์„ ๊ฐ€๋ณ๊ฒŒ ์œ ์ง€ํ•˜๋ฉด ์ด๋Ÿฌํ•œ ๋ณ„๋„์˜ ํด๋ž˜์Šค ์•ˆ์— ๋กœ์ง์„ ์ €์žฅํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํด๋ž˜์Šค๋ฅผ ์„œ๋น„์Šค, ํŠธ๋žœ์žญ์…˜, ์•ก์…˜ ๋“ฑ์œผ๋กœ ๋ถˆ๋ฆฝ๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

๋” ๋งŽ์€ ์•ˆํ‹ฐํŒจํ„ด๊ณผ ์ด์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒŒ์‹œ๋ฌผ์—์„œ ๋ชจ๋“  ๋‚ด์šฉ์„ ๋‹ค๋ฃจ๋ ค๊ณ  ํ•˜๋ฉด ๊ณต๊ฐ„๊ณผ ์‹œ๊ฐ„์ด ๋„ˆ๋ฌด ๋งŽ์ด ๊ฑธ๋ฆฌ๊ณ  ๋šฑ๋šฑํ•ด ๋ณด์ผ๊ฒƒ์ž…๋‹ˆ๋‹ค.(์šฐ๋ฆฌ๊ฐ€ ์ด์•ผ๊ธฐํ•œ ๋ชจ๋ธ ๋ฐ ์ปจํŠธ๋กค๋Ÿฌ ์ฒ˜๋Ÿผ) Rails MVC ํŒจํ„ด์˜ ๋ชจ๋“  ์ธก๋ฉด์„ ์ž์„ธํžˆ ์•Œ์•„๋ณด๋Š” ์‹œ๋ฆฌ์ฆˆ๋ฅผ ๊ผญ ์‹œ์ฒญํ•ด๋ณด์„ธ์š”. ๊ฑฐ๊ธฐ์„œ ๊ฐ€์žฅ ์œ ๋ช…ํ•œ ์•ˆํ‹ฐํŒจํ„ด์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์šฐ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋•Œ๊นŒ์ง€๋Š” ํŒจํ„ด๊ณผ ์•ˆํ‹ฐํŒจํ„ด์ด ๋ฌด์—‡์ธ์ง€, ๊ทธ๋ฆฌ๊ณ  Ruby On Rails ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ํŒจํ„ด์— ๋Œ€ํ•œ ๊ฐœ์š”๋ฅผ ์žฌ๋ฏธ์žˆ๊ฒŒ ์ฝ์œผ์…จ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๋‹ค์Œ ์‹œ๊ฐ„๊นŒ์ง€, ๊ฑด๋ฐฐ!

PS. Ruby Magic ๊ฒŒ์‹œ๋ฌผ์ด ๋ณด๋„๋˜๋Š” ๋Œ€๋กœ ์ฝ๊ณ  ์‹ถ์œผ์‹œ๋ฉด Ruby Magic ๋‰ด์Šค๋ ˆํ„ฐ๋ฅผ ๊ตฌ๋…ํ•˜์‹œ๊ณ  ๋‹จ ํ•˜๋‚˜์˜ ๊ฒŒ์‹œ๋ฌผ๋„ ๋†“์น˜์ง€ ๋งˆ์„ธ์š”!

728x90
728x90