잊지 않겠습니다.

angularJS를 이용한 single document web application을 개발 할 때, 내용에 대한 정리가 필요해서 한번 정리해봅니다.

angularJS MVVM 이해하기


(그림의 출처는 http://www.mimul.com 입니다.)

Spring MVC를 이용하고 있다면, MVC의 개념에 대해서는 잘 이해하고 있을겁니다. 개인적으로 javascript framework에서 이야기하는 MVVM이라는 것은 약간의 marketing 용어라는 느낌이 강하게 들기 때문에, 거의 기존의 MVC와 동일하게 생각하면 됩니다.

Model

데이터 관리 및 통신등의 역활을 담당합니다. 이는 Spring MVC를 이용한 개발시에 표현되는 @Service와 동일하게 생각해도 됩니다. Service와 Factory로 구성이 되며 이는 singleton 여부와 동일하게 생각하면 됩니다. Service는 singleton으로, factory는 prototype으로요.

주로 사용되는 것은 REST API를 호출하는 call layer로 구성이 되는 경우가 많습니다. 이에 대한 결과에 대한 caching과 logic을 처리하는 것이 주가 되는데 개인적으로 추천을 한다면 Restangular module을 사용하는 것이 보다 더 편하고 좋은 코드 가독성을 가지고 올 수 있습니다. (https://github.com/mgonto/restangular)

Controller

DOM event handling과 $scope를 이용한 DataBinding 역활을 담당합니다. Spring MVC에서의 Controller와 Model의 역활이 합쳐져 있다고 이해하시면 편합니다. @Controller : Controller class, Model: $scope 로 생각하시면 좋습니다. DataBinding과 DOM event handling을 모두 가지고 있기 때문에 $scope의 코드의 method 들은 최대한 간결하고 명확한 naming이 좋습니다.

View

HTML 영역입니다. View의 표현이 중복되는 것이 많아진다면 directive를 만들어서 처리를 하면 HTML의 재활용도를 높일 수 있습니다. 다만, 저같이 기억력이 안좋은 사람에게는 사용할 때마다 directive의 code를 다시 한번 확인해야지 되는 단점을 가질 수 도 있습니다.;

angularJS를 이용한 개발 시 유의점

Memory leak

이 상황은 single document application을 구성할 때, 가장 주의해야지 되는 내용입니다. 지금까지 javascript로 개발하는 상황과 많은 차이를 가지고 오는 부분이 이 memory leak 부분이라고 생각합니다. 이 부분에 대한 주의가 필요합니다. memory leak이 발생되는 상황임을 알 수 있는 방법은 chrome의 profile에서 memory snapshot을 얻어서 확인 할 수 있습니다. 일단 3가지 질문을 통해서 javascript의 memory leak이 발생되고 있는지를 확인 할 수 있습니다.

page가 너무 많은 memory를 사용하는 경우

너무나 많은 memory를 사용하고 있다면 chrome의 timeline memory view와 chrome task manager를 통해서 확인 할 수 있습니다. 경험적으로는 사용하지 않는 DOM을 계속해서 가지고 있는 경우. (unbind DOM, fragmented DOM)이 계속해서 생성이 되는 경우에 주로 이런 현상이 발생됩니다. 더 이상 필요없게 되는 DOM에 대한 event listener를 unbind 시키는 것이 매우! 중요합니다.

page가 memory leak에서 자유로운지

Chrome Profile에서 볼 수 있는 Object allocation tracker를 이용해서 javascript object의 할당을 확인해보는 것이 좋습니다. snapshot을 이용해서 GC가 일어날 때와 일어나지 않았을 때의 memory 차이를 알아보고 page가 이동이 된 후도 계속 남는 memory가 많은지를 확인해봐야지 됩니다.

Forcing GC가 자주 일어나는지

chrome timeline memory view를 이용하면 GC가 얼마나 자주 일어나는지를 확인할 수 있습니다. 이것이 자주 일어난다면 너무나 많은 객체들이 생성되고 있다는 것을 의미합니다. 객체를 너무나 많이 생성하고 있는 것에 대한 고민이 필요합니다.

위의 상황들중에서 가장 문제가 될 수 있는 것은 javascript의 fragmented DOM 또는 unbinded DOM에 대한 event listener로 인한 memory leak 입니다. 이는 jQuery UI를 angularJS single document application에서 사용하는 경우, 자주 발생할 수 있는데, 이는 각각의 component 들에 대한 event binding이 정상적으로 clear 되지 못해서 발생되는 문제입니다. 이 부분에 대해서는 다음 Blog post를 참조해보세요.

http://rinat.io/blog/angularjs-and-jquery-plugins-memory-leaks

이러한 unbinded DOM의 event listener를 해지하기 위해서는 $destroy event의 처리가 필요합니다.
다음과 같은 code snippet가 각 controller에서 위치해야지 됩니다. 이 부분은 사용되는 javascript UI framework 등에서 어떻게 memory에서 모든 객체들을 제거할 수 있는지에 대한 document를 참고하시는 것이 좋습니다. (jquery 기반의 UI component는 destroy() method를 갖는 것이 일반적입니다.)

$scope.on('$destroy', function() {
    var dateTimePicker = $scope.element('#dateTimePicker');
    if(dateTimePicker != null) {
        dateTimePicker = dateTimePicker.datetimepicker();
        dateTimePicker.destroy();
    }
});

또한, $destroy에서는 controller에서 사용되고 있는 모든 timer들의 해재역시 처리해주는 것이 좋습니다.

Directive의 올바른 이해와 사용

Directive를 올바르게 사용하는 것은 매우 중요한 문제입니다. 가장 우선적으로, Directive의 scope의 정의를 정확히 아는 것이 중요합니다. Controller의 $scope와 독립적인 scope가 생성 되나, 양방향 또는 단방향으로 controller와 통신을 하는 것이 가능합니다.

예를 들어, scope를 다음과 같이 선언할 수 있습니다.

    scope: {
      title: '@',
      parentCodeId: '@',
      data: '@',
      ngModel: '=',
      text: '=',
      name: '@',
      nullItem: '@',
      styleValue: '@'
    },

title, parentCodeId, data, name, nullItem, styleValue의 경우에는 attribute를 이용한 값 설정과 동일합니다. directive의 scope를 변경하는 것으로 controller가 변경되지 않습니다. 그렇지만, ngModel, text와 같이 ‘=’로 선언된 내용은 다릅니다. ‘=’로 선언된 내용은 directive를 통해서 수정된 값이 controller의 값을 변경시키게 됩니다.

이러한 양방향 DataBinding을 이용하게 되는 경우, Directive에서 event를 호출할 필요가 거의 없습니다. 최대한 event를 호출해서 처리하는 코드양을 줄이는 것이 속도면에서 유리합니다.

DataBinding의 처리

angularJS에서의 DataBinding은 $scope의 모든 변수에 대해서 이전의 값과 현재의 값을 비교하여, 값이 다른 경우 DOM을 갱신하는 방식입니다. $scope의 변수의 값을 비교하는 타이밍은 아래와 같습니다.

  • scope.$apply()가 호출될 때.
  • DOM event가 발생될 때. (onchange, onclick etc…)
  • http 응답이 발생된 경우. ($http, $resource)
  • url이 변경되는 경우
  • $timeout event가 발생되는 경우

이 부분에 대한 개선은 다음과 같이 처리하는 것이 좋습니다.

기본적인 angularJS module의 이용

jQuery와 angularJS를 혼용해서 사용하게 되는 경우가 많은데, 위와 같은 문제 때문에 최대한 angularJS의 모듈들을 사용하는 것이 좋습니다. 가장 좋은 것은 jQuery method를 사용하지 않는것이 좋습니다. 예를 들어 jQuery의 기본 $.ajax는 가장 자주 사용되는 http ajax 통신 method입니다. 그렇지만, 이 코드를 사용하는 경우, scope의 DOM update가 되지 않습니다. 따라서, $http service를 이용하는 것이 좋습니다.

또한, window의 경우에도 $window, document는 $document를 이용하는 것이 좋습니다. 그리고, WebSocket을 이용하는 경우도 역시 마찬가지의 문제가 발생됩니다. WebSocket의 promise를 반환하고 그 값을 얻어낼 때, $apply() method를 호출해주어야지 됩니다.

$watch 는 최대한 가볍게

$watch method는 잦은 호출이 발생될 수 있는 code입니다. 다음과 같은 처리가 반드시 필요합니다.

slow — Anything faster than 50ms is imperceptible to humans and thus can be considered as "instant".
limited — You can't really show more than about 2000 pieces of information to a human on a single page. Anything more than that is really bad UI, and humans can't process this anyway.

TIP

이 부분은 개인적으로 사용해보니 좋았던 TIP들입니다. 다른 분들은 다르게 느끼실 수 있는 부분입니다.

Parent Controller를 이용한 Controller common method의 공유

공통적으로 사용하고 싶은 method들을 MainController에 넣어두면 Controller를 작성하기가 매우 편해집니다. 예를 들어 back() 처리라던지, Confirm/Alert 과 같은 알람기능이나 resize에 대한 기본적인 처리와 같은것을 넣어두면 매우 유용하게 사용가능합니다. 이를 지원하기 위해서 기존 ng-view 상단에 ng-controller를 지정해서 처리해주면 됩니다.

    <div ng-controller="MainCtrl">
      <section ng-view="" onload="completedRender()"></section>
    </div>
angular.module('fmsPublicWebApp').controller('MainCtrl', function ($scope, $timeout, $window, Codeservice, $location) { 
    $scope.showGlobalMessage = function(type, title, message) {

    }
});
$http, $resource보다는 Restangular를 사용

bower를 이용해서 쉽게 추가 가능한 Restangular를 사용하면 좋은 가독성을 갖는 코드를 짜는 것이 가능합니다. 약간 옆으로 길어지는 코드 pattern이 생기긴 하지만, 직관적인 호출이라는 강점을 가질 수 있습니다.

jQuery Library를 사용할 때는 주의할것!

최대한 jquery에 의존하지 않고 사용하는 것이 더 편합니다. angularJS는 jqLite라는 jquery의 mini version을 가지고 있습니다. 기본적인 jQuery select와 같은 기능들은 대부분 가지고 있기 때문에 angular.element를 사용하는 것이 더 좋습니다.

그리고, jQuery UI component를 사용하게 되는 경우에는, directive로 새로 만들어서 사용하는 것을 강력 권장합니다. 위에서 이야기드린 것처럼, jQuery UI library의 destroy를 정확하게 호출하지 않는 경우에는 memory leak이라는 매우 미묘한 문제를 가지고 오게 됩니다. 개발이 한참 진행된 이후에 memory leak을 발견하게 되는 경우에는 찾기가 정말 힘들어질 수 있습니다.

$scope에는 외부에 노출되어야지 되는 변수 및 method만을 정의 한다.

$scope의 값은 angularjs의 dirty-search의 대상입니다. 불필요한 내부값 및 내부 method 들을 $scope에서 사용해서 굳이 성능저하를 만들 필요는 없습니다.

Summary

AngularJS는 정말로 재미있는 Framework입니다. 특히 single document application을 개발할 때, 그 장점을 더 살릴수 있습니다. 그렇지만, 다음 항목들에 대해서는 좀더 주의가 필요합니다.

  1. memory leak
  2. DataBinding의 이해
  3. 재사용 가능한 HTML과 Controller


Posted by xyzlast Y2K

Gradle + yo deploy script

gradle을 이용한 build script 만들기 재미가 붙어, 이번에는 yo로 만든 web application을 auto-deploy를 지원하는 script를 한번 작성해봤습니다.

지원하는 시나리오는 다음과 같습니다.

  1. 이번에 작성한 2개의 public web과 mobile web을 모두 사용 가능
  2. 다음과 같은 process를 진행 가능해야지 됨
  • grunt build
  • svn에 새로 deploy할 web application의 압축본을 add & commit
  • ftp를 이용한 web application 배포

먼저, 여러 web application을 지원하기 위한 조건은 외부 parameter를 받는 방법이 가장 좋아서 target을 지정하기로 했습니다.

작성된 web aplication들의 구조는 다음과 같습니다.

.
├── admin-web
├── mobile-web
├── public-web
└── sample-web

여기서 sample-web의 경우에는 팀원들에게 같이 공유할 sample web application이기 때문에 배포 대상이 되지 못하고, admin, mobile, public의 경우에는 배포 대상이 될 수 있습니다. 간단히 이름을 기준으로 admin, mobile, public을 기준으로 삼고, 이를 parameter로 받으면 될 수 있다는 판단이 내려졌습니다.

이제 다음 조건들이 필요합니다.

  • grunt,svn 과 같은 command의 실행
  • grunt deploy가 된 후에 zip compress
  • ftp를 이용한 data copy

gradle을 이용한 command의 실행

기본적으로 gradle을 이용한 command는 type을 Exec로 잡아주면 처리가 가능합니다. 이와 같은 sub directory 내에서 실행되어야지 될 command는 workingDir 값을 지정해줘서 처리가 가능합니다. 다음은 grunt build를 하는 code입니다.

task build(type: Exec) {
    workingDir getWebDir()
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
        commandLine 'cmd', '/c', 'grunt', 'build'
    } else {
        commandLine 'grunt', 'build'
    }
    ext.output = {
        return standardOutput.toString()
    }
}

윈도우즈 계통과 linux/mac을 모두 지원하기 위해서 OS의 type을 설정해줘야지 됩니다. windows에서 cmd와 /c를 이용하는 것만을 주의하면 매우 간단한 코드입니다.

zip 파일 압축

zip 압축은 gradle에서 기본으로 제공하고 있는 Zip type을 이용하면 됩니다. zip type의 경우, from과 destinationDir만을 주의해서 처리하면 됩니다. 다음은 만들어진 distPath의 모든 내용을 yyyyMMddHHmm-hostname.zip 형식으로 압축을 하는 코드입니다.

task compressDist(type: Zip) {
    dependsOn 'build'

    from file(getDistPath())
    destinationDir file(deployPath)

    def now = new Date()
    def dateFormat = new SimpleDateFormat("yyyyMMddHHmm");
    def hostname = InetAddress.getLocalHost().getHostName().toLowerCase()

    filename = String.format("%s-%s.zip", dateFormat.format(now), hostname)
    archiveName filename
}

FTP 파일 전송

gradle은 자체적으로 FTP를 지원하지 않습니다. 다만 ant 를 지원하고 있기 때문에, ant의 FTP를 이용하면 됩니다. 그런데 ant ftp의 경우에는 send시에 hang이 걸리는 버그를 가지고 있습니다. passive mode를 true로 설정하는 것으로 hang이 걸리는 경우를 조금 덜 하게 할 수 있긴 하지만, 그래도 send에서 hang이 걸리는 것을 완벽하게 막지는 못합니다. 이 부분에 대해서는 좀 더 논의가 필요할 것 같습니다. 다음은 ftp 전송 코드입니다.

task 'upload' {
    dependsOn 'commitSvn'
    doLast {
        def remoteDir = getFtpPath()
        ant {
            taskdef(name: 'ftp', classname: 'org.apache.tools.ant.taskdefs.optional.net.FTP', classpath: configurations.ftpAntTask.asPath)
            ftp(action: 'mkdir', remotedir: remoteDir, server: ftpUrl, userid: ftpUsername, password: ftpPassword)
            ftp(action: 'delete', remotedir: remoteDir, server: ftpUrl, userid: ftpUsername, password: ftpPassword) {
                fileset() { include(name: '**/*') }
            }
            ftp(action: 'send', remotedir: remoteDir, verbose: true,
                    depends: true, binary: true, passive: true,
                    server: ftpUrl, userid: ftpUsername, password: ftpPassword) {
                fileset(dir: getDistPath()) {
                    include(name: '**/**/*')
                }
            }
        }
    }
}

각 build task에 대한 dependency를 추가하고, target argument를 받아서 처리하도록 코드 수정을 마져 완료한 최종 코드는 다음과 같습니다.

import org.apache.tools.ant.taskdefs.condition.Os

import java.text.SimpleDateFormat

configurations {
    ftpAntTask
}

repositories {
    mavenCentral()
}

dependencies {
    ftpAntTask("org.apache.ant:ant-commons-net:1.8.2") {
        module("commons-net:commons-net:1.4.1") {
            dependencies "oro:oro:2.0.8:jar"
        }
    }
}

def ftpUrl = '192.168.13.210'
def ftpUsername = 'ykyoon'
def ftpPassword = 'qwer12#$'
def filename = ''

def getDistPath() {
    return String.format('%s/dist', getWebDir())
}

def getWebDir() {
    return String.format('%s-web', project.target)
}

def getDeployPath() {
    return String.format('../deployed/%s-web/', project.target);
}

def getFtpPath() {
    return String.format('www-fms/%s', project.target);
}

task build(type: Exec) {
//    dependsOn 'upSvn'
    workingDir getWebDir()
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
        commandLine 'cmd', '/c', 'grunt', 'build'
    } else {
        commandLine 'grunt', 'build'
    }
    ext.output = {
        return standardOutput.toString()
    }
}

task compressDist(type: Zip) {
    dependsOn 'build'

    from file(getDistPath())
    destinationDir file(deployPath)

    def now = new Date()
    def dateFormat = new SimpleDateFormat("yyyyMMddHHmm");
    def hostname = InetAddress.getLocalHost().getHostName().toLowerCase()

    filename = String.format("%s-%s.zip", dateFormat.format(now), hostname)
    archiveName filename
}

task upSvn(type: Exec) {
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
        commandLine 'cmd', '/c', 'svn', 'up'
    } else {
        commandLine 'svn', 'up'
    }
    ext.output = {
        return standardOutput.toString()
    }
}

task addSvn(type: Exec) {
    dependsOn 'compressDist'

    String svnParam = getDeployPath() + filename
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
        commandLine 'cmd', '/c', 'svn', 'add', svnParam
    } else {
        commandLine 'svn', 'add', svnParam
    }
    ext.output = {
        return standardOutput.toString()
    }
}

task commitSvn(type: Exec) {
    dependsOn 'addSvn'
    String svnParam = getDeployPath() + filename
    String svnLog = '-mCommit file before deployed'

    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
        commandLine 'cmd', '/c', 'svn', 'add', svnParam, svnLog
    } else {
        commandLine 'svn', 'commit', svnParam, svnLog
    }
    ext.output = {
        return standardOutput.toString()
    }
}

task 'upload' {
    dependsOn 'commitSvn'
    doLast {
        def remoteDir = getFtpPath()
        ant {
            taskdef(name: 'ftp', classname: 'org.apache.tools.ant.taskdefs.optional.net.FTP', classpath: configurations.ftpAntTask.asPath)
            ftp(action: 'mkdir', remotedir: remoteDir, server: ftpUrl, userid: ftpUsername, password: ftpPassword)
            ftp(action: 'delete', remotedir: remoteDir, server: ftpUrl, userid: ftpUsername, password: ftpPassword) {
                fileset() { include(name: '**/*') }
            }
            ftp(action: 'send', remotedir: remoteDir, verbose: true,
                    depends: true, binary: true, passive: true,
                    server: ftpUrl, userid: ftpUsername, password: ftpPassword) {
                fileset(dir: getDistPath()) {
                    include(name: '**/**/*')
                }
            }
        }
    }
}

task 'deploy' {
    dependsOn 'build'
    dependsOn 'upload'
    doLast {
        println 'grunt build and upload'
    }
}

task help << {
    println '--------- gradle web command help ----------------------'
    println 'gradle deploy -Ptarget=public : public web build'
    println 'gradle deploy -Ptarget=mobile : mobile web build'
    println '--------- end line -----------------------------------------'
}

모두들 즐거운 코딩 되세요. ^^

Posted by xyzlast Y2K
TAG gradle, Yo

HikariCP 소개

Java 2014.08.21 17:10

HikariCP

BoneCP를 재치고 놀라운 속도를 자랑하는 DB Connection Pool입니다. BoneCP의 경우, Hibernate 4.x 버젼에서의 지원이 조금 애매해진 경향이 있습니다. (최신 버젼의 Hibernate에서는 에러가 발생합니다.) 반면에 HikariCP의 경우에는 Hibernate와의 통합 jar가 나오는 등, 계속해서 밀어주고 있다는 느낌이 강하게 듭니다.

GitHub page

https://github.com/brettwooldridge/HikariCP

Hibernate + HikariCP

build.gradle에 다음 dependency들을 추가합니다.

    compile 'org.slf4j:slf4j-api:1.7.5'
    compile 'com.zaxxer:HikariCP:2.0.1'
    compile 'org.javassist:javassist:3.18.2-GA'

hibernate.cfg.xml 파일에 다음 항목을 추가합니다.

<property name="connection.provider_class">com.zaxxer.hikari.hibernate.HikariConnectionProvider</property>
<property name="hibernate.hikari.dataSourceClassName">com.mysql.jdbc.jdbc2.optional.MysqlDataSource</property>
<property name="hibernate.hikari.dataSource.url">jdbc:mysql://localhost/test</property>
<property name="hibernate.hikari.dataSource.user">root</property>
<property name="hibernate.hikari.dataSource.password">qwer12#$</property>
<property name="hibernate.hikari.dataSource.cachePrepStmts">true</property>
<property name="hibernate.hikari.dataSource.prepStmtCacheSize">250</property>
<property name="hibernate.hikari.dataSource.prepStmtCacheSqlLimit">2048</property>
<property name="hibernate.hikari.dataSource.useServerPrepStmts">true</property>

Spring + HikariCP

@Configuration을 이용해서 HikariCP를 사용하는 법은 다음과 같습니다.

DataSource를 이용하는 경우 (ex: mssql server)
    @Bean(destroyMethod = "shutdown")
    public DataSource dataSource() {
        HikariDataSource dataSource = new HikariDataSource();

        dataSource.setUsername(env.getProperty(CONNECT_USERNAME));
        dataSource.setPassword(env.getProperty(CONNECT_PASSWORD));

        dataSource.setDataSourceClassName("com.microsoft.sqlserver.jdbc.SQLServerDataSource");
        dataSource.addDataSourceProperty("url", env.getProperty(CONNECT_URL));

        int minConnection = Integer.parseInt(env.getProperty(CONNECT_MIN));
        dataSource.setMinimumIdle(minConnection);
        int maxConnection = Integer.parseInt(env.getProperty(CONNECT_MAX));
        dataSource.setMaximumPoolSize(maxConnection);

        return dataSource;
    }
Driver를 이용하는 경우 (ex: mssql server)
    @Bean(destroyMethod = "shutdown")
    public DataSource dataSource() {
        HikariDataSource dataSource = new HikariDataSource();

        dataSource.setUsername(env.getProperty(CONNECT_USERNAME));
        dataSource.setPassword(env.getProperty(CONNECT_PASSWORD));
        dataSource.setDriverClassName(env.getProperty(CONNECT_DRIVER));
        dataSource.setJdbcUrl(env.getProperty(CONNECT_URL));

        int minConnection = Integer.parseInt(env.getProperty(CONNECT_MIN));
        dataSource.setMinimumIdle(minConnection);
        int maxConnection = Integer.parseInt(env.getProperty(CONNECT_MAX));
        dataSource.setMaximumPoolSize(maxConnection);

        return dataSource;
    }

HikariCP property

HikariCP의 property 설정은 다음과 같습니다.

autoCommit (default : true)

connection이 종료되거나 pool에 반환될 때, connection에 속해있는 transaction을 commit 할지를 결정합니다.

readOnly (default : false)

database connection을 readOnly mode로 open합니다. 이 설정은 database에서 지원하지 않는다면 readOnly가 아닌 상태로 open되기 때문에, 지원되는 database 목록을 확인해보고 사용해야지 됩니다.

transactionIsolation (default : none)

java.sql.Connection 에 지정된 Transaction Isolation을 지정합니다. 지정된 Transaction Isoluation은 다음과 같습니다.

  • Connection.TRANSACTION_NONE : transaction을 지원하지 않습니다.
  • Connection.TRANSACTION_READ_UNCOMMITTED : transaction이 끝나지 않았을 때, 다른 transaction에서 값을 읽는 경우 commit되지 않은 값(dirty value)를 읽습니다.
  • Connection.TRANSACTION_READ_COMMITTED : transaction이 끝나지 않았을 때, 다른 transaction에서 값을 읽는 경우 변경되지 않은 값을 읽습니다.
  • Connection.TRANSACTION_REPEATABLE_READ : 같은 transaction내에서 값을 또다시 읽을 때, 변경되기 전의 값을 읽습니다. TRANSACTION_READ_UNCOMMITTED 와 같이 사용될 수 없습니다.
  • Connection.TRANSACTION_SERIALIZABLE : dirty read를 지원하고, non-repeatable read를 지원합니다.

기본값을 각 Driver vendor의 JDBCDriver에서 지원하는 Transaction Isoluation을 따라갑니다. (none으로 설정시.)

category (default : none)

connection에서 연결할 category를 결정합니다. 값이 설정되지 않는 경우, JDBC Driver에서 설정된 기본 category를 지정하게 됩니다.

connectionTimeout(default: 30000 - 30 seconds)

connection 연결시도시 timeout out값을 설정합니다. 이 시간내로 connection을 연결하는데 실패하면 SQLException을 발생합니다.

idleTimeout(default : 600000 - 10 minutes)

connection Pool에 의하여 확보된 connection의 maximum idle time을 결정합니다. connection Pool에 의하여 확보된 connection이 사용되지 않고, Pool에 의해서만 이 시간동안 관리된 경우, connection을 DB에 반환하게 됩니다. 값을 0으로 설정하는 경우, 확보된 connection을 절대 반환하지 않습니다.

maxLifetime(default : 1800000 - 30 minutes)

connection Pool에 의하여 확보된 connection의 최대 생명주기를 지정합니다. connection을 얻어낸지, 이 시간이상되면 최근에 사용하고 있던 connection일지라도, connection을 close시킵니다. 사용중에 있던 connection은 close 시키지 않습니다. (사용이 마쳐지면 바로 close 됩니다.) HikariCP에서는 이 값을 30~60 minutes 사이의 값을 설정하라고 강력권고합니다. 값을 0로 설정하는 경우 lifetime은 무제한이 됩니다.

leakDetectionThreshold (default : 0)

connectionPool에서 반환된 connection의 올바른 반환이 이루어졌는지를 확인하는 thread의 갯수를 지정합니다. 이 값을 0로 지정하는 경우, leak detection을 disable 시키게 됩니다. 만약에 또다른 connection pool을 사용하고 있다면, 다른 connection pool에서 만들어진 connection을 leak으로 판단하고 connection을 닫아버릴 수 있습니다.

jdbc4ConnectionTest (default : true)

connection을 맺은다음, Connection.isValid() method를 호출해서 connection이 정상적인지를 확인합니다. 이 property는 다음에 나올 connectionTestQuery에 매우 밀접한 영향을 받습니다.

connectionTestQuery (default : none)

Connection.isValid() method를 지원하지 않는 ‘legacy’ database를 위한 빠른 query를 지정합니다. (ex: VALUES 1) jdbc4ConnectionTest가 더 유용하기 때문에 사용하지 않는 것이 좋습니다.

connectionInitSql (default : none)

새로운 connection이 생성되고, Pool에 추가되기 전에 실행될 SQL query를 지정합니다.

dataSourceClassName (default : none)

JDBC driver에서 지원되는 dataSourceClassName을 지정합니다. 이 값은 driverClassName이 지정된 경우, 지정할 필요가 없습니다.

dataSource (default : none)

사용자가 만든 dataSource를 Pool에 의하여 wrapped하는 것을 원하는 경우, 이 값을 지정하여 사용합니다. HikariCP는 이 문자열을 이용해서 reflection을 통해 dataSource를 생성합니다. 이 값이 설정되는 경우, dataSourceClassName, driverClassName 과 같은 값들은 모두 무시 됩니다.

driverClassName

HikariCP에서 사용할 DriverClass를 지정합니다. 이 값이 지정되는 경우, jdbcUrl이 반드시 설정되어야지 됩니다.

jdbcUrl

jdbcUrl을 지정합니다. driverClassName이 지정된 경우, jdbcUrl을 반드시 지정해줘야지 됩니다.

minimumIdle (default : maximumPoolSize)

connection Pool에서 유지할 최소한의 connection 갯수를 지정합니다. HikariCP에서는 최고의 performance를 위해 maximumPoolSize와 minimumIdle값을 같은 값으로 지정해서 connection Pool의 크기를 fix하는 것을 강력하게 권장합니다.

# maximumPoolSize

connection Pool에서 사용할 최대 connection 갯수를 지정합니다. 이 부분은 운영환경과 개발환경에 매우 밀접하게 연결되는 부분으로, 많은 테스트 및 운영이 필요합니다.

username

Connection을 얻어내기 위해서 사용되는 인증 이름을 넣습니다.

password

username과 쌍이 되는 비밀번호를 지정합니다.

poolName (default : auto-generated)

logging과 JMX management에서 지정할 pool의 이름을 지정합니다.

registerMbeans (default : false)

JMX management Beans에 등록되는 될지 여부를 지정합니다.

Posted by xyzlast Y2K