강좌 & 팁
글 수 2,412
2014.06.12 11:45:30 (*.134.169.166)
42389
자바스크립트를 단순하게 사용한다면 모듈이라는 개념은 그리 필요 없을 것이다.
하지만 조금만 복잡한 것을 웹 프로그램을 작성한다면 이 모듈화는 필수적이다.
node.js 는 아예 이 모듈화를 지원하지만
브라우져에서는 기본적으로 지원하지 않는다.
그렇지만 이 모듈화는 필수적이므로 여러분이 만드는 것에 대한 모듈화를
하는 과정을 학습해 보고 가장 보편적인 형태를 만들어 보자.
그리고 최근에 대세인 네임스페이스 역시 구현해 보자
각각의 단계별로 구현할 것이므로 겁먹지 말고 따라해 보고 이해해 보자..
우선 이전에 강좌에서 JSHint 가 욕하지 않는 형태를 살펴 보자.
다음과 같은 형태를 제시했을 것이다.
(function(){
"use strict";
var 변수 = 1;
console.log( '변수 = ' , 변수 );
})();
이전에는 JSHint 에 욕먹지 않을 형태로 제안한 샘플 소스지만
이대로는 단독으로 밖에 쓰지 못하고 이것을 외부에서 사용하기 어렵다.
왜?
이 구분은 분명히 함수 이기 때문이고 함수안에서 선언한 변수나 함수는
외부에서 사용할 수 없기 때문이다.
그렇다면 이 저 내부에서 선언한 변수를 외부에서 어떻게 쓸 수 있을까?
보통 모듈이란 독립된 함수들의 집합이 일반적이다. 물론 모듈내의 변수도 있지만...
일단 다음과 같은 가정을 해 보자..
여러분은 자주 쓰는 함수가 있다고 해 보자.
예를 들면
자주쓰는함수1();
자주쓰는함수2();
이 함수명을 보면 알겠지만 중복될 가능성이 높다.
그래서 앞에다 접두사를 붙여 이렇게 함수 이름을 지으면 중복성을 조금은 제거 할수 있다.
구분1_자주쓰는함수1();
구분1_자주쓰는함수2();
이렇게 쓰는 방식은 조금 무식해 보인다.
뭔가 세련된 방식이 있으면 좋지 않을까?
이때 모듈명을 사용하는 것이다.
그리고 하나의 파일은 모듈명과 일치 하도록 만든다.
먼저 문제점을 제기하기 위해서 다음과 같은 샘플 소스를 만들어 보자.
[C007_module_bad_main.html]------------------------------------------------------
<!DOCTYPE html>
<html lang="ko">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<head></head>
<body>
</body>
<script src="C007_module_bad_module1.js"></script>
<script src="C007_module_bad_main.js"></script>
</html>
--------------------------------------------------------------------------------
[C007_module_bad_module1.js]------------------------------------------------------
(function(){
"use strict";
var 자주쓰는함수1 = function(){
console.log( '자주쓰는함수1()가 호출되었습니다.' );
};
})();
--------------------------------------------------------------------------------
[C007_module_bad_main.js]------------------------------------------------------
(function(){
"use strict";
자주쓰는함수1();
})();
--------------------------------------------------------------------------------
[C007_jshintrc]------------------------------------------------------
{
"undef": true,
"devel": true
}
--------------------------------------------------------------------------------
크롬 브라우저로 실행해 보면 콘솔에 다음과 같은 문구를 볼수 있다.
Uncaught ReferenceError: 자주쓰는함수1 is not defined C007_module_bad_main.js:3
당연하다.
자주쓰는함수1은 C007_module_bad_module1.js 에서 선언하지만 함수로 감싼 형태가 되기 때문에
외부에서 참조 할수 없다.
JSHint 는 뭐라고 나올까?
다음과 같이 검사해 보자
> jshint -c C007_jshintrc --verbose C007_module_bad_main.js
C007_module_bad_main.js: line 3, col 5, '자주쓰는함수1' is not defined. (W117)
1 error
자주쓰는함수1 가 정의되지 않았다고 한다.
물론 여기서 고려해 볼것은 검사 대상이 C007_module_bad_main.js 만 있기 때문에
JSHint 가 C007_module_bad_module1.js 에 있다는 것은 모른다.
문법적으로도 문제가 있고 검사 방법에도 문제가 있다.
차근 차근 하나씩 해결해 가 보자.
우선 자주쓰는함수1()을 전역으로 선언할 수 있는 방법은 뭘까?
가장 간단한 방법은 var 을 없애는 방법이다.
이때는 당연히 "use strict"; 를 쓰면 안된다.
다음과 같이 만들면 문제 없다.
[C007_module_bad_module1.js]------------------------------------------------------
(function(){
자주쓰는함수1 = function(){
console.log( '자주쓰는함수1()가 호출되었습니다.' );
};
})();
--------------------------------------------------------------------------------
정상적으로 호출될 것이고 동작하지만
우리가 원하는 방향은 아니다.
JSHint 도 투덜거릴 것이고 strict 모드도 지원하지 않기 때문에 찝찝하다.
또 이제 자주쓰는함수1() 은 다른 곳에서 이름이 충돌나면 .. 흠 상상하기 싫다.
일단 JSHint 의 투덜 거림도 방지하고 strict 모드도 지원하게 해 보자.
브라우저에서는 전역 변수로 만드는 방법은 window 전역 변수를 이용하는 방법이다.
다음과 같이 수정해 보자..
[C007_module_bad_module1.js]------------------------------------------------------
(function(){
"use strict";
window.자주쓰는함수1 = function(){
console.log( '자주쓰는함수1()가 호출되었습니다.' );
};
})();
--------------------------------------------------------------------------------
아마도 문제 없이 잘 수정될 것이다.
JSHint 를 검사하면 뭐라고 나올까?
다음과 같이 검사해 보자
> jshint -c C007_jshintrc --verbose C007_module_bad_module1.js
C007_module_bad_module1.js: line 4, col 1, 'window' is not defined. (W117)
1 error
window 가 없다고 나온다. ㅠㅠ
이것 역시 변수 선언 없이 사용하는 것으로 인지 하는 것이다.
당연히 이건 옵션으로 제거해 주어야 한다.
이와 관련된 옵션은
browser
이다.
다음과 같이 옵션을 설정한 후 JSHint 를 돌려 보자.
[C007_jshintrc]------------------------------------------------------
{
"undef": true,
"browser": true,
"devel": true
}
--------------------------------------------------------------------------------
이제 JSHint 로 검사하면 아무런 에러 메세지가 나오지 않는다.
이번엔 다시 C007_module_bad_main.js 를 검사해 보자.
> jshint -c C007_jshintrc --verbose C007_module_bad_main.js
C007_module_bad_main.js: line 3, col 5, '자주쓰는함수1' is not defined. (W117)
1 error
역시 그런 함수 선언된 적이 없다고 나온다.
자 검사 대상에서 필요한 함수는 다른 곳에서 정의 되어 있다.
그렇다고 JSHint 는 스스로 그것을 알 수는 없다.
이런 경우에는 어떻게 할까?
JSHint 를 지원하는 다른 유틸리티를 사용하면 해결 할 수 있지만
여기서는 JSHint 에 집중하자.
이런 것을 처리 하는 옵션이 있는데
global 과 globals
입니다.
우선
global
을 이용해서 문제를 해결해 봅시다.
인라인 방식으로 옵션을 설정해 보죠
다음과 같이 C007_module_bad_main.js 을 수정해 봅니다.
[C007_module_bad_main.js]------------------------------------------------------
/* global 자주쓰는함수1 */
(function(){
"use strict";
자주쓰는함수1();
})();
--------------------------------------------------------------------------------
이렇게 수정 후 다음과 같이 검사하면
> jshint -c C007_jshintrc --verbose C007_module_bad_main.js
에러가 없다고 나오는 겁니다.
이렇게 하는 것이 무척 귀찮게 되는 JSHint 의 문제이기는 한데.
이런 귀찮음도 모듈로 처리하면 쉬워 집니다. ^^
이렇게 하지 않고 환경 설정 파일에 넣는 것이
globals
입니다.
C007_jshintrc 파일을 다음과 같이 수정해 봅시다.
[C007_jshintrc]------------------------------------------------------
{
"undef": true,
"browser": true,
"devel": true,
"globals": {
"자주쓰는함수1" : true
}
}
--------------------------------------------------------------------------------
그리고 다음과 같이 C007_module_bad_main.js 를 원래 대로 만듭니다.
[C007_module_bad_main.js]------------------------------------------------------
(function(){
"use strict";
자주쓰는함수1();
})();
--------------------------------------------------------------------------------
이렇게 수정 후 다음과 같이 검사하면
> jshint -c C007_jshintrc --verbose C007_module_bad_main.js
에러가 없다고 나올 겁니다.
어찌되었든 global 과 globals 옵션에 대해서 살펴 보았는데..
이제 원래 모듈에 대해서 알아 봅시다.
위와 같이 전역 변수화 하는 방법은
var 을 없애는 방법과 window 를 사용하는 방법이 있죠..
자 이제 조금 더 살펴 봅시다.
window 가 전역 변수 이기는 하지만 다른 앞서간 친구들이 하는 방법을 따라해 봅시다.
뭐 별거는 아니고 모듈 쪽을 다음과 같이 바꿉니다.
그전에 샘플 파일을 다시 한번 정리해 봅시다.
[C008_module_good1_main.html]------------------------------------------------------
<!DOCTYPE html>
<html lang="ko">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<head></head>
<body>
</body>
<script src="C008_module_good1_module1.js"></script>
<script src="C008_module_good1_main.js"></script>
</html>
--------------------------------------------------------------------------------
[C008_jshintrc]-----------------------------------------------------------------
{
"undef": true,
"browser": true,
"devel": true,
"globals": {
"자주쓰는함수1" : true
}
}
--------------------------------------------------------------------------------
[C008_module_good1_main.js]-----------------------------------------------------
(function(){
"use strict";
자주쓰는함수1();
})();
--------------------------------------------------------------------------------
[C008_module_good1_module1.js]--------------------------------------------------
(function(window){
"use strict";
window.자주쓰는함수1 = function(){
console.log( '자주쓰는함수1()가 호출되었습니다.' );
};
})(window);
--------------------------------------------------------------------------------
브라우저에서 실행하면 콘솔에 다음과 같이 나타날 겁니다.
자주쓰는함수1()가 호출되었습니다. C008_module_good1_module1.js:5
이 문장들은 JSHint 를 돌려 보면 아무런 에러를 내지 않습니다.
문법 에러가 없다는 것입니다.
여기서 우리는 C008_module_good1_module1.js 를 살펴 보아야 합니다.
모듈에서 사용하는 어떤 함수를 전역으로 만들 때 window 를 이용합니다.
그런데 이 모듈 에는 이름 없는 함수 선언과 함께 이 함수의 매계변수로
window 를 받고
마지막에 실행 때 window를 넘깁니다.
여기서 마지막 라인의 실행에 넘긴 window는 브라우저에 정의된 전역 변수 인 window 이고
함수 내부에서 사용하는 window 는 매개변수입니다.
이렇게 하는 이유는 전역 심볼을 참조 하는 것 보다 매개변수 또는 내부에 선언된 변수를
참조 하는 것이 탐색 속도가 더 빠르기 때문에 조금이라도 빠르게 수행하게 하기 위한
꼼수입니다.
왜 다들 이렇게 하는지 이제 이해 하셨나요?
그래서 이런 인자들은 주로 자주 사용되는 인자들을 이렇게 전달해 버립니다.
window 가 그 한 예이고 jQuery 를 내부적으로 사용한다면 이런 변수도 전달하면 됩니다.
자 우리는 이렇게 하기 위해서 남들이 주로 사용하는 형태로 좀 더 깔끔하게 다듬어 봅시다.
[C009_module_good2_module1.js]--------------------------------------------------
(function(window,$,undefined){
"use strict";
window.자주쓰는함수1 = function(){
console.log( '자주쓰는함수1()가 호출되었습니다.' );
};
})(window,jQuery);
--------------------------------------------------------------------------------
함수에 넘어가는 매개 변수로 세가지가 있습니다.
window
$
undefined
앞에 두개는 각각 window 전역 변수와 jQuery 를 많이 쓰기 때문에 넘기는 것이고
마지막 undefined 는 매개변수 끝을 알려 주는 효과가 있습니다.
이렇게 하면 모듈을 로드하면 자주쓰는함수1 을 포함합니다.
그런데 jQuery 는 우리의 HTML 페이지에는 포함하고 있지 않죠..
당연히 이것은 수행에 문제가 생깁니다.
jQuery 는 솔찍히 안 쓸수 없죠..
그래서 우리는 jQuery 를 포함하는 완전한 소스들을 구성해 봅시다.
[C009_module_good2_main.html]------------------------------------------------------
<!DOCTYPE html>
<html lang="ko">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<head></head>
<body>
</body>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script src="C009_module_good2_module1.js"></script>
<script src="C009_module_good2_main.js"></script>
</html>
--------------------------------------------------------------------------------
[C009_jshintrc]-----------------------------------------------------------------
{
"undef" : true,
"browser" : true,
"jquery" :true,
"devel" : true,
"globals" : {
"jQuery" : true,
"자주쓰는함수1" : true
}
}
--------------------------------------------------------------------------------
[C009_module_good2_main.js]-----------------------------------------------------
/* global 자주쓰는함수1 */
(function(){
"use strict";
자주쓰는함수1();
})();
--------------------------------------------------------------------------------
[C009_module_good2_module1.js]--------------------------------------------------
(function(window,$,undefined){
"use strict";
window.자주쓰는함수1 = function(){
console.log( '자주쓰는함수1()가 호출되었습니다.' );
};
})(window,jQuery);
--------------------------------------------------------------------------------
우선 html 코드를 보면 다음과 같은 문장이 추가 된 것을 알 수 있습니다.
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
jQuery 를 다운로드 해서 사용하는 것을 권장하지만
시험을 위해서 다운로드 하고 복사해 넣고 귀찮잖아요..
그래서 이 강좌에서는 위와 같이 직접 참조 하게 만들어 버립니다.
솔찍히 버전업 때문에 자신이 작성한 모듈의 호환성 문제가 생길수 있기 때문에
권장하지는 않습니다.
단지 여기서는 귀찮아서 저렇게 해 버리는 겁니다.
여러분은 가급적 버전에 따른 것을 다운 받고 고정 파일을 참조 하시기 바랍니다.
이렇게 jQuery 를 포함 시켰다면 JSHint 옵션도 좀 더 추가해 주어야 합니다.
그래서 다음과 같은 내용이 C009_jshintrc 에 추가 되었음을 알 수 있습니다.
"jquery" :true,
"globals" : {
:
"jQuery" : true,
:
}
이것을 다음과 같이 검사하면 에러가 없어야 합니다.
> jshint -c C009_jshintrc --verbose C009_module_good2_main.js C009_module_good2_module1.js
지금까지 모듈화를 통해서 함수를 외부에 공개하게 했습니다만..
이렇게 복잡하게 하는게 단순히 이런 이유가 아니죠...
우린 다음과 같은 형태로 쓰고 싶었던 거죠..
유틸.자주쓰는함수1();
이래야 함수이름도 중복이 되지 않고 직관적이고 관리도 쉽죠..
모듈화도 한거고
자 이렇게 하려면 어떻게 하는 것이 좋을까요?
설명하기 보다는 소스로 보여 드리는 것이 좋겠죠?
우선 괜찮은 형태의 모듈 구성 소스를 보여 드리죠.
대부분 이렇게 합니다.
[C010_module_good3_module1.js]--------------------------------------------------
/* exported 유틸 */
var 유틸 = (function(유틸,$,undefined){
"use strict";
유틸.자주쓰는함수1 = function(){
console.log( '유틸 모듈에 있는 자주쓰는함수1()이 호출되었습니다.' );
};
return 유틸;
})(window.유틸 || {},jQuery);
--------------------------------------------------------------------------------
우선 이 소스부터 설명해 드리죠..
먼저 유틸이라는 변수이름으로 모듈명을 갖는 전역 변수를 다음과 같이 만듭니다.
var 유틸 = (function(유틸,$,undefined){
return 유틸;
})(window.유틸 || {},jQuery);
물론 선언후 () 연산자에 의해서 실행 됩니다.
이때 매개변수로 두가지가 들어가는데
두번째는 아시겠지만 첫번째는 조금 이해가 안되시는 분들이 계실 것 같습니다.
이건 이 모듈 파일을 두번 이상 로드 했을 때 여러개의 객체가 생기지 않도록 하는 것이죠
그리고 이 안에 모듈을 통해서 밖으로 서비스 하려는 함수들은
유틸.자주쓰는함수1 = function(){
};
이런 형태로 선언해 가면 됩니다.
물론 변수 역시
유틸.쓸수있는변수 = 0;
이런식으로 선언해 주면 되죠..
그런데 이 유틸은 JSHint 검사시 재 선언 부분에 에러로 지적 받을 수 있습니다.
이런 경우에는 외부에서 사용되는 것이니까 무시하라는 옵션을 주어야 하는데
다음과 같이 선언하면 됩니다.
/* exported 유틸 */
어찌되었든
이렇게 모듈 방식으로 만들어서 유틸.XXXX = X; 와 같은 형식으로
외부 노출을 하지 않은 다른 함수와 변수는 내부에서만 사용되므로
이름 오역 문제도 자동으로 해결되죠...
일종의 private 함수 나 변수가 되어 버리는 겁니다.
자 이 모듈에 포함된 함수와 변수를 외부 모듈에서 쓸 때는 어떻게 해야 할까요?
간단 하죠...
다음이 쓴 예입니다.
[C010_module_good3_main.js]-----------------------------------------------------
(function(){
"use strict";
유틸.자주쓰는함수1();
})();
--------------------------------------------------------------------------------
여러분이 다른 모듈 라이브러리 쓰듯이 쓰시면 됩니다.
참 쉽죠?
여기서 위 파일을 JSHint 로 검사를 하면 에러가 납니다.
왜?
유틸 이라는 전역 변수를 참조 하기 때문이죠,.
이런 것들은 에러가 없개 하기 위해서 다음과 같은 JSHint 설정 파일을 만들어야 합니다.
[C010_jshintrc]-----------------------------------------------------------------
{
"undef" : true,
"unused" : true,
"browser" : true,
"jquery" : true,
"devel" : true,
"globals" : {
"jQuery" : true,
"유틸" : true
}
}
--------------------------------------------------------------------------------
이제 이 파일을 로드할 HTML 파일을 다음과 같이 만들면 끝입니다.
[C010_module_good3_main.html]------------------------------------------------------
<!DOCTYPE html>
<html lang="ko">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<head></head>
<body>
</body>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script src="C010_module_good3_module1.js"></script>
<script src="C010_module_good3_main.js"></script>
</html>
--------------------------------------------------------------------------------
이제 여러분은 위와 같은 기본 형태로 자신만의 모듈들을 만들어 가시면 됩니다.
쉽죠?