다음 주에는 테마와 컴포넌트가 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를 참고하세요.
다시 한번 말씀드리지만, 이는 테마/컴포넌트가 자신의 모듈 중 하나에서 가져올 때만 영향을 미칩니다. 코어 모듈을 가져오는 것은 이 변경 사항의 영향을 받지 않습니다.
도움이 되셨으면 좋겠습니다. 질문이 있으시면 알려 주세요!