AMP

CSS ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ฐ ํŠธ๋žœ์ง€์…˜ ํŠธ๋ฆฌ๊ฑฐ

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

์ •ํ™•ํ•œ ์‹œ๊ฐ„ ์ง€์ •์ด ํ•„์š”ํ•˜์ง€ ์•Š์œผ๋ฉฐ ํฌ๊ธฐ๊ฐ€ ์ž‘๊ณ  ์ œํ•œ๋œ ์• ๋‹ˆ๋ฉ”์ด์…˜์—๋Š” CSS๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

CSS ๋ฐ ํ‚ค ํ”„๋ ˆ์ž„ ์ •์˜

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ AMP์—์„œ CSS๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋ฌธ์„œ ํ—ค๋“œ์˜ <style amp-custom> ํƒœ๊ทธ์—์„œ. 75,000 ๋ฐ”์ดํŠธ ์ œํ•œ.
  • ์ธ๋ผ์ธ ์Šคํƒ€์ผ. ์ธ๋ผ์ธ ์Šคํƒ€์ผ์˜ ๊ฐ ์ธ์Šคํ„ด์Šค๋Š” 1,000 ๋ฐ”์ดํŠธ ์ œํ•œ์ด ์žˆ์œผ๋ฉฐ ์ธ๋ผ์ธ ์Šคํƒ€์ผ์€ 75,000 ๋ฐ”์ดํŠธ <style amp-custom> ์ œํ•œ์— ํฌํ•จ.
  • ๋ฌธ์„œ ํ—ค๋“œ์˜ <style amp-keyframes> ํƒœ๊ทธ์—์„œ. 500,000 ๋ฐ”์ดํŠธ ์ œํ•œ. ํ‚ค ํ”„๋ ˆ์ž„ ์†์„ฑ์œผ๋กœ ํ•œ์ •.

AMP์˜ CSS ์‚ฌ์šฉ๊ณผ ๊ด€๋ จํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์Šคํƒ€์ผ ๋ฐ ๋ ˆ์ด์•„์›ƒ์„ ์ฐธ์กฐํ•˜์„ธ์š”.

ํŽ˜์ด์ง€๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด AMP๋Š” <amp style-custom> ํƒœ๊ทธ์—์„œ CSS ํฌ๊ธฐ๋ฅผ 75,000 ๋ฐ”์ดํŠธ๋กœ ์ œํ•œํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์Šคํƒ€์ผ์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ <amp style-keyframes> ํƒœ๊ทธ์˜ 500,000 ๋ฐ”์ดํŠธ ์ œํ•œ์€ ๊ท€์ค‘ํ•œ ์‚ฌ์ดํŠธ ์Šคํƒ€์ผ ๋ฆฌ์†Œ์Šค๋ฅผ ์†Œ๋ชจํ•˜์ง€ ์•Š๋Š” ์ •๊ตํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

  <style amp-custom>
    div {
      width: 100px;
      height: 100px;
      background: red;
      position: relative;
      animation: mymove 5s infinite;
    }
  </style>
</head>
<body>

<div></div>
  <style amp-keyframes>
   @keyframes mymove {
      0%   {transform: translatey(0px);}
      25%  {transform: translatey(200px);}
      75%  {transform: translatey(50px);}
      100% {transform: translatey(100px);}
    }
  </style>
</body>

ํด๋ž˜์Šค ์ถ”๊ฐ€, ์ œ๊ฑฐ ๋ฐ ์ „ํ™˜

AMP ์•ก์…˜ toggleClass๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ •์˜๋œ ์š”์†Œ์— ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

elementName.toggleClass(class="className")

์• ๋‹ˆ๋ฉ”์ด์…˜ ํ–„๋ฒ„๊ฑฐ ๋ฉ”๋‰ด์™€ ๊ฐ™์ด ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ž™์…˜์„ ํ—ˆ์šฉํ•  ๋™์ผํ•œ ์š”์†Œ์—์„œ ํด๋ž˜์Šค๋ฅผ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<div
  id="hamburger"
  tabindex="1"
  role="button"
  on="tap:hamburger.toggleClass(class='close')"
></div>

force ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋ฉด toggleClass ์•ก์…˜์ด ๋‹ค๋ฅธ ์š”์†Œ์— ์ ์šฉ๋˜๊ฑฐ๋‚˜ ๋‘ ํด๋ž˜์Šค ๊ฐ„ ์ „ํ™˜์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

<button
  on="tap:magicBox.toggleClass(class='invisible', force=true),magicBox.toggleClass(class='visible', force=false)"
>
  Disappear
</button>
<button
  on="tap:magicBox.toggleClass(class='visible', force=true),magicBox.toggleClass(class='invisible', force=false)"
>
  Reappear
</button>

ํด๋ž˜์Šค๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ์žฌ์ ์šฉ์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š์œผ๋ ค๋Š” ๊ฒฝ์šฐ false ๊ฐ’์˜ force ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์ œ๊ฑฐ๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ์•Š์œผ๋ ค๋Š” ๊ฒฝ์šฐ true ๊ฐ’์˜ force ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

CSS ๋ฐ ์ƒํƒœ

amp-bind๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํƒœ๊ฐ€ ์ง€์ •๋œ CSS ํด๋ž˜์Šค๋ฅผ ๊ฐœ์ˆ˜์— ์ƒ๊ด€์—†์ด ์ถ”๊ฐ€ ๋˜๋Š” ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<head>
  <script
    async
    custom-element="amp-bind"
    src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"
  ></script>
  <style amp-custom>
    div {
      height: 100px;
      width: 100px;
      margin: 1em;
      background-color: green;
      margin-left: 100px;
      transition: 2s;
    }
    .visible {
      opacity: 1;
    }
    .invisible {
      opacity: 0;
    }
    .left {
      transform: translatex(-50px);
    }
    .right {
      transform: translatex(50px);
    }
    button {
      margin-top: 1rem;
      margin-left: 1rem;
    }
  </style>
</head>
<body>
  <amp-state id="magicBox">
    <script type="application/json">
      {
        "visibleBox": {
          "className": "visible"
        },
        "invisibleBox": {
          "className": "invisible"
        },
        "moveLeft": {
          "className": "left"
        },
        "moveRight": {
          "className": "right"
        }
      }
    </script>
  </amp-state>
  <div [class]="magicBox[animateBox].className"></div>
  <button on="tap:AMP.setState({animateBox: 'invisibleBox'})">Disappear</button>
  <button on="tap:AMP.setState({animateBox: 'visibleBox'})">Reappear</button>
  <button on="tap:AMP.setState({animateBox: 'moveLeft'})">Move Left</button>
  <button on="tap:AMP.setState({animateBox: 'moveRight'})">Move Right</button>
</body>
์ด ์ฝ”๋“œ ์กฐ๊ฐ์„ Playground์—์„œ ์—ด๊ธฐ

์šฐ์„  ๋ฌธ์„œ head์˜ <style amp-custom> ํƒœ๊ทธ์— CSS ํด๋ž˜์Šค ๋ชฉ๋ก์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์—ฌ๋Ÿฌ ํด๋ž˜์Šค ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

.visible {
  opacity: 1;
}
.invisible {
  opacity: 0;
}
.left {
  transform: translatex(-50px);
}
.right {
  transform: translatex(50px);
}

๋‹ค์Œ์œผ๋กœ ํด๋ž˜์Šค์™€ ์ƒํƒœ์˜ ํŽ˜์–ด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

<amp-state id="magicBox">
  <script type="application/json">
    {
      "visibleBox": {
        "className": "visible"
      },
      "invisibleBox": {
        "className": "invisible"
      },
      "moveLeft": {
        "className": "left"
      },
      "moveRight": {
        "className": "right"
      }
    }
  </script>
</amp-state>

์š”์†Œ์™€ ํด๋ž˜์Šค๋ฅผ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

<div [class]="magicBox[animateBox].className"></div>

์—ฐ๊ฒฐ๋œ AMP ์•ก์…˜ ๋˜๋Š” ์ด๋ฒคํŠธ์—์„œ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ์˜ˆ์‹œ๋Š” ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ž™์…˜์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

<button on="tap:AMP.setState({animateBox: 'invisibleBox'})">Disappear</button>
<button on="tap:AMP.setState({animateBox: 'visibleBox'})">Reappear</button>
<button on="tap:AMP.setState({animateBox: 'moveLeft'})">Move Left</button>
<button on="tap:AMP.setState({animateBox: 'moveRight'})">Move Right</button>

์ด๋Ÿฌํ•œ ๋ฐฉ์‹์œผ๋กœ amp-bind๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ •์˜๋œ ํด๋ž˜์Šค์— ํด๋ž˜์Šค๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋‹ค๋ฅธ ํด๋ž˜์Šค ์ œ๊ฑฐ ์‹œ ๋‹ค์‹œ ๋ช…์‹œํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.