์ด ๊ธ€์€ Glen Maddern์ด ์ž‘์„ฑํ•œ CSS Modules๋ผ๋Š” ๊ธ€์„ ํ•œ๊ธ€๋กœ ๋ฒˆ์—ญํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ด€์‹ฌ์ด ์ƒ๊ธฐ์‹ ๋‹ค๋ฉด ์›๋ฌธ๋„ ์ฝ์œผ์‹œ๋Š” ๊ฒƒ์„ ์ถ”์ฒœ๋“œ๋ฆฝ๋‹ˆ๋‹ค.


๋ฏธ๋ž˜์— ์˜ค์‹  ๊ฒƒ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค

์ตœ๊ทผ CSS๊ฐœ๋ฐœ์˜ ์ „ํ™˜์ ์„ ๊ผฝ๋Š”๋‹ค๋ฉด, ์•„๋งˆ 2014๋…„ 11์›”์— ์žˆ์—ˆ๋˜ Christopher Chedeau์˜ "CSS in JS"๋ฐœํ‘œ๋ฅผ ์„ ํƒํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‹น์‹œ๋Š” ๋งˆ์น˜ ๊ณ ์—๋„ˆ์ง€ ์ถฉ๋Œ ํ›„์— ์ž…์ž๋“ค์ด ์ œ๊ฐ๊ฐ์˜ ๋ฐฉํ–ฅ์œผ๋กœ ํฉ์–ด์ง€๋“ฏ์ด, ์„œ๋กœ ๋‹ค๋ฅธ ์ƒ๊ฐ๋“ค์ด ํฉ์–ด์ ธ ์žˆ๋˜ ๊ณผ๋„๊ธฐ์˜€์Šต๋‹ˆ๋‹ค. ์˜ˆ์ปจ๋Œ€ React Style, jsxstyle, Radium๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด React์—์„œ์˜ ์Šคํƒ€์ผ๋ง์— ์žˆ์–ด ๊ฐ€์žฅ ์ตœ์‹ ์˜, ์ž˜ ๋งŒ๋“ค์–ด์ง„, ์“ธ๋งŒํ•œ ๊ฒƒ๋“ค์ด์—ˆ์Šต๋‹ˆ๋‹ค(๊ทธ๋“ค์ด ํ”„๋กœ์ ํŠธ ReadmeํŒŒ์ผ์— ๋ช…์‹œํ•ด๋‘์—ˆ๋“ฏ์ด ๋ง์ด์ฃ ). ๋งŒ์•ฝ ๋ฐœ๋ช…์ด๋ผ๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ธ์ ‘ํ•œ ๊ฐ€๋Šฅ์„ฑ์„ ์ฐพ์•„๊ฐ€๋Š” ๊ณผ์ •์ด๋ผ๋ฉด, Christopher๋Š” ์ˆ˜๋งŽ์€ ๊ทธ๋Ÿฌํ•œ ๊ฐ€๋Šฅ์„ฑ๋“ค์„ ๋”์šฑ ์ธ์ ‘ํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ˆ˜๋งŽ์€ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ์ด ์Šฌ๋ผ์ด๋“œ๋Š” ์ •๋ง ํ™• ์™€๋‹ฟ์•˜์„ ๊ฒ๋‹ˆ๋‹ค.

๋ฒˆ์—ญ: CSS๊ฐ€ ํ™•์žฅํ•ด๊ฐ์— ๋”ฐ๋ผ ์ƒ๊ธฐ๋Š” ๋ฌธ์ œ๋“ค

  1. ์ „์—ญ ๋„ค์ž„์ŠคํŽ˜์ด์Šค
  2. ์˜์กด์„ฑ
  3. Dead Code Elimination
  4. Minification
  5. ๋ณ€์ˆ˜ ๊ณต์œ 
  6. ๋น„๊ฒฐ์ •์  ํ•ด์„
  7. ๊ณ ๋ฆฝ

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

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

์ž, ์ด์ œ CSS Modules๊ฐ€ ๋Œ€์ฒด ๋ฌด์—‡์ด๊ณ , ์™œ ๊ทธ๊ฒƒ์ด ๋ฏธ๋ž˜์ธ ๊ฒƒ์ธ์ง€ ์•Œ๋ ค๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์šฐ๋ฆฌ๋Š” ์ •๋ง๋กœ ์ตœ์„ ์„ ๋‹คํ•ด CSS์— ๋Œ€ํ•ด์„œ ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.(Jony Ive ํ†ค์œผ๋กœ)

1๋‹จ๊ณ„. ์ง€์—ญ๋ณ€์ˆ˜๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ.

CSS Modules์—์„œ๋Š”, ๊ฐ ํŒŒ์ผ์ด ๋ณ„๋„๋กœ ์ปดํŒŒ์ผ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋ฐ˜์ ์ธ ์ด๋ฆ„๋“ค๋กœ ๋œ ๊ฐ„๋‹จํ•œ ํด๋ž˜์Šค ์„ ํƒ์ž(selector)๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ์ „์—ญ ๊ณต๊ฐ„์„ ์˜ค์—ผ์‹œํ‚ฌ ๊ฑฑ์ •์„ ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ๋ง์ด์ฃ . ์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ์˜ 4๊ฐ€์ง€ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฐ„๋‹จํ•œ ์ œ์ถœ ๋ฒ„ํŠผ์„ ๋งŒ๋“ ๋‹ค๊ณ  ํ•ฉ์‹œ๋‹ค.

Normal
Disabled
Error
In Progress

CSS Modules ์ด์ „์˜ ๊ตฌํ˜„

๋งŒ์•ฝ Suit/BEM-style ํด๋ž˜์Šค๋ช…๊ณผ ์ผ๋ฐ˜์ ์ธ CSS์™€ HTML์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค:

/* components/submit-button.css */
.Button { /* all styles for Normal */ }
.Button--disabled { /* overrides for Disabled */ }
.Button--error { /* overrides for Error */ }
.Button--in-progress { /* overrides for In Progress */
<button class="Button Button--in-progress">Processing...</button>

์œ„์˜ ๊ตฌํ˜„๋„ ์ถฉ๋ถ„ํžˆ ์ข‹์Šต๋‹ˆ๋‹ค, ์ •๋ง๋กœ์š”. ๋„ค์ข…๋ฅ˜์˜ ๋ณ€ํ˜•์„ ์ •์˜ํ–ˆ์ง€๋งŒ, BEM-style์„ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์€ ์ƒ์† ์„ ํƒ์ž(nested selector)๋Š” ์—†๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. ์šฐ์„  ๋‹ค๋ฅธ ๊ธฐ์กด์˜ ์Šคํƒ€์ผ์ด๋‚˜ ์šฐ๋ฆฌ๊ฐ€ ๊ฐ€์ ธ๋‹ค ์“ด ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค๊ณผ์˜ ์ค‘๋ณต์„ (์•„๋งˆ๋„)๋ง‰๊ธฐ ์œ„ํ•ด์„œ ๋Œ€๋ฌธ์ž ํ•œ๊ธ€์ž๋ฅผ ๊ฐ€์ง„ ๊ธฐ๋ณธ์ ์ธ Button์„ ์ •์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ ๋Š” --modifier ๋ฌธ๋ฒ•์„ ์ด์šฉํ•ด์„œ ๋‹ค๋ฅธ ๋ณ€ํ˜•๋“ค์ด ์œ„์˜ ๊ธฐ๋ณธ ์Šคํƒ€์ผ์„ ํ•„์š”๋กœ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋ช…ํ™•ํžˆ ํ–ˆ์Šต๋‹ˆ๋‹ค.

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

CSS Modules์„ ์‚ฌ์šฉํ•œ ๊ตฌํ˜„

CSS Modules๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์šฐ๋ฆฌ์˜ ์ด๋ฆ„๋“ค์ด ๋„ˆ๋ฌด ์ผ๋ฐ˜์ ์ธ ๊ฒƒ์— ๋Œ€ํ•ด ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋ƒฅ ์ œ์ผ ์ ์ ˆํ•ด ๋ณด์ด๋Š” ๊ฒƒ์„ ์“ฐ์„ธ์š”:

/* components/submit-button.css */
.normal { /* all styles for Normal */ }
.disabled { /* all styles for Disabled */ }
.error { /* all styles for Error */ }
.inProgress { /* all styles for In Progress */

ํ˜น์‹œ **"button"**์ด๋ผ๋Š” ๋‹จ์–ด๋ฅผ ์ „ํ˜€ ์“ฐ์ง€ ์•Š์•˜๋‹ค๋Š” ์ ์„ ๋ฐœ๊ฒฌํ•˜์…จ๋‚˜์š”? ์™œ ๊ทธ๋ ‡๊ฒŒ ํ–ˆ์„๊นŒ์š”? ํŒŒ์ผ๋ช…์ด ์ด๋ฏธ **"submit-button.css"**์ด๊ธฐ ๋•Œ๋ฌธ์ด์ฃ . ๋‹ค๋ฅธ ์–ธ์–ด์—์„œ ๋ชจ๋“  ์ง€์—ญ ๋ณ€์ˆ˜๋ช…์˜ ์•ž์— ํ˜„์žฌ ์ž‘์—…์ค‘์ธ ํŒŒ์ผ๋ช…์„ ๋ถ™์ผ ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  CSS๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์—ฌ์•ผ ํ•˜์ฃ .

CSS Modules๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ํŒŒ์ผ์„ ๋กœ๋“œํ•˜๋“ฏ์ด require๋‚˜ import๋ฅผ ์จ์„œ ์ปดํŒŒ์ผ ๋ฉ๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์œผ๋กœ ์œ„์˜ ๋‚ด์šฉ์ด ๊ฐ€๋Šฅํ•ด์ง€๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

/* components/submit-button.js */
import styles from './submit-button.css';

buttonElem.outerHTML = `<button class=${styles.normal}>Submit</button>`

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

<button class="components_submit_button__normal__abc5436">
  Processing...
</button>

๋งŒ์•ฝ ์œ„์™€ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ DOM์—์„œ ๋ณด์‹œ๊ฒŒ ๋œ๋‹ค๋ฉด, ์ž˜ ๋™์ž‘ํ•˜๊ณ  ์žˆ๋‹ค๋Š” ์ฆ๊ฑฐ์ž…๋‹ˆ๋‹ค!

๋‹น์‹ ์ด ์ € ๊ณ ๋ฆด๋ผ๊ณ , CSS Modules๊ฐ€ ์ƒ์–ด์ž…๋‹ˆ๋‹ค.

(credit: [Christopher Hastings](http://www.topatoco.com/merchant.mvc?Screen=PROD&Store_Code=TO&Product_Code=RB-HIGHFIVE&Category_Code=RB))

๋„ค์ด๋ฐ ๊ทœ์น™

์šฐ๋ฆฌ์˜ ๋ฒ„ํŠผ ์˜ˆ์‹œ๋ฅผ ํ•œ๋ฒˆ ๋” ๋ด…์‹œ๋‹ค:

/* components/submit-button.css */
.normal { /* all styles for Normal */ }
.disabled { /* all styles for Disabled */ }
.error { /* all styles for Error */ }
.inProgress { /* all styles for In Progress */

์–ด๋–ค ํ•œ ํด๋ž˜์Šค๊ฐ€ **"๊ธฐ์ดˆ"**๊ฐ€ ๋˜๊ณ  ๋‹ค๋ฅธ ๊ฒƒ๋“ค์ด ๊ทธ๊ฒƒ์„ "๋ฎ์–ด์“ฐ๋Š”" ๋ชจ์–‘์ƒˆ๊ฐ€ ์•„๋‹ˆ๋ผ, ๋ชจ๋“  ํด๋ž˜์Šค๋“ค์ด ๋…๋ฆฝ์ ์ธ ๊ฒƒ์„ ๋ณด์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. CSS Modules์—์„œ๋Š” ๋ชจ๋“  ํด๋ž˜์Šค๊ฐ€ ๊ทธ ๋ณ€ํ˜•์— ํ•„์š”ํ•œ ๋ชจ๋“  ์Šคํƒ€์ผ์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค(์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€๋Š” ์กฐ๊ธˆ ์ด๋”ฐ ๋ณด์—ฌ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค). ์ด๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ํด๋ž˜์Šค๋“ค์„ ์‚ฌ์šฉํ•  ๋•Œ ํ›จ์”ฌ ํŽธ๋ฆฌํ•˜๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค:

/* ์ด๋ ‡๊ฒŒ ํ•˜์ง€ ๋งˆ์„ธ์š” */
`class=${[styles.normal, styles['in-progress']].join(" ")}`

/* ๋‹จ์ผ ํด๋ž˜์Šค๋ช…์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ํฐ ์ฐจ์ด๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค */
`class=${styles['in-progress']}`

/* ์บ๋ฉ€์ผ€์ด์Šค๋Š” ์‹ฌ์ง€์–ด ํ›จ์”ฌ ๋‚ซ์ฃ  */
`class=${styles.inProgress}`

ํ‚ค ์ž…๋ ฅ์„ ํ•œ๋ฒˆ ํ•  ๋•Œ ๋งˆ๋‹ค ๋ˆ์„ ๋ฐ›๋Š”๋‹ค๋ฉด ์ด์•ผ๊ธฐ๋Š” ๋‹ค๋ฅด์ง€๋งŒ์š”!

React ์˜ˆ์‹œ

CSS Modules์€ React๋งŒ์„ ์œ„ํ•œ ํŠน๋ณ„ํ•œ ๋ฌด์–ธ๊ฐ€๊ฐ€ ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ React์™€ ํ•จ๊ป˜ํ•  ๋•Œ CSS Modules๋Š” ํŠนํžˆ ์‚ฌ์šฉํ•˜๊ธฐ ์ข‹์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ์˜๋ฏธ์—์„œ ์•ฝ๊ฐ„ ๋” ๋ณต์žกํ•œ ์˜ˆ์ œ๋ฅผ ํ•œ๋ฒˆ ๋ณด๋Š” ๊ฒƒ๋„ ๊ฐ€์น˜๊ฐ€ ์žˆ๊ฒ ์ง€์š”:

/* components/submit-button.jsx */
import { Component } from 'react';
import styles from './submit-button.css';

export default class SubmitButton extends Component {
  render() {
    let className, text = "Submit"
    if (this.props.store.submissionInProgress) {
      className = styles.inProgress
      text = "Processing..."
    } else if (this.props.store.errorOccurred) {
      className = styles.error
    } else if (!this.props.form.valid) {
      className = styles.disabled
    } else {
      className = styles.normal
    }
    return <button className={className}>{text}</button>
  }
}

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

ํ•˜์ง€๋งŒ ์ด๋Š” ์‹œ์ž‘์— ๋ถˆ๊ณผํ•ฉ๋‹ˆ๋‹ค. ์ž‘์„ฑ๋œ ์Šคํƒ€์ผ๋“ค์„ ์–ด๋–ป๊ฒŒ ๋ฐฐ์น˜ํ•  ๊ฒƒ์ธ์ง€์— ๋Œ€ํ•ด์„œ ์ƒ๊ฐํ•ด๋ด…์‹œ๋‹ค. ๊ทธ์— ์žˆ์–ด์„œ๋„ CSS Modules๋Š” ๋‹น์‹ ์„ ๊ณ ํ†ต์œผ๋กœ๋ถ€ํ„ฐ ์ง€์ผœ์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

2๋‹จ๊ณ„. ๊ตฌ์„ฑ์ด ๋ชจ๋“  ๊ฒƒ์ด๋‹ค.

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

/* BEM Style */
innerHTML = `<button class="Button Button--in-progress">`

/* CSS Modules */
innerHTML = `<button class="${styles.inProgress}">`

ํ•˜์ง€๋งŒ ์ž ์‹œ๋งŒ์š”, ๊ทธ๋Ÿผ ์–ด๋–ป๊ฒŒ ๋ชจ๋“  ์ƒํƒœ๋“ค์ด ๊ณต์œ ํ•˜๋Š” ์Šคํƒ€์ผ๋“ค์„ ์ •์˜ํ• ๊นŒ์š”? ๊ทธ ํ•ด๋‹ต์ด ์•„๋งˆ๋„ CSS Modules๊ฐ€ ๊ฐ€์ง€๋Š” ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๋ฌด๊ธฐ์ธ ๊ตฌ์„ฑ(composition)์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

.common {
  /* all the common styles you want */
}
.normal {
  composes: common;
  /* anything that only applies to Normal */
}
.disabled {
  composes: common;
  /* anything that only applies to Disabled */
}
.error {
  composes: common;
  /* anything that only applies to Error */
}
.inProgress {
  composes: common;
  /* anything that only applies to In Progress */
}

composes ํ‚ค์›Œ๋“œ๋Š” .normal์ด .common์˜ ๋ชจ๋“  ์ƒํƒœ๋ฅผ ๊ฐ€์ง„๋‹ค๋Š” ๊ฒƒ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. Sass๋กœ ์น˜๋ฉด @extends๊ฐ™์€ ๊ธฐ๋Šฅ์ด์ฃ . ํ•˜์ง€๋งŒ Sass๋Š” CSS ์„ ํƒ์ž๋“ค์„ ๋ฎ์–ด์”Œ์›Œ์„œ ๊ทธ๊ฒƒ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ๋ฐ˜๋ฉด, CSS Modules๋Š” ์–ด๋–ค ํด๋ž˜์Šค๋ฅผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ์ถ”์ถœํ•  ์ง€๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

Sass์˜ ์ฒ˜๋ฆฌ๋ฐฉ์‹

์œ„์—์„œ ์–ธ๊ธ‰ํ–ˆ๋˜ BEM ์˜ˆ์‹œ๋ฅผ ๊ฐ€์ ธ์™€์„œ ํ•œ๋ฒˆ Sass์˜ @extends๋ฅผ ์ ์šฉํ•˜์—ฌ ์ˆ˜์ •ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

.Button--common { /* font-sizes, padding, border-radius */ }
.Button--normal {
  @extends .Button--common;
  /* blue color, light blue background */
}
.Button--error {
  @extends .Button--common;
  /* red color, light red background */
}

์œ„์˜ Sass๋Š” CSS๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ปดํŒŒ์ผ๋ฉ๋‹ˆ๋‹ค:

.Button--common, .Button--normal, .Button--error {
  /* font-sizes, padding, border-radius */
}
.Button--normal {
  /* blue color, light blue background */
}
.Button--error {
  /* red color, light red background */
}

๊ทธ ์ดํ›„์—๋Š” ๊ทธ๋ƒฅ ์ •์˜๋œ ํด๋ž˜์Šค ์ค‘ ํ•˜๋‚˜๋ฅผ ๋งˆํฌ์—…์— <button class="Button--error">์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•ด์„œ ์›ํ•˜๋Š” ๊ณต์šฉ ์Šคํƒ€์ผ๊ณผ ํŠน์ • ์Šคํƒ€์ผ์„ ๋™์‹œ์— ์–ป์–ด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์‹ค๋กœ ๊ฐ•๋ ฅํ•œ ๊ฐœ๋…์ด์ง€๋งŒ, ๊ตฌํ˜„ ๊ทธ ์ž์ฒด์— ์ฃผ์˜ํ•˜์ง€ ์•Š์œผ๋ฉด ์•ˆ๋˜๋Š” ์—์ง€ ์ผ€์ด์Šค๋‚˜ ํ•จ์ •์ด ์ˆจ์–ด์žˆ์Šต๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ๋ฌธ์ œ์ ๊ณผ ์ฝ์„ ๊ฑฐ๋ฆฌ๋“ค์„ Hugo Giraudel์ด ์—ฌ๊ธฐ์— ๋ชจ์•„์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

CSS Modules์˜ ์ฒ˜๋ฆฌ๋ฐฉ์‹

composes ํ‚ค์›Œ๋“œ๋Š” ๊ฐœ๋…์ƒ์œผ๋กœ๋Š” @extends์™€ ๋น„์Šทํ•˜์ง€๋งŒ, ์ „ํ˜€ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์‹œ์—ฐ์„ ์œ„ํ•ด์„œ ๋‹ค์Œ ์˜ˆ์‹œ๋ฅผ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

.common { /* font-sizes, padding, border-radius */ }
.normal { composes: common; /* blue color, light blue background */ }
.error { composes: common; /* red color, light red background */ }

์œ„์˜ ์ฝ”๋“œ๋Š” ์ปดํŒŒ์ผ ๋˜์–ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ณผ ๋•Œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณด์ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค:

.components_submit_button__common__abc5436 { /* font-sizes, padding, border-radius */ }
.components_submit_button__normal__def6547 { /* blue color, light blue background */ }
.components_submit_button__error__1638bcd { /* red color, light red background */ }

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ import styles from "./submit-button.css"๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค:

styles: {
  common: "components_submit_button__common__abc5436",
  normal: "components_submit_button__common__abc5436 components_submit_button__normal__def6547",
  error: "components_submit_button__common__abc5436 components_submit_button__error__1638bcd"
}

์ฆ‰ ์šฐ๋ฆฌ๋Š” ์—ฌ์ „ํžˆ styles.normal์ด๋‚˜ styles.error์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์‹ค์ œ๋กœ DOM์—๋Š” ์—ฌ๋Ÿฌ ํด๋ž˜์Šค๊ฐ€ ๋ Œ๋”๋ง๋˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด์ฃ .

<button class="components_submit_button__common__abc5436 
               components_submit_button__normal__def6547">
  Submit
</button>

์ด๊ฒƒ์˜ ๊ตฌ์„ฑ(composes)์˜ ํž˜์ž…๋‹ˆ๋‹ค. ์ด๋ฏธ ์ž‘์„ฑ๋œ ๋งˆํฌ์—…์„ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ CSS ์„ ํƒ์ž๋“ค์„ ๋‹ค์‹œ ์“ฐ์ง€ ์•Š๊ณ ๋„, ์—ฌ๋Ÿฌ ๋…๋ฆฝ์ ์ธ ์Šคํƒ€์ผ ๊ทธ๋ฃน๋“ค์„ ํ•ฉ์น  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด์ฃ  ๐Ÿ‘Œ

๋‹จ๊ณ„ 3. ํŒŒ์ผ ๊ฐ„ ๊ณต์œ 

Sass๋‚˜ LESS๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ž‘์—…ํ•˜๋Š” ๊ฒฝ์šฐ, @import๋ฅผ ์จ์„œ ๊ฐ€์ ธ์˜จ ํŒŒ์ผ๋“ค์€ ๋™์ผํ•œ ์ „์—ญ ๊ณต๊ฐ„์— ์กด์žฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ๋ณ€์ˆ˜๋‚˜ ๋ฏน์Šค์ธ์„ ํ•œ ํŒŒ์ผ์— ์ •์˜ํ•ด์„œ, ๋‹ค๋ฅธ ๋ชจ๋“  ํŒŒ์ผ์—์„œ ๊ฐ€์ ธ๋‹ค ์“ฐ๋Š” ๊ฒƒ์ด์ฃ . ๊ทธ๋Ÿฐ ๋ฐฉ์‹์€ ํŽธ๋ฆฌํ•  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ, ํŒŒ์ผ ๊ฐ„์— ์ „์—ญ ๋ณ€์ˆ˜๋ช…์ด ์ถฉ๋Œํ•  ์ง€ ๋ชจ๋ฅด๋Š” ์ƒํƒœ๊ฐ€ ๋˜๋ฉด(๊ทธ๊ฒƒ์€ ๋‹ค๋ฅธ ์ „์—ญ ์ด๋ฆ„๊ณต๊ฐ„์ด๊ธฐ ๋•Œ๋ฌธ์—) ์–ด์ฉ” ์ˆ˜ ์—†์ด variables.scss๋‚˜ settings.scss๋ฅผ ์ˆ˜์ •ํ•ด์•ผ๋งŒ ํ•˜๋Š” ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ๋˜๋ฉด ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ด๋–ค ๋ณ€์ˆ˜์— ์˜์กด์„ฑ์ด ์žˆ๋Š”์ง€๋ฅผ ๋ณด๊ธฐ๊ฐ€ ํž˜๋“ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‹น์‹ ์˜ ์„ค์ • ํŒŒ์ผ๋“ค์€ ๋‹ค๋ฃจ๊ธฐ๊ฐ€ ๊ต‰์žฅํžˆ ํž˜๋“ค์–ด์ง€์ฃ .

๋ฌผ๋ก  ์กฐ๊ธˆ ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•๋ก ๋“ค๋„ ์žˆ์Šต๋‹ˆ๋‹ค(์‚ฌ์‹ค Ben Smithett๊ฐ€ ์ž‘์„ฑํ•œ Sass์™€ Webpack์„ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์ง์ ‘์ ์œผ๋กœ CSS Modules ํ”„๋กœ์ ํŠธ์— ์˜ํ–ฅ์„ ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ผญ ์ฝ์–ด๋ณด์„ธ์š”). ํ•˜์ง€๋งŒ Sass๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํ•œ ์—ฌ์ „ํžˆ Sass์˜ ์ „์—ญ์  ํŠน์„ฑ์— ๋ฌถ์ผ ์ˆ˜ ๋ฐ–์— ์—†์Šต๋‹ˆ๋‹ค.

CSS Modules๋Š” ๋™์‹œ์— ํ•œ ํŒŒ์ผ์”ฉ๋งŒ ์ฒ˜๋ฆฌ๋˜๊ธฐ ๋•Œ๋ฌธ์—, ์˜ค์—ผ๋  ์ „์—ญ ํ™˜๊ฒฝ ์ž์ฒด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ํ•˜๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ import๋‚˜ require๋ฅผ ์จ์„œ ์˜์กด์„ฑ์„ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—, CSS Modules๋Š” ํŒŒ์ผ๊ฐ„์— ๊ตฌ์„ฑ(composes)๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค:

/* colors.css */
.primary {
  color: #720;
}
.secondary {
  color: #777;
}
/* other helper classes... */
/* submit-button.css */
.common { /* font-sizes, padding, border-radius */ }
.normal {
  composes: common;
  composes: primary from "../shared/colors.css";
}

๊ตฌ์„ฑ(composition)์„ ์ด์šฉํ•ด์„œ, ์™„์ „ํžˆ ์ผ๋ฐ˜์ ์ธ ํŒŒ์ผ์ธ colors.css์— ์ ‘๊ทผํ•ด ์›ํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ์ง€์—ญ ๋ณ€์ˆ˜๋ช…์œผ๋กœ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ตฌ์„ฑ์€ ์–ด๋–ค ํด๋ž˜์Šค๋“ค์„ ์ถ”์ถœํ• ์ง€ ๋งŒ์„ ๋ณ€๊ฒฝํ•˜๊ณ  CSS์ž์ฒด๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ธฐ๋•Œ๋ฌธ์—, composes๋ฌธ๋“ค์€ ๋ธŒ๋ผ์šฐ์ €์— ๋ณด์—ฌ์ง€๊ธฐ ์ „์— CSS์—์„œ ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค:

/* colors.css */
.shared_colors__primary__fca929 {
  color: #720;
}
.shared_colors__secondary__acf292 {
  color: #777;
}
/* submit-button.css */
.components_submit_button__common__abc5436 { /* font-sizes, padding, border-radius */ }
.components_submit_button__normal__def6547 {}
<button class="shared_colors__primary__fca929
               components_submit_button__common__abc5436 
               components_submit_button__normal__def6547">
  Submit
</button>

์‚ฌ์‹ค, ๋ธŒ๋ผ์šฐ์ €์— ๋ณด์—ฌ์ง€๊ธฐ ์ „๊นŒ์ง€๋Š”, ์šฐ๋ฆฌ์˜ ์ง€์—ญ ํด๋ž˜์Šค๋ช…์ธ *"normal"์€ ์ž๊ธฐ ์ž์‹ ๋งŒ์˜ ์Šคํƒ€์ผ์€ ์ „ํ˜€ ๊ฐ–์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ์€ ์ข‹์€ ๊ฒƒ์ด์ฃ ! ์ด๋Š” ๊ฒฐ๊ตญ ์ง€์—ญ์ ์œผ๋กœ ์˜๋ฏธ์žˆ๋Š” ๊ฐ์ฒด("normal"*์ด๋ผ๋Š” ์ด๋ฆ„์„ ๊ฐ€์ง„ ํ•ญ๋ชฉ)๋ฅผ CSS ์ฝ”๋“œ ํ•œ์ค„๋„ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๊ณ  ๋งŒ๋“ค์–ด๋‚ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•จ์œผ๋กœ์จ ์šฐ๋ฆฌ ์‚ฌ์ดํŠธ๊ฐ€ ์‹œ๊ฐ์ ์œผ๋กœ ๋ถˆ์—ฐ์†ํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์„ ๋ง‰์„ ์ˆ˜ ์žˆ๊ณ , ๋˜ํ•œ ์œ ์ €๋“ค์˜ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ฝ๊ฒŒ๋˜๋Š” ๋ถˆํ•„์š”ํ•œ ์ฝ”๋“œ์˜ ํŒฝ์ฐฝ๋„ ๋ง‰์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ : ๊ทธ๋Ÿฌํ•œ ๋นˆ ํด๋ž˜์Šค๋“ค์€ csso๊ฐ™์€ ํˆด์„ ์‚ฌ์šฉํ•ด์„œ ์‰ฝ๊ฒŒ ์ฐพ์•„ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹จ๊ณ„ 4. ๋‹จ์ผ ์ฑ…์ž„ ๋ชจ๋“ˆ๋“ค

๊ตฌ์„ฑ(composition)์€ ์œ ์ €๋“ค๋กœ ํ•˜์—ฌ๊ธˆ ํ•œ ์š”์†Œ์— ์žˆ์–ด ์–ด๋–ค ์Šคํƒ€์ผ๋“ค์ด ๊ทธ๊ฒƒ์„ ๊ตฌ์„ฑํ•˜๋Š”์ง€๊ฐ€ ์•„๋‹ˆ๋ผ, ๊ทธ๊ฒƒ์ด ๋ฌด์—‡์ธ์ง€๋ฅผ ์„œ์ˆ ํ•˜๊ฒŒ ๋•์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ์— ๊ทธ๊ฒƒ์ด ๊ฐ•๋ ฅํ•œ ๊ฒƒ์ด์ฃ . ๊ทธ๊ฒƒ์€ ๊ฐœ๋…์  ๊ฐœ์ฒด๋“ค(elements)๋“ค์„ ์–‘์‹์  ๊ฐœ์ฒด๋“ค(rules)๋กœ ๋Œ€์‘์‹œํ‚ค๋Š”๋ฐ ์žˆ์–ด ๊ธฐ์กด๊ณผ๋Š” ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์ธ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์ด๊ณ  ํ‰๋ฒ”ํ•œ CSS๋กœ ์ž‘์„ฑ๋œ ๋‹ค์Œ ์˜ˆ์‹œ๋ฅผ ํ•œ๋ฒˆ ๋ณด์‹œ์ฃ :

.some_element {
  font-size: 1.5rem;
  color: rgba(0,0,0,0);
  padding: 0.5rem;
  box-shadow: 0 0 4px -2px;
}

ํ•œ๊ฐœ์˜ ์š”์†Œ์™€ ์—ฌ๋Ÿฌ ์Šคํƒ€์ผ๋“ค. ๊ฐ„๋‹จํ•˜์ฃ . ํ•˜์ง€๋งŒ, ์‚ฌ์‹ค ์—ฌ๊ธฐ์—๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰ color, font-size, padding, box-shadow์™€ ๊ฐ™์€ ๋ชจ๋“  ๊ฒƒ์ด ์ด๊ณณ์— ๋ช…์‹œ๋˜์–ด ์žˆ์ง€๋งŒ, ์‚ฌ์‹ค ๊ทธ ์Šคํƒ€์ผ๋“ค์€ ๋‹ค๋ฅธ ๊ณณ์—์„œ ์‚ฌ์šฉ๋  ์ง€๋„ ๋ชจ๋ฅธ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. Sass๋ฅผ ์จ์„œ ๋ฆฌํŒฉํ† ๋งํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค:

$large-font-size: 1.5rem;
$dark-text: rgba(0,0,0,0);
$padding-normal: 0.5rem;
@mixin subtle-shadow {
  box-shadow: 0 0 4px -2px;
}

.some_element {
  @include subtle-shadow;
  font-size: $large-font-size;
  color: $dark-text;
  padding: $padding-normal;
}

์กฐ๊ธˆ ๋‚˜์•„์กŒ๊ตฐ์š”. ํ•˜์ง€๋งŒ ์ „์ฒด ๋‚ด์šฉ์— ์žˆ์–ด ๋ฐ˜์ •๋„๋ฅผ ์ถ”์ถœํ•ด๋ƒˆ์„ ๋ฟ์ž…๋‹ˆ๋‹ค. $large-font-size๊ฐ€ ํƒ€์ดํฌ๊ทธ๋ž˜ํ”ผ๋ฅผ ์œ„ํ•ด์„œ ์กด์žฌํ•œ๋‹ค๊ฑฐ๋‚˜ $padding-normal์ด ๋ ˆ์ด์•„์›ƒ์„ ์œ„ํ•ด์„œ ์กด์žฌํ•œ๋‹ค๋Š” ๊ฒƒ์€ ๊ทธ ์ด๋ฆ„์— ์˜ํ•ด์„œ ํ‘œํ˜„๋  ๋ฟ์ด์ง€ ์–ด๋Š ๊ณณ์—์„œ๋„ ๊ฐ•์ œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ box-shadow์™€ ๊ฐ™์€ ์„ ์–ธ ๊ทธ ์ž์ฒด๋ฅผ ๋ณ€์ˆ˜ํ™”ํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ์—๋Š” ์–ด์ฉ” ์ˆ˜ ์—†์ด @mixin์ด๋‚˜ @extends๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

CSS Modules๊ณผ ํ•จ๊ป˜๋ผ๋ฉด

๊ตฌ์„ฑ(composition)์„ ์‚ฌ์šฉํ•ด์„œ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ถ€๋ถ„๋“ค๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

.element {
  composes: large from "./typography.css";
  composes: dark-text from "./colors.css";
  composes: padding-all-medium from "./layout.css";
  composes: subtle-shadow from "./effect.css";
}

์œ„์˜ ํ˜•์‹์— ์˜ํ•ด ์ž์—ฐ์Šค๋ ˆ ๋งŽ์€ ๋‹จ์ผ ๋ชฉ์ ์˜ ํŒŒ์ผ๋“ค์ด ์ƒ๊ฒจ๋‚ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋‹ค๋ฅธ ๋ชฉ์ ์˜ ์Šคํƒ€์ผ๋“ค์„ ์ด๋ฆ„๊ณต๊ฐ„์ด ์•„๋‹ˆ๋ผ ํŒŒ์ผ์‹œ์Šคํ…œ์œผ๋กœ ๊ตฌ๋ถ„์ง“๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋งŒ์ผ ์—ฌ๋Ÿฌ ํด๋ž˜์Šค๋“ค์„ ํ•œ ํŒŒ์ผ๋กœ๋ถ€ํ„ฐ ๊ตฌ์„ฑํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•œ์ค„๋กœ ์„ ์–ธํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค:

/* this short hand: */
.element {
  composes: padding-large margin-small from "./layout.css";
}

/* is equivalent to: */
.element {
  composes: padding-large from "./layout.css";
  composes: margin-small from "./layout.css";
}

์ด๋Ÿฐ ๋ฐฉ๋ฒ•๋“ค์€, ์‚ฌ์ดํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ์‹œ๊ฐ์  ์š”์†Œ์— ๋Œ€ํ•ด ๋ณ„๋ช…(aliases)์„ ๋ถ™์ž„์œผ๋กœ์จ ๊ทนํžˆ ์„ธ๋ถ„ํ™”๋œ ํด๋ž˜์Šค๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

.article {
  composes: flex vertical centered from "./layout.css";
}

.masthead {
  composes: serif bold 48pt centered from "./typography.css";
  composes: paragraph-margin-below from "./layout.css";
}

.body {
  composes: max720 paragraph-margin-below from "layout.css";
  composes: sans light paragraph-line-height from "./typography.css";
}

์œ„์˜ ๋ฐฉ๋ฒ•๋ก ์€ ์ œ๊ฐ€ ์กฐ๊ธˆ ๋” ์—ฐ๊ตฌํ•ด๋ณด๊ณ  ์‹ถ์€ ๋ถ€๋ถ„์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ์ œ ์ƒ๊ฐ์—๋Š”, ์ด๋Ÿฐ ๋ฐฉ๋ฒ•๋ก ์ด Tachyons์™€ ๊ฐ™์€ ์•„ํ† ๋ฏนํ•œ(atomic) CSS ๋ฐฉ๋ฒ•๋ก ์˜ ๊ฐ€์žฅ ์ข‹์€ ๋ฉด๊ณผ Semantic UI๊ฐ™์€ ๊ฒƒ๋“ค์˜ ๊ฐ€๋…์„ฑ์„ ํ•ฉ์ณ๋†“์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ง„์งœ๋ฐฐ๊ธฐ์˜, ์‹ ๋ขฐํ• ๋งŒํ•œ ๊ฒฉ๋ฆฌ(isolation)์™€ ํ•จ๊ป˜์š”.

ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๋Š” CSS Modules๋ผ๋Š” ์ด์•ผ๊ธฐ์˜ ์‹œ์ž‘์ ์— ์„œ์žˆ์„ ๋ฟ์ž…๋‹ˆ๋‹ค. ํ•œ๋ฒˆ ๋‹ค์Œ ํ”„๋กœ์ ํŠธ์—์„œ CSS Modules์„ ์จ๋ณด์‹œ๊ณ , ์šฐ๋ฆฌ๊ฐ€ ๊ทธ ๋ฏธ๋ž˜๋ฅผ ์ž˜ ๋งŒ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ์„ธ์š”.

์ž, ์‹œ์ž‘ํ•ด๋ด…์‹œ๋‹ค!

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

ํ•˜์ง€๋งŒ ์‹ฌ์ง€์–ด๋Š” ๋” ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•ด๋ณผ ์ˆ˜ ์žˆ๋„๋ก, ์•„๋ฌด๊ฒƒ๋„ ์„ค์น˜ํ•˜์ง€ ์•Š๊ณ ๋„ ์˜ˆ์ œ๋“ค์„ ๋‘˜๋Ÿฌ๋ณผ ์ˆ˜ ์žˆ๋Š” ์ž‘์€ Plunkr๋ฅผ ๋งŒ๋“ค์–ด๋‘์—ˆ์Šต๋‹ˆ๋‹ค! ํ•œ๋ฒˆ ์‚ดํŽด๋ณด์„ธ์š”:

์ค€๋น„๊ฐ€ ๋˜์‹  ๊ฒƒ ๊ฐ™๋‹ค๋ฉด CSS Modules์˜ ์ฃผ์ €์žฅ์†Œ๋ฅผ ์‚ดํŽด๋ณด์„ธ์š”. ์งˆ๋ฌธ์ด ์žˆ์œผ์‹œ๋ฉด, ์ด์Šˆ๋ฅผ ์˜ฌ๋ ค์„œ ๋Œ€ํ™”์˜ ๋ง‰์„ ์—ด์–ด์ฃผ์„ธ์š”. CSS ModulesํŒ€์€ ์ž‘๊ธฐ๋•Œ๋ฌธ์— ์•„์ง ๋ชจ๋“  ์‚ฌ์šฉ์˜ˆ์‹œ๋ฅผ ๋‘˜๋Ÿฌ๋ณด์ง€๋Š” ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๋Ÿฌ๋ถ„์˜ ์ด์•ผ๊ธฐ๋ฅผ ๋“ฃ๊ณ ์‹ถ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ํ–‰๋ณตํ•œ ์Šคํƒ€์ผ๋ง ํ•˜์„ธ์š”, ์นœ๊ตฌ๋“ค!