Quantcast
Channel: Uncle Bae's Story
Viewing all 50 articles
Browse latest View live

SpringBoot에서 Log설정하기.

$
0
0

SpringBoot에서 Log 설정하기. 

Spring Boot에서 로깅을 위해서는 spring-boot-starter-logging을 의존성에 추가하면 된다.
웹 어플리케이션을 사용한다면, spring-boot-starter-web만 사용하면 된다.

Maven 의존성 추가하기 :
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

application.properties 설정하기. 
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate=ERROR
미세한 설정을 위해서는 classpath:logback.xml을 설정할 수 있다. 






Elasticsearch Configuration

$
0
0

Environment Variables 

- EL은 자바의 환경변수 JAVA_OPTS에 따른다.
- 가장 중요한 환경 설정값은 -Xmx (최대 메모리),  -Xms (최소메모리) 의 설정이다.
- 대부분 JAVA_OPTS는 그대로 두고, ES_JAVA_OPTS를 설정하여 JVM의 환경변수를 변경한다.
- ES_HEAP_SIZE 환경 변수는 힙 메모리를 설정하는 값이다.
    - 이 설정값을 설정하면 ES_MIN_MEM(기본 256m), ES_MAX_MEM(기본 1g)을 같은 값으로 설정한다.
# 추천 :
  - 값은 min과 max를 동일하게 하는것
  - mlockall enable로 설정하기.

System Configuration 

File Descriptors

- 머신에서 설정된 open file의 수를 다음으로 증가 시킨다. 32k --> 64k (추천)
- 얼마나 많은 수의 파일이 오픈 될 수 있는지 설정을 한다.
    -Des.max-open-files=true
- 이렇게 설정하면 엘라스틱 서치가 올라올때 최대 오픈할 수 있는 파일 수를 프린트한다.
- 다른 방법으로는 엘라서틱 질의를 통해서 max_file_descriptors를 확인할 수 있다.
curl localhost:9200/_nodes/process?pretty

Virtual memory 

Elasticsearch는 인덱스 저장을 위해서 hybrid mmaps / niofs 디렉토리를 기본으로 사용한다.
  * niofs : 파일시스템에 샤드 인덱스를 NIO를 이용하여 저장한다. 멀티 쓰레드가 동시에 동일한 파일을 읽을 수 있도록 지원한다. Windows에서는 사용하지 않도록 권고한다. Java 구현체에 버그가 있다.
  * mmaps : 파일스스템에 샤드 인덱스를 memory에 파일을 매핑하는 방식으로 저장한다. 메모리 매핑은 파일 크기와 동일한 가상메모리 공간을 이용한다.

기본 OS의 제약은 mmap에 그대로 적용되며, 너무 아래로 떨어지게 되면 out of memory 예외가 발생한다. Linux에서는 다음 명령어를 통해서 제한 값을 올릴 수 있다.
sysctl -w vm.max_map_count=262144
이 값을 영구적으로 설정하기 위해서는 vm.max.map_count 설정을 /etc/sysctl.conf 에 지정하자.
root계정으로만 가능하다.
  * 만약 .deb, .rpm으로 Elasticsearch를 설치한경우라면 이 값은 자동으로 설정된다.

Memory Settings 

대부분의 운영 체제는 시스템 캐시를 위해 가능한 많은 메모리를 사용하고자 한다. 그리고 어플리케이션에서 사용하지 않는 메모리는 swap out 하고자 시도한다. 엘라스틱 서치의 프로세스도 swap이 될 수 있다.
Swapping은 노드의 안정성을 유지하기 위해서 성능을 심각하게 떨어뜨리게 된다. 이러한 swap를 피하는 것이 좋다.

Disable swap 

가장 단순한 옵션으로 swap를 완젼히 정지 시킨다. 일반적으로 EL은 box에서 서비스를 수행하고, ES_HEAP_SIZE에 의해서 컨트롤 된다. swap를 enable로 설정할 필요가 없다.
리눅스 시스템에서 다음 명령어를 통해서 swap를 임시로 끌 수 있다.
sudo swapoff -a 
영구적으로 끄기 위해서는 /etc/fstab를 수정하고, swap로 된 모든 라인을 커멘트 처리 하면 된다.
Windows에서는 다음 경로에서 disable할 수 있다.
System Properties -> Advanced -> Performance -> Advanced -> Virtual memory

Configure swappiness 

두번째 옵션은 sysctl 값인 vm.swappiness를 0으로 설정하는 방법이다.
이것은 커널이 swap 하고자 하는 경향을 줄여주고, 일반적인 환경에서는 swap를 수행하지 않도록 해준다. 긴급 상황에서만 swap를 수행한다.

# 참고 : 커널버젼 3.5-rc1 이상 버젼에서는 swappiness의 값을 0으로 설정하면 OOM killer 가 나타나면 swapping을 수행하는 대신에 해당 프로세스를 kill한다. swappiness 를 1로 설정하면 swapping을 수행한다.

mlockall

이 옵션은 mlockall을 이용하며  Linux / Unix시스템의 기능을 이용한다.  혹은 윈도우에서는 VirtualLock 기능을 이용한다.
이것은 RAM의 공간에 lock을 거는 방법으로 Elasticsearch 메모리가 swapped out되는 것을 막아준다. 이것은 config/elasticsearch.yml 파일에 다음과 같이 정의하는 것으로 설정이 가능하다.
bootstrap.mlockall: true
Elasticsearch가 실행된후 해당 옵션이 성공적으로 적용되었는지 확인은 다음과 같이 수행할 수 있다.
curl http://localhost:9200/_nodes/process?pretty
만약 mlockall이 false로 설정 되어 있다면 mlockall요청이 실패 했음을 의미한다. 이 이유는 Linux/Unix시스템에서 EL이 메모리 lock권한을 가지고 있지 않은 경우 발생한다.
이것은 ulimit -l unlimited 값을 root권한으로 지정하면 가능하다.
mlockall이 실패할 수 있는 또 다른 이유는 /tmp 디렉토리가 noexec옵션으로 마운트 된 경우에 가능성이 있다. 이것은 Elasticsearch를 실행할때 옵션을 선택하여 temp 위치를 변경하는 것으로 해결이 가능하다.
./bin/elasticsearch -Djna.tmpdir=/path/to/new/dir
# mlockall은 JVM이나 shell session을 종료시킬 수 있는데 이것은 가용한 용량보다 더 많은 메모리를 할당하고자 하는 경우 발생될 수 있다.

Elasticsearch Settings

elasticsearch설정 파일은 ES_HOME/config 폴더 아래에 있다. 폴더는 2개의 파일이 있으며, elasticsearch.yml로 Elasticsearch 설정을 하기 위한 파일과, logging.yml로 로기을 설정하기 위함이다.

설정 포맷은 YAML로 되어 있다.

다음은 모든 Base module들의 네트워크를 변경하는 것으로 바인드 시키고 publish를 해준다.
network :
    host : 10.0.0.4

 Paths

사용 환경에서 데이터의 위치와 로그 파일의 위치를 지정하고자 하는 니즈가 있을 것이다.
path :
    logs: /var/log/elasticsearch
    data: /var/data/elasticsearch

Cluster name

실제 환경에서는 절대로 클러스터 이름을 지정하는 것을 빼먹지 말자. 이것은 discover과 auto-join을 위해서 꼭 필요하다.
cluster:
    name: <NAME OF YOUR CLUSTER>
서로다른 환경에서 동일 클러스터 이름을 사용하지 않도록 주의하자. 그렇지 않으면 잘못된 클러스터에 노드가 조인된다. 예를 들면 logging-dev, logging-stage, logging-prod와 같이 클러스터를 나누자.

Node name

기본 노드 이름을 변경하고자 할수도 있다. 각 노드는 노출되는 호스트의 이름이 된다. 기본적으로 Elasticsearch는 랜덤하게 지정된다.
node:
    name: <NAME OF YOUT NODE>
머신의 호스트 이름은 환경 변수 HOSTNAME에 지정된 내역을 따른다. 만약 머신에 하나의 엘라스틱 서치 노드를 수행시킨다면 ${...}을 이용하여 호스트 이름을 지정할 수 있다.
node:
    name: ${HOSTNAME}
내부적으로 모든 설정은 namespaced 구조로 설정된다.
예를 들어 이전에 설명한 node.name으로 지정하여 설정할 수 있다. 이 의미는 설정을 JSON형식으로 쉽게 지정할 수 있다. 만약 JSON형식으로 처리하고자 한다면 elasticsearch.yml을 elasticsearch.json으로 변경하면 된다.

Configuration styles

{
    "network" : {
        "host" : "10.0.0.4"
    }
}
이것은 외부 설정인 ES_JAVA_OPTS를 이용하여 설정할 수 있다.
elasticsearch -Des.network.host=10.0.0.4
다른 옵션은 es.default 이다. prefix가 es.이며 이는 기본 설정을 의미한다. 이값은 명시적으로 설정파일에 지정하지 않으면 기본값으로 설정된다는 의미이다.

다른 옵션은 ${...}을 이용하는 것으로 이것은 환경 변수로 설정된 값을 이용한다.
{
    "network" : {
        "host" : "${ES_NET_HOST}"
    }
}
추가적으로 설정파일에 내역을 저장하지 않고자 한다면 ${prompt.text}혹은 ${prompt.secret} 을 이용할 수 있다. 이는 foreground로 설정값을 입력하라고 묻게 된다.
${prompt.secret} 는 터미널로 입력 하도록 물어본다.
node:
    name: ${prompt.text}
elasticsearch 커맨드를 실행하면 다음과 같은 프롬프트 화면을 보게 된다.
Enter value for [node.name]
# Elasticsearch는 ${prompt.text}혹은 ${prompt.secret}을 설정했을때 백그라운드로 실행하면 elasticsearch는 정상적으로 시작되지 않는다.

Index Settings

인덱스들이 생성되면 각 자신들의 설정을 제공할 수 있다. 예를 들어 다음은 인덱스를 메모리 기반의 저장소로 생성한다. 파일시스템을 생성하지 않고 말이다. 포맷은 YAML이나 JSON으로 지정이 가능하다.
$ curl -XPUT http://localhost:9200/kimchy/ -d \
'
index:
    refresh_interval: 5s
'
인덱스 레벨 설정은 node레벨과 같이 설정이 가능하다. 예를 들어 elasticsearch.yml파일에서 다음과 같이 지정할 수 있다.
index :
    refresh_interval: 5s
이것이 의미하는 바는 각 인덱스는 실행된 특정 노드에서 생성된 것을 획득하며, 이것은 시작시 특별히 지정하지 않으면 설정에 따라 메모리에 인덱스를 저장하게 될 것이다. 즉, 인덱스 레벨의 설정은 노드 설정에 지정된 값을 오버라이딩 한다. 물론 상단 예는 설정의 일부분이다.
$ elasticsearch -Des.index.refresh_interval=5s
모든 인덱스 레벨의 설정값은 각 인덱스 모듈에서 찾을 수 있다.

Loggin 

Elasticsearch는 내부 로깅을 위한 추상화를 제공한다. log4j를 이용한다. 이것은 log4j 설정을 통해서 가능하며 YAML을 이용하여 설정한다. 그리고 로깅 설정 파일은 config/logging.yml 에 지정이 가능하다.
JSON과 properties 포맷도 지정이 가능하다. 로깅 설정 파일은 여러개를 로드 할 수 있다. 이들은 시작시에 하나로 머지된다.
logger 섹션은 자바 패키지를 포함하고 있다. 그리고 각 해당하는 로그 레벨을 지정을 할 수 있다. 이것은 org.elasticsearch 프리픽스를 사용하지 않아도 되도록 한다.
appender 섹션은 로그들의 목적지를 포함한다.

Deprecation logging

추가적으로 elasticsearch는 deprecated된 액션의 로깅을 가능하게 한다. 예를 들어 앞으로 특정 기능을 이관할 필요가 있을때 미리 결정할 수 있도록 해준다. 기본적으로 deprecation로깅은 disabled되어 있다. config/logging.yml파일 에서 DEBUG로 로그 레벨을 설정할 수 있다.
deprecation: DEBUG, deprecation_log_file
이것은 일별 롤링 로그 파일을 로그 디렉토리에 생성한다.

from : https://www.elastic.co/guide/en/elasticsearch/reference/2.1/setup-configuration.html












Spring Transactional Propagation

$
0
0

트랜잭션 전파 : 


MANDATORY
현재 트랜잭션을 지원한다. 만약 트랜잭션이 존재하지 않으면 exception을 던진다. 

NESTED
만약 현재 트랜잭션이 존재하는 경우라면, 중첩된 트랜잭션을 실행한다.
트랜잭션이 없다면 PROPAGATION_REQUIRED 처럼 동작한다.
NEVER
트랜잭션 없이 수행되도록 한다. 만약 트랜잭션이 존재한다면 exception을 던진다. 
NOT_SUPPORTED
트랜잭션을 지원하지 않는다.
원래 현재 트랜잭션은 대기한다. 
REQUIRED
현재 트랜잭션을 지원하며, 존재하지 않는다면 신규 트랜잭션을 생성한다. 
REQUIRES_NEW
신규 트랜잭션을 생성한다. 그리고 원래 현재 트랜잭션은 대기한다. 
SUPPORTS
Support a current transaction, execute non-transactionally if none exists.
트랜잭션을 지원한다. 트랜잭션이 존재하지 않았다면 트랜잭션 없이 수행한다. 




Hibernate LazyInitializationException 해결

$
0
0

LazyInitializationException


Hibernate를 이용하여 개발하다보면, 자주 늘 만나는 녀석이다.
이러한 현상이 발생하는 이유는 다음과 같다.


1. 조회 서비스가 Select를 위한 서비스 (트랜잭션이 걸린)에 조회 요청을 한다.
2. 조회 결과가 반환 되면서 트랜잭션 종료
    - 이때 Entity는 영속상태가 아니라, 준영속 상태로 빠진다.
    - 만약 EAGER 패치로 가져왔다면 Entity의 하위 Entity가 함께 가져왔을 것이다.
      그러나 Lazy로 가져온 경우라면 하위 엔터티는 존재하지 않는 상태임.
3. Lazy 로 가져온 데이터가 준 영속 상태에서 하위 엔터티를 조회하면 LazyInitializationException이 발생한다.


해결방법 :

1. Use Hibernate.initialize :
   Hibernate.initialize 를 이용하여 하위 엔터티를 초기화 한다.
   (여기서 초기화라는 것은 프록시에 해당 엔터티 객체들을 로드해 둔다는 의미이다.)

Hibernate.initialize(topics.getComents());

2. Use Join FETCH
    - JPQL에서 JOIN FETCH문법을 이용하여 한번에 조인 결과를 다 가져온다.
    - EAGER 패치 설정을 기본으로 걸어서 한번에 결과를 가져온다.

3. Use OpenSessionInViewFilter / OpenSessionInViewInteceptor
    - LazyInitializationException은 View에서 보통 많이 발생한다. 이러한 경우 SessionInViewFilter를 걸어서 필터단까지 영속성을 가지고 가거나, 더 짧게는 SessionInViewInterceptor를 걸어 핸들러 단까지만 영속성을 가지고 가는 방법이 있다.

    - 사용시 다음을 주의해야한다.
        - 의도하지 않은 DB변경이 발생하지 않도록 주의해야한다.
        - 성능에 문제를 일으킬 수 있다.

<filter>
    <filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

# 참고 : 
- 일반적으로 LazyInitializationException은 View를 그릴때 해당 문제가 발생한다.
- 만약 데이터 저장시 발생하는 문제라면, 혹은 서비스단에서 해당 데이터 조합을 미리 가져와서 뷰에대한 데이터를 만들어 줄 수 있다면, 트랜잭션을 Service단까지 올려서 처리하면 된다.

Eclipse static import 등록하기

$
0
0
Hamcrest나 Assert 등은 자동으로 ctrl + space를 해도 import되지 않는다.
이때 설정을 통해 한번에 필요한 패키지를 등록해두고 사용하면 편리하다.

1. Eclipse 등록하기

Preferences > Java > Editor > Content Assist > Favorites

다음과 같은 내용을 등록한다.
org.hamcrest.Matchers.*
org.hamcrest.CoreMatchers.*
org.junit.*
org.junit.Assert.*
org.junit.Assume.*
org.junit.matchers.JUnitMatchers.*


참고로 등록할때에는 마지막 클래스 까지만 등록한다. 



2. 사용하기 
등록후 assertThat에 커서를 옮기고 ctrl + space를 클릭하면 자동으로 임포트 된다. 




AngularJS Concept

$
0
0
AngularJS 사이트에서 AngularJS에 대한 개념 설명을 정리하고자 한다.

용어 :


ConceptDescription
Template추가적인 마크업을 포함한 HTML
Directives커스텀 속성(attributes)와 엘리먼트(elements)를 말하며 HTML을 확장한다. 
Modelview에서 사용자에게 보여주고자 하는 데이터이며, 사용자와 인터렉션을 하는 대상 객체이다. 
Scope컨트롤러, 디렉티브, 표현식 등이 접근할 수 있는 컨텍스트를 말하며, 이는 모델이 저장되는 곳이다.
Expressionsscope로 부터 변수 혹은 함수(functions)에 접근한다. 
Compiler템플릿을 파싱하고, 초기화 하는 명령어와 표현식을 초기화 한다. 
Filter사용자에게 노출되기 위해서 표현식의 값을 포매팅한다. 
View사용자가 보게될 내용 
Data Binding모델과 뷰사이에 데이터를 싱크한다. 
Controller뷰 뒷단에 있는 비즈니스 로직이다. 
Dependency Injection객체와 함수(functions)를 생성하고 엮어준다. 
Injector의존성을 인젝션하는 컨테이너이다. 
Module컨트롤러, 서비스, 필더, 디렉티브를 포함하는 app의 서로다른 부분에 해당하는 컨테이너이다. 이것은 Injector에 의해서 설정된다. 
Service재사용가능한 비즈니스 로직으로 views에 독립적인 객체이다. 

데이터 바인딩 : 

다음 예제는 수량과 가격을 계산하는 예제로 각 통화에 따라서 값을 노출하는 예이다.

--------------------------------------------------------------------------------------------
<div ng-app ng-init="qty=1;cost=2">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="qty">
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="cost">
  </div>
  <div>
    <b>Total:</b> {{qty * cost | currency}}
  </div>
</div>
--------------------------------------------------------------------------------------------

Invoice:
Quantity: 
Costs: 
Total: $6.00

샘플 예제 : https://docs.angularjs.org/guide/concepts#template



Template : 상단 코드의 HTML영역을 Template라고 부른다. 
Compiler : Angular가 시작되면, 상기 마크업을 파싱하고 처리하는 역할을 한다. 
View : 로드되고 변환되고 DOM으로 렌더링 된 것을 view라고 부른다. 
Directives : 상기 코드에서 ng-app등과 같은 것을 directive라고 부른다. 이는 특정 행위를 수행하며 이는 attributes나 elements에 적용이 된다. 

상기 예제는 ng-app 속성에 해당하는 directive를 이용하고 있으며 어플리케이션의 초기화를 자동으로 수행하는 역할을 한다. 

input 엘리먼트에 있는 ng-model 디렉티브는 입력된 값을 저장하고 업데이트 하는 역할을 한다. 

Custom directives to access the DOM:
DOM에 접근하는 Custom directives : Angular에서는 오직 directive를 통해서만 DOM에 접근하도록 하고 있다. 이것은 매우 중요한 포인트로 DOM에 직접 접근하는 것은 매우 테스트하기가 어렵게 만든다. 만약 DOM에 직접 접근하고자 한다면 이것을 위한 Custom directive를 작성하는 것을 권고한다. 
{{ expression | filter }} 로 두겹의 {혹은 } 를 사용하여 데이터를 표시하고 있다. 이것은 마크업의 값을 평가할때(evaluate) 해당 값을 교체하게 된다.
Expression은 템플릿에서 JavaScript와 같은 코드를 작성할 수 있는 것으로, Angular가 값을 읽고 쓸 수 있도록 해준다. 

이 변수값은 global값이 아니다. JavaScript에서와 같이 scope내에서 유효한 값이된다. 
Scope는 Angular에서 값의 범위를 제공하며, 이는 expression을 위한 접근가능한 범위이다. 

값은 scope에 저장되며, 이것은 model에서 접근된다. 
상단 예제는 입력 필드에서 들어온 값을 서로 곱한 값을 저장하고 보여준다. 

Filter는 사용자에게 보여줄 표현식의 데이터를 포매팅하는 것이다. 상기 예제에서는  currency 포맷을 이용하여 금액을 표시하고 있다. 

상기 예제에서 중요한 것은 Angular가 live binding을 제공한다는 것이다. 입력값이 변경되면 표현식의 값이 자동적으로 재 계산되며, 이 값은 계산된 값으로 DOM에 반영된다. 

이 개념은 two-way data binding이라고 한다. 
One-way data binding : 
  기존의 템플릿을 이용한 방식 JSTL, FreeMarker 등과 같이 서버에서 모델과 템플릿을 조합하여 최종 결과를 클라이언트에 전송하여 브라우저가 렌더링 하는 방식
Two-way data binding : 
  템플릿이 라이브 View로 컴파일되고, 뷰의 변경사항이 모델에 반영되고, 모델의 변경사항이 View에 즉각 변경되는 형태를 말한다. 

Controller : 

상기 예제에서 추가적인 로직을 넣어보자. 이는 서로다른 통화에서 가격을 계산하고, 주문을 결제하는 내용을 담고 있다.

------------------------------------------------------------------
angular.module('invoice1', [])
.controller('InvoiceController', function() {
  this.qty = 1;
  this.cost = 2;
  this.inCurr = 'EUR';
  this.currencies = ['USD', 'EUR', 'CNY'];
  this.usdToForeignRates = {
    USD: 1,
    EUR: 0.74,
    CNY: 6.09
  };

  this.total = function total(outCurr) {
    return this.convertCurrency(this.qty * this.cost, this.inCurr, outCurr);
  };
  this.convertCurrency = function convertCurrency(amount, inCurr, outCurr) {
    return amount * this.usdToForeignRates[outCurr] / this.usdToForeignRates[inCurr];
  };
  this.pay = function pay() {
    window.alert("Thanks!");
  };

});

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


------------------------------------------------------------------
<div ng-app="invoice1" ng-controller="InvoiceController as invoice">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="invoice.qty" required >
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="invoice.cost" required >
    <select ng-model="invoice.inCurr">
      <option ng-repeat="c in invoice.currencies">{{c}}</option>
    </select>
  </div>
  <div>
    <b>Total:</b>
    <span ng-repeat="c in invoice.currencies">
      {{invoice.total(c) | currency:c}}
    </span>
    <button class="btn" ng-click="invoice.pay()">Pay</button>
  </div>

</div>

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


Invoice:
Quantity: 
Costs:  
Total: USD2.70 EUR2.00 CNY16.46 



Controller : 상기 코드를 보면 controller이라는 새로운 자바 스크립트 코드가 들어 있다. 
이는 실제 controller인스턴스를 생성하는 코드이며, 컨트롤러의 목적은 값을 노출하고, expression과 directive에 기능을 추가하는 역할을 한다. 

컨트롤러를 생성하고나면 이를 사용하기 위해서는 HTML에 ng-controller디렉티브를 추가하여 사용할 수 있다. 이 디렉티브는 Angular에 InvoiceController가 디렉티브의 엘리먼트와 그 하위의 엘리먼트 전체에대한 처리를 책임진다는 의미이다. 

문법은 InvoiceController as invoice로 Angular에게 컨트롤러의 인스턴스와 이 인스턴를 저장하고 있는 invoice 변수를 현재 scope에 지정하였다는 것을 알려준다. 

이후에는 invoice.이라는 프리픽스를 붙여서 컨트롤러 인스턴스에 접근할 수 있다. 

ng-repeat라는 디렉티브를 이용하여 Currency 리스트를 view에 노출할 수 있다. 

상기에 저정된 total 함수는 {{ invoice.total(...) }} 의 형태로 처리결과를 화면에 노출할 수 있다. 

버튼의 경우 ngClick를 이용하여 해당 이벤트를 기동할 수 있도록 한다. 이것은 버튼이 클릭된경우 해당하는 표현식을 검증하는 방식으로 수행된다. 

새로운 자바 스크립트 파일은 module를 생성하고 있다. 이것은 컨트롤러를 등록하는 역할을 수행하고 있다. 

Service :

InvoiceController 는 모든 예제를 포함하고 있다. 그러나 어플리케이션이 확장되고 커지면 view에 독립적인 모델로 Controller에서 부터 로직 부분을 Service로 떼어내는 것이 좋다. 
이를 통해서 다른 파트에서 동일한 처리를 하는 Service를 재사용 가능하게 된다. 
이후 우리가 가격계산하는 로직이 변경되는 경우 Controller의 변경없이 Service의 변경만으로 원하는 작업을 수행할 수 있다.


------------------------------------------------------------------
angular.module('finance2', [])
.factory('currencyConverter', function() {
  var currencies = ['USD', 'EUR', 'CNY'];
  var usdToForeignRates = {
    USD: 1,
    EUR: 0.74,
    CNY: 6.09
  };
  var convert = function (amount, inCurr, outCurr) {
    return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
  };

  return {
    currencies: currencies,
    convert: convert
  };
});
------------------------------------------------------------------


------------------------------------------------------------------
angular.module('invoice2', ['finance2'])
.controller('InvoiceController', ['currencyConverter', function(currencyConverter) {
  this.qty = 1;
  this.cost = 2;
  this.inCurr = 'EUR';
  this.currencies = currencyConverter.currencies;

  this.total = function total(outCurr) {
    return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr);
  };
  this.pay = function pay() {
    window.alert("Thanks!");
  };
}]);
------------------------------------------------------------------


------------------------------------------------------------------
<div ng-app="invoice2" ng-controller="InvoiceController as invoice">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="invoice.qty" required >
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="invoice.cost" required >
    <select ng-model="invoice.inCurr">
      <option ng-repeat="c in invoice.currencies">{{c}}</option>
    </select>
  </div>
  <div>
    <b>Total:</b>
    <span ng-repeat="c in invoice.currencies">
      {{invoice.total(c) | currency:c}}
    </span>
    <button class="btn" ng-click="invoice.pay()">Pay</button>
  </div>
</div>

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

우리는 이전예제에서 convertCurrency 함수를 새로운 파일에 이동시켰다. 
그렇다면 어떻게 controller이 새로 때어낸 함수를 이용할 수 있을까? 


Dependency Injection : 여기서 사용하는 것이 Depencency Injection이다. 이는 소프트웨어 디자인 패턴으로 어떻게 객체나 함수가 생성되는지 그리고 어떻게 이러한 의존성을 획득하고 사용할 수 있는지를 알려준다. 
Angular에서 directives, filters, controllers, services 등등은 생성되고, depencency를 추가할 수 있도록 되어 있다. 

의존성을 이용하기 위해서는 의존성 추가 파트에 해당 의존성을 추가해 주어야 한다. Angular에서 이러한 일을 해주는 것이 module이다. Angular가 시작되면 ng-app 디렉티브의 설정을 이용하게 되며, 모든 의존성 있는 모듈들을 추가한다.

상기 예제에서는 ng-app="invoice2"디렉티브를 템플릿에 추가하였으며 이것은 Angular에게 invoice2를 어플리케이션의 메인 모듈이며 이를 이용하겠다고 알리는 것이다. 
이후 코드 부분에서 angular.module('invoice2', ['finance2']) 를 이용하여 invoice2가 finance2 모듈에 의존성을 갖는다는 것을 알려준다. Angualr는 InvoiceController를 이용하며 게다가 currencyConverter 서비스도 함께 이용하게 된다. 

이제 Angular는 어플리케이션의 모든 파트를 알고 있게 되었다. 이제는 그것들을 생성하는 일이 남아 있다. 이전 섹션에서는 controllers는 factory 함수를 이용하여 컨트롤러가 생성된 것을 확인했다. 서비스도 다양한 방법으로 그들의 factory를 정의할 수 있다. 상단의 예제에서 우리는 currencyConverter함수를 서비스 팩토리를 이용하여 반환하였다. 

어떻게 InvoiceController가 currencyConverter 함수의 레퍼런스를 획득할 수 있을까? 
Angular에서는 단순하게 정의한 생성자 아규먼트로 이 행위를 수행할 수 있다. injector는 객체를 올바른 순서로 생성하고, 이전 생성된 객체를 의존성이 있는 객체의 팩토리에 전달할 수 있는 방법을 제공한다. 
InvoiceController는 currencyConverter로 이름된 아규먼트를 가진다. 이를 통해서 Angular는 컨트롤러와 서비스의 의존성에 대해 알게 되며, 컨트롤러에서 서비스의 인스턴스를 아규먼트를 통해서 접근할 수 있게 된다. 

이전 섹션과 이 섹션의 차이는 이제는 module.controller함수에서 배열을 전달하고 있다는 것이다. 
배열의 첫번째는 의존성이 있는 서비스의 이름을 의미하며, 이것은 컨트롤러에서 필요한 값이다. 배열의 마지막 엔트리는 생성자 함수이다. Angualr에서는 array를 이용하여 DI를 수행하여 코드를 최소화 하고 있다. 

Accessing the backend

------------------------------------------------------------------
angular.module('invoice3', ['finance3'])
.controller('InvoiceController', ['currencyConverter', function(currencyConverter) {
  this.qty = 1;
  this.cost = 2;
  this.inCurr = 'EUR';
  this.currencies = currencyConverter.currencies;

  this.total = function total(outCurr) {
    return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr);
  };
  this.pay = function pay() {
    window.alert("Thanks!");
  };
}]);
------------------------------------------------------------------


------------------------------------------------------------------
angular.module('finance3', [])
.factory('currencyConverter', ['$http', function($http) {
  var YAHOO_FINANCE_URL_PATTERN =
        '//query.yahooapis.com/v1/public/yql?q=select * from '+
        'yahoo.finance.xchange where pair in ("PAIRS")&format=json&'+
        'env=store://datatables.org/alltableswithkeys&callback=JSON_CALLBACK';
  var currencies = ['USD', 'EUR', 'CNY'];
  var usdToForeignRates = {};

  var convert = function (amount, inCurr, outCurr) {
    return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
  };

  var refresh = function() {
    var url = YAHOO_FINANCE_URL_PATTERN.
               replace('PAIRS', 'USD' + currencies.join('","USD'));
    return $http.jsonp(url).then(function(response) {
      var newUsdToForeignRates = {};
      angular.forEach(response.data.query.results.rate, function(rate) {
        var currency = rate.id.substring(3,6);
        newUsdToForeignRates[currency] = window.parseFloat(rate.Rate);
      });
      usdToForeignRates = newUsdToForeignRates;
    });
  };

  refresh();

  return {
    currencies: currencies,
    convert: convert
  };
}]);

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


------------------------------------------------------------------
<div ng-app="invoice3" ng-controller="InvoiceController as invoice">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="invoice.qty" required >
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="invoice.cost" required >
    <select ng-model="invoice.inCurr">
      <option ng-repeat="c in invoice.currencies">{{c}}</option>
    </select>
  </div>
  <div>
    <b>Total:</b>
    <span ng-repeat="c in invoice.currencies">
      {{invoice.total(c) | currency:c}}
    </span>
    <button class="btn" ng-click="invoice.pay()">Pay</button>
  </div>
</div>
------------------------------------------------------------------

이 finance모듈에서 우리는 $http를 이용하고 있다. 이것은 내장 서비스로 Angular가 서버의 backend로 접근할 수 있도록 제공해준다. $http는 XMLHttpRequest와 JSONP 전송을 wrapping하고 있다. 

from : https://docs.angularjs.org/guide/concepts





Angualr Scope란?

$
0
0

Scope란 무엇인가? 

Scope는 어플리케이션 모델을 참조하고 있는 객체를 말한다.
이것은 expression을 위한 컨텍스트를 실행하게 된다.
Scope는 DOM 구조와 닮은 계층적 구조로 나열되어 있다.
Scopes는 expressions와 이벤트 전이를 watch할 수 있다.

Scope의 특징 : 

Scopes는 $watch API를 제공하며 이는 모델의 변경을 감시한다.
Scopes는 $apply API를 제공하며, Angular왕국 (controllers, services, Angular event handlers)와 같은 외부로 부터 발생된 변경사항을 부로 반영한다.

Scope는 공유된 모델 속성에 접근을 제공하는 반면 어플리케이션 컴포넌트의 속성에 접근하기 위한 중첩된 접근 제한도 될 수 있다. 중첩된 scope는 "child scopes"혹은 "isolate scopes"들이며, "child scope"부모 스콥으로부터 속성을 상속 받는다. "isolate scope"는 그렇지 못하다.

Scope는 evaluated된 expression에 대한 컨텍스트를 제공한다. 예를 들어 {{username}}표현식은 username 속성이 정의된 특정 스콥에 대한 evaluate가 없다면 무의미한 것이된다.

Data-Model로서의 Scope : 

Scope는 어플리케이션의 컨트롤러와 view를 서로 접착하는 접착제이다. template 링킹 단계동안 directives는 scope에서 $watch 표현식을 설정한다. $watch는 속성의 변경에 대한 알림이 되도록 지시할 수 있다. 이것은 DOM 값을 업데이트 하게 된다.

컨트롤러와 디렉티브는 scope에 대한 참조를 가진다. 그러나 서로는 참조할수 없다. 이러한 고립정책은 DOM으로 부터 controller을 분리 시킨다.
이것은 매우 중요한 포인트로 컨트롤러는 뷰가 필요 없게 하고, 뷰는 컨트롤러가 필요없게 만들어 주어 어플리케이션 스토리의 테스트를 매우 향상 시켜준다.

------------------------------------------------------------------
angular.module('scopeExample', [])
.controller('MyController', ['$scope', function($scope) {
  $scope.username = 'World';

  $scope.sayHello = function() {
    $scope.greeting = 'Hello ' + $scope.username + '!';
  };
}]);
------------------------------------------------------------------


------------------------------------------------------------------
<div ng-controller="MyController">
  Your name:
    <input type="text" ng-model="username">
    <button ng-click='sayHello()'>greet</button>
  <hr>
  {{greeting}}
</div>
------------------------------------------------------------------

이 예제는 MyController에 'World'를 할당하고 scope의 username에 할당한다. scope는 할당된 입력값을 알리고, "Hello"와 결합하여 렌더링 한다. 이 예제는 어떻게 컨트롤러가 데이터를 scope에 쓸수 있는지 보여주는 좋은 예이다.

유사하게 controller는 sayHello메소드에서 본것과 같이 행위를 할당할 수 도 있다. 이것은 사용자가 "greet"를 클릭하는 경우 수행될 수 있도록 한다. sayHello메소드는 username속성을 읽고 greeting 속성을 생성한다. 이 예제는 scope 의 속성값을 자동적으로 업데이트 하도록 HTML입력 위젯에 바인딩 하는 내용을 보여준다.

{{greeing}}에 포함된 로직은 다음과 같다.
- 템플릿의 DOM노드에서 {{greeting}}으로 정의된 scope를 탐색한다. 이 예제에서는 MyController에서 scope에 지정되어 있다. 이 scope의 게층구조에 대해서는 이후에 설명하도록 하자.
- greeting표현식을 Evaluate한다. 이는 상단 스콥을 탐색하여, 결과값을 DOM엘리먼트에 반영한다.

scope내에 속성값에 데이터는 view에 렌더링 된다고 생각할 수 있다. scope는 모든 관련된 뷰에서singlesource-of-truth이다.

뷰의 관점에서 테스트를 위해서 컨트롤러의 분리는 매우 필요한 것이다. 왜냐하면 뷰에 집중하도록 하여 컨트롤러가 함께 있는 산만함을 없애준다.

------------------------------------------------------------------
it('should say hello', function() {
  var scopeMock = {};
  var cntl = new MyController(scopeMock);

  // Assert that username is pre-filled
  expect(scopeMock.username).toEqual('World');

  // Assert that we read new username and greet
  scopeMock.username = 'angular';
  scopeMock.sayHello();
  expect(scopeMock.greeting).toEqual('Hello angular!');
});
------------------------------------------------------------------

Scope Hierarchies

각 Angular 어플리케이션은 하나의  scope를 가진다. 그러나 몇개의 자식 scope들을 가질 것이다.
어플리케이션은 복수개의 스콥을 가질 수 있다. 왜냐하면 directives는 새로운 자식 scope를 생성하기 때문이다. (directive 는 새로운 스콥을 생성한다. ) 새로운 스콥이 생성되면, 그들의 부모 scope의 자식으로 추가된다. 이 생성트리는 DOM과 같이 병행하여 추가된다.

Angular가 {{name}}를 evaluate할때 name속성으로 주어진 엘리먼트와 연결된 scope를 우선 찾는다. 만약 properties가 존재하지 않는다면 부모 스콥을 찾게 되며, root에 다다를때까지 이러한 행위가 반복된다. JavaScript에서 이 행위는 prototypical inheritance로 알려져 있다. 자식의 스콥은 prototypically 상속을 자신의 부모로 부터 받는다.

다음 예제는 어플리케이션에서 scopes에 대한 일러스트를 보여준다. 그리고 프로퍼티의 prototypical inheritance 를 보여준다. 이 예제는 스콥의 영역을 보여주고 있다.


------------------------------------------------------------------
<div class="show-scope-demo">
  <div ng-controller="GreetController">
    Hello {{name}}!
  </div>
  <div ng-controller="ListController">
    <ol>
      <li ng-repeat="name in names">{{name}} from {{department}}</li>
    </ol>
  </div>
</div>
------------------------------------------------------------------


------------------------------------------------------------------
angular.module('scopeExample', [])
.controller('GreetController', ['$scope', '$rootScope', function($scope, $rootScope) {
  $scope.name = 'World';
  $rootScope.department = 'Angular';
}])
.controller('ListController', ['$scope', function($scope) {
  $scope.names = ['Igor', 'Misko', 'Vojta'];
}]);
------------------------------------------------------------------


------------------------------------------------------------------
.show-scope-demo.ng-scope,
.show-scope-demo .ng-scope  {
  border: 1px solid red;
  margin: 3px;
}
------------------------------------------------------------------

Hello World!
  1. Igor from Angular
  2. Misko from Angular
  3. Vojta from Angular

Angular는 자동적으로  ng-scope 클래스를 scope가 추가된 엘리먼트의 클래스에 위치시킨다. <style> 로 정의된 곳은 (붉은색 표시) 신규 scope가 위치된 영역이다. 이 자식 스콥들이 필요한 이유는 repeater가 {{name}}표현식을 검증하기 때문이다. 그러나 각 scope 표현은 서로다른 결과를 나타낸다. {{department}}의 검증은 root scope로 부터 prototypically inherit 되어진다. 이것은 오직 department 속성만 존재하기 때문이다.

DOM으로 부터 Scopes 순회하기 

Scopes는 DOM에 $scope 데이터 속성으로 추가된다. 그리고 디버깅 목적으로 순회하게 된다. rootScope는 DOM중에서 ng-app 디렉티브의 위치에 정의된다. 일반적으로 ng-app는 <html> 엘리먼트에 위치한다. 그러나 다른 엘리먼트에도 들어갈 수 있다. 예를 들면 Angular에 의해서한 부분만 제어되기를 원하는 경우가 그렇다.

디버거에서 scope 테스트 :
1. 브라우저에서 원하는 엘리먼트에서 오른쪽 클릭을 하여 'inspect element'를 선택한다. 그러면 브라우저 디버거가 열리고 원하는 위치가 하이라이트 된다.
2. 현재 선택된 엘리먼트에서 $0 값을 콘솔에 입력하여 디버거에 접근한다.
3. 콘솔에서 angular.element($0).scope()로 해당 $scope에 접근할 수 있다.

Scope Events Propagation

scope는 DOM 이벤트와 같은 방법으로 이벤트를 전파할 수 있다. 이벤트는 scope의 자식으로 broadcast 할수 있고, 자식에서 부모로 emitted 할 수 있다.
------------------------------------------------------------------
angular.module('eventExample', [])
.controller('EventController', ['$scope', function($scope) {
  $scope.count = 0;
  $scope.$on('MyEvent', function() {
    $scope.count++;
  });
}]);

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


------------------------------------------------------------------
<div ng-controller="EventController">
  Root scope <tt>MyEvent</tt> count: {{count}}
  <ul>
    <li ng-repeat="i in [1]" ng-controller="EventController">
      <button ng-click="$emit('MyEvent')">$emit('MyEvent')</button>
      <button ng-click="$broadcast('MyEvent')">$broadcast('MyEvent')</button>
      <br>
      Middle scope <tt>MyEvent</tt> count: {{count}}
      <ul>
        <li ng-repeat="item in [1, 2]" ng-controller="EventController">
          Leaf scope <tt>MyEvent</tt> count: {{count}}
        </li>
      </ul>
    </li>
  </ul>
</div>
------------------------------------------------------------------

Scope Life Cycle

일반적인 브라우저가 이벤트를 수신하는 플로우는 자바스크립트의 콜백에 대한 실행을 수행하는 것이다. 콜백이 완료되면 브라우저는 DOM을 다시 렌더링하고 추가적인 이벤트를 기다리기 위해 return한다. 

브라우저가 자바스크립트 코드를 호출할때 Angular 외부의 컨텍스트를 수행하면, 이것은 Angular가 모델의 변경사항을 감지하지 못한다는 의미이다. 모델의 변경을 수행하기 위해서는 $apply를 수행하여 Angular가 컨텍스트를 실행할 수 있도록 해주어야 한다. 오직 모델의 변경은 $apply 메소드 내부에서 수행된 경우에만 Angular에 의해서 처리될 수 있다. 예를 들어 DOM 이벤트를 리슨하는 디렉티브 ng-click와 같은 디렉티브는 $apply 메소드의 내부에서 evaluation이 수행되어야 한다. 

expression이 evaluating이 두행되고 난 뒤에는 $apply메소드는 $digest를 수행한다. $digest 단계에서scope는 $watch 익스프레션의 전체를 검증하고 이전 값과 비교를 수행한다. dirty checking은 비동기적으로 끝이난다. 이 의미는 $scope.username="angulat"과 같이 할당한 값이 $watch 에 의해서 즉시 노티되지 않는다는 것을 의미한다. 대신에 $watch 노티는 다음 $digest단계까지 딜레이 된다. 이 딜레이는 $watch가 수행되는 동안 다른 $watch가 수행되지 않는다는 것을 보장하는 것이다. 만약 $watch가 모델의 값을 변경하면 이것은 강제적으로 $digest사이클을 수행한다.

1. Creation : 
  root scope가 $injector에 의해서 어플리케이션 bootstrap 동안 생성된다. 템플릿 링킹동안 몇가지 디렉티브들은 새로운 child scope들을 생성한다. 

2. Watcher registration : 
  템플릿 링킹동안 디렉티브는 scope에 watches를 등록한다. 이 watches는 모델을 DOM으로 전파할때 이용된다. 

3. Model mutation : 
  모델 변경이 적합하게 관찰되기 위해서는 scope.$apply()에서 처리되어야한다. Angular API는 명시적으로 수행한다. 추가적으로 $apply는 controller에서는 동기적으로 수행되어야 하며, $http, $timeout혹은 $interval서비스에서는 비동기적으로 수행된다. 

4. Mutation ovservation : 
  $apply의 끝에서는 Angular는 $digest root scope에서 사이클을 수행한다. 이것은 모든 자식 scope에 전달된다. $digest 사이클동안 모든 $watch 표현 혹은 함수들은 모델의 변경에 대한 체크가 수행되며, 만약 변경이 발견되면 $watch listener는 호추된다. 

5. Scope destruction : 
  자식 scope가 더이상 필요하지 않은경우 자식 scope의 생성자는 삭제된다. 이것은 scope.$destroy() API를 통해 수행되며 이것은 $digest이 자식 scope를 호출하는 것을 멈추고 gabage collector에 의해서 재생되기를 기다린다. 

Scopes and Directives : 

compilation 단계동안 compiler는 DOM템플릿에 대한 디렉티브 매치를 수행한다. 디렉티브는 보통 2개의 카테고리에 속한다.
- Ovserving directives : {{ }} 과 같은 표현식으로, register listener는  $watch()메소드를 사용한다. 이 디렉티브 타입은 표현식이 변경되는 경우 노티가 수행되며 이것은 view를 업데이트 할 수 있게 된다.
- Listener directives : ng-click와 같은 것으로 등록된 리스너는 해당 이벤트가 fire된경우 연관된 표션식을 수행하고, $apply()메소드를 이용하여 뷰를 업데이트 한다.

외부 이벤트가 수신되면, (사용자 액션 혹은 XHR) 연관된 expression은 $apply메소드를 통해서 적용되어야 하며 이것은 모든 리스너가 정확히 업데이트 된다는 것을 의미한다.

Directives that Create Scopes 

대부분의 케이스에서 directive와 scope는 상호작용 한다. 그러나 scope의 새로운 인스턴스를 생성하지 않는다. 그러나 몇몇 directive는 ng-controller혹은 ng-repeat는 새로운 자식 scope를 생성하고, 이 자식 스코프를 DOM에 추가한다.
사용자는 angular.element(aDomElement).scope() 메소드를 이용하여 스콥을 순회할 수 있다.
directive guide를 참조하여 추가적인 정보를 획득하고, isolate scope에 대해서 확인하자.

Controllers and Scopes

Scopes와 Controllers 는 다음 상황에서 각각 상호작용 한다.
- Controller가 scope를 사용하여 컨트롤러 메소드를 템플릿에 노출하고자 하는경우
- Controller 정의 메소드가 Model을 변경하는 경우 (scope의 속성..)
- Controller가 모델에서 watches를 등록하는 경우, 이 watch는 컨트롤러가 특정행위를 수행하면 즉시 실행한다.

ng-controller파트를 추가로 확인하자.

Scope $watch Performance Considerations

특정 속성의 소콥에 대한 변경에 대해서 Dirty checking 은 Angular에서 공통적인 오퍼레이션이다. 이것은 dirty checking은 효과적으로 수행되어야 함을 의미한다. dirty checking함수는 어떠한 DOM접근도 하지 않도록 해야한다. DOM접근은 자바스크립트 객체에서 속성 접근보더 더 느린 순으로 접근이 된다.

Scope $watch Depths

Dirty checking는 3가지 전략으로 수행될 수 있다.
By reference
By collection contents
By Value

이 3가지 전략은 그들이 검출하고 성능적인 특징이 따라 종류가 달라진다.

- Watch by reference (scope.$watch (watchExpression, listener)) 은 다음과 같이 변경을 검출한다. 전체 값이 watch expression 이 새로운 값으로 변경되어 반환된 경우에 검출이 된다. 만약 값이 배열 혹은 객체이면, 내부 변경은 검출되지 않는다. 이것은 가장 효율적인 전략이다.

- Watch collection contents (scope.$watchCollection(watchExpression, listener))은 변경을 다음과 같이 검출한다. 배열 혹은 객체 내부의 변경이 발생하는 경우 : 아이템이 추가, 삭제, 재배열되어지는 경우에 처리된다. 검출은 shallow하게 처리된다. 이것은 중쳑된 컬렉션에서는 많지 않다. 컬렉션 컨텐츠의 watching은 watch by reference보다는 더 비용이 든다. 왜냐하면 컬렉션 컨텐츠가 관리되기 위해서 복사 되기 때문이다. 그라나 이 전략은 복사를 최소한으로 수행한다.

- Watch by value (scope.$watch( watchExpression, listener, true)) 는 임의의 중첩된 데이터 구조의 변경을 검출한다. 이것은 가장 강력한 변경 감지 전략이다. 그러나 가장 비용이 비싸다. 전체 충접 구조의 데이터 구조를 탐색하며, 각 다이제스트에서 수행되어 져야한다. 그리고 전체 복사가 메모리 내에서 이루어져야 한다.

Integration with the browser event loop

아래 나열된 다이어그램과 예제는 어떻게 Angular가 브라우저의 event loop와 상호작용하는지 보여준다.

1. 브라우저 이벤트 루프는 event가 도착하기를 대기한다. event는 사용자 인터렉션, 타이머 이벤트, 혹은 네트워크 이벤트이다. (네트워크 이벤트 = 서버로 부터 결과 전송)

2. 이벤트의 콜백은 수행된다. 이것은 자바스크립트 컨텍스트에 들어간다. 콜백은 DOM구조를 변경할 수 있다.

3. 콜백이 수행되면 브라우저는 JavaScript 컨텍스트를 버리고, 다시 DOM의 변경을 기반으로 뷰를 다시 그린다.

Angular는 일반적인 JavaScript를 변경하며 이는 주어진 이벤트 프로세싱 루프에 의해서 수행된다. 이것은 자바스크립트를 전통적인 방법에서 Angular 수행방식을 분리한다. 오직 Angular 실행 컨텍스트에서만 오퍼레이션이 적용되며, 이것은 Angular data-binding, exeception handling, property watching등으로 부터 수행된다. 또한 $apply를 이용할 수 있으며 이는 JavaScript로 부터 Angular excution으로 들어갈 수 있도록 해준다. 염두해야할 것은 대부분의 장소(controller, service)에서 $apply는 이미 디렉티브에 의해서 호출되어지며 이는 이벤트 핸들링을 수행한다. $apply를 명시적으로 호출하는 것은 custom이벤트 콜백을 구현할때, 혹은 서드파티 라이브러리 콜백과 함께 수행되어야 할때 적용된다.


1. Angular가 scope.$apply(stimulusFn) 호출에 의해서 컨텍스트 수행에 들어갈때 stimulusFn은 Angular에서 수행되고자 하는 작업을 의미한다.

2. Angular는 stimulusFn()을 실행하고 이는 보통 어플리케이션의 상태를 변경한다.

3. Angular는 $digest루프에 들어간다. loop는 $evalAsync큐와 $watch리스트 처리를 수행하도록 만들어 졌다. $digest루프는 모델이 안정화 될때까지 반복한다. 이의미는 $evalAsync 큐가 비고 $watch리스트가 더이상 변경을 감지할 수 없을때 까지이다.

4. $evalAsync큐는 현재 스택 프레임의 외부에서 발생이 필요한 스케쥴 작업에 이용된다. 그러나 브라우저 뷰가 렌더링 하기전에 수행된다. 이것은 보통 setTimeout(0)으로 완료된다. 그러나 setTimeout(0) 접근은 느리고 많은 view 플리킹을 수반한다.

5. $watch리스트는 지난 반복의 변경된 표현식의 셋이다. 만약 변경이 검출되면 $watch함수는 새로운 값으로 DOM의 값을 변경한다.

6. $digest루프가 끝이나면 실행은 Angular와 JavaScript 컨텍스트로 벗어난다. 이것은 다른 변경으로 인해서 DOM이 다시 렌더링 된 다음에 이어진다.

다음은 어떻게 Hello world 예제가 사용자의 입력으로 인해서 데이터 바인딩을 수행하는지 설명한다.
1. 컴파일 단계동안 :
    1. ng-model과 input directive는 keydown을 셋업하고 <input>컨트롤에 리슪ㄴ다.
    2. interpolation은 $watch를 셋업하여 name의 변경을 알림받는다.

2. runtime단계 동안 :
    1. 'X'키가 브라우저에서 눌려졌고 키다운 이벤트가 입력 컨트롤러에서 발생하였다.
    2. input디렉티브는 입력값의 변경을 캡쳐하고, $apply("name = 'X';")를 호출하여 Angular 실행 컨텍스트 내부의 어플케이션 모델을 변경한다.
    3. Angular는 name = 'X'; 를 모델에 적용한다.
    4. $digest루프가 시작된다.
    5. $watch 리스트가 name속성의 변경을 검출하고 interpolation에 알린다. 이것은 DOM업데이트를 수행하게 된다.
    6. Angular는 존재하는 컨텍스가 있으며, interpolation을 검증한다. 이것은 DOM을 수행한다.
    7. 브라우저는 변경된 텍스트로 부라우저를 다시 그린다.










from : https://docs.angularjs.org/guide/scope





[ 자바] 이미지해싱하기

$
0
0

package com.unclebae.image.hash;

import java.io.*;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class ImageHash {

    public static final String HASHING_MD5 = "MD5";

    public String hashing(String imagePath) {

        if (imagePath == null) {
            return null;
        }

        MessageDigest md = null;

        try {
            md = MessageDigest.getInstance(HASHING_MD5);
        } catch (NoSuchAlgorithmException e) {
            System.out.printf("There is not hasing algorithm.");
            return null;
        }

        byte[] bytes = new byte[1024];
        int readBytes = 0;

        StringBuffer sb = new StringBuffer();
        try {
            InputStream resourceAsStream = new BufferedInputStream(new FileInputStream(new File(imagePath)));
            while((readBytes = resourceAsStream.read(bytes)) != -1) {
                md.update(bytes, 0, readBytes);
            }

            byte[] mdBytes = md.digest();

            for (byte bt : mdBytes) {
                sb.append(Integer.toString((bt & 0xff) + 0x100, 16).substring(1));
            }

            resourceAsStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }

        return sb.toString();
    }

    public static void main(String[] args) {
        ImageHash hash = new ImageHash();
        String hashing = hash.hashing("/Users/kido/Documents/사진/01.jpg");
       
        System.out.println("Hash Result : " + hashing);
    }
}








[Zookeeper] 설치 StandAlone

$
0
0
1. Zookeeper 다운로드 받기 
http://zookeeper.apache.org/releases.html

2. zookeeper 디렉토리를 만들고 해당 tar 파일 풀기. 
> tar -xvf zookeeperXXX.tar

3. Standalone 버젼 설치 
Standalone버젼은 단순히 conf/zoo.cfg 파일을 생성하고 다음 내용을 추가하면 된다.

tickTime=2000
dataDir=/var/lib/zookeeper
clientPort=2181
- tickTime :
    millisecond 단위의 시간이다.
    heartbeat과 최소 세션 타임아웃 시간으로 사용된다.
- dataDir :
    in-memory 데이터베이스 스냅샷을 저장하는 위치이다.
    지정하지 않는경우 메모리에 트랜잭션 업데이트 로그를 저장하게 된다.
- clientPort :
    클라이언트 접속을 Listen하는 포트

4. Zookeeper 실행하기.
> bin/zkServer.sh start

# 알아둘것 : 
zookeeper 스탠드얼론은 replication 을 하지 않는다. 그러므로 zookeeper 프로세스가 실패되면 서버가 다운로드 된다.
그러면 zookeeper가 관리하는 모든 클러스터 노드들은 동작하지 않는다.

5. Zookeeper에 접근하여 몇가지 작업 해보기 
5.1 Zookeeper에 접근하기
> bin/zkCli.sh -server 127.0.0.1:2181
5.2 help 명령
[zkshell: 0] help
ZooKeeper host:port cmd args
get path [watch]
ls path [watch]
set path data [version]
delquota [-n|-b] path
quit
printwatches on|off
create path data acl
stat path [watch]
listquota path
history
setAcl path acl
getAcl path
sync path
redo cmdno
addauth scheme auth
delete path [version]
deleteall path
setquota -n|-b val path

5.3 ls /
[zkshell: 8] ls /
[zookeeper]

5.4 znode생성해보기
[zkshell: 9] create /zk_test my_data
Created /zk_test

5.5 ls /
[zkshell: 11] ls /
[zookeeper, zk_test]

5.6 get /zk_test
[zkshell: 12] get /zk_test
my_data
cZxid = 5
ctime = Fri Jun 05 13:57:06 PDT 2009
mZxid = 5
mtime = Fri Jun 05 13:57:06 PDT 2009
pZxid = 5
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0
dataLength = 7
numChildren = 0

5.7 zk_test 내의 스트링을 my_data --> junk로 바꾸기
[zkshell: 14] set /zk_test junk
cZxid = 5
ctime = Fri Jun 05 13:57:06 PDT 2009
mZxid = 6
mtime = Fri Jun 05 14:01:52 PDT 2009
pZxid = 5
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0
dataLength = 4
numChildren = 0
[zkshell: 15] get /zk_test
junk
cZxid = 5
ctime = Fri Jun 05 13:57:06 PDT 2009
mZxid = 6
mtime = Fri Jun 05 14:01:52 PDT 2009
pZxid = 5
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0
dataLength = 4
numChildren = 0

5.8 znode delete하기
[zkshell: 16] delete /zk_test
[zkshell: 17] ls /
[zookeeper]
[zkshell: 18]


주키퍼는 뭐하는놈일까?
주키퍼 Stand alone 설치하기
주키퍼 단일머신 멀티 인스턴스 설치하기


[Zookeeper] 설치 동일 장비에 멀티 인스턴스

$
0
0
1. 주키퍼 다운로드 
- 아래 주소를 참조하자.
http://uncle-bae.blogspot.com/2016/02/zookeeper-standalone.html

2. 멀티 인스턴스를 위한 디렉토리 생성 
> mkdir -p /usr/unclebae/server1
> mkdir -p /usr/unclebae/server2
> mkdir -p /usr/unclebae/server3

3. 각 디렉토리에서 다운받은 주키퍼 tar를 복사한다. 
> cp zookeeperXXX.jar /usr/unclebae/server1
> cp zookeeperXXX.jar /usr/unclebae/server2
> cp zookeeperXXX.jar /usr/unclebae/server3

4. 주키퍼 압축 풀기 
> cd /usr/unclebae/server1
> tar -xvf zookeeperXXX.jar

5. 주키퍼를 위한 데이터 디렉토리 생성 및 클러스터 이름 설정 
> mkdir -p /usr/unclebae/server1/datas
> mkdir -p /usr/unclebae/server2/datas
> mkdir -p /usr/unclebae/server3/datas

> vim /usr/unclebae/server1/datas/myid
파일이 열리면 다음 정보를 입력한다.
나머지 2번, 3번 서버도 동일하게 작업한다.
2번서버는 2, 3번서버는 3으로 설정한다.

1

6. 주키퍼 설정 파일을 생성한다.
각각 디렉토리의 conf/zoo.cfg를 생성하여 다음 내용을 입력한다.
>vim /usr/unclebae/server1/zookeeper/conf/zoo.cfg

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/Users/naver/Documents/00.TOOLS/solr_cluster/server1/datas/zookeeper
dataLogDir=/Users/naver/Documents/00.TOOLS/solr_cluster/server1/datas/zookeeper/logs
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
server.1=localhost:2888:3888
server.2=localhost:2889:3889
server.3=localhost:2890:3890

server1과 server2, server3은 clientPort를 다음과 같이 다르게 지정해야한다.
clientPort=2181
clientPort=2182
clientPort=2183

클라이언트 클러스터링은 server.1, server.2, server.3 으로 myid에 지정한 1, 2, 3이 들어오게 한다.


7. 클러스터 테스트하기. 
bin/zkCli.sh -server localhost:2181 로 1번 서버에 접속한다.
[zk: localhost:2181(CONNECTED) 0] ls /
[zookeeper]
znode를 생성한다.

[zk: localhost:2181(CONNECTED) 2] create /test-node test
Created /test-node
노드 확인하기.

[zk: localhost:2181(CONNECTED) 3] ls /
[test-node, zookeeper]

2번서버에도 zookeeper클라이언트로 접속하기.
bin/zkCli.sh -server localhost:2182로 서버에 접근한다.

[zk: localhost:2182(CONNECTED) 3] ls /
[test-node, zookeeper]

2번 서버에서도 1번서버에 생성한 test-node가 보인다. (즉, 복제가 된 것이다. )

주키퍼가 정상으로 설치 되었음을 확인한다.


주키퍼는 뭐하는놈일까?
주키퍼 Stand alone 설치하기
주키퍼 단일머신 멀티 인스턴스 설치하기





[zookeeper] 주키퍼는 뭐하는 놈일까?

$
0
0
Zookeeper : 
    - 오픈소스로 만들어진 분산된 어플리케이션을 위한 분산 코디네이션 서비스를 제공한다.
    - 즉, 주키퍼는 클라이언트가 서로 공유하는 데이터를 관리해주는 역할을 해준다.

분산 코디네이션 : 
    - 분산된 시스템 간에 동기화 서비스 제공, 공유자원의 관리를 수행하는 시스템
    - 분산코디네이션 시스템을 갖추기가 매우 어렵다. 에러를 검출하거나, 레이스 컨디션 및 데드락 상황을 파악하는 기능과 같은 어려운 작업을 해준다.

zookeeper의 특징 
1. Simple
    공유된 네임스페이스를 통해서 분산 코디네이션을 실현한다. 이러한 네임스페이스는 일반적인 파일 시스템과 유사하게 동작하여 단순함을 제공한다.
    name space는 znode라고 불리는 데이터 저장소로 구성되어 있다.
    이러한 네임스페이스는 in-memory에 저장되며, 빠른 처리 속도를 제공한다.

2. replicated
    ensemble라고 불리는 호스트 셋을 스스로 복제한다.
 

    주키퍼 서비스는 각각에 대해서 서로 알수 있도록 구성된다.
    이들은 상태값, 트랜잭션 로그, 저장소의 스냅샷 정보들을 상호 공유한다.
    클라이언트는 서버에 접속해서 요청을 보내거나, 응답을 받고, 이벤트를 watch하거나 heat beats를 전송한다. 서버와의 커넥션이 끊어지면 클라이언트는 다른 살아있는 서버로 접속을 시도한다.

3. ordered
    주키퍼 서버는 각각의 업데이트 결과를 모든 주키퍼 트랜잭션에 반영한다.
    이러한 순차적인 작업은 고수준 추상화로 구현되어 있다.

4. fast
    주키퍼는 읽기 성능이 매우 빠르다. 읽기와 쓰기의 비율은 10:1 의 수준이다.


zookeeper의 데이터 모델

네임스페이스는 일반적인 파일시스템과 매우 유사하다. 네임스페이스는 (/) 로 계층구조로 관리된다.

Node

주키퍼는 파일시스템처럼 보인다. 차이점이 있다면 각 노드는 데이터를 가지며, 이 노드는 역시 자식 노드들을 가질 수 있는 구조이다.
파일이 디렉토리도 될 수 있는 구조이다.

주키퍼는 이러한 노드에 다음과 같은 정보를 저장한다.
- 상태정보
- 설정정보
- 정보에 대한 위치정보

znode :
    일반적으로 주키퍼에 네임스페이스에 저장되는 노드를 말한다.
    주키퍼 데이터 노드라고도 한다.
    데이터 변경 버젼관리, ACL변경, 타임스템프, 캐시 검증, 업데이트된 내용의 코디네팅등 대한 상태값을 저장한다.
    클라이언트가 데이터를 참조할때 버젼 정보도 함께 내려주는 역할을 한다.

Ephermeral node :
    세션이 유지되는 동안 존재하는 노드이다.
    세션이 종료되면 이 znode는 제거된다.
 
Conditional updates and Watches
주키퍼는 watches에 대한 개념을 제공한다.
클라이언트는 watch를 znode에 설정할 수 있다.
watch는 znode의 변경에 대해서 트리거되거나, 제거될 수 있다.
watch가 트리거 되면 클라이언트는 znode의 변경사실에 대한 패킷을 수신받는다.

제공하는 기능 :
- Sequential Consistency : 클라이언트로 부터 없데이트가 발생하면, 순차적으로 전송한다.
- Atomicity : 성공 혹은 실패만 존재한다, 중간상태는 존재하지 않는다.
- Single System Image : 클라이언트는 서비스에 대한 동일한 뷰만 보게 된다.
- Reliability : 한번 업데이트가 되면클라이언트가 그 내용을 업데이트 하지 않는 이상 영원히 지속된다.
- Timeliness : 클라이언트는 시스템의 특정 업데이트된 시간에 대해서 뷰를 보장한다.

클라이언트 API : 
주키퍼는 다음과 같은 매우 단순한 인터페이스를 제공한다.
- create : 트리내에 노드를 생성한다.
- delete : 노드를 삭제한다.
- exists : 노드가 존재하는지 검사한다.
- get data : 노드로 부터 데이터를 읽어들인다.
- set data : 노드에 데이터를 저장한다.
- get children : 노드의 자식 리스트를 조회한다.
- sync : 데이터가 전파 되기를 기다린다.

주키퍼는 뭐하는놈일까?
주키퍼 Stand alone 설치하기
주키퍼 단일머신 멀티 인스턴스 설치하기

from : https://zookeeper.apache.org/doc/trunk/zookeeperOver.html

[Spring] Transaction Propagation

$
0
0
Spring에서는 호출되는 메소드에 대한 트랜잭션 경계를 다음과 같이 지원한다.

1. MANDATORY :
    호출전에 트랜잭션이 반드시 존재해야 한다. 존재하지 않은경우 예외 방생

2. NESTED :
    호출되는 메소드에 이미 트랜잭션이 걸려 있는경우 중첩된 트랜잭션이 실행된다.
    중첩된 트랜잭션은 메인 트랜잭션에 독립적으로 Commit/Rollback 될 수 있다.
    메인 트랜잭션이 있는경우 --> 중첩 트랜잭션 생성
    메인 트랜잭션이 없는경우 --> REQUIRED와 동일하게 신규 생성함
    (# 참고 : 벤더 의존적, 지원안될 수 있음)

3. NEVER :
    트랜잭션이 없는 상태에서 메소드가 실행 되어야 한다.
    트랜잭션이 걸려 있다면 예외 발생.

4. SUPPORTED :
    트랜잭션이 없는 상황에서 실행되어야 한다.
    트랜잭션이 있다면, 트랜잭션이 종료될때까지 대기한다.

5. REQUIRED :
    트랜잭션 상황에서 실행 되어야한다.
    진행중인 트랜잭션이 있다면 진행중인 트랜잭션 내에서 실행된다.
    없다면 새로운 트랜잭션을 생성한다.

6. REQUIRED_NEW : 
    신규 트랜잭션 내에서만 실행이 되어야한다.
    이미 트랜잭션이 실행중이라면 종료될때 까지 대기한다.

7. SUPPORTS :
    진행중인 트랜잭션이 없어도 실행이 된다.
    진행중인 트랜잭션이 있다면 해당 트랜잭션 내에서 실행이 된다.

[영어] Go, Come, Get, Be 장소에 대한 동사

$
0
0
Go와 Come 

Go :
    어떤 사람이나 물건이 있는 장소에서 멀어져 가는 경우
    출발점을 중심으로함

Come :
    목적하는 방향으로 다가옴을 의미한다.
    도착점을 중심으로함

example )
Are you going to the party ?
    단순히 상대에게 파티에 갈 의사가 있는지 물어봄

Are you coming to the party ?
    파티에 나도 가는데 너도 가느냐?의 의미로 물어봄

He went home.
    회사나 특정 장소에서 집으로 막 나온 시점 (출발기준)

He came home.
    이미 집에와서 쉬고 있는 시점 (도달기준)


Get 
    get은 도착지로 갈때까지의 노력이나 과정을 포함한다.

example )
Could you tell me how to get to the nearest station?
    가까운 역에 도착하는 방법을 가르쳐 주시겠습니까? (가까운역에 어떻게 가나요?)

Can you go to the party this evening?
    단순히 파티에 갈 수 있느냐?의 의미

Can you get to the party this evening?
    어떠한 일이 있어도 파티에 갈 수 있느냐?의 의미

Be
    도착해서 도착지에 머물고 있음을 의미한다.

example )
Please be here at three.
    3시에 이곳에 있어주세요.

I'll come (back) here in a minute. (X)
I'll be (back) here in a minute. (O)
    곧 돌아오겠습니다.


[Shell] bash version upgrade on Mac

$
0
0
Mac에서 Bash 쉘 버젼을 4버젼으로 업그레이드 하기.

1. 현재 버젼 확인
> bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darw
2. brew 인스톨 하기.
- brew가 인스톨 되어 있지 않다면 다음 명령어로 인스톨 한다.
> sudo ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

3. bash 업그레이드 하기.
>  sudo brew update && brew install bash 

4. 버젼 확인하기.
> bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15)
Copyright (C) 2007 Free Software Foundation, Inc. 
- 상기 내용을 보면 여전히 bash버젼은 3.2.57이다.
- 이것은 bash test가 여전히 3.X버젼을 가리키고 있기 때문이다. 이제 글로벌 변수인 $BASH_VERSION을 참조하도록 변경해보자.

5. 쉘 변경하기.
- 기존 bash는 /bin/bash였다. 이 쉘을 /usr/local/bin/bash로 변경하자.
> sudo /etc/shells
/usr/local/bin/bash    <-- 새로 설치된 bash 경로를 넣어준다. 
> chsh -s /usr/local/bin/bash
> chsh   <-- bash가 변경된 것을 확인할 수 있다.  
 - 이제 버젼을 확인해보면 바뀌어 있는것을 확인할 수 있다.

6. bash --version


# 확인해야할 사항 :
> echo $BASH_VERSION
BASH_VERSION: Undefined variable. 

Problem. 이 결과가  정상적으로 버젼을 알려주지 못하는 이유를 모르겠다.
--> 찾아서 해결하기.

Resolve.
--> 서버를 재시동 하니 $BASH_VERSION에 값이 설정되었음..

[Spring Study] spring 설정

$
0
0
Spring 프로젝트환경설정 

1. Spring framework 의존성설정 
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.2.5.RELEASE</version>
    </dependency>

    2. POM프로퍼티설정 
      <properties>
      <java-version>1.8</java-version>
      <org.springframework-version>4.2.5.RELEASE</org.springframework-version>
      <org.aspectj-version>1.6.10</org.aspectj-version>
      <org.sl4j-version>1.6.6</org.sl4j-version>
      </properties>

      3. Mysql Connect J 설정 
        <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.38</version>
        </dependency>

        4. try-with 구문활용
          try (Autoacloseable 인터페이스를구현한타입의변수) {
          작업하기
          } catch (Exception) {
          예외처리하기
          }

          5. JUnit 설정
            <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
            </dependency>

            6. Mybatis / Mybatis-spring / spring-jdbc / spring-test 설정하기
              <!— Mybatis —>
              <dependency>
              <groupId>org.mybatis</groupId>
              <artifactId>mybatis</artifactId>
              <version>3.3.1</version>
              </dependency>

              <!— Mybatis spring —>
              <dependency>
              <groupId>org.mybatis</groupId>
              <artifactId>mybatis-spring</artifactId>
              <version>1.2.4</version>
              </dependency>

              <!— spring-jdbc —>
              <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-jdbc</artifactId>
              <version>4.2.5.RELEASE</version> <!— 스프링의버젼과동일하게맞춘다. —> 
              </dependency>

              <!— spring-test —>
              <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-test</artifactId>
              <version>4.2.5.RELEASE</version> <!— 스프링의버젼과동일하게맞춘다. —> 
              </dependency>

              7. 기본적인 root-context네임스페이스설정하기
                1. aop
                2. beans
                3. context
                4. jdbc
                5. mybatis-spring

              8. DataSource 설정하기
                <bean id=“dataSource” class=“org.springframework.jdbc.datasource.DriverManagerDataSource”>
                <property name=“driverClassName” value=“com.mysql.jdbc.Driver”/>
                <property name=“url” value=“jdbc:mysql://127.0.0.1:3306/book_ex”/>
                <property name=“username” value=“kido”/>
                <property name=“password” value=“kids”/>
                </bean>

                9. Junit 테스트 (스프링올려서테스트하기)
                  @RunWith(SpringJUnit4ClassRunner.class)
                  @ContextConfiguration(
                  locations={“file:src/main/webapp/WEB-INF/spring/**/*.xml”})

                  10. Mybatis 연동
                  10.1 root-context.xml SqlSessionFactory 추가하기
                    < bean id=“sqlSessionFactory” class=“org.mybatis.spring.SqlSessionFactoryBean”>
                    <property name=“dataSource” ref=“dataSource”/>
                    <property name=“configLocation” value=“classpath:/mybatis-config.xml”/>
                    <property name=“mapperLocations” value=“class path:/mappers/**/*Mapper.xml”/>
                    </bean>
                    <!— SqlSession Template SqlSessionFactory 연결하기—>
                    <bean id=“sqlSession” class=“org.mybatis.spring.SqlSessionTemplate” destroy-method=“clearCache” >
                    <constructor-arg name=“sqlSessonFactory” ref=“sqlSessionFactory”/>
                    </bean>

                    10.2 resources/mybatis-config.xml 추가하기
                      <?xml version=“1.0” encoding=“utf-8”?>
                      <!DOCTYPE configuration
                      PUBLIC “-//mybatis.org//DTD Config 3.0//EN”
                      <configuration>
                      </configuration>

                      11. Jackson Bind 연동
                        <dependency>
                        <groupId>com.fasterxml.jackson.core</groupId>
                        <artifactId>jackson-databind</artifactId>
                        <version>2.7.2</version>
                        </dependency>

                        상기내용추가후 : @ResponseBody 해주면된다

                        12. 자바서블릿 API 추가. (서블릿테스트를하기위해서자동생성버젼에서 3.1올려준다. )
                          <dependency>
                          <groupId>javax.servlet</groupId>
                          <artifactId>javax.servlet-api</artifactId>
                          <version>3.1.0</version>
                          </dependency>


                          13.  Mybatis-log4j 설정하기
                          <dependency>
                          <groupId>org.bgee.log4jdbc-log4j2</groupId>
                          <artifactId>log4jdbc-log4j2-jdbc4</artifactId>
                          <version>1.16</version>
                          </dependency>

                          <!— root context dataSource 수정하기—>
                          <bean id=“dataSource” class=“org.springframework.jdbc.datasource.DriverManagerDataSource”>
                          <property name=“driverClassName” value=“net.sf.log4jdbc.sql.jdbcapi.DriverSpy”/>
                          <property name=“url” value=“jdbc:log4jdbc:mysql://127.0.0.1:3306/book_ex”/>
                          <property name=“username” value=“kido”/>
                          <property name=“password” value=“kido”/>
                          </bean>

                          <!— log4jdbc.log4j2.properties 생성—>
                          log4jdbc.spylogdelegator.name=“net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator

                          <!— logback.xml 생성하기—>
                          <?xml version=“1.0” encoding=“utf-8” ?>
                          <configuration>
                          <include resource=“org/springframework/boot/logging/logback/base.xml”/>

                          <logger name=“jdbc.sqlonly” level=“DEBUG”/>
                          <logger name=“jdbc.sqltiming” level=“INFO”/>
                          <logger name=“jdbc.audit” level=“WARN”/>
                          <logger name=“jdbc.resultset level=“ERROR”/>
                          <logger name=“jdbc.resultsettable” level=“ERROR”/>
                          <logger name=“jdbc.connection” level=“INFO”/>
                          </configuration>



                          [Spring] CSV 엑셀 다운로드시 한글 처리

                          $
                          0
                          0
                          Spring으로 웹 개발시 데이터를 CSV로 다운로드 하는 경우 컨텐츠 내용에 한글이 깨지는 경우가 발생한다.

                          다음과 같이 처리해주면 해결이 된다.


                          1. HttpServletResponse 를 Controller에 등록하기. 
                          public void listExcelExport(HttpServletResponse response) {
                          상기 내역처럼 response 객체가 필요하다.

                          2. request 객체에 컨텐츠 타입 설정하기. 
                          response.setContentType("application/ms-excel; charset=UTF-8");
                          response.setCharacterEncoding("UTF-8");
                          response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
                          3. 출력 스트림을 생성하고 UTF-8 에 대한 BOM을 추가한다. 
                          OutputStream out = response.getOutputStream();
                          out.write(0xEF);
                          out.write(0xBB);
                          out.write(0xBF);
                          4. 이후 컨텐츠 내역을 출력한다. 
                          out.write(.... contents ....);
                          out.flush();

                          참고자료 : 
                          https://ko.wikipedia.org/wiki/%EB%B0%94%EC%9D%B4%ED%8A%B8_%EC%88%9C%EC%84%9C_%ED%91%9C%EC%8B%9D

                          참고자료를 요약하면 BOM (Byte Order Mark)로 유니코드의 엔디안을 구별하기 위해 사용하는 문자를 설정해준다.

                          이를 통해서 UTF-XXX에 따라 엔디안의 종류를 맞춰주는 작업을 한다.

                          즉 출력 스트림의 맨 앞에 BOM을 기술해서 어떠한 타입의 엔디안을 사용하는지 파악하여 정확하게 인코딩을 해줄 수 있게 된다.

                          EncodingRepresentation
                          UTF-8EF BB BF
                          UTF-16 빅 엔디안FE FF
                          UTF-16 리틀 엔디안FF FE
                          UTF-32 빅 엔디안00 00 FE FF
                          UTF-32 리틀 엔디안FF FE 00 00
                          SCSU0E FE FF
                          UTF-EBCDICDD 73 66 73
                          BOCU-1FB EE 28


                          [The Lean Startup][Running Lean] 정리

                          $
                          0
                          0

                          Lean Startup 


                          린 경령이란? 
                          - 성공한 스타트업이 훌륭한 초기 계획 (플랜 A라고 부른다)을 세우고 출발했기 때문이 아니라. 자원이 소진하기전에 성공적인 계획을 찾았기 때문이다.

                          왜 스타트업은 어려움을 겪는가?
                          1. 성공할 수 있는 제품을 만드는 방법의 오해
                            - 미래를 내다보고 완벽한 과정을 계획하는 선지자의 이야기가 아니라. 점진적인 혁신 그리고 실패를 통해 탄생하였다.

                          2. 고객 참여에 대한 문제
                            - 고객의 참여가 있지만, 고객의 검증은 출시 이후로 미루어지고, 제품의 개발 과정에서 고객과 격리된다.
                            - 제품이 만들어지는 동안 고객의 참여를 끊임없이 반영하고 검증하는 과정이 필요하다.

                          3. 고객이 모든 답을 알더라도, 단순히 고객에게 그 답을 묻기만 해서는 안된다.
                            - 만약 사람들에게 무엇을 원하는지 물었다면, 사람들은 더 빠른 말이 필요하다고 답했을 것이다. -- 헨리포드
                            - 고객이 무엇을 원하는지 알아내는 것은 고객의 일이 아니다. -- 스티브잡스
                            - 즉, 정확한 맥락과 상황을 주면 고객은 자신의 문제를 정확히 표현할 수 있다. 그러나 해결책을 찾는것은 당신의 몫이다.

                          더 낳은 방법?
                          - 속도 학습, 집중을 위한 방법
                          - 고객 행동 평가를 통해 비젼의 검증
                          - 제품 개발 주기 전체에서 고객을 참여시키기
                          - 짧은 반복 개선 과정을 통해 제품과 시장을 동시에 검증
                          - 원칙에 따라 엄격하게 운영

                          고객개발 
                          - 스티브 개리 블랭크가 만든 용어
                          - 개발 전체 과정에서 고객과 지속적인 피드백의 순환 고리를 만드는 과정
                          - 핵심 :
                            사무실 밖으로 나가라   -- 스티브 개리 블랭크
                          - 답의 대부분은 회사 밖에서 얻을 수 있다. 컴퓨터나 연구실 안에는 답이 없다. 고객을 직접 만나야한다.

                          린 스타트업
                          - 에릭 리스의 트레이드 마크
                          - 고객개발, 애자일 소프트웨어 개발 방법론, 린 기법의 종합개념.
                          - 린 :
                            낭비를 없애거나 자원을 효율적으로 사용하는 것을 의미한다.
                          - 목표 단위 시간당 (고객에 대한)학습을 극대화 하는 것
                          - 소규모의 빠른 개발을 통해서 비젼의 검증
                          성공하는 스타트업은 자원을 다 써버리기 전에 여러 번 충분한 개선 과정을 반복할 수 있는 스타트업이다.  -- 에릭리스
                          부트 스트래핑  
                          - 은행이나 투자나로부터 조달하는 외부 투자나 자금 규모를 최소화 하는데 사용하는 기법의 집합
                          - 린에서는 매출을 통해 자금을 조달하는 목표를 말한다.

                          적절한 시기, 적절한 행동
                          - 스타트업의 본질은 무질서하다.
                          - 그러나 중요한 핵심 행동은 몇개 밖에 없다. 이 행동에 초점을 맞추고 나머지는 무시하라.

                          Running Lean의 구성 
                          1. 로드맵을 작성하라.
                            - 린의 정수를 담은 3가시 핵심 메타원칙
                          2. 플랜 A를 문서화 하라.
                            - 린 캔버스의 작성
                          3. 사업 계획에서 가장 위험한 부분을 식별하라.
                            - 사업 계획에서 가장 중요한 초점과, 위험 순위 결정방법 지정
                          4. 계획을 체계적으로 검증하라.
                            - 제 1안을 검증하고 반복하는 방법

                          저자의 경험 
                          1. 비밀리에 제품을 구축
                          - 잘못된 생각이라 회상
                          - 처음 창업자는 자신의 아이디어를 남들이 훔칠 것으로 생각한다.
                            a. 사람들은 대부분 아주 초기 단계일 때의 아이디어가 가진 잠재력을 간파하지 못한다.
                            b. 결정적으로 사람들은 당신의 아이디어에 관심이 없다.

                          2. 아무도 원하지 않는 제품을 만들기에는 인생이 너무 짧다.

                          3. 고객의 말에 귀를 귀울이는 것은 매우 중요하지만, 그 방법을 알아야 한다.
                            - 다양한 사용자 층중에서 어떤 사용자를 대상으로 할것인지 분명하지 않아서 피드백의 우선순위를 정하지 못했다고함.
                            - 가장 많이 피드백 오는 것에 따르다 보면 일회성의 기능이 잔뜩 포함된 비대한 제품이 됨.

                          실천이 이론을 이긴다. 
                          - 어떤 과정을 따르냐가 중요한 것이 아니라. 결과를 내는 것이 목표가 되어야한다.
                          - 직접 경험하고 엄격히 검증해보고 상황에 맞게 조정하자.

                          왕도는 없다. 
                          - 어떤 방법도 성공을 보장하지 못한다.
                          - 하지만 지속적인 개선과 학습을 가능하게 하는 피드백 고리를 만들어 줄수 있는 방법은 있다. (이책이 그것이다. )


                          참고자료 :
                          The Lean Startup
                          Slide Share : Running Lean
                          RunningLean.co


                          [Spring] AOP 정리

                          $
                          0
                          0
                          AOP란?
                          AOP (Aspect oriented programming)의 약자이며, Spring framework 의 핵심 기능이다. 
                          AOP는 프로그램 로직을 concern이라는 작은 단위로 분리한다. 이러한 concern은 프로그램의 여러 위치에서 동작하며 이를 cross-cutting concern이라고한다.

                          AOP를 이용하면 좋은점 : 

                          AOP는 어떠한 서비스나 메소드를 호출할때 필요한 수행작업을 하면서도, 핵심 로직에 더 집중할 수 있도록 해주는 특별한 설계기법이다.

                          예를 들어 햄버거 만드는 과정을 하나의 서비스라고 해보자. 
                          이때 우리는 하나의 햄버거가 만들어 지는 시간을 측정해야할 필요가 생겼다고 하자. 

                          기존의 방법대로 프로그래밍을 해야한다고 한다면 우리는 햄버거 만드는 과정에 대한 서비스 자체를 수정해야한다. 
                          햄버거를 만들기 시작할때 스톱워치를 켜야하고, 햄버거를 만들고, 햄버거를 포장까지 하고난뒤 스톱워치를 정지하여 시간을 측정하는 기록지에 적어야 한다.

                          스톱워치를 시작하는 행위나 정지하는 행위 그리고 측정내역을 기록하는 행위는 햄버거를 만드는 핵심 서비스 과장과는 완젼히 상관이 없는 행위이다. 

                          이러한 작업을 수행하기 위해서 우리는 시간을 측정하는 기능을 AOP를 이용하여 기록하게 할 수 있고, 햄버거를 만드는 서비스는 수정할 필요 없다. 
                          즉, 이러한 핵심작업은 신경쓰지 않고 필요한 작업을 할 수 있고, 신뢰성이 있는 서비스를 제공해 줄 수 있으며, 나아가 햄버거 만드는 작업이 변경이 되더라도 시간을 기록하는 행위 자체를 또다시 분석하거나 손대지 않아도 된다. 

                          AOP는 핵심 기능에 충실하게 프로그래밍 할 수 있도록 하여, 더 견고하고, 확장성 있는 프로그램을 개발 할 수 있도록 해준다. 

                          AOP의 용어 :

                          1. Aspect : 
                            - cross-cutting에 필요한 제공되는 API의 셋을 가지는 모듈이다. 
                            - 예를 들어 로깅에 필요한 로깅모듈을 AOP의 aspect라고 부른다. 

                          2. Join point : 
                            - AOP aspect 플러그인이 되는 프로그램의 위치를 의미한다. Spring AOP 프레임워크에서 사용되는 위치이다. 

                          3. Advice : 
                            - 메소드가 실행되기 전이나 후에 실제 액션을 의미한다. 이것은 실제 프로그램이 수행되는 코드의 특정 조각이다. 

                          4. Pointcut : 
                            - 이것은 하나 이상의 join point의 셋이다. 이는 표현식이나 패턴을 이용하여 AOP를 수행될 수 있도록 지정한다. 

                          5. Introduction : 
                            - introduction은 존재하는 클래스에 새로운 메소드나 속성들을 추가하도록 허용한다. 

                          6. Target object : 
                            - 하나 혹은 여러개의 aspect에 의해서 advice 받는 객체를 말한다. 이것은 항상 proxy된 객체이다. 

                          7. Weaving : 
                            - Weaving은 aspect를 객체에 연결해주는 과정을 이야기한다. 이것은 컴파일타임에 완료되거나, 로드타임, 혹은 실행시점에 연결이 될 수 있다. 

                          AOP에서 Advice의 타입 
                          AOP에서는 5가지 advice 를 가진다. 

                          1. Before : 
                            - 메소드가 실행되기 이전에 실행되는 advice이다. 

                          2. After : 
                            - 메소드가 실행되고 나올때 수행되는 adivce이다. 

                          3. After-Returning : 
                            - 메소드가 완젼히 수행된 이후에 수행되는 advice이다. 

                          4. After-throwing : 
                            - exception이 throw되는 경우에 수행되는 advice이다. 

                          5. Around : 
                            - 메소드가 호출되었을때 시작과 종료시점에 수행된다. 

                          적용 방식 : 
                          - Spring framework는 2가지 적용 방식을 제공하고 있다. 

                          1. XML Schema based : Sample Source Download 
                            - Aspects를 일반적인 XML 방식의 설정으로 구현할 수 있도록 한다. 

                            - @AspectJ 는 일반적인 자바5 이후의 annotation 을 이용한 선언적인 방식을 사용한다. 


























                          [Common DBCP] 설정 및 예

                          $
                          0
                          0

                          DBCP 문제 해결기 

                          1. 문제상황 : 
                          - 오픈하고 지금까지 커넥션 문제가 없었으나, 요청이 현재 3배 이상 증가하여 커넥션 문제가 발생하고 있었음. 
                          - 발생문제 : Cannot get a connection, pool error Timeout waiting for idle object 관련 예외가 계속해서 발생함. 
                          - DBA로 부터 너무많은 커넥션 요청이 들어온다는 보고를 받음 

                          2. 분석 : 
                          - 커넥션을 대기하는 시간동안 커넥션을 획득하지 못해서 발생한 오류 
                          - 즉, maxWaitMillis를 넘어서도 커넥션을 획득하지 못하는 문제가 발생. 

                          3. 커넥션 설정 확인 : 
                          BEFORE : 
                          maxWait=600
                          slowQueryTime=300
                          initialSize=5
                          maxActive=30
                          maxIdle=20
                          minIdle=5
                          useStatementCache=true
                          statementCacheSize=250
                          testOnBorrow=false
                          testOnReturn=false
                          testWhileIdle=false
                          timeBetweenEvictionRunsMillis=600000
                          minEvictableIdleTimeMillis=3600000
                          queryTimeout=0
                          useSqlLogForamt=false
                          connectionProperties=oracle.net.CONNECT_TIMEOUT=5000;oracle.jdbc.ReadTimeout=60000
                          # beta
                          beta.queryTimeout=7
                          beta.useSqlLogForamt=false


                          --> 상기 설정은 하나의 커넥션이 1시간동안 idle로 되어 잇으면 커넥션이 notValid한 것으로 판단하는 것이다. 
                          --> 초기 maxActive는 30으로 설정되었고, maxIdle값은 20으로 설정되었다. 

                          #즉 상기 설정을 통해서 커넥션이 20개 이상이 되는경우 커넥션을 맺고, 사용후에 바로 close한다. 그리고 요청이 필요한경우 다시 connection을 맺기 위해서 DB접근을 계속하는 상황이 된다. 

                          #또한 실제 Idle한 커넥션은 1시간이 지나면 evict 되어 재 접속이 필요한 상황이 된다. 

                          AFTER : 수정이후 

                          maxWait=600
                          slowQueryTime=300

                          initialSize=5
                          maxActive=30
                          maxIdle=30
                          minIdle=5
                          useStatementCache=true
                          statementCacheSize=250
                          testOnBorrow=false
                          testOnReturn=false
                          testWhileIdle=true
                          validationQuery=SELECT 1 FROM DUAL
                          timeBetweenEvictionRunsMillis=150000
                          minEvictableIdleTimeMillis=-1
                          numTestsPerEvictionRun=5
                          queryTimeout=0
                          useSqlLogForamt=false
                          connectionProperties=oracle.net.CONNECT_TIMEOUT=5000;oracle.jdbc.ReadTimeout=60000
                          # beta
                          beta.queryTimeout=7
                          beta.useSqlLogForamt=false
                          - maxActive와 maxIdle값을 같게 설정하였다. 즉, 커넥션제한을 미리 설정해두고, 사용후 반납하는 일이 없도록 설정하였다. 
                          - 상기 설정은 idle상태의 커넥션을 대상으로 validationQuery를 보내게 설정하였다. 
                          - 또한 커넥션 검사시간을 150000으로 설정하였다. 매 150000마다 커넥션 검사를 시작한다. 
                          - minEvictableIdleTimeMillis=-1로 설정하여 풀에 있는 커넥션은 무조건 살아 있다고 가정한다.
                          - numTestsPerEviceionRun=5로 설정하여 한번  커넥션 valid 할때바다 5개를 검사하도록 지정하였다. 

                          4. 결과 
                          - 커넥션을 기다리다가 타임아웃 되는 현상이 사라졌다.
                          - 매 순간마다 커넥션을 계속 맺는 현상이 사라졌다. 

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

                          Apache Common DBCP

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

                          Common DBCP란 :
                          Common DBCP는 Apache Common의 프로젝트 하위에 있는 데이터베이스 커넥션 풀을 제공하기 위한 기능을 제공한다.

                          DB 트랜잭션을 처리하기 위해서 가장 비용이 많이 드는 것은 데이터베이스와 커넥션을 신규로 생성하는 과정으로 DBCP를 이용하면 생성된 커넥션을 pool에 넣고 재사용하여 성능을 극대화 하는 방법을 제공한다.

                          DBCP2의 특징 : 
                          common-dbcp2 는 common-pool2 패키지 내에 포함되어 있으며, Java 7 이상에서 동작한다. (JDBC 4.1)

                          DBCP2는 Commons Pool2를 기반으로 한다. 
                          기존 DBCP보다 성능이 더 좋아졌다. 
                          JMX을 지원하여 DBCP 1.x버젼에 비해서 더 많은 기능을 제공한다. 


                          DBCP Configuration : 

                          1. 기본 설정 : 
                          1.1 username : 커넥션을 생성하기 위한 DB사용자 이름
                          1.2 password : 커넥션을 생성하기 위한 DB사용자 비밀번호
                          1.3 url      : 커넥션을 생성하기 위한 DB URL
                          1.4 driverClassName : 사용될 JDBC의 풀 자바 클래스 이름 
                          1.5 connectionProperties : 새로운 커넥션 생성을 위해서 JDBC 드라이버에 전달할 속성들을 의미한다. 
                             [propertyName=property;]* 의 형식을 가진다. 

                          2. 트랜잭션 관련 설정 : 
                          2.1 defaultAutoCommit 
                          - 기본값 : driver default
                          - 설명 : 풀에 생성된 커넥션의 auto-commit상태를 설정한다. 설정을 하지 않으면 setAutoCommit메소드가 호출되지 않는다. 

                          2.2 defaultReadOnly 
                          - 기본값 : driver default
                          - 설명 : 풀에 의해서 생성된 커넥션의 read-only상태를 설정한다. 설정을 하지 않으면 setReadOnly메소드가 호출되지 않는다. (Infomix와 같은 몇몇 드라이버는 이 설정을 지원하지 않는다.)

                          2.3 defaultTransactionIsolation
                          - 기본값 : driver default
                          - 설명 : 기본 트랜잭션 Isolation을 지정한다. 
                          NONE
                          READ_COMMITTED
                          READ_UNCOMMITTED
                          REPEATABLE_READ
                          SERIALIZABLE

                          2.4 defaultCatalog
                          - 기본값 : 없음
                          - 설명 : 풀에 생성된 커넥션의 기본 카탈로그를 설정한다. 

                          2.5 cacheState 
                          - 기본값 : true
                          - 설명 : true로 설정하면 풀된 커넥션은 첫번째 읽기와 쓰기를 수행할때 설정된 readOnly와 autoCommit 값을 캐시해두고 이하 쓰기시에 이를 적용한다. 만약 커넥션이 직접 접근 되어야 하거나 readOnly나 autoCommit 설정이 캐시에서 변경이 되더라도 현재 커넥션에는 반영되지 않는다. 캐싱을 하지 않고자 하는 케이스에서는 이 값을 false로 두면 된다. 

                          2.6 defaultQueryTimeout 
                          - 기본값 : null
                          - 설명 : 이값이 null이 아닌경우 여기에 설정된 정수형의 값은 풀에 의해서 관리되는 커넥션으로 부터 생성된 스테이트 먼트에 사용될 쿼리 타임아웃을 지정하게 된다. null의 의미는 driver의 기본값을 사용하겠다는 의미이다. 

                          2.7. enableAutocommitOnReturn
                          - 기본값 : null
                          - 설명 : true로 설정하면 풀이 커넥션으로 반환될때 Connection.setAutoCommit(true)인지 체크되고, 해당값으로 설정된다. 만약 auto commit가 false인경우라면 true로 변경 설정된다. 

                          2.8 rollbackOnReturn
                          - 기본값 : true
                          - 설명 : true의 의미는 풀로 커넥션이 반환될때 롤백이 수행된다. 단 auto commit이 false이고, 커넥션이 read only모드가 아니어야한다. 


                          3. 커넥션 개수 설정 
                          3.1 initialSize 
                          - 기본값 : 0
                          - 설명 : 풀이 시작될때 초기에 설정될 커넥션의 개수를 지정한다. 1.2버젼부터 지원

                          3.2 maxTotal 
                          - 기본값 : 8
                          - 설명 : active connection의 최대 개수를 지정한다. 이것은 동시에 풀에 할당될 수 있는 최대 커넥션의 개수이다. 음수로 설정하면 제한이 없다는 의미가 된다. 

                          3.3 maxIdle
                          - 기본값 : 8
                          - 설명 : 풀에서 idle로 남아 잇을 수 있는 최대 개수이다. 이 값보다 넘는 커넥션은 릴리즈 된다. 음수는 제한이 없다는 의미이다. 

                          3.4 minIdle
                          - 기본값 : 0
                          - 설명 : 풀에 idle로 남아있는 최소 개수이다. 즉 풀에 남아 있을 수 있는 최소 커넥션 개수이다. 

                          3.5 maxWaitMillis
                          - 기본값 : idenfinitely
                          - 설명 : 최대 밀리세컨을 지정하는 것으로 커넥션을 얻기 위해 기다리는 시간이다. 커넥션이 모두 다 쓰고 있는 상태에서 스레드가 하나의 커넥션을 얻기 위해 대기하는 시간이다. -1로 설정하면 무한정 기다린다. 

                          NOTE : 
                          - 만약 매우 무거운 시스템에서 maxIdle값을 너무 작게 설정하면, 커넥션을 닫고나서 바로 즉시 커넥션을 연결하는 일이 발생할 수 있다. 이 결과는 커넥션을 순간만 이용하고 커넥션을 여는 시간보다 닫는시간이 짧게 되는 현상이 나타난다. 이것은 idle 커넥션의 개수가 maxIdle까지 증가하는 현상을 원인이 된다. 
                          - 무거운 시스템에서 가장 좋은 maxIdle 값은 시스템마다 다를 것이다. 그러나 기본값은 좋은 시작 포인트가 된다. 

                          4. 커넥션 유지를 위한 설정 
                          4.1 validationQuery
                          - 기본값 : 없음
                          - 설명 : validationQuery는 풀에 커넥션을 반환하기 전이나, 풀을 획득하기 전에 커넥션이 valid한지를 검사한다. 이를 지정하고자 하는경우 select쿼리는 반드시 1로만 반환하도록 설정해야한다. 만약 이를 지정하지 않으면 커넥션은 isValid()메소드를 통해서 valid한지 검사하게 된다. 

                          4.2 validationQueryTimeout
                          - 기본값 : no timeout
                          - 설명 : 커넥션 validation 쿼리가 실패하기 전까지 시간을 의미한다. 만약 양수로 설정하면 setQueryTimeout메소드를 통해서 설정이 되며, Statement를 통해서 validation쿼리가 수행된다.

                          4.3 testOnCreate
                          - 기본값 : false
                          - 설명 : 생성된 이후에 객체가 validate되었는지 여부를 나타낸다. 만약 객체가 validate에서 실패한경우 객체 생성은 실패가 된다.

                          4.4 testOnBorrow
                          - 기본값 : true
                          - 설명 : 풀에서 커넥션을 얻어오기전에 객체가 valid한지 검사한다. 만약 객체가 validate에서 실패하면, 풀로 부터 객체를 drop하며, 다른 객체를 얻기위해 시도한다.

                          4.5 testOnReturn
                          - 기본값 : false
                          - 설명 : 풀로 객체를 반환하기 전에 validated된 값인지 검사한다.

                          4.6 testWhileIdle
                          - 기본값 : false
                          - 설명 : idle object evictor에 의해서 검증되어지며, 만약 객체가 검증에서 false가 되면 pool로 부터 드롭한다.

                          4.7 timeBetweenEvictionRunsMills
                          - 기본값 : -1
                          - 설명 : idle object evictor스레드의 실행 주기간격을 설정한다. 양수 값이 아닌경우에는 idle object evictor를 실행하지 않는다.

                          4.8 numTestsPerEvictionRun
                          - 기본값 : 3
                          - 설명 : idle object evictor 스레드가 매번 실행할때 검사할 객체의 개수를 지정한다.

                          4.9 minEvictableIdleTimeMillis
                          - 기본값 : 1000 * 60 * 30
                          - 설명 :풀에 객체가 idle상태로 있을 수 있는 최소한의 시간값을 지정한다.

                          4.10 softMiniEvictableIdleTimeMillis
                          - 기본값 : -1
                          - 설명 : idle collection evictor에 의해서 제거되기 전에 풀에 idle 상태로 남아있을 수 있는 시간을 의마힌다. 이것은 추가적인 상황을 함께 고려되며 적어도 "minIdle"커넥션을 남기도록 한다. miniEvictableIdleTimeMillis가 양수로 설정이 되면 minEvictableIdleTimeMillis는 우선 수행된다. 이것은 idle connections들이 evictor에 의해서 방문되어질때 idle time은 우선 miniEvictableIdleTimeMillis에 대해서 비교되어진다. (풀에 있는 idle 커넥션의 수를 고려하지 않는다.) 그리고 softMinEvictableIdleTimeMillis에 대해서 수행이 되며 minIdle constraint를 포함한다.

                          4.11 maxConnLifetimeMillis
                          - 기본값 : -1
                          - 설명 : 커넥션의 최대 라이프타임을 지정한다. 이 값이 지나게 되면 다음번 activation, passivation,혹은 validation테스트에서 실패하게 된다. zero의 값 혹은 더 적은 값은 라이프 타임이 없는 것을 의미한다.

                          4.12 logExpiredConnections
                          - 기본값 : true
                          - 설명 : 로그 메시지로 maxConnLifetimeMillis를 초과한 경우에 커넥션이 닫혔음을 로그로 남긴다. 이 속성을 false로 설정하여 익스파이어된 커넥션로깅을 막도록 설정할 수 있다.

                          4.13 connectionInitSqls
                          - 기본값 : null
                          - 설명 : SQL statements의 컬렉션으로 물리적 커넥션의 최기화에 사용되어진다. 이것은 커넥션이 처음 생성될때 작용한다. 이 스테이트먼트는 오직 한번만 수행된다. 설정된 커넥션 팩토리가 커넥션을 생성한다.

                          4.14 lifo
                          - 기본값 : true
                          - 설명 : True로 설정하면 borrowObject를 하였을때 가장 최근에 사용된 (즉 마지막에 입력된) 커넥션을 반환한다. (만약 idle 커넥션이 가능한 경우에 가능하다.) False는 풀은 FIFO 큐로 동작한다. 커넥션들은 풀에 들어온 순서대로 풀에서 커넥션을 반환한다.

                          5. SQL문장에 대한 풀 설정 
                          5.1 poolPreparedStatements :
                          - 기본값 : false
                          - 설명 : 현재 풀을 위한 statement 풀링을 설정한다.

                          5.2 maxOpenPreparedStatements
                          - 기본값 : undefined
                          - 설명 : 최대 개수의 오픈 스테이트먼트를 의미한다. 이것은 statement pool에 동시에 할당될 수 있는 값으로, 음수를 지정하면 제한이 없음을 의미한다.

                          - 상기 설정은 다음 메소드를 이용하는 경우 풀에 할당된다.
                          -- public PreparedStatement prepareStatement(String sql)
                          -- public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)

                          NOTE : 
                          - 커넥션이 다른 statements를 위해서 몇몇의 리소스를 남겨 두었느지 확인하라. Pooling PreparedStatements는 데이터베이스에서 해당 커서를 오픈항 상태로 둘수 있다. 이런경우 Pooling이 커서를 모두 소비해 버릴 수 있다. 특히 maxOpenPreparedStatements가 기본(제한없음)으로 설정된경우 그리고 어플리케이션이 큰 수의 서로다른 PreparedStatements를 하나의 커넥션에서 열고 있는 상태라면 더욱 주의해야한다. 이러한 문제를 피하기 위해서는 maxOpenPreparedStatements는 하나의 커넥션당 열수 있는 최대 커서의 수보다 작은 값을 가지도록 설정해야한다.

                          6. 나머지 설정 : 
                          - 아래 참고자료를 참조하자.
                          참고자료 : https://commons.apache.org/proper/commons-dbcp/configuration.html






                          [Spring] Interceptor and Filter

                          $
                          0
                          0

                          Interceptor And Filter in Spring


                          Spring에서 Interceptor와 Filter은 서로 같은 기능을 수행한다.

                          유사점 : 
                          1. filter와 Interceptor은 처리 방식에 있어서는 서로 동일하다.
                          2. 비즈니스 로직을 처리하기 전에 처리해야할 전처리를 수행하게 할수 있고.
                          3. 비즈니스 로직을 수행후에 해야할 후처리 작업을 수행할 수 있다.
                          4. 또한 지정된 request에 대해서만 filter나 interceptor가 동작하도록 제어할 수 있다.
                          5. 둘다 설정파일에 기술한 순서대로 실행이 된다.

                          차이점 : 
                          1. 호출시점 : 
                            - Filter : Servlet이 수행되기 이전, 그리고 이후에 처리된다.
                            - Interceptor : Dispatcher Servlet 호출 이후 Handler가 처리되기 이전, 그리고 이후에 실행된다.

                            - 아래 그림을 확인해보면 명확하다.


                          2. 메소드 시그너처의 차이
                          2.1 Filter : 
                            - init() : 필터 초기화
                            - doFilter() : 전, 후처리용 메소드, 내부에서 filterChain.doFilter를 호출하여 해당 비즈니스처리를 계속해서 수행할 수 있다.
                            - destory() : 필터 종료이후 실행.

                          2.2 Interceptor : 
                            - preHandle() : 핸들러가 수행되기 전에 실행됨.
                            - postHandle() : 핸들러가 수행되고 나서 실행됨.
                            - afterCompletion() : View작업까지 완료되고 나서 실행됨.

                          3. 파라미터의 차이
                          - Filter는 ServletRequest, ServletResponse등을 받게 된다.
                          - Interceptor은 HttpServletRequest, HttpServletResponse, 그리고 대상핸들러 등을 받아서 처리할 수 있다.

                          # ServletRequest와 HttpServletRequest의 차이.
                          SERVLETREQUESTHTTPSERVLETREQUEST
                          javax.servlet패키지에 포함됨. javax.servlet.http 패키지에 포함됨. 
                          ServletRequest의 하위 인터페이스임
                          getParameter()와 같은 많은 메소드를 제공 ServletRequest의 다양한 메소드를 상속받음.
                          getQueryString()등과 같은 추가 메소드를 제공함
                          HTTP프로토콜의 헤더, 바디, 컨텐츠타입, 쿠키, 세션등  다양한 정보를 쉽게 사용할 수 있도록함. 
                          GenericServlet과 함께 사용됨. HttpServlet과 함께 사용됨. 
                          프로토콜에 독립적으로 사용가능 프로토콜에 독립적이나, HTTP프로토콜 에 특화되어 사용 
                          HTTP가 사용되지 않았을때 클라이언트에서 요청이 들어오면 web container는 servletRequest 객체를 생성하고 service()메소드로 전달하는 방식으로 운용되었다. 클라이언트에서 요청이 HTTP프로토콜을 이용하여 들어온경우 Web container는 HttpServletRequest 객체를 생성하고 service()메소드에 전달하여 사용한다.
                          from : http://way2java.com/servlets/java-made-clear-difference-servletrequest-httpservletrequest/


                          3. 설정하기. 
                          3.1 Filter 설정 

                          <filter>
                            <filter-name>testFilter</filter-name>
                            <filter-class>com.unclebae.filter.test.TestFilter</filter-class>
                            <init-param>
                              <param-name>name</param-name>
                              <param-value>KIDO</param-value>
                            </init-param>
                          </filter>

                          <filter-mapping>
                            <filter-name>testFilter</filter-name>
                            <url-pattern>/test/*</url-pattern>
                          </filter-mapping>

                          3.2 Interceptor 설정 

                          spring-context.xml

                          ...
                          <interceptors>
                            <interceptor>
                              <mapping path="/test" />
                              <beans:bean class="com.unclebae.interceptor.test.TestInterceptor"></beans:bean>
                            </interceptor>
                          </interceptors>

                          4. 소스구조 
                          4.1 Filter 소스구조. 
                          package com.unclebae.testMvc.filter;
                          import java.io.IOException;
                          import javax.servlet.Filter;
                          import javax.servlet.FilterChain;
                          import javax.servlet.FilterConfig;
                          import javax.servlet.ServletException;
                          import javax.servlet.ServletRequest;
                          import javax.servlet.ServletResponse;
                          public class TestFilter implements Filter {
                          @Override
                          public void init(FilterConfig config) throws ServletException {
                          //필터 초기화
                          }
                          @Override
                          public void doFilter(ServletRequest request, ServletResponse response,
                          FilterChain chain) throws IOException, ServletException {
                          //1. 선처리  작업
                          //2. 다음처리 진행.
                          chain.doFilter(request, response);
                          //3. 후처리 진행.
                          }
                          @Override
                          public void destroy() {
                          //초기화 했던 작업들을 릴리즈 한다. }
                          }


                          4.2 Interceptor소스구조. 
                          - HandlerInterceptorAdapter를 이용한 소스 (Adapter은 인터페이스를 사전에 구현한 소스임)
                          package com.unclebae.testMvc.interceptor;
                          import javax.servlet.http.HttpServletRequest;
                          import javax.servlet.http.HttpServletResponse;
                          import org.springframework.web.servlet.ModelAndView;
                          import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
                          public class TestInterceptorUsingAdapter extends HandlerInterceptorAdapter {
                          @Override
                          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                          //전처리 수행
                          //true반환시 다음 실행, false실행시 다음 실행 불가 .
                          return true;
                          }
                          @Override
                          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
                          //후처리 수행.
                          }
                          @Override
                          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
                          // 모든 view가 렌더를 마치고 수행.
                          }
                          }

                          - Interceptor 인터페이스를 이용한 소스
                          package com.unclebae.testMvc.interceptor;
                          import javax.servlet.http.HttpServletRequest;
                          import javax.servlet.http.HttpServletResponse;
                          import org.springframework.web.servlet.HandlerInterceptor;
                          import org.springframework.web.servlet.ModelAndView;
                          public class TestInterceptor implements HandlerInterceptor {
                          @Override
                          public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                          Object object) throws Exception {
                          // TODO Auto-generated method stub
                          return false;
                          }
                          @Override
                          public void postHandle(HttpServletRequest request, HttpServletResponse response,
                          Object object, ModelAndView modelAndView) throws Exception {
                          // TODO Auto-generated method stub
                          }
                          @Override
                          public void afterCompletion(HttpServletRequest request,
                          HttpServletResponse response, Object object, Exception ex)
                          throws Exception {
                          // TODO Auto-generated method stub
                          }
                          }


                          Viewing all 50 articles
                          Browse latest View live