nexacro 필수값 체크

/*----------------------------------------------------------------------------------------------
 * 설명     : 필수값 체크
 * 파라미터 : pContainer : (필수)필수값 체크를 할 대상을 포함하는 Container Object Array.
 *                pSetting : (선택)
 *                  moveFocus : (선택), true(기본)-값이 없는 element로 focus 이동, false : focus 이동 않함.
                    popupAlert : (선택) 필수값없을시 alert 메시지를 popup시킬지 여부.
                       true : (기본) popup 함.
                 false : popup 않함.
 * 리턴값    : pResult : 
                    successYn : true : 필수값이 모두 입력됐을경우, false : 필수값이 모두 입력되지 않았을경우.
          message : check 결과 메시지. 필수값 오류시 오류 내용
-----------------------------------------------------------------------------------------------*/
this.gf_ErpCheckMandatory = function(pContainer, pSetting)
{
  var bPopupAlert = true;
  
  try {
    bPopupAlert = pSetting.popupAlert;
  } catch(e) {};
  
  var oResult = {
      successYn : true,
      message : "정상"
  };
  
  if( pContainer instanceof nexacro.Div ) {
    var aTargetList = document.getElementById(pContainer._unique_id).querySelectorAll(".essential");
    for( var idx = 0; idx < aTargetList.length; idx++) {
      try{
        if(this.gf_IsNull(nexacro._findComponentForId(aTargetList[idx].id).value)) {
          oResult.successYn = false;
          oResult.message = "필수값이 없습니다. ["+ aTargetList[idx].title+"]";
          if(bPopupAlert){
            this.gf_Alert(this,oResult.message);
          };
          nexacro._findComponentForId(aTargetList[idx].id).setFocus();
          return oResult;
        };
      } catch(e) {};
      
    };
  } else if( pContainer instanceof nexacro.Grid ) { 
    var aMandatoryCols = [];
    for( var idx = 0; idx < pContainer._formats.default._headcells.length ; idx++) {
      if(pContainer._formats.default._headcells[idx].cssclass._value == "essential") {
        aMandatoryCols.push(pContainer._formats.default._headcells[idx]._col);
      };
    };
    
    for( var nRow = 0; nRow < pContainer.bindDS.rowcount; nRow++ ) {
      for( var idx = 0; idx < aMandatoryCols.length; idx++ ) {
        var nColIdx = aMandatoryCols[idx];
        var sColId = pContainer._formats.default._bodycells[nColIdx].text._bindexpr;
        var sText = "";
        if( !this.gf_IsNull(sColId) && this.gf_IsNull(pContainer.bindDS.getColumn(nRow,sColId)) ) {
          oResult.successYn = false;
          sText = this.gf_Nvl(pContainer._formats.default._headcells[nColIdx].tooltiptext._value, pContainer._formats.default._headcells[nColIdx].text._value);
          oResult.message = "필수값이 없습니다. ["+ sText+"("+(nRow+1)+"행)]";
          if(bPopupAlert){
            this.gf_Alert(this,oResult.message);
          };
          return oResult;
        };
      };
    };
  };
  
  return oResult;
};
사용예)
oResult = this.gf_ErpCheckMandatory(this.tab_main.tap_reg.form.div_base, {popupAlert:false});
  if(!oResult.successYn) {
    return oResult;
  };

nexacro container(div,grid)의 필수값 체크

this.gf_ErpCheckMandatory = function(pContainer, pSetting)
{
  var bPopupAlert = true;
  
  try {
    bPopupAlert = pSetting.popupAlert;
  } catch(e) {};
  
  var oResult = {
      successYn : true,
      message : "정상"
  };
  
  if( pContainer instanceof nexacro.Div ) {
    var aTargetList = document.getElementById(pContainer._unique_id).querySelectorAll(".essential");
    for( var idx = 0; idx < aTargetList.length; idx++) {
      try{
        if(this.gf_IsNull(nexacro._findComponentForId(aTargetList[idx].id).value)) {
          oResult.successYn = false;
          oResult.message = "필수값이 없습니다. ["+ aTargetList[idx].title+"]";
          if(bPopupAlert){
            this.gf_Alert(this,oResult.message);
          };
          nexacro._findComponentForId(aTargetList[idx].id).setFocus();
          return oResult;
        };
      } catch(e) {};
      
    };
  } else if( pContainer instanceof nexacro.Grid ) { 
    var aMandatoryCols = [];
    for( var idx = 0; idx < pContainer._formats.default._headcells.length ; idx++) {
      if(pContainer._formats.default._headcells[idx].cssclass._value == "essential") {
        var oColInfo = {
            col : pContainer._formats.default._headcells[idx]._col,
            cell : pContainer._formats.default._headcells[idx]._cellidx
            };
        aMandatoryCols.push(oColInfo);
      };
    };
    
    for( var nRow = 0; nRow < pContainer.bindDS.rowcount; nRow++ ) {
      for( var idx = 0; idx < aMandatoryCols.length; idx++ ) {
        var nColIdx = aMandatoryCols[idx].col;
        var nCellIdx = aMandatoryCols[idx].cell;
        var sColId = pContainer._formats.default._bodycells[nColIdx].text._bindexpr;
        var sText = "";
        if( !this.gf_IsNull(sColId) && this.gf_IsNull(pContainer.bindDS.getColumn(nRow,sColId)) ) {
          oResult.successYn = false;
          sText = this.gf_Nvl(pContainer._formats.default._headcells[nCellIdx].tooltiptext._value, pContainer._formats.default._headcells[nCellIdx].text._value);
          oResult.message = "필수값이 없습니다. ["+ sText+"("+(nRow+1)+"행)]";
          if(bPopupAlert){
            this.gf_Alert(this,oResult.message);
          };
          return oResult;
        };
      };
    };
  };
  
  return oResult;
};

eclipse 단축키

  • 편집기 block 지정/해제 : shift + alt + a
  • package import : shift + ctrl + m
  • method를 참조하는 resource 찾기 : shift + ctrl + g
  • 대문자 전환 : shift + ctrl + y
  • 소문자 전환 : shift + ctrl + x
  • code templage 적용 : shift + alt + j
  • formatter 적용 : shift + ctrl + f

API Log 처리 개요(Spring)

API process

api log sequence diagram

고려 사항
– API Log의 저장은 비동기 처리를 하는것에 성능 관점에서 유리하다.
– 실시간 Log의 검색등이 필요할 경우 Database에 실시간 또는 짧은 시간 간격으로 저장한다.
– 실시간 Log의 검색이 필요하지 않을 경우, was log로 기록후 일단위 배치를 통해 database로 기록한다.
– Log의 통계는 일단위 배치를 통해서 수행한다.

API Log 구성 항목

API Log에 기록될 항목들을 아래와 같이 정리해 보았다.
*표시가 붙은 항목은 가급적 포함시켜야 하는 항목이다.

api transaction id*
ㆍ구성예 : api was의 host명 + ‘-’ + api was에서 생성한 uuid
ㆍhost name 조회 방법 예(java) : InetAddress.getLocalHost().getHostName()
ㆍuuid 획득 방법 예(java) : UUID.randomUUID()
ㆍ생성 : WAS

client trnsaction id
ㆍclient에서 전달한 client transaction id
ㆍ생성 : request header

result code*
ㆍ처리 결과 코드
ㆍ코드 정보를 DB table등으로 관리하여 사용한다.
ㆍ생성 : WAS

request time*
ㆍ처리 요청 시각
ㆍ생성 : WAS

process time*
ㆍapi 처리시간( 단위 : 초 ).
ㆍ1/1000 초 사용시 0초로 기록되는 경우가 발생하니 프로젝트 성격에 따라 이하 단위까지 측정하여 기록할지 결정하도록 한다. 0초로 기록됐을 경우 표시는 ‘0.001 초 이하’ 로 표시하면 된다.
ㆍ생성 : WAS

api server instance*
ㆍapi를 처리한 api was instance host name
ㆍ생성 : WAS

response content*
ㆍapi 응답 메시지 내용
ㆍ문자열보다 json type 사용 권장.
ㆍ생성 : WAS

user id
ㆍapi 요청자
ㆍ생성 : request header or request parameter

request parameter content*
ㆍ요청 파라메터 내용을 json 형식으로 표시.
ㆍ문자열보다 json type 사용 권장.
ㆍ생성 : request parameter

client id
ㆍclient가 복수이거나 client instance가 복수일 경우 기록.
ㆍ생성 : request header

client request time
ㆍclient에서 전달한 요청 시각.
ㆍ생성 : request header

url*
ㆍdomain과 request parameter를 제외한 api request url.
ㆍ생성 : WAS( from HttpServletRequest )

method *
ㆍhttp request method
    > GET : 주요 기능이 조회일 경우.
    > POST : 주요 기능이 추가일 경우.
    > PUT : 주요 기능이 변경일 경우.
    > DELETE : 주요 기능이 삭제일 경우.
ㆍ생성 : WAS( from HttpServletRequest )

ip address
ㆍapi를 요청한 ip 주소
ㆍ생성 : WAS( from HttpServletRequest )

write time*
ㆍapi log  등록 시각.
ㆍinsert시 CURRENT_TIMESTAMP 사용.
ㆍ생성 : DBMS.

referer*
ㆍapi를 요청한 referer.
ㆍ생성 : request header 또는 WAS( from HttpServletRequest )

Spring 비동기 처리.

1. 비동기 설정. ( SpringAsyncConfig.java )
package com.etoos.cmn.spring;

import java.util.concurrent.Executor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import com.etoos.cmn.util.ConfigUtil;

@Configuration
@EnableAsync
public class SpringAsyncConfig {
    @Bean(name = "apiLogExecutor")
    public Executor apiLogExecutor() {
      
      int corePoolSize = ConfigUtil.getInt("apilog.thread.pool.core-size");
      int maxPoolSize = ConfigUtil.getInt("apilog.thread.pool.max-size");
      int queueSize = ConfigUtil.getInt("apilog.thread.queue.size");
      
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(corePoolSize);
        taskExecutor.setMaxPoolSize(maxPoolSize);
        taskExecutor.setQueueCapacity(queueSize);
        taskExecutor.setThreadNamePrefix("apiLogExecutor-");
        taskExecutor.initialize();
        return taskExecutor;
    }
    
}
2. 비동기 서비스에 thread executor 지정.
package com.etoos.api.common.service;


import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.etoos.api.common.vo.ApiLogVO;

@Service
public class ApiLogService {

  @Autowired
  @Qualifier("apiLogSqlSession")
  private SqlSession apiLogSqlSession;
  
  
  /**
   * API Log 저장
   * 
   * @param apiLogInfo API Log 정보
   */
  @Async("apiLogExecutor")
  @Transactional(propagation = Propagation.REQUIRES_NEW)  
  public void insertApiLog(ApiLogVO apiLogInfo) {
    apiLogSqlSession.insert("apiLog.insertApiLog", apiLogInfo);
  }

}