Tailwind์™€ ํ•„์š”ํ•œ ๊ฒƒ๋“ค ์„ค์น˜

npm install -D tailwindcss postcss autoprefixer 
npx tailwindcss init -p

postcss์™€ autoprefixer๋Š” ์™œ ํ•„์š”ํ• ๊นŒ?

tailwind๋งŒ ์„ค์น˜ํ•˜๋ฉด ๋  ์ค„ ์•Œ์•˜์œผ๋‚˜ tailwind๊ฐ€ ์ž‘๋™ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” postcss์™€ autoprefixer๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ์ด ๋‘˜์˜ ์—ญํ• ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž.

postcss

postcss์˜ ๊ณต์‹๋ฌธ์„œ์—๋Š” PostCSS is a tool for transforming styles with JS plugins. ๋ผ๊ณ  ์ ํ˜€์žˆ๋‹ค. js๋ฅผ ์ด์šฉํ•˜์—ฌ css๋ฅผ ๋ณ€ํ™˜์‹œํ‚จ๋‹ค๋Š” ์ด์•ผ๊ธฐ์ด๋‹ค.

postcss๋Š” ๋‹ค๋ฅธ scss, Stylus๋“ค๊ณผ๋Š” ๋‹ค๋ฅด๊ฒŒ ํ›„์ฒ˜๋ฆฌ๊ธฐ์˜ ์„ฑ๊ฒฉ์„ ์ง€๋‹Œ๋‹ค.

CSS ์ „์ฒ˜๋ฆฌ๊ธฐ ํ™”๋ฉด์— ๋ณด์ด๊ธฐ ์ „์— CSS๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ , ์Šคํƒ€์ผ์„ ์ ์šฉ CSS ์ž‘์„ฑ โ†’ ์‹คํ–‰ ์ „์— ๊ธฐ๋ณธ CSS๋กœ ์ปดํŒŒ์ผ โ†’ ๋ Œ๋”๋ง Stylus, Sass, Less ๋“ฑ

CSS ํ›„์ฒ˜๋ฆฌ๊ธฐ ํ™”๋ฉด์— ๋ณด์—ฌ์ง„ ๋’ค์— CSS ์Šคํƒ€์ผ์„ ์ ์šฉ CSS ์ž‘์„ฑ โ†’ ๋ Œ๋”๋ง โ†’ ์™ธ๋ถ€ ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜์—ฌ ์Šคํƒ€์ผ ๋ณ€๊ฒฝ PostCSS ๋“ฑ

postCSS๋Š” ์œ„์™€ ๊ฐ™์ด Parser -> plugins -> Stringifier ์˜ ๊ณผ์ •์„ ๊ฑฐ์นœ๋‹ค.

๊ฐ€์žฅ ๋จผ์ € Parser์—์„œ๋Š” ๋ฌธ์ž์—ด์„ tokenizingํ•œ ๋‹ค์Œ AST๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋‹จ์ผ ํŒŒ์ผ์„ ์ž‘์„ฑํ•˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์นœ๋‹ค. ๊ธฐ์กด์˜ CSS์— ๋Œ€ํ•ด์„œ ํ† ํฐํ™” ํ•˜๋Š” ๊ณผ์ •์„ ์‚ดํŽด๋ณด์ž.

.className { color: #FFF; }

์œ„์™€ ๊ฐ™์€ CSS๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ์šฐ์„  ์ด๋Ÿฌํ•œ CSS์— ๋Œ€ํ•ด tokenizingํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด

[
    ["word", ".className", 1, 1, 1, 10]
    ["space", " "]
    ["{", "{", 1, 12]
    ["space", " "]
    ["word", "color", 1, 14, 1, 18]
    [":", ":", 1, 19]
    ["space", " "]
    ["word", "#FFF" , 1, 21, 1, 23]
    [";", ";", 1, 24]
    ["space", " "]
    ["}", "}", 1, 26]
]

์ด๋Ÿฐ ์‹์œผ๋กœ ๊ฐ๊ฐ ๋‹จ์ผ ํ† ํฐ์— ๋Œ€ํ•ด์„œ ๋ฐฐ์—ด๋กœ ๊ด€๋ฆฌํ•˜๋ฉฐ, ํ† ํฐ ํƒ€์ž…, ๋‹จ์–ด, ์œ„์น˜ ์ •๋ณด ๋“ฑ์„ ๊ด€๋ฆฌํ•œ๋‹ค.

const token = [
    // ํ† ํฐ ํƒ€์ž…
    'word',
 
    // ๋งค์นญ๋œ ๋‹จ์–ด
    '.className',
 
    // ์•ž ๋‘ ์ˆซ์ž -> ํ† ํฐ์˜ ์‹œ์ž‘ ์œ„์น˜(optional)
    // `space`์™€ ๊ฐ™์€ ํ† ํฐ์€ ์œ„์น˜ ์ •๋ณด ์—†์Œ
 
    // ์—ฌ๊ธฐ์„œ ์ฒซ ๋ฒˆ์งธ ์ˆซ์ž๋Š” ์ค„ ๋ฒˆํ˜ธ์ด๊ณ , ๋‘ ๋ฒˆ์งธ ์ˆซ์ž๋Š” ํ•ด๋‹น ์ค„์˜ ์—ด ๋ฒˆํ˜ธ
    1, 1,
 
    // ์ด ํ† ํฐ์ฒ˜๋Ÿผ ์—ฌ๋Ÿฌ ๋ฌธ์ž์˜ ํ† ํฐ์— ๋Œ€ํ•ด ๋ ์œ„์น˜(optional)
    // ์ˆซ์ž๋Š” ์œ„์—์„œ ์„ค๋ช…ํ•œ ๊ทœ์น™๊ณผ ๊ฐ™์Œ
    1, 10
]
 

์ด๋ ‡๊ฒŒ ํ† ํฐํ™”๋œ ๊ฐ๊ฐ์˜ CSS์— ๋Œ€ํ•ด์„œ postcss์˜ lib/parse.js์™€ lib/parser.js์˜ ๋ชจ๋“ˆ์„ ํ†ตํ•ด ํŒŒ์‹ฑํ•œ๋‹ค. ์ด ํŒŒ์„œ๋ฅผ ํ†ตํ•ด์„œ CSS์— ๋Œ€ํ•œ AST๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

'use strict'
 
let Container = require('./container')
let Input = require('./input')
let Parser = require('./parser')
 
function parse(css, opts) {
  let input = new Input(css, opts)
  let parser = new Parser(input)
  try {
    parser.parse()
  } catch (e) {
    if (process.env.NODE_ENV !== 'production') {
      if (e.name === 'CssSyntaxError' && opts && opts.from) {
        if (/\.scss$/i.test(opts.from)) {
          e.message +=
            '\nYou tried to parse SCSS with ' +
            'the standard CSS parser; ' +
            'try again with the postcss-scss parser'
        } else if (/\.sass/i.test(opts.from)) {
          e.message +=
            '\nYou tried to parse Sass with ' +
            'the standard CSS parser; ' +
            'try again with the postcss-sass parser'
        } else if (/\.less$/i.test(opts.from)) {
          e.message +=
            '\nYou tried to parse Less with ' +
            'the standard CSS parser; ' +
            'try again with the postcss-less parser'
        }
      }
    }
    throw e
  }
 
  return parser.root
}
 
module.exports = parse
parse.default = parse
 
Container.registerParse(parse)

ํ•ด๋‹น parse์—์„œ๋Š” parser ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ๋‹ค์‹œ๊ธˆ ํ† ํฐํ™”๋œ ๊ฒƒ๋“ค์„ AST๋กœ ๋งŒ๋“œ๋Š” ๊ณผ์ •์„ ๊ฑฐ์น˜๊ณ , ์ด์— ๋Œ€ํ•œ root๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

๊ทธ๋ ‡๊ฒŒ ๋งŒ๋“ค์–ด์ง„ ast์— ๋Œ€ํ•ด์„œ๋Š” processor(lib/processor.js)๋ฅผ ํ†ตํ•ด์„œ ํ”Œ๋Ÿฌ๊ทธ์ธ๋“ค์„ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  ๋ฌธ๋ฒ•์ ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์นœ๋‹ค.

'use strict'
 
let Document = require('./document')
let LazyResult = require('./lazy-result')
let NoWorkResult = require('./no-work-result')
let Root = require('./root')
 
class Processor {
  constructor(plugins = []) {
    this.version = '8.4.47'
    this.plugins = this.normalize(plugins)
  }
 
  normalize(plugins) {
    let normalized = []
    for (let i of plugins) {
      if (i.postcss === true) {
        i = i()
      } else if (i.postcss) {
        i = i.postcss
      }
 
      if (typeof i === 'object' && Array.isArray(i.plugins)) {
        normalized = normalized.concat(i.plugins)
      } else if (typeof i === 'object' && i.postcssPlugin) {
        normalized.push(i)
      } else if (typeof i === 'function') {
        normalized.push(i)
      } else if (typeof i === 'object' && (i.parse || i.stringify)) {
        if (process.env.NODE_ENV !== 'production') {
          throw new Error(
            'PostCSS syntaxes cannot be used as plugins. Instead, please use ' +
              'one of the syntax/parser/stringifier options as outlined ' +
              'in your PostCSS runner documentation.'
          )
        }
      } else {
        throw new Error(i + ' is not a PostCSS plugin')
      }
    }
    return normalized
  }
 
  process(css, opts = {}) {
    if (
      !this.plugins.length &&
      !opts.parser &&
      !opts.stringifier &&
      !opts.syntax
    ) {
      return new NoWorkResult(this, css, opts)
    } else {
      return new LazyResult(this, css, opts)
    }
  }
 
  use(plugin) {
    this.plugins = this.plugins.concat(this.normalize([plugin]))
    return this
  }
}
 
module.exports = Processor
Processor.default = Processor
 
Root.registerProcessor(Processor)
Document.registerProcessor(Processor)
์ด๋ ‡๊ฒŒ ๋ฌธ๋ฒ•์ ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์„ ๋งˆ์น˜๋ฉด ์ด๋ฅผ ๋‹ค์‹œ ์ˆœ์ˆ˜ CSS ๊ตฌ๋ฌธ์œผ๋กœ ๋ฐ”๊ฟ”์ค˜์•ผ ํ•˜๋Š”๋ฐ, Stringifier๊ฐ€ AST๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ ๊ฐ ๋…ธ๋“œ์— ๋Œ€ํ•ด CSS ๋ฌธ์ž์—ด์„ ์ƒ์„ฑํ•ด๋‚ธ๋‹ค. 

autoprefixer

ํ…œํ”Œ๋ฆฟ ๊ฒฝ๋กœ ์ง€์ •

/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

index.css์— tailwind ์„ค์ • ์ถ”๊ฐ€

 
@tailwind base;
@tailwind components;
@tailwind utilities;