관리 메뉴

솔트펀 티스토리

Javascript 언어 본문

MOBILE APP/Javascript

Javascript 언어

SALTFUN 2020. 1. 26. 19:02

https://opentutorials.org/course/743

0. INTRO
1. 언어소개
2. 자바스크립트 기본

      - 실행방법과 실습환경
      - 주석
      - 조건문
      - 배열
      - UI와 API 그리고 문서보는 법
      - 숫자와 문자
      - 줄바꿈과 여백
      - 반복문
      - 객체
      - 정규표현식
      - 변수
      - 비교
      - 함수
      - 모듈

3. 함수지향

      - 유효범위
      - arguments
      - 값으로서의 함수와 콜백
      - 함수의 호출
      - 클로저

4. 객체지향

      - 객체지향 프로그래밍
      - this
      - 표준 내장 객체의 확장
      - 참조
      - 생성자와 new [constructor]
      - 상속
      - Object
      - 전역객체
      - prototype
      - 데이터 타입

5. 패턴

- 재귀함수

6. 마무리

TOP

0. INTRO

Youtube 재생목록

여기서는 Javascript 언어 자체에 대한 내용을 다루고 있다. 문법적인 부분에 대해 보다 많은 내용을 다룬다.

Javascript 관련 기타 다른 강의

  • WEB2- JavaScript https://opentutorials.org/course/3085 
    웹페이지를 사용자와 상호작용하게 만들고자 하는 이들을 위한 내용. 게임이나 애플리케이션처럼 웹페이지를 동적으로 만들 수 있는 방법도 들어 있다.
  • 웹브라우저 자바스크립트 https://opentutorials.org/course/1375
    웹브라우저를 자바스크립트로 제어하는 방법.
  • WEB2 - Node.js https://opentutorials.org/course/3332
    JavaScript를 이용하여 Node.js를 제어해 동적으로 HTML 코드를 생성하는 웹애플리케이션을 만드는 방법
  • JavaScript (nodejs) - 폐지 예정 https://opentutorials.org/course/2136 상단의 course/3332로 공부할 것을 권고
    JavaScript와 Nodejs를 이용하여 웹애플리케이션을 만드는 방법

TOP

1. 언어소개

참고

TOP

2. 자바스크립트 기본

- 실행방법과 실습환경
- 숫자와 문자
- 변수
- 주석
- 줄바꿈과 여백
- 비교
- 조건문
- 반복문
- 함수
- 배열
- 객체
- 모듈
- UI와 API 그리고 문서보는 법
- 정규표현식

• 실행방법과 실습환경

설치

실행

Fig 1. <script> 태그 내용이 javascript

<script>

      javascript 코드

</script>

메모장 등으로도 편집이 가능

 

a. 웹페이지에서 자바스크립트 작성

b. 크롬 개발자 도구 사용

  https://opentutorials.org/course/580

크롬/F12/Console   콘솔창에서 javascript를 직접 실행시킬 수 있다. HTML코드가 전혀 필요없고 순수하게 javascript코드만 작성하면 된다. 명령어를 입력하고 <Enter> 하면 바로 결과를 볼 수 있음. 긴 코드는 파일로 작성하여 작업하는 것이 더 편리하다.

Fig 1의 11 행 대신에 console.log('Hello world'); 를 해주면 alert 와 달리 메세지가 콘솔에 나타난다

Fig 2. console.log()

도구의 선택

Sublime Text :  Editor  유료. 가볍고 여러가지 확장기능이 많다

참고 https://opentutorials.org/module/406/3595

개발자는 좋은 도구를 찾는데 신경을 써야 한다. 인지의 한계를 넘어설 수 있다.

ChapterHead

• 숫자와 문자

수의 표현

alert(1); 

alert(2*8);

alert(6/2);

console.log(3*5);

10*2   콘솔에서는 바로 계산됨

수의 연산

Math.pow(3, 2);  -> 3^2     수학과 관련된 클래스 Math

Math.round(10.6); -> 11   반올림

Math.ceil(10.2);  -> 11   가장 가까운 높은 정수

Math.floor(10.2); ->10 가장 가까운 낮은 정수

Math.sqrt(9);   -> 3  제곱근

Math.random();   1보다 작은 랜덤 수

문자의 표현

alert('coding everybody');

alert("coding everybody");

alert('egoing's coding everybody');  오류 -> alert('egoing\'s coding everybody');

' -> \'

\n  줄바꿈

typeof 수식 ->  수식의 자료형을 되돌림     alert(typeof "1");

문자에 대한 명령들

"coding" + "everybody"

문자열.length  문자열의 길이

자바스크립트 사전 참고 https://opentutorials.org/course/50

http://js.studio-kingdom.com/javascript/ 일본어페이지

문자열.indexof("문자열1")     문자열에서 문자열1의 위치를 0부터 계산

"code".indexof("d")  -> 2

2진수, 이진수

parseInt("2345234").toStriing(2)

decodeURI()    코드화된 URI를 보기 쉽게 바꾸는 함수.  endoceURI()  반대기능

 

base64

ChapterHead

• 변수

사용법

var a=1;    변수는 var로 시작한다.

var a = 'coding', b = 'everybody' ;

 

변수의 효용

 

• 주석

ChapterHead

// 주석

/*

여러 줄 짜리 주석

*/

ChapterHead

• 줄바꿈과 여백

javascript는 줄이 바뀌면 명령이 끝난 것으로 간주. 

명시적으로 명령이 끝난 뒤에 ; 을 붙이는 것이 좋다. 한 줄에 여러 개의 명령을 쓸 때에는 반드시 ; 으로 구분해야 함.

 

 

ChapterHead

• 비교

비교연산자

 a = 1  대입연산자, 2항 연산자

비트연산자

연산자사용방법설명

비트 AND a & b 피연산자를 비트로 바꿨을 때 각각 대응하는 비트가 모두 1이면 그 비트값에 1을 반환.
비트 OR a | b 피연산자를 비트로 바꿨을 때 각각 대응하는 비트가 모두 1이거나 한 쪽이 1이면 1을 반환.
비트 XOR a ^ b 피연산자를 비트로 바꿨을 때 대응하는 비트가 서로 다르면 1을 반환.
비트 NOT ~ a 피연산자의 반전된 값을 반환.
왼쪽 시프트 a << b Shifts a in binary representation b (< 32) bits to the left, shifting in zeros from the right.
부호 유지 오른쪽 시프트 a >> b Shifts a in binary representation b (< 32) bits to the right, discarding bits shifted off.
부호 버림 오른쪽 시프트 a >>> b Shifts a in binary representation b (< 32) bits to the right, discarding bits shifted off, and shifting in zeros from the left.

==과 ===

동등연산자 equal operator  ==    좌항과 우항이 같으면 true, 다르면 false

일치연산자 strict equal operator 엄격한 동등 연산자 ===     좌항과 우항이 엄격하게 같은가

1 === "1"      false

1 == "1"       true

가급적이면 ===을 써라

 

 

===를 사용하자!

alert(null == undefined);       true      null: 값이 없다. 프로그래머가 의도한 상황. undefined: 값이 정의되지 않음. 프로그래머가 의도하지 않은 상황

alert(null === undefined);     false  엄격하게 보면 다르다.

Boolean[true, false], Number[-1, 0.23, 2, 34, 4], String["a", "do", "go"] 과 같이 undefined, null 역시 각각의 데이터 타입이다.

alert(true == 1);     true

alert(true === 1);     false

alert(true == "1");     true

alert(true === "1");     false

alert(0 === -0);    true

alert(NaN === NaN);         false    NaN: 0을 0으로 나누었을 때. 성립하지 않는 수다

 

참고

 http://dorey.github.io/JavaScript-Equality-Table/ (==과 ===의 차이점)

 

부정과 부등호

alert(1!=2);    /true   !: 부정

!==는 !=과 ==의 관계와 같다. 정확하게 같지 않다는 의미. ===의 true/false 결과가 반대로 나옴

 

ChapterHead

• 조건문

조건문이란

if(true/false) {

}

 

else, else if

if(true/false){

}else{

}

 

if(true/false){

}else if(true/false){

}else{

}

조건문의 응용

prompt('당신의 나이는?');     입력창이 뜬다

Fig 3 변수 앞에 var가 없어도 실행은 된다
Fig 4. 변수 앞에 var를 써주는 것이 좋다

 

 

논리연산자

&&  AND

||     OR

!   NOT

 

Fig 5. 논리연산자를 이용

 

boolean의 대체제

if(0)

if(1)

수자 1은 true로 간주

''  빈자열은 false

'문자열'은 true

undefined, null, NaN 은 false

Fig 6. if() 에 들어가 녹색이면 true, 흰색이면 false

 

 

ChapterHead

• 반복문

기본문법

loop, iterate라고 함

while(조건){

        반복해서 실행할 코드

}

Fig 7. document.write  그대로 html 코드안에 삽입됨

while(true){

    document.write("Coding everybody <br />");

}     무한루프에 빠짐

 

반복조건

Fig 8. while 에 조건을 넣어줌

for문

for(초기화; 반복조건; 반복이 될 때마다 실행되는 코드) {

     반복해서 실행될 코드

}

Fig 9. for 문

i = i+1;   ->  i++;       ++i;

alert(i++);   먼저 i를 출력하고 i값을 증가. ++i는 먼저 증가하고 i 값을 사용

i는 iterator의 약자로 보면 됨

 

반복문의 효용

 

 

반복문의 제어(break, continue)

Fig 10. i 값이 5에 이르면 for문을 탈출. break
Fig 11. i 값이 5가 되면 continue 가 실행되어 다음 번 반복과정 실행

반복문의 중첩

Fig 12. 반복문 안에 반복문이 있음

 

 

 

ChapterHead

• 함수

함수란

Fig 13. 함수의 형식
Fig 14. 함수의 예시

 

함수의 효용

 

입력

 

출력

 

다양한 정의 방법

Fig 15. 함수를 정의하는 방법 중의 하나
Fig 16. 일반적인 함수 정의

Fig 15와 Fig 16은 numbering이라는 함수를 서로 다른 방법으로 정의하고 있지만 기능은 똑같다. 호출 할때에는 numbering(); 과 같은 방법으로 한다.

Fig 17은 Fig 15를 설명하기 위해 노랑선으로 표시를 하였는데, 함수의 정의 부분이 numbering이라는 변수에 대입된 것으로 보면 된다. 그리하여 numbering이라는 변수가 함수를 가지게 된 것이다. 그리하여 변수이름뒤에 괄호를 붙여 함수를 호출할 수 있는 것이다.

Fig 18. 익명함수

함수의 이름도 없고 정의한 후에 함수를 괄호로 둘러싸고 뒤에 또다시 함수를 호출하듯이 닫혀있는 괄호쌍을 덧붙이면 함수의 정의와 동시에 바로 호출도 진행되게 되는데 이런 함수를 익명함수라고 한다. 1회성을 호출하는 경우에 이런 테크닉을 쓰는데 이름이 필요없고 바로 써야 하는 경우에 사용하는 방법이다.  이것은 Java의 내부 inner 클래스 중 한 종류인 익명 Anonymous 클래스와 매우 비슷해보인다.

javaScript는 함수의 언어라 불릴 정도로 함수가 언어에서 차지하는 위상이 매우 높다.

 

ChapterHead

• 배열

배열의 문법

Fig 19.. 배열을 담을 때는 [ ]안에 넣고 ,으로 구분한다

배열 안에 있는 하나 하나의 값을 element 원소라고 한다.

원소의 번호를 가리키는 것을 index, 색인이라고 한다.

 

 

배열의 효용

 

배열과 반복문의 조우

 

데이터의 추가

Fig 19. 배열의 데이터를 수정

배열.push(p)   p를 마지막에 추가

배열.concat(p1, p2, ..., pn)   여러 개의 데이터를 추가

배열.unshift(p)    p를 앞에 추가

배열.splice(p1, p2, p3,...)  p1 번째부터 p2 개 만큼 삭제하고 p3... 을 삽입한다.

concat 연결한다는 뜻 concatenate

javascript 사전을 이용하여 더 자세한 내용에 대해 알아 볼 수 있다.

제거와 정렬

Fig 20. 배열 데이터의 제거와 정렬

배열.shift()  첫 번째 원소를 제거

배열.pop();   마지막 원소를 제거

배열.sort()  정렬     javascript 사전 참조

배열.reverse()  역순정렬

 

ChapterHead

• 객체

객체의 문법

만약 인덱스로 문자를 사용하고 싶다면 객체(dictionary)를 사용해야 한다. 다른 언어에서는 연관배열(associative array) 또는 맵( map), 딕셔너리(Dictionary)라는 데이터 타입이 객체에 해당한다.

객체는 객체지향이라는 프로그래밍의 패러다임과 중요하게 연결이 되는 개념이지만 여기서는 데이터를 담아내는 그릇으로서의 객체를 다루고 있으며 객체지향에 대해서는 뒤에서 다룰 것이다.

배열은 [ ] 을 사용하지만 객체는 { }를 사용한다. 객체의 key는 문자열이며, value는 상관없다.

Fig 21. 객체는 여러 가지 방법으로 정의할 수 있다

Fig 21은 객체를 만드는 3가지 방법을 보여준다. 

처음에는 직접 값을 넣어주는 것이다. grades라는 객체의 값을 넣어주는데 'saltfun', 'mirgiana', 'toga' 는 key 이며 [배열의 index 개념], 12, 6, 30 은 value 이다.

points는 먼저 빈 객체를 정의하고 다음 key 별로 값을 주고 있다.

birds는 new Object() 로 먼저 객체를 생성한 후 값을 주고 있다.

값을 가져올 때에는 객체명[key] 혹은 객체명.key  라고 해주면 되는데 [ ] 안에 key 를 쓸 때에는 문자열 식이어야 한다.

birds['parrot'] 과 birds.parrot, 그리고 birds['par' + 'rot'] 는 모두 같은 뜻이다.

 

객체와 반복문의 조우

Fig 22. for 문에서 iterator in 객체 의 형식을 사용
Fig 23.(key in wear) 를 (var key in wear) 즉 iterator 앞에 var를 선언해주는데 여기서는 생략했음

객체가 아닌 배열에서도 객체와 마찬가지로 index 대신 in을 사용하여 for 문을 사용할 수 있다.

Fig 24. 브라우저에서 우클릭/Inspect[요소검사]

브라우저에서 우클릭/Inspect[요소검사] 를 하여 HTML 코드를 볼 수 있다. 개발자도구의 Elements 탭과 같다.

Fig 25. Fig 23 하단에 의해 생성된 HTML 태그

Fig 25 의 HTML 태그는 Fig 23 하단의 document.write에 의해 만들어진 태그이다.

for in 문은 객체 뿐 아니라 배열에서도 사용 가능하다.

 

객체지향 프로그래밍

Fig 26. 객체의 value 역시 객체일 수 있다

객체를 정의할 때 { } 의 내용을 행바꾸기하여 쓸 수 있다. 객체의 value는 객체일 수도 있다. 

grades.list , grades['list']  는 모두 {'saltfun': 30, 'mirgiana':45, 'toga':52} 를 가리킨다.

또한 30, 45, 52 등 객체 value 안의 value를 가리키려면 다음의 방법들을 쓸 수 있으며 의미는 모두 같다.

grades.list.toga

grades.list['toga']

grades['list'].toga

grades['list']['toga']

grades 객체 안에는 위에서 본 것과 같이 'list' key 에 해당하는 value 외에도 또 다른 값을 담을 수 있는데 함수[함수 역시 객체]도 가능하다.

Fig 27. 객체의 value 는 함수도 가능하다

이때 함수를 호출하기 위해 key로 지정한 후 grades['show'](); 와 같이 뒤에 ( ) 를 덧붙인다. Fig 27에서는 ( )를 붙이면 함수가 호출되어 실행되어 웹브라우저에 결과가 나타나지만 [document.write에 의해 grades['show']의 내용을 나타낸다], ( )가 없으면 그냥 콘솔에 value 만을 보여주고 웹브라우저에는 영향을 주지 못한다는 것을 보여주고 있다.

마치도 grades라는 클래스안의 'show'라는 메서드 처럼. 놀랍다.  함수도 값이고 함수도 변수에 저장될 수 있고 값으로서의 함수는 객체 안에 저장될 수 있는 것이다.

함수 안에서 this를 사용하면 함수가 속해있는 객체를 가리키는 변수를 뜻한다.

Fig 28. 함수 안의 this는 함수가 속한 객체 grades를 뜻함
Fig 29. 웹브라우저에서는 객체의 value가 객체일 때 값을 보여주지 않고 그냥 object라는 내용만 보여준다
Fig 30. console.log 를 이용하면 console에서 객체 안 value 가 객체여도 내용을 볼 수 있다
Fig 31. console.log의 인자는 컴마로 구분하여 여러 개를 줄 수 있다

grades['show']();   를 grades.show(); 로 쓸 수 있다.

grades는 'list'라는 데이터와 'show'라는 함수를 그룹핑한 그릇이라고 볼 수 있다. 'list' 데이터와 'show' 함수는 아무런 관련성이 없는 건 아니고 어느 정도 연관성을 갖고 있으며 이러한 것들을 가리켜 객체지향 프로그래밍이라고 한다.   서로 연관되어 있는 데이터와 연관되어 있는 처리를 하나의 그릇 안에 모아 그루핑해놓은 프로그래밍 스타일 기법이 객체지향 프로그래밍이다. 자세한 내용은 뒤에서 다룬다.

 

ChapterHead

• 모듈

모듈이란

프로그램은 작고 단순한 것에서 크고 복잡한 것으로 진화한다. 그 과정에서 코드의 재활용성을 높이고, 유지보수를 쉽게 할 수 있는 다양한 기법들이 사용된다. 그 중의 하나가 코드를 여러개의 파일로 분리하는 것이다. 이를 통해서 얻을 수 있는 효과는 아래와 같다.

  • 자주 사용되는 코드를 별도의 파일로 만들어서 필요할 때마다 재활용할 수 있다.
  • 코드를 개선하면 이를 사용하고 있는 모든 애플리케이션의 동작이 개선된다.
  • 코드 수정 시에 필요한 로직을 빠르게 찾을 수 있다.
  • 필요한 로직만을 로드해서 메모리의 낭비를 줄일 수 있다.
  • 한번 다운로드된 모듈은 웹브라우저에 의해서 저장되기 때문에 동일한 로직을 로드 할 때 시간과 네트워크 트래픽을 절약 할 수 있다. (브라우저에서만 해당)

모듈이란

순수한 자바스크립트에서는 모듈(module)이라는 개념이 분명하게 존재하지는 않는다. 하지만 자바스크립트가 구동되는 호스트 환경에 따라서 서로 다른 모듈화 방법이 제공되고 있다. 이 수업에서는 자바스크립트의 대표적인 호스트 환경인 웹브라우저에서 로직을 모듈화하는 방법에 대해서 알아볼 것이다.

호스트 환경이란?

호스트 환경이란 자바스크립트가 구동되는 환경을 의미한다. 자바스크립트는 브라우저를 위한 언어로 시작했지만, 더 이상 브라우저만을 위한 언어가 아니다. 예를들어 node.js는 서버 측에서 실행되는 자바스크립트다. 이 언어는 자바스크립트의 문법을 따르지만 이 언어가 구동되는 환경은 브라우저가 아니라 서버측 환경이다. 또 구글의 제품 위에서 돌아가는 Google Apps Script 역시 자바스크립트이지만 google apps script가 동작하는 환경은 구글 스프레드쉬트와 같은 구글의 제품 위이다. 여러분은 자바스크립트의 문법을 이용해서 PHP와 같은 서버 시스템을 제어(node.js)하거나 구글의 제품(Google Apps Script)을 제어 할 수 있다. 지금 당장은 어렵겠지만, 언어와 그 언어가 구동되는 환경에 대해서 구분해서 사고 할 수 있어야 한다. 이를 위해서는 다양한 언어를 접해봐야 한다.

호스트 환경에 따라 모듈을 사용하는 방법이 다르기 때문에 각각의 환경별로 모듈화 방법을 별도로 학습해야 한다. 여기서는 기본적으로 웹브라우저에서 파일을 include 하는, 즉 모듈화 하는 방법에 대해 중점적으로 다룬다. 다른 호스트 환경에서의 모듈화 방법은 각자의 매뉴얼을 참고하여햐 한다. 

모듈화

Fig 32. HTML안에 javascript 코드가 들어있다

이제 Fig 32안의 javascript 함수를 따로 파일로 분리하여 모듈화를 해본다. 8행의 type="text/javascript" 최신 javascript 스펙에서는 생략이 가능하다.

Fig 33과 같이 5행에 외부의 javascript 파일을 불러오는 <script> 태그를 넣어주면 src 속성에 지정한 .js 로 된 javascript 파일을 <script> 태그 사이에 삽입한다.

Fig 33. javascript 코드를 분리하여 5행에서 외부파일로 읽어옴
Fig 34. greeting.js 파일에 welcome( ) 함수를 따로 정의하여 모듈화를 함

 

Node.js 의 모듈화

nodejs 는 서버쪽 javascript, 웹브라우저에서 동작하는 javascript는 클라이언트쪽 javascript 이다. nodejs에서 어떻게 모듈을 로드하는 가를 통해 모듈을 로드하는 기능 자체는 javascript 자체가 가지고 있는 기능이 아니고 각각의 호스트환경 [nodejs, 웹브라우저, 구글스프레드시트 등] 에 따라 javascript를 분할하여 읽어들이는 메카니즘이 다르다는 것을 다소 파악할 수 있을 것이다. 

nodejs는 모듈화된 javascript파일을 읽어 올 때 require 함수를 호출하는데, 가져오려고 하는 javascript 파일을 함수의 인자값으로 준다.

Fig 35. nodejs 에서 외부 javascript 파일을 불러오는 예시
Fig 36. 모듈로 존재하는 파일의 내용

Fig 36에서와 같이 모듈로 존재하는 파일에서 외부에서 호출될 함수를 정의할 때 앞에 exports. 가 있는 것을 볼 수 있다.

node.demo.js 를 실행시키면 결과는 다음과 같다.

Fig 37. node.demo.js 의 실행결과
Fig 38. cmd 창에서 실행방법

 

라이브러리란

라이브러리는 모듈과 비슷한 개념이다. 모듈이 프로그램을 구성하는 작은 부품으로서의 로직을 의미한다면 라이브러리는 자주 사용되는 로직을 재사용하기 편리하도록 잘 정리한 일련의 코드들의 집합을 의미한다고 할 수 있다. 프로그래밍의 세계에는 휼룡한 라이브러리가 많다. 좋은 라이브러리를 선택하고 잘 사용하는 것은 프로그래밍의 핵심이라고 할 수 있다. 

프로그래밍의 세계에는 아주 많은 라이브러리가 존재한다. 프로그래머들의 독특한 문화인 오픈 소스를 통해 만들어진 수많은 라이브러리가 있기 때문에 뭔가를 직접 만드는 것도 좋겠지만 누군가가 이미 만들어 놓았는지 당연히 먼저 검색, 질문 등을 통하여 찾아보는 것이 순서이다. 공개된 라이브러리는 만들 때 목적이 그 자체였고 여러 사람이 모여서 만들었기 때문에 혼자 힘으로 그보다 더 나은 결과물을 얻어내는 것은 거의 불가능에 가깝다. 따라서 프로그래밍을 할 때에는 좋은 라이브러리가 있는지를 먼저 잘 찾아내는 것이 매우 중요하다.

 javascript로 웹브라우저를 제어하는 방법으로는 기본적으로 웹브라우저와 javascript가 제공하는 하는 것들을 사용하는 것인데, 다른 어떤 좋은 라이브러리라고 하여도 모두 이것들이 허용하는 범위의 기술을 이용하여 만들어진 것이다.

하지만 라이브러리는 웹브라우저나 javascript가 받아들일 수 있는 범위의 기술을 특정목적에 맞게 잘 짜 놓은 코드들의 집합이기 때문에, 좋은 라이브러리 한 줄의 기능을 직접적으로 구현하려고 하면 1달, 1년이 걸릴 수도 있고 길이가 10000행에 이를 수도 있다. 

javascript의 라이브러리에서 사람들에게 널리 알려지고 많이 쓰이는 라이브러리 중의 하나는 현 시점에서 jQuery라고 할 수 있다. 하지만 라이브러리도 그 시대의 유행이나 흐름에 따라 달라지기 때문에 접하는 시점에 따라 사람들이 많이 사용하는 라이브러리도 변할 수 있다는 점을 항상 념두에 두어야 한다.

https://jquery.com/

Fig 39. jQuery 홈페이지

jQuery를 사용하기 위해 중요한 두 가지가 있다. 우선 jQuery의 javascript 파일을 자신의 웹페이지에 가져와야 한다. 다운로드를 받으면 된다.

또 하나는 API Documentation인데, API 는 jQuery가 사용자들에게 제공하는 명령어 리스트이다. 즉 jQuery 명령어들의 사용설명서가 API Documentation 이다.  http://api.jquery.com

jQuery 파일을 다운 받는 것과 어떤 기능이 있는지를 파악하는 것이 핵심이다.

 

라이브러리의 사용

자세한 내용은 jQuery 수업을 참고하고 여기서는 라이브러리가 무엇인가를 체험하기 위한 예시 정도로 다룬다.

Fig 40. 다운로드. uncompressed, developmet jQuery x.x.x
Fig 41. jQuery-3.4.1.js 의 내용

jQuery-3.4.1.js는 10598행에 이른다. jQuery를 이용하는 방법을 살펴보자.

Fig 42.
Fig 43. Fig 42의 웹브라우저 화면

이제 Fig 42 의 HTML 파일에 jQuery.js 파일을 삽입하고 브라우저에서 잘 읽어 들였는지 확인해보자

Fig 44. jquery-3.4.1.js 읽어들이기
Fig 45. 웹페이지/우클릭/Inspect [요소검사]
Fig 46. 개발자도구/Network 탭을 Reload 하면 jquery.js 가 확인됨

Fig 44의 10~13 행을 제어할 것인데, 14행 뒤에 <script> 태그를 써보자. jQuery에서 약속되어있는 예약어 $로 시작해야한다. jQuery는 모든 경우 $로 시작한다.

'#list': id 값이 list인 태그를 가리킴

'#list li': id 값이 list 인 태그의 하위태그가 li 인 태그를 말함. Fig 44의 10~13 행

Fig 47. jQuery 사용
Fig 48. Fig 47의 웹브라우저 화면

어떤 특정한 버튼을 눌렀을 때 위에서 본 것과 같은 'good'이 'You and I Forever!!!' 로 바뀌게 해보자. 버튼 추가 방법은 이렇다. 14행 뒤에 <input> 태그 를 삽입한다.

Fig 49. 버튼 삽입
Fig 50. execute 버튼

이제 execute버튼을 클릭했을 때 good에서 You and I Forever!!! 로 바뀌게 한다.

Fig 51. id가 execute-btn 인 태그에서 click이 발생하면 ( ) 안의 function 실행
Fig 52 버튼을 누를 때 내용이 변한다

Fig 52와 같은 동작을 구현하기 위해 jQuery를 사용하면 17, 18행의 단 2 줄의 코드만 사용하였지만 jQuery를 사용하지 않고 javascript와 웹브라우저의 기본기능만으로 구현하려면 코드길이가 꽤 만만치 않다.

Fig 53. Fig 51과 같은 기능을 구현. jQuery를 쓰지 않음

Fig 53의 코드가 Fig 51과 같은 기능을 수행한다고는 하지만 여러 가지 문제점을 가지고 있는 완벽치 않은 코드이다.

 

ChapterHead

• UI와 API 그리고 문서보는 법

UI와 API

User Interface

Application Programming Interface의 약자로 프로그램이 동작하는 환경을 제어하기 위해서 환경에서 제공되는 조작 장치이다. 이 조작 장치는 프로그래밍 언어를 통해서 조작할 수 있다. 아래 영상은 UI와 API의 차이점을 설명하기 위한 자료이다.

Fig 54. 브라우저에 상관없이 javascript 코드를 주소창에 입력하면 실행이 된다
Fig 55.

 

문서 보는 법

레퍼런스와 튜토리얼

프로그래밍을 공부하기 위한 자료는 크게 레퍼런스(reference)와 tutorial(안내서)가 있다. 통상 튜토리얼은 언어의 문법을 설명하고, 레퍼런스는 명령어의 사전을 의미하다. 본 수업은 자바스크립트에 대한 일종의 안내서라고 할 수 있고, 자바스크립트 사전은 레퍼런스라고 할 수 있다. 

자바스크립트의 API

자바스크립트의 API는 크게 자바스크립트 자체의 API와 자바스크립트가 동작하는 호스트 환경의 API로 구분된다. 

자바스크립트 API 문서

  • ECMAScript (표준문서) 
  • ECMAScript는 javascript와 같은 의미이다. javascript의 원래 이름은 LiveScript 였고, Java랑 합쳐지면서 JavaScript가 되고 표준화의 길을 걷게 되면서 ECMA에 표준화를 위임. ECMA에서 javascript라는 이름을 사용할 수 없었기 때문에 ECMAScript라는 이름을 사용하여 표준안이 만들어지게 되었다.

Fig 56. ECMA Script

javascript의 기능을 추가하거나 없애거나 하는 일을 ECMA International 이라는 기구에서 진행하는데 그 기구에서 표준안에 대한 문서가 ECMA-262.pdf 이다. 764페이지에 달할 정도로 방대하고 어렵다. 일반적인 javascript 프로그래머들을 위한 매뉴얼이 아닌 javascript가 동작하는 환경을 만드는 개발자들을 위한 문서라고 볼 수 있다. 따라서 이 문서를 보고 공부를 할 수 있다면 가장 좋겠으나 쉬운 일은 아니다. 어렵긴 하지만 가장 표준이기 때문에 야심찬 분들은 도전해봐도 좋을 것이다.MDN: Mozilla Development Network 즉 Firefox에서 제공하는 레퍼런스이며, 생활코딩의 자바스크립트 사전은 MDN의 자바스크립트 레퍼런스를 아주 많이 참고한 한글화된 레퍼런스라고 볼 수 있다.  MDN에는 javascript뿐 아니라 HTML과 CSS에 대해서도 자세한 내용을 다루고 있다.  한글화는 제한적이지만 생활코딩의 자바스크립트사전보다는 훨씬 풍부한 내용을 담고 있다.

Fig 57. MDN 의 JavaScript references

MSDN: 마이크로소프트에서도 JScript에 대한 내용을 제공하고 있다.

여기 까지는 JavaScript라는 언어자체에 대한 API이고 JavaScript가 기본적으로 제공하는 API이기 때문에 호스트환경에 상관없이 공통적으로 사용할 수 있다. 

호스트 환경의 API 문서

각각의 호스트 환경마다 다른 API가 있다. 

웹브라우저API 는 역시 모질라의 MDN Web APIs 에 대한 링크로 가면 엄청난 량의 자료가 있다. 

Fig 58. MDN의 Web APIs

 

Node.js : nodejs 환경에서 제어하고 사용해야 하는 API들에 대한 내용을 볼 수 있다.

Google Apps Script API: 구글의 제품들을 javascript를 써서 프로그래밍적으로 제어하기 위한 여러 가지 호스트 환경의 API들이 나열 되어있다.  

Gmail, Map, Site, 등 구글의 제품들을 제어할 수 있다.

 

ChapterHead

• 정규표현식

오리엔테이션

정규표현식: regular expression

정규표현식(regular expression)은 문자열에서 특정한 문자를 찾아내는 도구다. 이 도구를 이용하면 수십줄이 필요한 작업을 한줄로 끝낼 수 있다.

정규표현식은 하나의 언어라고 할 수 있다. 그러므로 본 수업에서 정규표현식의 모든 것을 다루는 것은 불가능하다. 본 수업은 아래와 같은 전략을 취하고 있다.

  1. 입문자에게 정규표현식이 무엇인가에 대한 개념을 알려준다. 초심자에게는 사용법까지 공부하는 것은 무리다. 나중에 문자를 처리해야하는 상황이 생겼을 때 이곳을 찾아오거나 본 수업을 완주했을 때 마지막 단계로 본 수업을 공부한다.
  2. 정규표현식을 이미 알고 있는 개발자에게는 정규표현식을 자바스크립트에서는 어떻게 사용하는가를 알려준다. 
  3. 정규표현식 자체에 대한 학습이 필요하다면 정규표현식 수업을 공부하자.

정규표현식은 JavaScript 뿐만아니라, JAVA에서도, Perl에서도 기타 수많은 언어들에서 사용되며 정규표현식 전반에 대해 여기서 다루는 것은 무리이고 단지 javascript에서 어떻게 사용하는 지에 대해서만 다룬다. 

정규표현식은 아주 강력한 도구이고 특히 javascript와 같이 web과 관련된 즉 어떤 정보와 관련된 일을 처리하는 언어에서는 정규표현식이 매우 중요하다. 

정규표현식은 예를 들어 이런 거다. 

생활코딩: htttp://opentutorials.org/course/1 입니다.

네이버: http://naver.com 입니다

이런 내용이 있다 치자. 이제 문장 안에 있는 주소를 링크를 걸어주기 위해 <a>태그를 써야 한다면 이렇게 될 것이다.

Fig 60

그런데 이렇게 처리해야 할 내용이 아주 많다면 사람이 하기에는 매우 어려울 것이다. 이런 것들을 프로그래밍적으로 아주 짧은 코드로 처리할 수 있도록 해주는 것이 바로 정규표현식이다.

어떤 정보 안에 특정 텍스트가 있는지를 찾아내야 한다거나 어떤 패턴에 해당되는 것을 찾아 다른 텍스트로 치환해야 한다거나 하는 경우에 정규표현식이 필요하다.

 

패턴 만들기

생성: 정규표현식은 두가지 단계로 이루어진다. 하나는 컴파일(compile) 다른 하나는 실행(execution)이다. 우선 컴파일부터 알아보자.

컴파일: 어떤 작업을 하기 위해, 예를 들어 문자를 치환한다거나 특정 문자가 있는지를 판단한다거나 등의 작업들을 하기 위해 필요로 하는 대상을 찾는 것, 다시 말해 패턴을 찾는 것을 해야 하는데 그것이 컴파일 단계이다. 

즉 패턴을 찾는 것이다.

실행: 찾은 패턴, 즉 찾은 대상에 대해 구체적인 작업을 진행하는 것이 실행 단계이다. 

우선 컴파일 단계부터 알아보자.

정규표현식에서 패턴을 만드는 방법에는 크게 2가지가 있다. 

첫번째는 정규표현식 리터럴이라는 방법이다.   var str = "a"; 와 비슷한데 " "가 아닌 / /안에 찾고자 하는 대상을 넣는다. 아래 그림에서 /a/ 의 a가 찾고자 하는 대상이다라는 것을 pattern이라는 변수에 담아 pattern이라는 변수를 통해 찾고자 하는 것을 사용할 수 있게 되는 것이다.

또 하나의 방법은 정규표현식 객체 생성자라는 방법이다. 똑같이 변수 이름을 pattern으로 하고 new RegExp('a') 로 정규표현식 객체를 만든다. 찾고자 하는 패턴이 'a'라는 것을 의미한다.  이것은 /a/와 같은 의미를 가진다.

둘다 정규표현식 객체를 pattern 이라는 변수에 저장하고 있다. 이 두 가지 방법은 각기 장단점을 가지고 있다.

Fig 61. 패턴을 만드는 두 가지 방법

이렇게 찾고자 하는 정보를 pattern이라는 변수에 저장하였다. 뒤에서 pattern이라는 변수가 출현하는 것은 찾고자 하는 것이 a라는 것을 뜻한다. 

 

RegExp 객체의 정규 표현식

 정규표현식을 통해서 할 수 있는 작업은 크게 3 가지 정도로 얘기할 수 있다. 어떤 패턴을 만들면 패턴에 해당되는 정보를 추출하려는 경우, 이를테면 어떤 문자열이 길게 있을 때, 그 안에서 url에 해당한 정보만 추출해서 뽑아내고 싶다, 또는 email 주소에 해당되는 정보만을 추출해서 빼내고 싶다라고 하는 경우에 정규표현식을 사용한다.

또한 그러한 정보가 문자열안에 있는지 테스트하는 것, 다른 정보로 치환하는 것, 등이 정규표현식을 통해서 하는 중요한 작업이다.

 RegExp.exec()   RegExp 는 정규표현식 객체를 의미한다. 지금 예시에서는 pattern에 들어있는 것이 정규표현식 객체이다. pattern. 이라고 하면 pattern 객체가 사용할 수 있는 함수[메소드] 추천어가 뜨는데 exec 함수를 사용하면 인자로 주어진 문자열 안에서 pattern을 찾는다.

Fig 62.
Fig 63.
Fig 64
Fig 65. pattern에 /a./을 넣었을 때 결과

Fig 65에서는 /a./ 이라고 패턴을 지정하였다. 여기서 점 . 은 특수한 기호이다. 점은 1개의 문자를 의미한다. 즉 a뒤에 오는 임의의 문자를 뜻한다.

Fig 66. test 함수의 실행

test는 패턴을 찾으면 true, 찾지 못하면 false 를 반환한다.  

.exec 함수는 추출, .test 함수는 존재유무를 테스트하는 것이다. 

 

String 객체의 정규 표현식

이번에는 RegExp가 아닌 문자열에 대해 정규표현식을 사용하는 방법을 살펴본다.

String.match()

Fig 67. 패턴을 인자로 주고 문자열에 대해 함수를 적용
Fig 68. replace는 치환을 진행한다

옵션

정규표현식에는 옵션이 있는데 옵션을 통해 정규표현식이 동작하는 방법을 좀 다르게 설정할 수 있다. 옵션 중에서 i와 g에 대해 살펴 본다.

i 옵션은 대소문자를 구분하지 않는다.

Fig 69. i옵션을 사용하면 대소문자 구분없이 찾는다

g 옵션은 global의 약자이다. 

Fig 70. g옵션은 전체 문자열에 대해 패턴을 모두 찾아준다. 옵션은 함께 사용할 수도 있다

 

캡쳐

정규표현식과 관련한 여러 가지 도구들

먼저 다음의 정규표현식이 어떤 의미를 가지는지 위의 정규표현식 관련 도구들을 사용하여 살펴보도록 하자.

(\w+)\s(\w+)     

( ) 그룹을 나타낸다 

\w:  대소영문자 수자 즉, a~z, A~Z, 0~9 를 의미한다

+: 하나이상

(\w+): 영대소문자숫자 가 하나 이상인 것. 한 개 단어라고 생각하면 이해하기 쉽다

\s: white space 즉 하나의 공백

결론은 띄어쓰기로 갈라진 2개의 단어를 뜻한다.

https://regexper.com 에 위의 정규식을 입력하면 도식화해서 보여준다.

Fig 71. regexper에서 보여주는 정규표현식의 그래프
Fig 72. 정규표현식을 입력하면 밑의 예시에서 식에 부합하는 것들을 하이라이트로 표시해준다. 공백으로 구분된 2 개의 단어들이 표시됨
Fig 73

Fig 73 과 같이 ( )를 사용하여 그룹을 지정하고 지정된 그룹을 가져와 사용하는 기능, 사용할 수 있는 개념을 캡쳐라고 한다.

치환

Fig 74. url 있는 곳을 찾아 <a>태그를 생성하여 링크 만들어주기
Fig 75. url를 찾아내기 위한 정규표현식 패턴

\b 에 대한 내용 참조: boundary https://ohgyun.com/392
Matches a word boundary position between a word character and non-word character or position (start / end of string)  단어와 단어가 아닌 문자 사이의 경계를 말함. 문자열의 시작 혹은 끝을 뜻한다. 단어는 (\w+) 인데 [a-zA-Z0-9_] 와 완벽히 같다.

(?:https?):   우선 뒤의 물음표는 앞의 문자가 있어도 되고 없어도 됨을 뜻한다. 즉 http 혹은 https를 뜻한다. (?:ABC)를 사용하면 그룹으로 묶어는 주지만 캡쳐는 하지 않은 비 캡쳐링 그룹이 된다. 이는 특정한 수량 한정자 등을 적용은 하려 하지만 최종 결과에서 따로 구분하여 사용할 필요가 없는 경우에 작용한다. Groups multiple tokens together without creating a capture group.

\/:  /에 대한 메타문자 \/\/ 은 결국 // 임. 

[ ] 안의 내용은 캐릭터 셋

* : 앞의 문자 가 0개 이상

0-9, a-z: 영문자 수자, 맨 뒤에 i 옵션이 있기 때문에 대소문자 모두 포함.

기타 나열된 특수기호는 그 자체

m 옵션: Multiline  여러 줄 모드 사용. 여기서 ^ 및 $는 각 줄의 시작 부분 및 끝 부분과 일치합니다

replace하는 부분의 코드를 자세히 살펴보자.

Fig 76. replace 메소드의 인자로서 함수를 사용하고 있음

content라는 String 문자열에 대해 replace 함수[메소드]를 적용하는데 그 첫 번째 인자 urlPattern은 패턴이고, 두 번째 인자로는 어떤 텍스트가 아닌 function 함수를 전달하고 있다. replace 메소드가 실행될 때 urlPattern에 해당되는 패턴을 content 안에서 찾을 때마다 replace의 두 번째 인자로 정의된 function이 호출된다. 그리고 호출된 시점에서 검색된 결과 문자열[이 예시에서는 어떤 url일 것이다. http://opentutorials.org/course/1 또는 http://naver.com] 을 호출되는 function의 인자로 전달한다. 그리고 그것을 가공하여 return 해준다. 그러면 찾은 문자열과 return 하는 문자열이 치환된다. 다 찾고 최종적으로 변경된 내용을 result 에 저장한다.

 여기서 함수를 호출하는 부분은 뒤 부분에 있는 '값으로서의 함수와 콜백' 섹션에서 살펴 볼것이다. 

 

 

 

문자수 세기

a="온늘 니니랴나. 티 니아. 너라, 나."
aa= a.replace(/[ .,]/g,"")
console.log(aa, aa.length)

공백, 컴마, 점을 제거하고 총 문자 수 세기

TOP

3. 함수지향

- 유효범위
- 값으로서의 함수와 콜백
- 클로저
- arguments
- 함수의 호출

함수지향 카테고리의 하위 수업들은 함수형 언어로서 자바스크립트의 면모를 다룬다. 자바스크립트의 핵심적인 도구는 함수다. 자바스크립트의 함수는 매우 강력하다. 함수에 대한 이해 없이는 자바스크립트를 잘 다루기 어렵다. 또한 자바스크립트에서 함수는 객체를 이해하는 데 가장 중요한 기초를 이룬다. 

• 유효범위

전역변수와 지역변수

javascript와 같은 언어를 일컬어서 함수형 언어라고도 부르는데 javascript에서 함수는 모듈화의 근간이라고 할 수 있다. 코드를 재사용하게 하고 정보의 구성이나 정보를 감추고 어떤 객체의 행위를 지정하는 등에 함수를 사용한다.

유효범위 scope 는 변수의 수명을 의미한다. 

var vscope = 'global';
        function fscope(){
        	alert(vscope);
        }
        fscope();

fscope 함수는 자기 안에 정의되지 않고 바깥쪽에 있는 vscope이라는 변수에 접근하고 있다. 

var vscope = 'global';
        function fscope(){
        	var vscope = 'local';
        	alert(vscope);
        }
        fscope();

이 경우 경고창에 뜨는 것은 'global'이 아닌 'local'이다. 함수 안 즉 함수 측에서 봤을 때 더 가까운 쪽의 변수의 값을 가리키기 때문이다. 

지역변수: local variables   함수 fscope 안에서 정의된 vscope 은 지역변수

전역변수: global variables  함수 fscope 밖에서 정의된 vscope은 전역변수이다. 

지역변수는 function의 { } 안에서만 접근할 수 있지만 전역변수는 javascript 애플리케이션 전역에서 접근할 수 있는 변수가 된다.

var vscope = 'global';
        function fscope(){
        	alert(vscope);
        }
        function fscope2(){
        	alert(vscope);
        }
        fscope();
        fscope2();
        

fscope와 fscope2는 모두 global을 출력한다.

var vscope = 'global';
        function fscope(){
        	var vscope = 'local';
            var lv = 'local variables';
            alert(lv);
        }
        fscope();
        alert(lv);

함수 안에서 alert(lv); 에 의해 'local variables'가 경고창에 뜨지만 함수 밖의 alert(lv); 에서는 함수 내부의 함수로 접근이 불가하기 때문에 undefined 가 발생한다.

var vscope = 'global';
        function fscope(){
          var vscope = 'local';
        }
        fscope();
        alert(vscope);
        

 위 코드가 실행되면 global 이 경고창에 뜬다.

var vscope = 'global';
        function fscope(){
          vscope = 'local';
        }
        fscope();
        alert(vscope);
        

함수 안에서 vscope 앞에 있던 var를 없앴을 때의 실행결과는 local이다.

var vscope = 'global';
        function fscope(){
        	var vscope = 'local';
        	vscope = 'local';
        }
        fscope();
        alert(vscope);
        

이 경우에도 경고창에는 global이 나타난다.

전역변수를 써야 할 확실한 이유를 모른다면 지역변수를 쓰는 것좋다. 어떤 언어이든 전역변수의 사용은 신중을 기해야 한다. 

 

유효범위의 효용

Fig 77. 함수 a( ) 에서 i 를 지역변수로 정의

함수 a( ) 에서 i를 지역변수로 정의하기 때문에 for 문의 i [전역변수] 에 영향을 주지 못한다. 하지만 a( ) 안에서

var i = 0; 대신에 var 를 없앤 i=0; 으로 하면 전역변수 i 에 접근하는 것으로 된다.

Fig 78. 함수 a( )의 i는 전역변수 즉 for 문의 i 를 가리킴

 

전역변수를 사용하는 법

책소개: 더글라스 크락포드의 "자바스크립트 핵심 가이드" 

http://one-shore.com/aaron/books/ 이외에도 여러 가지 책이 있음

JavaScript: The Good Parts by Douglas Crockford

 

이 코드를 통해 말하고자 하는 것은 전역변수를 사용하려고 할 때 어떤 객체형식의 전역변수를 하나만 만들고 그 안에 속성값으로 원하는 다른 변수들을 만들어 사용한다는 것이다. 그러면 다른 사람과 협업하는 경우라도, 변수들 끼리 충돌할 가능성은 현저히 낮아진다.

Fig 79. 객체로 된 하는 전역변수를 만들고 그 안에 원하는 다른 변수를 속성으로 하여 사용

그런데 위 그림에서와 같이 MYAPP이라고 하는 전역변수 하나 조차도 사용하고 싶지 않다면 다음의 방법을 쓸 수도 있다. 자기가 만든 로직 전체를 함수로 감싸준다. function(){ 전체 코드 }

Fig 80. 로직 전체를 function(){ }로 감싸준다

하지만 이렇게만 하면 오류가 발생한다. 이렇게 정의된 함수는 바로 어떤 변수에 넣어 주든지, 아니면 바로 호출해 사용해야 한다. 바로 변수에 넣어주는 것은 이렇다.

fVar = function(){ .....}   

그리고 사용할 때에는 fVar();  라고 하면 되는 것이다.

Fig 80-1. 변수에 함수를 정의하고 호출

 

바로 호출 할 때에는 마지막에 괄호 ( ) 를 붙이고 다시 / 이 전체를 다시 괄호 ( ) 로 감싸준다 [아래 그림과 같이]. 

Fig 81. Fig 80에서 정의한 함수를 바로 사용할 때에는 뒤에 ( )를 붙인 후 이 전체를 괄호 ( ) 로 감싸줌

MYAPP은 앞에 var 이 있기 때문에 [2행] 함수 안에서 선언된 변수가 된다. 즉 더 이상 전역변수가 아니라 이 함수의 지역변수가 된다는 얘기다.

Fig 81-1.  익명함수로 만들고 바로 호출

물론 이렇게 함수이름을 사용할 수도 있다.

Fig 83. 이것은 사실 myappfn 이라는 변수 안에 함수가 들어있는 것. 즉 myappfn 은 전역변수

이때 myappfn 이라는 함수의 이름도 변수 안에 함수가 들어있는 것이고, 그러면 결국 myappfn 이 전역변수가 된다. 단 하나의 전역변수도 허용하고 싶지 않다고 하면 Fig 82와 같이 함수를 정의하고 바로 호출하는 [이런 함수를 익명함수, 즉 이름이 없는 함수라고 한다] 기법을 사용한다.  이런 기법으로 전역변수가 전혀 존재하지 않는 애플리케이션을 구축할 수 있게 되는 것이다. 이런 방법은 jQuery 에서도 사용할 수 있는 방법이고, javascript로 만들어진 많은 라이브러리들에서 모듈화라는 기법으로 많이 사용하는 방법이다.

 

 

유효범위의 대상

지역변수를 선언할 수 있는 곳은 함수로 제한된다. 다른 언어와 비교하며 약간 헷갈릴 수 있는 부분인데, 다른 언어들에서는 블록(대체로 { }) 안에서 유효범위를 제공한다. 

for(int i = 0; i < 10; i++){
        	String name = "saltfun";
        }
        System.out.println(name);

예를 들어 자바에서는 위의 코드가 허용되지 않는다. 자바에서 지역변수를 선언할 때에는 변수 이름 앞에 데이터 형식을 지정해주는데, 즉 String name 은 javascript의 var name 과 같이 변수를 선언하는 것과 같은 의미를 가진다. System.out.println은 javascript의 console.log처럼 이해하면 될 것이다. 위의 자바 코드가 에러를 발생시키는 이유는 { }에서 선언된 즉 for 문의 지역변수가 되기 때문이다. { } 밖에서 호출하면 자바 입장에서는 존재하지 않는 변수를 호출한 것이다. 하지만 다음의 javascript는 좀 다르다.

for(var i = 0; i < 0; i++){
        	var name = 'You and I Forever';
        }
        alert(name);

여기서 for 문 안에서 선언한  var name 은 for 문의 { } 안에서만 유효한 변수가 아니고, 바깥쪽에서도 유효한 변수가 아니라 for문 밖에서도 유효한 변수가 된다. javascript에서는 함수의 { } 안에서 선언된 변수만이 함수 안에서의 지역변수가 되는 것이고 for문 if 문 등의 { } 안에서 선언된 변수는 지역변수로서의 의미를 갖지 않는다. 

정적 유효 범위

자바스크립트는 함수가 선언된 시점에서의 유효범위를 갖는다. 이러한 유효범위의 방식을 정적 유효범위(static scoping), 혹은 렉시컬(lexical scoping)이라고 한다.   lexical: 어휘의

정적 유효범위 혹은 lexical scoping은 뒤에서 다루게 될 클로져 와 연결되어 있는 개념이다.

Fig 84. 함수 안에서 지역변수를 선언하고 다른 함수 호출 후 호출된 함수안에서 변수에 접근하고 있다

Fig 84에서 함수 b 안에서 사용하는 변수 i는 자신을 호출한 함수 a 에서 선언한 지역 변수 i [3번째 행] 가 아닌 첫 행에서 선언된 전역 변수 i 이다. 즉 이때 브라우저에 나타나는 값은 5이다.

Fig 85. 브라우저/개발자도구[F12]/Console 에서 바로 확인 가능

조금만 생각해도 이게 말이 된다. b라는 함수는 어디서든지 호출이 될 것이고 b를 호출하는 곳이 함수라면 거기에 i 가 꼭 있어야 한다는 것은 좀 말이 안되는 것 같다. b 는 자신을 호출한 함수와 상관없이 자신이 선언된 시점에서 전역변수를 찾는 것이다. 즉 사용될 때가 아닌 정의될 때의 전역변수가 사용되게 된다는 것이다.

이것이 정적 유효범위, lexical scoping 의 개념이다.

프로그램의 규모가 커지게 되면 변수의 유효범위에 대해 신경을 써야 한다. 알 수 없는 문제들이 이런 유효범위에서 발생할 가능성이 크기 때문이다. 

Fig 86. 참고
Fig 86-1

https://hacks.mozilla.org/2015/07/es6-in-depth-let-and-const/

http://blog.nekoromancer.kr/2016/01/26/es6-var-let-그리고-const/

 

 

ChapterHead

• 값으로서의 함수와 콜백

함수의 용도 1

JavaScript에서는 함수도 객체다. 다시 말해서 일종의 값이다. 거의 모든 언어가 함수를 가지고 있다. JavaScript의 함수가 다른 언어의 함수와 다른 점은 함수가 값이 될 수 있다는 점이다. 다음 예제를 통해서 그 의미를 알아보자.

값은 변수에 담을 수 있고 javascript에서는 객체인 함수도 역시 변수에 담을 수 있다.

function a() { }

위 코드에서는 a라는 함수를 정의하고 있다.  하지만 이것은 단순한 정의가 아닌 a 라는 변수에 함수를 담는다는 의미를 갖고 있다. 좀 더 분명한 방법을 보면 이해가 될 터인데 실제로는 다음의 의미인 것이다. 

var a = function() { }

즉 함수는 값으로서의 의미를 가지고 a라는 변수에 담기고 있다. 자바스크립트기본/객체/객체지향프로그래밍 에서도 이미 언급된바 있지만, 객체안 key의 value로서도 저장될 수 있다.

Fig 87. b는 객체의 key, 함수는 객체의 value

사실 객체 안에서의 key는 어떤 변수라고 볼 수 있다. 즉 b라는 변수 에 함수값을 저장한 것이다. 이것을 속성 property라고 한다. property에 저장되어 있는 값이 함수이면 이 함수를 메소드라고 한다. 여기서는 a라는 변수 안에 담겨있는 객체 [{ }안에 key:value 로 정의됨] 안에는 b라는 속성이 들어있고, 그 속성의 값은 함수이고 이 함수를 메소드, 또는 메소드 b라고 부른다. 

함수는 값이기 때문에 다른 함수의 인자로도 전달될 수 있다.

Fig 88.   cal 함수를 호출할 때 첫 인자가 함수이름으로도 쓰일 수 있다니(-o-)

함수 cal의 반환값은 func(num) 이며 func는 인자로 전달받는 다. 즉 increase를 전달 받으면 increase(1)이 되어 increase 함수를 호출하게 되는 것이다. 함수이름을 인자로 받아 그것으로 함수를 호출할 수 있다니, 놀랍다. 정말로 쓰고 싶었던 기능이다.

 

함수의 용도 2

함수는 인자로도 사용될 수 있고 리턴값으로도 사용될 수 있다.

Fig 89.

cal('plus')는 return funcs[mode] 에 의해 funcs 라는 객체의 'plus' key에 들어 있는 함수를 뜻하며 (2,1)는 이 함수를 호출하면서 전달하는 2개의 인자가 되는 것이다.

당연히 함수를 배열의 값으로도 사용할 수 있다.

Fig 90. 여러 개의 함수를 배열의 값으로 저장함

 

콜백

콜백은 값으로서의 함수라는 주제와 밀접하게 연결되어있다고 볼 수 있다.

어떤 함수가 수신하는 인자 역시 함수인 경우를 콜백이라고 한다. 함수가 값으로 사용될 수 있는 특성을 이용하면 함수의 인자가 함수로 전달될 수 있다. 값으로 전달된 함수는 호출될 수 있기 때문에 이를 이용하면 함수의 동작을 완전히 바꿀 수 있다. 인자로 전달된 함수 sortNumber[compareFn]의 구현에 따라서 sort의 동작방법이 완전히 바뀌게 된다.

배열을 정리하는 방법에 대하여 설명을 하면서 간단하게 보았던 예제가 바로 콜백의 사례인데, 값으로서의 함수라고 하는 자바스크립트 함수의 특성을 활용한 사례라고 볼 수 있다.

Fig 91. 배열의 예시

Fig 91에서 sort()는 함수를 호출하는 것인데, 이렇게 함수 앞에 numbers. 처럼 뭔가가 있다면 그 뭔가는 객체이다. 즉 numbers는 배열 객체이다. 첫 행의 코드는 배열 객체를 만들어 numbers라고 하는 변수에 담아둔다는 뜻이다. 배열 객체에는 sort라고 하는 함수가 정의되어 있기 때문에 배열객체.sort() 를 통해 배열이 가지고 있는 함수 sort를 호출할 수 있게 되는데, 이때 sort는 객체에 속해 있기 때문에 함수라기보다는 메소드라고 부른다. 또한 sort 와 같이 javascript가 기본적으로 가지고 있으면서 사용자에게 제공하는 기능의 경우 내장객체, 내장메소드, built-in 객체, built-in method 라고 부른다. 사용자가 만드는 객체, 메소드, 함수 들은 사용자 정의 객체, 사용자 정의 함수라고 한다.

sort 라는 함수의 내부적인 코드는 모른다. 알 수도 있겠지만 그것은 브라우저마다 다르고 브라우저 소스코드를 까봐야 아는 것이다.

numbers.sort() 하면 numbers를 정렬한 결과가 numbers에 다시 저장된다.

Fig 92. sort 메소드가 numbers 의 내용에 영향을 준다

결과를 보면 알 수 있듯이 수의 크기에 따라 정렬된 것이 아니라, 문자열의 순서로 정열된 것을 알 수 있다. 

https://www.opentutorials.org/course/50/10 생활코딩/클라이언트/JavaScript사전/Array(배열)/sort 를 보면 sort 내장메소드의 문법과 설명에 대해 알 수 있는데, sort의 인자로는 옵션으로 compareFn [설명에서는 sortfunc 라고 함]라는 비교함수를 줄 수 있다. 옵션이라는 말은 줄 수 도 있고, 주지 않아도 default로 하는 동작이 있다는 뜻이다. 

Fig 93. 생활코딩/클라이언트/JavaScript사전/Array(배열)/sort 의 내용
Fig 94. sort의 인자 예시

sort는 주어진 배열 객체의 값들을 정렬할 때 compareFn 변수가 가리키는 함수의 내용을 실행하는 것을 통하여 각각의 값들이 어떤 것이 우선순위가 높은가를 판별한다. compareFn 변수의 이름은 임의로 정할 수 있으며, 이 변수에는 함수가 담기게 되며, 이 함수는 자기의 포맷을 가지고 있고 그 포맷을 지켜야 하는데, 그 포맷은 바로 정의하는 함수의 인자를 2개로 하는 것이다.  이 인자 두 개를 x, y 라고 한다면 x, y는 객체의 원소들을 비교할 때 사용할 기준 

Fig 95. Edge/개발자모드[F12]/Console 탭에서 실행해봄(크롬과 유사)

함수는 한 번 호출하였는데 배열 객체의 값이 두 개씩 순차적으로 출력되는 것을 확인할 수 있다. return 값이 없기 때문에 numbers 배열 객체가 변하지 않고 그대로 출력되었다. 즉 배열의 값을 순차적으로 2 개씩 compareFn의 인자롤 받는 다는 것을 알 수 있다.

코드를 좀 추가해 return 값을 x, y 비교에 따라 서로 다르게 해본다.

Fig 96. compareFn의 두 인자를 비교해 더 클때, 작을 때, 같을 때 return값을 다르게 하였다. 결과 크기 순서대로 정렬됨

console.log의 결과에서 맨 처음에 동그라미2는 그러한 결과과 2 번 반복되었음을 나타낸다. x 와 y를 비교하여 더 크면 2 (정수 값이면 아무 값이든 상관 없다), 같으면 0, 작으면 -1을 return 하도록 하였으며 x, y값이 많이 출력되는 것으로 미루어 sort가 실행되는 과정에 compareFn이 여러 번 호출 되는 것을 알 수 있다. 어떤 로직인지는 많이 생각해봐야 할 것 같다. 결론적으로 sort 내장메소드는 옵션인자를 주고 그 옵션인자는 함수로 정의되어야 하며  그 함수는 배열 객체의 요소들을 2 개씩 받는다는 것이다. 그리고 return 값을 어떻게 주느냐에 따라 sort 되는 결과가 달라지는데, sort에 대한 더 자세한 내용은 여기를 클릭해보면 된다.

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

Fig 96을 간단히 다음과 같이 해도 결과는 같다.

Fig 97. return x-y 가 두 변수의 크기에 따라 정수, 0, 부수를 반환한다
Fig 98. return y - x 를 사용하여 정렬의 순서를 역순으로 함

여기서 compareFn 함수, 즉 위의 그림에서 comparefunctn 콜백함수이며, 내부적으로 인자를 통해 호출된 후, 이 콜백함수의 반환값을 다시 전달받은 sort는 그 반환값에 따라 동작결과가 달라진다. 즉 값으로서 함수[여기서는 compareFn]를 사용할 수 있기 때문에 원래 함수 [즉 여기서는 sort]의 동작방법을 값을 전달하는 것을 통해 제어할 수 있게 되는 것이다. 콜백이 가능한 것은 javascript의 함수가 값이기 때문에 가능한 것이다.

 

비동기 콜백

콜백은 비동기처리에서도 유용하게 사용된다. 시간이 오래걸리는 작업이 있을 때 이 작업이 완료된 후에 처리해야 할 일을 콜백으로 지정하면 해당 작업이 끝났을 때 미리 등록한 작업을 실행하도록 할 수 있다. 다음 코드는 일반적인 환경에서는 작동하지 않고 서버 환경에서만 동작한다. 

비동기 콜백이 무엇인지 예를 들어 보자.

구독자가 약 1만명 정도 되는 홈페이지를 운영한다 치자. 이 홈페이지는 글을 작성하면 구독자 만명에게 이메일을 발송한다. 서버쪽에서 이런 시스템을 만든다는 것은 그렇게 쉬운 일은 아니다. 글을 작성한 사람이 글을 작성하고 만명의 사용자들에게 이메일을 보낼 때 1명 1s 가 소요된다고 해도 10,000s 걸린다. 거의 3시간이나 말이다. 글작성 -> 이메일발송 [3시간 소요] -> 작성완료.   이런 서비스를 사용하려는 사람은 아마 없을 것이다.

이렇게 글작성 -> 이메일발송 -> 작성완료 [모두 3시간]  이렇게 순서대로 쭉 진행하는 것을 동기적 처리라고 한다. 

한편 글작성 -> 이메일발송예약 -> 작성완료 라고 하면 3시간이 아닌 매우 짧은 시간에 끝나게 된다. 그리고 내부적으로 사용자에게 노출되지 않는 어떤 프로그램이 반복적으로 동작하면서 이메일 발송예약이 들어왔는지를 확인하여 발송예약이 들어와 있다면 이메일 발송작업을 백그라운드에서 3시간동안 진행하면 되는 것이다. 

이것은 마치 todo 같은 거다. 어떤 일을 하다가 처리해야 하는데 아직 처리해야 하는 일들을 머리속에서 계속 생각하고 있으면 지금 하는 일이 진행되지 않을 것이다. 그런 경우에는 해야 할 일들을 어디에 적어놓고 나중에 처리하려고 하는 것이 todo 인데, 이렇게 처리하는 방식을 비동기적인 처리라고 한다. 동기적처리와 비동기적처리는 정보공학에서 중요한 개념이다.

비동기처리는 javascript에서는 어떤 경우에 쓸 수 있을까? 바로 Ajax 라는 것이 있는데 Asynchronous Javascript and XML 의 약자이고 이 말자체는 별로 의미가 없다. XML은 별로 쓰지 않기 때문이고 쓰지 않는 경우가 더 많다. 중요한 것은  이 기법인데 자세한 내용은 생활코딩의 Ajax 수업을 찾아보면 도움을 받을 수 있다.

생활코딩/WEB/WEB2 - JavaScript/ Ajax

https://www.opentutorials.org/course/3281

웹페이지에서 어떤 정보를 변경한다고 하면 주소가 바뀌면서 웹페이지 전체를 새로 다운받게 된다. 페이지를 바꿀 때마다 새로 다운 받고 내용이 바뀌는 것이 일반적인 방법이다. 

Fig 100. 사이트에서 일어난 여러가지 사건들이 99개 이상이 일어나면 아이콘이 나타나며 클릭하면 로딩바가 뜨고 리스트가 출력된다

하지만 Fig 99와 Fig 100과 같이 웹페이지 없는 내용을 서버에 요청하여 그 결과를 표시해주는 것도 가능한데 그것은 Ajax 를 썼기 때문이다.

개발자도구/Network 탭:  웹브라우저가 웹서버랑 통신하는 내역을 보여준다. 우측 상단의 내컨텐츠를 클릭하는 순간 produce_ajax라는 목록이 나오는데, 클릭하는 순간 javascript를 통해 웹브라우저에게 서버랑 통신을 하여 그 결과를 가져오라고 시켰기 때문에 그렇게 되는 것이다.

Fig 101. 내 컨텐츠를 클릭하는 순간 Network 탭에 produce_ajax라는 항목이 보인다

이제 저 항목을 클릭하면 아주 복잡한 내용을 볼 수 있는데, 웹서버가 보내준 정보들이며, 이 정보를 받아 해석한 다음에 javascript를 이용하여 프로그래밍적으로 HTML코드를 생성하여 Fig 99와 같은 내용을 출력하게 되는 것이다.

Fig 102. 항목을 클릭하여 그 내용을 본다

이 과정에서 클릭했을 때 웹페이지가 변경되지 않고 서버와 웹브라우저가 내부적으로 조용히 통신하는 이러한 기법을 Ajax라고 부르는 것이다. 웹이라는 것이 단순히 문서에서 벗어나 위의 동작들[Fig 99~100 등의]을 수행할 수 있도록 결정적인 역할을 한 것이 이러한 Ajax 기술인 것이다. Ajax를 제어할 때 비동기적인 제어를 하게 되는데, 만일 동기적인 제어라고 하면 내컨텐츠를 클릭하였을 때, 서버가 멀리 예를 들어 해외에 있으면 그 서버에서 Fig 102와 같은 정보를 전송해줄 때까지 javascript는 마냥 기다리고 있어야 한다. 그렇게 되면 컨텐츠를 클릭한 순간 웹페이지는 freezing 되어버리고 아무것도 할 수 없는 상태가 되는 것이다. 하지만 비동기 통신을 하기 때문에 내컨텐츠를 클릭한 순간에 서버와 웹브라우저는 내부적으로 통신을 진행하고 그동안 다른 웹페이지를 보거나 내용을 아래로 내리거나 하는 일련의 다른 작업들을 할 수 있게 되는 것이다. 그것이 바로 비동기적인 처리이다.

이런 작업을 하는 기본적인 방법은 javascript를 이용하여 하는 것인데 브라우저마다 사용방법이 다르며 좀 불편하다. jQuery를 이용하면   다소 복잡한 작업을 한 줄의 코드로 작성할 수 있고 그 과정에서 콜백이 사용된다. 

한가지 예를 살펴보도록 하자. 아래의 datasourco.json.js 라는 파일은 JSON이라는 형태의 파일인데, JSON은 javascript의 객체를 만드는 방법으로서, 다음 파일은 "title"은 "JavaScript"의 값을 가지고 "author"라는 key는 "egoing"이라는 값을 가진다. 그리고 웹서버에서 이 내용을 출력해보면 Fig 103-1 과 같다. 

Fig 103. datasource.json.js 의 내용

이 내용을 출력하기 위해 Bitnami WAMP 웹서버를 돌리고 홈페이지폴더안에 똑같이 하위폴더를 구성한 후 위의 datasource.json.js를 저장하였다.

Fig 103-1. 브라우저로 접속해봄

그리고 다음 HTML코드를 살펴보자.

Fig 104. demo1.html
Fig 104-1. demo1.html

4행에서 jquery를 삽입하고 있다. jQuery가 가지고 있는 기능 중에 ajax 통신을 하도록 하는 기능이 있는데 그것이 바로 8행의 $.get() 이다. $는 jQuery가 사용자들에게 제공하는 특수한 객체로서 그 객체가 가지고 있는 메소드 중 get이라는 메소드를 호출하였다. 첫 인자는 json 타입 데이터의 url이고[Fig 103-1 주소창의 http://localhost/javascript/function_value_6508/datasource.json.js 을 그대로 써도 됨], 두번째 인자로는 함수를 전달하도록 약속이 되어있는데, 서버랑 통신이 끝났을 때 호출되도록 약속된[호출되도록 기대하는] 함수이다. $.get함수는 첫 인자의 url에 해당하는 정보를 가져온 다음에 두번째 인자에 지정한 함수를 호출하도록 약속되어있다. 그리고 그 함수에는 인자를 전달해야 하는데, 그 인자 값은 바로 서버에서 가져온[즉 $.get의 첫 인자인 url 의] 내용 [즉 Fig 103-1의 내용] 이다. 4행의 내용에서 jquery 버전을 바꿔도 동작에는 문제가 없으며 jquery-3.4.1.min.js를 jquery-3.4.1.js 로 바꿔도 동작에는 문제가 없다. 아마도 code.jquery.com에 버전별로 min.js와 .js 를 모두 가지고 있는 듯 하다.

결과를 보기 위해 demo1.html을 datasource.json.js 가 있는 폴더에 저장한 후 브라우저에서 개발자모드/Console을 확인한다.

Fig 105. demo1.html 의 결과

9~11행에 정의된 함수의 내용에 따라 원하는 결과를 만들어 낼 수 있다는 뜻이다.  콜백이라는 기법을 통하여 사용자에게 일련의 처리를 위임하고 있는 것이다. 누가 Ajax를 콜하든 간에 서버에서 정보를 가져오는 행위자체는 다 똑같다. 그 똑같은 부분은 바로 $.get이 알아서 처리하는 것이고 정보를 가져온 후 그 정보에 대해 어떤 처리를 할 것인가에 대해서는 서로 다르기 때문에 [그런 것을 비즈니스 로직이라고 함], 그것을 사용자에게 위임을 해야 하는데 그 위임을 하는 기법이 바로 콜백 함수를 통해서 인자를 전달받는 것이다. 그러면 우리는 함수를 인자로, 즉 매개변수의 값으로 전달하는 것을 통하여 $.get이 동작하는 방법을 완전히 바꿔놓을 수 있다. 

이러한 특징은 javascript를 이용하여 고난이도의 테크닉, 또는 유지보수하기 쉬운 코드를 작성하는데 있어서 아주 중요한 열쇠와 같은 역할을 하는 문법적인 특성이다.

ChapterHead

• 클로저

외부함수와 내부함수

클로저(closure)는 내부함수가 외부함수의 맥락(context)에 접근할 수 있는 것을 가르킨다. 클로저는 자바스크립트를 이용한 고난이도의 테크닉을 구사하는데 필수적인 개념으로 활용된다.

규모가 큰 코드를 작성하거나 다른 사람과 협업을 해야 하는데 다른 사람이 쓴 코드가 closure로 작성된 경우가 있을 수 있는데 그런 경우에는 클로저라는 개념이 중요하게 사용된다. 

클로저에 대해 얘기하기 전에 먼저 내부함수와 외부함수에 대해 알아보자.

JavaScript는 함수 안에서 또 다른 함수를 선언할 수 있다. 다음 예제의 결과는 경고창에 coding everybody가 출력된다.

Fig 106. 함수 안에서 정의된 함수

두 번째 행의 함수는 사실 inner라는 변수에 함수를 값으로 넣은 것이다.

var inner = function{ ... }  이렇게 outter라는 변수 안에서 inner라는 변수를 만들고 이 변수에 함수를 할당하고 있는 것. 이런 테크닉을 쓰는 경우는 여러 가지가 있겠지만 그 중에서 가시적으로 지적할 수 있는 것은 어떤 함수가 있는데 그 함수 안에서만 사용되는 함수가 있을 수 있을 때 안에서 사용되는 함수를 그 함수의 바깥쪽에 선언하게 되면 응집성이 떨어지게 되어 함수 안에 선언하여 보기도 편하고 그 안에 무언가 끼어들 가능성도 줄어들 것이다.

다음 예시는 앞의 예시에서 var title의 위치를 변경한 것인데 inner 는 내부함수, outter는 외부함수이고, title은 외부함수에 정의된 지역변수이다.

Fig 107. title은 inner 를 포함한 outter 안에 정의된 지역변수

inner 함수에서 title이라는 변수를 사용할 때 Fig 107처럼 inner 내부에 title이 정의되지 않았다면 자신을 포함하고 있는 외부함수에서 정의되었는지를 먼저 확인하고 있다면 그 변수를 가리키게 되는 것이다. 즉 내부함수에서 외부함수의 지역변수에 접근할 수 있다. 이런 것을 클러저라고 한다.

 

클로저란

클로저(closure)는 내부함수와 밀접한 관계를 가지고 있는 주제다. 내부함수는 외부함수의 지역변수에 접근 할 수 있는데 외부함수의 실행이 끝나서 외부함수가 소멸된 이후에도 내부함수가 외부함수의 변수에 접근 할 수 있다. 이러한 메커니즘을 클로저라고 한다. 아래의 코드는 경고창으로 coding everybody를 출력한다.

Fig 108.

먼저 function outter(){  ...  } 로 외부함수를 정의하고, 그 안에서 반환값으로 function(){ ... } 과 같이 내부함수를 정의하고 있다.   inner = outter(); 에 의해 변수 inner의 결과는 outter()의 반환값인 내부함수 function(){ ... } 이 된다. inner()를 사용하여 inner라는 변수에 담겨 있는 함수를 호출하면 function(){ ... }이 실행되면서 title 값이 경고창에 뜨게 되는 것이다. 여기서 이상한 것은 inner = outter(); 에 의해 outter()는 return 하여 반환값을 돌려주었고 그 의미는 생을 마감했다는 뜻이다. 종료되었다는 뜻이다. outter가 종료되었음에도 불구, inner()를 사용해 inner에 담겨진 함수를 호출한 순간에는 function(){  ...  }의 내용이 실행되는데 여기서는 title이라는 변수를 사용하고  이 변수는 내부함수 즉 그 함수가 아닌 외부함수에 존재한다. 외부함수는 이미 생을 마감했는데 그 외부함수에서 파생된 내부함수에서, 이미 사라진 외부함수에 접근을 시도하였으며 그 접근이 성공적으로 이루어지고 있는 것이다. 마치 무슨 영혼처럼 말이다.

이것이 closure의 아주 독특함이라고 할 수 있는 부분이다. 내부함수를 포함하고 있는 외부함수에 접근할 수 있을 뿐만 아니라 외부함수가 종료된 이후에도 내부함수를 통해서 접근할 수 있다는 것이 closure의 중요한 특징이다.

 

private variable

privage variable은 소프트웨어가 커지는 과정에서 어떤 정보가 있을 때 그 정보를 아무나 수정하지 못하도록 방지하는 것을 말한다. 코드를 통해 살펴볼 건데, 아래 예제는 closure를 이용하여 영화의 제목을 저장하고 있는 객체를 정의하고 있다.

Fig 109.

factory_movie 함수는 반환값으로 객체를 return하고 있는데 이 객체는 get_title, set_title 이라는 key의 value로 각각 모두 함수가 정의되어 있다.  객체를 반환하는데 객체의 값들은 함수란 얘기다. get_title, set_title 이라는 속성에 함수가 정의되어있기 때문에 이 속성들은 메소드인 것이다. 2개의 메소드가 정의되어 있는 객체이다.

위의 클로저에서도 보았듯이 return 값으로 함수를 반환하는 것은 그것을 포함하고 있는 함수의 내부함수라고 했고, 여기서는 return 값이 객체이고 그 객체안의 속성값이 함수라는 점이 차이가 있지만 이것은 엄연하게 내부함수이고 이를 포함하고 있는 fatory_movie(title)은 외부함수다. 이 맥락에서 메소드들 function()과 function(_title)은 factory_movie의 내부함수이다. 다만 그것이 객체 안에 들어있다는 점만 다를 뿐이다. 이것들이 내부함수란 얘기는 클로저에서 보았듯이 이 내부함수를 포함하고 있는 외부함수의 지역변수에 접근할 수 있으며 이 외부함수가 생을 마감한 후에도 접근할 수 있다는 뜻이다.

get_title 의 함수는 title을 return 하는데 이 title 값은 내부함수가 외부함수의 지역변수에 접근이 가능하기 때문에 factory_movie(title)의 title 값이다. get_title의 함수를 호출하면 factory_movie(title)로 전달된 title 값을 리턴한다.

set_title 은 _title을 전달받아 title 다시말해 factory_movie(title)의 title 값을 변경하게 된다.

11, 12행에서 ghost와 matrix는 factory_movie 함수를 호출했고 이 함수의 return 값은 두개의 함수를 속성값으로 하는 객체이다. 얼마나 신기한가? 이때 title의 값은 factory_movie를 호출하면서 전달된다. ghost와 matrix는 모두 2 개의 함수를 속성 값으로 하는 객체를 가지고 있지만 그 두 객체는 당연히 서로 다른 객체이며 그것들이 접근할 수 있는 title의 값 역시 서로 다르다. 이름은 title로서 같지만 서로 다른 변수란 얘기다.

13, 14행은 ghost와 matrix의 속성 뒤에 괄호( )를 붙임으로서 객체안 get_title속성의 함수를 호출하였다. 4행의 코드  즉 title 값이 return 된다. 이때 두 변수 ghost와 matrix 모두 title값을 리턴하지만 이 title은 factory_movie(title)의 title 값인데 전달받을 때 값이 다르기 때문에 return title의 값 역시 다르다.

15행에 의해 '공각기동대'가 ghost 가 가지고 있는 객체의 set_title 속성의 함수를 통해 전달되고 결국 title의 값을 변화시키지만 이때 변화되는 title은 ghost에만 해당되는 것이지 같은 객체구조를 가지고 있는 다른 변수 즉 matrix의 title에는 영향을 주지 못한다. 실행결과는 다음과 같다.

Fig 109. private variables 예시

factory_movie를 통해 11,12행에서 2 개의 객체를 만들었고, 이 2 개의 객체는 각각 자신들이 실행된 그 시점에서의 맥락, 외부함수의 지역변수에 접근할 수 있고, 그 지역변수의 값은 유지가 되기 때문에 set_title을 통해 title값을 바꾼다는 것은 그 객체가 접근할 수 있는 title 값만을 바꿀 뿐, 다른 객체가 접근할 수 있는 title 값에는 아무런 영향을 주지 못한다.

위에서 살펴본 코드의 진짜 효용은 무엇일까? 그것은 바로 private variable 이다. 비밀변수가 가능하다는 것이다. ghost와 matrix에는 객체가 담겨 있고, get_title과 set_title은 언제든지 접근할 수 있는 메소드이다.  오픈되어 있고, 누구나 접근할 수 있는 public이다. 그런데 get_title과 set_title이 내부적으로 사용하고 있는 변수는 외부함수의 지역변수인 title이다. 그런데 외부함수 factory_movie의 지역변수인 title은, 외부함수 factory_movie가 호출되어 return 하고 생을 마감했기 때문에, factory_movie의 내부함수[메소드]인 get_title과 set_title을 통해서만 접근할 수 있는 변수가 되어버리는 것이다.

이런 프라이빗 변수는 규모가 큰 프로그램을 만들 때라야만 그 쓸모를 체감할 수 있는데, 소프트웨어가 커지다 보면 많은 사람이 참여하게 되고 코드를 만들게 된다. 그 안에는 미래의 자기자신, 과거의 자기자신도 포함되어 있다. 과거에 내가 만든 코드가 있지만 시간이 지나면 기억이 가물가물하게 되고 오늘 내가 만든 코드도 시간이 흐르면 한 순간에 이해하기 어려운 것이다.

그런 경우에 많은 코드가 소프트웨어 안에 존재하게 되는데, 그 데이터가 누구나 수정할 수 있는 어떠한 방식으로든 고칠 수 있는 형태의 데이터가 된다는 것은 결국에 가서 그 소프트웨어가 망가질 가능성이 크다는 얘기다. 전역변수, 지역변수도 그런 맥락에서 필요한 개념인 것이다. 이 private variable 역시 마찬가지이다. 

위의 코드에서 title이라는 정보에 접근하기 위해 get_title, set_title이라는 메소드를 통해야만 한다면 일단 title이라는 변수를 아무나 수정할 수 없기 때문에 같은 이름의 어떤 변수를 어떤 의미로 사용하든 이 title의 맥락에는 영향을 줄 수 없다. 훨씬 안전하다라는 얘기이다.

다음 set_title 메소드를 이용하여 title의 값을 바꾸는 부분의 코드를 문자열이 아니면 경고창을 보내는 식으로 아래와 같이 코드를 바꿔보고 실행시켜보자. 19행에서 인자 값을 수값으로 주었다.

Fig 110. set_title 의 조건을 넣어줄 수 있다
Fig 110-1.  Fig 110의 결과. 문자열이 아니라는 경고창이 뜬다

즉 우리가 title이라는 변수의 값을 private하게 비밀변수로 꽁꽁 묶어놓고, 아주 깊은 곳에 숨겨 놓고 그 변수의 값을 수정할 때에는 set_title을 통해서만 가능하고 그 값을 가져올 때에는 get_title을 통해서만 가져오도록 하면 데이터가 훨씬 더 안전하게 저장되고 안전하게 수정될 수 있다는 것이다. 클로저는 javascript가 private한 변수를 사용할 수 있도록 하는 아주 좋은 메커니즘이라고 할 수 있다.  

참고 Private 속성은 객체의 외부에서는 접근 할 수 없는 외부에 감춰진 속성이나 메소드를 의미한다. 이를 통해서 객체의 내부에서만 사용해야 하는 값이 노출됨으로서 생길 수 있는 오류를 줄일 수 있다. 자바와 같은 언어에서는 이러한 특성을 언어 문법 차원에서 지원하고 있다.

클로저의 응용

클로저를 사용하면서 범하기 쉬운 실수 하나를 통해 클로저에 대해 좀 더 살펴보자

이 예제는 자주 일어나는 실수이기 때문에 많이 언급된다.

Fig 111

arr[i] 에는 함수가 들어간다. 즉 arr[0] ~ arr [4] 에는 function(){return 0} ~ function(){return 4} 가 아닌 function(){return i} 가 저장된다.

그리고 for 문을 지나게 되면 i 는 5가 되어있고 arr[0]( )~arr[4]( ) 함수를 호출하면 이때 i 값이 5 이기 때문에 return i에 의해 5가 출력되는 것이다. 그래서 5가 다섯번 출력된다.

https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures mdn 문서를 참조하면 이해하기가 훨씬 쉬워진다.

이제 arr[i]에 대입되는 function()을 내부함수로 하는 외부함수를 정의하고 그 외부함수의 지역변수의 값을 내부함수가 참조하도록 해보자.

Fig 112.

이렇게 정의해줌과 동시에 인자 i를 넣고 바로 호출하면 매번 i 값이 id 를 통해 내부함수의 return id 로 전달되어 arr[i] 에는 함수가 아니라 function(id){  }(i) 의 실행결과가 들어가게 된다. 그 실행결과는 결국 내부 함수에 의해 retrun id 가 되며 이때 id 값은 매번 for 순환의 i 값을 전달받게 되는 것이다.

클로저 참고

 

ChapterHead

• arguments

arguments란?

arguments 라고 하는 객체에 대해 알아본다. arguments 객체는 함수 안에서 함수의 여러 가지 정보들을 담고 있는, 특히 인자와 관련된 정보들을 담고 있는 객체인데 배열과 사용방법이 아주 유사하기 때문에 유사 배열이라고도 함. 배열과 비슷하지만 배열은 아니다. 

Fig 113. sum( ) 함수는 인자이 없지만 호출 시에 인자가 있으면 arguments[]로 전달 받는다

 

function length

매개변수와 관련된 두가지 수가 있다. 하나는 함수.length, 다른 하나는 arguments.length이다. arguments.length는 함수로 전달된 실제 인자의 수를 의미하고, 함수.length는 함수에 정의된 인자의 수를 의미한다. 아래의 코드를 보자.

Fig 114. 함수에 정의된 인자의 수와 실제로 전달된 인자의 수

 

ChapterHead

• 함수의 호출

apply 소개

난이도가 있는 테크닉으로 가면 자주 사용하는 방법에 대해서도 좀 보자.

기본적인 정의•호출 방법: Fig 115 1~2행은 정의 3행은 호출

Fig 115

자바스크립트에서 함수는 일종의 객체이다. 객체는 속성을 가지고 있고 그 속성에 값이 저장되어 있으면 그냥 속성 property 이라고 부르지만 그 속성에 함수가 들어있다면, 그것을 메소드라고 부른다.  Fig 115의 함수 func는 객체이며 메소드를 가지고 있는데 이 메소드는 javascript가 기본적으로 제공하는 내장된 메소드, 내장된 객체이다.  func는 Function이라는 객체의 인스턴스다. 따라서 func는 객체 Function이 가지고 있는 메소드들을 상속하고 있다. func 라는 함수를 정의하면 우리는 func.apply, func.call 이라는 메소드에 접근할 수 있다. 이것들이 하는 역할은 정의된 func를 호출하는 역할이다. 지금 이야기하려는 메소드는 Function.apply과 Function.call이다. 아래와 같이 sum이라는 함수를 정의하면 기본적으로 sum 을 호출하는 방법은 sum(2, 3);  처럼 하면 되는데, 아래 그림의 마지막 행처럼 sum.apply 를 써서 호출할 수도 있다.  결과는 3이다.

Fig 116

 

Fig 117. 함수뒤에 점을 찍으면 추천어가 뜨는데 함수에서 쓸 수 있는 속성과 메소드들이다. apply와 call도 보인다
Fig 118

Fig 118에서 와 같이 Console을 통해 함수가 쓸 수 있는 내장메소드들을 볼 수 있는데 [native code] 는 브라우저가 가지고 있는 내장코드를 의미하고 있다. 

함수 sum은 Function 객체의 인스턴스다. 그렇기 때문에 객체 Function 의 메소드 apply를 호출 할 수 있다. apply 메소드는 두개의 인자를 가질 수 있는데, 첫번째 인자는 함수(sum)가 실행될 맥락이다. 맥락의 의미는 다음 예제를 통해서 살펴보자. 두번째 인자는 배열인데, 이 배열의 담겨있는 원소가 함수(sum)의 인자로 순차적으로 대입된다. Function.call은 사용법이 거의 비슷하다 여기서는 언급하지 않는다. 

sum(1, 2);  를 sum.apply(null, [1, 2]); 라고 해주어도 결과는 같다. 이때 인자의 위치를 잘 확인해보면, 기본호출방법과는 달리 sum.apply에서는 배열 안에 인자를 넣어주고 있다.  

 

apply 의 사용

Fig 119

위의 코드에서 함수 sum 안의 this는 정의되는 시점에서는 어느 객체인지 정해져 있지 않다.  sum 함수를 호출할 때 sum( ) 을 사용하지 않고 sum.apply( ) 를 사용하고 있는데 Fig 116과 달리 첫 인자를 null로 하지 않고 객체를 지정하고 있다. 그러면 이때 인자로 전달하는 객체 o1 혹은 o2이 바로 sum 함수 안에서 this 가 되는 것이다. 

이것은 마치도 sum.apply(o1) 이 o1.sum처럼 작동한다는 것이다. 즉 o1에 대해 sum을 적용하는 것처럼 동작한다. 이해를 돕기 위해 코드를 좀 바꿔보자.

Fig 120

이 코드는 o1 객체의 sum 속성 뒤에 ( )를 붙여 함수를 호출하고 있으며, 이 함수는 자신의 모든 요소들을 더하고 있는데 3개 요소를 더한 후 마지막 sum 속성의 값도 더한다. 즉 함수 정의문을 하나의 문자열로 하여 연결한 결과를 보여주는 것이다.

Fig 120의 결과

Fig 121. 객체의 요소 유형 type이 함수가 아닐 때만 더한다

Fig 121과 같이 객체의 타입을 비교하는 코드를 삽입하면 결과는 Fig 119와 같아진다.

이렇게 복잡한 코드를 보다 보기 좋고 단순하게 할 수 있는 방법이 바로 Fig 119인 것이다. 그러면 sum이라는 함수가 마치도 o1이나 o2의 메소드인 것처럼 사용할 수 있는 것이다. 이것이 apply 를 써서 해결할 수 있는 방법 중 하나다.

 

ChapterHead

TOP

4. 객체지향

- 객체지향 프로그래밍
- 생성자와 new [constructor]
- 전역객체
- this
- 상속
- prototype
- 표준 내장 객체의 확장
- Object
- 데이터 타입
- 참조

객체지향 프로그래밍은 크고 견고한 프로그램을 만들기 위한 노력의 산물이다. 객체지향이라는 큰 흐름은 현대적 프로그래밍 언어들을 지배하고 있는 가장 중요한 맥락이라고 할 수 있다. 하지만 자바스크립트의 객체지향은 다른 언어들의 객체지향과 사뭇 다르다. 특히 Java나 C++과 같은 주류 객체지향 언어에 익숙한 독자라면 극심한 혼란을 경험할 수도 있다.

최소한 주류가 된 언어라면 그 언어가 추구한 나름대로의 지향점이 있을 것이다. 그 지향점에 대해서 이해하고 언어를 대한다면 훨씬 더 즐겁게 언어를 음미할 수 있을 것이다. 특히 모든 처리의 중심에 함수를 두는 자바스크립트를 공부하다 보면 객체지향을 이렇게도 추구 할수도 있다는 것을 알게 될 것이다.

• 객체지향 프로그래밍

오리엔테이션

객체지향 프로그래밍(Object-Oriented Programming : OOP)은 좀 더 나은 프로그램을 만들기 위한 프로그래밍 패러다임으로 로직을 상태(state)와 행위(behave)로 이루어진 객체로 만드는 것이다. 이 객체들을 마치 레고 블럭처럼 조립해서 하나의 프로그램을 만드는 것이 객체지향 프로그래밍이라고 할 수 있다. 다시 말해서 객체지향 프로그래밍은 객체를 만드는 것이다. 따라서 객체지향 프로그래밍의 시작은 객체란 무엇인가를 이해하는 것이라고 할 수 있다. 말이 어렵게 느껴지지 않는가? 그것은 아직 객체 지향에 대한 체험이 없기 때문이다. 본 수업에서는 객체 지향에 대해서 코드 없이 이야기 할 수 있는 것들에 대해서만 이야기 할 생각이다. 객체 지향에 대한 오리엔테이션이라고 생각하고 가벼운 마음으로 나머지 내용을 읽어보자.

객체지향 프로그래밍을 학습하는데 장애 중의 하나는 번역이다. Object를 번역한 객체는 현실에서는 거의 쓰지 않는 말이고, 머랄까 철학적인 느낌을 자아낸다. 그래서 객체지향 프로그래밍을 처음 접하는 입문자들은 객체지향 프로그래밍을 철학적인 탐구의 대상으로 파악하는 경향을 보이는데, 필자의 생각에 이것은 공부를 어렵게 할 뿐 도움이 되지 않는다. 쉽게 생각하자. 객체는 변수와 메소드를 그룹핑한 것이다.

오픈튜토리얼의 구성은 대체로 이렇다.

Fig 122

이런 사이트의 경우 일반적으로 알고 있는 소프트웨어와는 좀 다르지만 이런 웹서비스들도 애플리케이션이라고 부르고 이것도 분명히 소프트웨어이다. 아주 많은 경우에 이런 사이트를 구축하기 위해 JAVA를 사용한다. 

이 사이트에는 글목록을 출력하는 로직, 본문을 출력하는 로직, 댓글을 출력수정추가•삭제하는 로직이 있을 것이다. 즉 하나의 프로그램은  하나의 취지를 가진 로직으로 이루어지는 것이 아니라 여러 개의 목적성을 갖고 있는 로직들의 집합으로 볼 수 있다.

처음 서비스를 만들고 애플리케이션을 만들 때에는 코드가 많지 않다. 위에서 본 절차적프로그래밍 기법으로 충분히 사이트를 구축할 수 있다. 그런데 사이트의 규모가 커지고 사이트를 구축하기 위해 동원되는 인원들이 많아지고 오랜 시간동안 프로젝트가 진행되는 과정에서 여러 가지 어려움들을 겪게 된다. 전문용어로는 막장이라고 한다고.

그러면 이 사이트에 들어가는 로직들을 기능 별로 그루핑하려는 생각이 자연스럽게 솟아나게 된다. 

그럼 어떻게 할까? 이 사이트를 구성하고 있는 글목록, 본문, 댓글과 같은 기능적으로 로직들을 구분하여 글목록 관련 로직이라고 하면 변수이름 앞에 list_xxx, 본문 관련 변수•메소드라면 article_xxx 와 같이 명명한다거나, 글목록 관련 변수나 메소드 또는 본문 관련 변수 메소드 들을 각각의 기능별로 쪼개서 코드상으로 서로 연관되어 있는 코드끼리 모아놓는 다거나 하는 여러 가지 조치들을 취하게 되는데 그것이 일종의 그루핑, 분류화, categorizing 등이다. 이렇게 소프트웨어가 커지면서 발생하는 여러가지 문제들[소위 막장], 이런 막장으로 해 절망감 속에서 허덕이다 보면 자연스럽게 그것을 해소할 수 있는 방법들에 대해 모색하게 된다. 

그러한 모색으로부터 나온 결과 중 하나가 객체지향이라고 볼 수 있다. 계속해서 Fig 122를 예를 들어 얘기를 하는 것이지만, 본문, 댓글, 글목록 과 관련되어 있는 로직들[변수, 메소드]을 서로 연관되어 있는 기능 별로 그루핑하는 기능을 프로그래밍차원에서, 언어차원에서 제공하게 되는 것이다. 그렇게 하여 그루핑된 하나하나의 단위들을 객체라고 부른다.

본문이 있으면 이 객체 안에는 그와 관련된 변수와 메소드들이 응집되어 있고, 또 댓글과 관련되어 있는 변수와 메소드들[추가•삭제•변경•수정 등]이 아래 그림과 같이 객체 단위로 응집되어 있는 것이다.

Fig 123. 기능에 따른 로직 별로 객체를 이루고 관련된 변수와 메소드들을 가지고 있다

동시에 서로 연관성이 없는 다른 성격을 가진 로직들과는 객체를 경계로 분리되어 있는 것이다. 서로 연결되어 있는 것은 객체를 껍데기로 응집되어 있고 연관성이 없는 것들은 객체라는 껍데기를 중심으로 하여 구분되는 것이 객체의 가장 중요한 기능이다. 

이렇게 객체를 분류하여 소프트웨어를 좀 더 명확하게 이해하고 쉽게 관리할 수 있도록 기능 별로 로직들을 구획화시키고 분류하고 categorizing 하면 자연스럽게 객체라고 하는 문법적인 기능이 나타나게 되는 것이고 그러한 기능은 어디에 알맞고 또는 다른 사이트를 구축할 때도 쓸 수 있다라는 생각을 하게 되는 것이다. 그러면 이 댓글이라는 객체를 다른 사이트 혹은 다른 애플리케이션에서도  가져다 쓸 수 있게 되는 것이다. 이러한 것이 프로그래밍에서 가장 중요한 부분의 하나인 재활용성에 객체가 기여하는 부분이다. 이미 사용하고 있는 것들을 다른 곳에서도 사용하려고 하게 되고 실제로 그렇게 하려면 또 다른 문제들을 발생시키게 되는데, 그러한 문제들을 완화시키기 위해 로직을 제한하고 컨트롤하는 여러 가지 기능들이 추가되게 되는데 이제 마주하게 될 객체지향의 여러 가지 기능들이 그러한 맥락에서 나타나게 되었을 것이다. 객체지향프로그래밍을 객체를 만드는 것이고 객체는 추상적으로 생각할 대상이 아니며 언어차원에서 매우 구체적인 문법적인 기능이 제공된다. 하나의 객체 안에는 그 객체가 가지고 있는 취지 [예를 들어 본문인지 댓글인지] 와 연관되어 있는 변수와 메소드들이 들어 있고 그리고 연관성이 없는 다른 로직과 구분해주는 역할을 하는 것이 객체라고 보면 된다. 

 

추상화

문법과 설계

객체지향 프로그래밍 교육은 크게 두 가지로 구분된다.

문법

하나는 객체지향을 편하게 할 수 있도록 언어가 제공하는 기능을 익히는 것이다. 이러한 기능들은 if, for문처럼 문법적인 구성을 가지고 있다. 이 문법을 이해하고, 숙지해야 객체를 만들 수 있다. 객체를 만드는 법에 대한 학습이라고 할 수 있다. 우리 수업은 여기에 초점이 맞춰져 있다.

설계

두 번째는 좋은 객체를 만드는 법이다. 

객체지향을 배우고자 한다면 일단은 문법을 먼저 정복하는 수밖에 없다. 객체지향프로그래밍을 하기 위해서는 어떤 코드를 어떻게 작성해야 하고 그것이 어떤 기능을 가지고 있는가 하는 것들을 익히는 것이 기본적인 것이고 어떤 측면에서 가장 쉬운 일이라고 할 수 있다. 정말로 어려운 것은 객체지향을 이용하여 현실을 잘 담아낼 수 있는 소프트웨어를 개발하는 것이다. 이것을 다른 말로는 설계를 잘하는 법이라고 할 수 있다. 설계는 현실에서 우리가 관심이 있는 어떤 특성 혹은 관점을 소프트웨어화 시켜서 문제를 해결하는 것을 프로그래밍, 프로그램이라고 할 수 있다. 문제는 이 현실이 아주 복잡하다는 것이다. 소프트웨어는 현실보다 훨씬 단순하다. 소프트웨어도 현실 안에 포함되기 때문에 당연히 현실이 훨씬 더 복잡한 것이다. 좋은 설계는 현실을 잘 반영해야 한다. 현실은 복잡하다. 하지만 그 복잡함 전체가 필요한 것은 아니다. 아래의 그림을 보자.

Fig 124 런던의 위성사진 지도, 전철 노선

위의 그림은 런던의 지도다. 여러분이 지하철을 이용한다면 어떤 지도를 선호할까? 오른쪽 하단의 지도를 선호할 것이다. 왼쪽 상단의 지도는 현실의 복잡함을 나타낸다. 오른쪽 하단의 지도는 지하철 탑승자의 관심사만을 반영하고 있다. 역 간의 거리나 실제 위치와 같은 요소들은 모두 배제하고 있다. 복잡함 속에서 필요한 관점만을 추출하는 행위를 추상화라고 한다.

지하철 노선도가 디자인의 추상화라고 한다면 프로그램을 만든다는 것은 소프트웨어의 추상화 abstract 라고 할 수 있다. 객체 지향 프로그래밍은 좀 더 현실을 잘 반영하기 위한 노력의 산물이다. 이것은 단순히 객체 지향의 문법을 이용해서 객체를 만든다고 달성되는 것이 아니다. 고도의 추상화 능력이 필요하다. 좋은 설계는 문법을 배우는 것보다 훨씬 어려운 일이다. 심지어 이것은 지식을 넘어서 지혜의 영역이다. 좋은 설계를 위한 조언들은 많지만 이러한 조언들은 조언자의 입을 떠나는 순간 생명력을 잃어버린다. 지식은 전수되지만 지혜는 전수되지 않기 때문이다. 스스로 경험하고 깨우쳐서 자기화시켜야 한다. 필자도 그 긴 여정을 따라가고 있는 견습생에 불과하다.

객체지향의 설계 원칙이나 객체 지향의 철학적인 의미는 대단히 중요하다. 하지만 이러한 것들을 지금 언급한다면 여러분은 미궁 속에 빠지게 될 것이다. 그래서 필자가 제안하는 것은 일단은 지식부터 익히자는 것이다. 언어가 지원하는 객체지향 문법을 배우고, 이것들이 어떻게 동작하는지를 충분히 이해한 다음에 비로소 설계 원칙도 이야기할 수 있고, 객체와 사물의 비유도 시도해 볼 수 있을 것이다. 여기서는 몇 가지 객체지향이 추구하는 지향점을 가볍게 이야기하고 다음 토픽부터 구체적인 문법을 알아볼 것이다.

 

부품화

프로그래밍은 정신적인 활동이다. 정신적인 것은 실체가 없고, 무한하고, 유연하다. 이러한 특성은 정신이 가진 장점이면서  소프트웨어의 극치다. 하지만 정신의 이러한 특성은 때로 오해나 모순 같은 문제점을 유발한다. 소프트웨어도 이러한 문제점을 그대로 상속받는다. 이러한 문제점을 극복하기 위한 노력 중의 하나가 부품화라고 할 수 있다. 객체 지향과 부품화를 동일시 할 수는 없지만 부품화라고 하는 소프트웨어의 큰 흐름은 객체 지향이 만들어지는데 지대한 공헌을 했다고 할 수 있다. 하드웨어에서 이루어지는 부품화의 예를 보자.

아래의 컴퓨터는 초창기의 컴퓨터다.

 

Fig 126. 초창기 컴퓨터

본체와 모니터와 키보드가 하나로 단일화되어 있다. 이것의 문제점은 분명하다. 모니터가 고장 나면 컴퓨터를 바꿔야 한다. 키보드가 고장 나도 컴퓨터를 교체해야 한다.

Fig 127. 부품화된 컴퓨터

그래서 위와 같이 모니터와 본체와 컴퓨터를 분리했다. 다시 말해서 부품화 시킨 것이다. 기능들을 부품화 시킨 덕분에 소비자들은 더 좋은 키보드나 저렴한 모니터를 선택할 수 있게 되었다. 또 문제가 생겼을 때 그 문제가 어디에서 발생한 것인지 파악하고 해결하기가 훨씬 쉬워진다.

위의 그림에서 모니터와 키보드 그리고 본체를 분리하는 기준은 무엇일까? 그 기준을 세우는 것이 추상화일 것이다. 위 제품의 기획자는 컴퓨터를 입력과 출력 그리고 연산 & 저장으로 분류하고 있다. 이 분류에 따라서 부품들을 모으고 분리해서 모니터, 키보드, 본체와 마우스라는 개별적인 완제품을 만들고 있다. 이 완제품들을 부품으로 조합하면 컴퓨터라는 하나의 완제품이 만들어진다.

물론 정해진 답이 있는 것은 아니다. 아래 컴퓨터는 저장 장치를 부품화시키고 있다.

Fig 128. 저장장치가 부품화된 컴퓨터

또 아래 장치는 현시점에서 최신 데스크탑이다. 그런데 부품화를 제거하고 있다. 기술이 경량화되면서 컴퓨터는 더욱 작아지게 되었고, 그 결과 컴퓨터를 부품화하는 것의 매력이 반감되고 있기 때문이 아닐까? 부품화가 중요한 것임에는 분명하지만 그 보다 중요한 것은 적절함이다. 그래서 설계가 어려운 것이다.

Fig 129. 최신 일체화된 데스크탑 컴퓨터

객체 지향은 부품화의 정점이라고 할 수 있다. 하지만 우리는 아직 객체 지향을 배우지 않았다. 그래서 우리가 배운 것 중에서 부품화의 특성을 보여줄 수 있는 기능을 생각해보면 좋을 것 같다. 메소드는 부품화의 예라고 할 수 있다. 메소드를 사용하는 기본 취지는 연관되어 있는 로직들을 결합해서 메소드라는 완제품을 만드는 것이다. 그리고 이 메소드들을 부품으로 해서 하나의 완제품인 독립된 프로그램을 만드는 것이다. 메소드를 사용하면 코드의 양을 극적으로 줄일 수 있고, 메소드 별로 기능이 분류되어 있기 때문에 필요한 코드를 찾기도 쉽고 문제의 진단도 빨라진다.

그런데 프로그램이 커지면 엄청나게 많은 메소드들이 생겨나게 된다. 메소드와 변수를 관리하는 것은 점점 어려운 일이 되기 시작한다. 급기야는 메소드가 없을 때와 같은 상황에 봉착하게 된다. 메소드는 프로그래밍의 역사에서 중요한 도약이었지만, 이 도약이 성숙하면서 새로운 도약지점이 보이기 시작한 것이다.

그 도약 중의 하나가 객체 지향 프로그래밍이다. 이것의 핵심은 연관된 메소드와 그 메소드가 사용하는 변수들을 분류하고 그룹핑하는 것이다. 바로 그렇게 그룹핑 한 대상이 객체(Object)다. 비유하자면 파일과 디렉토리가 있을 때 메소드나 변수가 파일이라면 이 파일을 그룹핑하는 디렉토리가 객체라고 할 수 있다. 이를 통해서 더 큰 단위의 부품을 만들 수 있게 되었다. 객체를 만드는 법에 대해서 호기심이 생기지 않는가? 이런 호기심을 유발시키는 것이 이번 토픽의 목적이다. 객체를 만드는 법은 다음 토픽에서 알아보고 지금은 부품화에 대해서 조금 더 생각해보자.

은닉화, 캡슐화

그런데 부품화라고 하는 목표는 단순히 동일한 기능을 하는 메소드와 변수를 그룹핑한다고 달성되는 것은 아니다. 제대로된 부품이라면 그것이 어떻게 만들어졌는지 모르는 사람도 그 부품을 사용하는 방법만 알면 쓸 수 있어야 한다. 이를테면 모니터가 어떻게 동작하는지 몰라도 컴퓨터와 모니터를 연결하는 방법만 알면 화면을 표시 할 수 있는 것과 같은 이치다. 즉 내부의 동작 방법을 단단한 케이스 안으로 숨기고 사용자에게는 그 부품의 사용방법만을 노출하고 있는 것이다. 이러한 컨셉을 정보의 은닉화(Information Hiding), 또는 캡슐화(Encapsulation)라고 부른다. 자연스럽게 사용자에게는 그 부품을 사용하는 방법이 중요한 것이 된다.  

인터페이스

잘 만들어진 부품이라면 부품과 부품을 서로 교환 할 수 있어야 한다. 예를들어보자. 집에 있는 컴퓨터에 A사의 모니터를 연결하다가 B사의 모니터를 연결 할 수 있다. 또 집에 있던 모니터에 A사의 컴퓨터를 연결해서 사용하다가 새로운 컴퓨터를 구입하면서 B사의 컴퓨터를 연결 할 수 있다. 모니터와 컴퓨터는 서로가 교환관계에 있는 것이다. 이것은 모니터와 컴퓨터를 연결하는 케이블의 규격이 표준화 되어 있기 때문에 가능한 일이다. 아래의 그림을 보자. 모니터와 컴퓨터를 연결하는 케이블인 HDMI를 보여준다.

 

Fig 130. HDMI Connector Pins

컴퓨터와 모니터를 만드는 업체들은 위와 같은 케이블의 규격을 공유한다. 모니터 입장에서는 컴퓨터가, 컴퓨터 입장에서는 모니터가 어떤 식으로 만들어졌는지는 신경쓰지 않는다. 각각의 부품은 미리 정해진 약속에 따라서 신호를 입, 출력하고, 연결점의 모양을 표준에 맞게 만들면 된다. 이러한 연결점을 인터페이스(interface)라고 한다. 위의 그림을 보면 HDMI 케이블의 연결점은 특유의 생김새가 있다. 만약 HDMI 케이블을 랜선을 연결하는 구멍에 연결하려고 한다면 어떻게 될까? 동작하지 않을 뿐 아니라 연결 자체가 되지 않는다. 인터페이스란 이질적인 것들이 결합하는 것을 막아주는 역할도 하는 것이다. 즉 인터페이스는 부품들 간의 약속이다. 이러한 약속을 프로그래밍적으로는 어떻게 구현하는가도 살펴본다.

지금까지 객체를 부품으로 비유해서 설명 했다. 그런데 비유는 비유일 뿐이다. 비유는 의도한 유사점 뿐만 아니라 의도하지 않은 차이점까지도 전달될 가능성이 있기 때문이다. 비유의 함정이라고 할 수 있다. 소프트웨어는 하드웨어가 아니다. 하드웨어가 할 수 없는 것을 소프트웨어는 할 수 있다. 그 중의 하나가 복제와 상속이다. 이러한 개념을 구체적인 문법 없이 설명하는 것은 효용이 크지 않을 뿐만 아니라 자칫 흥미를 저해할 위험이 있기 때문에 여기서는 설명하지 않았다. 소프트웨어가 있기 이전부터 하드웨어가 이룩한 성취를 잘 수용하면서 동시에 소프트웨어 다운 소프트웨어를 만드는 것은 우리게게 주어진 숙제라고 할 수 있다.

 

Designed by   Mourad Mokrane

Designed by Mourad Mokrane

 

ChapterHead

• 생성자와 new

소개

객체지향에 대해 위에서 언급한 부분은 java, php 등 모든 언어에 있어 공통적인 부분이라고 할 수 있다. 여기서부터는 javascript가 어떻게 객체지향이라는 프로그래밍 패러다임에 접근하고 있는지를 살펴본다. javascript는 다른 객체지향언어와는 차이가 있다. 자기만의 아주 독특한 성격을 가지고 있고 이런 계열의 언어를 prototype-based programming 이라고 한다. 프로토타입기반 프로그래밍이라는 카테고리에 속해있다. 전통적인 함수형 언어의 특성을 그대로 가지고 있는 것이 아니고 객체지향언어가 가지고 있는 문법을 비슷하게 사용하면서 사실은 함수형 언어의 특성을 가지고 있기 때문에 매우 혼란스러울수 있다. 그래서 javascript는 알면 알수록 미궁에 빠지는 느낌이 드는 언어이기도 하다.

javascript가 추구하는 객체지향 프로그래밍 스타일은 대단히 자유롭고 유연하다. java나 c++과 같은 언어들이 추구하는 객체지향이 규제를 통해 버그의 가능성을 최소화시키고 거대한 소프트웨어를 만들기 위한 다양한 매니지먼트 도구들을 엄격하고 깐깐하게 도입한 언어들이라고 한다면, javascript는 마치 예술과 같은 느낌이지만 거기에 익숙해지기 전까지는 대단히 혼란스럽고 뭔가 엉성한 느낌이 들 수도 있다. 하지만 javascript는 그것이 추구하는 것이 있고 그로 인해 오늘날 가장 많은 사람들이 사용하는 언어가 될 수 있었다. 

객체지향프로그램을 간단히 말하면 서로 연관되어있는 변수와 메소드를 하나의 객체라고 하는 그릇에 넣는 것이다. 그리고 서로 연관되어 있지 않은 것들은 별도의 객체에 담아 분리를 시키는 것이 [Fig 123] 객체지향프로그램의 기본적인 형태라고 할 수 있다. 연관되어 있는 것들을 그룹핑, categorizing 하는 것이 마치도 연관된 파일들을 그룹핑하는 것과 같은데, 그러한 것들을 제공해주는 문법적인 체계가 객체란 것이고 그 객체를 이용하여 여러 가지 연관되어 있는 로직들을 객체화시키게 되면 각각의 로직들은 하나하나가 일종의 독립된 프로그램처럼 독립성을 갖게 된다. 즉 여러 완제품의 부품으로 사용할 수 있다는 것이다. 객체지향을 통해 도달코자 하는 목적은 바로 좋은 부품의 로직을 만드는 것이며 쉬운 일은 아니다.

 

객체생성

Fig 131. Object 를 만들고 점을 찍으면 그 안의 속성들을 볼 수 있다

Fig 131의 1행에서 { } 은 Object 라는 이름의 비어있는 객체를 만든다. 이 Object는 데이터를 담을 수 있는 비어있는 상자라고 보면 된다.  객체를 가리키는 변수뒤에 점을 찍고 속성을 써주면 그 속성값을 변경 또는 꺼낼 수 있다.

person.name 처럼 말이다. person은 비어있는 Object 를 가리키고 name은 Obect의 속성 property 중 하나다.

3행에서 person의 introduce 속성에는 함수를 넣었으며 이 함수를 메소드라고 부른다.

6행: introduce 뒤에 ( ) 를 붙여 정의한 함수를 호출하는데 함수 안의 this 는 이 함수가 속해있는 Object 즉 person 이라는 변수가 담고 있는 객체를 가리키는 것이다.

이 코드는 객체를 만들고 있는데 [3행], 객체 안에 들어가는  property [2행] 와 method [3행] 가 객체를 정의하는 3행과 분리가 되어 있다. 때문에 중간에 다른 코드가 끼어드는 것과 같은 일련의 행위들에 의해 객체를 정의함에 있어서 집중도가 떨어질 수 있다. 즉 객체를 만드는 과정이 분산되어 있다. 아래의 코드는 그런 문제를 해결할 수 있다. 

Fig 132

위에서처럼 객체를 만드는 2가지 방법 중 어느 것이 좋다 나쁘다는 건 아니다.

Fig 133. 같은 구조를 가지는 객체를 이용해 여러 객체를 만든다고 하자

Fig 133 과 같이 같은 구조의 Object를 서로 다른 변수를 써서 여러 개 만든다고 하면 introduce 메소드는 완전히 중복되는 것을 알 수 있으며 만일 이 메소드의 내용을 수정해야 한다면 객체의 수가 많을 경우 엄청난 문제가 될 수 있다. 이러한 중복은 코드의 가독성, 코드의 량, 유지보수의 측면에서 매우 부정적인 현상이다.  이것을 해결하는 방법이 생성자와 new이다.

 

생성자와 new

javascript는 함수형 언어이고 javascript를 이해하는 가장 중요한 키워드는 객체이기 전에 함수이다. 

생성자(constructor)는 객체를 만드는 역할을 하는 함수다. 자바스크립트에서 함수는 단순히 재사용 가능한 로직의 묶음이 아니라 객체를 만드는 창조자라고도 할 수 있다.

Fig 134. new

Fig 134 에서 Person이라는 함수를 정의하고 p0에 넣은 후 p0의 값을 보면 undefined 이지만 new Person() 을 p에 넣고 p의 값을 보면 Person{ } 인 것을 볼 수 있다. { } 은 비어 있는 객체라는 뜻이다. Person( ) 앞에 new 가 붙어있으면 Person( )는 함수라고 부르는 것이 아니라 생성자라고 부른다. 객체의 생성자인 것이다. p에는 비어있는 Person{ } 객체가 만들어지는 것이다. 함수가 객체의 창조자로서의 역할을 하는 것이다. 

자바의 경우는 클래스 안에 생성자가 존재하고 [즉 생성자는 클래스에 소속되어 있다] 생성자를 호출하는 것을 통해 클래스의 인스턴스, 즉 클래스의 객체를 만들어 낸다. javascript는 생성자가 어디에 소속되어 있지 않다. 생성자는 그냥 함수일 뿐이다. 함수 앞에 new를 붙여 실행하면 객체를 만들어 내는 것이다. javascript에는 클래스라는 존재가 없다.  

Fig 135

함수를 호출할 때 new을 붙이면 새로운 객체를 만든 후에 이를 리턴한다. 위의 코드는 새로운 객체를 변수 p에 담았다. 여러사람을 위한 객체를 만든다면 아래와 같이 코드를 작성해야 할 것이다. 

Fig 135-1

별로 개선된 것이 없다. 

Fig 135-2

생성자 내에서 이 객체의 프로퍼티를 정의하고 있다. 이러한 작업을 초기화 init, initialize 라고 한다. 이를 통해서 코드의 재사용성이 대폭 높아졌다. 7,10행의 Person은 함수가 아닌 생성자이다.

코드를 통해서 알 수 있듯이 생성자 함수는 일반함수와 구분하기 위해서 첫글자를 대문자로 표시한다.

 

ChapterHead

• 전역객체

전역객체(Global object)는 특수한 객체다. 모든 객체는 이 전역객체의 프로퍼티다. 

Fig 136

func();와 window.func();는 모두 실행이 된다. 모든 전역변수와 함수는 사실 window 객체의 프로퍼티다. 객체를 명시하지 않으면 암시적으로 window의 프로퍼티로 간주된다. 

Fig 136-1

자바스크립트에서 모든 객체는 기본적으로 전역객체의 프로퍼티임을 알 수 있다.

전역객체 API

생활코딩의 자바스크립트사전 을 보면 Global 이라는 객체가 있으며 Global 안에 정의되어 있는 API 들이 전역객체가 가지고 있는 API 이고 전역변수 또는 전역 함수를 정의하게 되면 그 함수와 변수는 사실상 Global 객체의 property로 직접 추가한다는 의미를 가진다. 이 사전에 보여주는 메소드들은 javascript에서 기본적으로 제공하고 있는 표준화된 메소드들이다.

ECMAScript에서는 전역객체의 API를 정의해두었다. 그 외의 API는 호스트 환경에서 필요에 따라서 추가로 정의하고 있다. 이를테면 웹브라우저 자바스크립트에서는 alert()이라는 전역객체의 메소드가 존재하지만 node.js에는 존재하지 않는다. 또한 전역객체의 이름도 호스트환경에 따라서 다른데, 웹브라우저에서 전역객체는 window이지만 node.js에서는 global이다. 

Fig 137. nodejs 실행 후 global 명령어 실행
Fig 137-1. nodejs에서 global 명령어 실행결과
Fig 137-2. nodejs에서 전역객체는 global

 

 

 

ChapterHead

• this

함수와 this

this는 함수 내에서 함수 호출 맥락(context)를 의미한다. 맥락이라는 것은 상황에 따라서 달라진다는 의미인데 즉 함수를 어떻게 호출하느냐에 따라서 this가 가리키는 대상이 달라진다는 뜻이다. 함수와 객체의 관계가 느슨한 자바스크립트에서 this는 이 둘을 연결시켜주는 실질적인 연결점의 역할을 한다.

함수호출

함수를 호출했을 때 this는 무엇을 가르키는지 살펴보자. this는 전역객체인 window와 같다.

Fig 138. func( ) 를 호출하면 이때 this는 window이다

this는 함수 안에서 사용할 수 있는 약속되어 있는 변수이다. 

메소드의 호출

Fig 139. 객체의 소속인 메소드의 this는 그 객체를 가르킨다

객체의 소속인 메소드의 this는 그 객체를 가리킨다. 

1
2
3
4
5
6
7
8
var o = {
    func : function(){
        if(o === this){
            document.write("o === this");
        }
    }
}
o.func();

생성자의 호출

아래 코드는 함수를 호출했을 때와 new를 이용해서 생성자를 호출했을 때의 차이를 보여준다.

var funcThis = null; 
         
        function Func(){
            funcThis = this;
        }
        var o1 = Func();
        if(funcThis === window){
            document.write('window <br />');
        }
         
        var o2 = new Func();
        if(funcThis === o2){
            document.write('o2 <br />');
        }

Fig 140

 

Func( )는 같은 함수이지만 o2 에 넣을 때처럼 생성자로 사용될 때에는 this의 값이 생성될 객체를 가리킨다. 하지만 Func( )를 o1에 넣듯이 함수로 호출하게 되면 이 함수 안에서 this의 값은 전역객체인 window를 가리킨다.

생성자는 빈 객체를 만든다. 그리고 이 객체내에서 this는 만들어진 객체를 가르킨다. 이것은 매우 중요한 사실이다. 생성자가 실행되기 전까지는 객체는 변수에도 할당될 수 없기 때문에 this가 아니면 객체에 대한 어떠한 작업을 할 수 없다. 

Fig 141. o에는 Func( ) 생성자에 의해 만들어진 객체가 할당되는데 함수가 실행되는 시점에서는 아직 o가 생성되지 않았기 때문에 undefined가 나옴

 

apply, call/ 객체로서 함수

javascript가 얼마나 유연한 언어인가를 극명하게 보여주는 예제이다. apply와 call에 대해서는 앞에서도 한 번 언급하였는데 다시 한 번 자세히 알아본다.

Fig 142

Fig 142와 같은 함수를 다음과 같이 Function( ) 생성자를 사용하여 sum2에 할당할 수 도 있다.

Fig 143. new Function( ) 생성자를 사용하여 객체를 생성

Function( )은 생성자 함수인데, 앞의 두 개의 인자는 일반적으로 함수를 정의할 때의 매개변수와 같으며 그리고 마지막에 등장하는 인자가 바로 그 함수의 본문에 해당되는 것이다. Fig 142에서 function sum(x,y)는 sum 이라고 하는 함수 객체를 만드는 것이다. 

그런데 Fig 143에서와 같이 함수 객체를 생성하는 것은 대단히 불편하다.  만약에 함수의 세번째 인자에 해당하는 함수의 본문내용이 길다면 저런 식으로 생성할 수는 없을 것이다.

그래서 Fig 142에서처럼 함수를 정의하면 javascript는 함수 객체를 만들어주는 것이다. 이것을 함수 리터럴 Literal 이라고 한다.

그리고 우리가 객체를 만들 때 var = o{  } 와 같은 식으로 만드는데 이것은 객체 리터럴이 new Object 라고 할 수도 있는 것이다.

배열 변수를 만들 때 var a = [1, 2, 3]; 과 같이 만드는데 이것은 배열 리터럴이다. new Array(1, 2, 3) 이라고 할 수도 있다.

new .... 과 같이 객체를 명시적으로 만들 수도 있지만 그것보다 훨씬 더 편리하게 값을 만들 수 있게 해주는 문법적인 체계를 리터럴이라고 부르는 것이다. 이렇게 객체를 생성하면 그 객체는 property를 가지게 된다. 함수가 가지고 있는 property 중에 ECMAScript에서 정의하고 있는 메소드 중 하나가 apply와 call이다. 

Fig 144. 생성자로 생성한 sum2에는 익명의 함수가 들어있다

 

apply 와 this

Fig 145
Fig 146

func.apply(o); 라고 하게 되면 함수 호출 컨텍스트를 대입하게 된 것이다. 이렇게 되면 함수 안에서 this 는 첫 번째 인자로 지정한 o가 된다. 

전통적인 객체지향에서 메소드라는 것은 객체에 강하게 소속되어 있다. 객체에 포함되어 있는 것이다. 그래서 그 메소드는 다른 곳에 가지 못한다. 객체에 고정되어 있는 것이다. 마치 객체가 주인이고 메소드는 그 주인에게 종속되어 있는 노예와 같은 상태라고 할 수 있다. 실제로 프로그램에서 많이 쓰는 표현이다. Master, Slave . 이것은 일반적인 객체지향의 개념인데 javascript는 유연하다.

위 코드에서 window, o, p, 라는 객체와 함수가 있는데, func( ); 라고 호출하면 func가 마치도 window의 메소드처럼 호출이 되었고, func.apply(o); 라고 호출하면 func는 o의 메소드처럼, func.apply(p); 라고 호출하면 func는 마치 p의 메소드처럼 되는 것이다. 함수도 심지어 객체이기 때문에 window, o, p와 대등한 것인데, 어떤 방식으로 호출하느냐 에 따라 여러 객체의 소속이 된다는 것이다. 주인-노예 관점에서 바라본다면 마치 옛날에는 slave였던 메소드가 현대사회의 개인이 된 것처럼 되어 버렸다. 어느 한 주인에게 종속되지 않는 개인이 된 것이다. 그런데 이 개인도 어떤 맥락에서 호출하느냐에 따라 누군가의 노예가 되는 것이다. 마치 돈을 주는 사람이 갑이 되고 돈을 받는 사람이 을이 되는 것처럼.  javascript에서 함수는 일반적인 객체지향 언어에서 메소드보다 훨씬 더 위상이 높으며 어떻게 호출하느냐에 따라 맥락적으로 어떤 존재에 종속되기도 하는 것이다. 

this는 그것이 소속된 객체를 가리킨다.

 

ChapterHead

• 상속 inheritance

상속이란

객체는 연관된 로직들로 이루어진 작은 프로그램이라고 할 수 있다. 상속은 객체의 로직을 그대로 물려 받는 또 다른 객체를 만들 수 있는 기능을 의미한다. 단순히 물려받는 것이라면 의미가 없을 것이다. 기존의 로직을 수정하고 변경해서 파생된 새로운 객체를 만들 수 있게 해준다.  

객체는 하나의 컨테이너이고 그 안에는 어떤 로직, 어떤 작업과 관련되어 있는 변수, 메소드가 모아져 있다. 그러한 객체의 특성으로 인해 이 객체를 그대로 물려받아 새로운 객체를 만들 수 있다. 이 새로운 객체는 부모가 되는, 즉 오리지널이라고 할 수 있는 객체가 가지고 있는 변수와 메소드에 접근할 수 있게 되는데, 결국 원본객체[부모객체]와 동일한 기능을 가지고 있다는 뜻이다. 그런데 오리지널과 상속받은 객체가 똑같은 기능성을 가지고 있다면 그냥 오리지널을 쓰면 되지 상속을 쓸 필요가 없을 것이다. 중요한 것은 상속받은 객체가 부모객체의 기능들을 추가•제거 하여 자신의 맥락에 맞게 부모객체의 로직을 재활용 할 수 있다는 것이다. 그것이 상속의 기본적인 동작 방법이다.

오리지널 객체의 기능을 상속받는 객체가 동일하게 가질 수 있다는 것이 상속의 개념이다.

Fig 147. Fig 135-2와 같은 코드

이 코드를 아래와 같이 변경시켜보자. 그러면 결과는 똑같다. 그런데 이것은 상속을 하기 위한 준비작업이다.

Fig 148. 상속을 위한 준비작업

앞서 우리는 어떤 객체를 생성할 때 그 객체가 기본적으로 가지고 있어야 할 속성 property 를 세팅하기 위한 방법으로 생성자를 사용한다는 것에 대해 보았는데 생성자말고도 다른 방법으로 할 수도 있다. Fig 148에서 1행에서 Person이라는 생성자를 만들고, 그 생성자 바깥쪽에서 [4행] Person이라는 객체의 prototype이라는 약속된 프로퍼티에 name이라는 프로퍼티를 준 것이다. Person이라는 생성자에는 prototype이라는 property가 있는데 그 안에는 어떤 객체가 들어가 있다. .name을 하여 값을 줄 수 있고, .introduce 에 함수를 할당하게 되면 [5행] 메소드를 정의하게 된다. 이렇게 상속을 위한 준비를 마쳤다.

 

상속의 사용법

이제 javascript에서 상속을 어떻게 구현하는가를 살펴보자. javascript에서 상속의 개념과 상속을 어떻게 사용하는가 하는데 초점을 맞춘다. 

Fig 149

Programmer이라는 생성자를 만들었다. 그리고 이 생성자의 prototype과 Person의 객체를 연결했더니 Programmer 객체도 메소드 introduce를 사용할 수 있게 되었다.

Programmer가 Person의 기능을 상속하고 있는 것이다. 단순히 똑같은 기능을 갖게 되는 것이라면 상속의 의의는 사라질 것이다. 부모의 기능을 계승 발전할 수 있는 것이 상속의 가치다.

15행에서 p1은 introduce메소드를 사용하고, 14행에서 p1은 new Programmer( ) 생성자에 의해 Programmer 라는 객체를 담게 되는데, 9행의 Programmer 객체가 정의되는 곳을 보면 introduce 라는 메소드가 존재하지 않는다. Programmer 객체인 p1이 introduce 메소드를 사용할 수 있는 이유는 12행에 의해 Programmer 가 Person 객체를 상속받기 때문에 가능한 것이다. Person 객체는 introduce 메소드를 가지고 있다.

12행을 보면 알 수 있듯이 Programmer.prototype 속성에 new Person() 생성자를 넣어 Person 객체를 넣은 것이다. Programmer.prototype 에 new Person 하게 되면 Person의 prototype 과 똑같은 객체를 만들어 Programmer.prototype에 넣어준다. 그리하여 Programmer.prototype은 .name이라는 변수와 .introduce라는 메소드를 가지게 되는 것이다.

기능의 추가

Fig 150. 앞의 코드에 약간의 내용을 추가하여 수정함

위의 코드에 따르면 Person 객체는 introduce라는 메소드를 가지고 있고 Programmer는 Person을 상속받아 introduce라는 메소드를 가지고 있는데다 추가로 coding이라는 메소드도 가지고 있다. 즉 introduce는 중복되는 부분이기 때문에 따로 가지고 있는 것이 아니라 상속으로 가져오고 자기만 가지고 있는 coding만 정의하여 따로 가지고 있게 되는 것이다.

이런 기능을 이용하여 코드를 조금 더 추가해보자.

Fig 151.  Person을 상속받는 Designer 라는 객체를 새로 만들고 designing이라는 메소드 추가

Person이라는 부모객체가 있고 Programmer와 Designer는 Person을 상속받는 자식객체로서 introduce라는 메소드를 공통으로 가지고 있으며 만일 6행의 내용을 변경하여 My name 을 My nickname으로 변경하면 일일히 메소드마다 변경해야 하는 뻘짓은 생기지 않을 것이며 한 번에 모든 객체에게 반영이 되는 것이다.

여기서 핵심적인 내용은 바로 prototype 이라는 것이다. 앞에서도 잠깐 언급했지만 javascript를 prototype-based language라고 부르는 이유가 여기에 있다. prototype은 javascript에서 아주 중요하고 객체, 함수 이런 관계에서 상당한 비중을 차지하기 때문에 뒤에서 좀 더 자세히 다룰 것이다.

 

ChapterHead

• prototype

prototype은 상속의 구체적인 수단이라고 말할 수 있다. prototype은 javascript의 객체지향을 지탱하고 있는 핵심적인 기능이고 javascript를 일반적인 객체지향언어와 구분하는 아주 중요한 개념이다. prototype을 통해서 javascript는 상속의 개념을 제공하고 있다.

prototype

그럼 prototype이란 무엇인가? 한국어로는 원형정도로 번역되는 prototype은 말 그대로 객체의 원형이라고 할 수 있다. 함수는 객체다. 그러므로 생성자로 사용될 함수도 객체다. 객체는 프로퍼티를 가질 수 있는데 prototype이라는 프로퍼티는 그 용도가 약속되어 있는 특수한 프로퍼티다. prototype에 저장된 속성들은 생성자를 통해서 객체가 만들어질 때 그 객체에 연결된다.

Fig 152

이 코드에 의하면 맨 상위에 Ultra라고 하는 객체가 있고, 하위에 Super 가 Ultra를 상속받고, Sub가 Super 밑에서 Super를 상속받는다.

o.ultraProp 라고 하면 먼저 o가 sub객체로 생성되었기 때문에 sub에 ultraProp라는 property가 있는지 확인하고, 없으면 그것의 부모인 Super에서 확인하고, 거기도 없으면 Super의 부모인 Ultra에서 ultraProp 라는 property가 있는지 확인하는 식으로 된다.

그러면 이러한 것을 가능하게 하는 prototype이라고 하는 것이 도대체 무엇인지를 살펴보자. 생성자는 기본적으로 함수이다. 10행처럼 함수를 호출할 때 new를 앞에 붙여서 호출하면 이때 함수는 단순한 함수가 아니라 생성자라는 함수가 되는 것이고 그것의 실행결과는 새로운 객체를 만들어 그 객체를 리턴하기 때문에 o라고 하는 변수 안에는 생성자를 통해 만들어진 객체가 들어가게 된다. 이것이 생성자의 역할이다. 

그냥 비어있는 객체를 생성하는 것이 생성자의 역할이라고 한다면 별로 가치가 없을 터인데 [ var o = { } 라고 해도 되니까 말이다], new 를 통해 o라는 변수는 단순한 객체가 아닌, 정의된 변수, 메소드 [즉 특정 로직과 데이터]등을 가지고 있는 객체를 담게 되는 것이다.  

 그러면 우리가 얻고자 하는 어떤 객체의 원형 [즉 변수들과 메소드들을 가진 원형이 되는 객체] 은 어딘 가에 저장이 되어있는데 그곳이 바로 prototype 이라고 하는 property 이다. 즉 Sub 라는 함수는 객체이기 때문에 property를 가질 수 있고, 그중 prototype이라는 특수한 property 가 있는데 그 안에는 어떤 객체가 정의되어 있다. 그래서 나중에 new를 써서 호출하면 바로 이 prototype에 정의되어 있는 객체를 리턴하여 꺼내주게 된다.

Fig 153

Fig 152의 10행에서 o에는 Sub 생성자에 의해 Sub 객체가 들어가고, Sub.prototype에는 Super 생성자에 의해 Super 객체가 들어가고 Super.prototype에는 Ultra 생성자에 의해 Ultra 객체가 들어가며 Ultra.prototype.ultraProp에 true값이 정의 되어 있다. 이렇게 prototype에 의해 연결되어 있는 것을 prototype chain 이라고 한다.    Sub.prototype 이 Super라는 객체를 가리키고, new Super() 라는 객체는 Super.prototype을 가리키고, 또 new Ultra() 는 Ultra.prototype을 가리키고...

생성자 Sub를 통해서 만들어진 객체 o가 Ultra의 프로퍼티 ultraProp에 접근 가능한 것은 prototype 체인으로 Sub와 Ultra가 연결되어 있기 때문이다. 내부적으로는 아래와 같은 일이 일어난다.

  1. 객체 o에서 ultraProp를 찾는다.
  2. 없다면 Sub.prototype.ultraProp를 찾는다.
  3. 없다면 Super.prototype.ultraProp를 찾는다.
  4. 없다면 Ultra.prototype.ultraProp를 찾는다.

prototype는 객체와 객체를 연결하는 체인의 역할을 하는 것이다. 이러한 관계를 prototype chain이라고 한다.

Super.prototype = Ultra.prototype 으로하면 안된다. 이렇게하면 Super.prototype의 값을 변경하면 그것이 Ultra.prototype도 변경하기 때문이다. Super.prototype = new Ultra();는 Ultra.prototype의 원형으로 하는 객체가 생성되기 때문에 new Ultra()를 통해서 만들어진 객체에 변화가 생겨도 Ultra.prototype의 객체에는 영향을 주지 않는다.

Fig 154

참 재미있는 일이다. o2.ultraProp의 값이 정해지지 않으면 부모객체의 ultraProp값을 가져오지만 값을 지정해주면 값을 따로따로 가진다는 것을 알 수 있다. 즉 자식객체에서 속성의 값이 정해지지 않으면 부모 또는 부모의 부모쪽에서 속성값을 가져오지만 따로따로 값을 가지고 있을 수도 있다는 얘기다. 

하지만 Super.prototype = Ultra.prototype 으로 하면 얘기는 달라진다. 이렇게하면 Super.prototype의 값을 변경하면 그것이 Ultra.prototype도 변경하기 때문이다. Super.prototype = new Ultra();는 Ultra.prototype의 원형으로 하는 객체가 생성되기 때문에 new Ultra()를 통해서 만들어진 객체에 변화가 생겨도 Ultra.prototype의 객체에는 영향을 주지 않는다.

 

ChapterHead

• 표준 내장 객체의 확장

표준 내장 객체란

표준 내장 객체(Standard Built-in Object)는 자바스크립트가 기본적으로 가지고 있는 객체들을 의미한다. 내장 객체가 중요한 이유는 프로그래밍을 하는데 기본적으로 필요한 도구들이기 때문에다. 결국 프로그래밍이라는 것은 언어와 호스트 환경에 제공하는 기능들을 통해서 새로운 소프트웨어를 만들어내는 것이기 때문에 내장 객체에 대한 이해는 프로그래밍의 기본이라고 할 수 있다.

자바스크립트는 아래와 같은 내장 객체를 가지고 있다. 

  • Object
  • Function
  • Array
  • String
  • Boolean
  • Number
  • Math
  • Date
  • RegExp

이제 우리는 내장객체라는 하늘에서 뚝떨어진 이것들이 무엇인지를 보다 잘 이해할 수 있게 되었다. new가 무엇인지, 함수가 객체를 어떻게 만드는지도 알았다. 또 원한다면 자바스크립트의 내장 객체와 같은 것을 우리도 만들 수 있다는 것 [사용자 정의 객체] 도 알았다. 이러한 지식을 바탕으로 좀 더 멋진 일을 해보자.

배열을 확장

배열을 확장해보자. 아래 코드는 배열에서 특정한 값을 랜덤하게 추출하는 코드다. 

Fig 155. floor는 소수점을 떼어버리고 가장 낮은 정수를 돌려줌

이렇게 작성해도 된다! 잘못한 것이 아니다. 하지만 조금 더 세련된 방법은 이 함수를 배열 객체에 포함시키는 것이다. 그렇게하면 마치 배열에 내장된 메소드인 것처럼 위의 기능을 사용할 수 있다.

배열이 가지고 있는 어떤 특정한 값을 랜덤하게 획득할 수 있는 기능을 모든 배열의 객체가 가질 수 있도록 코드를 변경해보자.

Fig 156. prototype 으로 정의함으로써 arr처럼 Array를 상속받으면 거기에 정의된 함수를 적용할 수 있다

 

ChapterHead

• Object

Object 객체는 객체의 가장 기본적인 형태를 가지고 있는 객체이다. 다시 말해서 아무것도 상속받지 않는 순수한 객체다. 자바스크립트에서는 값을 저장하는 기본적인 단위로 Object를 사용한다. 

동시에 자바스크립트의 모든 객체는 Object 객체를 상속 받는데, 그런 이유로 모든 객체는 Object 객체의 프로퍼티를 가지고 있다.

Object 객체는 모든 객체의 부모객체이기 때문에 Object.prototype은 모든 객체들의 prototype 원형이 된다. 즉 Object 객체의 prototype은 모든 객체가 사용할 수 있는 기능이다. 즉 모든 객체가 공통적으로 가지고 있어야 할 기능이 있다면 Object의 prototype으로 지정했을 것이다. 모든 객체가 가지고 있었으면 하는 기능이 있다면 Object의 prototype에 여러 가지 기능을 추가하는 것을 통해 모든 객체가 사용할 수 있는 기능을 추가할 수 있다는 거다. 

 

Object API 사용법

Object의 매뉴얼을 어떻게 읽는지 살펴보고 Object 가 가지고 있는 2 가지 형태의 메소드를 알아보자.

Mozilla Developer 홈페이지:https://developer.mozilla.org/en-US/ 

Fig 157

 

이 홈페이지에서 Javascript Reference에서 Object 뿐만 아니라 다른 객체들의 사용법에 대한 내용도 확인할 수 있다. 

Fig 158. 메소드 아래부분 .prototype. 이 끼어있는 메소드들

위쪽에 있는 그룹은 prototype 없이 바로 점을 통해 이어진다. 그러면 prototype 이 있는 그룹과 없는 그룹의 차이점은 무엇일까? 보면 Object.key 메소드가 있는데 값을 수정하면서 직접 실행해보는 것도 가능하다.

Fig 159. Object.keys( )

Object.prototype.toString 도 있다.

Fig 160. Object.prototype.toString( )

이제 이 두 가지 메소드 즉 Object.prototype.toString()과 Object.keys() 를 사용하는 방법을 통해 prototype이 있는 것과 없는 것의 차이를 비교해보자.

Fig 161
Fig 162. 위 코드의 결과

위의 코드를 보면 prototype이 있는 것과 없는 것은 호출방법이 다르다. prototype이 있는 것은 16, 19행과 같이 먼저 객체를 만들고  a.toString() 과 같이 메소드가 마치 자신의 메소드인 것처럼 호출하지만, prototype이 없는 것은 Object.keys(arr) 와 같이 인자로서 전달하는 방식으로 사용한다. 

아마도 Object.key는 내부에서 이런 식으로 정의되었을 것이다. Object.keys = function() { ... }

그리고 Object.prototype.toString은 내부에서 이렇게 정의되었을 것이다. Object.prototype.toString = function(){  ...  }

어떤 메소드가 prototype의 소속이라는 것은 new Object(); 라고 생성자함수를 실행시키는 순간, 객체를 만드는데 그 생성자의 prototype이라고 하는 특수한 property에 저장되어 있는 객체를 원형으로 하는 객체가 생성되는 것이다.

그러면 그렇게 생성된 객체는 당연히 toString이라는 메소드를 사용할 수 있게 되기 때문에 사용할 때에는 그 객체가 가지고 있는 메소드처럼 사용하게 된다.

Object는 모든 객체의 공통적인 부모이기 때문에 19행에서처럼 Array 라는 객체를 생성할 때에도 Array는 내부적으로 부모인 Object를 상속받는다. 따라서 Array를 통해서 만들어진 객체는 a.toString()과 같이 Object에 정의 되어 있는 prototype.toString 메소드를 사용할 수 있게 되는 것이다.

Object는 javascript의 모든 객체들에게 상속되는 공통적인 부모객체이기 때문에 Object.prototype. 으로 정의된 함수들은 javascript의 모든 객체들이 공통적으로 갖게 되는 메소드들이다. 바꿔말하면 자신이 프로그램을 만들 때 만일에 프로그램 안의 모든 객체들이 공통으로 가져야 할 메소드가 필요하다면 Object 객체의 prototype을 정의하고 수정하는 것을 통해 그러한 기능을 만들 수 있다.

또한 MDN [Mozilla Developer Network] 의 Javascript Reference 에서 Object 등의 항목을 참조하면 Specifications 가 있는데 그것은 ECMAScript 의 어떤 버전에서 정의되었는가를 보여준다. 현재 [2014] 모든 브라우저에서 쓸 수 있는 기능은 ECMAScript 3버전에서 정의된 기능이다.

Fig 163

 

Object 확장

Object 객체를 확장하여 우리가 만들고 있는 애플리케이션에서 어느 객체에서나 사용할 수 있는 메소드를 만드는 방법을 살펴본다.

구체적인 구현을 하기 전에 무엇을 구현할 것인가, 구현한 것을 어떻게 사용하게 될 것인가를 먼저 살펴보자. 특정 객체가 아닌 배열을 포함하여 javascript의 모든 객체에서 아래 코드와 같은 기능을 사용하도록 하기 위해 Object.prototype으로 메소드를 정의한다.

이렇게 하여 Object를 확장하여 모든 객체가 사용할 수 있는 메소드를 애플리케이션에 추가하는 방법을 보았는데 사실 한가지 문제를 안고 있고 따라서 별로 권장되는 방법은 아니다.

Object 확장의 위험

위에서 살펴본 방법을 신중하게 사용해야 하는 이유는 모든 객체에 영향을 줄 수 있기 때문이다. 

Fig 164
Fig 165

Fig 164 실행 후 바로 Fig 165를 실행하면 객체 o의 값들이 출력되는데 정의한 함수도 나오는 것을 볼 수 있다. Object에만 정의한 줄 알았던 contain이라는 메소드도 포함되어 있는 것, 즉 프로그램 안의 모든 객체에 영향을 주는 것이다.

Fig 166

이것은 배열인 a에 대해서도 마찬가지다.

애초 o, a 를 사용하려고 했을 때에는 for문을 돌려 모든 원소들을 꺼낼 때 contain 이 나올 것은 전혀 원치 않았던 부분이다.

따라서 모든 객체에 영향을 주는 이런 행위는 편리한 것 같으면서도 그만큼 위험이 따른다.

이럴 때 사용할 수 있는 방법이 하나 있긴 하다.

Fig 167

바로 hasOwnProperty() 메소드를 사용하는 것이다. 프로퍼티의 해당 객체의 소속인지를 체크해볼 수 있는 hasOwnProperty를 사용하면 된다. hasOwnProperty는 인자로 전달된 속성의 이름이 객체의 속성인지 여부를 판단한다. 만약 prototype으로 상속 받은 객체라면 false가 된다. 

 

ChapterHead

• 데이터 타입

원시 데이터 타입

이제 데이터 타입에 대해서 조금 더 심도 있는 이야기를 할 수 있게 되었다. 데이터 타입이란 데이터의 형태를 의미한다. 데이터 타입은 크게 두가지로 구분할 수 있다. 객체와 객체가 아닌 것. 그럼 객체가 아닌 것 [원시 데이터 타입, 기본데이터타입]은 무엇일까?

  • 숫자
  • 문자열
  • 불리언(true/false)
  • null
  • undefined

객체가 아닌 데이터 타입을 원시 데이터 타입(primitive type)이라고 한다. 그 외의 모든 데이터 타입들은 객체다. 

객체 데이터 타입은 참조 데이터 타입이다. 

래퍼 객체 wrapper object

이런 분류가 중요한 것은 아니고, javascript 개발자 입장에서 이러한 것들은 원시 데이터 타입과 객체가 서로 다르게 동작할 때에 효용이 있는 것이다.

앞에서 설명할 때에는 문자열이 원시 데이터타입이라고 해놓고서는 위의 코드를 보면 문자열은 분명히 프로퍼티와 메소드가 있다. str과 length사이에 있는 점은 Object access Operator 객체 접근 연산자이다. 즉 점을 썼을 때 점 앞에 있는 것은 객체라는 뜻이다. 그렇다면 객체다. 그런데 왜 문자열이 객체가 아니라고 할까? 그것은 내부적으로 문자열이 원시 데이터 타입이고 문자열과 관련된 어떤 작업을 하려고 할 때 자바스크립트는 임시로 문자열 객체를 만들고 사용이 끝나면 제거하기 때문이다. 이러한 처리는 내부적으로 일어난다. 그렇기 때문에 몰라도 된다. 하지만 원시 데이터 타입과 객체는 좀 다른 동작 방법을 가지고 있기 때문에 이들을 분별할 필요가 있다.

내부적으로 볼 때에는 1행과 2행 사이에 str = new String('coding'); 이라는 코드가 있다고 생각하면 이해하기 쉽다.

문자열이 객체가 아니라는 것을 보여주는 코드가 아래에 있다.

str.prop를 하는 순간에 자바스크립트 내부적으로 String 객체가 만들어진다. prop 프로퍼티는 이 객체에 저장되고 이 객체는 곧 제거 된다. 그렇기 때문에 prop라는 속성이 저장된 객체는 존재하지 않게된다. 이러한 특징은 일반적인 객체의 동작 방법과는 다르다.

하지만 문자열과 관련해서 필요한 기능성을 객체지향적으로 제공해야 하는 필요 또한 있기 때문에 원시 데이터 형을 객체처럼 다룰 수 있도록 하기 위한 객체를 자바스크립트는 제공하고 있는데 그것이 래퍼객체(wrapper object)다. javascript는 이러한 작업을 자동으로 처리해준다. 어떤 언어들은 이러한 작업을 자동으로 처리하지 않기 때문에 개발자가 명시적으로 wrapper object를 만들어서 사용하기도 한다.

래퍼객체로는 String, Number, Boolean이 있다. null과 undefined는 래퍼 객체가 존재하지 않는다. 따라서 null.prop 이런 식으로 쓰면 타입에러가 발생한다.

 

ChapterHead

• 참조

복제

참조와 함께 살펴볼 개념은 복제라는 개념이다. 이 복제와 참조의 차이를 통해 두 가지의 개념을 좀 더 분명하게 드러내도록 해보자. 전자화된 시스템의 가장 중요한 특징은 복제다. 현실의 사물과 다르게 전자화된 시스템 위의 데이터를 복제 하는데는 비용이 거의 들지 않는다. 바로 이러한 특징이 소프트웨어를 기존의 산업과 구분하는 가장 큰 특징일 것이다. 프로그래밍에서 복제가 무엇인가를 살펴보자.

결과는 당연하다. 값을 변경한 것은 변수 b이기 때문에 변수 a에 담겨있는 값은 그대로이다. 변수 b의 값에 변수 a의 값이 복제된 것이다.

참조

그런데 자연의 산물이 아니라 거대한 약속의 집합인 소프트웨어의 세계에서 당연한 것은 없다. 이것이 당연하지 않은 이유는 다음 예제를 통해서 좀 더 분명하게 드러난다.

이 코드의 주인공은 아래와 같다.

놀라운 차이점이 있다. 변수 b에 담긴 객체의 id 값을 2로 변경했을 뿐인데 a.id의 값도 2가 된 것이다. 이것은 변수 b와 변수 a에 담긴 객체가 서로 같다는 것을 의미한다. 참조(reference)의 세계에 온 것을 환영한다.

앞서 필자는 전자화된 세계에서 가장 중요한 특징으로 복제를 들었다. 그런데 복제만으로 전자화된 시스템을 설명하는 것은 조금 부족하다. 비유하자면 복제는 파일을 복사하는 것이고 참조는 심볼릭 링크(symbolic link) 혹은 바로가기(윈도우)를 만드는 것과 비슷하다. 원본 파일에 대해서 심볼릭 링크를 만들면 원본이 수정되면 심볼릭 링크에도 그 내용이 실시간으로 반영되는 것과 같은 효과다. 심볼릭 링크를 통해서 만든 파일은 원본 파일에 대한 주소 값이 담겨 있다. 누군가 심볼릭 링크에 접근하면 컴퓨터는 심볼릭 링크에 저장된 원본의 주소를 참조해서 원본의 위치를 알아내고 원본에 대한 작업을 하게 된다. 다시 말해서 원본을 복제한 것이 아니라 원본 파일을 참조(reference)하고 있는 것이다. 덕분에 저장 장치의 용량을 절약할 수 있고, 원본 파일을 사용하고 있는 모든 복제본이 동일한 내용을 유지할 수 있게 된다. 참조는 전자화된 세계의 극치라고 할 수 있다.

프로그래밍에서 광범위하게 사용하는 라이브러리라는 개념도 일종의 참조라고 할 수 있다. 공용 라이브러리를 사용하게 되면 하나의 라이브러리를 여러 애플리케이션에서 공유해서 사용하게 된다. 라이브러리의 내용이 변경되면 이를 참조하고 있는 애플리케이션에도 내용이 반영되게 된다. 또 우리가 변수를 사용하는 이유도 말하자면 참조를 위해서라고 할 수 있을 것이다. 본질을 파악하면 이해력도 높아지고 암기할 것도 줄어든다.

아래 두 개의 구문의 차이점을 생각해보자.

무엇일까? 전자는 데이터형이 숫자이고 후자는 객체다. 숫자는 원시 데이터형(기본 데이터형, Primitive Data Types)이다. 자바스크립트에서는 원시 데이터형을 제외한 모든 데이터 타입은 객체이다. 객체를 다른 말로는 참조 데이터 형(참조 자료형)이라고도 부른다. 기본 데이터형은 위와 같이 복제 되지만 참조 데이터형은 참조된다. 모든 객체는 참조 데이터형이다. 이를 그림으로 나타내면 아래와 같다.

Fig 168

정리하면 변수에 담겨있는 데이터가 원시형이면 그 안에는 실제 데이터가 들어있고, 객체면 변수 안에는 데이터에 대한 참조 방법이 들어있다고 할 수 있다.

Fig 169

Fig 169의 코드를 설명하면 다음과 같다. 3번 째 행에서는 b에 {'id':2}라는 새로운 객체를 할당하면서 a, b가 참조하는 객체가 달라진다. 그리하여 a.id 의 값은 1이 출력되는 것이다.

함수

그럼 일종의 변수할당이라고 할 수 있는 메소드의 매개변수는 어떻게 동작하는가를 살펴보자. 조금 복잡하므로 꼼꼼하게 살펴봐야 한다. 예제를 보자.

다음은 원시 데이터 타입을 인자로 넘겼을 때의 동작 모습이다.

다음은 참조 데이터 타입을 인자로 넘겼을 때 동작하는 장면이다. 

함수 func의 파라미터 b로 전달된 값은 객체 a이다 (b = a).  그리고 3행에서는 b를 새로운 객체로 대체하고 있으며 그것은 b가 가리키는 객체를 변경하는 것이기 때문에 객체 a에 영향을 주지 않는다.

하지만 아래는 다르다.

파라미터 b는 객체 a의 레퍼런스다. 이 값의 속성을 바꾸면 그 속성이 소속된 객체를 대상으로 수정작업을 한 것이 되기 때문에 b의 변경은 a에도 영향을 미치게 된다. 

코드와 오픈소스

Fig 169. twitter에서 만든 bootstrap. 아주 잘 나가는 오픈 소스의 프로젝트 페이지. Fork 기능. github에 로그인하여 Fork를 눌러 이 세계적인 프로젝트를 내것으로 만들 수 있다. 수정, 새로운 제품 제작, 판매도 가능
Fig 170. 레고 디지털 디자이너

https://www.lego.com/en-us/ldd

 

ChapterHead

TOP

5. 패턴

패턴은 자주 사용하는 로직들의 구현방법과 그것에 대한 이름을 의미한다. 이를 통해서 문제를 해결하는 탁월한 방법들을 빠르게 익힐 수 있다. 또한, 문제를 해결하기 위한 방법을 논의할 때 패턴의 이름을 사용함으로써 의사소통을 효율적으로 할 수 있다. 본 토픽의 하위 토픽을 통해서 다양한 패턴들을 알아본다.

• 재귀함수

재귀함수는 함수 자신을 호출하는 프로그래밍 기법이다. 재귀 함수에 대해서는 웹브라우저 자바스크립트 모듈에서 언급하고 있기 때문에 이 수업을 참고하자. 

http://opentutorials.org/module/904/6700

 

 

 

 

 

ChapterHead

TOP

6. 마무리

엄격하고 격조를 중시하는 자바와 대비되는 자유분방하고 개성이 확실한 자바스크립트.

최소한으로 배워 최대한으로 써먹어야 한다.

  • 자바스크립트 완벽 가이드 6판
    (Javascript: The Definitive Guide 6/E Part 1.   Part 2. )

    데이비드 플래너건 저 | 구경택, 박경욱, 변치훈, 이의훈 역
    이 책은 자바스크립트의 거의 모든 것을 담고 있습니다. 두께 만큼이나 많은 내용을 담고 있기 때문에 자바스크립트 개발자라면 한권쯤은 소장하고 계시면 좋을 것 같습니다. 
     
  • 자바스크립트 핵심가이드
    (JavaScript: The Good Parts)
    더글라스 크락포드 저 | 김명식 역
    이 책은 자바스크립트 구루 중의 한명인 더글라스 크락포드님이 쓰신 책입니다. 자바스크립트에서 좋은 점과 나쁜 점을 구분해서 좋은 점만 사용하도록 유도하고 있는 책입니다. 대가의 책답게 간결하면서 힘이 넘치는 책입니다. 다만 초보자에게는 몹시 난해한 책일 수 있습니다. 하지만 보면 볼수록 씹는 맛이 나는 그런 책입니다. 

     
  • 자바스크립트 닌자 비급
    (Secrets of the JavaScript Ninja)
    존레식, 베어 바이볼트 저 | 강대명, 김광훈, 이의호 역 
    이 책은 jQuery의 창시자인 존레식님이 쓰신 책입니다. 자바스크립트의 모든 내용을 다루는 책이 아닙니다. 쉬운 책도 아닙니다. 따라서 초심자에게는 권하고 싶은 책은 아닙니다만, 자바스크립트에 대해서 메너리즘을 느끼기 시작하는 중급자에게는 정체감을 뛰어넘게 하는 다양한 기법과 좋은 사례들이 소개되어 있습니다. 

     
  • 자바스크립트를 깨우치다
    (JavaScript Enlightenment)
    코디 린들리 저 | 김태곤 역
    저는 이 책을 소장하고 있지는 않습니다. 서점에서 이 책을 봤는데 자바스크립트의 고급 개념들을 비교적 쉬운 예제를 통해서 소개하고 있었습니다. 제 기억으로는 각각의 단계에 대해서 상세하게 설명하고 있기 때문에 애매함에 관대하지 않은 분들은 그 원리를 파악하는데 도움이 될 것 같습니다. 
     
  • 자바스크랩트 재진입하기
    파이어폭스를 만든 모질라 재단에서 제공하는 자바스크립트 학습자료입니다. 원 페이지에 거의 모든 것을 담았네요. 이미 프로그래밍을 해본 분에게 적당할 것 같습니다. 
    https://developer.mozilla.org/ko/docs/A_re-introduction_to_JavaScript

 

TOP

Comments