클로져(Closures)

js 2014. 11. 3. 11:24

자바스크립트를 배우려는 사람들에게 클로져는 어렵게 느껴지지만 자바스크립트를 깊게 알기 위해서 반드시 넘어야할 산이다.

다음 함수를 생각해보자.

function init() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  displayName();
}
init();

init() 함수는 name 이라는 지역변수를 만들고 displayName() 이라는 함수를 정의한다. displayName() 은 내부함수라고 불리는데 이는 함수 init() 안에 정의되었고 init() 함수 안에서만 사용할 수 있기 때문이다. displayName() 함수는 지역변수를 가지지 않지만 외부에서 정의된 name변수를 사용하고 있다.

코드를 한번 실행해보라. 잘 동작할 것이다. 이 예제는 함수 스코핑(functional scoping) 을 보여주기 위해 소개했다. 자바스크립트에서 중첩된 함수는 그 함수 외부에서 정의된 변수를 사용할 수 있다. 

다른 예제를 보자.

function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

이 예제를 실행해 보면 위의 예제 init() 함수와 동일한 결과를 보이는걸 알 수 있다(알람창에 "Mozilla" 문자열이 보일 것이다). 위 예제와 다른 점은 외부함수의 리턴 값이 내부함수 displayName() 라는 것이다. 흥미롭지 않은가?

이 코드가 문제없이 실행되는 것은 직관적이지 않다. 일반적으로 함수안에 정의된 지역변수는 함수가 종료되기 전까지만 존재한다. makeFunc() 함수가 종료될 때 이 함수 내부에 정의된 지역변수는 없어지는게 상식적이다. 이 코드가 문제없이 동작하는 걸 보면 다른 일이 일어나고 있는 것 같다!

이 퍼즐에 대한 해답은  myFunc 함수가 클로져(closure) 를 갖는다는 것이다. 클로져는 두 개의 것으로 이루어진 특별한 오브젝트이다. 첫 번째는 함수이고 두 번째는 그 함수가 만들어진 환경이다. 그 함수가 만들어진 환경은 함수가 만들어질 때 사용할 수 있었던 변수들로 이루어진다. 이 경우에 myFunc 는 displayName 함수와 "Mozilla" 문자열을 포함하는 클로져이다.

조금 더 흥미로운 예제를 보자. makeAdder 라는 함수이다.

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

print(add5(2));  // 7
print(add10(2)); // 12

이 예제에서 makeAdder(x) 라고 하는 하나의 인자를 받는 함수를 만들었다. 이 함수는 x라는 인자를 받아서 새로운 함수를 반환한다. 반환하는 함수는 y라는 인자를 받아서 x+y를 돌려주는 함수이다.

makeAdder 는 함수 공장(function factory)이다. 특정한 수를 인자에 더해서 돌려주는 함수들을 '찍어낸다'. 위의 예제에서 두개의 함수를 찍어냈다. 첫째는 인자에 5를 더하는 함수이고 둘째는 인자에 10을 더하는 함수이다.

add5 와 add10 는 둘다 클로져이다. 두 함수는 같은 정의를 가지지만 다른 환경을 저장한다. add5의 환경에서 x는 5이지만 add10 의 환경에서 x는 10이다.

실용적인 클로져

이제까지는 이론이었다. 클로져는 실용적인가? 이제는 실용적인 사용 방법을 알아보자. 어떤 데이터(환경)와 함수를 연관시키는데 클로져를 사용할 수 있다. 이건 객체지향 프로그래밍과 유사하다. 객체지향 프로그래밍에서는 객체가 데이터(그 객체의 속성)와 하나 이상의 메쏘드를 연관시킨다.

결론적으로 함수에서 오브젝트를 사용하려고 할 때 클로져를 사용할 수 있다.

웹 프로그래밍에서 이런 일이 많이 일어난다. 많은 자바스크립트 코드가 이벤트를 기반으로 짜여진다. (특정한 동작을 만들고 클릭이나 키보드 누르기에 이 동작을 연결시킨다) 이벤트에 반응하는 코드를 만든다고 할 수 있겠다. 이런 코드들을 콜백(callback)이라고 부른다.

여기에 실용적인 예제가 있다. 페이지의 글자 크기를 조정하는 몇 개의 버튼을 만든다고 생각해보자. body 엘리먼트에 px단위로 font-size를 설정하고 다른 엘리먼트에서는 상대적인 em 단위로 font-size를 설정하면 되겠다.

body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}
h2 {
  font-size: 1.2em;
}

이제 body 엘리먼트의 font-size만 바꾸면 font-size가 em단위로 설정된 다른 엘리먼트들의 글자 크기도 바뀔 것이다.

자바스크립트 코드이다.

function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

size12size14, size16 은 body 엘리먼트의 글자 크기를 각각 12, 14, 16 픽셀로 바꾸는 함수이다. 이제 이 함수를 버튼과 연결시키자.

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>

JSFIDDLE에서보기

 

클로져를 이용해서 private 함수 흉내내기

몇몇 언어(예를들어 자바)는 같은 클래스 내부의 메쏘드에서만 호출할 수 있는 private 메쏘드를 지원한다.

자바스크립트는 이를 지원하지 않지만 클로져를 이용해서 흉내낼 수 있다. private 함수는 코드에 제한적인 접근만을 허용한다는 점 뿐만 아니라 전역 네임스페이스를 깔끔하게 유지할 수 있다는 점에서 중요하다.

아래에 모듈 패턴이라고 알려진 클로져를 통해 몇 개의 public 함수가 private 함수와 변수에 접근하는 코드가 있다.

var Counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }   
})();

alert(Counter.value()); /* 0 */
Counter.increment();
Counter.increment();
alert(Counter.value()); /* 2 */
Counter.decrement();
alert(Counter.value()); /* 1 */

이전 예제에서는 각 클로져가 자기만의 환경을 가졌지만 이 예제에서는 하나의 환경을 Counter.incrementCounter.decrement, Counter.value 세 함수가 공유한다.

공유되는 환경은 정의되자마자 실행되는 익명 함수 안에서 만들어진다. 이 환경에는 두 개의 private 아이템이 존재한다. 하나는 privateCounter라는 변수이고 나머지 하나는 changeBy라는 함수이다. 이 두 아이템 모두 익명함수 외부에선 접근할 수 없다. 하지만 익명함수 안에 정의된 세개의 public 함수에서 사용되고 반환된다.

이 세개의 public 함수는 같은 환경을 공유하는 클로져이다. 자바스크립트 어휘 스코핑(lexical scoping) 덕분에 세 함수 모두 privateCounter 변수와 changeBy 함수에 접근할 수 있다.

익명 함수가 카운터를 정의하고 이것을 Counter 변수에 할당한다는 걸 알아차렸을 것이다. 이 함수를 다른 변수에 저장하고 이 변수를 이용해 여러개의 카운터를 만들수도 있다.

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }  
};

var Counter1 = makeCounter();
var Counter2 = makeCounter();
alert(Counter1.value()); /* 0 */
Counter1.increment();
Counter1.increment();
alert(Counter1.value()); /* 2 */
Counter1.decrement();
alert(Counter1.value()); /* 1 */
alert(Counter2.value()); /* 0 */

두개의 카운터가 어떻게 독립적으로 존재하는지 주목하라. makeCounter() 함수를 호출하면서 생긴 환경은 호출할 때마다 다르다. 클로져 변수 privateCounter 는 다른 인스턴스를 가진다.

객체지향 프로그래밍을 사용할 때 얻는 이점인 정보 은닉과 캡슐화를 클로져를 사용함으로써 얻을 수 있다.

자주하는 실수: 반복문 안에서 클로져 만들기

자바스크립트 1.7의 let 키워드 가 도입되기 이전에는 반복문 안에서 클로져를 생성해서 문제가 되는 경우가 빈번했다. 다음 예제를 보자.

<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();

JSFIDDLE에서보기

helpText 배열은 세개의 도움말을 정의한다. 각 도움말은 입력 필드의 ID와 연관된다. 이 세개의 정의를 반복하며 입력필드에 onfocus 이벤트가 발생했을 때 입력필드에 해당하는 도움말을 표시한다.

이 코드를 실행해보면 제대로 동작하지 않는다는 것을 알 수 있다. 어떤 필드에 포커스를 주더라도 나이에 관한 도움말이 표시된다.

이유는 onfocus 이벤트에 지정한 함수가 클로져라는 것이다. 이 클로져는 함수 본체와 setupHelp 함수의 스코프로 이루어져 있다. 세개의 클로져가 만들어졌지만 각 클로져는 하나의 환경을 공유한다. 반복문이 끝나고 onfocus 콜백이 실행될 때 콜백의 환경에서 item 변수는 (세개의 클로져가 공유한다) helpText 리스트의 마지막 요소를 가리키고 있을 것이다.

여러개의 클로져를 이용해서 문제를 해결할 수 있다. 위에서 언급한 함수 공장을 사용해보자.

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function makeHelpCallback(help) {
  return function() {
    showHelp(help);
  };
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}

setupHelp();

JSFIDDLE에서보기

예상한대로 작동한다. 콜백이 하나의 환경을 공유하지 않고 makeHelpCallback 함수가 만든 새로운 환경을 가진다. 이 환경에는 helpText 배열로부터 해당하는 문자열이 help 변수에 담겨있다.

추가로 원문에는 없지만 makeHelpCallback 함수를 이용하지 않고 즉시 실행 함수를 이용하면 아래와 같다.

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = (function(help) {
      return function() {
        showHelp(help);
      }
    })(item.help);
  }
}

setupHelp();

 

성능과 관련해서

클로져가 필요하지 않은 작업인데도 함수안에 함수를 만드는 것은 스크립트 처리 속도와 메모리 사용량 모두에서 현명한 선택이 아니다.

예를들어 새로운 오브젝트나 클래스를 만들 때 오브젝트 생성자에 메쏘드를 정의하는 것 보다 오브젝트의 프로토타입에 정의하는것이 좋다. 오브젝트 생성자에 정의하게 되면 생성자가 불릴때마다 메쏘드가 새로 할당되기 때문이다.

비현실적이지만 설명을 위해 예제를 첨부했다.

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function() {
    return this.name;
  };

  this.getMessage = function() {
    return this.message;
  };
}

위의 코드는 일일히 메쏘드를 만들면서 클로져의 이점을 살리지 못하고 있다.

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype = {
  getName: function() {
    return this.name;
  },
  getMessage: function() {
    return this.message;
  }
};

또는 다음처럼 하자

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};

위의 두 예제에서는 상속된 속성은 모든 오브젝트에서 사용될 수 있고 메쏘드 정의가 오브젝트가 생성될 때마다 일어나지 않는다. 오브젝트 모델에 대한 자세한 설명을 참고하라.

https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures

Posted by 조신부리
,

리눅스 명령어

linux 2014. 11. 3. 10:49

이 글은 현재 프로젝트에서 사용중인 CentOS Linux 기준으로 작성이 되었지만, 대부분의 리눅스에서 사용되어지는 bash셸(Bourne Again Shell) 및 공통 유틸리티에 대해서 다루므로 특정 리눅스 배포판에 관계없이 대부분 사용할 수 있습니다.

 

대부분의 리눅스관련 서적과 같이 명령어를 정리하자면, 글이 장황해지고 포스팅으로써 분량이 맞지 않기 때문에 커맨드라인을 효과적으로 사용 할 수 있는 유틸리티를 중심으로 소개합니다.

 

1. 명령어: cd

 

cd -


위와 같이 ‘cd  -’ 를 사용하여 이전 디렉토리로 전환을 쉽게 할수 있다.

하지만 이것의 문제점은 바로 이전의 위치만 가능하다는데 있다. 만약에 현재 위치를 기억해두고, 다른 여러디렉토리를 이동후 기억된 위치로 되돌아가고자 할때 유용하게 사용할수 있는 것이 있다.

 

pushd, popd


위와 같이 ‘pushd‘ 명령어를 사용하여 현재 디렉토리 위치를 저장하고 다른디렉토리로 이동하게 된다.  그 이후  여러다른 디렉토리로 이동후에 언제라도 ‘popd‘를 사용하여 이전에 기억해둔 곳으로 전환할 수 있다.

 

2. 명령어: history

 

!!

쌍느낌표(Double exclamation)  ‘!!‘ 는 바로 직전 명령어를 실행한다. 다음 예제를 보자.

 

!! | grep Linux

이 기능을 다음과 같이 쉽게 응용 할 수 있다. 이전 명령어에 쉽게 추가하기

 

sudo !!

다음과 같이 root 권한 명령어인데 ‘sudo’ 없이 사용한 명령어를 쉽게 불러와 사용할수 있다. 이는 방향키를 올려서 이전 명령어를 불러 오고 다시 앞쪽으로 이동하여 에디트하는 번거로움을 피할수 있다.

 

echo !! > script.sh

가끔 출력 결과를 파일이나 스크립트로 저장할 필요가 있을때가 있다. 이런 경우  ‘!!‘를 사용하여 반복을 피할 수 있다.


지금까지 ‘!!‘에 대해서 알아보았다. 하지만 ‘!‘의 기능은 조금 다른데, 이는 명령어 이력번호를 호출하는데 사용된다. 다음 예제를 보자.

 

!이력번호

history

이 명령어를 치면 아래와 같이 이전 명령어들이 각각 고유 식별번호가 존재하는데, 그 식별번호를 사용하여 재 사용이 가능하다. 다음 예제를 보자.

!1093

이 명령어는 1093번 명령어를 재실행한다.

 

!-이력상대순번

마지막 명령어로 부터 뒤로 두번째 명령어를 재호출할때  !-2  이렇게 사용하면 된다.

 

이전 명령어의 argument로 명령어 실행

사실 이건 알아도 자주 안써서 기억 할 수 없기 때문에 거 의 사용 하지 못한다..

Linux1_10
그 외 다음과 같다.

!^ 이전 명령어의 첫번째 argument이다.

!$ 이전 명령어의 마지막 argument이다.

!* 이전 명령어의 전체 argument가 있다.

다음과 같이 응용도 가능하다, 바로 이전의 인수가 아닌 특정 명령어의 인수를 사용할 수 있다.

$ ls !ls:2

 

‘![keyword]‘ 이전 명령어의 keyword로 명령어 실행

Linux1_11

 

!ls  ls로 시작되는 명령어중 가장 최근 명령어를 찾아서 재 실행한다. (IBM AIX 등의 korn셸에서는  r  ls  )

 

vi 모드

커맨드 라인 인터페이스에서 입력 모드를 vi 모드를 사용하면, 이전 명령어를 검색하여 좀 더 효율적인 히스토리 기능을 사용할 수 있다.

다음처럼 설정하여, 커맨드라인 에디트를 vi로 사용한다. 기본적으로 emacs로 설정되어 있다. ($HOME/.bash_profile 에 설정하면, 다음 로그인시에 기본으로 설정이 된다.)
$ set -o vi

$set -o 를 입력하면 전체 커맨드 라인 옵션이 나오는데 vi 모드가 on 되어 있는지 확인하자.

Linux1_33

 

다음은 vi 모드에서 커맨드 라인 사용 방법이다. (vi에 익숙한 사용자는 바로 알겠지만, vi 에디터를 모르더라도 다음 부분만 익히면 된다.)

ESC 키를 쳐서 명령어 모드(ex mode)로 진입한다. (vi 에디터와 동일)
$ /uname <엔터>

이후부터, n 키를 치면서 다음 명령어를 순차적으로 검색할 수 있다.

원하는 명령어를 찾고 나서 수정을 원하는 단어까지 w 키를치고 커서를 빠르게 이동하자

단어까지 이동하였으면 cw 입력하여 단어 편집 상태로 진입하여 타이핑하면 빠르게 커맨드 라인 입력을 할 수 있다.

(사실 저는 거의 vi모드로 대부분 커맨드 명령어를 처리합니다. )

 

Control-R

컨트롤키와 r 키를 동시에 입력하면, 검색어 문자 입력 대기 상태로 진입한다.

(reverse-i-search)”:

키워드를 입력할때마다 점진적으로 히스토리에서 검색한다. 다시 한번더 컨트롤 + r 키를 입력하면 순차적으로 그 이전의 명령어를 검색하여 보여준다. 원하는 명령어를 찾으면 바로 엔터를 입력 또는 명령어를 수정할때는 왼쪽 또는 오른쪽 방향기로 이동 후 수정한다.

Linux1_34

 

이상 히스토리 기능에 대해서 살펴 보았습니다. 이것 외에도 이전명령어 치환하는 기능 등이 있으나 개인적으로도 이정도 기능외는 잘 사용하지 않을뿐더러 사용하지 않으면 잊어버리기 때문에 무용지물이 되버리곤 합니다.  이상의 기능만 잘 활용하면 커맨드 라인에서 키보드 타이핑을 하는 고통을 일부나마 줄일수 있을것이라 생각합니다.

 

3. 특수문자 { }

커맨드 라인 인터페이스에서 콤마는 파일명을 나열하고 명령에게 인수로 전달한다.

touch 유틸리티는 파일의 접근, 수정 시간을 현재 시간이나 사용자가 명시한 시간으로 바꾼다. 파일이 존재하지 않을 경우에는 빈 파일을 생성한다.

Linux1_12

mkdir  유틸리티는 디렉토리 생성한다.

Linux1_13

 

다음과 같이 파일명 인수가 콤마 갯수 만큼 확장하고 명령어에게 전달하고 있음을 확인 할 수 있다.

echo 유틸리티는 셸이 전달하는 인수를 출력한다.

Linux1_14

 

4. 특수문자 ?

물음표 기호(?)는 셸에게 파일명을 생성케 하는 특수 문자다. 이 문자는 기존의 파일명에 있는 문자를 하나에 해당된다.

Linux1_15

5. 특수문자 *

별표(*)는 물음표와 비슷한 기능을 수행하지만, 어떤 개수의 문자열에라도 대응될 수 있다는 점이 다르다.

Linux1_16

 

6. 특수문자 [ ]

문자 주위를 둘러싼 괄호 기호 [ ]는 셸이 각각의 문자를 포함하는 파일명을 검색하게한다.

Linux1_17

 

7. 백그라운드에서의 프로그램 실행

지금까지의 예제에서 명령은 모두 포그라운드에서 실행됐다. 포그라운드에서 명령을 실행하면 셸은 명령의 실행이 끝날 때까지 기다렸다가 사용자에게 프롬프트를 내보낸다. 반면 백그라운드에서 명령을 실행하면 사용자는 이명령의 실행이 끝날 때까지 기다릴 필요 없이 바로 다른 명령을 실행할 수 있다.

작업(job)은 파이프로 연결 가능한 여러 개의 명령을 가리킨다. 포그라운드 작업의 경우에는 한 윈도우나 한 화면에서 하나의 작업만 실행할 수 있다. 그러나 백그라운드 작업의 경우에는 여러 개를 실행하는 것이 가능하다. 사용자는 한번에 하나 이상의 작업을 실행함으로써 리눅스의 중요한 기능 중 하나인 멀티태스킹(multitasking) 기능을 사용할 수 있다. 실행 시간이 길면서 작업 진행에 대한 별도의 감시가 필요치 않은 명령을 실행할 때는 백그라운드 실행 기능이 매우 유용하다.

 

백그라운드에서 명령을 실행하려면 커맨드 라인의 마지막에서 RETURN 키를 입력하기 전에 & 기호를 입력한다. 그러면 셸은 작업에 번호를 매기고 이 작업 번호를 괄호로 둘러싸서 출력한다. 작업번호 뒤에는 PID(Process Identification) 번호가 나온다. 명령  뒤에 나오는 [1]이라는 숫자는 셸이 이 작업에 대한 작업 번호를 1을 할당했다는 것을 의미한다.

백그라운드 작업의 실행이 끝나면 다음과 같은 메시지를 볼 수 있다.

[1]+ Done    program

Linux1_19

 

포그라운드에서 백그라운드로의 작업 이동

포그라운드 작업의 실행은 작업 보류 키, 일반적으로  CONTROL-Z 키를 입력함으로써 일시 중지할 수 있다. 셸은 프로세스를 멈추고 키보드로부터의 표준 입력을 중지시킨다. 이때 사용자가 bg 명령과 작업 번호를 입력하면 중지된 작업은 백그라운드로 옮겨서 다시 시작된다. 중지된 작업이 하나밖에 없다면 작업 번호를 입력할 필요가 없이 fg만 입력한다. 만약 백그라운드 작업이 하나 이상이라면 fg 인수로써 %번호를 입력해야한다.

Linux1_18

 

kill : 백그라운드 작업의 중단

인터럽트 키(보통은  CONTROL-C)로는 백그라운드에서 실행되는 프로세스를 중단할 수 없다. 이러한 경우에는 kill 명령어를 사용해야 한다. 커맨드 라인에 kill과 중단하려는 프로세스의 PID를 입력하거나 퍼센트 기호(%)와 작업 번호를 입력한다.

Linux1_20

 

여기서 잠깐!

kill 명령어 대해서 시그널에 대해서만 잠깐 언급을 하자면, 가끔 습관적으로 무조건 강제 종료 시그널(-9)를 날리는 분을 계신데.. Java 데몬이나 기타 서버를 종료 시킬때 무조건 강제 종료시키기 보다 서버에게 종료시 자원 정리 시간을 주어 안전하게 종료하게 하자.

 

1. SIGTERM(15) 시그널

: 프로세스에게 보내어 DB 접속 및 각종 아직 커밋이 되지 않은 정보를 안전하게 저장할 수 있도록 기회를 준다.  프로세스가 이 시그널에도 무 반응시에만 강제 시그널를 보낸다.

kill 13377  ( kill -15 13377  또는  kill -TERM 13377)

2. SIGHUP(1) 시그널

: 재 시작 시그널이다. ( kill -1 13377 또는 kill  -HUP  13377 )

3. SIGKILL (9)

: 묻지도 따지지도 않고(?) 무조건 강제 종료한다. (kill -9 13377)

 

8. 검색 명령

리눅스에서 명령을 입력하면 셸은 프로그램에서 사용하는 디렉토리를 검색해 처음 이름이 일치하는 프로그램을 실행한다. 이때 검색하는 디렉토리 목록을 검색 경로(search path)라고 한다. 검색 경로를 변경하는 방법은 PATH 환경변수를 변경하는 것이다. 사용자가 검색 경로를 변경하지 않으면 셸은 표준 디렉토리만 검색해 보고 검색 작업을 멈추게 된다. 물론 표준 디렉토리 외의 다른 디렉토리에도 유용한 유틸리티가 존재할 수 있다.

 

which /  whereis: 유틸리티의 위치 검색

which 유틸리티는 유틸리티 파일의 전체 경로를 보여줌으로써 유틸리티(명령어)의 위치를 알려준다. 셸은 검색 경로를 탐색해서 처음 나오는 명령을 실행한다. which 는 이런 경우 셸이 어떤 프로그램을 실행할지를 알려준다.

which유틸리티는 명령어가 예상 외의 동작을 수행할 경우 유용하다. which를 실행 해 보면 표준이 아닌 버전의 툴을 실행하고 있는지, 혹은 자신이 원하는 툴과는 전혀 다른 툴을 실행하고 있는지를 알 수 있게 될 것이다.

다음과 같이 which ls 명령어로 확인 해 보면 alias가 먼저 실행한다는것을 알 수 있다.

참고) 역슬래쉬(\)를 명령어 앞에 사용하면  alias 가 아닌 실제 명령어를 실행한다. ex)  \ls

Linux1_28

 

프로그램의 이름이 주어지면 which는 검색 경로에 있는 디렉토리를 차례대로 검색, 프로그램의 위치를 찾는다. 검색 경로에 해당 프로그램이 하나 이상 존재하는 경우에 which는 처음 나오는 명령에 대한 정보만 보여준다.

whereis 유틸리티는 표준 디렉토리(standard directory) 목록을 통해 파일을 검색하며, 검색 경로와는 독립적으로 작동한다. whereis는 사용자가 지정한 바이너리(실행)파일, 매뉴얼 페이지 및 프로그램 소스 코드의 위치 검색에 사용할 수 있는 데, 이 유틸리티는 which와 달리 모든 해당 파일에 대한 정보를 보여준다.

Linux1_29

 

apropos: 키워드 검색

특정 작업을 수행하는 데 필요한 명령을 알지 못한다면 apropos 유틸리티로 키워드를 검색하면 된다. (apropos를 제대로 실행하려면 makewhatis로 whatis 데이터베이스를 설정하고 주기적으로 관리해야 한다. 이 작업은 보통 cron으로 처리한다.) 이 유틸리티는 man 페이지의 맨 위에 나오는 짧은 설명줄(description line)에서 키워드를 검색해 일치하는 항목이 있는 명령만 보여준다. man 유틸리티를 -k(keyword) 옵션과 함께 사용하면 apropos를 실행한 것과 동일한 결과를 볼 수 있다.

다음 예제는 apropos를 who 키워드와 함계 호출한 결과를 보여준다. 이 키워드와 관련된 다른 유용한 유틸리도 찾아서 보여준다.

Linux1_30

 

locate: 파일 검색

locate유틸리티는 로컬 시스템에서 파일을 검색하는 작업을 수행한다. locate는 updatedb 유틸리티로 생성/업데이트한 locate 데이터베이스가 있어야만 정확하게 검색할 수 있따. 일반적으로 이 데이터베이스는 cron 스크립트로 하루에 한 번씩 업데이트된다.

Linux1_31

 

grep: 파일명 검색 또는 파일내 문자열 검색

which / whereis / apropos / locate 등은 유틸리티 검색 하는데 사용되지만, grep 유틸리티는 하나 이상의 파일에서 행 단위로 간단한 문자열이나 정규 표현식으로 표현된 pattern을 찾는다.

-r 옵션은 하위디렉토까지 모두 검색한다.

-l 옵션은 파일내의 텍스트와 패턴이 매치되는 텍스트를 출력하지 말고 매칭된 파일 목록만 출력한다.

-L 옵션은 -l 옵션과 반대로 비 매칭된 파일 목록을 출력한다.

Linux1_32

 

9. 접근 권한

 

접근 권한

파일에 접근할 수 있는 사용자 유형은 다음 3가지로 구분할 수 있다.

- 파일의 소유자(owner)

- 파일 소유자가 속해 있는 그룹 멤버(group)

- 그 밖의 모든 사람(other)

각 사용자별 읽기, 쓰기, 실행 의 파일 접근 권한을 설정 할 수 있으므로 모두 9가지 방법이 있다는 것을 의미한다.

Linux1_21

 

ls -l 권한 보기

첫 번째 문자 하이픈(-)으로 표기되어 있다. 이 파일은 일반 파일이기 때문이다. (디렉토리는 d가 표시된다.)

그 다음에 오는 문자 9개는 각 사용자별 owner, group, other 에 읽기(r), 쓰기(w), 실행(x) 권한이 부여된다. (- 문자는 권한이 없음을 나타낸다.)

Linux1_23

 

접근 권한 변경은 두가지 심볼릭과 절대값으로 변경이 가능하다.

 

접근권한 변경: 심볼릭

심볼릭 모드: 사용자 종류

Linux1_24

심볼릭 모드: 연산자

Linux1_25

심볼릭 모드: 퍼미션

Linux1_26

파일 소유자는 어떤 사용자에게 파일 접근 권한을 줄지, 그리고 어떤 접근 권한을 줄지를 결정해서 설정할 수 있다. 파일 소유자는 chmod(change mode) 유틸리티를 사용해서 파일의 접근 권한을 변경할 수 있다.

심볼릭 접근 권한 변경 예)  chmod a+rw letter

의미:  모든(a) 사용자에게 읽고 쓰는 권한(rw)을 부여(+)하라.

다음은 접근 권한의 변경 유틸리티 chmod 로 심볼릭 모드 사용 예이다.

Linux1_22

 

접근권한 변경: 절대값

8진수를 사용하여 접근 모드를 설정할 수 있다.

절대값 접근 권한 변경 예) chmod 755 letter

의미: 소유자는 모든 권한을 그외 그룹과 다른 사용자는 읽기와 실행 권한을 부여하라

다음은 접근 권한의 변경 유틸리티 chmod 로 절대값 모드 사용 예이다.

참고1) 셸 스크립트의 경우, 읽기와 실행 퍼미션이 있어야 실행할 수 있지만 바이너리 파일은 실행 퍼미션만 있어도 실행할 수 있다.

참고2) 디렉토리 경우 실행 퍼미션이란?  디렉토리 안으로 진입 가능 여부를 뜻한다.

Linux1_27

 

10. yum : 시스템을 최신 버전으로 유지하기

 

패키지 갱신과 설치

리눅스 초기 릴리즈에는 갱신을 관리하는 툴이 없었다. RPM  툴로 개별 소프트웨어 패키지를 설치하거나 갱신할 수 있었지만, 패키지와 그 패키지와 종속 관계를 갖는 패키지를 찾는 일은 전적으로 사용자의 몫이었다.

Terra Soft, 이 회사는 자동으로 패키지를 찾고 종속 관계를 검사하는 Yellow Dog Updater를 만들었다. 이 후 다른 배포판에 포팅되었다. Yellow dog Updater, Modified ( Yum ) 는 RedHat 또는 CentOS 리눅스의 기본 패키지 관리자 이다.

 

패키지 저장소 환경파일

환경파일 : /etc/yum.conf

 

업데이트

시스템 유틸리티이므로 루트 사용자에서 실행을 해야한다. update 옵션은 설치된 모든 패키지를 갱신한다. 설치된 패키지의 패키지 헤더를 내려받은 후 계속 진행할 것인지 묻고, 갱신된 패키지를 내려받아 설치한다.

$ yum update

 

패키지 설치

새로운 패키지와 그것과 종속 관계를 가지는 패키지를 설치하려면, yum install 명령어 다음에 패키지 이름을 붙인다.

$ yum  install  telnet

 

패키지 삭제

비숫한 구문을 사용하여 yum으로 패키지를 삭제 할 수 있다.

$ yum  remove  telnet

 

11. 설치하고 싶은 유틸리티 찾기

자신이 설치하고 싶은 어플리케이션에 대해서 패키지명을 모르면 설치를 할 수 없다. 또한 자신이 원하는 유틸리티가 어느 패키지에 속해 있는지 알 수 없을때, 다음과 같이 검색하여 찾아 볼 수 있다.

1. 먼저 패키지 목록 조회를 하라.

  • # yum  list  vimtutor

-Error: No matching Packages to list

 

2. 유틸리티 이름으로 검색을 하라.

  • # yum  search  vimtutor

-No Matches found

 

3. 패키지 내에 서 제공하는 파일명을 검색 하라.

  • # yum  provides  vimtutor

-You can use “*/vimtutor” and/or “*bin/vimtutor” to get that behaviour

-No Matches found

 

4. 위에서 제시한 방법대로패키지 서브 디렉토리 까지 검색하라

  • # yum  provide  */vimtutor

-vim-enhanced-7.2.xxxxxxx

 

위와같이, 출력이 되었다면 찾기에 성공한것이다. 그럼 이것을 설치해야 하는데, 다음처럼 패키지명(버전 문자 이전까지)만 입력하면 대부분 설치가 가능하다.

$ yum  install  vim-enhanced

설치가 성공적으로 끝나면 vimtutor 를 실행하여 제대로 설치되었는지 확인 해보자.

 

5. 위 4가지 방법으로 못 찾으면 인터넷 검색을 적극 활용하여 해결의 실마리를 찾도록 한다.

 

이상으로 간단하게 정리하고 마무리를 하고자 합니다. 실무에서 가장 빈번하게 쓰이는 커맨드 유틸리티를 중점적으로 알아보았고, 백그라운드 프로세스 관리와 파일 접근 권한 관리에 대해서 알아 보았습니다. 또한 명령어 검색 및 소스파일내 문자열 검색 까지 살펴 보았고요, 마지막으로 패키지 유지 관리까지 정리해 보았습니다.

제가 프로젝트에서도 이 정도 사용있으며 그렇게 큰 불편없이 잘 사용하고 있습니다. 그외 간단한 셸 스크립트 정도만 익혀두시면, 정기적인 백업 스크립트를 만들어 스케줄에 등록하여 사용 할 수도 있어 활용범위가 넓어 질것입니다. 끝까지 읽어 주셔셔 감사합니다.

 

참조 도서 및 사이트


Posted by 조신부리
,

함수(function) 다시 보기

js 2014. 11. 3. 10:27

자바스크립트는 한동안 개발자들의 많은 오해와 편견으로 toy language 취급을 받아 왔습니다. 누구든지 쉽게 배워 간단히 적용할 수 있다는 생각에 깊이 있는 학습이 이뤄지지 않았습니다. 하지만, 현대의 웹 애플리케이션이 시대적 요구와 사용자의 기대로 인해 점점 더 복잡한 대규모 시스템으로 발전해 나가면서 자바스크립트에 대한 관심이 크게 늘고 있습니다. 이에 자바스크립트에 대한 올바른 이해를 위해 자바스크립트에서 가장 중요한 주제인 함수에 대해 간략히 설명하겠습니다.

대부분 자바스크립트에서 함수를 설명할 때 “자바스크립트에서 함수는 first-class object(또는 citizen, value)다”라는 정의는 항상 빠지지 않고 등장하는 단골문장입니다. 하지만, first-class object에 대한 정확한 언급이 없어 그냥 흘려 듣기 쉽습니다.

그렇다면 first-class object란 무엇일까요?

  • first-class object는 변수에 저장할 수 있어야 합니다.
  • first-class object는 함수의 파라미터로 전달할 수 있어야 합니다.
  • first-class object는 함수의 반환값으로 사용할 수 있어야 합니다.
  • first-class object는 자료 구조에 저장할 수 있어야 합니다.

위와 같은 조건들을 충족시키는 객체를 first-class object라 부릅니다. Java에서 메소드는 위 조건들을 충족시키지 못하기 때문에 first-class citizen으로 취급되지 않습니다.

자바스크립트에서 함수는 first-class object다.

또한, 함수는 변수의 스코프를 결정하고 private 변수 또는 메소드 뿐만 아니라 함수의 특징을 이용하여 public 속성과 메소드를 제공하며 자바스크립트 모듈을 작성하는 좋은 도구이기도 합니다.

함수에 대한 올바르고 정확한 이해는 자바스크립트를 이해하는 데 있어 핵심 중의 하나이며 대규모 웹 애플리케이션이나 Single Page Applications(SPAs)을 개발하는데 있어 가장 중요한 개념이 됩니다.

 

함수와 익명함수

자바스크립트에서 함수를 정의하는 방법은 Function 객체를 사용하는 방법과 연산자인 function을 사용하는 방법이 있습니다.  일반적으로 Function 객체를 사용한 정의 방법은 많이 사용되지는 않습니다. 연산자인 function을 이용한 함수 정의 방식은 함수선언문(function declaration)과 함수표현식(function expression)으로 나뉩니다.

우리는 그간 아래와 같이 함수를 정의하고 사용해 왔습니다.

 이와 같은 방식을 함수선언식(function declaration)이라고 합니다. 함수선언식으로 정의된 함수는 자바스크립트 인터프리터가 스크립트가 로딩되는 시점에 바로 초기화하고 이를 VO(variable object)에 저장합니다.  그렇기 때문에 함수 선언의 위치와는 상관없이 소스 내 어느 곳에서든지 호출이 가능합니다.

함수 정의할 때 “함수는 first-class object이므로 변수에 할당될 수 있다.” 라는 전제 하에 아래와 같이 작성할 수 있습니다.

이렇게 정의한 방식을 함수표현식(function expression)이라고 합니다. 함수가 변수에 할당되었으므로 “함수는 객체이다.”라는 정의가 가능합니다. 함수표현식은 함수선언식과는 달리 스크립트 로딩 시점에 VO에 함수를 저장하지 않고 runtime시에 해석되고 실행되므로 이 두가지를 구분하는 것은 중요합니다.

함수선언식으로 함수를 정의하면 사용하기에 쉽지만 대규모 애플리케이션을 개발하는 경우 인터프리터가 너무 많은 코드를 VO에 저장하므로 애플리케이션의 응답속도는 현저히 떨어질 수 있으므로 주의해야 할 필요가 있습니다. 참고로, 스크립트 파일을 모듈화하고 이를 필요한 시점에 비동기 방식으로 로딩하여 http 요청을 줄이고 응답속도와 사용자 체감속도를 향상시킬 수 있습니다.

 

즉시실행함수(Immediately-invoked function expression)

자바스크립트에서 가장 큰 문제점 중의 하나는 글로벌 스코프에 정의된 것은 코드 내의 어디서든지 접근이 가능하다는 것입니다. 하지만, 외부에 공유되면 안되거나 공유될 필요가 없는 속성이나 메소드가 있습니다. 또한, 다른 스크립트 파일 내에서 동일한 이름으로 명명된 변수나 함수가 있을 경우 원치 않는 결과를 가져올 수 있습니다.

익명 함수표현식으로 함수를 하나 정의하고 실행해 보겠습니다. 그리고 외부에서 함수 내의 변수에 접근해 보겠습니다.

 이번에는 익명 즉시실행함수로 함수를 정의해 보겠습니다.

위 두개의 코드는 동일한 동작을 수행합니다.

함수표현식은 함수를 정의하고, 변수에 함수를 저장하고 실행하는 일련의 과정을 거칩니다.  하지만, 즉시실행함수를 사용하면 이와 같은 과정을 거치지 않고 즉시 실행된다는 특징이 있습니다. 차이점이라면 단순히 함수를 괄호”()”로 랩핑한 게 전부입니다.  이런 함수를 즉시실행함수(IIFE)라 부릅니다.

이번에는 변수를 선언하고 이 변수에 즉시실행함수를 할당해 보겠습니다.

콘솔에는 “private” 라고 출력됩니다. 

즉시실행함수 내에서 선언한 변수를 외부에서도 접근할 수 있음을 확인할 수 있습니다. 변수의 접근 범위가 함수 내부가 아닌 외부에서도 가능해진 것입니다.  이와 같이, 즉시실행함수는 변수의 스코프를 포함하는데 사용되며 외부에서 함수 내의 변수에 접근할 경우 이를 통제할 수 있습니다. 즉시실행함수는 글로벌 네임스페이스에 변수를 추가하지 않아도 되기 때문에 코드 충돌이 없이 구현할 수 있어 플러그인이나 라이브러리 등을 만들 때 많이 사용됩니다.

아래 두개의 코드는 기명 함수표현식과 즉시실행함수에서 파라미터를 전달하는 방법을 보여줍니다.

위 두개의 코드 블럭은 동일한 동작을 수행합니다. 또한, 앞서의 예제처럼 괄호”()”로 랩핑한 차이 밖에 없습니다.

jQuery나 Prototype 라이브러리는 동일한 $라는 글로벌 변수를 사용합니다. 만약, 이 두개의 라이브러리를 같이 사용한다면 $ 변수에 대한 충돌이 생길 것입니다. 하지만, 즉시실행함수의 코드 블럭에서 jQuery를 위한 $ 변수를 사용하고자 한다면 아래와 같이 파라미터를 전달하는 방법으로 Prototype의 $ 변수에 대한 overwritting을 예방할 수 있습니다.

 

모듈 패턴(Module Pattern)

현대의 웹 애플리케이션은 점점 더 복잡하고 고도화된 대규모 애플리케이션이나 데스크탑 애플리케이션의 모습을 닮아가는 형태(Rich Internet Application)로 진화하고 있는 추세입니다. 하나의 파일에 모든 코드를 담는 것은 불가능하고 설사 그렇게 작성되었다 하더라도 많은 문제점을 내포할 뿐만 아니라 유지보수시 골치 아픈 경험을 자주 하게될 것입니다. 그렇다면 이를 어떻게 극복할 수 있을까요? 그에 대한 답은 자바스크립트 함수의 특징을 이용한 모듈화에서 부터 찾을 수 있습니다.

Java나 C++과 같은 고급언어들은 언어 자체적으로 모듈화를 지원하는 방법을 제공하지만 자바스크립트는 언어레벨에서 캡슐화를 위한 접근제어자(private, public 등), 모듈 간의 구분을 위한 package가 명시적으로 제공되지 않습니다. 하지만, 명시적으로 제공되지 않는 이런 지원도구들을 자바스크립트 함수의 특징을 이용하여 유사하게 제공할 수 있습니다.

즉시실행함수는 우리가 작성한 코드들 뿐만 아니라 함께 사용하는 외부 라이브러리와도 충돌없이 구동하는 샌드박스(sandbox)를 제공합니다. 이 특징과 단위기능별로 작성된 코드를 분리된 개별 파일 형태로 유지한다면 앞서 언급한 모듈화를 위한 조건을 해결할 수 있습니다.

위에 작성된 즉시실행함수는 name, sex, position속성과 payBonus메소드를 가진 객체를 clerk변수에 반환합니다. 다시 말해, 즉시실행함수가 clerk변수에 저장되는 것이 아니라 즉시실행함수의 반환값이 clerk변수에 저장됩니다. name, sex, position변수를 글로벌 스코프에 추가하지 않고 단지 clerk변수만 추가되었습니다. 애플리케이션의 규모가 커지면 글로벌 변수에 대한 충돌을 고려해야 하므로 글로벌 변수 사용을 자제해야 합니다.
또한, name, sex, position속성은 clerk변수에 저장된 객체의 속성이므로 외부에서 접근이 가능(public)하지만 salary, totalBonus변수는 즉시실행함수 내의 변수이므로 외부에서 접근할 수 없습니다(private). paySalary()메소드를 호출하여 지급된 급여인 1800을 반환값을 가져올 수 있고 payBonus()메소드를 호출하여 지급된 상여금인 90을 반환할 수 있으며 payBosnus()메소드를 한번 더 호출하여 80을 반환할 수 있습니다. 여기서 주목해야 할 부분은 payBonus()메소드를 호출할 때마다 totalBonus 변수에 저장된 값이 업데이트 되지만 paySalary()메소드는 여러번 호출해도 salary 변수에 저장된 값은 업데이트 되지 않는 점입니다.

즉시실행함수를 위와 같이 사용하여 언어레벨에서 제공하지 못하는 모듈화 지원도구를 극복할 수 있으며 이렇게 작성된 코드를 분리된 파일로 구성하면 재사용성을 높일 수 있습니다.

자바스크립트 모듈 작성시 코드 순서

Javascript로 SPA를 구현할  때, 다음과 같은 순서의 코드로 모듈을 작성하게 되면 협업하는 개발자들에게 집약되고 일관된 코드를 제공하여 많은 도움이 될 것입니다. 이렇게 코드의 순서를 정하는 이유는 집약되고 일관된 코드를 제공하는데 있으므로 코드의 순서는 개발상황과 모듈용도에 맞춰가며 조정하고 추가 및 삭제될 수도 있습니다. 아래 순서는 단지 코드작성 순서의 예일뿐입니다.

  1. 모듈 스코프 내에서 사용할 변수 작성
  2. 유틸리티 메소드 작성
  3. DOM 조작 메소드 작성
  4. 이벤트 핸들러 작성
  5. Public 메소드 작성

아래 코드는 라이브러리를 모듈화하는 코딩기법을 정리해 봤습니다. jQuery, Backbone, underscore, requireJs 등 많은 자바스크립트 라이브러리나 프레임워크가 아래와 같은 코딩기법을 사용하고 있습니다.

 

맺음말

현대의 웹 애플리케이션은 시대와 환경의 요구로 인해 대규모 형태로 개발되고 있으며 데스크탑 애플리케이션을 닮아가는 추세이다. 자바스크립트로 이런 웹 애플리케이션을 개발하기 위해서는 자바스크립트 함수에 대한 올바른 이해가 뒷바침되어야 합니다.

특히, 즉시실행함수를 이용한 변수에 대한 스코프, 글로벌 네임스페이스 오염 방지, 언어레벨에서 명시적으로 지원하지 않는 캡슐화 도구를 모듈패턴으로 극복하는 방법 그리고 다른 라이브러리와 충돌없이 우리의 소스를 유지하는 방법은 꼭 이해해야  합니다.

다음에는 클로져(Closure)에 대한 내용이나 scope chain, prototype chain에 대해 올려볼 계획입니다.

http://www.nextree.co.kr/p4150/

 

Posted by 조신부리
,