Javascript로 개발하다보면, 처음에는 아주 쉽고 재밌게 느껴지며 너무 편하다고생각이 된다.

그러나 일정수준 이상되면 첫번째 한계에 부딪히게 되는데 그게바로 "Closure" 이다.

심지어 clousre에 대한 job interview 대답에 따라 $40k 까지 연봉이 차이가 날수도 있다는 blog posting까지 보았다.

(https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36)

(뇌피셜일수도 있으니 주의하자)

그럼 이토록 중요한 클로저가 무엇인지 다시한번 상기하고 필요성에 대해서 얘기해보고자 한다.

 

 

Closure

 먼저 클로저를 얘기하기전에 var에 대해서 이야기해보자, var로 선언된 변수는 기본적으로 function scope 내에서만 사용되어진다. 

function() {
  var a = 1;
  console.log(a); // works
}    
console.log(a); // fails

 

내가만약 위의 함수를 오류가 없이 동작시키려면 global scope를 사용해야 할것이다.

var a = 1;
function() {
  console.log(a); // works
}    
console.log(a); // works

 

지역변수는 보통 function이 끝나게 되면 메모리에서 사라진다.

그렇다면 우리는 local variable을 계속 참조 할 수 없을까? 이를 closure가 답변해준다.

 

 

클로저는 쉽게말해 persistance한 local variable scope이다. (persistance = 영속적인 한글로 표현하면 좀 어색해진다.)

클로저는 코드의 실행이 function 밖으로 이동했음에도 불구하고 지속되는 persistance local scope이다.
더 쉽게말해서 function scope 밖에서도 그 local variable에서 변수를 reference 할 수 있게 해준다. 
(객체지향적 설계를 가능하게 한다.)

다음을 코드를 보면 더 이해가 잘 될것이다.

outer = function() {
  var a = 1;
  var inner = function() {
    console.log(a);
  }
  var add = function() {
  	a += 1;
  }
  return {inner,add}; // this returns a function
}

var fnc = outer(); // execute outer to get inner 
fnc.inner();
fnc.add();
fnc.inner();

//output
//1
//2

위 코드에서처럼 a는 local variable임에도 불구하고 영속성을 갖는다. 이게 클로져이다.

JavaScript에서는 함수가 처음 선언 될 때 var a가 hoisting 되고, 함수가 계속 존재하는 한 var a도 지속되어진다.

a는 outer scope에 속한다. inner scope는 outer scope에 대한 참조가 있다. fnc는 inner scope를 가리키는 변수다. fnc가 지속되는 한 a또한 사라지지 않고 존재한다. closure 안에 있다.

이를 좀더 유식하게 말하면 "특정 함수가 참조하는 변수들이 선언된 렉시컬 스코프(lexical scope)는 계속 유지되는데, 그 함수와 스코프를 묶어서 closure 라고한다.

 

 

실질적인 closure에 관한 예제

먼저, 우리가 버튼을 클릭할때마다, 버튼이 클릭된 횟수를 세는 기능을 만든다고 가정해보자.

<button onclick="updateClickCount()">click me</button>  

 

1) Global variable을 쓰면 가장 쉽게 해결가능하다.

var counter = 0;

function updateClickCount() {
    ++counter;
    // do something with counter
}

 

그러나 counter라는 글로벌변수는 쉽게 노출되며, updateClickCount라는 함수의 호출없이 ++ 시킬수 있다.

 

2)그러면 counter function scope 내부로 옮긴다면?

function updateClickCount() {
    var counter = 0;
    ++counter;
    // do something with counter
}

counter는 항상 1로 될것이다. counter는 persistance한 local variable 이 아니기 때문이다.

 

3)Nested functions를 쓰면?

function countWrapper() {
    var counter = 0;
    function updateClickCount() {
    ++counter;
    // do something with counter
    }
    updateClickCount();    
    return counter; 
}

 당신이 외부 scope에서 updateClickCount 함수에 접근할수 있고, counter=0라는 선언을 한번만 호출할 수 있다면, 가장 좋은 방법일 것이다.

 

정답) Closure가 출격한다면 어떻게 될까? C L O S U R E ! (with IIFE)

 var updateClickCount=(function(){
    var counter=0;

    return function(){
     ++counter;
     // do something with counter
    }
})();

 

위처럼 한다면 우리가 처음 의도한대로 동작할 것이다.
local variable을 영속성을 갖게할수 있고 또 local variable을 객체지향적으로 encapsulation 할 수 있게된다.

이에 더하여 counter변수는 global scope에서 hoisting 되지 않으므로 변수의 충돌 또한 방지해준다.

 

<script>
var updateClickCount=(function(){
    var counter=0;

    return function(){
      ++counter;
      document.getElementById("spnCount").innerHTML=counter;
    }
  })();
</script>

<html>
  <button onclick="updateClickCount()">click me</button>
    <div> you've clicked 
  		<span id="spnCount"> 0 </span> times!
  	</div>
</html>

 

 

https://stackoverflow.com/questions/2728278/what-is-a-practical-use-for-a-closure-in-javascript

 

What is a practical use for a closure in JavaScript?

I'm trying my hardest to wrap my head around JavaScript closures. I get that by returning an inner function, it will have access to any variable defined in its immediate parent. Where would this be

stackoverflow.com

 

 

PS. 우리는 let으로 문제를 해결했던 for 문 딜레마도 closure를 통해 해결할 수 있다.

var i = 0;
for(i = 0; i<3; i++) {
	(function(index){
    	cosnole.log(My value : index);
    }(i))
}

 

 

Scenario


최근에 es6로 프로그래밍을 하면서, let을 많이쓰게되었고, let은 단순히 지역변수라고만 생각했엇다.

근데 다시 곰곰히 생각해보니 var도 function scope 이므로 지역변수로 사용할 수 있지않은가?

라는 생각이 들었다. 책에서 읽었던 내용중에 let은 bracket scope 였던게 생각이났다.

function run() {
  var foo = "Foo";
  let bar = "Bar";

  console.log(foo, bar);

  {
    let baz = "Bazz";
    console.log(baz);
  } //사실 braket scope을 그닥 많이쓰진 않는다.

  console.log(baz); // ReferenceError
} 

run();

 사실 나의경우는 component 기반 es6 기반 프레임워크&라이브러리로 javscript를 쓰다보면 bracket scope을 그닥 많이 쓰진않는다. 그럼 let이라는게 왜 등장을 한지 배경을 좀 조사해보았다.

 

var funcs = [];

for (var i = 0; i < 3; i++) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  funcs[j]();
}
//output: expect 0, 1, 2 but
//My value: 3
//My value: 3
//My value: 3

위 예제의 경우 let을 써야하는 이유가 더 와닿는다. (실제로도 나는 let은 for문에서 가장많이쓰는것같다.)

 

Solution


먼저 var로 선언된 변수를 이해 하려면, hoisting을 이해해야 한다. 

 

hoisting 


사전적의미로 들어올리다 이다. 자바스크립트의 hoisting을 이해하려면 다음 소스코드부터 보자.

 

function run() {
  console.log(foo); // undefined -- ReferenceError가 나지않는다.
  var foo = "Foo";
  console.log(foo); // Foo
}

run();

 

 위 소스코드에서 3번째 줄에서 오류가 나지않는 이유는 hoisting 되었기 때문이다. 변수가 생성된 시점은 3번째줄이지만,  scope 상단으로 hosting 되어진다. 이는 변수들이 선언되기 전에 스코프내에서 접근할 수 있다는 것을 의미한다.
(var는 초기화 되기 전까지는 블록의 시작에 "temporal dead zone"에 있다고들 말한다.)  

 

function checkHoisting() {
  console.log(foo); // ReferenceError
  let foo = "Foo";
  console.log(foo); // Foo
}

checkHoisting();

그러나 위의 소스코드는 error가 난다. 이유는 let은 호이스팅 되지 않기 때문에 선언되기 전에 foo에 접근을 시도하기 때문이다. 이는 아주 중요한 차이점을 갖는다.

 

주의점

gloval scope에선 let을 쓰지 않도록 주의해야한다.

var foo = "Foo";  // globally scoped
let bar = "Bar"; // globally scoped

console.log(window.foo); // Foo
console.log(window.bar); // undefined

 

또한 var는 재정의할 수있으므로 주의해서 써야한다.

var foo = "foo1";
var foo = "foo2"; // No problem, 'foo' is replaced.

let bar = "bar1";
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared

 

 

그리고 아까위의 for문은 다음과 같이 고칠 수 있다.

var funcs = [];
// let's create 3 functions
for (let i = 0; i < 3; i++) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}
//output
//My value: 0
//My value: 1
//My value: 2

 

*주의 : ie에서는 let을 지원하지않으므로, webpack과 같은 bundle을 쓰지않는다면 쓰지말자.

 

Scenario


최근 git cli를 사용하려고 노력중이다보니, Source tree를 사용하지 않고 직접 cmd를 치다보면 merge를 해야할지 rebase를 해야할지, 헷갈리때가 있다. 또한 옵션에 대해서도 잘 모르고 써었는데 한번 정리해보자 한다.

 

 

Solution


기본적으로 git pull = git fetch + git merge 이다.

그러면 어느때 git merge를 쓰고 어느때 git rebase를 써야할까?

 

Merge

현재 C2 parent에서 brach가 둘로 분기되었다 가정하자
master로 checkout 한후 merge 하였다. (checkout master && merge experiment)

위의 경우가 통상적인 merge 방법이나 이렇게 되면 작업 history가 가시적일 순 있어도 branch가 많아지면 많아질수록 알아보기가 어려워진다. 사람들은 이러한 이유로 rebase를 쓴다.

 

rebase

똑같은 상황 가정

 

 

'experiment에서 master rebase (checkout experiment && rebase master) 

master 브랜치를 Fast-forward시킨다 ('앞으로 진행한' 커밋인 master 브랜치 포인터는 최신 커밋으로 이동한다. 이런 Merge 방식을 'Fast forward'라고 부른다.)

 

완료후 정리된 커밋모습

 

위와같이 c3` 커밋메세지 기반으로 history가 정리됨을 알수있다.

tip) git rebase [basebranch] [topicbranch] 처럼 인자를 준다면 일일히 checkout 하지 않고 rebase할 수있다.

위 그림같은 경우는
$ git rebase master experiment

 

 

 Merge

Rebase 

 특징

 -  branch의 최종 결과만을 가지고 합병

 - 지정한 브랜치를 베이스로 기준 삼아 합병

 - 중복수정된 로그가 남지않는다.

 - 브랜치의 변경사항을 순서대로 다른 브랜치에 적용하면서 합병

 장점

 - 이해하고 사용하기 쉬움

 - 브랜치 컨텍스트 유지

 - history가 단순해짐

 - branch가 많을때 커밋을합치는 직관적인방법

 단점

 - 히스토리가 난잡

 - 커밋 순서대로 rebase를 하는데, 각 커밋마다 충돌해소를 순서대로 해주어야하여 복잡함

 

추가유용 명령어
- FILE 되돌리기 및 삭제편

git checkout [-- 파일명]
아직 스테이징이나 커밋을 하지 않은 파일의 변경내용을 취소하고 이전 커밋상태로 돌린다. svn에서 revert와 동일하다 (그냥 git checkout branch를 할경우 현재 활성화된 브랜치를 바꾸는 명령어이다)  

git checkout -- . wd에 수정된내용 모두 되돌림

git diff [--cached]
스테이징영역과 현재 작업트리의 차이점을 뵤어준다. --cached 옵션을 추가하면 스테이징영역과 저장소의 차이점을 볼 수 있다. git diff HEAD를 입력하면 저장소, 스테이징영역, 작업트리의 차이점을 모두 볼 수 있다. 파라미터로 log와 동일하게 범위를 지정할 수 있으며 --stat를 추가하면 변경사항에 대한 통계를 볼 수 있습니다.
git diff --ours 머지이전과 머지이후 결과비교

git reset — hard HEAD^
commit한 이전 코드 취소하기

git merge --abort 머지 취소하기(커밋이나 stash 하지않는 존재시 못함)

git config — global user.name “user_name ”
git 계정Name 변경하기

git config — global user.email “user_email” 
git 계정Mail변경하기

git stash / git stash save “description”
작업코드 임시저장하고 브랜치 바꾸기

git stash pop
마지막으로 임시저장한 작업코드 가져오기

git reset HEAD [-- filesName] stage에 있는 파일 내리기

git reset — soft HEAD^ 
코드는 살리고 commit만 취소하기

git reset — merge 
merge 취소하기

git reset — hard HEAD && git pull  git 코드 강제로 모두 받아오기

git reset --hard HEAD 머지하기이전상태로 모두되돌림

git reset --[hard] [mixed] [soft] 

--hard: reset하기 전까지 했던 staging area, working directory의 작업까지 모두 reset!
(모든 게 잘못됐어! 나 돌아갈래~ 꽃피던 때부터 정갈하게 다시 해보자!)
--mixed(default): staging area은 reset, reset하기 전까지 했던 working directory의 작업은 남겨둠.
(현재 작업물은 지우긴 싫고, 이전 버전으로 돌아가서 add할지 말지 결정해야 할 때)
--soft: reset하기 전까지 했던 staging area, working directory의 작업은 남겨둠.
(reset한 버전과 현재까지의 작업을 합쳐 새로운 버전 만들 때)
git reset --hard 커밋된 파일빼고 모두삭제
git clean -df 커밋,stage 되지 않은 파일 폴더 모두삭제 [untrack 중인] (매우 유용) 

 

-- 유용한명령어들

git add .
git commint -m "{msg}"
git remote -v  --list all
git remote set-url origin "{repo-url}"
git remote add origin "{repo-url}"
git remote remove origin
git push --set-upstream origin master 초기
git config --list
git commit --author "asd<asd@gmail.com>" -m
git log --graph --oneline --all  로그를 커밋메시지는 한줄로 그래프형태로 모두보기
git log --graph --oneline --all --pretty="format:[%h]%s - %an" 커미터지정해서 보기

+git stash

stash는 다시 한번 말하지만 하고 있던 작업을 잠시 담아두는 역할을 한다.

따라서 명령어는 크게 두 가지만 기억하면 된다.

git stash(=git stash save) : 하던 작업을 저장하고 가장 최근 commit상태로 만든다.

git stash pop 또는 git stash apply : 저장되어 있는 작업중 가장 최근 stash를 가져온다.

이 외에 명령어 옵션들

git stash list : stash 목록을 봄 stash@[숫자]형식으로 보여지며 0번이 가장 최근 1,2,3... 이런식으로 밀림

git stash drop[stash@[숫자]] : stash를 따로 지정하지 않으면 최신의 stash삭제

* git stash pop은 git stash apply + git stash drop을 같이 한 것과 같은 효과임.

  즉, git stash pop은 한번 불러오면 stash 목록에 저장한 시점이 삭제되어있고 git stash apply는 해당 stash를 불러와도 여전히 list에 남아 있음.

 

Scenario


react를 쓰다보면 (asd는 fuction ) asd.bind(this) 이런구문을 쓰게된다.

솔직히 this의 인자에대한 의미도 잘알지못했고

또한 component의 onclick을 재정의할때,

onClick= handleOnclick.bind(this,{flag:'add',value:'asd'}); 이런식으로 개인적으로 자주쓰는편인데, 

위와 같은식으로 쓸때는 재정의한 fuction에서 인자를 어떻게 받아줘야할지 고민이었다.

 

 

Solution


내가 이해한 바로는 Fuction.proptotype.bind 를 쓰는 이유는 2가지가 있다.

 

 

첫번째는 Fuction의 스코프를 지정할 때이고

두번째는 매개변수를 고정하기 위해서이다.

 

첫번째의 예는 다음과 같이 쉽게 이해 할 수있다.

this.x = 9;
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 81

var retrieveX = module.getX;
retrieveX();
// 9 반환 - 함수가 전역 스코프에서 호출됐음

// module과 바인딩된 'this'가 있는 새로운 함수 생성
// 신입 프로그래머는 전역 변수 x와
// module의 속성 x를 혼동할 수 있음
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81

출처:https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

scope를 달리하여 호출할때 불러오는 변수가 유동적으로 변경된다.

기본적으로 react는 컴포넌트 기반이므로, 원하는 컴포넌트의 스코프를 사용하기 위해

bind(this)를 해주는것이다.

첨언: 초보 JavaScript 프로그래머로서 흔한 실수는 객체로부터 메소드를 추출한 뒤 그 함수를 호출할때, 원본 객체가 그 함수의 this로 사용될 것이라 기대하는 겁니다(예시 : 콜백 기반 코드에서 해당 메소드 사용). 그러나 특별한 조치가 없으면, 대부분의 경우 원본 객체는 손실됩니다. (출처:모질라재단)

 

두번째 방법으로 사용할 때는 미리 지정된 파라미터가 있는 함수를 만드는 것이다.

상황1. 고정된 인수로 override 해버리는경우
function addArguments(arg1, arg2) {
    return arg1 + arg2
}

var result1 = addArguments(1, 2); // 3

// 첫 번째 인수를 지정하여 함수를 생성합니다.
var addThirtySeven = addArguments.bind(null, 37); 

var result2 = addThirtySeven(5); // 37 + 5 = 42 

// 두 번째 인수는 무시됩니다.
var result3 = addThirtySeven(5, 10); // 37 + 5 = 42



상황2 전달받은 인수를 뒤로 밀어버리는경우,
function list() {
  return Array.prototype.slice.call(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

// 선행될 인수를 설정하여 함수를 생성합니다. 
var leadingThirtysevenList = list.bind(null, 37);

var list2 = leadingThirtysevenList();  // [37]

var list3 = leadingThirtysevenList(1, 2, 3);  // [37, 1, 2, 3]

 

위 코드를 본다면 아주 쉽게 이해 할 수있다.

 

Scenario


원래 Spring Project에서 http request/response를 json 형태로 주고받았으나 jsp를 리턴해줄 경우가 생겼다. 그러나 jsp 리소스를 리턴해주려고하니 몇가지 에러가 나는 것 이었다.

1.jsp 자원을 요청해주는 버튼을 클릭하니
아래와 같은 문구가  chorme console 창에 뜨는 것이 아닌가?

Refused to display 'http://localhost:8080/xxx/xxx' in a frame because it set 
'X-Frame-Options' to 'DENY'.

 

구글링을 해보니 "기본적으로 X-Frame-Options는 클릭 잭킹 공격을 막기 위해 거부 됨으로 설정됨" 이라고 한다.

*클릭재킹: 웹 사용자가 자신이 클릭하고 있다고 인지하는 것과 다른 어떤 것을 클릭하게 속이는 악의적인 기법

 

2. Spring Security 설정을 통해 (1) 번 문제는 고칠 수 있엇으나, 이제는 갑자기 404에러가 났다.

	<mvc:view-resolvers>
    	<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
			<property name="order" value="0"/>
	        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />    
	    </bean>
	</mvc:view-resolvers>

 위와 같이 설정된 return values handler 설정을 고친 방법을 알아보자.

Solution


1. X-frame-Options 해결방법.

xml 설정을 사용중이라면 Filter체인에서 아래와 같이 headers 설정을 해주면 된다.

<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:security="http://www.springframework.org/schema/security"> 
  <security:http>
      <security:headers>
           <security:frame-options disabled="true"></security:frame-options>
      </security:headers>
  </security:http>
</beans>

 

2. Jsp resource 404 not found 해결방법.

ReturnValuesHandler의 프로젝트 내부의 경로옵션인 prefix와 suffix를 써주지 않아서 jsp 리소스를 써주지 않아서 일어난 일이다. 보통 jsp 리소스를 /WEB-INF/views에 많이 놓지만, 나는 /jsp/ 라는 폴더에 넣어주었다. 

<mvc:view-resolvers>
    <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
		<property name="order" value="0"/>
	    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />    
		<property name="prefix" value="/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>
</mvc:view-resolvers>

위와 같이 설정해주니 잘 동작하였다.

 

--------------

*Xframe관련 출처:https://cnpnote.tistory.com/entry/SPRING-Spring-Security%EC%97%90%EC%84%9C-X-Frame-Options%EC%9D%91%EB%8B%B5-%ED%97%A4%EB%8D%94%EB%A5%BC-%EB%B9%84%ED%99%9C%EC%84%B1%ED%99%94%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C

Scenario


내 프로젝트에서 commons.jar 내부에 있는 commonController의 로직을 바꾸고 싶었다. 내 프로젝트 내부에서 새로 정의한다면 오버라이드 되지 않을까? 하고 생각해보았다.

 

 

Solution


class load scenario
예를 들어보자. 만약 내 프로젝트의 이름은 test이고 test 프로젝트 내부에서 a.jar에 대한의존성을 추가하였다.

a.jar 내부에있는 com.a.a 라는 class가 있었다.  
test 프로젝트 내부에서 com.a.a 라는 경로와 똑같이 class를  만든다면 어느 class가 load가 될까?

-> 정답은 배포되었을시에 web-inf에 있는 classes 폴더에있는 것들이 최우선순위로 load 되고 그다음부터는 lib 내부에 있는 jar들이 알파벳 순서에따라 class가 loadㄷ가 된다.

참고사항: tomcat 실행시 java -verbose:class 옵션을주면  어느 jar에서 load 되었는지 알게해줌
+추가 공부내용: 배포될때 WEB-INF/classes 폴더는 classpath와 관계가 있으니 잘 확인해볼 것

 

Scenario


Maven Pom.xml 파일을 업데이트 한뒤, compile error는 모두 잡았으나 tomcat을 실행할때 계속해서 

java.util.zip.ZipException: invalid LOC header (bad signature)

.....

No naming context bound to this class loader 

위와 같은 에러가 계속해서 떳다. 

에러를 해결하기 위한 삽질에 대해 적어보겠다.

 

Solution


사실

java.util.zip.ZipException: invalid LOC header (bad signature)

에러가 떳을때 구글링을 해보면 가장 많이 나오는 해결 방법은 그냥 maven repository를 삭제했다가 update를 하라고 가장 많이나온다. 하지만 나의경우에는 다 삭제 후 메이븐을 다시업데이트 해도 에러가 계속해서 났다. 그리고 메이븐 업데이트 할때 시간이 엄청나게 오래걸려서 스트레스가 쌓여만 갔다.

가장좋은 방법은 

1. java.util.zip.ZipException 내 ZipException 메소드에 break point를 걸어준다

2.에러가 날때 왼쪽 스택트레이스를 보면 JarFile.getManifestFromReference() 하는 부분이 있다. 그 부분을 더블 클릭해 본다.

Zipexceiption Break point setting 시 starck trace

3.manEntry 변수 내부에 jar를 확이하여 그 jar파일만 삭제 후 다시 받아본다.

위와 같이 하면 통짜로 repository를 삭제 할 필요가 없이 자르만 삭제 후 다시받으면된다

 

manEntry 내부 member 들

 

출처: https://stackoverflow.com/questions/32090921/deploying-maven-project-throws-java-util-zip-zipexception-invalid-loc-header-b

 현재 컴퓨터의 환경

Os

 Windows 10

 IDE

 STS 4.1.3

 Jdk

 1.8.0_211

 

1. STS.ini 최적화 설정변경

* 현재 사용중인 STS.ini 설정내용

-Dosgi.requiredJavaVersion=1.8

-vm

C:/Program Files/Java/jdk1.8.0_172/bin/javaw.exe

--add-modules=ALL-SYSTEM

-Dosgi.module.lock.timeout=10

-Dorg.eclipse.swt.browser.IEVersion=10001

-XX:+UseParallelGC

-XX:permSize=128m

-Xms1024m

-Xmx4096m-Dosgi.requiredJavaVersion=1.8
-vm
C:/Program Files/Java/jdk1.8.0_211/bin/javaw.exe
-Xms1024m
-Xmx4096m
-XX:+UseParallelGC
-XX:+UseStringDeduplication
--add-modules=ALL-SYSTEM
-XX:permSize=128m
-Xverify:none
-XX:+UseParallelGC
-XX:+AggressiveOpts
-XX:-UseConcMarkSweepGC
-XX:PermSize=128M
-XX:MaxPermSize=128M
-XX:NewSize=128M
-XX:MaxNewSize=128M
-XX:NewSize=128M

 

 

  • 선택사항

-Xverify:none

-XX:+UseParallelGC

-XX:+AggressiveOpts

-XX:-UseConcMarkSweepGC

-XX:PermSize=128M

-XX:MaxPermSize=128M

-XX:NewSize=128M

-XX:MaxNewSize=128M

-XX:NewSize=128M

  • 설명

1) -vm 

    C:/Program Files/Java/jdk1.8.0_172/bin/javaw.exe 

    >이클립스에서 사용할 JDK

2) -Dosgi.requiredJavaVersion=1.8

    > 이클립스가 사용할 최소버전의 JDK

3) -Xverify:none 

    > 설정시 이클립스 초기 구동시 클래스 및 플러그인의 유효성 검사를 건너뛰겠다는 의미이며 구동속도 향상

4) -XX:+UseParallelGC

   > 병렬 가비지 컬렉션사용, 병렬처리로 인한 처리속도 향상

5) -XX:+AggressiveOpts

   > https://www.opsian.com/blog/aggressive-opts/

6) -XX:-UseConcMarkSweepGC

   > http://zzikjh.tistory.com/entry/Java-GC%ED%83%80%EC%9E%85-%EB%B0%8F-%EC%84%A4%EC%A0%95-%EC%A0%95%EB%B3%B4

7) -XX:PermSize=128M

   -XX:MaxPermSize=128M

   > JVM 클래스와 메소드를 위한공간이며 Out of Memory 발생시 크기 상향조절

8) -XX:NewSize=128M

   -XX:MaxNewSize=128M

   > 새로 생성된 객체들을 위한 공간

9) -Xms1024m

   > 이클립스가 사용하는 최소메모리

10) -Xmx4096m

  > 이클립스가 사용하는 최대메모리 - 사용할 컴퓨터에따라 할당크기를 변경해줘야됨.

 

2. 이클립스 Show Heap Status

 - Window > preferences > General - Show heap status 체크

 

3. 스펠링체크 설정 해제

 - Window > preferences > General > Text Editors > Spelling - Enable spell checking 체크해제

 

4. Automatic Updates

 - Window > preferences > Install/Update > Automatic Updates - Automatically find new updates and notify me 체크해제

 

5. 이클립스 인코딩 UTF-8 설정

- Window > preferences > General >Workspace - Text file encoding

 

6.Controll 눌렀을시 느려지는 현상 해결

- Window -> Preferences -> General -> Editors -> Text Editors -> Hyperlinking 나는 다 uncheck

Scenario


스프링 시큐리티 소스를 적용하려고 했는데, 소스 옆에 GA 및 SNAPSHOT이라는 태그가 붙여 있었다. 가장 높고 최신 버전의 소스가 좋은 것인지 아니면 무엇을 보고 판단해야 할지 모르겠어서 포스팅

 

 

Solution


- GA (General Availability) - 테스트가 완료된 정식 릴리즈 버전으로 안정적으로 운영되어야 하는 프로젝트에서 사용. 대부분 GA 버전을 가져다 사용하면 된다.

- RC (Release Candidate) - 베타 버전이다. 정식 릴리즈 버전은 아니므로 기능은 픽스되었으나, 안정적 동작은 보장할 수없다.

- M (Milestone) - 테스트 버전이다. 아직 기능이 픽스되지 않아 구현될때마다 테스트 버전이 릴리즈 될 수 있다.

- SNAPSHOT - 스냅샷이 붙으면 아직 개발단계라는 의미이며, 일종의 백업시점인 것이다.

Scenario


기존 AS-IS 프로젝트의 url pattern이 아래와 같이 .do 로 끝나는 url만 이 action이라는 이름의 dispatcher sertvlet으로 요청을 받게 설정이 되어 있었다.

    <servlet-mapping>
        <servlet-name>action</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

나는 이를 /* 또는 / 으로 받으려고 했는데, main.html을 못불러오는게 아닌가?

    <servlet-mapping>
        <servlet-name>action</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

 

이유를 알고자 다음과 같이 정리하였다.

 

Solution


위를 알려면 서블릿 컨테이너와 웹 어플리케이션의 연동 방법을 알아야한다.

실제로 WEB-INF 외부에 있는 jsp 파일을 요청하더라도, 직접 접근하는게 아니라 tomcat was라는 미들웨어를 통해  브라우저가 알아볼 수 없는 jsp 파일을 html로 변형해서 서버로 내려준뒤 브라우저로 내려준다.

(was는 동적 파일 처리(jsp 같은 파일) web server는 정적 파일 처리)

위와 같은 내용는 tomcat web.xml에 다음과 같은 설정을 통해 알 수 있다.

<servlet-mapping>
	<servlet-name>jspServlet</servlet-name>
	<url-pattern>*.jsp</url-pattern>
</servlet-mapping>

 

앞의 사전지식을 사용하여 url pattern 문제에 대해 접근해보자.

'/*' 은 요청받는 모든 URL을 처리한다는 의미이다. 모든 요청을 처리하게 되면 .jsp라는 view에 대한 요청도 action 이라는 이름의 Dispatcher Servlet이 처리하겠다는 것이다. 이렇게 되면 .jsp 파일은 tomcat web,xml 에서 정의된 jsp Servlet으로 처리되어지지 않고 .jps 요청에 대한 view를 내려줄 수 없다. (action dispatcher servlet설정에 의해 마스킹)

'/' 로 해준다면 default servlet 타게되는 요청들이 action servlet을 타게 되는데  *.jsp은 톰캣에 설정에 의해 dispatcher servlet에서 처리되지 않고 WAS내 jsp servlet으로 처리되므로 제대로 view를 리턴해줄 수 있으나. 여기서 문제는 톰캣 설정에서 default servlet 은 .html .css .js 같은 정적인 파일들을 처리 해주었으나 이제 처리해 줄 수 없으므로, 정적인 파일들에 대하여 404 not found error가 뜨게된다. (action servlet이 그것을 마스킹 하게 되어버린다.)

이제 여기서 필요한 설정이 context-dispatcherServlet.xml 내에 아래와 같은 설정이다.

<mvc:resources location="/resources/" mapping="/resources/**"/>
//또는
<mvc:default-servlet-handler />

위와  xml을 설정하게 된다면, 정적파일을 호출하게 되는 Request URL에 대하여 default servlet으로 위임 하는 것이다.

+ Recent posts