일부 테마/컴포넌트를 깨뜨릴 수 있는 예정된 핵심 변경 사항 (4월 12일)

다음 주에는 테마와 컴포넌트가 QUnit 테스트를 사용할 수 있도록 하는 이 PR을 병합할 예정입니다. 다만 이 PR은 Discourse가 테마의 JavaScript를 처리/전사(transpile)하는 방식도 변경합니다. 코어 코드에 많은 수정을 하지 않고서는(이는 다른 하위 호환성 문제를 일으킬 수 있습니다) 이러한 변경을 하위 호환 방식으로 만드는 것은 매우 어렵기 때문에, 사이트를 업그레이드할 때 테마/컴포넌트의 JavaScript가 깨질 수 있습니다.

이 글에서는 테마/컴포넌트에 영향을 줄 수 있는 변경 사항이 무엇인지, 그리고 이를 수정하기 위해 무엇을 해야 하는지 설명하겠습니다.

1. <script type="text/discourse-plugin"> 태그 내부의 JavaScript는 strict mode가 활성화된 상태로 실행됩니다

이 변경 사항은 <script type="text/discourse-plugin"> 태그 내부에 있지 않은 JS 코드에는 영향을 미치지 않습니다. 코드가 일반적인 <script> 태그나 독립적인 .js 파일에 있다면 이 변경 사항으로부터 완전히 안전합니다.

테마/컴포넌트가 이 변경 사항의 영향을 받는지 확인하는 가장 쉬운 방법은 JS 코드를 즉시 실행되는 함수 표현식(IIFE)으로 감싸고 코드 맨 위에 "use strict";를 추가하는 것입니다. 예를 들어, 현재 테마 코드가 다음과 같다고 가정해 보겠습니다:

<script type="text/discourse-plugin" version="0.8.11">
  a = 5;
  console.log(a);
</script>

IIFE로 감싸면 다음과 같이 보일 것입니다("use strict";는 strict mode를 활성화하므로, strict mode에서 코드가 어떻게 동작하는지 테스트하기 위해 중요합니다):

<script type="text/discourse-plugin" version="0.8.11">
  (function() {
    "use strict";
    a = 5;
    console.log(a);
  })();
</script>

이렇게 한 후 컴포넌트가 더 이상 작동하지 않는다면, 사이트를 업그레이드할 때 깨질 것입니다. 코드를 수정하려면 먼저 MDN 문서에서 JavaScript Strict mode에 대해 읽고, 테마/컴포넌트가 strict mode에서 금지된 동작을 하고 있는지 확인하는 것을 강력히 권장합니다. 금지된 동작이 있다면, 그런 동작을 하지 않도록 코드를 리팩토링해야 합니다.

가장 자주 발생하는 오류는 var(또는 let/const) 키워드 없이 변수를 선언할 때 발생하는 ReferenceError입니다. 위 예시에서 a = 5; 라인은 var를 추가하는 것을 잊었기 때문에 strict mode에서 ReferenceError 예외를 발생시킵니다. 수정 후 코드는 다음과 같습니다:

<script type="text/discourse-plugin" version="0.8.11">
  (function() {
    "use strict";
    var a = 5;
    console.log(a);
  })();
</script>

테스트/수정이 완료되면 IIFE와 "use strict"; 라인을 제거하셔도 됩니다.

2. 테마 JavaScript 모듈 경로에 테마 ID가 접두사로 추가됩니다.

얼마 전 테마 JavaScript를 여러 파일로 분리할 수 있는 새로운 기능을 추가한 적이 있습니다. 다가오는 변경 사항을 설명하기 위해 이 기능에 대한 맥락을 조금 설명해 드리겠습니다.

독립적인 JavaScript 파일이 포함된 테마/컴포넌트가 Discourse 인스턴스에 설치되면, Discourse는 모든 JavaScript 파일을 순회하며 각각에 대해 JavaScript 모듈을 생성합니다. 각 모듈은 고유한 식별자(즉, 경로)가 필요하므로, Discourse는 파일 경로(약간의 변경 사항 포함)를 모듈 경로로 사용합니다.

예를 들어, 테마/컴포넌트에 javascripts/discourse/helpers/my-helper.js라는 파일이 있다면, Discourse는 해당 파일에 대한 모듈을 생성하고 discourse/helpers/my-helper를 경로로 지정하며, 해당 모듈에는 원래 파일 내의 JavaScript의 전사된 버전이 포함됩니다.

모듈의 좋은 점은 한 모듈에서 다른 모듈로 클래스/함수/오브젝트 등을 가져올 수 있다는 것입니다. 예를 들어, 다음과 같은 import 문을 사용하여 my-helper에서 xyz라는 함수를 다른 모듈로 가져올 수 있습니다:

// javascripts/discourse/controllers/my-theme-controller.js

import { xyz } from "discourse/helpers/my-helper";

다음 주에 병합할 PR은 테마 모듈 경로에 접두사를 추가합니다. 따라서 우리 예시에서 my-helper는 discourse/helpers/my-helper에서 discourse/theme-<theme_id>/helpers/my-helper로 변경됩니다. 이는 모듈 경로가 변경되었기 때문에 import 문이 더 이상 작동하지 않음을 의미합니다. 이를 수정하려면 import 문의 경로를 절대 경로에서 상대 경로로 단순히 변경하면 됩니다:

// javascripts/discourse/controllers/my-theme-controller.js

import { xyz } from "../helpers/my-helper";

이제 import 문이 다시 작동해야 합니다. (2)번 항목의 영향을 받는 실제 컴포넌트 예시와 수정 방법은 이 PR들 1과 2를 참고하세요.

다시 한번 말씀드리지만, 이는 테마/컴포넌트가 자신의 모듈 중 하나에서 가져올 때만 영향을 미칩니다. 코어 모듈을 가져오는 것은 이 변경 사항의 영향을 받지 않습니다.

도움이 되셨으면 좋겠습니다. 질문이 있으시면 알려 주세요!

31개의 좋아요

자세한 설명 감사합니다 :slight_smile:

이 변경 사항이 테마 컴포넌트에서 사용되는 플러그인 내 파일의 “절대” 경로에 영향을 미칠까요? 예를 들어 layouts 플러그인과 함께 작동하는 테마 컴포넌트는 다음과 같이 layouts 플러그인 자체의 헬퍼를 필요로 합니다.

requirejs('discourse/plugins/discourse-layouts/discourse/lib/layouts')

예를 들어 layouts category list 위젯을 참고하세요.

여기서 경로 변경은 플러그인 자산 파이프라인의 네임스페이스(플러그인 이름 대신 테마 ID 사용)와 일관성을 갖게 하며, 테마 컴포넌트에서 사용되는 플러그인 자산 경로는 그대로 유지될 것 같습니다. 그리고 위와 같은 require 문도 여전히 작동할 것 같습니다. 맞습니까?

7개의 좋아요

네, 맞습니다 :+1:

6개의 좋아요

@loginerror, 이 주제가 유용할 거라고 생각해요 :wink:

7개의 좋아요

@Terrapop
이 부분에 대해 우려가 있으셨던 것 같습니다.

5개의 좋아요

이 기능이 제한적으로 배포되었을 때 이미 코드를 수정했습니다.

5개의 좋아요