0%

아파치(Apache) 웹서버 윈도우10 환경에 설치

windows10 64bit 운영체제에 아파치 웹서버를 설치하는 방법을 설명하겠다.


1. 웹 브라우저를 통해 https://www.apachelounge.com/download/ 로 이동 >![](/images/2017-06-11-apache설치_1.png) 원하는 아파치 웹서버 버전을 선택후 64bit zip 파일을 클릭하여 다운받는다. >![](/images/2017-06-11-apache설치_2.png)
  1. 해당 파일을 C:\ 경로로 이동후 압축을 푼다.


    폴더 내부로 이동하여 Apache24 폴더를 잘라내기 한 후 다시 C:\ 경로로 이동하여 붙여넣기 한다.

최종적으로 아래와 같이 폴더 구성이 되면 된다.

  1. 아파치 웹서버 설치전 환경 설정

C:\Apache24\conf 경로로 이동하여 httpd.conf 파일을 수정해야 한다.
파일을 메모장으로 연뒤 원하는 항목을 수정하면 되는데 여러 항목중에 다음 항목들이 중요하다.

기본셋팅
ServerRoot “c:/Apache24”
Listen 80
DocumentRoot “c:/Apache24/htdocs”

ServerRoot 항목은 기본적으로 “c:/Apache24” (경로 구분을 역슬래시로 해야 한다.) 되어 있다. 이 디폴트 셋팅을 그대로 사용하기 위해서 2번처럼 폴더 경로를 지정한 것이다. 만약 위 설명과 다른 폴더경로에 웹서버 폴더를 셋팅 했다면 ServerRoot 항목의 셋팅을 알맞게 수정해 주면 된다.

Listen 항목은 웹서버의 포트(port)설정이다. 웹서버는 기본적으로 80포트이다.(디폴트라 생략이 가능하다. www.naver.comwww.naver.com:80 은 같다)
원하는 포트가 있다면 역시 수정하라. (필자는 8080으로 수정하였다. )

DocumentRoot 항목은 웹서버에서 실행할 웹사이트 root 폴더를 지정해야 한다. ServerRoot처럼 디폴트로 “c:/Apache24/htdocs”로 셋팅 되어 있다.
원하는 경로가 있다면 수정하라.
부연설명으로 웹서버 기본 url(현재는 http://localhost:80) 접근시 웹서버는 DocumentRoot 에 설정된 폴더 내부의 index.html 를 디폴트로 리다이렉트 한다.

  1. 설치
    관리자 모드로 cmd(명령 프롬프트) 창을 연다.
    키보드의 윈도우 버튼을 누른뒤 cmd라고 검색하면 나온다.
    명령 프롬프트 항목에 마우스 우클릭 한 뒤 “관리자 권한으로 실행”을 선택한다.

cmd 창이 열리면 cd C:\Apache243\bin 을 입력하여 경로를 이동한다.

이동한뒤 아래 명령어를 입력하여 아파치 서버를 설치한다.
httpd.exe -k install

뭔가 error라고 나오긴 하는데 무시해도 지장 없다.(도메인 관련 error 인것 같다)

만약 설치한 아파치 서버를 삭제 하고 싶다면 아래 명령어를 입력하면 된다.
httpd.exe -k uninstall

  1. httpd.exe -k install 명령어 입력 후

윈도우 탐색기에서 C:\Apache24\bin 경로로 이동후 AapcheMonitor 응용 프로그램을 실행한다.

윈도우의 오른쪽 하단 작업 표시줄을 보면 ApacheMonitor가 실핼된 것을 확인 할수 있다.

해당 아이콘을 우클릭후 Apache2.4 > Start 를 클릭하면 웹서버가 동작한다.
반대로 웹서버를 종료하고 싶다면 Stop 을 클릭하면 된다.

  1. 웹서버 동작 확인

웹서버를 start 한뒤에 인터넷 브라우저 창을 열고 주소창에

1
http://localhost:설정한포트번호

또는

http://127.0.0.1:설정한포트번호 

를 입력한다.

필자는 http.conf 파일에서 포트를 8080 으로 수정하였으므로

http://localhost:8080 을 입력하겠다.

위처럼 DocumentRoot 경로의 index.html 브라우저창에 열리는 것을 확인할 수 있다.
윈도우 관리도구등 기타 설명은 상단의 유투브 영상을 참고하길 바란다.

REST(REpresentational State Transfer: 표현 상태 전송)는 웹 아키텍처 스타일이다.

HTTP 스펙 설계에 참여했던 로이필딩이라는 사람이 2000년대 발표한 네트워크(웹) 아키텍처 스타일이다.
여기서 네트워크 아키텍처 스타일이란 한가지의 웹 아키텍쳐 가 아니라 여러 가지 복수의 아키텍쳐의 공통된 성질, 양식 규정 혹은 독특항 방식의 집합을 네트워크 아키텍쳐 스타일이라고 한다.
REST를 네트워크 아키텍쳐 스타일이라고 하지만, 사실 HTTP 기반위에 존재하는 아키텍쳐 스타일이므로 웹 아키텍처 스타일이라고 부르는 것도 틀린 표현은 아닌 것 같다.

RESTful은 어떤 대상이 REST 의 제약에 따르고 있고, 그래서 상당히 REST스럽다? REST답다 라는 형용사적 의미이다.

예를 들어 어떤 서버 A가 존재 한다고 하자. 이 A서버는 REST라는 아키텍츠 스타일의 제약을 충실히 따르고 있다면, 이 A 서버를 RESTful 한 서버라 부를 수 있다.

REST의 의미

REST는 수많은 아키텍처 스타일 중에서도, 특히 네트워크 시스템의 아키텍처 스타일 이다.
그리고 REST는 웹의 대표적인 아키택처 스타일인 클라이언트/서버를 기반으로 하는 복합 아키텍처 스타일이다.
, 즉 로이필딩은 REST 기반으로 네트워크를 설계하면 효과적이라고 생각했다.

REST = 클라이언트/서버 + 스테이트리스 서버(Stateless Server) + 캐시(cache) + 유니폼 인터페이스(uniform Interface) + 계층화 시스템 + 코드 온 디맨

클라이언트/서버
말그대로 클라이언트 와 서버로 구성되어 있으며 클라이언트에서 request하면 서버에서 response한다.

스테이트리스 서버
클라이언트의 어플리케이션 상태를 서버에서 관리하지 않는다는 의미이다. 서버가 어플리케이션의 상태를 가지지 않게되면, 그만큼 서버 측의 구현이 갈결해지는 장점이 있다.
하지만 현실적으로 스테이트리스가 아닌 웹 서비스와 웹 API가 많이 사용된다. 특히 HTTP를 스테이트풀 하게 만드는 대표적인 것은 Cookie를 사용한 세션관리이다.
REST의 관점에서 본다면, Cookie를 사용한 세션관리는 HTTP의 잘못된 확장이다. 다만 REST의 기준으로 잘못되었다고 해서, Cookie를 사용한 폼 인증을 그만둘 수 없는 것도 현실이다. Cookie는 스테이트리스 서버의 이점을 버린다는 것을 이해한 후 최소한으로 이용 해야 한다.

캐시
리소스의 신선도에 기초해, 한번 가져온 리소스를 클라이언트 쪽에서 돌려쓰는 방식.

유니폼 인터페이스 (리소스, http 메소드, 메세지)
URI로 지정한 리소스에 대한 조작을 통일되고 한정적인 인터페이스로 수행하는 아키텍처 스타일을 말한다.
예를 들어 HTTP1.1 에서 GET, POST등 8개의 메소드만 정의되어 있고, 보통은 이 이상 늘어나지 않는다. 어떻게 보면 확장성이 떨어진다고 보여지지만 이런 제약이 현재 웹의 통일된 인터페이스를 만들어 낸 것이다.

REST에서는 모든 것을 명사형 리소스로 표현하며, 의미가 명확한 한정된 HTTP 메소드를 사요하고, 메세지를 담아서 통신한다.
아래는 대표적으로 사용하는 메소드 4개이다.

메소드명기능
getsearch
postcreate
putupdate
deletedelete

아래는 실제 REST 메세지의 표현 형테이다.
http://somthingweb/user/ 주소(리소스 url)에 POST(CREATE)해라 rocky라는 이름으 user를 이라는 의미를 갖는 메세지이다.

1
2
3
4
5
6
HTTP POST , http://somthingweb/user/
{
"user":{
"name":"rocky"
}
}

계층화 시스템

유니폼 인터페이스로 인해 발생되는 이점중 하나이다. 통일된 인터페이스로 인해 시스템 전체를 계층화 하기 쉽다. 프록시 서버, 로드벨런서 등등을 http 인터페이스로 통일하여 계층화 하여 사용 할수 있다.

코드 온 디맨드(code on demand)

프로그램 코드를 서버에서 다운 받아 클라이언트에서 실행하는 아키텍처 스타일이다.
처음에 이 개념은 서버에서 발생하는 부하를 클라이언트쪽으로 부담시켜 서버의 부하를 낮추것이 목적이었다.

예를 들어 javascript나 flash, java 애플릿 등이 여기에 해당한다.
반대로 jsp, asp 같이 백엔드에서 페이지를 렌더링해서 클라이언트에 보내는 경우, 수많은 화면 페이지들은 *.jsp 또는 *.asp 이런 확장자를 가지게 된다. 이는 화면단 소스가 백엔드 기술에 종속된다는 것을 의미한다.
jsp, asp 이런 기술들은 백엔드에 프론트엔드가 기술적으로 종속된다는 단점이 있지만, 이런 단점을 무색하게 할 정도로 강력한 기능을 제공해 왔다.

하지만 근래 javascript의 눈부신 발달, jquery, ajax 기타 수많은 블라블라.js 등으로 인해 이런 백엔드 서버페이지 기술, jsp, asp이런 것들이 제공하는 기술들을 대체하는 시대가 되었다. 본래의 서버의 부하를 낮추는 기능 뿐만 아니라 프론트 엔드가 서버에 독립적으로 동작할수 있는 특성이 생겨버렸다.

요즘의 REST, RESTful 하다라는 말이 유행하게 된 가장 큰 이유는 발전된 javascript, jquery, ajax를 통해 백엔드에 독립적인 프론트엔드 개발이 가능한 환경 즉 코드 온 디멘드가 가능한 환경이 되었기 때문이라고 생각된다.
특히 ajax통신은 http + xml or json 형태의 통신형태로 인해 REST가 지향하는 유니폼 인터페이스를 구현하는 기반이 되었다고 생각된다.

jquery, ajax를 이용하여 프론트엔드는 백엔드에 종속되지 않고 백엔드 시스템과 통신할수 있다.
이런 시점에서 적절한 웹 아키텍처가 필요했고 로이필딩이 2000년대 논문을 통해 발표한 REST라는 웹 아키텍처 스타일이 트랜드상 선호되는것 같다.

클로저(closure)의 사용시 주의사항

클로저는 그 기묘한 특성?상 다양한 활용이 가능하지만, 무분별하게 사용시 성능문제나 코드의 난독화가 발생한다고 한다.
이번글에서는 클로저 사용시 결과 예측을 실수 할수있는 ,즉 헷갈리는 클로저 예제 소스를 통해서 주의할 점을 알아보도록 하겠다.

1.클로저를 선언하는 외부함수의 유효범위 스코프는 클로저에 의해 변경이 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
function outerFunc(argNum){
var num = argNum;
return function(x){
num += x;
console.log('num:' + num);
};

}

var exam = outerFunc(40);
exam(5);
exam(-10);

결과

  • num:45
  • num:35

사실 주의할 점 까지는 아니지만, 클로저의 가장 중요한 개념이다.
클로저를 처음 접할때 가장 햇갈리는 부분이 이부분일 것이다.

클로저는 살아있다(alive)! 이 말이 무엇이냐면, outerFunc(40); 로 외부함수는 호출되고 종료되었지만, exam 이라는 변수에 클로저가 생성되고 할당 되었다.
이 클로저 exam 이 존재 하기 때문에 자바스크립트 엔진은 exam 이 생성될 때, 접근할 수 있는 영역에 대한 부분을 메모리에서 지우지 않고 보존하고 있다. 즉 exam 은 자신을 생성한 outerFunc의 살아있는 유효범위 스코프에 접근하여 값 접근 및 할당이 가능하다.

  1. 하나의 클로저가 여러 함수 객체의 스코프 체인에 들어가 있는 경우도 있다.
1
2
3
4
5
6
7
8
9
10
11
function func(){
var x = 1;
return {
func1 : function(){ console.log(++x); },
func2 : function(){ console.log(-x); }
};
}

var exam = func();
exam.func1();
exam.func2();

결과

  • 2
  • -2

이 예제도 그렇게 어려운 예제는 아닐 것이다.
func()함수가 호출되며 하나의 객체를 리턴하고 그 객체는 func1, func2 두개의 메소드를 가지고 있다.
여기서 헷갈릴 가능성이 있는 부분은 리턴한 객체 exam 의 func1과 func2이 각각 다른 x에 접근 할 것이라 생각 할 수도 있다.
결과를 확인하면 exam.func1, 과 exam.func2과 동일한 x를 접근 하는 것을 확인 할 수 있다.

func1과 func2는 [동일한 함수 호출]에 의해 만들어 졌기 때문에 같은 x[동일한 함수 호출의 유효범위 스코프]에 접근 하는 것이다.

2번 예제를 통해 한가지 더 모호한 점은 클로저라는 어떤 대상의 구분이다.

한개의 클로저의 범위를 정의 한다면, 한개의 외부함수의 유효범위 스코프와 그 유효범위 스코프를 공유하는 내부함수(1개 이상)라고 볼 수 있을것 같다.

3.클로저를 정의하는 함수 내에서 루프를 이용하는 경우

1
2
3
4
5
6
7
8
9
10
11
12
function constfuncs(){
var funcs = [];
for(var i =0; i < 10; i++){
funcs[i] = function() { return i;}
}
return funcs;
}

var funcs = constfuncs();
funcs[0]();
funcs[4]();
funcs[9]();

결과

  • 10
  • 10
  • 10

결과를 0, 4, 9 가 리턴 될 것이라 예측 할 수도 있다. (필자 역시…)
여기서 중요한 점은 클로저와 연관된 유효범위 체인이 ‘살아있다’라는 것이다.
중첩함수의 유효범위에 대한 내부 사본이나 변수 바인딩의 스냅샷은 만들어지지 않는다.
이미 funcs0 이 호출되는 시점에는 자신을 생성할 때 호출된 constFuncs 함수의 var i 는 for문이 완벽하게 동작하고 종료되었기 때문에 10인 것이다.

  1. this와 arguments 사용시 주의점

이 부분은 클로저 사용시 주의사항이라기 보다는 내부함수(중첩함수) 사용시 공통적으로 주의해야 할 부분이다.

모든 함수 호출에는 this값이 있고, 외부 함수가 this 값을 별도로 변수로 저장하지 않으면 클로저는 외부 함수의 this값에 접근 할 수 없다.
arguments는 키워드는 아니지만, 모든 함수 호출에 자동으로 선언된다. 클로저 함수는 자신만의 arguments를 가지고 있기 때문에, 외부 함수가 argumengs을 다른 이름의 변수에 저장하지 않는 한, 클로저는 외부 함수의 arguments에 접근 할 수없다.

1
2
3
4
5
6
7
8
9
10
11
12
13
function outerFunc1(){

console.dir(this);
console.dir(arguments);

return function(){
"use strict"; // this가 Window 전역객체로 할당 되는것을 강제로 막았다.
console.dir(this);
console.dir(arguments );
};
}

var test1 = outerFunc1(111, '가나다');

1
test1();

리턴된 클로저를 호출시 this 는 undefined(use strict를 통해 강제로 막았기 때문에) 그리고 arguments객체는 존재는 하지만 클로저 자신이 호출될때 생성된 arguments이기 때문에 외부함수 outerFunc1의 arguments에는 접근 할 수 없다.

해결방법 외부함수의 this와 arguments 객체를 외부함수 지역변수에 할당하자!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function outerFunc2(){

console.dir(this);
console.dir(arguments);

var outThis = this; // this를 클로저가 접근할 수 있도록 지역변수에 할당한다.
var outArgu = arguments; // arguments를 클로저가 접근할 수 있도록 지역변수에 할당한다.

return function(){
"use strict";
console.dir(outThis);
console.dir(outArgu );
};
}

var test2 = outerFunc2(222, 'abc');

1
test2();

외부함수의 this 와 arguments 를 지역변수에 할당하여 클로저가 접근할 수 있도록 할 수 있다.

클로저(closure)의 활용

본 글은 독자가 자바스크립트 클로저의 개념에 대한 이해를 전제하에 작성된 글이다.

자바스크립트의 클로저의 개념에 대해서는 공부를 했지만, 막상 이 클로저라는 것을 활용해 보려니 도통 감이 오질 않는다.
클로저를 활용한다는 말인 즉, 클로저를 통해서 어떤 프로그래밍 적 효과를 발생시키고 그 효과를 활용 한다는 말일 것이다.

그렇다면 간단한 예제를 통해 그 효과와 그 효과를 활용하는 방법을 알아보자.
(클로저는 성능적인 면과 자원적인 면에서 약간 손해를 볼수 있으므로 무차별적으로 사용해서는 안된다고 한다.)

활용1 - 함수 캡슐화

설명 할 클로저는 함수형 프로그래밍 기법이지만, 캡슐화를 구현할 수 있다.
캡슐화란 객체지향 프로그래밍의 하나의 개념 중 하나이다.
객체지향 프로그래밍의 캡슐화란 다음과 같다.

캡슐화란?

객체의 속성(data fileds)과 행위(메소드, methods)를 하나도 묶는다.
실제 구현 내용 일부를 외부에 감추어 은닉한다.

하지만 자바스크립트에서 기본적인 객체선언은 캡슐화의 두번째 측면인 실제 구현내용을 외부에 감추어 은닉할수가 없다.
간단한 객체 리터럴 예제로 확인해보자.

1
2
3
4
5
var countObj1 = {
n : 0,
count : function(){ return this.n++;},
reset : function(){ this.n = 0}
}

countObj1객체의 의도는 메소드 count를 이용해서 n을 1씩 증가시키는 것이다.
그리고 reset 메소드를 이용하여 n을 0로 초기화 시킨다.

그리고 위처럼 사용 할수 있다. 하지만 객체의 내부 프로퍼티인 n을 다른 방식으로 접근하여 호출 또는 재정의 할 수 있다.

위처럼 내부 객체의 내부 프로퍼티에 직접 접근하여 재 할당 될 수 있다.
만약 악의적인 코드 또는 실수로 인해 외부에서 직접 n을 접근 할 경우 의도치 않은 동작을 할 수도 있다.

이렇게 외부에서 임의로 데이터를 접근 또는 훼손하는 것을 막는 기법이 캡슐화의 데이터 은닉이다.
하지만 안타깝게도 자바스크립트에서는 문법적으로 캡슐화의 데이터 은닉를 지원하지 않는다. (java, c++에서는 문법적으로 지원한다.)

이 캡슐화의 데이터 은닉을 자바스크립트에서는 함수형 프로그래밍 기법인 클로저를 이용해 구현 할 수 있다.

1번 예제에 클로저를 적용하여 내부 변수 n을 외부에서 접근 못하게 해보자.(캡슐화의 데이터은닉)

1
2
3
4
5
6
7
8
9
10
function counter(){
var n = 0;
return {
count: function() { return n++; },
reset: function() { n = 0; }
};
}

var countA = counter();
var countB = counter();

위 소스코드에서는 counter 함수가 리턴하는 객체의 메소드 count()와 reset()가 클로저가 된다. (이부분이 이해가 안간다면 클로저의 개념과 대해 다시 공부해야 한다.)

2번 예제는 1번예제와 객체 생성방법이 다르지만, 만들어진 객체의 사용은 동일하다.
count 메소드를 이용하여 n에 1을 더하고 , reset 메소드를 이용하여 n을 0으로 초기화 하고 있다.

그렇다면 2번예제에서도 1번예제처럼 직접 객체에 접근하여 n을 수정해 보자.

countA.n = 100; 을 통해 n이라는 객체에 새로운 변수를 추가 할 수 있다.
하지만 countA의 메소드(클로저)는 이 countA의 멤버변수 n을 사용하는 것이 아니라 자기 자신의 외부함수 counter의 지역변수 n을 사용하기 때문에 객체의 메소드를 호출시 계획한 대로 결과가 나오는 것을 확인 할수있다.
이것은 클로저를 통해 변수 n이 은닉되어 있기때문에 가능한 결과이다.

2번 예제를 이용해 클로저에 대한 부연설명을 조금 더 해보자면,
countA.count()와 countA.reset() 메소드는 하나의 n을 공유한다.
하지만 countA 와 countB는 n을 공유하지 않고 각각의 n을 사용한다.
counter함수가 호출되어 새로운 객체를 리턴 할 때마다 새로운 유효범위 체인과 새로운 내부변수(n)이 생성되기 때문이다.
여기서 이 counter의 내부변수 n이 캡슐화된 외부로부터 은닉된 실제 구현내용이 된다.
이런 캡슐화를 통해서 내부변수 n이 외부로 부터 오염되는 것을 막을수 있다.

활용2 - 함수의 호출방법의 가공

setTimeout 함수는 첫번째 인자로 받는 함수 참조값을 특정시간(두번째 인자) 이후에 실행시키는 내장함수이다.
문제는 첫번째 인자로 넘기는 함수에 매개변수를 전달 할 수 없다는 것이다.
하지만 클로저를 이용해 전달할 함수를 클로저로 변환하여 호출하는 방법을 가공 할수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function sum(a, b) {
console.log("%d + %d = %d", a, b, a + b);
}

function callLater(func, a, b) {

function tempFn() {
func(a, b)
}

return tempFn;
}

var func = callLater(sum, 1, 2);
setTimeout(func, 8000);

간단한 예제이다. callLater 함수를 이용해 setTimeout에게 전달할 함수의 호출환경을 미리 설정해 놓고 그것을 클로저(tempFn)로 만들어 func 변수에 할당했다.
func 클로저는 인자가 필요 없으므로 setTimeout에 첫번째 인자로 전달할 수 있다.

자바스크립트에서는 함수도 일급객체이므로 변수처럼 다른 함수의 인자로 전달하여 실행시키는 경우가 많은데, 전달할 인자 함수에 매개변수를 클로저를 통해 미리 조립하여 전달하는 방식으로 클로저를 사용 할 수 있다.

클로저의 활용 방법을 간단한 2가지 예로 설명했는데, 사실 더 다양한 방법이 있을 것이다.
스스로 더 공부하고 코딩하면서 클로저의 다양한 활용방법을 찾아보길 바란다.

클로저(closure)란?

클로저의 사전적 의미를 오렐리의 코뿔소책에서 발췌해왔다.

함수객체와, 함수의 변수가 해석되는 유효범위(변수바인딩의 집합)을 아울러 컴퓨터 과학 문언에서는 클로저(closure)라고 일컫는다.
기술적으로 모든 자바스크립트 함수는 클로저인데, 함수는 객체이고 함수 자신과 관련된 유효범위 체인을 가지고 있기 때문이다.

대충 보니 자바스크립트에서 함수객체 본인과, 그 함수객체가 접근할수 있는 유효범위를 포괄적으로 클로저라고 한다는것 같다.
하지만 위 글로는 야구의 마무리투수와 클로저의 차이를 구별하긴 어렵다. 역시 소스코드로 클로저를 이해해보자.

1번 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
var testVal = '나는 전역변수이다.';

function test(){
var testVal = '나는 지역변수이다.';

function innerF(){
return testVal;
}//innerF

return innerF();
}//test

test();

1번 예제를 보자, 내부 함수를 선언하고 실행해서 리턴하는 간단한 예제이다.
여기서 마지막 test();의 결과를 보자.

전역변수가 아닌 test함수의 지역변수가 선택되어 console.log함수에 의하여 호출되는 것을 확인 할 수 있다.
음 innerF함수가 test함수 내부에서 실행되고 그 값이 리턴된 것이니 당연한 결과라 생각이 된다.

그렇다면 다음 예제를 보도록 하자.

2번 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
var testVal = '나는 전역변수이다.';

function test(){
var testVal = '나는 지역변수이다.';

function innerF(){
return testVal;
}//innerF

return innerF;
}//test

test()();

1번예제와 거의 같지만, 다른점은 innerF 내부함수는 test함수 내부에서 실행되지 않고 그냥 리턴이 되어버린다. 그리고 전역 영역에서 innerF함수를 실행한다.
test()();의 결과를 한번 보자.

클로저를 모르는 분들이라면 이 결과에 이상함 또는 충격과 공포를 느낄 것이다. innerF 함수는 전역영역에서 실행이 되었다. 따라서 전역에 있는 testVal을 사용할것이라고 쉽게 예상한다. 하지만 그 결과는 여전이 test함수의 지역변수를 사용한다는 것을 알수 있다.

보통 여기서 1차 멘붕이 온다.(필자가 그랬으니 당신들도 그러해야한 합니다. 나만 바보일순 없으니…)
왜???? test()(); 동작결과가 지역변수를 호출하는 거지????
분명 innerF함수는 전역영역에서 실행되었는데????
전역영역의 testVal을 가져와서 “저의 선택은 나는 전역변수이다. 입니다.” 이렇게 나와야 되는데????

그리고 C와 같은 저수준 프로그래밍 언어를 공부한 사람들은 곧 2차 멘붕이 온다. (역시 당신들도 그러해야한 합니다….)
부모함수 test함수는 호출이 이미 끝났잖아????
리턴된 함수는 이미 실행이 종료(close)된 test함수의 내부 변수에 어떻게 접근할수 있는 거지?????
test함수가 실행이 끝나지 않은 것인가???? (참고로 test함수는 종료된것이 맞다.)

예제2번의 결과가 나오는 이유는 바로

자바스크립트의 함수가 동작할때 사용하는 유효범위는 함수를 호출하는 시점에서의 변수 유효범위가 아니라, 함수가 정의된 시점의 변수 유효범위 체인를 사용하여 함수가 실행되기 때문이다.
다시 강조를 하면 호출하는 시점이 아니라! 정의되는 시점!!!
이런 특징을 어휘적 유효범위(lexical scoping)이라고 부른다고 한다.
이것이 굉장히 중요하다. 자바스크립트는 이런 어휘적 유효범위 때문에 클로저라는 것을 구현할수 있는 것이다.
test함수가 실행이 종료된 뒤에도 여전히 innerF함수가 test함수의 지역변수를 사용할수 있는 이유는 innerF함수는 자신의 정의된 시점에 자신이 접근하여 사용할수 있는 유효범위 체인 정보를 별도의영역(내부적으로…)에 저장하고 관리하고 있기 때문이다.

즉 2번 예제의, innerF함수는 전역영역에서 실행되었지만, innerF함수가 선언된 그 시점의 유효범위를 가지고 동작하기 때문에 선언된 시점의 test()함수 내부의 지역변수를 사용하게 된 것이다.

그리고 이번에 공부하고 있는 클로저라는 녀석은 저 test()함수 객체를 말한다. 2번 예제에서는 리턴된 함수를 변수에 안담아서 보기가 좀 그렇지만 var a = test(); a(); 이 a를 클로저라고 부른다.
글 맨위에서 사전적 의미의 클로저(함수의 유효범위와 함수객체)가 있지만 실질적으로 어떤 함수 내부에서 정의되고 외부로 리턴된 함수를 클로저라고 말한다.

클로저(closure)라는 말은 어원은 함수의 지역변수가 유효범위 체인에 바인딩 되어 있고, 따라서 그 함수는 함수의 변수에 ‘따라 닫힌다’는 뜻에서 유래한 용어라고 하는데, 이름을 잘못 지은것 같다. 오히려 개인적으로 이 이름때문에 이해하기 힘든 부분이 있었던것 같다.(마무리 투수만 생각 나더라…)

누군가 당신에게 자바스크립트 클로저(closure)를 물어본다면?

귀찮을 때

함수다.

진지할 때

함수객체와 그 함수객체가 선언된 시점의 유효범위 체인을 통틀어서 클로저라고 부를수 있다.
더 구체적으로 클로저를 지칭 하자면 자신(함수)을 정의한 바깥쪽 함수에 바인딩된 지역변수(그리고 전달인자)를 포착할수 있는 함수객체이다.

이 클로저라는 녀석을 이용해서 다양한 코딩 기법(자바의 private 멤버변수 같은)이 있는데, 그건 아직 공부를 안해서 나중에 시간이 난다면 끄적여 보도록 하겠다. 이만 뿅!.

자바스크립트의 객체 생성방법 3가지

1.객체 리터럴을 이용한 객체 생성 방식

리터럴 방식 객체생성은 하나의 객체를 그 즉시 생성한다.
거두절미하고 소스와 주석으로 객체 리터럴을 설명하겠다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var filmograyphyList = ['도날드덕', '환타지아']; // 배열 객체를 선언한다.

var man = {}; // 비어있는 프로퍼티가 없는 리터럴로 man 이라는 객체를 생성

//duck 이라는 이름의 객체를 객체리터럴을 통해 생성한다.
var duck = {
//객체 내부에는 그 객체의 특징(element)이 되는 프로퍼티를 여러개 갖을수 있으며
//이 프로퍼티에는 자바스크립트에 존재하는 모든 것을 담을수 있다.(원시값을 포함해 함수, 배열 ...)
name: '도날드 덕',
age: 82,
sex : man, // 프로퍼티에 객체가 할당된다.
별명 : '도날드 트럼프', // 자바스크립트 프로퍼티명은 영어가 아닌 한글도 가능하다.

filmograyphy : filmograyphyList, // 프로퍼티에 객체배열을 할당한다.

'home adress' : '디지니 랜드', // 일반적으로 프로퍼티 명을 뛰어 쓰기로 사용할수 없으나
//예외적으로 싱글쿼티션(') 또는 더블쿼티션("")으로 감싼 프로퍼티명은 사용이 가능하다.
// 프로퍼티 네이밍은 일반적으로 자바스크립트 변수 네이밍
//규칙을 따른다.(ex:숫자로 시작하는 변수명은 사용할수 없다.)
// 하지만 싱글쿼티션(') 또는 더블쿼티션("")으로 감싼 프로퍼티명은 이런 자바스크립트의 //변수명 작성 규칙 제한을 받지 않는다.

for : 'test2222', // for, if 등 시스템 예약어는 변수명으로는 사용할수 없다 하지만 자바스크립트
//객체프로퍼티명으론 사용할수 있다.
'if' : 'test111', // 하지만 이런 예약어들의 사용을 권장하지 않으며, 만약 불가피 하다면 문자열로
//감싸 사용하길 권장한다.

//프로퍼티에는 함수또한 담길수 있으며, 특이하게 객체의 프로퍼티중 함수를 담고 있는 프로퍼티는 메소드라고 부른다.
say : function(){
console.log('꽥 꽥');
}

};

리터럴이라는 말을 위키피디아에서 찾아보니 컴퓨터 과학분야에서, 소스코드의 고정된 값을 대표하는 용어라고한다.( 즉 객체 리터럴 방식은 객체를 생성하는 표기법 정도로 이해하고 넘어가자. 리터널에 대해서는 인터넷에 한번 찾아보길 바란다.)

위 소스는 obj1이라는 이름의 객체를 리터럴 방식으로 생성한 것이다.
객체 리터럴은 중괄호 { } 를 이용하여 프로퍼티명과 프로퍼티의 값을 감싼 형태로 생성 된다.
{ ‘프로퍼티이름’ : ‘프로퍼티값’ }

더 자세한 내용은 위의 동영상을 참고하길 바란다.

2.생성자를 이용한 객체 생성

생성자 함수를 이용해 반복적으로 동일한 형태(프로퍼티를 갖는)객체를 생성하는 일반적인 방법.

일단 자바스크립트의 생성자란 객체를 반복적으로 생성할수 있는 함수이다.
대중적인 객체지향 언어인 java, c++ 같은 언어에서는 class라는 객체의 설계도 내부의 생성자라는 함수를 가지고 있는데, 자바스크립트에서는 클래스라는 객체의 설계서가 따로 존재하지 않으며 이 생성자 내부에서 객체의 설계내용을 작성한다.

그리고 자바스크립트에서는 기본적인 함수와 생성자간의 문법적 구별이 없다. A라는 함수가 있다면 이것을 그냥 함수처럼 호출할수도 있고, new라는 키워드로 생성자로 호출할수도 있다.

역시 거두절미하고 소스를 보자.

1
2
3
4
5

var obj1 = new Object(); // 자바스크립트에 내장된 기본 Object 생성자를 이용해 빈 객체를 생성.
var arrayObj1 = new Array(10); // 자바스크립트에 내장된 기본 Array 생성자를 이용해 빈 배열 객체를 생성.
var dateObj1 = new Date(); // 자바스크립트에 내장된 기본 Date 생성자를 이용해 날짜 정보를 담은 Date 객체를 생성.
// Object, Array, Date 뿐만아니라 여러 다른 기본 내장 객체 생성자가 존재한다.

위처럼 내장된 생성자 객체들을 이용하여 객체를 생성할수 있다.
이런 내장된 생성자 말고 프로그래머가 직접 원하는 객체를 생성할수 있도록 생성자를 만들수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//Duck이라는 생성자를 만든다. 자바스크립트에서는 일반 함수와 생성자의 문법적 차이가 없다.(개인적으로 불만이다.)
//생성자와 함수를 구별하기 위해 보통 생성자의 이름은 대문자로 선언한다.
function Duck(dName, dAge){

//생성자 함수의 내부에 있는 this라는 키워드는 이 함수가 new로 호출되었을때 생성해서 리턴하는 객체를 가리키며
//this.name은 그 생성될 객체의 프로퍼티를 생성하겠다라는 의미이다.

this.name = dName; // 생성자 함수를 호출시 전달받는 전달인자를 name이라는 프로퍼티에 할당한다.
this.age = dAge; // 생성자 함수를 호출시 전달받는 전달인자를 age이라는 프로퍼티에 할당한다.
this.종 = '오리';

//객체 리터럴과 다르게 아래처럼은 객체 프로퍼티의 프로퍼티명을 문자열로 만들수 없다.
//this."home adress" = '디지니 랜드';

//하지만 이런식으로 객체가 유사배열인 것을 이용하여, 배열 형태로 프로퍼티를 생성할 시 문자열로 프로퍼티명을 생성할수 있다.
this["home adress"] = '디지니 랜드';

this.for = '하하하?';

this.say = function(){
console.log('꽥 꽥');
};

}// Duck end

자 위 소스로 객채를 생성할수 있는 함수 즉 생성자를 선언했다.
이제 위 생성자를 통해 객체를 생성해 보자.

1
2
3
4
5
6
//new 라는 키워드의 의미는 new 다음에 올 함수를 생성자로서 호출하겠다는 의미이다.
//여기서 중요한 것은 new로 호출된 모든 함수는 객체를 반드시 생성해서 리턴하게 된다.
//생성자는 어떤 형태의 객체를 만들 목적으로 만들어 진 것일뿐 일반함수와 같다.
var duck1 = new Duck('도날드 덕', 82);

console.dir(duck1);

생성된 객체를 console.dir로 열어보면 Duck생성자로 생성된 객체의 프로퍼티를 확인 할 수 있다.

일단 생성자를 하나 만들었다면,

1
2
var duck2 = new Duck('레임 덕', 5);
var duck2 = new Duck('베이징 덕', 3);

이처럼 반복적으로 객체를 생성하는 것이 가능하다.

지금 설명하는 생성자를 사용하여 객체를 생성하는 이유는 동일 형태의 객체를 반복적으로 생성할 수 있기 때문이다.
(물론 필요에 따라 객체리터럴로 객체를 생성해도 된다. 뭐가 좋고 나쁘다는 말이 아니다.)

자바스크립트에서 생성자가 다른 객체지향 언어와 개념적 차이가 있는데, 일반적인 객체지향언어에서 클래스라는 개념이 있다.
이 클래스라는 개념은 객체의 설계도 역할을 한다. 예를 들어 객체가 가져야 할 프로퍼티들을 정의 하는데, 자바스크립트에서는 이 클래스라는 개념이 없으며,그 대신 자바스크립트에서 생성자에서 this.프로퍼티명 을 이용하여 이 클래스의 설계도와 같은 기능을 포함하고 있다.

  1. Object.create 매소드를 이용해한 객체 생성

자바스크립트의 객체의 상속을 따라가다보면 최상위 객체가 존재하신다.(필자는 god object라고 장난스래 부른다)
그리고 이 최상위 객체를 이용하여 자바스크립트에서 객체를 생산하시는 성모마리아님 같은 최상위 생성자(자바스크립트 내장 상성자)가 역시 존재하는데, 그 분이 Object()생성자 이시다.
이 Object생성자 객체의 create 메소드를 이용하면, 객체 생성과 동시 객체 상속을 조금더 손쉽게 할수 있다.
이 기능은 ecma5(이끄마파이브) 부터 지원하며, 자세한 내용은 아래 MDN을 참고!

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/create

절대 귀찮아서 Object.create 설명은 글로 안쓴것이 아니다. 솔직히 나도 잘 모ㄹ…. 콜록콜록

자바스크립트에서 객체란?

일단 자바스크립트는 객체지향 언어이고, 객체란 일종의 숫자, 문자열과 같은 데이터 형태 중 하나이다.

자바스크립트의 객체를 설명하기 앞서 객체(Object:오브젝트)라는 말을 간단히 설명하고 가겠다. (이미 안다면 그냥 넘어가시길…)
프로그래밍 영역에서 객체(Object:오브젝트) 어떤 의미일까?
영어로는 물건, 물체등으로 해석된다.
컴퓨터 용어의 Object도 영어의 그것과 크게 다르지 않다.

사람은 어떤 물건, 예를들어 자동차와 같은 물체를 인식(or이해)할 수 있다.
예를 들어 아래의 자동차를 차에대해 전혀 모르는 친구에게 설명해본다고 하자.


>![](/images/2017-06-11-javascript3_1.JPG)

이 차의 회사는 쉐보레이고, 차종은 카마로 범블비 에디션이고, 색상은 노란색이며, 바퀴는 4개 달려있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var car1 = {
company: '쉐보레',
wheels: 4,
color: '#FFE400', //노란색 RGB 색상표
name : '카마로 범블비 에디션',
'배기량' : '5000',
transform : function(){
console.log('범블비로 변신');
},
run : function(){
console.log("범블비 달린다!");
}
};

function do_something(car1); // 이차로 무언가 해봐!, 분해를 하던, 폐차를 하던

당신이 자바스크립트에서 변수라는 것을 알고 있다면 car1 이라는 것이 카마로라는 자동차의 설명 정보를 담고 있다는 것을 어렴풋이 느낄수 있을것이다. 그리고 do_something 이라는 함수에 car1 을 전달인자로 넘겨 버렸다.
위 소스의 car1이 자바스크립트에서 객체이다.

쉽게 말해서 프로그래밍 영역에서 객체란 어떤 대상의 복합적인 데이터를 담고 있는 데이터이다.
당신이 알고 있는 어떤 무언가(대상)를 다른 사람에게 설명하듯이 컴퓨터에게 그것들을 인식시키기 위해 그 대상의 특징(element)를 만들고 그 특징에 대한 설명정보를 담고 있는 것, 그것을 객체라고 부른다.
그리고 보통 이 객체라는 단위의 자료형(type)을 가지고 있는 프로그래밍 언어를 객체지향언어(객체라는 개념과 객체지향 프로그래밍을 지원)라고 부른다.
그리고 자바스크립트는 객체를 지원하는 객체지향 언어이다.

자바스크립트의 객체의 특징

  • 자바스크립트 객체는 프로퍼티를 갖는다. 그리고 프로퍼티는 데이터프로퍼티와 접근자프로퍼티 두 종류가 있다.
  • 자바스크립트 객체는 메소드를 갖는다.
  • 숫자, 문자열, 불린값, null, undefined 같은 기본 타입을 제외한 모든 값은 객체다. (함수, 배열, 정규표현식 등)
  • 클래스라는 개념이 없다.(java, c++와의 객체와 약간 생성, 사용방법이 다르다.)
  • 자바스크립트 객체는 참조타입이다.

함수호이스팅(Function Hoisting)

자바스크립트 영역에서 유명한 더글라스 클락포드는 함수를 생성할 때 선언식보다는 함수 표현식만을 사용할 것을 권고 하고 있다.
그 이유가 이 함수호이스팅 때문이라고 설명하는데, 대체 함수호이스팅 이란 무엇일까?

함수호이스팅의 = 함수 선언문으로 생성된 함수가 해당 소스코드의 유효범위의 맨 위로 끌어올려진다.

일단 소스코드를 보자

예제1(함수 선언문)

1
2
3
4
5
6
7
8
fn_test(1); //a호출:함수 선언문 이전에 호출

//함수 선언문
function fn_test(temp){
console.log('fn_test run... temp:'+ temp);
}

fn_test(2); //b호출:함수 선언문 이후에 호출

예제1 결과

만약 자바스크립트의 함수호이스팅을 모른다면 예제1의 결과를 의아하게 볼 수 있다.
a호출에서 아직 선언되지 않은 시점에서 fn_test함수를 호출 한 결과 오류가 발생하기는 커녕 정상적으로 함수가 호출되어 동작 했다.

이런 동작의 이유가 함수 호이스팅이다.
비록 함수선언문으로 생성된 함수 fn_test는 자동으로 소스코드 최상단으로 끌어올려진다.(a호출 보다 위로)

예제1의 함수호이스팅 동작의 상상

1
2
3
4
5
6
7
8
//함수 선언문, 소스코드 최상단으로 끌어 올려짐
function fn_test(temp){
console.log('fn_test run... temp:'+ temp);
}

fn_test(1); //a호출:함수 선언문 이전에 호출

fn_test(2); //b호출:함수 선언문 이후에 호출

즉 예제1은 위 소스와 같은 동작을 한다는 것이다.
함수 선언문은 함수 호이스팅에의해 항상 소스의 최초시점에 호출 되기때문에 호출a에서 fn_test가 정상 동작 한 것이다.

얼핏 보면 함수호이스팅은 편리한 기능처럼 보이기도 하다.
“어쨋든 오류는 없이, 소스코드가 동작하는데 편리한 기능 아닌가?”라고 생각 할 수도 있다.

마이클 더글락스는 함수 선언문은 소스코드를 엉성하게 만들어, 코더의 의도하지 않는 소스의 동작을 야기하기 때문에 지양해야 한다고 한다.
그리고 함수 선언문 대신 함수호이스팅이 발생 하지 않는, 함수 표현식으로 함수를 생성해서 사용 할 것을 권고 한다.

예제2(함수 표현식)

1
2
3
4
5
6
7
fn_test(1);  // a 호출: 함수 표현식 이전 호출

var fn_test = function(temp){
console.log('fn_test start… temp:' + temp);
};

fn_test(2); // b 호출: 함수 표현식 이후 호출

예제2 결과

예제1 함수 선언식과 다르게 함수 표현식으로 선언된 함수는 함수호이스팅이 발생하지 않으므로, 함수 생성 이전에 호출 할 경우 아직 존재하지 않는 함수를 호출게 되므로 오류가 발생한다.

개인적으로 화면 단 에서 간단한 DOM제어용 함수는 선언문으로 사용해도 상관 없지만, nodeJS나 Angular등 복잡하고 많은 분량의 자바스크립트 코드 개발시에는 더글라스 말대로 함수 표현식을 사용 하는것이 적당한 것 같다.

자바스크립트 책을 보다보면, 입급객체, 일급함수이런 말들이 나온다.

자바스크립트의 객체는 일급객체이다.
자바스크립트의 함수는 일금함수이다.

이게 대체 무슨 말일까?
어려워 할 것 없다. 이미 아는 것이지만, 단어가 생소해서 혼동이 올 뿐이다.

1급시민(일급시민:first class citizen)

: 일급객체, 일금함수 이런 말 이전에 일급시민이라는 말 부터 정리해 보도록 하자. 그 이유는 일급객체는 일급시민인 객체를 말하며, 일급함수는 일급시민인 함수를 말하기 때문이다.

일급시민을 간단히 말하자면 차별받지 않고 시민으로서 누릴수 있는 권한을 모두 누릴수 있는 사람을 일급시민이라고 한다.

  • 1급 시민은 투표권이 있다.
  • 1급 시민은 군인이 될 수 있다.
  • 1급 시민은 정치에 참여 할 수 있다.

1800년데 영국에서는 1급시민, 2급시민이 존재 했으며 성인 남성의 경우 1급시민으로서 정치참여가 가능한데 반해, 2급시민인 여성은 정치 참여를 할 수 없었다고 한다.

1급시민 이라는 말은 권한을 많이 누리는 대상이라고 볼 수 있다.

프로그래밍 영역에서 1급시민이란?

: 일단 프로그래밍 영역에서 1급시민을 정의하려면 1급시민이는 것들이 누려야할 권한이 뭔저 정의가 되어 있어야 한다.(실제 투표권 같은 권한 말이다.)

  • 1급 시민은 함수의 인자가 될 수 있다.
  • 1급 시민은 함수의 리턴이 될 수 있다.
  • 1급 시민은 변수에 할당 될 수 있다.

프로그래밍 영역에서 위와 같은 권한을 누리는 것들을 보통 일급시민이라 여긴다. 즉 자바스크립트에서 원시값(일반 자료형)은 1급 시민이다.

1
2
3
4
5
6
7
var temp_int = 1; // 숫자는 변수에 담긴다.
function fn_test_int(a){
return a;
}

var ret_int = fn_test_int(3); // 숫자는 함수의 인자가 된다.
console.log(ret_int); // 숫자는 함수의 리턴이 된다.

1급객체(일급객체:first class object)

자바스크립트의 객체는 1급객체이다.

라는 말은 자바스크립트에서 객체는 1급시민이 누리는 권한을 모두 가지고 있다는 말이다.
따라서 자바스크립트의 객체도 원시값 처럼 다음과 같다.

  • 자바스크립트의 개체는 함수의 인자가 될 수 있다.
  • 자바스크립트의 객체는 함수의 리턴이 될 수 있다.
  • 자바스크립트의 객체는 변수에 할당 될 수 있다.
1
2
3
4
5
6
7
8
var temp_obj = { name :'first class object1'};   // 객체는 변수에 담긴다.

function fn_test_obj(obj){
return obj;
}

var ret_obj = fn_test_obj(temp_obj); // 객체는 함수의 인자가 된다.
console.dir(ret_obj); // 객체는 함수의 리턴이 된다.

1급함수(일급함수:first class function)

자바스크립트의 함수는 1급함수이다.

라는 말은 자바스크립트에서 함수역시 1급시민이 누리는 권한을 모두 가지고 있다는 말이다.따라서 자바스크립트의 함수도 원시값 처럼 다음과 같다.

  • 자바스크립트의 함수는 함수의 인자가 될 수 있다.
  • 자바스크립트의 함수는 함수의 리턴이 될 수 있다.
  • 자바스크립트의 함수는 변수에 할당 될 수 있다.
1
2
3
4
5
6
7
8
9
10
var fn_outer = function(){
console.log('fn_outer 함수 동작...');
return function(){
console.log('리턴되는 함수가 동작...');
};
};

var ret_fn = fn_outer();
ret_fn(); // fn_outer함수의 리턴된 함수가 실행된다. 즉 함수는 함수의 리턴이 될수있다.
ret_fn();

java의 예외처리 내용이 햇갈려서 간만에 java책을 들여다보니 내가 몰랏던 기능들이 있어서 정리해 본다.

1.멀티 catch 구문 a.하나의 catch 블록에서 여러개의 Exception을 잡아서 처리하는 구문이다.

예제1-a

1
2
3
4
5
try{
// ...
}catch(AException a | BException b){
//...
}

자바 7부터 추가된 기능이라고 한다.

자동리소스 닫기(try-with-resources)

a.기존의 try-catch-finally 로 처리하던 관용적인 리소스(db 커넥션, 소켓 등등) 반납 로직을 간편하게 처리하기 위해 나온듯 하다.

b.자동리소스 닫기(try-with-resources)는 코드의 예외 발생 여부와 상관 없이 사용중인 리소스 (각종 입출력 스트림, 서버소켓, 소켓, 각종 채널)의 close() 메소드를 자동으로 호출하여 안전하게 리소스를 반납해준다.

예제 2-b

1
2
3
4
5
6
7
8
9
10
11
try(AStream as = new AStream()){
// 한개의 리소스 사용시
}catch(IOException e){
//...
}

try(AStream as = new AStream(); BStream as = new BStream();){
// 복수 개의 리소스 사용시
}catch(IOException e){
//...
}

기존의 finally를 이용하여 리소스를 클로즈 하는 행위를 자동으로 처리해주는 편리하느 기능이라고 생각 된다.

c. 자동리소스 닫기(try-with-resources)를 사용하기 위해서는 조건이 있다. AutoCloseable이라는 인터페이스를 구현한 리소스객체만 자동리소스 닫기(try-with-resources)를 적용 할 수 있다