mulbangul.libraries.yml 파일 작성하기

테마 문서: https://www.drupal.org/docs/8/theming-drupal-8/adding-stylesheets-css-and-javascript-js-to-a-drupal-8-theme

모듈 문서: https://www.drupal.org/node/2274843

테마에서 사용할 라이브러리를 mulbangul.libraries.yml 에 정의한다. 이와 관련된 완전한 문서는 asset libraries 를 참조한다.

라이브러리에는 보통 자바스크립트(js), 스타일시트(css), 폰트 등이 있다.

드루팔은 높은 수준의 원칙을 준수합니다: 자바스크립트와 css 같은 자산들은 필요할 때만 로드된다. 모든 페이지에서 모든 자산을 로드하는 것은 프론트앤드의 성능을 저하시키기 때문에 드루팔은 그렇게 하지 않는다.

Css와 Js 를 사용하기 위한 과정

  1. css파일과 js파일을 적절한 위치에 저장한다.
  2. "library"를 정의하고, 사용할 css와 js의 경로를 지정한다.
  3. library를 모든 페이지나 특정 twig 템플릿 또는 전처리 함수의 렌더링 요소와 같이 필요한 곳에 "첨부"한다.

library 정의하기

사용할 모든 라이브러리는 mulbangul.libraries.yml에 정의해야 하고, 각각의 라이브러리는 css와 js 파일을 다음과 같이 상세하게 기입해야한다.

드루팔 8은 모든 페이지에서 jQuery를 기본적으로 로드하는 방법을 사용하지 않는다. 라이브러리가 jquery를 필요로 한다면 dependencies를 통해 jquery를 정의해주어야 한다.

cuddly-slider:
  version: 1.x
  css:
    layout:
      css/cuddly-slider-layout.css: {}
    theme:
      css/cuddly-slider-theme.css: {}
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery

css 정의 부분에 있는 layout과 theme는 css 파일의 스타일 유형을 나타내는 key 이다. js에서는 사용하지 않으며 이것을 CSS weight이라고 한다. CSS weight은 스타일 레벨을 다음과 같이 5가지로 설정할 수 있고, 아래의 5가지 이외에는 사용할 수 없고 사용할 경우 엄격한 경고를 발생한다. 그리고 CSS weight은 반드시 아래의 순서대로 정의하여야 한다.

  • base: CSS 재설정/표준화 및 HTML 요소 스타일링. Key는 CSS_BASE = -200 의 가중치를 가진다.
  • layout: 그리드 시스템과 웹페이지의 매크로 배열. Key는 CSS_LAYOUT = -100 의 가중치를 가진다.
  • component: 독립적이고, 재사용가능한 UI 요소. Key는 CSS_COMPONENT = 0 의 가중치를 가진다.
  • state: 구성요소들의 클라이언트측 변화를 처리하는 스타일. Key는 CSS_STATE = 100 의 가중치를 가진다.
  • theme: 구성요소들의 순수한 시각적 스타일링(“look-and-feel”). Key는 CSS_THEME = 200 의 가중치를 가진다.

모든 페이지에서 로드되어야 하는 스타일시트(CSS 파일)를 위해, 대부분의 테마는 global-styling 라이브러리를 사용한다.

global-styling:
  version: 1.x
  css:
    theme:
      css/layout.css: {}
      css/style.css: {}
      css/colors.css: {}
      css/print.css: { media: print }

자산들의 로딩 순서

*.libraries.yml 파일에 나열된 순서가 로딩되는 순서이다. 모든 JS 자산들은 기본적으로 footer에서 로드된다. 하지만 필요한 JS가 실행되지 않으면 화면에 보여지지 않는 UI요소들을 위한 JS는 다음과 같이 header 속성을 true로 설정하여 header에 로드할 수 있다.

js-header:
  header: true
  js:
    header.js: {}

js-footer:
  js:
    footer.js: {}

CSS properties

다음 속성들은 선택사항이고 CSS 자산별로 적용된다.

browsers 자산을 브라우져의 조건에 따라 로드한다. {IE:'lte IE 9','!IE':false}
group 자산들을 그룹별로 집계한다. Default: The SMACS group in which the asset is placed. 거의 사용되지 않는다.
media 미디여 유형 { media:print}
minified 자산이 이미 최소화 되었는지 여부 기본값: false { type: external, minified:true}
preprocess Whether the assets should be aggregated. 기본값: true { preprocess:false}
type 자산의 출처 기본값: file { type: external, minified:true}
weight Adjusts order relative to other assets (within the same SMACS group). 기본값: 0. -50부터 +50까지의 숫자. { weight:1}

JS properties

다음 속성들은 선택사항이고 JS 자산별로 적용된다.

attributes 스크립트의 추가 속성 { type: external, attributes:{ async:true}}
browsers 자산을 브라우져의 조건에 따라 로드한다. {IE:'lte IE 9','!IE':false}
preprocess Whether the asses should be aggregated. 기본값: true { preprocess:false}
type 자산의 출처 기본값: file { type: external, minified:true}
weight Adjusts order relative to other assets within the same group (settings, library, default, theme). { weight:1}

라이브러리의 재정의와 확장

*.libraries.yml 파일에 정의한 라이브러리를 재정의하려면 *.info.yml 파일을 만들고, libraries-overridelibraries-extend 를 사용한다. *.info.yml 파일에 추가한 재정의는 서브테마에 상속된다.

*.info.yml 파일에 사용된 stylesheets-remove 속성 은 더이상 사용되지 않고, 드루팔 9에서는 제거된다. stylesheets-override 속성은 이미 제거됐다.

libraries-override

재정의를 할 때 사용되는 로직은 다음과 같다:

  1. 라이브러리 이름은 원래 모듈(또는 코어)의 네임스페이스를 사용한다.
  2. 키는 가장 최근에 재정의한 경로를 사용한다.
  3. 경로는 파일의 전체경로여야 한다.

예제:

libraries-override:
  contextual/drupal.contextual-links:
    css:
      component:
        /core/themes/stable/css/contextual/contextual.module.css: false

여기에서 contextual/drupal.contextual-links 는 코어 라이브러리의 네임스페이스이고, /core/themes/stable/css/contextual/contextual.module.css: 는 이 라이브러리를 가장 최근에 재정의한 것의 전체경로이다. 이 경우에 그 파일은 false 값으로 재정의됐다.

마지막 부분이 실제 파일의 시스템 경로이고, 다른 부분은 네임스페이스를 참조한다는 것이 중요하다. css:component: 행은 재정의되는 라이브러리의 구조를 반영한다.

참고) 라이브러리의 구조 : 드루팔 8은 CSS의 규칙으로 다음과 같은 SMACSS 스타일 분류를 따른다. 보다 조사한 내용은 문서를 참조한다.

Base — CSS reset/normalize plus HTML element styling.
Layout — macro arrangement of a web page, including any grid systems.
Component — discrete, reusable UI elements.
State — styles that deal with client-side changes to components.
Theme — purely visual styling (“look-and-feel”) for a component.

파일 시스템의 경로에 의존하는 것은 사이트의 파일 구조가 바귀었을 때 경로가 끊어질 수 있다는 것을 의미한다. 이러한 이유때문에 stream wrappers를 사용함으로써 전체 경로에 대한 의존을 제거는 것에 대한 이슈가 있다.

CSS나 JS 자산 또는 사용하는 테마가 모듈과 테마로부터 상속받은 라이브러리 전체를 제거하거나 대체하기 위해 libraries-override 를 사용할 수 있는 방법이 몇가지가 있다.

libraries-override:
  # Replace an entire library.
  core/drupal.collapse: mytheme/collapse

  # Replace an asset with another.
  subtheme/library:
    css:
      theme:
        css/layout.css: css/my-layout.css

  # Replace a core module JavaScript asset.
  toolbar/toolbar:
    js:
      js/views/BodyVisualView.js: js/views/BodyVisualView.js

  # Remove an asset.
  drupal/dialog:
    css:
      theme:
        dialog.theme.css: false

  # Remove an entire library.
  core/modernizr: false

libraries-extend

libraries-extend 는 라이브러리가 첨부될 때마다 추가적인 테마 종속 라이브러리를 추가함으로써 라이브러리의 자산을 변경하는 방법을 제공한다.
libraries-extend 는 라이브러리를 다수의 다른 라이브러리로 확장하는 것이다.

이 기능은 특정 구성요소를 테마에서 다르게 스타일링하는데 이상적이다. 또한 모든 페이지에서 CSS를 로드하지 않으면서 구성요소의 모양을 바꾸기 위해 global CSS 같은 파일을 수정하지 않아도 된다.

# Extend drupal.user: add assets from classy's user libraries.
libraries-extend:
  core/drupal.user: 
    - classy/user1
    - classy/user2

라이브러리를 페이지에 첨부하기

어떤 라이브러리는 모든 페이지에서 로드될 필요가 없다. 더 빠른 성능을 위해 라이브러리가 사용되지 않는 것에서는 로드하지 않는 것이 좋다. 아래에 라이브러리를 선택적으로 로드하는 예제가 있다.

Twig template 에서 라이브러리를 첨부하기

Twig 템플릿인 *.html.twig 에서는 attach_library() 함수를 사용하여 라이브러리를 첨부할 수 있다.

{{ attach_library('fluffiness/cuddly-slider') }}
<div>Some fluffy markup {{ message }}</div>

모든 페이지에 라이브러리를 첨부하기

테마에서 사용하는 모든 페이지에 라이브러리를 첨부하려면, *.info.yml 파일에서 libraries 키 아래에 사용할 라이브러리를 지정한다.

name: Example
type: theme
core: 8.x
libraries:
  - fluffiness/cuddly-slider

원하는 만큼 라이브러리를 지정할 수 있고, 지정한 모든 라이브러리는 모든 페이지에서 로드된다.

*.info.yml 파일을 수정하면, 새로운 정보가 드루팔에 로드될 수 있도록 캐쉬 클리어를 해야한다.

라이브러리를 일부 페이지들에만 첨부하기

어떤 경우에는 라이브러리를 모든 페이지가 아닌 일부 페이지에서만 활성화하면 된다. 예를 들어 어떤 블록이 보여질때나 어떤 노드 유형이 보여질때만 라이브러리가 필요할 수 있다.

이러한 작업은 .theme파일에서 THEME_preprocess_HOOK() 함수를 구현하면 가능하다. "THEME"를 테마의 기계명으로 바꾸고, "HOOK"을 테마 hook의 기계명으로 바꾼다.

예를 들어, 사이트 점검 페이지에 자바스크립트를 첨부하고 싶다면, "HOOK" 은 "maintenance_page"으로 바꾼다. 그러면 함수는 다음과 같다:

function mulbangul_preprocess_maintenance_page(&$variables) {
  $variables['#attached']['library'][] = 'mulbangul/cuddly-slider';
}

다른 테마 훅과 비슷한 일을 할 수도 있다. 예를 들어 "block" 훅에서 어떤 블록이 전처리되고 있는지, "node" 훅에서 어떤 노드 유형이 전처리되고 있는지 등을 감지할 수 있다.

You can do something similar for other theme hooks, and of course your function can have logic in it — for instance to detect which block is being preprocessed in the "block" hook, which node type for the "node" hook, etc.

중요한 참고사항! 이 경우, 조건에 해당하는 캐시호환성 메타데이터(cacheability metadata)를 지정해야 한다. 위의 예제는 아무런 조건없이 실행되므로 캐시호환성 메타데이터가 없다. 가장 일반적인 사용 사례는 현재 경로를 기반으로 라이브러리를 연결하는 것이다.

function fluffiness_preprocess_page(&$variables) {
  $variables['page']['#cache']['contexts'][] = 'route';
  if (\Drupal::routeMatch()->getRouteName() === 'entity.node.preview') {
    $variables['#attached']['library'][] = 'fluffiness/node-preview';
  }
}

구성가능한 자바스크립트 첨부하기

어떤 경우에는 계산된 PHP 정보에 따라 페이지에 자바스크립트를 추가하고 싶을 때가 있다.

이러한 경우, 자바스크립트 파일을 만들고, 라이브러리를 정의하고 첨부한다. 또한 자바스크립트 설정(settings)을 첨부하고 자바스크립트 파일이 drupalSettings을 통해 이 설정들을 읽을 수 있도록 한다. 하지만 drupalSettings 이 자바스크립트 파일을 사용할 수 있으려면, jQuery를 사용할 수 있게 한것과 똑같은 작업을 해야한다: drupalSettings 에 대한 종속성을 선언하는 것이다.

cuddly-slider:
  version: 1.x
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery
    - core/drupalSettings

function fluffiness_page_attachments_alter(&$page) {
  $page['#attached']['library'][] = 'fluffiness/cuddly-slider';
  $page['#attached']['drupalSettings']['fluffiness']['cuddlySlider']['foo'] = 'bar';
}

여기서 'bar' 는 계산된 값이다. (여기에서도 cacheability metadata 가 필요하다.)

그러면 cuddly-slider.jsdrupalSettings.fluffiness.cuddlySlider.foo 에 접근할 수 있게된다(그리고 그것은 ==='bar' 일 것이다):

(function ($, Drupal, drupalSettings) {

  'use strict';

  Drupal.behaviors.mybehavior = {
    attach: function (context, settings) {

      console.log(drupalSettings.fluffiness.cuddlySlider.foo);

    }
  };

})(jQuery, Drupal, drupalSettings);

스크립트 요소에 속성 추가하기

스크립트 태그에 속성을 추가하려면 스크립트 URL 다음에 있는 JSON에 attributes 키를 추가해야 한다. attributes 키 다음에 있는 오브젝트에서, 스크립트에 표시할 속성명을 새로운 키로 추가한다. 이 키의 값은 속성의 값이 된다. 만약 값이 true 라면, 속성은 요소의 값을 보이지 않고 속성만 보여준다.

예를 들어:

https://maps.googleapis.com/maps/api/js?key=myownapikey&signed_in=true&libraries=drawing&callback=initMap:
  type: external
  attributes:
    defer: true
    async: true
    data-test: map-link

이렇게 하면 아래의 마크업처럼 결과를 보여준다.:

<script src="https://maps.googleapis.com/maps/api/js?key=myownapikey&signed_in=true&libraries=drawing&callback=initMap" async defer data-test="map-link"></script>

CDN / 외부에서 호스트되는 라이브러리

CDN (Content Delivery Network)으로 외부에 있는 자바스크립트를 사용할 때가 있다. 예를 들어 웹폰트는 외부 URL로만 사용할 수 있다.

외부 라이브러리는 external 선언하면 사용할 수 있다(type: external 로 지정해야한다). 또한 정의를 할 때 외부 라이브러리에 대한 정보들을 포함하는 것이 좋다.

(보통 라이브러리를 CDN을 통해서 로드하는 것은 좋지 않다. 가능하면 그렇지 않는게 좋다. 성능과 보안측면에서 보다 많은 오류가 발생하고, 더 많은 TCP/IP 연결을 설정해야 하고, 대개 브라우져 캐시를 할 수 없는 경우가 많다. )

angular.angularjs:
  remote: https://github.com/angular
  version: 1.4.4
  license:
    name: MIT
    url: https://github.com/angular/angular.js/blob/master/LICENSE
    gpl-compatible: true
  js:
    https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js: { type: external, minified: true }\

페이지의 요청과 동일한 프로토콜로 외부 파일을 요청하려면 프로토콜과 관련된 URL을 지정한다:

js:
    //ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js: { type: external, minified: true }

또한 CSS를 추가하려면 아래에 있는 Font Awesome 와 같이 하면 된다:

font-awesome:
  remote: https://fortawesome.github.io/Font-Awesome/
  version: 4.5.0
  license:
    name: MIT
    url: https://fortawesome.github.io/Font-Awesome/license/
    gpl-compatible: true
  css:
    theme:
      https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css: { type: external, minified: true }

인라인 자바스크립트

인라인 자바스크립트는 사용하지 않는것이 좋다. 인라인으로 사용하고자 하는 JS는 파일로 저장하는 것이 좋다. 왜냐하면 자바스크립트는 클라이언트에게 캐시되기 때문이고 그렇게 해야 자바스크리브를 검토할 수 있다.

마크업을 생성하는 인라인 자바스크립트

이것은 보통 필요가 없다. 자바스크립트는 파일로 저장해라.

마크업을 생성하는 인라인 자바스크립트 예로는 광고, 소셜 미디어 고유 버튼, 소설 미디어 목록 위젯등이 있다. 하지만 이것들은 단지 사이트의 콘텐츠를 꾸미거나 상호작용하는 것이 아닌 외부 콘텐트를 자바스크립트를 통해 가져오는 것뿐인 특별한 종류의 콘텐츠/마크업이다.

이런 자바 스크립트는 사용자 블록이나 Twig 템플릿에 직접 작성하면 된다.

예 :

<script type="text/javascript"><!--
ad_client_id = "some identifier"
ad_width = 160;
ad_height = 90;
//--></script>
<script type="text/javascript" src="http://adserver.com/ad.js"></script>
<a class="twitter-timeline" href="https://twitter.com/wimleers" data-widget-id="307116909013368833">Tweets by @wimleers</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>

전체 페이지에 영향을 미치는 인라인 자바스크립트

모든 인라인 자바스크립트는 사용하지 않는 것이 좋다. 모든 페이지에 영향을 미치는 인라인 자바스크립트의 예는 분석(예. 구글 애널리틱스)과 호스팅되는 폰트가 있다. 모든 페이지에 영향을 미치는 자바스크립트는 두가지로 분류할 수 있다: 프론트 앤드/스타일링과 논리적인 것.

프론트 앤드/스타일링의 경우(예를 들어 호스팅되는 폰트) JS는 테마에 있다. JS를 html.html.twig 파일에 직접 넣어야 한다.

폰트의 경우, 폰트가 로드되는 동안 FOUT (Flash Of Unstyled Text)를 막기 위해 최상의 사용자 경험을 줄 수 있는 위치에 있어야 한다. (JS를 통해 로드되는 폰트는 CSS 이전에 <HEAD> 에 있어야 한다)

(이것에 관한 좋은 글은 “Async Typekit & the Micro-FOUT” 이 있다.)

모듈에 관련된 또 다른 경우는 "Adding stylesheets (CSS) and JavaScript (JS) to a Drupal 8 module" 를 참조하라.

통합 모듈에서의 인라인 자바스크립

모든 인라인 자바스크립트는 사용하지 않는 것이 좋다. 만약 위 에제중 하나를 사용해야한다면, 사용하기 전에 고려해야 할 사항이 있다.

사이트 사용자가 제공하는 인라인 자바스크립트를 허용하는 필드를 제공할때 고려해야 할 것이 두가지가 있다:

  1. 이러한 인라인 자바스크립트를 허용하는 필드, 양식이나 페이지는 반드시 권한을 부여해야 한다.

    예: MODULE.routing.yml

    MODULE.settings:
      path: /admin/config/services/MODULE
      defaults:
        _title: 'MODULE settings'
        _form: \Drupal\MODULE\Form\MODULESettings
      requirements:
        _permission: 'administer site configuration'
    
  2. 설정객체에 저장된 값은 자신의 캐시메타데이터(CacheableMetadata)에 대해 렌더링 시스템에게 알려주어야 한다. 그래야 값이 변경될 때 요소의 렌더링 캐시가 적절히 지워지거나 만료된다.

예: MODULES.module

<?php

/**
 * @file
 * Integrates MODULE in a Drupal site.
 */

use Drupal\Core\Render\Markup;

/**
 * Implements hook_page_bottom().
 */
function MODULE_page_bottom(array &$page_bottom) {
  $settings = \Drupal::config('MODULE.settings');
  $user = \Drupal::currentUser();
  $page_bottom['MODULE'] = [
    '#markup' => Markup::create($settings->get('js_code')),
    '#cache' => [
      'contexts' => ['user'],
      'tags' => ['user:' . $user->id()],
    ],
  ];
  // Add config settings cacheability metadata.
  /** @var Drupal\Core\Render\Renderer $renderer */
  $renderer = \Drupal::service('renderer');
  $renderer->addCacheableDependency($page_bottom['MODULE'], $settings);
}

Differences with Drupal 7

More information

results matching ""

    No results matching ""