본문 바로가기
Web Development/Javascript

[Javascript / Spring Legacy / JSP] Spring Framework에서 자바스크립트 모듈 import 하는 방법 (without NodeJS)

by 감자맹고우 2022. 5. 18.
728x90
반응형

오늘도 어김없이 스프링 프로젝트를 진행하는 도중, Javascript 라이브러리(three.js)를 쓸 일이 생겼다.

최근 Javascript 라이브러리는 NodeJS를 기본사양으로 하기 때문에 import/export로 모듈화하여 사용되는 경우가 많다. three.js도 그런 상황이었는데, 레퍼런스를 보고 따라하다보니 import를 해야했는데 NodeJS가 아니라서 에러가 지속적으로 발생하면서 해결되지 않았다.

그러나 결국 갖은 구글링과 시도 끝에 해결했는데 다른 사람들도 시간을 많이 낭비할 수 있을 것 같아 이렇게 공유하고자 한다.

 

참고로, three.js는 three.module.js 로 자바스크립트 모듈을 사용하는 방식과 three.js / three.min.js 와 같이 기존 방식을 모두 제공한다. 이외의 모듈 코드도 jsm폴더와 js폴더로 구분해서 각각을 이용할 수 있도록 구성이 되어있으니 기존 방식을 사용하고 싶은 사람은 기존 방식으로 외부 참조하면 정신건강에 이롭다.

 

(물론, 장기적으로는 모듈 방식이 좋다. three.js가 모듈 방식을 권장하고 모듈 방식으로 개발되고 있으며, 기존 방식에 대한 업데이트 지원이 약화되고 있기 때문에 유지보수가 필요한 프로젝트라면 모듈 방식을 이 참에 적용해보는 것이 좋다)

 

[ 에러 발생 내역 ]

 

일단 해결과정에서 겪었던 에러 목록은 다음과 같다.

그러니 해당 에러를 겪어서 찾아온 분들도 아래의 해결방법을 참고하면 해결의 실마리를 얻거나 해결할 수 있을 것으로 생각한다.

 

1. Uncaught SyntaxError: Cannot use import statement outside a module

2. Failed to resolve module specifier "three". Relative references must start with either "/", "./", or "../".

3. The requested module 'three' does not provide an export named 'EventDispatcher'

 

 

[ 해결 방법 ]

 

1.

보통 JSP에서 외부 JS를 참조할 때는 아래와 같은 방식으로 참조하지만, 

 

<!-- JSP -->
<head>
    <script src="static/js/threejs-dev/build/three.module.js"></script>
    <script src="static/js/test.js"></script>
</head>

 

export 로 처리된 코드를 해당 방식으로 참조하고 test.js에서 import해서 코드를 작성하려고 하면, 1번의 에러를 만나게 된다. 말그대로 모듈 밖에서 import statement를 사용할 수 없다는 뜻인데 해결방법은 간단하다.

 

에러에 나와있는 것처럼 import 하려는 script 태그에 type 속성을 module로 지정해주면 된다.

(export 처리된 script가 아니다!)

 

<!-- JSP -->
<head>
    <script src="static/js/threejs-dev/build/three.module.js"></script>
    <script type="module" src="static/js/test.js" defer></script>
</head>

 

위 코드처럼 test.js에 import해서 사용할 것이므로 type속성을 module로 지정하고, defer 속성을 추가하여 처리 순서에 있어 명확성을 부여했다.

 

 

2.

이제 문제는 import를 어떻게 할 것이냐인데, 처음 생각했던 import 방식은 다음과 같았다.

 

/* test.js */

//1st trial
import THREE from 'static/js/threejs-dev/build/three.module.js';

//2nd trial
import THREE from './threejs-dev/build/three.module.js';

//3rd trial
import * as THREE from './threejs-dev/build/three.module.js';

 

결과는 모두 동일하다. 2번의 에러를 만나게 된다.

아니 첫번째 시도는 그렇다고 쳐도, 두번째부터는 ./로 시작하도록 수정했는데 왜 똑같은 에러를 만날까.

 

구글링과 여러 가지 시도를 해본 끝에, 원인은 시작부분을 제외하고 .이 들어가는 경로가 있으면 안된다는 것을 알 수 있었다.

문제는 three.module은 three-module 이런식으로 고쳐본다고 하더라도 파일 확장자인 .js만큼은 붙여야 정확한 경로가 된다는 것이었다. (NodeJS와는 다르게 .js를 붙이지 않으면 경로를 받아오지 못한다)

 

반응형

 

여기서 포기할까 했지만, 한 번 해결해놓으면 앞으로 도움이 될거란 생각에 끝까지 해보기로 했다.

그렇게 찾은 방법은 바로 importmap 이었다.

 

일단, 가장 중요한 것은 지원되는 브라우저라고 생각한다.

 

https://caniuse.com/import-maps

 

다행히 내가 개발하는 프로젝트는 기준을 크롬으로 하고 있기 때문에 쓸 수 있었지만, IE나 Firefox, Safari 등을 지원해야 한다면 다른 방법을 찾아야 한다... (지못미)

하지만, 다른 경우에도 해결할 수 있는 운좋게 방법을 찾았다. import할 때 from의 경로에 .js를 써주되, import하는 자바스크립트 모듈에서도 .js를 추가하는 방법이다. 일단 이 글에서는 importmap을 사용하여 해결하기 때문에, 여기에서는 이까지만 설명하겠다.

 

아무튼, importmap을 사용할 수 있다면 이제 다음과 같이 에러를 해결할 수 있다.

 

<!-- JSP -->
<head>
    <script type="importmap">
        {
            "imports": {
                "three": "./static/js/threejs-dev/build/three.module.js",
                "threejs-dev/examples/jsm/capabilities/WebGL": "./static/js/threejs-dev/examples/jsm/capabilities/WebGL.js"
            }
        }
    </script>
    <script type="module" src="static/js/test.js" defer></script>
</head>

 

위와 같이 JSP에서 importmap으로 import할 자바스크립트 모듈을 imports 에 "import할 명칭" : "실제 경로" 형식으로 넣어주면 아래와 같이 JS에서 import할 수 있다.

 

위에서 three.module.js는 .이 확장자를 제외하고도 있기 때문에 three라고 아예 명칭을 변경해주었고, 나머지 모듈은 NodeJS에서 import하는 것처럼 경로로 보일 수 있게 설정해주었다.

 

/* test.js */

import * as THREE from 'three';
import WebGL from 'threejs-dev/examples/jsm/capabilities/WebGL';

 

여기서 주의할 점은, 자바스크립트 모듈이 export 이면 중괄호{} 를 넣어 import하고, export default 라면 WebGL처럼 중괄호 없이 import하면 된다.

또, *로 파일의 모든 export를 한 번에 import하고 싶다면 as로 명칭을 정해서 import하면 된다.

 

이렇게 하면 import가 더이상 에러를 발생시키지 않으며, 라이브러리를 원하는대로 활용 가능하다!

 

 

※ 추가로, Failed to parse import map: invalid JSON와 같은 에러를 만날 수 있다. 이는 html에서 importmap의 imports의 JSON 형식이 잘못되었다는 뜻인데, 가장 흔하게 발생할 수 있는 원인은 바로 imports 마지막 값 뒤에 쉼표(,) 를 붙여서이다. 따라서, 해당 에러를 만났을 때는 마지막 값 뒤의 쉼표를 제거하면 해결된다.

 

======================================================================

 

3.

The requested module 'three' does not provide an export named 'EventDispatcher'

 

이 에러의 원인은 외부 스크립트를 제대로 참조하지 않으면 발생한다.

 

즉, 외부 스크립트가 export된 자바스크립트 모듈이 아닐 가능성이 높다.

확인 방법은 The requested module 뒤에 나오는 '명칭' 의 파일 경로로 지정한 파일을 열었을 때 아래 쪽에 export나 export default로 처리가 되어있는지 확인하면 된다.

예를 들어, 위에서는 /static/js/threejs-dev/build/three.module.js 파일에 export 처리 여부를 확인하면 된다.

 

보통 three.js에서는 three.module.js가 아니라 three.js / three.min.js 를 파일 경로로 지정했을 때 발생한다.

 

 

이외에 THREE duplicate 이런 단어들이 들어간 에러(기억이 잘 나지 않음)도 만날 수 있는데, 이 에러는 script를 여러번 참조했을 때 발생한다. 예를 들어, script로 three.min.js를 외부참조하고 script importmap에서 three.module.js를 다시 외부참조하는 경우에 발생하는데 하나를 제거해주면 해결된다!

 

🤞 도움이 되셨기를 바랍니다. 한 번의 클릭과 댓글은 어딘가의 누군가에게 진실로 큰 힘이 됩니다. 🐱‍🏍

 

728x90
반응형

댓글