Explorar el Código

Merge remote-tracking branch 'origin/xodud1202' into jsh77b

jsh77b hace 5 años
padre
commit
ea6a03025f
Se han modificado 23 ficheros con 4806 adiciones y 11 borrados
  1. 75 0
      src/main/java/com/style24/front/biz/dao/TsfCartDao.java
  2. 27 0
      src/main/java/com/style24/front/biz/dao/TsfGoodsDao.java
  3. 180 3
      src/main/java/com/style24/front/biz/service/TsfCartService.java
  4. 85 0
      src/main/java/com/style24/front/biz/service/TsfGoodsService.java
  5. 53 5
      src/main/java/com/style24/front/biz/web/TsfCartController.java
  6. 0 1
      src/main/java/com/style24/front/support/env/TsfConstants.java
  7. 53 0
      src/main/java/com/style24/persistence/domain/Cart.java
  8. 26 0
      src/main/java/com/style24/persistence/domain/Goods.java
  9. 26 0
      src/main/java/com/style24/persistence/domain/GoodsStock.java
  10. 247 1
      src/main/java/com/style24/persistence/mybatis/TsfCart.xml
  11. 52 1
      src/main/java/com/style24/persistence/mybatis/TsfGoods.xml
  12. 1298 0
      src/main/webapp/WEB-INF/views/web/cart/cartListFormWeb.html
  13. 10 0
      src/main/webapp/WEB-INF/views/web/error/500Web.html
  14. 36 0
      src/main/webapp/ux/plugins/gaga/gaga.agGrid.css
  15. 1874 0
      src/main/webapp/ux/plugins/gaga/gaga.agGrid.js
  16. 68 0
      src/main/webapp/ux/plugins/gaga/gaga.alert.js
  17. 377 0
      src/main/webapp/ux/plugins/gaga/gaga.dx5.js
  18. 73 0
      src/main/webapp/ux/plugins/gaga/gaga.se2.js
  19. 129 0
      src/main/webapp/ux/plugins/gaga/gaga.smarteditor.js
  20. 110 0
      src/main/webapp/ux/plugins/gaga/gaga.summernote.js
  21. 1 0
      src/main/webapp/ux/plugins/jquery/jquery-1.12.4.min.js
  22. 5 0
      src/main/webapp/ux/plugins/jquery/jquery-ui.min.js
  23. 1 0
      src/main/webapp/ux/plugins/jquery/jquery.serializeObject.min.js

+ 75 - 0
src/main/java/com/style24/front/biz/dao/TsfCartDao.java

@@ -1,6 +1,11 @@
 package com.style24.front.biz.dao;
 
 import com.style24.core.support.annotation.ShopDs;
+import com.style24.persistence.domain.Cart;
+import com.style24.persistence.domain.GoodsStock;
+
+import java.util.Collection;
+import java.util.List;
 
 /**
  * 장바구니 Dao
@@ -10,5 +15,75 @@ import com.style24.core.support.annotation.ShopDs;
  */
 @ShopDs
 public interface TsfCartDao {
+    /**
+     * 장바구니 대상 상품 수량 조회
+     * @param Cart
+     * @return
+     * @author xodud1202
+     * @since 2021. 02. 01
+     */
+   int getHasGoodsCartCnt(Cart cart);
+
+    /**
+     * 장바구니 등록 상품 확인
+     * @param Cart
+     * @return
+     * @author xodud1202
+     * @since 2021. 02. 01
+     */
+    Collection<Integer> selectHasSetItemCartList(Cart param);
+
+    /**
+     * 장바구니 등록 상품 확인
+     * @param Cart
+     * @return
+     * @author xodud1202
+     * @since 2021. 02. 01
+     */
+    Collection<Integer> selectHasNormalDealItemCartList(Cart param);
+
+    /**
+     * 장바구니 신규 등록
+     * @param Cart
+     * @return void
+     * @author xodud1202
+     * @since 2021. 02. 01
+     */
+    void insertCartInfo(Cart param);
+
+    /**
+     * 장바구니 상세 신규 등록
+     * @param Cart
+     * @return void
+     * @author xodud1202
+     * @since 2021. 02. 01
+     */
+    void insertCartDetailInfo(Cart param);
+
+    /**
+     * 장바구니 상세 신규 등록
+     * @param Cart
+     * @return void
+     * @author xodud1202
+     * @since 2021. 02. 01
+     */
+    void updateCartInfo(Cart param);
+
+    /**
+     * 장바구니 이력 정보 저장
+     * @param Cart
+     * @return void
+     * @author xodud1202
+     * @since 2021. 02. 02
+     */
+    void insertCartHst(Cart param);
 
+    /**
+     * 장바구니 상세 이력 정보 저장
+     * @param Cart
+     * @return void
+     * @author xodud1202
+     * @since 2021. 02. 02
+     */
+    void insertCartDetailHst(Cart param);
 }

+ 27 - 0
src/main/java/com/style24/front/biz/dao/TsfGoodsDao.java

@@ -1,6 +1,8 @@
 package com.style24.front.biz.dao;
 
 import com.style24.core.support.annotation.ShopDs;
+import com.style24.persistence.domain.Goods;
+import com.style24.persistence.domain.GoodsStock;
 
 /**
  * 상품 Dao
@@ -10,5 +12,30 @@ import com.style24.core.support.annotation.ShopDs;
  */
 @ShopDs
 public interface TsfGoodsDao {
+    /**
+     * 상품 정보 조회
+     * @param goods
+     * @return
+     * @author xodud1202
+     * @since 2021. 01. 28
+     */
+    Goods getGoodsInfo(Goods goods);
 
+    /**
+     * 사은품 구성 상품 정보 조회
+     * @param goods
+     * @return
+     * @author xodud1202
+     * @since 2021. 01. 28
+     */
+    Goods getGoodsCompsInfo(Goods goods);
+
+    /**
+     * 상품 재고 조회
+     * @param goodsStock
+     * @return
+     * @author xodud1202
+     * @since 2021. 01. 28
+     */
+    GoodsStock getGoodsStockInfo(GoodsStock goodsStock);
 }

+ 180 - 3
src/main/java/com/style24/front/biz/service/TsfCartService.java

@@ -1,11 +1,19 @@
 package com.style24.front.biz.service;
 
+import com.style24.core.support.env.TscConstants;
+import com.style24.core.support.session.TscSession;
+import com.style24.front.biz.dao.TsfCartDao;
+import com.style24.front.support.env.TsfConstants;
+import com.style24.front.support.security.session.TsfSession;
+import com.style24.persistence.domain.Cart;
+import com.style24.persistence.domain.Goods;
+import com.style24.persistence.domain.GoodsStock;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
-import com.style24.front.biz.dao.TsfCartDao;
-
-import lombok.extern.slf4j.Slf4j;
+import java.util.Collection;
 
 /**
  * 장바구니 Service
@@ -20,4 +28,173 @@ public class TsfCartService {
 	@Autowired
 	private TsfCartDao cartDao;
 
+	@Autowired
+	private TsfGoodsService goodsService;
+
+	/**
+	 * 장바구니 저장
+	 * 단품 : goodsCd, optCd, optCd1, optCd2
+	 * 세트 : goodsCd,
+	 * @param cart
+	 */
+	@Transactional("shopTxnManager")
+	public String saveCartInfo(Collection<Cart> params) {
+		Cart cart = new Cart();
+		// JSESSION_ID 저장
+
+
+		// 로그인 유무 확인 (로그인이 되어 있지 않으면 regNo 를 0으로 장바구니에 저장한다.)
+		// TODO 로그인여부체크
+
+		// 장바구니 상품 및 재고 가능 여부 체크
+		for(Cart param : params) {
+			cart.setJsessionId(TscSession.getSessionId());
+			cart.setRegNo(0);
+			cart.setUpdNo(0);
+
+			// 상품 마스터 정보 확인
+			Goods goods = new Goods();
+			goods.setGoodsCd(param.getGoodsCd());
+			goods = goodsService.getGoodsInfo(goods);
+			if (goods == null) {
+				throw new IllegalArgumentException("상품 정보가 존재하지 않습니다.");
+			}
+
+			cart.setGoodsCd(param.getGoodsCd());
+			int goodsCartCnt = cartDao.getHasGoodsCartCnt(param);
+			if(param.getGoodsQty() + goodsCartCnt > goods.getDayMaxOrdQty()) {
+				throw new IllegalArgumentException("1일 구매한도 수량이 초과되었습니다.");
+			}
+
+			// 상품 재고 확인
+			GoodsStock checkParam = new GoodsStock();
+			checkParam.setGoodsCd(param.getGoodsCd());
+			checkParam.setItemCd(param.getItemCd());
+			checkParam.setOptCd(param.getOptCd());
+			checkParam.setGoodsQty(param.getGoodsQty());
+			checkParam.setGoodsType(param.getGoodsType());
+			String stockResult = goodsService.getCheckStock(checkParam);
+
+			if(!"SUCCESS".equals(stockResult)) {
+				throw new IllegalArgumentException(stockResult);
+			}
+
+			cart = param;
+		}
+
+		// 장바구니 정보 수정
+		if(cart.getGoodsType().equals(TscConstants.GOODS_TYPE.SET.value())) {
+			// 세트상품일 경우
+			saveSetTypeCartInfo(params);
+		} else {
+			// 세트 상품이 아닐 경우
+			saveNormalDealCartInfo(params.iterator().next());
+		}
+
+		return "SUCCESS";
+	}
+
+	@Transactional("shopTxnManager")
+	public void saveSetTypeCartInfo(Collection<Cart> params) {
+		Cart cart = new Cart();
+		StringBuilder sb = new StringBuilder();
+
+		// TODO 로그인 정보 세팅
+		cart.setRegNo(0);
+		cart.setCustNo(0);
+		cart.setUpdNo(0);
+
+		for(Cart param : params) {
+			sb.append("UNION ALL SELECT '").append(param.getGoodsCd()).append("' AS GOODS_CD, '")
+					.append(param.getItemCd()).append("' AS ITEM_CD, '")
+					.append(param.getOptCd()).append("' AS OPT_CD\n");
+		}
+
+		// cart 정보 세팅
+		cart.setJsessionId(TscSession.getSessionId());
+		cart.setContentsLoc(params.iterator().next().getContentsLoc());
+		cart.setAfLinkCd(params.iterator().next().getAfLinkCd());
+		cart.setIthrCd(params.iterator().next().getIthrCd());
+		cart.setPlanDtlSq(params.iterator().next().getPlanDtlSq());
+		cart.setGoodsCd(params.iterator().next().getGoodsCd());
+		cart.setGoodsQty(params.iterator().next().getGoodsQty());
+		cart.setGoodsType(params.iterator().next().getGoodsType());
+		cart.setItemCdSql(sb.toString());
+
+		if("C".equals(params.iterator().next().getCartGb())) {
+			cart.setCartGb(TscConstants.CartGb.CART.value());
+		} else if("O".equals(params.iterator().next().getCartGb())) {
+			if("P".equals(TsfSession.getFrontGb())) {
+				cart.setCartGb(TscConstants.CartGb.PC_ORDER.value());
+			} else {
+				cart.setCartGb(TscConstants.CartGb.MOB_ORDER.value());
+			}
+		} else {
+			cart.setCartGb(TscConstants.CartGb.CREATE_ORDER.value());
+		}
+
+		// 같은 장바구니 상품 확인
+		Collection<Integer> cartSqList = cartDao.selectHasSetItemCartList(cart);
+
+		if(cartSqList != null && cartSqList.size() > 0) {		// 장바구니 기존재
+			if(cartSqList.size() > 1) {							// 장바구니 조회 결과 이상시 insert or select 수정 필요
+				throw new IllegalArgumentException("장바구니 조회에 실패하였습니다. 관리자에게 문의해주세요.");
+			} else {
+				cart.setCartSq(cartSqList.iterator().next());
+				cartDao.updateCartInfo(cart);               // 장바구니 정보 수정
+				cartDao.insertCartHst(cart);                // 장바구니 수정 이력 저장
+			}
+		} else {
+			cartDao.insertCartInfo(cart);					// 장바구니 마스터 정보 저장
+			cartDao.insertCartHst(cart);                    // 장바구니 이력 정보 저장
+			for(Cart param : params) {
+				param.setCartSq(cart.getCartSq());
+				param.setRegNo(cart.getRegNo());
+				param.setCustNo(cart.getCustNo());
+				param.setUpdNo(cart.getUpdNo());
+
+				cartDao.insertCartDetailInfo(param);		// 장바구니 상세 저장
+				cartDao.insertCartDetailHst(param);         // 장바구니 상세 이력 저장
+			}
+		}
+	}
+
+	@Transactional("shopTxnManager")
+	public void saveNormalDealCartInfo(Cart param) {
+		// TODO 로그인 정보 세팅
+		param.setJsessionId(TscSession.getSessionId());
+		param.setRegNo(0);
+		param.setCustNo(0);
+		param.setUpdNo(0);
+
+		if("C".equals(param.getCartGb())) {
+			param.setCartGb(TscConstants.CartGb.CART.value());
+		} else if("O".equals(param.getCartGb())) {
+			if("P".equals(TsfSession.getFrontGb())) {
+				param.setCartGb(TscConstants.CartGb.PC_ORDER.value());
+			} else {
+				param.setCartGb(TscConstants.CartGb.MOB_ORDER.value());
+			}
+		} else {
+			param.setCartGb(TscConstants.CartGb.CREATE_ORDER.value());
+		}
+
+		// 같은 장바구니 상품 확인
+		Collection<Integer> cartSqList = cartDao.selectHasNormalDealItemCartList(param);
+
+		if(cartSqList != null && cartSqList.size() > 0) {		// 장바구니 기존재
+			if(cartSqList.size() > 1) {							// 장바구니 조회 결과 이상시 insert or select 수정 필요
+				throw new IllegalArgumentException("장바구니 조회에 실패하였습니다. 관리자에게 문의해주세요.");
+			} else {
+				param.setCartSq(cartSqList.iterator().next());
+				cartDao.updateCartInfo(param);
+				cartDao.insertCartHst(param);                // 장바구니 수정 이력 저장
+			}
+		} else {
+			cartDao.insertCartInfo(param);					// 장바구니 마스터 정보 저장
+			cartDao.insertCartHst(param);                   // 장바구니 수정 이력 저장
+			cartDao.insertCartDetailInfo(param);			// 장바구니 상세 저장
+			cartDao.insertCartDetailHst(param);             // 장바구니 수정 이력 저장
+		}
+	}
 }

+ 85 - 0
src/main/java/com/style24/front/biz/service/TsfGoodsService.java

@@ -1,5 +1,8 @@
 package com.style24.front.biz.service;
 
+import com.style24.core.support.env.TscConstants;
+import com.style24.persistence.domain.Goods;
+import com.style24.persistence.domain.GoodsStock;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -20,4 +23,86 @@ public class TsfGoodsService {
 	@Autowired
 	private TsfGoodsDao goodsDao;
 
+	/**
+	 * 상품 정보 조회
+	 * @param goods
+	 * @return
+	 * @author xodud1202
+	 * @since 2021. 01. 28
+	 */
+	public Goods getGoodsInfo(Goods goods) {
+		return goodsDao.getGoodsInfo(goods);
+	}
+
+	/**
+	 * 구성 상품 정보 조회
+	 * @param goods
+	 * @return
+	 * @author xodud1202
+	 * @since 2021. 01. 28
+	 */
+	public Goods getGoodsCompsInfo(Goods goods) {
+		return goodsDao.getGoodsCompsInfo(goods);
+	}
+
+	/**
+	 * 상품 재고 정보
+	 * @param goodsStock
+	 * @return
+	 * @author xodud1202
+	 * @since 2021. 01. 28
+	 */
+	public GoodsStock getGoodsStockInfo(GoodsStock goodsStock) {
+		return goodsDao.getGoodsStockInfo(goodsStock);
+	}
+
+	/**
+	 * 상품 재고 체크
+	 * @param goodsStock
+	 * goodsCd   (원상품코드) 필수
+	 * goodsType (상품타입) 필수
+	 * optCd     (옵션코드) 필수
+	 * goodsQty  (확인 재고 수량) 필수
+	 * itemCd    (세트 구성 상품코드) 세트
+	 * @return String
+	 * @author xodud1202
+	 * @since 2021. 01. 28
+	 */
+	public String getCheckStock(GoodsStock param) {
+		if(param.getGoodsType().equals(TscConstants.GOODS_TYPE.SET.value())) {		// 세트상품이면 구성 상품코드로 조회
+			GoodsStock stockCheck = new GoodsStock();		// 재고 조회 결과
+			stockCheck.setGoodsCd(param.getItemCd());
+			stockCheck.setOptCd(param.getOptCd());
+
+			stockCheck = goodsDao.getGoodsStockInfo(stockCheck);					// 구성 상품 재고 조회
+
+			Goods compsInfo = new Goods();
+			compsInfo.setGoodsCd(param.getGoodsCd());
+			compsInfo.setCompsGoodsCd(param.getItemCd());
+			compsInfo.setGoodsType(param.getGoodsType());
+
+			compsInfo = goodsDao.getGoodsCompsInfo(compsInfo);						// 구성상품 정보 조회
+
+			log.info("CHECK param.getGoodsQty() INFO >> " + param.getGoodsQty());
+			log.info("CHECK compsInfo.getQty() INFO >> " + compsInfo.getQty());
+			log.info("CHECK stockCheck.getCurrStockQty() INFO >> " + stockCheck.getCurrStockQty());
+
+			// 재고 체크
+			if(param.getGoodsQty() * compsInfo.getQty() > stockCheck.getCurrStockQty()) {
+				return param.getItemCd() + "의 재고가 충분하지 않습니다.";
+			}
+		} else {
+			GoodsStock stockCheck = new GoodsStock();		// 재고 조회 결과
+			stockCheck.setGoodsCd(param.getGoodsCd());
+			stockCheck.setOptCd(param.getOptCd());
+
+			stockCheck = goodsDao.getGoodsStockInfo(stockCheck);					// 구성 상품 재고 조회
+
+			if(param.getGoodsQty() > stockCheck.getCurrStockQty()) {
+				return param.getGoodsCd() + "의 재고가 충분하지 않습니다.";
+			}
+		}
+
+		return "SUCCESS";
+	}
 }

+ 53 - 5
src/main/java/com/style24/front/biz/web/TsfCartController.java

@@ -1,14 +1,19 @@
 package com.style24.front.biz.web;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.gagaframework.web.rest.server.GagaResponse;
 import com.style24.core.support.message.TscMessageByLocale;
+import com.style24.core.support.session.TscSession;
 import com.style24.front.biz.service.TsfCartService;
 import com.style24.front.support.controller.TsfBaseController;
-
+import com.style24.persistence.domain.Cart;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.ModelAndView;
+
+import java.util.Collection;
 
 /**
  * 장바구니 Controller
@@ -27,4 +32,47 @@ public class TsfCartController extends TsfBaseController {
 	@Autowired
 	private TsfCartService cartService;
 
+	/**
+	 * 장바구니 화면
+	 * @return
+	 * @author xodud1202
+	 * @since 2021. 01. 28
+	 */
+	@GetMapping("/list/form")
+	public ModelAndView cartListForm() {
+		ModelAndView mav = new ModelAndView();
+		mav.setViewName(super.getDeviceViewName("cart/cartListForm"));
+		return mav;
+	}
+
+	/**
+	 * 장바구니 등록
+	 * @param param
+	 * goodsCd : 상품코드 (세트 : 세트상품코드, 딜상품 : 원상품코드, 일반상품 : 상품코드)
+	 * itemCd : 구성상품코드 (세트. 세트 아닐 경우 입력X)
+	 * optCd : 옵션코드
+	 * goodsType : 상품타입 (공통코드 G026)
+	 * goodsQty : 장바구니 등록 수량
+	 * cartGb : O = 바로주문, C = 장바구니
+	 * afLinkCd : 제휴링크코드
+	 * ithr_cd : 유입경고
+	 * contents_loc : 컨텐츠 위치
+	 * planDtlSq : 기획전상세번호
+	 * dealGoodsCd : 딜상품코드 (딜상품코드)
+	 * @return ModelAndView
+	 * @author xodud1202
+	 * @since 2021. 01. 28
+	 */
+	@ResponseBody
+	@PostMapping("/save")
+	public String freeGoodsPromotionSave(@RequestBody Collection<Cart> params) {
+		try {
+			String result = cartService.saveCartInfo(params);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return e.getMessage();
+		}
+
+		return message.getMessage("SUCC_0001");
+	}
 }

+ 0 - 1
src/main/java/com/style24/front/support/env/TsfConstants.java

@@ -48,5 +48,4 @@ public class TsfConstants {
 //			return value;
 //		}
 //	}
-
 }

+ 53 - 0
src/main/java/com/style24/persistence/domain/Cart.java

@@ -0,0 +1,53 @@
+package com.style24.persistence.domain;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.style24.persistence.TscBaseDomain;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 장바구니
+ *
+ * @author xodud1202
+ * @since 2021.01.22
+ */
+@SuppressWarnings("serial")
+@Data
+public class Cart extends TscBaseDomain {
+	// 장바구니 정보
+	private int cartSq;				// 장바구니 번호
+	private int cartDtlSq;          // 장바구니 상세 번호
+	private int custNo;				// 고객번호
+	private int planDtlSq;			// 기획전상세번호
+	private int goodsQty;			// 장바구니 등록 수량
+	private int ordNo;				// 주문번호
+	private String cartGb;			// 장바구니 구분 (공통코드G026)
+	private String goodsCd;			// 상품번호
+	private String productNo;		// ProductNo(WMS)
+	private String productCode;		// ProductCode(WMS)
+	private String jsessionId;		// JSESSIONID
+	private String afLinkCd;		// 제휴링크코드
+	private String ithrCd;			// 유입경로(공통코드 G027)
+	private String contentsLoc;		// 컨텐츠위치(공통코드G028)
+	private String dealGoodsCd;		// 딜상품코드
+
+	// 장바구니 상세 정보
+	private String itemCd;			// 단품코드(상품). 일반상품과 딜상품은 상품코드와 동일
+	private String optCd;			// 옵션코드
+	private String optCd1;			// 옵션코드1
+	private String optCd2;			// 옵션코드2
+	private String skuModelNo;		// SKUModelNo(WMS)
+	private int itemQty;			// 장바구니 등록된 상품의 기준재고
+
+	// 상품 정보
+	private int currStockQty;		// 기준 재고
+	private int qty;				// 구성 상품 기준 재고 수량
+	private String goodsType;		// 상품 타입
+	private String compsGoodsCd;	// 세트 구성상품 코드
+	private String itemCdSql;		// 상품 조회 쿼리문
+
+	// 다다익선 정보
+
+	// 즉시할인쿠폰 정보
+}

+ 26 - 0
src/main/java/com/style24/persistence/domain/Goods.java

@@ -0,0 +1,26 @@
+package com.style24.persistence.domain;
+
+import com.style24.persistence.TscBaseDomain;
+import lombok.Data;
+
+/**
+ * 장바구니
+ *
+ * @author xodud1202
+ * @since 2021.01.22
+ */
+@SuppressWarnings("serial")
+@Data
+public class Goods extends TscBaseDomain {
+	// 상품 정보
+	private String goodsCd;			// 상품번호
+	private String productNo;		// ProductNo(WMS)
+	private String productCode;		// ProductCode(WMS)
+	private String skuModelNo;		// SKUModelNo(WMS)
+	private String goodsType;		// 상품타입(공통코드G056)
+	private String compsGoodsCd;	// 세트 구성품 상품번호
+	private String optCd;			// 옵션코드
+	private String optCd1;			// 옵션코드1
+	private int dayMaxOrdQty;		// 일일 최대 구매 수량
+	private int qty;				// 세트 상품 구성 수량
+}

+ 26 - 0
src/main/java/com/style24/persistence/domain/GoodsStock.java

@@ -0,0 +1,26 @@
+package com.style24.persistence.domain;
+
+import com.style24.persistence.TscBaseDomain;
+import lombok.Data;
+
+/**
+ * 장바구니
+ *
+ * @author xodud1202
+ * @since 2021.01.22
+ */
+@SuppressWarnings("serial")
+@Data
+public class GoodsStock extends TscBaseDomain {
+	// 재고 정보
+	private int currStockQty;		// 가용재고
+	private int goodsQty;			// 안전재고
+	private String goodsCd;			// 상품번호
+	private String itemCd;			// 구성상품코드
+	private String goodsType;		// 상품구분
+	private String optCd;			// 옵션코드
+	private String optCd1;			// 옵션코드1(자사 : 컬러, 입점 : 옵션명1)
+	private String optCd2;			// 옵션코드1(자사 : 사이즈, 입점 : 옵션명2)
+	private String soldoutYn;		// 품절여부
+	private String skuModelNo;		// SKUModelNo(WMS)
+}

+ 247 - 1
src/main/java/com/style24/persistence/mybatis/TsfCart.xml

@@ -16,4 +16,250 @@
 	
 	
 
-</mapper>
+	<!-- 등록 상품 장바구니 수량 조회 -->
+	<select id="getHasGoodsCartCnt" parameterType="Cart" resultType="int">
+		/* TsfCart.getHasGoodsCartCnt : 등록 상품 장바구니 수량 조회 */
+		SELECT IFNULL(SUM(C.GOODS_QTY), 0) AS CNT
+		  FROM TB_CART C
+		 WHERE 1=1
+		   AND GOODS_CD = #{goodsCd}
+		<choose>
+			<when test="custNo == 0">
+		   AND C.JSESSION_ID = #{jsessionId}
+		   AND C.CUST_NO = 0
+			</when>
+			<otherwise>
+		   AND C.CUST_NO = #{custNo}
+			</otherwise>
+		</choose>
+	</select>
+
+	<!-- 장바구니 등록 세트 상품 확인 -->
+	<select id="selectHasSetItemCartList" parameterType="Cart" resultType="int">
+		/* TsfCart.selectHasSetItemCartList : 장바구니 등록 세트 상품 확인 */
+		SELECT C.CART_SQ
+		  FROM (SELECT GROUP_CONCAT(C.GOODS_CD, CD.ITEM_CD, CD.OPT_CD) AS SIZE_OPTION
+				     , C.CART_SQ
+			      FROM TB_CART C
+				 INNER JOIN (SELECT CD.ITEM_CD, CD.OPT_CD, CD.CART_SQ
+				    		   FROM TB_GOODS_COMPOSE GC
+				     		  INNER JOIN TB_CART_DETAIL CD
+				    			 ON GC.COMPS_GOODS_CD = CD.ITEM_CD
+				 				AND GC.GOODS_CD = #{goodsCd}
+				     		  ORDER BY CD.CART_SQ, GC.DISP_ORD) CD
+					ON C.CART_SQ = CD.CART_SQ
+			     WHERE 1=1
+				   AND C.CUST_NO = #{custNo}
+				   AND C.JSESSION_ID = #{jsessionId}
+			<choose>
+				<when test="regNo == 0">
+					AND C.JSESSION_ID = #{jsessionId}
+					AND C.CUST_NO = 0
+				</when>
+				<otherwise>
+					AND C.REG_NO = #{regNo}
+				</otherwise>
+			</choose>
+				   AND C.CART_GB = 'G026_BC'
+				   AND C.GOODS_CD = #{goodsCd}
+			     GROUP BY C.CART_SQ) C
+		 WHERE 1=1
+		   AND C.SIZE_OPTION = (SELECT GROUP_CONCAT(A.GOODS_CD, A.ITEM_CD, A.OPT_CD)
+							      FROM (SELECT NULL AS GOODS_CD, NULL AS ITEM_CD, NULL AS OPT_CD
+										${itemCdSql}
+							           ) A
+							     WHERE A.ITEM_CD IS NOT NULL)
+	</select>
+
+	<!-- 장바구니 등록 일반 or 딜 상품 확인 -->
+	<select id="selectHasNormalDealItemCartList" parameterType="Cart" resultType="int">
+		/* TsfCart.selectHasNormalDealItemCartList : 장바구니 등록 일반 or 딜 상품 확인 */
+		SELECT C.CART_SQ
+		  FROM TB_CART C
+		 WHERE 1=1
+		   AND C.CUST_NO = #{custNo}
+		   AND C.JSESSION_ID = #{jsessionId}
+		   AND C.GOODS_CD = #{goodsCd}
+	</select>
+
+	<!-- 장바구니 신규 등록 -->
+	<insert id="insertCartInfo" parameterType="Cart" keyProperty="cartSq">
+		/* TsfCart.insertCartInfo : 장바구니 신규 등록 */
+		INSERT INTO TB_CART (
+		          CART_GB
+		        , GOODS_CD
+		        , PRODUCT_NO
+		        , PRODUCT_CODE
+		        , GOODS_QTY
+		        , DEAL_GOODS_CD
+		        , JSESSION_ID
+		        , CUST_NO
+		        , AF_LINK_CD
+		        , ITHR_CD
+		        , CONTENTS_LOC
+		        , PLAN_DTL_SQ
+		        , REG_NO
+		        , REG_DT
+		        , UPD_NO
+		        , UPD_DT)
+		SELECT #{cartGb}
+			 , GOODS_CD
+			 , PRODUCT_NO
+			 , PRODUCT_CODE
+		     , #{goodsQty}
+		     , #{dealGoodsCd}
+			 , #{jsessionId}
+			 , #{custNo}
+			 , #{afLinkCd}
+			 , #{ithrCd}
+			 , #{contentsLoc}
+			 , #{planDtlSq}
+			 , #{regNo}
+		     , CURRENT_TIMESTAMP
+			 , #{updNo}
+			 , CURRENT_TIMESTAMP
+		  FROM TB_GOODS
+		 WHERE GOODS_CD = #{goodsCd}
+	</insert>
+
+	<!-- 장바구니 상세 신규 등록 -->
+	<insert id="insertCartDetailInfo" parameterType="Cart" keyProperty="cartDtlSq">
+		/* TsfCart.insertCartDetailInfo : 장바구니 상세 신규 등록 */
+		INSERT INTO TB_CART_DETAIL (
+		          CART_SQ
+		        , ITEM_CD
+		        , OPT_CD
+		        , OPT_CD1
+		        , OPT_CD2
+		        , SKU_MODEL_NO
+		        , PRODUCT_NO
+		        , PRODUCT_CODE
+		        , ITEM_QTY
+		        , DISP_ORD
+		        , REG_NO
+		        , REG_DT
+		        , UPD_NO
+		        , UPD_DT
+		)
+		SELECT #{cartSq}
+			 , IFNULL(GC.COMPS_GOODS_CD, G.GOODS_CD)		 <!-- 세트 상품이 아니면 원상품코드 -->
+		     , O.OPT_CD
+		     , O.OPT_CD1
+		     , O.OPT_CD2
+		     , O.SKU_MODEL_NO
+		     , G.PRODUCT_NO
+		     , G.PRODUCT_CODE
+		     , IFNULL(GC.QTY, 1)
+			 , IFNULL(GC.DISP_ORD, 1)						<!-- TB_GOODS_COMPOSE DISP_ORD 따라 CART_DETAIL도 동일하게 진행 -->
+			 , #{regNo}
+			 , CURRENT_TIMESTAMP
+			 , #{updNo}
+			 , CURRENT_TIMESTAMP
+		  FROM TB_GOODS G
+		 INNER JOIN TB_OPTION O
+		    ON G.GOODS_CD = O.GOODS_CD
+		  LEFT OUTER JOIN TB_GOODS_COMPOSE GC		<!-- 일반 상품 제외 확인 필요하여 join -->
+		    ON GC.COMPS_GOODS_CD = G.GOODS_CD
+		   AND GC.GOODS_CD = #{goodsCd}
+		   AND GC.COMPS_GOODS_CD = #{itemCd}
+		   AND GC.GOODS_TYPE = 'G056_S'				<!-- 세트 상품만 구성 상품 코드로 올림. deal 상품은 원상품코드로 올려야함 -->
+		 WHERE 1=1
+		<choose>
+			<when test="goodsType == 'G056_S'">
+		   AND O.GOODS_CD = #{itemCd}				<!-- 세트상품은 itemCd으로 조회 -->
+			</when>
+			<otherwise>
+		   AND O.GOODS_CD = #{goodsCd}
+			</otherwise>
+		</choose>
+		   AND O.OPT_CD = #{optCd}
+		 ORDER BY GC.DISP_ORD
+	</insert>
+
+	<!-- 장바구니 상세 UPDATE -->
+	<update id="updateCartInfo" parameterType="Cart">
+		/* TsfCart.updateCartInfo : 장바구니 상세 UPDATE */
+		UPDATE TB_CART SET
+			  GOODS_QTY = GOODS_QTY + #{goodsQty}
+		    , DEAL_GOODS_CD = #{dealGoodsCd}
+		    , AF_LINK_CD = #{afLinkCd}
+		    , ITHR_CD = #{ithrCd}
+		    , CONTENTS_LOC = #{contentsLoc}
+		    , PLAN_DTL_SQ = #{planDtlSq}
+			, UPD_NO = #{updNo}
+			, UPD_DT = CURRENT_TIMESTAMP
+		WHERE CART_SQ = #{cartSq}
+		  AND GOODS_CD = #{goodsCd}
+		  AND CUST_NO = #{custNo}
+	</update>
+
+	<!-- 장바구니 이력 정보 저장 -->
+	<insert id="insertCartHst" parameterType="Cart">
+		/* TsfCart.insertCartHst : 장바구니 이력 정보 저장 */
+		INSERT INTO TB_CART_HST (  CART_SQ
+								 , CART_GB
+								 , GOODS_CD
+								 , GOODS_QTY
+								 , PRODUCT_NO
+								 , PRODUCT_CODE
+								 , DEAL_GOODS_CD
+								 , ORD_NO
+								 , CUST_NO
+								 , AF_LINK_CD
+								 , ITHR_CD
+								 , CONTENTS_LOC
+								 , PLAN_DTL_SQ
+								 , REG_NO
+								 , REG_DT
+		)
+		SELECT C.CART_SQ
+			 , C.CART_GB
+			 , C.GOODS_CD
+		     , C.GOODS_QTY
+			 , C.PRODUCT_NO
+			 , C.PRODUCT_CODE
+			 , C.DEAL_GOODS_CD
+			 , IFNULL(#{ordNo}, 0) AS ORD_NO
+			 , C.CUST_NO
+			 , C.AF_LINK_CD
+			 , C.ITHR_CD
+			 , C.CONTENTS_LOC
+			 , PLAN_DTL_SQ
+			 , #{regNo}
+			 , CURRENT_TIMESTAMP
+		FROM   TB_CART C
+		WHERE  C.CART_SQ = #{cartSq}
+	</insert>
+
+	<!-- 장바구니 상세 이력 정보 저장 -->
+	<insert id="insertCartDetailHst" parameterType="Cart">
+		/* TsfCart.insertCartDetailHst : 장바구니 상세 이력 정보 저장 */
+		INSERT INTO TB_CART_DETAIL_HST ( CART_DTL_SQ
+									   , CART_SQ
+									   , ITEM_CD
+									   , OPT_CD
+									   , OPT_CD1
+									   , OPT_CD2
+									   , SKU_MODEL_NO
+									   , PRODUCT_NO
+									   , PRODUCT_CODE
+									   , ITEM_QTY
+									   , REG_NO
+									   , REG_DT
+		)
+		SELECT CD.CART_DTL_SQ
+			 , CD.CART_SQ
+			 , CD.ITEM_CD
+			 , CD.OPT_CD
+			 , CD.OPT_CD1
+			 , CD.OPT_CD2
+			 , CD.SKU_MODEL_NO
+			 , CD.PRODUCT_NO
+			 , CD.PRODUCT_CODE
+			 , CD.ITEM_QTY
+			 , #{regNo}
+			 , CURRENT_TIMESTAMP
+		FROM   TB_CART_DETAIL CD
+		WHERE  CD.CART_DTL_SQ = #{cartDtlSq}
+	</insert>
+</mapper>

+ 52 - 1
src/main/java/com/style24/persistence/mybatis/TsfGoods.xml

@@ -16,4 +16,55 @@
 	
 	
 
-</mapper>
+	<!-- 상품 정보 -->
+	<select id="getGoodsInfo" parameterType="Goods" resultType="Goods">
+		/* TsfGoods.getGoods */
+		SELECT G.GOODS_CD
+		     , G.PRODUCT_NO
+		     , G.PRODUCT_CODE
+			 , G.GOODS_TYPE
+			 , G.DAY_MAX_ORD_QTY
+		  FROM TB_GOODS G
+		 WHERE G.SELF_MALL_YN = 'Y'			<!-- 자사몰 노출 여부 -->
+		   AND G.GOODS_STAT = 'G008_90'		<!-- 상품 승인완료 -->
+		   AND G.GOODS_CD = #{goodsCd}
+	</select>
+
+	<!-- 구성 상품 정보 -->
+	<select id="getGoodsCompsInfo" parameterType="Goods" resultType="Goods">
+		/* TsfGoods.getGoodsCompsInfo */
+		SELECT GC.GOODS_CD
+			 , GC.COMPS_GOODS_CD
+			 , GC.GOODS_TYPE
+			 , GC.QTY
+		     , G.PRODUCT_NO
+			 , G.PRODUCT_CODE
+		  FROM TB_GOODS_COMPOSE GC
+		 INNER JOIN TB_GOODS G
+		    ON GC.COMPS_GOODS_CD = G.GOODS_CD
+		 WHERE GC.USE_YN = 'Y'
+		   AND G.SELF_MALL_YN = 'Y'			<!-- 자사몰 노출 여부 -->
+		   AND G.GOODS_STAT = 'G008_90'		<!-- 상품 승인완료 -->
+		   AND GC.GOODS_TYPE = #{goodsType}
+		   AND GC.GOODS_CD = #{goodsCd}
+		   AND GC.COMPS_GOODS_CD = #{compsGoodsCd}
+	</select>
+
+	<!-- 상품 재고 조회 -->
+	<select id="getGoodsStockInfo" parameterType="GoodsStock" resultType="GoodsStock">
+		/* TsfGoods.getGoodsStockInfo */
+		SELECT VS.GOODS_CD
+			 , VS.OPT_CD
+			 , VS.OPT_CD1
+			 , VS.OPT_CD2
+			 , VS.SOLDOUT_YN
+			 , VS.CURR_STOCK_QTY
+			 , O.SKU_MODEL_NO
+		  FROM VW_STOCK VS
+		 INNER JOIN TB_OPTION O
+		    ON VS.GOODS_CD = O.GOODS_CD
+		   AND VS.OPT_CD = O.OPT_CD
+		 WHERE VS.GOODS_CD = #{goodsCd}
+		   AND VS.OPT_CD = #{optCd}
+	</select>
+</mapper>

+ 1298 - 0
src/main/webapp/WEB-INF/views/web/cart/cartListFormWeb.html

@@ -0,0 +1,1298 @@
+<!DOCTYPE html>
+<html lang="ko"
+      xmlns:th="http://www.thymeleaf.org"
+      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+      layout:decorator="web/common/layout/DefaultLayoutWeb">
+<!--
+ *******************************************************************************
+ * @source  : cartListFormWeb
+ * @desc    : 장바구니
+ *============================================================================
+ * STYLE24
+ * Copyright(C) 2020 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2021.02.02   xodud1202   최초 작성
+ *******************************************************************************
+ -->
+<body>
+
+<th:block layout:fragment="content">
+    <!--  container -->
+    <div id="container" class="container od">
+        <div class="breadcrumb">
+            <ul>
+                <li class="bread_home"><a href="index.html">홈</a></li>
+                <li class="bread_2depth">쇼핑백</li>
+            </ul>
+        </div>
+        <div class="wrap">
+            <div class="content shopping_bag"> <!-- 페이지특정 클래스 = shop_bag -->
+                <div class="cont_head">
+                    <h2 class="t_c mb60">쇼핑백<button onclick="save();">이거이거</button></h2>
+                </div>
+                <div class="cont_body">
+                    <!-- CONT-BODY -->
+                    <section class="od_cont fl">
+                        <div class="sec_head">
+                            <div class="tbl type4">
+                                <table>
+                                    <colgroup>
+                                        <col width="150">
+                                        <col width="*">
+                                    </colgroup>
+                                    <tbody>
+                                    <tr>
+                                        <th>
+                                            배송방법 선택
+                                        </th>
+                                        <td>
+                                            <div class="form_field">
+                                                <div class="ml30">
+                                                    <input type="radio" name="radio" id="blt_ship1" value="">
+                                                    <label for="blt_ship1"><span><em>총알배송</em>10/15일 24:00 까지 도착</span></label>
+                                                </div>
+                                                <div class="ml30">
+                                                    <input type="radio" name="radio" id="blt_ship2" value="" checked="">
+                                                    <label for="blt_ship2"><span>총알 배송 안함 (10/25일 도착 예정)</span></label>
+                                                </div>
+                                            </div>
+                                        </td>
+                                    </tr>
+                                    </tbody>
+                                </table>
+                            </div>
+                        </div>
+                        <div class="sec_body">
+                            <!-- 총알배송 -->
+                            <div class="part_dlvr mt100">
+                                <h3 class="subH2 mb20">
+                                    STYLE24 총알배송
+                                    <span class="ml10">오늘 자정까지 도착</span>
+                                </h3>
+                                <div class="btn_area">
+                                    <button type="button" class="btn btn_default"><span><i class="ico ico_trash"></i>전체 삭제</span></button>
+                                </div>
+                                <div class="tbl type2">
+                                    <table>
+                                        <colgroup>
+                                            <col width="710">
+                                            <col width="190">
+                                            <col width="*">
+                                        </colgroup>
+                                        <tbody>
+                                        <tr>
+                                            <td class="t_l">
+                                                <div class="info_item">
+                                                    <div class="form_box">
+                                                        <p class="form_field">
+                                                            <input id="od_item1" type="checkbox" checked=""><label for="od_item1"><span class="sr-only">상품선택</span></label>
+                                                        </p>
+                                                    </div>
+                                                    <div class="thumb_box">
+                                                        <a href="">
+                                                            <img src="/images/pc/thumb/tmp_pdClickother1.jpg" width="100%" alt="">
+                                                        </a>
+                                                    </div>
+                                                    <div class="info_box">
+                                                        <p class="od_name">
+                                                            <a href="">
+                                                                <span class="brand">나이키 키즈</span>
+                                                                <span class="name">몰리겨울상하복 균일가 택1 유아동/상하복/기모상하복/상하의세트 몰리겨울상하복 균일가 택1 유아동/상하복/기모상하복/상하의세트몰리겨울상하복 균일가 택1 유아동/상하복/기모상하복/상하의세트</span>
+                                                            </a>
+                                                        </p>
+                                                        <p class="od_opt">
+                                                            <span class="option">옵션:<em>01_루돌프융기모상하복_D오렌지/110</em></span>
+                                                            <span class="count">수량:<em>99</em>개</span>
+                                                        </p>
+                                                        <p class="od_modify">
+                                                            <button type="button"><span>옵션/수량변경</span></button>
+                                                        </p>
+                                                    </div>
+                                                </div>
+                                            </td>
+                                            <td>
+                                                <div class="info_calc">
+                                                    <p class="price">
+                                                        <span class="selling_price">61,200원</span>
+                                                        <del>100,000원</del>
+                                                    </p>
+                                                    <p class="point"><span>49</span>p 적립예정</p>
+                                                    <p>
+                                                        <button type="button" class="btn btn_primary btn_sm"><span>즉시구매</span></button>
+                                                    </p>
+                                                    <p class="util">
+                                                        <span>
+                                                            <button type="button" class="btn_favorite"><span><i class="ico ico_like"></i><em class="sr-only">관심상품 추가</em></span></button>
+                                                        </span>
+                                                        <span>
+                                                            <button type="button" class="btn_delete"><span><i class="ico ico_trash"></i><em class="sr-only">상품삭제</em></span></button>
+                                                        </span>
+                                                    </p>
+                                                </div>
+                                            </td>
+                                            <td class="merge_row"> <!-- 같은 데이터 노출시 동일 영역끼리 병합 : 클래스명 merge_row 추가 -->
+                                                <div class="info_dlvr">
+                                                    <span class="dlvr_fee">3,000원</span>
+                                                    <a href="#" target="_blank">배송비 SAVE 상품 보기</a>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                            <td class="t_l">
+                                                <div class="info_item">
+                                                    <div class="form_box">
+                                                        <p class="form_field">
+                                                            <input id="od_item2" type="checkbox" checked=""><label for="od_item2"><span class="sr-only">상품선택</span></label>
+                                                        </p>
+                                                    </div>
+                                                    <div class="thumb_box">
+                                                        <a href="">
+                                                            <img src="/images/pc/thumb/tmp_pdClickother1.jpg" width="100%" alt="">
+                                                        </a>
+                                                    </div>
+                                                    <div class="info_box">
+                                                        <p class="od_name">
+                                                            <a href="">
+                                                                <span class="brand">페르지노 몬티</span>
+                                                                <span class="name">몰리겨울상하복 균일가 택1 </span>
+                                                            </a>
+                                                        </p>
+                                                        <p class="od_opt">
+                                                            <span class="option">옵션:<em>01_루돌프융기모상하복_D오렌지/110</em></span>
+                                                            <span class="count">수량:<em>100</em>개</span>
+                                                        </p>
+                                                        <p class="od_modify">
+                                                            <button type="button"><span>옵션/수량변경</span></button>
+                                                        </p>
+                                                    </div>
+                                                </div>
+                                            </td>
+                                            <td>
+                                                <div class="info_calc">
+                                                    <p class="price">
+                                                        <span class="selling_price">61,200원</span>
+                                                        <del>100,000원</del>
+                                                    </p>
+                                                    <p class="point"><span>49</span>p 적립예정</p>
+                                                    <p>
+                                                        <button type="button" class="btn btn_primary btn_sm"><span>즉시구매</span></button>
+                                                    </p>
+                                                    <p class="util">
+                                                        <span>
+                                                            <button type="button" class="btn_favorite"><span><i class="ico ico_like"></i><em class="sr-only">관심상품 추가</em></span></button>
+                                                        </span>
+                                                        <span>
+                                                            <button type="button" class="btn_delete"><span><i class="ico ico_trash"></i><em class="sr-only">상품삭제</em></span></button>
+                                                        </span>
+                                                    </p>
+                                                </div>
+                                            </td>
+                                            <td class="merge_row"> <!-- 같은 데이터 노출시 동일 영역끼리 병합 : 클래스명 merge_row 추가 -->
+                                                <div class="info_dlvr">
+                                                    <span class="dlvr_fee">3,000원</span>
+                                                    <a href="#" target="_blank">배송비 SAVE 상품 보기</a>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                            <td class="t_l">
+                                                <div class="info_item">
+                                                    <div class="form_box">
+                                                        <p class="form_field">
+                                                            <input id="od_item3" type="checkbox" checked=""><label for="od_item3"><span class="sr-only">상품선택</span></label>
+                                                        </p>
+                                                    </div>
+                                                    <div class="thumb_box">
+                                                        <a href="">
+                                                            <img src="/images/pc/thumb/tmp_pdClickother1.jpg" width="100%" alt="">
+                                                        </a>
+                                                    </div>
+                                                    <div class="info_box">
+                                                        <p class="od_name">
+                                                            <a href="">
+                                                                <span class="brand">Mollimelli 몰리멜리</span>
+                                                                <span class="name">몰리겨울상하복 균일가 택1 </span>
+                                                            </a>
+                                                        </p>
+                                                        <p class="od_opt">
+                                                            <span class="option">옵션:<em>01_루돌프융기모상하복_D오렌지/110</em></span>
+                                                            <span class="count">수량:<em>100</em>개</span>
+                                                        </p>
+                                                        <p class="od_modify">
+                                                            <button type="button"><span>옵션/수량변경</span></button>
+                                                        </p>
+                                                    </div>
+                                                </div>
+                                            </td>
+                                            <td>
+                                                <div class="info_calc">
+                                                    <p class="price">
+                                                        <span class="selling_price">61,200원</span>
+                                                        <del>100,000원</del>
+                                                    </p>
+                                                    <p class="point"><span>49</span>p 적립예정</p>
+                                                    <p>
+                                                        <button type="button" class="btn btn_primary btn_sm"><span>즉시구매</span></button>
+                                                    </p>
+                                                    <p class="util">
+                                                            <span>
+                                                                <button type="button" class="btn_favorite"><span><i class="ico ico_like"></i><em class="sr-only">관심상품 추가</em></span></button>
+                                                            </span>
+                                                        <span>
+                                                                <button type="button" class="btn_delete"><span><i class="ico ico_trash"></i><em class="sr-only">상품삭제</em></span></button>
+                                                            </span>
+                                                    </p>
+                                                </div>
+                                            </td>
+                                            <td class="merge_row"> <!-- 같은 데이터 노출시 동일 영역끼리 병합 : 클래스명 merge_row 추가 -->
+                                                <div class="info_dlvr">
+                                                    <span class="dlvr_fee">3,000원</span>
+                                                    <a href="#" target="_blank">배송비 SAVE 상품 보기</a>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                        </tbody>
+                                    </table>
+                                </div>
+                            </div>
+                            <!-- //총알배송 -->
+                            <!-- 일반배송 -->
+                            <div class="part_dlvr mt100">
+                                <h3 class="subH2 mb20">
+                                    STYLE24 일반배송
+                                </h3>
+                                <div class="btn_area">
+                                    <button type="button" class="btn btn_default"><span><i class="ico ico_trash"></i>전체 삭제</span></button>
+                                </div>
+                                <div class="tbl type2">
+                                    <table>
+                                        <colgroup>
+                                            <col width="710">
+                                            <col width="190">
+                                            <col width="*">
+                                        </colgroup>
+                                        <tbody>
+                                        <tr>
+                                            <td class="t_l">
+                                                <div class="info_item">
+                                                    <div class="form_box">
+                                                        <p class="form_field">
+                                                            <input id="od_item_11" type="checkbox" checked=""><label for="od_item_11"><span class="sr-only">상품선택</span></label>
+                                                        </p>
+                                                    </div>
+                                                    <div class="thumb_box">
+                                                        <a href="">
+                                                            <img src="/images/pc/thumb/tmp_pdClickother1.jpg" width="100%" alt="">
+                                                        </a>
+                                                    </div>
+                                                    <div class="info_box">
+                                                        <p class="od_name">
+                                                            <a href="">
+                                                                <span class="brand">Mollimelli 몰리멜리</span>
+                                                                <span class="name">몰리겨울상하복 균일가 택1 유아동/상하복/기모상하복/상하의세트 몰리겨울상하복 균일가 택1 유아동/상하복/기모상하복/상하의세트몰리겨울상하복 균일가 택1 유아동/상하복/기모상하복/상하의세트</span>
+                                                            </a>
+                                                        </p>
+                                                        <p class="od_opt">
+                                                            <span class="option">옵션:<em>01_루돌프융기모상하복_D오렌지/110</em></span>
+                                                            <span class="count">수량:<em>99</em>개</span>
+                                                        </p>
+                                                        <p class="od_modify">
+                                                            <button type="button"><span>옵션/수량변경</span></button>
+                                                        </p>
+                                                        <div class="od_moresale">
+                                                            <a href="" class="btn_moresale">
+                                                                <i class="ico ico_saletag"></i><span class="c_primary">다다익선 적용내역</span>
+                                                            </a>
+                                                            <div class="li_moresale">
+                                                                <ul>
+                                                                    <li>트러커 자켓 3개 이상 구매시 10% 할인</li>
+                                                                    <li>TBJ상품 2만원 이상 구매시 10% 할인</li>
+                                                                </ul>
+                                                            </div>
+                                                        </div>
+                                                    </div>
+                                                </div>
+                                            </td>
+                                            <td>
+                                                <div class="info_calc">
+                                                    <p class="price">
+                                                        <span class="selling_price c_primary">61,200원</span> <!-- 다다익선 할인적용된 가격 표기 시 클래스 c_primary 추가 -->
+                                                        <del>100,000원</del>
+                                                    </p>
+                                                    <p class="point"><span>49</span>p 적립예정</p>
+                                                    <p>
+                                                        <button type="button" class="btn btn_primary btn_sm"><span>즉시구매</span></button>
+                                                    </p>
+                                                    <p class="util">
+                                                        <span>
+                                                            <button type="button" class="btn_favorite"><span><i class="ico ico_like"></i><em class="sr-only">관심상품 추가</em></span></button>
+                                                        </span>
+                                                        <span>
+                                                            <button type="button" class="btn_delete"><span><i class="ico ico_trash"></i><em class="sr-only">상품삭제</em></span></button>
+                                                        </span>
+                                                    </p>
+                                                </div>
+                                            </td>
+                                            <td class="merge_row"> <!-- 같은 데이터 노출시 동일 영역끼리 병합 : 클래스명 merge_row 추가 -->
+                                                <div class="info_dlvr">
+                                                    <span class="dlvr_fee">배송비 무료</span>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                            <td class="t_l">
+                                                <div class="info_item">
+                                                    <div class="form_box">
+                                                        <p class="form_field">
+                                                            <input id="od_item12" type="checkbox" checked=""><label for="od_item12"><span class="sr-only">상품선택</span></label>
+                                                        </p>
+                                                    </div>
+                                                    <div class="thumb_box">
+                                                        <a href="">
+                                                            <img src="/images/pc/thumb/tmp_pdClickother1.jpg" width="100%" alt="">
+                                                        </a>
+                                                    </div>
+                                                    <div class="info_box">
+                                                        <p class="od_name">
+                                                            <a href="">
+                                                                <span class="brand">Mollimelli 몰리멜리</span>
+                                                                <span class="name">몰리겨울상하복 균일가 택1 </span>
+                                                            </a>
+                                                        </p>
+                                                        <p class="od_opt">
+                                                            <span class="option">옵션:<em>01_루돌프융기모상하복_D오렌지/110</em></span>
+                                                            <span class="count">수량:<em>100</em>개</span>
+                                                        </p>
+                                                        <p class="od_modify">
+                                                            <button type="button"><span>옵션/수량변경</span></button>
+                                                        </p>
+                                                        <div class="od_moresale">
+                                                            <a href="" class="btn_moresale">
+                                                                <i class="ico ico_saletag"></i><span class="c_primary">다다익선 대상 상품</span>
+                                                            </a>
+                                                            <div class="li_moresale">
+                                                                <ul>
+                                                                    <li>3개이상 구매시 10% 할인 <a href="">대상 상품 보기</a></li>
+                                                                    <li>TBJ 상품 2만원 이상 구매시 10% 할인 <a href="">대상 상품 보기</a></li>
+                                                                </ul>
+                                                            </div>
+                                                        </div>
+                                                    </div>
+                                                </div>
+                                            </td>
+                                            <td>
+                                                <div class="info_calc">
+                                                    <p class="price">
+                                                        <span class="selling_price">61,200원</span>
+                                                        <del>100,000원</del>
+                                                    </p>
+                                                    <p class="point"><span>49</span>p 적립예정</p>
+                                                    <p>
+                                                        <button type="button" class="btn btn_primary btn_sm"><span>즉시구매</span></button>
+                                                    </p>
+                                                    <p class="util">
+                                                            <span>
+                                                                <button type="button" class="btn_favorite"><span><i class="ico ico_like"></i><em class="sr-only">관심상품 추가</em></span></button>
+                                                            </span>
+                                                        <span>
+                                                                <button type="button" class="btn_delete"><span><i class="ico ico_trash"></i><em class="sr-only">상품삭제</em></span></button>
+                                                            </span>
+                                                    </p>
+                                                </div>
+                                            </td>
+                                            <td class="merge_row"> <!-- 같은 데이터 노출시 동일 영역끼리 병합 : 클래스명 merge_row 추가 -->
+                                                <div class="info_dlvr">
+                                                    <span class="dlvr_fee">배송비 무료</span>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                            <td class="t_l">
+                                                <div class="info_item">
+                                                    <div class="form_box">
+                                                        <p class="form_field">
+                                                            <input id="od_item13" type="checkbox" checked=""><label for="od_item13"><span class="sr-only">상품선택</span></label>
+                                                        </p>
+                                                    </div>
+                                                    <div class="thumb_box">
+                                                        <a href="">
+                                                            <img src="/images/pc/thumb/tmp_pdClickother1.jpg" width="100%" alt="">
+                                                        </a>
+                                                    </div>
+                                                    <div class="info_box">
+                                                        <p class="od_name">
+                                                            <a href="">
+                                                                <span class="brand">Mollimelli 몰리멜리</span>
+                                                                <span class="name">몰리겨울상하복 균일가 택1 </span>
+                                                            </a>
+                                                        </p>
+                                                        <p class="od_opt">
+                                                            <span class="option">옵션:<em>01_루돌프융기모상하복_D오렌지/110</em></span>
+                                                            <span class="count">수량:<em>100</em>개</span>
+                                                        </p>
+                                                        <p class="od_modify">
+                                                            <button type="button"><span>옵션/수량변경</span></button>
+                                                        </p>
+                                                    </div>
+                                                </div>
+                                            </td>
+                                            <td>
+                                                <div class="info_calc">
+                                                    <p class="price">
+                                                        <span class="selling_price">61,200원</span>
+                                                        <del>100,000원</del>
+                                                    </p>
+                                                    <p class="point"><span>49</span>p 적립예정</p>
+                                                    <p>
+                                                        <button type="button" class="btn btn_primary btn_sm"><span>즉시구매</span></button>
+                                                    </p>
+                                                    <p class="util">
+                                                            <span>
+                                                                <button type="button" class="btn_favorite"><span><i class="ico ico_like"></i><em class="sr-only">관심상품 추가</em></span></button>
+                                                            </span>
+                                                        <span>
+                                                                <button type="button" class="btn_delete"><span><i class="ico ico_trash"></i><em class="sr-only">상품삭제</em></span></button>
+                                                            </span>
+                                                    </p>
+                                                </div>
+                                            </td>
+                                            <td class="merge_row"> <!-- 같은 데이터 노출시 동일 영역끼리 병합 : 클래스명 merge_row 추가 -->
+                                                <div class="info_dlvr">
+                                                    <span class="dlvr_fee">3,000원</span>
+                                                    <a href="#" target="_blank">배송비 SAVE 상품 보기</a>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                        </tbody>
+                                    </table>
+                                </div>
+                            </div>
+                            <!-- //일반배송 -->
+                            <!-- 업체직배송 -->
+                            <div class="part_dlvr mt100">
+                                <h3 class="subH2 mb20">
+                                    업체직배송
+                                </h3>
+                                <div class="btn_area">
+                                    <button type="button" class="btn btn_default"><span><i class="ico ico_trash"></i>전체 삭제</span></button>
+                                </div>
+                                <div class="tbl type2">
+                                    <table>
+                                        <colgroup>
+                                            <col width="710">
+                                            <col width="190">
+                                            <col width="*">
+                                        </colgroup>
+                                        <tbody>
+                                        <tr>
+                                            <td class="t_l">
+                                                <div class="info_item">
+                                                    <div class="form_box">
+                                                        <p class="form_field">
+                                                            <input id="od_item_21" type="checkbox" checked=""><label for="od_item_21"><span class="sr-only">상품선택</span></label>
+                                                        </p>
+                                                    </div>
+                                                    <div class="thumb_box">
+                                                        <a href="">
+                                                            <img src="/images/pc/thumb/tmp_pdClickother1.jpg" width="100%" alt="">
+                                                        </a>
+                                                    </div>
+                                                    <div class="info_box">
+                                                        <p class="od_name">
+                                                            <a href="">
+                                                                <span class="brand">TBJ</span>
+                                                                <span class="name">몰리겨울상하복 균일가 택1 유아동/상하복/기모상하복/상하의세트 몰리겨울상하복 균일가 택1 유아동/상하복/기모상하복/상하의세트몰리겨울상하복 균일가 택1 유아동/상하복/기모상하복/상하의세트</span>
+                                                            </a>
+                                                        </p>
+                                                        <p class="od_opt">
+                                                            <span class="option">옵션:<em>01_루돌프융기모상하복_D오렌지/110</em></span>
+                                                            <span class="count">수량:<em>99</em>개</span>
+                                                        </p>
+                                                        <p class="od_modify">
+                                                            <button type="button"><span>옵션/수량변경</span></button>
+                                                        </p>
+                                                        <div class="od_moresale">
+                                                            <a href="" class="btn_moresale">
+                                                                <i class="ico ico_saletag"></i><span class="c_primary">다다익선 적용내역</span>
+                                                            </a>
+                                                            <div class="li_moresale">
+                                                                <ul>
+                                                                    <li>트러커 자켓 3개 이상 구매시 10% 할인</li>
+                                                                    <li>TBJ상품 2만원 이상 구매시 10% 할인</li>
+                                                                </ul>
+                                                            </div>
+                                                        </div>
+                                                    </div>
+                                                </div>
+                                            </td>
+                                            <td>
+                                                <div class="info_calc">
+                                                    <p class="price">
+                                                        <span class="selling_price c_primary">61,200원</span> <!-- 다다익선 할인적용된 가격 표기 시 클래스 c_primary 추가 -->
+                                                        <del>100,000원</del>
+                                                    </p>
+                                                    <p class="point"><span>49</span>p 적립예정</p>
+                                                    <p>
+                                                        <button type="button" class="btn btn_primary btn_sm"><span>즉시구매</span></button>
+                                                    </p>
+                                                    <p class="util">
+                                                        <span>
+                                                            <button type="button" class="btn_favorite"><span><i class="ico ico_like"></i><em class="sr-only">관심상품 추가</em></span></button>
+                                                        </span>
+                                                        <span>
+                                                            <button type="button" class="btn_delete"><span><i class="ico ico_trash"></i><em class="sr-only">상품삭제</em></span></button>
+                                                        </span>
+                                                    </p>
+                                                </div>
+                                            </td>
+                                            <td class="merge_row"> <!-- 같은 데이터 노출시 동일 영역끼리 병합 : 클래스명 merge_row 추가 -->
+                                                <div class="info_dlvr">
+                                                    <span class="dlvr_fee">3,000원</span>
+                                                    <span class="dlvr_shop">TBJ 업체직배송</span>
+                                                    <a href="#" target="_blank">배송비 SAVE 상품 보기</a>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                            <td class="t_l">
+                                                <div class="info_item">
+                                                    <div class="form_box">
+                                                        <p class="form_field">
+                                                            <input id="od_item22" type="checkbox" checked=""><label for="od_item22"><span class="sr-only">상품선택</span></label>
+                                                        </p>
+                                                    </div>
+                                                    <div class="thumb_box">
+                                                        <a href="">
+                                                            <img src="/images/pc/thumb/tmp_pdClickother1.jpg" width="100%" alt="">
+                                                        </a>
+                                                    </div>
+                                                    <div class="info_box">
+                                                        <p class="od_name">
+                                                            <a href="">
+                                                                <span class="brand">TBJ</span>
+                                                                <span class="name">몰리겨울상하복 균일가 택1 </span>
+                                                            </a>
+                                                        </p>
+                                                        <p class="od_opt">
+                                                            <span class="option">옵션:<em>01_루돌프융기모상하복_D오렌지/110</em></span>
+                                                            <span class="count">수량:<em>100</em>개</span>
+                                                        </p>
+                                                        <p class="od_modify">
+                                                            <button type="button"><span>옵션/수량변경</span></button>
+                                                        </p>
+                                                    </div>
+                                                </div>
+                                            </td>
+                                            <td>
+                                                <div class="info_calc">
+                                                    <p class="price">
+                                                        <span class="selling_price">61,200원</span>
+                                                        <del>100,000원</del>
+                                                    </p>
+                                                    <p class="point"><span>49</span>p 적립예정</p>
+                                                    <p>
+                                                        <button type="button" class="btn btn_primary btn_sm"><span>즉시구매</span></button>
+                                                    </p>
+                                                    <p class="util">
+                                                            <span>
+                                                                <button type="button" class="btn_favorite"><span><i class="ico ico_like"></i><em class="sr-only">관심상품 추가</em></span></button>
+                                                            </span>
+                                                        <span>
+                                                                <button type="button" class="btn_delete"><span><i class="ico ico_trash"></i><em class="sr-only">상품삭제</em></span></button>
+                                                            </span>
+                                                    </p>
+                                                </div>
+                                            </td>
+                                            <td class="merge_row"> <!-- 같은 데이터 노출시 동일 영역끼리 병합 : 클래스명 merge_row 추가 -->
+                                                <div class="info_dlvr">
+                                                    <span class="dlvr_fee">3,000원</span>
+                                                    <span class="dlvr_shop">TBJ 업체직배송</span>
+                                                    <a href="#" target="_blank">배송비 SAVE 상품 보기</a>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                            <td class="t_l">
+                                                <div class="info_item">
+                                                    <div class="form_box">
+                                                        <p class="form_field">
+                                                            <input id="od_item33" type="checkbox" checked=""><label for="od_item33"><span class="sr-only">상품선택</span></label>
+                                                        </p>
+                                                    </div>
+                                                    <div class="thumb_box">
+                                                        <a href="">
+                                                            <img src="/images/pc/thumb/tmp_pdClickother1.jpg" width="100%" alt="">
+                                                        </a>
+                                                    </div>
+                                                    <div class="info_box">
+                                                        <p class="od_name">
+                                                            <a href="">
+                                                                <span class="brand">ZIOZIA</span>
+                                                                <span class="name">몰리겨울상하복 균일가 택1 </span>
+                                                            </a>
+                                                        </p>
+                                                        <p class="od_opt">
+                                                            <span class="option">옵션:<em>01_루돌프융기모상하복_D오렌지/110</em></span>
+                                                            <span class="count">수량:<em>100</em>개</span>
+                                                        </p>
+                                                        <p class="od_modify">
+                                                            <button type="button"><span>옵션/수량변경</span></button>
+                                                        </p>
+                                                    </div>
+                                                </div>
+                                            </td>
+                                            <td>
+                                                <div class="info_calc">
+                                                    <p class="price">
+                                                        <span class="selling_price">61,200원</span>
+                                                        <del>100,000원</del>
+                                                    </p>
+                                                    <p class="point"><span>49</span>p 적립예정</p>
+                                                    <p>
+                                                        <button type="button" class="btn btn_primary btn_sm"><span>즉시구매</span></button>
+                                                    </p>
+                                                    <p class="util">
+                                                            <span>
+                                                                <button type="button" class="btn_favorite"><span><i class="ico ico_like"></i><em class="sr-only">관심상품 추가</em></span></button>
+                                                            </span>
+                                                        <span>
+                                                                <button type="button" class="btn_delete"><span><i class="ico ico_trash"></i><em class="sr-only">상품삭제</em></span></button>
+                                                            </span>
+                                                    </p>
+                                                </div>
+                                            </td>
+                                            <td class="merge_row"> <!-- 같은 데이터 노출시 동일 영역끼리 병합 : 클래스명 merge_row 추가 -->
+                                                <div class="info_dlvr">
+                                                    <span class="dlvr_fee">3,000원</span>
+                                                    <span class="dlvr_shop">ZIOZIA 업체직배송</span>
+                                                    <a href="#" target="_blank">배송비 SAVE 상품 보기</a>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                            <td class="t_l">
+                                                <div class="info_item">
+                                                    <div class="form_box">
+                                                        <p class="form_field">
+                                                            <input id="od_item34" type="checkbox" checked=""><label for="od_item34"><span class="sr-only">상품선택</span></label>
+                                                        </p>
+                                                    </div>
+                                                    <div class="thumb_box">
+                                                        <a href="">
+                                                            <img src="/images/pc/thumb/tmp_pdClickother1.jpg" width="100%" alt="">
+                                                        </a>
+                                                    </div>
+                                                    <div class="info_box">
+                                                        <p class="od_name">
+                                                            <a href="">
+                                                                <span class="brand">로엠</span>
+                                                                <span class="name">몰리겨울상하복 균일가 택1 </span>
+                                                            </a>
+                                                        </p>
+                                                        <p class="od_opt">
+                                                            <span class="option">옵션:<em>01_루돌프융기모상하복_D오렌지/110</em></span>
+                                                            <span class="count">수량:<em>100</em>개</span>
+                                                        </p>
+                                                        <p class="od_modify">
+                                                            <button type="button"><span>옵션/수량변경</span></button>
+                                                        </p>
+                                                    </div>
+                                                </div>
+                                            </td>
+                                            <td>
+                                                <div class="info_calc">
+                                                    <p class="price">
+                                                        <span class="selling_price">61,200원</span>
+                                                        <del>100,000원</del>
+                                                    </p>
+                                                    <p class="point"><span>49</span>p 적립예정</p>
+                                                    <p>
+                                                        <button type="button" class="btn btn_primary btn_sm"><span>즉시구매</span></button>
+                                                    </p>
+                                                    <p class="util">
+                                                            <span>
+                                                                <button type="button" class="btn_favorite"><span><i class="ico ico_like"></i><em class="sr-only">관심상품 추가</em></span></button>
+                                                            </span>
+                                                        <span>
+                                                                <button type="button" class="btn_delete"><span><i class="ico ico_trash"></i><em class="sr-only">상품삭제</em></span></button>
+                                                            </span>
+                                                    </p>
+                                                </div>
+                                            </td>
+                                            <td class="merge_row"> <!-- 같은 데이터 노출시 동일 영역끼리 병합 : 클래스명 merge_row 추가 -->
+                                                <div class="info_dlvr">
+                                                    <span class="dlvr_fee">3,000원</span>
+                                                    <span class="dlvr_shop">로엠 업체직배송</span>
+                                                    <a href="#" target="_blank">배송비 SAVE 상품 보기</a>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                        </tbody>
+                                    </table>
+                                </div>
+                            </div>
+                            <!-- //업체직배송 -->
+                            <!-- 예약배송 -->
+                            <div class="part_dlvr mt100">
+                                <h3 class="subH2 mb20">
+                                    예약배송
+                                </h3>
+                                <div class="btn_area">
+                                    <button type="button" class="btn btn_default"><span><i class="ico ico_trash"></i>전체 삭제</span></button>
+                                </div>
+                                <div class="tbl type2">
+                                    <table>
+                                        <colgroup>
+                                            <col width="710">
+                                            <col width="190">
+                                            <col width="*">
+                                        </colgroup>
+                                        <tbody>
+                                        <tr>
+                                            <td class="t_l">
+                                                <div class="info_item">
+                                                    <div class="form_box">
+                                                        <p class="form_field">
+                                                            <input id="od_item_21" type="checkbox" checked=""><label for="od_item_21"><span class="sr-only">상품선택</span></label>
+                                                        </p>
+                                                    </div>
+                                                    <div class="thumb_box">
+                                                        <a href="">
+                                                            <img src="/images/pc/thumb/tmp_pdClickother1.jpg" width="100%" alt="">
+                                                        </a>
+                                                    </div>
+                                                    <div class="info_box">
+                                                        <p class="od_name">
+                                                            <a href="">
+                                                                <span class="brand">TBJ</span>
+                                                                <span class="reserv_date">2020.12.25 배송예정</span>
+                                                                <span class="name">몰리겨울상하복 균일가 택1 유아동/상하복/기모상하복/상하의세트 몰리겨울상하복 균일가 택1 유아동/상하복/기모상하복/상하의세트몰리겨울상하복 균일가 택1 유아동/상하복/기모상하복/상하의세트</span>
+                                                            </a>
+                                                        </p>
+                                                        <p class="od_opt">
+                                                            <span class="option">옵션:<em>01_루돌프융기모상하복_D오렌지/110</em></span>
+                                                            <span class="count">수량:<em>99</em>개</span>
+                                                        </p>
+                                                        <p class="od_modify">
+                                                            <button type="button"><span>옵션/수량변경</span></button>
+                                                        </p>
+                                                        <div class="od_moresale">
+                                                            <a href="" class="btn_moresale">
+                                                                <i class="ico ico_saletag"></i><span class="c_primary">다다익선 적용내역</span>
+                                                            </a>
+                                                            <div class="li_moresale">
+                                                                <ul>
+                                                                    <li>트러커 자켓 3개 이상 구매시 10% 할인</li>
+                                                                    <li>TBJ상품 2만원 이상 구매시 10% 할인</li>
+                                                                </ul>
+                                                            </div>
+                                                        </div>
+                                                    </div>
+                                                </div>
+                                            </td>
+                                            <td>
+                                                <div class="info_calc">
+                                                    <p class="price">
+                                                        <span class="selling_price c_primary">61,200원</span> <!-- 다다익선 할인적용된 가격 표기 시 클래스 c_primary 추가 -->
+                                                        <del>100,000원</del>
+                                                    </p>
+                                                    <p class="point"><span>49</span>p 적립예정</p>
+                                                    <p>
+                                                        <button type="button" class="btn btn_primary btn_sm"><span>즉시구매</span></button>
+                                                    </p>
+                                                    <p class="util">
+                                                        <span>
+                                                            <button type="button" class="btn_favorite"><span><i class="ico ico_like"></i><em class="sr-only">관심상품 추가</em></span></button>
+                                                        </span>
+                                                        <span>
+                                                            <button type="button" class="btn_delete"><span><i class="ico ico_trash"></i><em class="sr-only">상품삭제</em></span></button>
+                                                        </span>
+                                                    </p>
+                                                </div>
+                                            </td>
+                                            <td class="merge_row"> <!-- 같은 데이터 노출시 동일 영역끼리 병합 : 클래스명 merge_row 추가 -->
+                                                <div class="info_dlvr">
+                                                    <span class="dlvr_fee">3,000원</span>
+                                                    <span class="dlvr_shop">TBJ 업체직배송</span>
+                                                    <a href="#" target="_blank">배송비 SAVE 상품 보기</a>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                            <td class="t_l">
+                                                <div class="info_item">
+                                                    <div class="form_box">
+                                                        <p class="form_field">
+                                                            <input id="od_item22" type="checkbox" checked=""><label for="od_item22"><span class="sr-only">상품선택</span></label>
+                                                        </p>
+                                                    </div>
+                                                    <div class="thumb_box">
+                                                        <a href="">
+                                                            <img src="/images/pc/thumb/tmp_pdClickother1.jpg" width="100%" alt="">
+                                                        </a>
+                                                    </div>
+                                                    <div class="info_box">
+                                                        <p class="od_name">
+                                                            <a href="">
+                                                                <span class="brand">TBJ</span>
+                                                                <span class="reserv_date">2020.12.25 배송예정</span>
+                                                                <span class="name">몰리겨울상하복 균일가 택1 </span>
+                                                            </a>
+                                                        </p>
+                                                        <p class="od_opt">
+                                                            <span class="option">옵션:<em>01_루돌프융기모상하복_D오렌지/110</em></span>
+                                                            <span class="count">수량:<em>100</em>개</span>
+                                                        </p>
+                                                        <p class="od_modify">
+                                                            <button type="button"><span>옵션/수량변경</span></button>
+                                                        </p>
+                                                    </div>
+                                                </div>
+                                            </td>
+                                            <td>
+                                                <div class="info_calc">
+                                                    <p class="price">
+                                                        <span class="selling_price">61,200원</span>
+                                                        <del>100,000원</del>
+                                                    </p>
+                                                    <p class="point"><span>49</span>p 적립예정</p>
+                                                    <p>
+                                                        <button type="button" class="btn btn_primary btn_sm"><span>즉시구매</span></button>
+                                                    </p>
+                                                    <p class="util">
+                                                            <span>
+                                                                <button type="button" class="btn_favorite"><span><i class="ico ico_like"></i><em class="sr-only">관심상품 추가</em></span></button>
+                                                            </span>
+                                                        <span>
+                                                                <button type="button" class="btn_delete"><span><i class="ico ico_trash"></i><em class="sr-only">상품삭제</em></span></button>
+                                                            </span>
+                                                    </p>
+                                                </div>
+                                            </td>
+                                            <td class="merge_row"> <!-- 같은 데이터 노출시 동일 영역끼리 병합 : 클래스명 merge_row 추가 -->
+                                                <div class="info_dlvr">
+                                                    <span class="dlvr_fee">3,000원</span>
+                                                    <span class="dlvr_shop">TBJ 업체직배송</span>
+                                                    <a href="#" target="_blank">배송비 SAVE 상품 보기</a>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                        </tbody>
+                                    </table>
+                                </div>
+                            </div>
+                            <!-- //예약배송 -->
+                        </div>
+                    </section>
+                    <section class="od_side fr">
+                        <div class="area_order">
+                            <div class="tit_box">
+                                <h3 class="subH2">결제 정보</h3>
+                                <span>
+                                    <em class="c_primary bold">14</em>개의 상품
+                                </span>
+                            </div>
+                            <div class="od_amount_box">
+                                <dl>
+                                    <div>
+                                        <dt>상품금액</dt>
+                                        <dd>1,746,500원</dd>
+                                    </div>
+                                    <div>
+                                        <dt>배송비</dt>
+                                        <dd>0원</dd>
+                                    </div>
+                                    <div>
+                                        <dt>할인금액</dt>
+                                        <dd><span class="c_primary">-1,746,500원</span></dd>
+                                    </div>
+                                </dl>
+                            </div>
+                            <div class="totalprice_box">
+                                <dl>
+                                    <dt>총 결제 예정 금액</dt>
+                                    <dd data-weight="price" data-font="lato"><span>3,546,200</span>원</dd>
+                                </dl>
+                            </div>
+                            <div class="btn_box">
+                                <button class="btn btn_primary btn_block btn_md"><span>주문하기</span></button>
+                            </div>
+                        </div>
+                        <div class="area_salecode">
+                            <h4 class="subH3 mb15">할인코드 입력</h4>
+                            <div class="form_field form_full">
+                                <div class="ui_col_12">
+                                    <div class="input_wrap form_full">
+                                        <label class="input_label sr-only">할인코드입력</label>
+                                        <input type="text" class="form_control" placeholder="할인코드를 입력해주세요.">
+                                    </div>
+                                </div>
+                                <button type="button" class="btn btn_default"><span>적용</span></button>
+                            </div>
+                            <div class="coupon_code">
+                                <p class="salecode_num">
+                                    할인코드 ABC123DEF
+                                </p>
+                                <p class="salecondition">
+                                    구매금액 300,000원 이상<br>
+                                    구매시 30% 할인
+                                </p>
+                            </div>
+                        </div>
+                        <div class="area_saleitem">
+                            <h4 class="subH3 mb15">다다익선 할인 대상이 있습니다.</h4>
+                            <div class="more_sale">
+                                <div class="item_gd">
+                                    <figure>
+                                        <a href="">
+                                            <span class="thumb"><img src="/images/pc/thumb/tmp_odSide2.jpg" alt=""></span>
+                                        </a>
+                                        <figcaption>
+                                            <a href="">
+                                                <div class="brand">CURLYSUE 컬리수</div>
+                                                <div class="name">[★2020 겨울신상★] 리버시블 경량점퍼 CPW0XQJM51 [겨울] 리버시블 경량점퍼 CPW0XQJM51 [겨울]</div>
+                                                <div class="price">
+                                                    <del>7,000,000</del>
+                                                    <span class="selling_price">1,000,000</span>
+                                                    <span class="discount">30%</span>
+                                                </div>
+                                            </a>
+                                        </figcaption>
+                                    </figure>
+                                </div>
+                                <div class="item_gd">
+                                    <figure>
+                                        <a href="">
+                                            <span class="thumb"><img src="/images/pc/thumb/tmp_odSide2.jpg" alt=""></span>
+                                        </a>
+                                        <figcaption>
+                                            <a href="">
+                                                <div class="brand">CURLYSUE 컬리수</div>
+                                                <div class="name">[★2020 겨울신상★] 리버시블 경량점퍼 CPW0XQJM51 [겨울] 리버시블 경량점퍼 CPW0XQJM51 [겨울]</div>
+                                                <div class="price">
+                                                    <span class="selling_price">1,000,000</span>
+                                                </div>
+                                            </a>
+                                        </figcaption>
+                                    </figure>
+                                </div>
+                                <div class="txt">
+                                    <a href="">
+                                        <i class="ico ico_saletag mr10"></i>
+                                        TBJ 브랜드 구매시 30,000원 할인
+                                    </a>
+                                </div>
+                            </div>
+                            <div class="more_sale">
+                                <div class="txt">
+                                    <a href="">
+                                        <i class="ico ico_saletag mr10"></i>
+                                        300,000이상 구매시 30% 할인
+                                    </a>
+                                </div>
+                            </div>
+                        </div>
+                    </section>
+                    <div class="clear"></div>
+                    <section class="od_recommend clear mt100">
+                        <h4 class="subH1 t_c mb40">추천상품</h4>
+                        <div class="item_slide">
+                            <div class="item_gd">
+                                <figure>
+                                    <a href="">
+                                        <span class="thumb"><img src="/images/pc/thumb/od_shopping_slide01.png" alt=""></span>
+                                    </a>
+                                    <figcaption>
+                                        <a href="">
+                                            <div class="brand">CURLYSUE 컬리수</div>
+                                            <div class="name">[★2020 겨울신상★] 리버시블 경량점퍼 CPW0XQJM51 [겨울]</div>
+                                            <div class="price">
+                                                <span class="selling_price">1,000,000</span>
+                                                <del>7,000,000</del>
+                                                <span class="discount">30%</span>
+                                            </div>
+                                            <div class="itemcolorchip">
+                                                <span class="chip_color35" value="ABM">BEIGE</span>
+                                                <span class="chip_color54" value="BDS">BLACK</span>
+                                                <span class="chip_color40" value="YBR">WHITE</span>
+                                            </div>
+                                            <div class="itemComment">#가을 느낌 물씬!</div>
+                                        </a>
+                                    </figcaption>
+                                </figure>
+                            </div>
+                            <div class="item_gd">
+                                <figure>
+                                    <a href="">
+                                        <span class="thumb"><img src="/images/pc/thumb/od_shopping_slide01.png" alt=""></span>
+                                    </a>
+                                    <figcaption>
+                                        <a href="">
+                                            <div class="brand">CURLYSUE 컬리수</div>
+                                            <div class="name">[★2020 겨울신상★] 리버시블 경량점퍼 CPW0XQJM51 [겨울]</div>
+                                            <div class="price">
+                                                <span class="selling_price">1,000,000</span>
+                                                <del>7,000,000</del>
+                                                <span class="discount">30%</span>
+                                            </div>
+                                            <div class="itemcolorchip">
+                                                <span class="chip_color35" value="ABM">BEIGE</span>
+                                                <span class="chip_color54" value="BDS">BLACK</span>
+                                                <span class="chip_color40" value="YBR">WHITE</span>
+                                            </div>
+                                            <div class="itemComment">#가을 느낌 물씬!</div>
+                                        </a>
+                                    </figcaption>
+                                </figure>
+                            </div>
+                            <div class="item_gd">
+                                <figure>
+                                    <a href="">
+                                        <span class="thumb"><img src="/images/pc/thumb/od_shopping_slide01.png" alt=""></span>
+                                    </a>
+                                    <figcaption>
+                                        <a href="">
+                                            <div class="brand">CURLYSUE 컬리수</div>
+                                            <div class="name">[★2020 겨울신상★] 리버시블 경량점퍼 CPW0XQJM51 [겨울]</div>
+                                            <div class="price">
+                                                <span class="selling_price">1,000,000</span>
+                                                <del>7,000,000</del>
+                                                <span class="discount">30%</span>
+                                            </div>
+                                            <div class="itemcolorchip">
+                                                <span class="chip_color35" value="ABM">BEIGE</span>
+                                                <span class="chip_color54" value="BDS">BLACK</span>
+                                                <span class="chip_color40" value="YBR">WHITE</span>
+                                            </div>
+                                            <div class="itemComment">#가을 느낌 물씬!</div>
+                                        </a>
+                                    </figcaption>
+                                </figure>
+                            </div>
+                            <div class="item_gd">
+                                <figure>
+                                    <a href="">
+                                        <span class="thumb"><img src="/images/pc/thumb/od_shopping_slide01.png" alt=""></span>
+                                    </a>
+                                    <figcaption>
+                                        <a href="">
+                                            <div class="brand">CURLYSUE 컬리수</div>
+                                            <div class="name">[★2020 겨울신상★] 리버시블 경량점퍼 CPW0XQJM51 [겨울]</div>
+                                            <div class="price">
+                                                <span class="selling_price">1,000,000</span>
+                                                <del>7,000,000</del>
+                                                <span class="discount">30%</span>
+                                            </div>
+                                            <div class="itemcolorchip">
+                                                <span class="chip_color35" value="ABM">BEIGE</span>
+                                                <span class="chip_color54" value="BDS">BLACK</span>
+                                                <span class="chip_color40" value="YBR">WHITE</span>
+                                            </div>
+                                            <div class="itemComment">#가을 느낌 물씬!</div>
+                                        </a>
+                                    </figcaption>
+                                </figure>
+                            </div>
+                            <div class="item_gd">
+                                <figure>
+                                    <a href="">
+                                        <span class="thumb"><img src="/images/pc/thumb/od_shopping_slide01.png" alt=""></span>
+                                    </a>
+                                    <figcaption>
+                                        <a href="">
+                                            <div class="brand">CURLYSUE 컬리수</div>
+                                            <div class="name">[★2020 겨울신상★] 리버시블 경량점퍼 CPW0XQJM51 [겨울]</div>
+                                            <div class="price">
+                                                <span class="selling_price">1,000,000</span>
+                                                <del>7,000,000</del>
+                                                <span class="discount">30%</span>
+                                            </div>
+                                            <div class="itemcolorchip">
+                                                <span class="chip_color35" value="ABM">BEIGE</span>
+                                                <span class="chip_color54" value="BDS">BLACK</span>
+                                                <span class="chip_color40" value="YBR">WHITE</span>
+                                            </div>
+                                            <div class="itemComment">#가을 느낌 물씬!</div>
+                                        </a>
+                                    </figcaption>
+                                </figure>
+                            </div>
+                            <div class="item_gd">
+                                <figure>
+                                    <a href="">
+                                        <span class="thumb"><img src="/images/pc/thumb/od_shopping_slide01.png" alt=""></span>
+                                    </a>
+                                    <figcaption>
+                                        <a href="">
+                                            <div class="brand">CURLYSUE 컬리수</div>
+                                            <div class="name">[★2020 겨울신상★] 리버시블 경량점퍼 CPW0XQJM51 [겨울]</div>
+                                            <div class="price">
+                                                <span class="selling_price">1,000,000</span>
+                                                <del>7,000,000</del>
+                                                <span class="discount">30%</span>
+                                            </div>
+                                            <div class="itemcolorchip">
+                                                <span class="chip_color35" value="ABM">BEIGE</span>
+                                                <span class="chip_color54" value="BDS">BLACK</span>
+                                                <span class="chip_color40" value="YBR">WHITE</span>
+                                            </div>
+                                            <div class="itemComment">#가을 느낌 물씬!</div>
+                                        </a>
+                                    </figcaption>
+                                </figure>
+                            </div>
+                            <div class="item_gd">
+                                <figure>
+                                    <a href="">
+                                        <span class="thumb"><img src="/images/pc/thumb/od_shopping_slide01.png" alt=""></span>
+                                    </a>
+                                    <figcaption>
+                                        <a href="">
+                                            <div class="brand">CURLYSUE 컬리수</div>
+                                            <div class="name">[★2020 겨울신상★] 리버시블 경량점퍼 CPW0XQJM51 [겨울]</div>
+                                            <div class="price">
+                                                <span class="selling_price">1,000,000</span>
+                                                <del>7,000,000</del>
+                                                <span class="discount">30%</span>
+                                            </div>
+                                            <div class="itemcolorchip">
+                                                <span class="chip_color35" value="ABM">BEIGE</span>
+                                                <span class="chip_color54" value="BDS">BLACK</span>
+                                                <span class="chip_color40" value="YBR">WHITE</span>
+                                            </div>
+                                            <div class="itemComment">#가을 느낌 물씬!</div>
+                                        </a>
+                                    </figcaption>
+                                </figure>
+                            </div>
+                            <div class="item_gd">
+                                <figure>
+                                    <a href="">
+                                        <span class="thumb"><img src="/images/pc/thumb/od_shopping_slide01.png" alt=""></span>
+                                    </a>
+                                    <figcaption>
+                                        <a href="">
+                                            <div class="brand">CURLYSUE 컬리수</div>
+                                            <div class="name">[★2020 겨울신상★] 리버시블 경량점퍼 CPW0XQJM51 [겨울]</div>
+                                            <div class="price">
+                                                <span class="selling_price">1,000,000</span>
+                                                <del>7,000,000</del>
+                                                <span class="discount">30%</span>
+                                            </div>
+                                            <div class="itemcolorchip">
+                                                <span class="chip_color35" value="ABM">BEIGE</span>
+                                                <span class="chip_color54" value="BDS">BLACK</span>
+                                                <span class="chip_color40" value="YBR">WHITE</span>
+                                            </div>
+                                            <div class="itemComment">#가을 느낌 물씬!</div>
+                                        </a>
+                                    </figcaption>
+                                </figure>
+                            </div>
+                        </div>
+                    </section>
+                    <!-- // CONT-BODY -->
+                </div>
+            </div>
+        </div>
+    </div>
+    <!-- // container -->
+
+    <script type="text/javascript">
+
+        $(function(){
+            // 추천상품 슬라이드
+            $('.od_recommend .item_slide').slick({
+                dots: true,
+                infinite: true,
+                speed: 300,
+                slidesToShow: 5,
+                slidesToScroll: 5,
+                adaptiveHeight: true
+            });
+
+            $('.part_dlvr').on('click','.btn_moresale',function(e){
+                //다다익선 적용내역 보기
+                e.preventDefault();
+                $(this).toggleClass('active');
+                $(this).parents('.od_moresale').find('.li_moresale').toggle();
+            }).on('click','.btn_favorite',function(e){
+                //관심상품 등록
+                e.preventDefault();
+                $(this).toggleClass('active');
+            });
+        });
+
+        function save() {
+            // 일반 & deal 상품 장바구니 등록 (일반&딜 상품도 배열에 담아서 전송해주세요.)let compsList = [];
+            /*let temp = new Object;
+            let compsList = [];
+            temp.goodsCd = "14373703";
+            temp.optCd = "블랙140";
+            temp.goodsQty = 1;
+            temp.goodsType = "G056_D";
+            temp.dealGoodsCd = "STY"
+            temp.cartGb = "C";
+            temp.afLinkCd = "afLinkCd";
+            temp.ithrCd = "G027_ZZZ";
+            temp.contentsLoc = "G028_YYY";
+            temp.planDtlSq = "123";
+            compsList.push(temp);
+            addCart(compsList);*/
+
+            // 세트상품 장바구니  (정렬순서는 TB_GOODS_COMPOSE.DISP_ORD ASC로 입력해주세요.)
+            /*let length = 2;
+            let compsList = [];
+            for(let j = 0 ; j < length ; j++) {
+                if(j == 0) {
+                    let temp = new Object;
+                    temp.goodsCd = "STYS000000016";
+                    temp.itemCd = '14373757';
+                    temp.optCd = "핑크120";
+                    temp.goodsQty = 9;
+                    temp.goodsType = "G056_S";
+                    temp.cartGb = "C";
+                    temp.afLinkCd = "afLinkCd";
+                    temp.ithrCd = "G027_ZZZ";
+                    temp.contentsLoc = "G028_YYY";
+                    temp.planDtlSq = "123";
+                    compsList.push(temp);
+                } else if (j == 1) {
+                    let temp = new Object;
+                    temp.goodsCd = "STYS000000016";
+                    temp.itemCd = '14373758';
+                    temp.optCd = "블랙100";
+                    temp.goodsQty = 9;
+                    temp.goodsType = "G056_S";
+                    temp.cartGb = "C";
+                    temp.afLinkCd = "afLinkCd2";
+                    temp.ithrCd = "G027_ZZZ2";
+                    temp.contentsLoc = "G028_YYY2";
+                    temp.planDtlSq = "1232";
+                    compsList.push(temp);
+                }
+            }
+
+            addCart(compsList);*/
+        }
+
+        function addCart(cartList) {
+            let jsonData = JSON.stringify(cartList);
+
+            $.ajax( {
+                type: "POST",
+                url : '/cart/save',
+                data : jsonData,
+                contentType: 'application/json',
+                dataType : 'text',
+                success : function(result) {
+                    alert(result);
+                }
+            });
+        }
+    </script>
+</th:block>
+</body>
+</html>

+ 10 - 0
src/main/webapp/WEB-INF/views/web/error/500Web.html

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Title</title>
+</head>
+<body>
+
+</body>
+</html>

+ 36 - 0
src/main/webapp/ux/plugins/gaga/gaga.agGrid.css

@@ -0,0 +1,36 @@
+/*
+ * agGrid css written by gagamel.
+ *
+ * Copyright (c) 2019 gagamel
+ *
+ * $Date: 2019-04-04 $
+ *
+ */
+
+.text-left { text-align: left; }
+.text-center { text-align: center; }
+.text-right { text-align: right; }
+
+/* Tooltip */
+.custom-tooltip {
+    position: absolute;
+    width: 150px;
+    height: 70px;
+    border: 1px solid cornflowerblue;
+    overflow: hidden;
+    pointer-events: none;
+    transition: opacity 1s;
+}
+
+.custom-tooltip.ag-tooltip-hiding {
+    opacity: 0;
+}
+
+.custom-tooltip p {
+    margin: 5px;
+    white-space: nowrap;
+}
+
+.custom-tooltip p:first-of-type {
+    font-weight: bold;
+}

+ 1874 - 0
src/main/webapp/ux/plugins/gaga/gaga.agGrid.js

@@ -0,0 +1,1874 @@
+/*
+ * ag-Grid(https://www.ag-grid.com) Common Java Script written by gagamel.
+ *
+ * Copyright (c) 2019 gagamel
+ * Dual licensed under GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2019-03-31 $
+ */
+
+var isNavigationKey = function(event) {
+	var KEY_LEFT = 37;
+	var KEY_UP = 38;
+	var KEY_RIGHT = 39;
+	var KEY_DOWN = 40;
+	var KEY_PAGE_UP = 33;
+	var KEY_PAGE_DOWN = 34;
+	var KEY_PAGE_HOME = 36;
+	var KEY_PAGE_END = 35;
+
+	var keyCode = event.keyCode;
+
+	return keyCode === KEY_LEFT || keyCode === KEY_RIGHT || keyCode === KEY_UP || keyCode === KEY_DOWN
+		|| keyCode === KEY_PAGE_DOWN || keyCode === KEY_PAGE_UP || keyCode === KEY_PAGE_HOME || keyCode === KEY_PAGE_END;
+}
+
+/**
+ * Customer loading
+ * @author gagamel
+ * @since 2019. 4. 10
+ */
+var getCustomLoadingOverlay = function() {
+	function CustomLoadingOverlay() {};
+
+	CustomLoadingOverlay.prototype.init = function(params) {
+		this.eGui = document.createElement('div');
+		this.eGui.innerHTML = ''
+			+ '<div style="'
+			+ 'background: url(/ux/plugins/gaga/loader.gif); border-style: none; background-repeat: no-repeat; '
+			+ 'position: absolute; top: 45%; left: 50%; width: auto; '
+			+ 'z-index: 101; padding: 16px; margin: 5px;'
+			+ '"></div>';
+	};
+
+	CustomLoadingOverlay.prototype.getGui = function() {
+		// Button disabled & progressBar creation
+		//$('.btn').each(function(idx) { $(this).attr('disabled', true); });
+		return this.eGui;
+	};
+
+	return CustomLoadingOverlay;
+}
+
+/**
+ * Datepicker component
+ * <pre>
+ *     cellEditor : 'datePicker'
+ * </pre>
+ * @author gagamel
+ * @since 2019. 4. 8
+ */
+var getDatePicker = function(pattern) {
+	if (typeof(pattern) == 'undefined') {
+		pattern = "yyyy/mm/dd";
+	}
+
+	// function to act as a class
+	function Datepicker() {};
+
+	// gets called once before the renderer is used
+	Datepicker.prototype.init = function(params) {
+		this.eGui = document.createElement('div');
+		this.eGui.innerHTML = '<input'
+			+ ' name="' + params.colDef.field + '"'
+			+ (!gagajf.isNull(params.value) ? ' value="' + params.value + '"' : '')
+			+ (!gagajf.isNull(params.maxlength) ? ' maxlength="' + params.maxlength + '"' : '')
+			+ (!gagajf.isNull(params.validType) ? ' data-valid-type="' + params.validType + '"' : '')
+			+ ' style="width: 100%; z-index: 999999"/>';
+
+		this.eInput = this.eGui.querySelector('input');
+
+		if (params.required) {
+			this.eInput.required = 'required';
+//			this.eInput.classList.add('required');
+		}
+
+		$(this.eInput).datepicker({
+			startView: 0,
+			format: pattern,
+			todayBtn: "linked",
+			keyboardNavigation: false,
+			forceParse: false,
+			autoclose: true,
+			todayHighlight: true,
+			onSelect: function(dateText, inst) {
+				params.stopEditing();
+			}
+		});
+	};
+
+	// gets called once when grid ready to insert the element
+	Datepicker.prototype.getGui = function() {
+		return this.eGui;
+	};
+
+	// focus and select can be done after the gui is attached
+	Datepicker.prototype.afterGuiAttached = function() {
+		this.eInput.focus();
+		this.eInput.select();
+	};
+
+	// returns the new value after editing
+	Datepicker.prototype.getValue = function() {
+		return this.eInput.value;
+	};
+
+	// any cleanup we need to be done here
+	Datepicker.prototype.destroy = function() {
+		// but this example is simple, no cleanup, we could
+		// even leave this method out as it's optional
+	};
+
+	// if true, then this editor will appear in a popup
+	Datepicker.prototype.isPopup = function() {
+		// and we could leave this method out also, false is the default
+		return true;
+	};
+
+	return Datepicker;
+}
+
+/**
+ * Datepicker component
+ * <pre>
+ *     cellEditor : 'dateTimer'
+ * </pre>
+ * @author gagamel
+ * @since 2020. 1. 09
+ */
+var getDateTimer = function(pattern) {
+	// function to act as a class
+	function DateTimer() {};
+
+	// gets called once before the renderer is used
+	DateTimer.prototype.init = function(params) {
+		this.eGui = document.createElement('div');
+
+		// 시간 초기화
+		var TimeH = '';
+		var TimeM = '';
+		var SecGb =  params.colDef.field;
+		if( SecGb.indexOf('ed') > -1 || SecGb.indexOf('end') > -1) {
+			TimeH = '23';
+			TimeM = '59';
+			this.sec = '59';
+		}else{
+			TimeH = '00';
+			TimeM = '00';
+			this.sec = '00';
+		}
+
+		if(typeof(params.value) != 'undefined') {
+			var time = params.value.split(":");
+			TimeH = time[0];
+			TimeM = time[1];
+		}
+
+		// 시간 셀렉트 박스
+		var time = 0;
+		var Hhtml = '';
+		Hhtml += '<select name="' + params.colDef.field + 'H">\n';
+		for (var i = 0; i < 24; i++) {
+			time = i;
+			if ( time < 10)
+				time = '0' + time;
+			if (TimeH != i) {
+				Hhtml += '<option value="' + time + '">' + time + '시</option>\n';
+			} else {
+				Hhtml += '<option selected="selected" value="' + time + '">' + time + '시</option>\n';
+			}
+		}
+		Hhtml += '</select>\n';
+
+		this.eGui.innerHTML = Hhtml;
+		this.eSelectH = this.eGui.querySelector('select');
+		this.eSelectH.innerHTML = Hhtml;
+
+		// 분 셀렉트 박스
+		time = 0;
+		var Mhtml ='';
+		Mhtml = '<select name="' + params.colDef.field + 'M">\n';
+		for (var i = 0; i < 60; i++) {
+			time = i;
+			if ( time < 10)
+				time = '0' + time;
+			if (TimeM != i) {
+				Mhtml += '<option value="' + time + '">' + time + '분</option>\n';
+			} else {
+				Mhtml += '<option selected="selected" value="' + time + '">' + time + '분</option>\n';
+			}
+		}
+		Mhtml += '</select>';
+
+		this.eGui.innerHTML = Mhtml;
+		this.eSelectM = this.eGui.querySelector('select');
+		this.eSelectM.innerHTML = Mhtml;
+		this.eGui.innerHTML = Hhtml + Mhtml; // html 전체가 있어야 하기 떄문에 마지막에 전체 입력
+
+		if (params.required) {
+			this.eSelectH.required = 'required';
+			this.eSelectM.required = 'required';
+//			this.eInput.classList.add('required');
+		}
+	};
+
+	// gets called once when grid ready to insert the element
+	DateTimer.prototype.getGui = function() {
+		return this.eGui;
+	};
+
+	// focus and select can be done after the gui is attached
+	DateTimer.prototype.afterGuiAttached = function() {
+		this.eSelectH.focus();
+	};
+
+	// returns the new value after editing
+	DateTimer.prototype.getValue = function() {
+		return $('[name=' + this.eSelectH.name + '] option:selected').val() + ':' + $('[name=' + this.eSelectM.name + '] option:selected').val() + ':' + this.sec ;
+	};
+
+	// any cleanup we need to be done here
+	DateTimer.prototype.destroy = function() {
+		// but this example is simple, no cleanup, we could
+		// even leave this method out as it's optional
+	};
+
+	// if true, then this editor will appear in a popup
+	DateTimer.prototype.isPopup = function() {
+		// and we could leave this method out also, false is the default
+		return false;
+	};
+
+	return DateTimer;
+}
+
+/**
+ * Numeric editor component
+ * <pre>
+ *     cellEditor: 'numericCellEditor'
+ * </pre>
+ * @author gagamel
+ * @since 2019. 4. 25
+ */
+var getNumericCellEditor = function() {
+	function isCharNumeric(charStr) {
+		return !!/\d/.test(charStr);
+	}
+
+	function getCharCodeFromEvent(event) {
+		event = event || window.event;
+		return (typeof event.which == "undefined") ? event.keyCode : event.which;
+	}
+
+	function isKeyPressedNumeric(event) {
+		var charCode = getCharCodeFromEvent(event);
+		var charStr = String.fromCharCode(charCode);
+		return isCharNumeric(charStr);
+	}
+
+	// function to act as a class
+	function NumericCellEditor() {};
+
+	// gets called once before the renderer is used
+	NumericCellEditor.prototype.init = function(params) {
+		// create the cell
+		this.eInput = document.createElement('input');
+
+		if (isCharNumeric(params.charPress)) {
+			this.eInput.value = params.charPress;
+		} else {
+			if (!gagajf.isNull(params.value)) {
+				this.eInput.value = params.value;
+			}
+		}
+
+		var that = this;
+		this.eInput.addEventListener('keypress', function(event) {
+			if (!isKeyPressedNumeric(event)) {
+				that.eInput.focus();
+				if (event.preventDefault) event.preventDefault();
+			//} else if (that.isKeyPressedNavigation(event)) {
+			} else if (isNavigationKey(event)) {
+				event.stopPropagation();
+			}
+		});
+
+		// only start edit if key pressed is a number, not a letter
+		var charPressIsNotANumber = params.charPress && ('1234567890'.indexOf(params.charPress) < 0);
+		this.cancelBeforeStart = charPressIsNotANumber;
+	};
+
+//	NumericCellEditor.prototype.isKeyPressedNavigation = function(event) {
+//		return event.keyCode === 39 || event.keyCode === 37;
+//	};
+
+	// gets called once when grid ready to insert the element
+	NumericCellEditor.prototype.getGui = function() {
+		return this.eInput;
+	};
+
+	// focus and select can be done after the gui is attached
+	NumericCellEditor.prototype.afterGuiAttached = function() {
+		this.eInput.focus();
+	};
+
+	// returns the new value after editing
+	NumericCellEditor.prototype.isCancelBeforeStart = function() {
+		return this.cancelBeforeStart;
+	};
+
+	// example - will reject the number if it contains the value 007
+	// - not very practical, but demonstrates the method.
+	NumericCellEditor.prototype.isCancelAfterEnd = function() {
+		var value = this.getValue();
+		return value.indexOf('007') > -1;
+	};
+
+	// returns the new value after editing
+	NumericCellEditor.prototype.getValue = function() {
+		return this.eInput.value;
+	};
+
+	// any cleanup we need to be done here
+	NumericCellEditor.prototype.destroy = function() {
+		// but this example is simple, no cleanup, we could  even leave this method out as it's optional
+	};
+
+	// if true, then this editor will appear in a popup
+	NumericCellEditor.prototype.isPopup = function() {
+		// and we could leave this method out also, false is the default
+		return false;
+	};
+
+	return NumericCellEditor;
+}
+
+/**
+ * Cell renderer component for checkbox with value (Y/N)
+ * <pre>
+ *     cellRenderer: 'booleanCellRenderer'
+ * </pre>
+ * @author gagamel
+ * @since 2019. 4. 8
+ */
+var getBooleanCellRenderer = function() {
+
+	// function to act as a class
+	function BooleanCellRenderer() {};
+
+	// gets called once before the renderer is used
+	BooleanCellRenderer.prototype.init = function(params) {
+		this.eGui = document.createElement('span');
+
+		if (!gagajf.isNull(params.value)) {
+			var checked = (params.value == "Y" || params.value == "1") ? "checked" : "";
+			var input = document.createElement('input');
+			input.type = "checkbox";
+			input.checked = checked;
+			input.value = params.value;
+			input.addEventListener('click', function (event) {
+				if (params.value == "Y") params.value = "N";
+				else if (params.value == "N") params.value = "Y";
+				else if (params.value == "1") params.value = "0";
+				else if (params.value == "0") params.value = "1";
+
+				// checked input value has changed, perform your update here
+				params.data[params.colDef.field] = params.value;
+				params.data.crud = "U";
+				params.api.updateRowData({update: [params.data]});
+			});
+			this.eGui.innerHTML = '';
+			this.eGui.appendChild(input);
+		}
+	};
+
+	// gets called once when grid ready to insert the element
+	BooleanCellRenderer.prototype.getGui = function() {
+		return this.eGui;
+	};
+
+	// focus and select can be done after the gui is attached
+	BooleanCellRenderer.prototype.afterGuiAttached = function() {
+
+	};
+
+	// returns the new value after editing
+	BooleanCellRenderer.prototype.getValue = function() {
+		return this.eGui.value;
+	};
+
+	// any cleanup we need to be done here
+	BooleanCellRenderer.prototype.destroy = function() {
+		// but this example is simple, no cleanup, we could
+		// even leave this method out as it's optional
+	};
+
+	// if true, then this editor will appear in a popup
+	BooleanCellRenderer.prototype.isPopup = function() {
+		// and we could leave this method out also, false is the default
+		return false;
+	};
+
+	return BooleanCellRenderer;
+}
+
+/**
+ * Checkbox editor component with value (Y/N)
+ * <pre>
+ *     cellEditor: 'booleanCellEditor'
+ * </pre>
+ * @author gagamel
+ * @since 2019. 4. 8
+ */
+var getBooleanCellEditor = function() {
+
+	// function to act as a class
+	function BooleanCellEditor() {};
+
+	// gets called once before the renderer is used
+	BooleanCellEditor.prototype.init = function(params) {
+		this.container = document.createElement('div');
+		this.value = params.value;
+		params.stopEditing();
+	};
+
+	// gets called once when grid ready to insert the element
+	BooleanCellEditor.prototype.getGui = function() {
+		return this.container;
+	};
+
+	// focus and select can be done after the gui is attached
+	BooleanCellEditor.prototype.afterGuiAttached = function() {
+
+	};
+
+	BooleanCellEditor.prototype.getValue = function() {
+		return this.value;
+	};
+
+	// any cleanup we need to be done here
+	BooleanCellEditor.prototype.destroy = function() {
+
+	};
+
+	// if true, then this editor will appear in a popup
+	BooleanCellEditor.prototype.isPopup = function() {
+		return true;
+	};
+
+	return BooleanCellEditor;
+}
+
+///**
+// * Cell renderer component for selectbox
+// * <pre>
+// *     cellRenderer: 'selectCellRenderer'
+// * </pre>
+// * @param comboList - Combo List
+// * @author gagamel
+// * @since 2019. 4. 29
+// */
+//var getSelectCellRenderer = function(comboList) {
+//
+//	// function to act as a class
+//	function SelectCellRenderer() {};
+//
+//	// gets called once before the renderer is used
+//	SelectCellRenderer.prototype.init = function(params) {
+//		this.eGui = document.createElement('span');
+//		var select = document.createElement('select');
+//
+//		console.log('========getSelectCellRenderer========');
+//		console.log(comboList);
+//
+//		var options;
+//		var comboLen = comboList.length;
+//		for (var i = 0; i < comboLen; i++) {
+//			options += '<option value="' + comboList[i].cd + '">' + comboList[i].cdNm + '</option>';
+//		}
+//
+//		console.log(options);
+//		select.appendChild(options);
+//
+//		//input.value = params.value;
+//		this.eGui.innerHTML = '';
+//		this.eGui.appendChild(select);
+//	};
+//
+//	// gets called once when grid ready to insert the element
+//	SelectCellRenderer.prototype.getGui = function() {
+//		return this.eGui;
+//	};
+//
+//	// focus and select can be done after the gui is attached
+//	SelectCellRenderer.prototype.afterGuiAttached = function() {
+//
+//	};
+//
+//	// returns the new value after editing
+//	SelectCellRenderer.prototype.getValue = function() {
+//		return this.eGui.value;
+//	};
+//
+//	// any cleanup we need to be done here
+//	SelectCellRenderer.prototype.destroy = function() {
+//		// but this example is simple, no cleanup, we could
+//		// even leave this method out as it's optional
+//	};
+//
+//	// if true, then this editor will appear in a popup
+//	SelectCellRenderer.prototype.isPopup = function() {
+//		// and we could leave this method out also, false is the default
+//		return false;
+//	};
+//
+//	return SelectCellRenderer;
+//}
+//
+///**
+// * Selectbox editor component
+// * <pre>
+// *     cellEditor: 'selectCellEditor'
+// * </pre>
+// * @author gagamel
+// * @since 2019. 4. 29
+// */
+//var getSelectCellEditor = function() {
+//	// function to act as a class
+//	function SelectCellEditor() {};
+//
+//	// gets called once before the renderer is used
+//	SelectCellEditor.prototype.init = function(params) {
+//		// create the cell
+//		this.eInput = document.createElement('select');
+//		this.eInput.value = params.value;
+//
+//		var that = this;
+//		this.eInput.addEventListener('change', function(event) {
+//			console.log('SelectCellEditor change');
+////			that.eInput.focus();
+////			if (event.preventDefault) event.preventDefault();
+//			event.stopPropagation();
+//		});
+//	};
+//
+//	// gets called once when grid ready to insert the element
+//	SelectCellEditor.prototype.getGui = function() {
+//		console.log('SelectCellEditor getGui');
+//		return this.eInput;
+//	};
+//
+//	// focus and select can be done after the gui is attached
+//	SelectCellEditor.prototype.afterGuiAttached = function() {
+//		console.log('SelectCellEditor afterGuiAttached');
+//		this.eInput.focus();
+//	};
+//
+//	// returns the new value after editing
+//	SelectCellEditor.prototype.isCancelBeforeStart = function() {
+//		console.log('SelectCellEditor isCancelBeforeStart');
+//		//return this.cancelBeforeStart;
+//	};
+//
+//	// not very practical, but demonstrates the method.
+//	SelectCellEditor.prototype.isCancelAfterEnd = function() {
+//		console.log('SelectCellEditor isCancelAfterEnd');
+//	};
+//
+//	// returns the new value after editing
+//	SelectCellEditor.prototype.getValue = function() {
+//		console.log('SelectCellEditor getValue');
+//		return this.eInput.value;
+//	};
+//
+//	// any cleanup we need to be done here
+//	SelectCellEditor.prototype.destroy = function() {
+//		console.log('SelectCellEditor destroy');
+//		// but this example is simple, no cleanup, we could  even leave this method out as it's optional
+//	};
+//
+//	// if true, then this editor will appear in a popup
+//	SelectCellEditor.prototype.isPopup = function() {
+//		console.log('SelectCellEditor isPopup');
+//		// and we could leave this method out also, false is the default
+//		return false;
+//	};
+//
+//	return SelectCellEditor;
+//}
+
+/**
+ * Text Tooltip component
+ * <pre>
+ *     textTooltip: TextTooltip
+ * </pre>
+ * @author gagamel
+ * @since 2019. 4. 29
+ */
+var getTextTooltip = function() {
+
+	// function to act as a class
+	function TextTooltip() {};
+
+	TextTooltip.prototype.init = function(params) {
+		var eGui = this.eGui = document.createElement('div');
+		var color = params.color || 'white';
+
+		eGui.classList.add('custom-tooltip');
+		eGui.style['background-color'] = color;
+		/*var data = params.api.getRowNode(params.rowIndex).data;
+		eGui.innerHTML =
+			'<p><span class"name">' + data.athlete + '</span></p>' +
+			'<p><span>Country: </span>' + data.country + '</p>' +
+			'<p><span>Total: </span>' + data.total + '</p>';*/
+
+		eGui.innerHTML = '<p><span>' + params.value + '</span>';
+	};
+
+	TextTooltip.prototype.getGui = function() {
+		return this.eGui;
+	};
+
+	return TextTooltip;
+}
+
+
+/**
+ * Text cell editor component
+ * <pre>
+ *     cellEditor: 'textCellEditor'
+ * </pre>
+ * @author gagamel
+ * @since 2019. 4. 30
+ */
+var getTextCellEditor = function() {
+
+	// function to act as a class
+	function TextCellEditor() {};
+
+	TextCellEditor.prototype.init = function(params) {
+		this.eGui = document.createElement('div');
+		this.eGui.innerHTML = '<input'
+			+ ' name="' + params.colDef.field + '"'
+			+ (!gagajf.isNull(params.value) ? ' value="' + params.value + '"' : '')
+			+ (!gagajf.isNull(params.maxlength) ? ' maxlength="' + params.maxlength + '"' : '')
+			+ (!gagajf.isNull(params.validType) ? ' data-valid-type="' + params.validType + '"' : '')
+			+ (!gagajf.isNull(params.onblur) ? ' onblur="' + params.onblur + '"' : '')
+			+ ' style="width: 100%"/>';
+
+		this.eInput = this.eGui.querySelector('input');
+
+		if (params.required) {
+			this.eInput.required = 'required';
+//			this.eInput.classList.add('required');
+		}
+
+//		this.eInput.addEventListener('input', this.inputChanged.bind(this));
+
+//		var that = this;
+//		this.eInput.addEventListener('keypress', function(event) {
+//			if (!isKeyPressedNumeric(event)) {
+//				that.eInput.focus();
+//				if (event.preventDefault) event.preventDefault();
+//			//} else if (that.isKeyPressedNavigation(event)) {
+//			} else if (isNavigationKey(event)) {
+//				event.stopPropagation();
+//			}
+//		});
+
+//		this.eInput.addEventListener('blur', function(event) {
+//			if (params.required) {
+//				if (gagajf.isNull(event.target.value)) {
+//					alert(params.colDef.headerName + ' 을(를) 입력해 주세요.');
+//					that.eInput.focus();
+//					if (event.preventDefault) event.preventDefault();
+//					event.stopPropagation();
+//					return false;
+//				}
+//			}
+//		});
+	}
+
+//	TextCellEditor.prototype.inputChanged = function(event) {
+//		console.log('TextCellEditor.prototype.inputChanged');
+//		console.log(event);
+//
+//		const val = event.target.value;
+//
+////		if (!this.isValid(val)) {
+////			this.eInput.classList.add('invalid-cell');
+////		} else {
+////			this.eInput.classList.remove('invalid-cell');
+////		}
+//
+//		if (this.eInput.required === 'required') {
+//			if (gagajf.isNull(val)) {
+//				alert('을(를) 입력해 주세요.');
+//				return false;
+//			}
+//		}
+//
+//		return true;
+//	}
+
+//	TextCellEditor.prototype.isValid = function(value) {
+//		return value.length <= this.maxLength;
+//	}
+
+	TextCellEditor.prototype.getValue = function() {
+		return this.eInput.value;
+	}
+
+	TextCellEditor.prototype.isCancelAfterEnd = function() {
+//		return !this.isValid(this.eInput.value);
+	}
+
+	TextCellEditor.prototype.getGui = function() {
+		return this.eGui;
+	}
+
+	TextCellEditor.prototype.destroy = function() {
+		this.eInput.removeEventListener('input', this.inputChanged);
+	}
+
+	return TextCellEditor;
+}
+
+
+/**
+ * 공통 그리드 util
+ */
+var gagaAgGrid = {
+	defaultBlank : " - ",
+	eGridDiv : "",
+
+	/**
+	 * 기본 Grid Options을 얻는다.
+	 * @param colDefs - Column Definition
+	 */
+	getGridOptions : function(colDefs) {
+		return {
+			// a default column definition with properties that get applied to every column
+			defaultColDef: {
+				sortable: true, // make every column sortable
+				resizable: true, // make every column resizable
+				filter: 'agTextColumnFilter', // make every column use 'text' filter by default
+				tooltipComponent: 'textTooltip',
+				suppressSizeToFit: true // this columns width to be fixed during 'size to fit' operation.
+			},
+
+//			floatingFilter: true, // display filter region
+
+			// if we had column groups, we could provide default group items here
+			defaultColGroupDef: {
+				//marryChildren: true
+			},
+
+			// define a column type (you can define as many as you like)
+			columnTypes: {
+				boolean: {
+					cellClass: 'text-center',
+					/*cellRenderer: function(params) {
+						return '<input type="checkbox" ' + (params.value ? "checked" : "") + '/>';
+					},*/
+					cellRenderer: 'booleanCellRenderer',
+					cellEditor: 'booleanCellEditor'
+				},
+				numeric: {
+					filter: 'agNumberColumnFilter'
+				},
+				date: {
+					filter: 'agDateColumnFilter',
+					filterParams: {
+						comparator: function(filterLocalDateAtMidnight, cellValue) {
+							// Dates are stored as yyyy/MM/dd
+							// We create a Date object for comparison against the filter date
+							var dateParts = cellValue.split('/');
+							var year = Number(dateParts[0]);
+							var month = Number(dateParts[1]) - 1;
+							var day = Number(dateParts[2]);
+							var cellDate = new Date(day, month, year);
+
+							// Now that both parameters are Date objects, we can compare
+							if (cellDate < filterLocalDateAtMidnight) {
+								return -1;
+							} else if (cellDate > filterLocalDateAtMidnight) {
+								return 1;
+							} else {
+								return 0;
+							}
+						}
+					},
+					suppressMenu: true
+				},
+				measure: {
+					// test 김유중
+//					chartDataType: 'series',
+//					cellClass: 'number',
+					valueFormatter: 'numberCellFormatter',
+					cellRenderer: 'agAnimateShowChangeCellRenderer'
+
+				},
+				numberValue: {
+					enableValue: true,
+					aggFunc: 'sum',
+					editable: true,
+//					valueParser: numberParser
+				},
+				nonEditable: {
+					editable: false
+				}
+			},
+
+			columnDefs: colDefs,
+
+			// Columns
+			suppressAutoSize: false, // suppresses auto-sizing columns
+
+			// Selection
+			//rowSelection: 'single', // 'single' or 'multiple'
+			//suppressRowClickSelection: false,
+			enableRangeSelection: true, // enable Range Selection
+
+			// Rendering & Styling
+			animateRows: true, // enable Row Animation
+
+			// Localization
+			localeText: {
+				noRowsToShow: '조회 결과가 없습니다.'
+			},
+
+			// Overlays
+			suppressLoadingOverlay: true, // disables the 'loading' overlay
+			loadingOverlayComponent: 'customLoadingOverlay',
+			loadingOverlayComponentParams: {
+				loadingMessage: 'Loading... one moment please...'
+			},
+
+			// Scrolling
+			//suppressHorizontalScroll: false,
+
+			statusBar: {
+				statusPanels: [
+					{ statusPanel: 'agTotalRowCountComponent', align: 'left' },
+					{ statusPanel: 'agFilteredRowCountComponent' },
+					{ statusPanel: 'agSelectedRowCountComponent' },
+					{ statusPanel: 'agAggregationComponent' }
+				]
+			},
+
+			// 헤더의 열에 aggFunc명(예, sum, avg 등) 표시 제거
+			suppressAggFuncInHeader: true,
+
+			// Grouping Custom Function
+			aggFuncs: {
+				// this overrides the grids built in sum function
+				'sum': this.sum,
+				'avg': this.avg,
+				'ratio': this.ratio
+			},
+
+			onGridReady: function(params) {
+				params.api.sizeColumnsToFit(); // 자동 맞춤
+			},
+
+			// 창 크기 변경 되었을 때 이벤트
+			onGridSizeChanged: function(params) {
+				params.api.sizeColumnsToFit(); // 자동 맞춤
+			},
+
+//			debug: true,
+
+			onCellValueChanged: function(event) {
+				// if cell is editable and changed value
+				if (event.colDef.editable && event.oldValue != event.newValue) {
+					/*var cellClass = event.colDef.cellClass;
+					event.colDef.cellClass = cellClass ? cellClass + ' modified' : 'modified';
+					var row = this.api.getDisplayedRowAtIndex(event.rowIndex);
+					this.api.redrawRows({ rowNodes: [row] });*/
+				}
+			},
+
+			onCellEditingStopped: function(event) {
+				if (event.colDef.editable && event.data.crud != "C") {
+					event.data.crud = "U";
+					//gridOptions.api.updateRowData({update: [event.data]});
+				}
+			},
+
+			// 총합계, 소계 등을 표시하기 위한 row의 스타일
+			getRowStyle: function(params) {
+				if (params.node.rowPinned) {
+					return {'font-weight': 'bold', 'color': '#721c24', 'background': '#f8d7da'};
+				}
+			},
+
+			components:{
+				customLoadingOverlay: getCustomLoadingOverlay(),
+				booleanCellRenderer: getBooleanCellRenderer(),
+				booleanCellEditor: getBooleanCellEditor(),
+				numericCellEditor: getNumericCellEditor(),
+				datePicker: getDatePicker(),
+				dateTimer: getDateTimer(),
+				textTooltip: getTextTooltip(),
+				textCellEditor: getTextCellEditor(),
+				ComboboxCellRenderer : getComboboxCellRenderer(),
+				ComboboxHeaderComponent : getComboboxHeaderComponent()
+			}
+		};
+	},
+
+	/**
+	 * Create a ag-Grid
+	 * @param gridId - ag-Grid ID
+	 * @param gridOptions - ag-Grid options
+	 * @author gagamel
+	 * @since 2019. 4. 8
+	 */
+	createGrid : function (gridId, gridOptions) {
+		eGridDiv = document.querySelector('#' + gridId);
+		if (typeof gridOptions.rowHeight == "undefined") gridOptions.rowHeight = 32;
+		new agGrid.Grid(eGridDiv, gridOptions);
+	},
+
+	/**
+	 * Progress bar
+	 */
+	showProgressbar : function(isLoading) {
+		if (isLoading) {
+			// Button disabled & progressBar creation
+			$('.btn').each(function(idx) { $(this).attr('disabled', true); });
+			var load_AjaxSubmit = '<div id="load_AjaxSubmit" style="'
+				+ 'background: url(/ux/plugins/gaga/loader.gif); border-style: none; background-repeat: no-repeat; '
+				+ 'position: absolute; top: 45%; left: 50%; width: auto; '
+				+ 'z-index: 101; padding: 16px; margin: 5px;'
+				+ '"></div>';
+			$(eGridDiv).append(load_AjaxSubmit);
+		} else {
+			// Button activated & progressBar remove
+			$('.btn').each(function(idx) { $(this).attr('disabled', false); });
+			$('#load_AjaxSubmit').remove();
+		}
+	},
+	
+	/**
+	 * Hide the status bar of bottom
+	 * gagaAgGrid.createGrid() 함수 사용 후 호출한다.
+	 * <pre>
+	 *     gagaAgGrid.createGrid('gridList', gridOptions);
+	 *     gagaAgGrid.hideStatusBar('gridList');
+	 * </pre>
+	 * @param gridId - ag-Grid ID
+	 * @author gagamel
+	 * @since 2021. 1. 14
+	 */
+	hideStatusBar : function(gridId) {
+		$('#' + gridId + ' .ag-status-bar').hide();
+	},
+
+	/**
+	 * Fetch data using json format.
+	 * <pre>
+	 *     var actionUrl = $('#searchForm').prop('action') + '?' + $('#searchForm').serialize();
+	 *     gagaAgGrid.fetch(actionUrl, gridOptions);
+	 * </pre>
+	 * @param actionUrl - request URL. 필수
+	 * @param gridOptions - ag-Grid options. 필수
+	 * @param formId - form ID. option. 옵션
+	 * @param callbackFn - Callback function. 옵션
+	 * @author gagamel
+	 * @since 2019. 4. 8
+	 */
+	fetch : function(actionUrl, gridOptions, formId, callbackFn) {
+//		gridOptions.api.showLoadingOverlay();
+
+		var _this = this;
+
+		if (typeof(formId) == 'undefined' || gagajf.isNull(formId)) { // formId 값이 없으면
+//			fetch(actionUrl).then(function(response) {
+//				return response.json(); // promise 반환
+//			}).then(function(data) {
+//				gridOptions.api.setRowData(data);
+//				_this.showProgressbar(false);
+//			});
+
+			$.ajax({
+				type : 'GET',
+				url : actionUrl,
+				data : null,
+				dataType : 'json',
+				beforeSend : function(xhr, settings) {
+					// Button disabled & progressBar creation
+					_this.showProgressbar(true);
+				},
+				complete : function() {
+					_this.showProgressbar(false);
+				},
+				success : function(data) {
+					try {
+						gridOptions.api.setRowData(data);
+					} catch (e) {
+						console.log(e);
+						mcxDialog.alert('오류로 인해 처리되지 않았습니다.');
+					}
+
+					if (typeof(callbackFn) == "function") {
+						callbackFn.call(this, data);
+					}
+				},
+				error : function(data) {
+					console.log(data);
+					_this.showProgressbar(false);
+					mcxDialog.alert('오류로 인해 처리되지 않았습니다.');
+				}
+			});
+		} else { // formId 값이 있으면
+			// comma(,) 제거
+			gagajf.removeCommaAtNumberFormattedInput(formId);
+			var jsonData = JSON.stringify($(formId).serializeObject());
+
+			$.ajax({
+				type : 'POST',
+				url : actionUrl,
+				data : jsonData,
+				dataType : 'json',
+				beforeSend : function(xhr, settings) {
+					// dataType: "json"일 때
+					xhr.setRequestHeader('Accept', 'application/json');
+					xhr.setRequestHeader('Content-Type', 'application/json');
+
+					// Button disabled & progressBar creation
+					_this.showProgressbar(true);
+				},
+				complete : function() {
+					_this.showProgressbar(false);
+				},
+				success : function(data) {
+					try {
+						gridOptions.api.setRowData(data);
+					} catch (e) {
+						console.log(e);
+						mcxDialog.alert('오류로 인해 처리되지 않았습니다.');
+					}
+
+					if (typeof(callbackFn) == "function") {
+						callbackFn.call(this, data);
+					}
+				},
+				error : function(data) {
+					console.log(data);
+					_this.showProgressbar(false);
+					mcxDialog.alert('오류로 인해 처리되지 않았습니다.');
+				}
+			});
+
+//			var options = {
+//				method: "POST",
+//				headers: { "Content-Type": "application/json; charset=utf-8" },
+//				body: jsonData
+//			};
+//
+//			fetch(actionUrl, options).then(function(response) {
+//				return response.json(); // promise 반환
+//			}).then(function(data) {
+//				gridOptions.api.setRowData(data);
+//				_this.showProgressbar(false);
+//			});
+		}
+
+		// Button activated & progressBar remove
+
+//		gridOptions.api.hideOverlay();
+	},
+
+	/**
+	 * 그리드의 총건수 가져오기
+	 * <pre>
+	 *     gagaAgGrid.getTotalCount(gridOptions);
+	 * </pre>
+	 * @param gridOptions - ag-Grid options
+	 * @author gagamel
+	 * @since 2019. 5. 8
+	 */
+	getTotalCount : function(gridOptions) {
+		return gridOptions.api.getDisplayedRowCount();
+	},
+
+	/**
+	 * 그리드 컬럼을 보이기/감추기
+	 * <pre>
+	 *     gagaAgGrid.showOrHideColumn(gridOptions, "useYn", false);
+	 * </pre>
+	 * @param gridOptions - ag-Grid options
+	 * @param colKey - column ID
+	 * @param isShow - show or hide (true/false)
+	 * @author gagamel
+	 * @since 2019. 4. 8
+	 */
+	showOrHideColumn : function(gridOptions, colKey, isShow) {
+		gridOptions.columnApi.setColumnVisible(colKey, isShow);
+	},
+
+	/**
+	 * 그리드 컬럼의 Header명을 설정
+	 * <pre>
+	 *     gagaAgGrid.setColumnHeaderName(gridOptions, 'M1', '1월');
+	 * </pre>
+	 * @param gridOptions - ag-Grid options
+	 * @param colKey - column ID or column group ID
+	 * @param headerName - header name
+	 * @author gagamel
+	 * @since 2019. 5. 7
+	 */
+	setColumnHeaderName : function(gridOptions, colKey, headerName) {
+		gridOptions.columnApi.getColumn(colKey).colDef.headerName = headerName;
+//		gridOptions.columnApi.resetColumnState();
+	},
+
+	/**
+	 * 그리드 컬럼 그룹의 Header명을 설정
+	 * <pre>
+	 *     gagaAgGrid.setColumnGroupHeaderName(gridOptions, 'M1', '1월');
+	 * </pre>
+	 * @param gridOptions - ag-Grid options
+	 * @param colKey - column ID
+	 * @param headerName - header name
+	 * @author gagamel
+	 * @since 2019. 5. 7
+	 */
+	setColumnGroupHeaderName : function(gridOptions, colKey, headerName) {
+		gridOptions.columnApi.getColumn(colKey).parent.originalColumnGroup.colGroupDef.headerName = headerName;
+//		gridOptions.columnApi.resetColumnState();
+	},
+
+	/**
+	 * 그리드 내에 변경된 데이터를 배열로 반환한다.
+	 * <pre>
+	 *     var changedData = gagaAgGrid.getChangedData(gridOptions);
+	 * </pre>
+	 * @param gridOptions - ag-Grid options
+	 * @author gagamel
+	 * @since 2019. 4. 8
+	 */
+	getChangedData : function(gridOptions) {
+		// Stop editing
+		gridOptions.api.stopEditing();
+
+		var changedData = [];
+
+		gridOptions.api.forEachNode(function(rowNode, index) {
+			if (rowNode.data.crud == "C" || rowNode.data.crud == "U" || rowNode.data.crud == "D") {
+				rowNode.data.index = index;
+				changedData.push(rowNode.data);
+			}
+		});
+
+		return changedData;
+	},
+
+	/**
+	 * 그리드 첫번째 row에 data를 갖는 행을 추가한다.
+	 * <pre>
+	 *     var data = { cdGb: "", cd: "", cdNm: "", cdChar: "40", cdNum: 1, cdDt: today, dispRk: 1, useYn: "Y" };
+	 *     gagaAgGrid.addRowData(gridOptions, data, "cdGb");
+	 * </pre>
+	 * @param gridOptions - ag-Grid options
+	 * @param data - 추가할 데이터
+	 * @param focusedColKey - Column ID to focus
+	 * @author gagamel
+	 * @since 2019. 4. 9
+	 */
+	addRowData : function(gridOptions, data, focusedColKey) {
+		data.crud = "C";
+
+		gridOptions.api.updateRowData({add: [data], addIndex: 0});
+
+		// Focused cell
+		gridOptions.api.setFocusedCell(0, focusedColKey, null);
+
+		// start editing cell
+		//gridOptions.api.startEditing({rowIndex: 0, colKey: focusedColKey});
+	},
+
+	/**
+	 * 그리드 내에 선택된 데이터를 가져온다
+	 * <pre>
+	 *     gagaAgGrid.selectedRowData(gridOptions);
+	 * </pre>
+	 * @param gridOptions - ag-Grid options
+	 * @author gagamel
+	 * @since 2019. 7. 9
+	 */
+	selectedRowData : function(gridOptions) {
+		return gridOptions.api.getSelectedRows();
+	},
+
+	/**
+	 * 그리드 내에 전체 데이터를 가져온다
+	 * <pre>
+	 *     gagaAgGrid.getAllRowData(gridOptions);
+	 * </pre>
+	 * @param gridOptions - ag-Grid options
+	 * @author qkwlstktma
+	 * @since 2019. 7. 25
+	 */
+	getAllRowData : function(gridOptions){
+		var data = [];
+
+		gridOptions.api.forEachNode(function(rowNode, index) {
+			data.push(rowNode.data);
+		});
+
+		return data;
+	},
+
+	/**
+	 * 그리드 내에 선택된 데이터를 삭제하고 배열에 담아 반환한다.
+	 * 실제 삭제되지 않고 해당 행이 보이지 않게 된다.
+	 * <pre>
+	 *     gagaAgGrid.removeRowData(gridOptions);
+	 * </pre>
+	 * @param gridOptions - ag-Grid options
+	 * @param isShow - Row 보이기(true/false. 디폴트는 보이는 것으로). 옵션
+	 * @author gagamel
+	 * @since 2019. 4. 9
+	 */
+	removeRowData : function(gridOptions, isShow) {
+		var selectedData = gridOptions.api.getSelectedRows();
+
+		var removedData = [];
+
+		selectedData.forEach(function(item, index) {
+			if (item.crud == 'C') {
+				gridOptions.api.updateRowData({remove: [item]});
+			} else {
+				removedData.push(item);
+				if (typeof(isShow) != 'undefined' && !isShow) {
+					gridOptions.api.updateRowData({remove: [item]});
+				}
+			}
+		});
+
+		return removedData;
+	},
+
+	/**
+	 * 그리드 내에 고정된(pinned) top 또는 bottom에 데이터 표시
+	 * <pre>
+	 *     var data = {
+	 *         sellDt: '총합계',
+	 *         orderQty: 100, orderAmt: 10000,
+	 *     };
+	 *     gagaAgGrid.setPinnedRowData(gridOptions, data, 'top');
+	 * </pre>
+	 * @param gridOptions - ag-Grid options. 필수
+	 * @param data - 고정된 row에 보일 데이터. 필수
+	 * @param topBottom - top/bottom. 옵션
+	 * @author gagamel
+	 * @since 2019. 9. 7
+	 */
+	setPinnedRowData : function(gridOptions, data, topBottom) {
+		if (typeof(topBottom) == 'undefined' || topBottom == 'top') {
+			gridOptions.api.setPinnedTopRowData([data]);
+		} else {
+			gridOptions.api.setPinnedBottomRowData([data]);
+		}
+	},
+
+	checkRequired : function(gridOptions, validItem) {
+		var isInvalid = true;
+
+		for (var i = 0; i < gridOptions.columnDefs.length; i++) {
+			var column = gridOptions.columnDefs[i];
+
+			if (typeof(column.cellEditorParams) == 'undefined')
+				continue;
+
+			if (typeof(column.cellEditorParams.required) == 'undefined')
+				continue;
+
+			if (!column.cellEditorParams.required)
+				continue;
+
+			if (!gagajf.isNull(validItem[column.field]))
+				continue;
+
+			mcxDialog.alert(column.headerName + '은[는] 필수 입력 항목입니다.');
+
+			// Focused cell
+			gridOptions.api.setFocusedCell(validItem.index, column.field, null);
+
+			isInvalid = false;
+			break;
+		}
+
+		return isInvalid;
+	},
+
+	validation : function(gridOptions, validData) {
+		var isInvalid = true;
+
+		validData.every(function(item, index) {
+			if (gagaAgGrid.checkRequired(gridOptions, item))
+				return true;
+
+			isInvalid = false;
+			return false;
+		});
+
+		return isInvalid;
+	},
+
+	/*// 그리드 값 넣기
+	setRowData : function (gridOptions, rowData) {
+		//$('#'+gridDiv).children().remove();
+		gridOptions.api.setRowData(rowData);
+	},*/
+
+	/**
+	 * mappings(코드목록배열) 중에 명칭만을 배열로 구성해 반환한다.
+	 * <pre>
+	 *     cellEditorParams: { values: gagaAgGrid.extractValues(mappings) }
+	 * </pre>
+	 * @param mappings - 코드목록배열. 필수
+	 *     예) { "10":"항공(AIR)", "20":"해운(OCEAN)", ... }
+	 * @author gagamel
+	 * @since 2019. 4. 7
+	 */
+	extractValues : function(mappings) {
+		return Object.keys(mappings);
+	},
+
+	/**
+	 * mappings(코드목록배열) 중에 키(key)에 메핑되는 값을 반환한다.
+	 * <pre>
+	 *     valueFormatter: function (params) { return gagaAgGrid.lookupValue(mappings, params.value); }
+	 * </pre>
+	 * @param mappings - 코드목록배열. 필수
+	 *     예) { "10":"항공(AIR)", "20":"해운(OCEAN)", ... }
+	 * @param key - 키. 필수
+	 * @author gagamel
+	 * @since 2019. 4. 7
+	 */
+	lookupValue : function(mappings, key) {
+		return mappings[key];
+	},
+
+	/**
+	 * mappings(코드목록배열) 중에 name(콤보박스 option text)에 메핑되는 Key를 반환한다.
+	 * <pre>
+	 *     valueParser: function (params) { return gagaAgGrid.lookupKey(mappings, params.newValue); }
+	 * </pre>
+	 * @param mappings - 코드목록배열. 필수
+	 *     예) { "10":"항공(AIR)", "20":"해운(OCEAN)", ... }
+	 * @param name - 콤보박스 option text. 필수
+	 * @author gagamel
+	 * @since 2019. 4. 7
+	 */
+	lookupKey : function(mappings, name) {
+		for (var key in mappings) {
+			if (mappings.hasOwnProperty(key)) {
+				if (name === mappings[key]) {
+					return key;
+				}
+			}
+		}
+	},
+
+	/**
+	 * value를 yyyy-MM-dd 형식으로 반환
+	 * <pre>
+	 *     cellRenderer: function (params) { return gagaAgGrid.toDateFormat(params.value); }
+	 * </pre>
+	 * @param value - 일자 (yyyyMMdd 형식)
+	 * @author gagamel
+	 * @since 2019. 4. 8
+	 */
+	toDateFormat : function(value) {
+		if (gagajf.isNull(value))
+			return "";
+
+		return value.replaceAll("/", "").replaceAll("-", "").toDate("YYYYMMDD").format("YYYY-MM-DD");
+	},
+
+	/**
+	 * value를 yyyy-MM-dd HH:mm:ss 형식으로 반환
+	 * <pre>
+	 *     cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }
+	 * </pre>
+	 * @param value - 일자 (yyyyMMddHHmmss 형식)
+	 * @param format - 일자표현식
+	 * @author gagamel
+	 * @since 2019. 6. 7
+	 */
+	toDateTimeFormat : function(value, format) {
+		if (gagajf.isNull(value))
+			return "";
+
+		var rst = "";
+
+		if (format == "YYYYMMDD") {
+			rst = value.replace(/[^0-9]/g, "").toDate("YYYYMMDDHHmmss").format("YYYYMMDD");
+		} else if (format == "hh:mm:ss") {
+			rst = value.replace(/[^0-9]/g, "").toDate("YYYYMMDDHHmmss").format("hh:mm:ss");
+		} else if (format == "yyyy-MM-dd") {
+			rst = value.replace(/[^0-9]/g, "").toDate("YYYYMMDDHHmmss").format("YYYY-MM-DD");
+		} else {
+			rst = value.replace(/[^0-9]/g, "").toDate("YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss");
+		}
+
+		return rst;
+	},
+
+	/**
+	 * value가 숫자 타입일 때 comma(,)를 붙여 반환
+	 * <pre>
+	 *     cellRenderer: function (params) { return gagaAgGrid.toAddComma(params.value); }
+	 * </pre>
+	 * @param value - 숫자
+	 * @author gagamel
+	 * @since 2019. 9. 8
+	 */
+	toAddComma : function(value) {
+		if (gagajf.isNull(value) || typeof value != 'number')
+			return '';
+
+		return value.addComma();
+	},
+
+	/**
+	 * value가 실수 타입일 때 comma(,)를 붙여 반환 소수점 n까지 반환
+	 * <pre>
+	 *     cellRenderer: function (params) { return gagaAgGrid.toFixed(params.value, n); }
+	 * </pre>
+	 * @param value - 숫자
+	 * @author ldh
+	 * @since 2019. 12. 10
+	 */
+	toFixed : function(value, n) {
+		if (gagajf.isNull(value) || typeof value != 'number')
+			return '';
+
+		return value.toFixed(n);
+	},
+
+	/*
+	convertTime : function (param){
+		if (!param.value) {
+			return this.defaultBlank;
+		} else {
+			return moment(param.value, "YYYYMMDDHHmmss").format("HH:mm");
+		}
+	},
+
+	convertTimestamp : function(param) { // 8자리 문자열을 날짜로 변환
+		if (!param.value) {
+			return this.defaultBlank;
+		} else {
+			return moment(param.value, "YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss");
+		}
+	},
+
+	formatDate : function(param) { // long 값을 날짜로 변환
+		if (!param.value) {
+			return this.defaultBlank;
+		} else {
+			return moment(parseInt(param.value)).format("YYYY-MM-DD");
+		}
+	},
+
+	formatTimestamp : function(param) { // long 값을 날짜로 변환
+		if (!param.value) {
+			return this.defaultBlank;
+		} else {
+			return moment(parseInt(param.value)).format("YYYY-MM-DD HH:mm:ss");
+		}
+	},
+
+	numberOnly : function(param){
+		if (!param.value || param.value =='')
+			return '';
+
+		if (typeof param.value === 'string') {
+			return param.value.replace(/[^0-9\.]+/g, '');
+		}
+
+		return CommonGrid.formatCurrency(param);
+	},
+
+	formatCurrency : function (param){
+		if (!param.value && param.value != "0") {
+			return this.defaultBlank;
+		}
+
+		return parseInt(param.value).format();
+	},
+
+	formatNumber : function(param){
+		if (!param.value && param.value != "0") {
+			return this.defaultBlank;
+		}
+
+		return numberFormatPoint2Digits(param.value);
+	},
+
+	convertCommon : function(codes, param){
+		if (!codes) return " - ";
+		for (var i = 0; i < codes.length;i++) {
+			if (param.value == codes[i].name) {
+				return codes[i].value;
+			}
+		}
+		return this.defaultBlank;
+	},
+
+	getCode : function(codes, value) {
+		for (var i = 0; i < codes.length; i++) {
+			if (value == codes[i].value) {
+				return codes[i].name;
+			}
+		}
+
+		return "";
+	},
+
+	checkMobile : function(gridOpts){
+		// 모바일 브라우저 가로크기 체크
+		if (document.body.clientWidth < 800) {
+			if (gridOpts){
+				gridOpts.columnDefs.forEach(function(el) {
+					el.pinned = null;
+					if(el.children){
+						el.children.forEach(function(subEl) {
+							subEl.pinned = null;
+						});
+					}
+				});
+			}
+		}
+	},
+
+	makeTopGrid : function(gridMainOpts, rowData, params) {
+		var colsSum = [];
+
+		gridMainOpts.columnDefs.forEach(function(coldefs, index) {
+			if (coldefs.children) {
+				coldefs.children.forEach(function(child, index) {
+					if (child.isSum && child.isSum == true){
+						var temp = {};
+						temp[child.field] = 0;
+						temp["field"] = child.field;
+						colsSum.push(temp);
+					}
+				});
+			} else {
+				if (coldefs.isSum && coldefs.isSum == true) {
+					var temp = {};
+					temp[coldefs.field] = 0;
+					temp["field"] = coldefs.field;
+					colsSum.push(temp);
+				}
+			}
+		});
+
+		rowData.forEach(function(rowvalue, index) {
+			colsSum.forEach(function(value, index) {
+				if (rowvalue[value.field] && !rowvalue[value.field].isNaN)
+					value[value.field] += rowvalue[value.field];
+			});
+		});
+
+		var resultSum = {}
+		colsSum.forEach(function(cValue, index) {
+			resultSum[cValue.field] = cValue[cValue.field];
+		});
+
+		if (params && params.length > 0) {
+			params.forEach(function(value, index) {
+				resultSum[value.field] = value.value;
+			});
+		}
+
+		resultSum['top'] = true;
+
+		var resultArray = [];
+		resultArray[0] = resultSum;
+		gridMainOpts.api.setPinnedTopRowData(resultArray);
+	}*/
+
+	// sum function has no advantage over the built in sum function.
+
+	// it's shown here as it's the simplest form of aggregation and
+	// showing it can be good as a starting point for understanding
+	// how the aggregation functions work.
+	sum : function(values) {
+		var result = 0;
+		values.forEach(function(value) {
+			if (typeof value === 'number') {
+				result += value;
+			}
+		});
+		return result;
+	},
+
+	// Average function
+	avg : function(values) {
+		var result = 0;
+		var cnt = 0;
+		values.forEach(function(value) {
+			if (typeof value === 'number') {
+				result += value;
+				cnt++;
+			}
+		});
+		return gagaAgGrid.toAddComma(Number(gagaAgGrid.toFixed(result / cnt)));
+	},
+
+	// Ratio function
+	ratio : function(values) {
+		var value1 = 0;
+		var value2 = 0;
+
+		values.forEach(function(value) {
+			if (value && value.value1) {
+				value1 += value.value1;
+			}
+			if (value && value.value2) {
+				value2 += value.value2;
+			}
+		});
+
+		return gagaAgGrid.setRatio(value1, value2);
+	},
+
+	/**
+	 * 2개의 값으로 비율값을 계산해 설정한다.
+	 * <pre>
+	 *     valueGetter: function (params) { if (!params.node.group) return gagaAgGrid.setRatio(params.data.payAmt, params.data.sellTagAmt); }
+	 * </pre>
+	 * @param value1 - 분자
+	 * @param value2 - 분모
+	 * @author gagamel
+	 * @since 2020. 5. 11
+	 */
+	setRatio : function(value1, value2) {
+		return {
+			value1: value1,
+			value2: value2,
+			toString: function() {
+				return value1 && value2 ? gagaAgGrid.toFixed(value1 / value2 * 100, 2) : 0;
+			}
+		};
+	},
+
+	// csv 파일 export
+	exportToCsv : function(title, gridOptions) {
+		var params = {
+			skipHeader: false,
+			skipFooters: false,
+			columnGroups: true,
+			skipGroups: false,
+			skipPinnedTop: false,
+			skipPinnedBottom: false,
+			allColumns: true,
+			onlySelected: false,
+			fileName: title + moment().format("YYYYMMDDHHmmss") + '.csv'
+		};
+
+		gridOptions.api.exportDataAsCsv(params);
+	},
+
+	/**
+	 * 그리드 데이터를 엑셀로 내보내기
+	 * <pre>
+	 * 		gagaAgGrid.exportToExcel('주문목록', gridOptionsOrderList, columnKeys);
+	 * 		or
+	 * 		gagaAgGrid.exportToExcel('주문목록', gridOptionsOrderList, columnKeys, true);
+	 * </pre>
+	 * @param title - 엑셀의 타이틀
+	 * @param gridOptions - Grid options
+	 * @param columnKeys - Column keys (출력하고자 하는 칼럼 정의)
+	 * @param isSelected - true/false
+	 * @author gagamel
+	 * @since 2019. 6. 7
+	 */
+	exportToExcel  : function(title, gridOptions, isSelected) {
+		var sColumnKeys = (typeof(columnKeys) == 'undefined') ? [] : columnKeys;
+		var bOnlySelected = (typeof(isSelected) == 'undefined') ? false : isSelected;
+
+		var params = {
+			columnKeys: sColumnKeys,
+			onlySelected: bOnlySelected, // true: Only export selected rows
+			fileName : title + "_" + new Date().format("YYYYMMDDHHmmss"),
+			sheetName: "DATA"/* ,
+			customHeader: title,
+			customFooter: "Copyright(c) 2019 TSIT, All rights reserved." */
+		};
+
+		gridOptions.excelStyles = [
+			{
+				id: 'dateFormat',
+				dataType: 'dateTime',
+				numberFormat: {
+					format: 'YYYY-MM-DD;@'
+				}
+			},
+			{
+				id: 'textFormat',
+				dataType: 'string'
+			}
+		]
+
+		gridOptions.api.exportDataAsExcel(params);
+	},
+
+}
+
+/*var gDefaultTxt = '';
+var gIsDisplayCode = true;
+var ComboboxCellRenderer = function(defaultTxt, isDisplayCode) {
+	if (defaultTxt) gDefaultTxt = defaultTxt;
+	if (isDisplayCode) gIsDisplayCode = isDisplayCode;
+}
+
+ComboboxCellRenderer.prototype.init = function(params) {
+	this.eGui = document.createElement('span');
+	if (!gagajf.isNull(params.value)) {
+		var checked = (params.value == "Y" || params.value == "1") ? "checked" : "";
+		var input = document.createElement('input');
+		input.type = "checkbox";
+		input.checked = checked;
+		input.value = params.value;
+		input.addEventListener('click', function (event) {
+			if (params.value == "Y") params.value = "N";
+			else if (params.value == "N") params.value = "Y";
+			else if (params.value == "1") params.value = "0";
+			else if (params.value == "0") params.value = "1";
+			//checked input value has changed, perform your update here
+			console.log("addEventListener params.value: "+ params.value);
+		});
+		this.eGui.innerHTML = '';
+		this.eGui.appendChild(input);
+	}
+};
+
+ComboboxCellRenderer.prototype.getGui = function() {
+	return this.eGui;
+};*/
+
+// cellRenderer: 'ComboboxCellRenderer'
+var getComboboxCellRenderer = function() {
+	function ComboboxCellRenderer() {};
+
+	ComboboxCellRenderer.prototype.init = function(params) {
+		this.eGui = document.createElement('span');
+		var span1 = document.createElement('span');
+		var span2 = document.createElement('span');
+		var span3 = document.createElement('span');
+		this.eGui.classList = 'cellChecked'
+		span1.classList = 'ag-selection-checkbox';
+		var check = 'params.data.' + params.colDef.field;
+		$(".allChecked").closest('div').each(function(){
+			if($(this).attr('col-id')==params.colDef.field){
+				if($(this).children('.allChecked').children('.ag-selection-checkbox').children('.ag-icon-checkbox-unchecked').hasClass('ag-hidden')){
+					eval(check+' = "Y"');
+				}else if($(this).children('.allChecked').hasClass('uncheckedAll')){
+					eval(check+' = "N"');
+				}
+			}
+		});
+		if(eval(check)=='Y'){
+			span2.classList = 'ag-icon ag-icon-checkbox-checked';
+			span3.classList = 'ag-icon ag-icon-checkbox-unchecked ag-hidden';
+		}else{
+			eval(check+' = "N"');
+			span2.classList = 'ag-icon ag-icon-checkbox-checked ag-hidden';
+			span3.classList = 'ag-icon ag-icon-checkbox-unchecked';
+		}
+		this.eGui.addEventListener('click', function (event) {
+			if(span2.classList.contains('ag-hidden')){
+				eval(check+' = "Y"');
+				span2.classList.remove('ag-hidden');
+				span3.classList.add('ag-hidden');
+				$(".allChecked").closest('div').each(function(){
+					if($(this).attr('col-id')==params.colDef.field){
+						$(this).children('.allChecked').removeClass('uncheckedAll');
+					}
+				});
+			}else{
+				eval(check+' = "N"');
+				span2.classList.add('ag-hidden');
+				span3.classList.remove('ag-hidden');
+				$(".allChecked").closest('div').each(function(){
+					if($(this).attr('col-id')==params.colDef.field){
+						$(this).children('.allChecked').children('.ag-selection-checkbox').children('.ag-icon-checkbox-checked').addClass('ag-hidden');
+						$(this).children('.allChecked').children('.ag-selection-checkbox').children('.ag-icon-checkbox-unchecked').removeClass('ag-hidden');
+					}
+				});
+			}
+		});
+		span1.appendChild(span2);
+		span1.appendChild(span3);
+		this.eGui.innerHTML = '';
+		this.eGui.appendChild(span1);
+	};
+
+	ComboboxCellRenderer.prototype.getGui = function() {
+		return this.eGui;
+	};
+
+	return ComboboxCellRenderer;
+}
+
+// headerComponent : 'ComboboxHeaderComponent'
+var getComboboxHeaderComponent = function() {
+	function ComboboxHeaderComponent() {};
+
+	ComboboxHeaderComponent.prototype.init = function(params) {
+		this.eGui = document.createElement('span');
+		var span1 = document.createElement('span');
+		var span2 = document.createElement('span');
+		var span3 = document.createElement('span');
+		this.eGui.classList = 'allChecked'
+		span1.classList = 'ag-selection-checkbox';
+		span2.classList = 'ag-icon ag-icon-checkbox-checked ag-hidden';
+		span3.classList = 'ag-icon ag-icon-checkbox-unchecked';
+		this.eGui.addEventListener('click', function (event) {
+			if(span2.classList.contains('ag-hidden')){
+				this.classList.remove('uncheckedAll');
+				span2.classList.remove('ag-hidden');
+				span3.classList.add('ag-hidden');
+				$(".ag-icon-checkbox-checked").closest('div').each(function(){
+					if($(this).attr('col-id')==params.column.colId){
+						$(this).children('.cellChecked').children('.ag-selection-checkbox').children('.ag-icon-checkbox-checked').addClass('ag-hidden');
+						$(this).children('.cellChecked').children('.ag-selection-checkbox').children('.ag-icon-checkbox-unchecked').removeClass('ag-hidden');
+						$(this).children('.cellChecked').click();
+					}
+				});
+			}else{
+				this.classList.add('uncheckedAll');
+				span2.classList.add('ag-hidden');
+				span3.classList.remove('ag-hidden');
+				$(".ag-icon-checkbox-checked").closest('div').each(function(){
+					if($(this).attr('col-id')==params.column.colId){
+						$(this).children('.cellChecked').children('.ag-selection-checkbox').children('.ag-icon-checkbox-checked').removeClass('ag-hidden');
+						$(this).children('.cellChecked').children('.ag-selection-checkbox').children('.ag-icon-checkbox-unchecked').addClass('ag-hidden');
+						$(this).children('.cellChecked').click();
+					}
+				});
+			}
+		});
+
+		span1.appendChild(span2);
+		span1.appendChild(span3);
+		span1.append(" "+params.displayName);
+		this.eGui.innerHTML = '';
+		this.eGui.appendChild(span1);
+	};
+
+	ComboboxHeaderComponent.prototype.getGui = function() {
+		return this.eGui;
+	};
+
+	return ComboboxHeaderComponent;
+}

+ 68 - 0
src/main/webapp/ux/plugins/gaga/gaga.alert.js

@@ -0,0 +1,68 @@
+/*
+ * Alert, Confirm Java Script written by gagamel.
+ *
+ * Copyright (c) 2010 gagamel
+ * Dual licensed under GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2019-07-01 $
+ */
+
+var gagaAlert = {
+	obj : {
+		message : '',
+		callback : '',
+		cancelCallback : ''
+	},
+	
+	show : function(type, message) {
+		var liTag = '<ul class="popup modal" data-width="350" style="min-width: 350px;">\n';
+		liTag += '		<li class="mdPopContent">' + message + '</li>\n';
+		liTag += '		<li class="mdPopBtnB aR">\n';
+		liTag += '			<button id="okBtn" type="button" class="btn btn-primary btn-lg" onclick="gagaAlert.ok();">확인</button>\n';
+		
+		if (type == 'confirm') {
+			liTag += '			<button type="button" class="btn btn-dark btn-lg" onclick="gagaAlert.cancel();">취소</button>\n';
+		}
+		
+		liTag += '		</li>\n';
+		liTag += '	</ul>\n';
+
+		if ($('#customAlert').length == 0) {
+			var tag = '<div class="popupWrap bgTrans" id="customAlert" style="z-index:900;">\n';
+			tag += liTag;
+			tag += '</div>';
+			$('body').append(tag);
+		} else {
+			$('#customAlert').append(liTag);
+		}
+		uifnMpopup('customAlert');
+		$("#okBtn").attr("tabindex", -1).focus();
+	},
+	
+	alert : function(message,callback) {
+		this.obj.callback = null;
+		gagaAlert.show('alert', message);
+		this.obj.callback = callback;
+	},
+	
+	confirm : function(message, callback, cancelCallback) {
+		gagaAlert.show('confirm', message);
+		this.obj.callback = callback;
+		this.obj.cancelCallback = cancelCallback;
+	},
+	
+	ok : function() {
+		uifnPopClose('customAlert');
+		if (typeof(this.obj.callback) != undefined && typeof(this.obj.callback) == 'function' && this.obj.callback != null ) {
+			this.obj.callback('abc');
+		}
+	},
+	
+	cancel : function() {
+		uifnPopClose('customAlert');
+		if (typeof(this.obj.cancelCallback) != undefined && typeof(this.obj.cancelCallback) == 'function' && this.obj.cancelCallback != null ) {
+			this.obj.cancelCallback('abc');
+		}
+	}
+	
+}

+ 377 - 0
src/main/webapp/ux/plugins/gaga/gaga.dx5.js

@@ -0,0 +1,377 @@
+/*
+ * dextupload X5(https://www.dextsolution.com) Common Java Script written by gagamel.
+ *
+ * Copyright (c) 2019 gagamel
+ * Dual licensed under GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2019-06-26 $
+ * 
+ * 사용 예)
+ * 		// HTML 태그는 다음과 같은 구조로 되어 있어야 하며
+ * 		// id 명칭은 dextIndex, dext5-container 가 필요하다.
+ * 		<ul class="dexterTable">
+ * 			<li class="dexterNo" id="dx5Index">
+ * 				<div>1</div>
+ * 				<div>2</div>
+ * 			</li>
+ * 			<li>
+ * 				<div id="dext5-container" style="width: 100%; height: 450px;"></div>
+ * 			</li>
+ * 		</ul>
+ * 
+ * 		<button type="button" class="btn btn-base btn-sm" id="btnAddFiles">파일 추가</button>
+ * 		<button type="button" class="btn btn-default btn-sm" onclick="gagaDx5.deleteChooseFile();">선택 삭제</button>
+ * 		<button type="button" class="btn btn-default btn-sm" onclick="gagaDx5.deleteAllFiles();">전체 삭제</button>
+ * 		<button type="button" class="btn btn-info btn-sm" onclick="gagaDx5.moveFile(true);">위로 이동</button>
+ * 		<button type="button" class="btn btn-info btn-sm" onclick="gagaDx5.moveFile(false);">아래로 이동</button>
+ * 		<button type="button" class="btn btn-base btn-sm" onclick="gagaDx5.previewImage();">이미지 보기</button>
+ * 		<button type="button" class="btn btn-base btn-sm" onclick="gagaDx5.uploadFiles();">업로드/수정</button>
+ *
+ * 		// Import할 자바스크립트 파일
+ * 		<script type="text/javascript" src="/dx5/dextuploadx5-configuration.js"></script>
+ *		<script type="text/javascript" src="/dx5/dextuploadx5.js"></script>
+ * 		<script type="text/javascript" src="/ux/plugins/gaga/gaga.dx5.js"></script>
+ * 
+ * 		<script type="text/javascript">
+ * 			// Dextupload X5 생성. 파일추가 버튼 ID는 22라인에 정의된 btnAddFiles과 동일해야 함
+ * 			$(function() { gagaDx5.createDX5("dext5", "btnAddFiles"); });
+ * 
+ * 			// Dextupload X5 생성 후 호출되는 이벤트
+ * 
+ * 			var onDX5Created = function(id) {
+ * 				var actionUrl = '/dextupload/goods/thumbnail/image/save'
+ * 						+ '/' + $('#goodsImgForm input[name=goodsCode]').val()
+ * 						+ '/' + $('#goodsImgForm input[name=goodsColor]').val();
+ * 				gagaDx5.onDX5Created(actionUrl, goodsImgList);
+ * 			}
+ * 
+ * 			// Dextupload X5 에러 시 호출되는 이벤트
+ * 			var onDX5Error = function(id, code, msg) {
+ * 				alert(id + " => " +  code + "\n" + msg);
+ * 			}
+ * 
+ * 			// Dextupload X5 업로드 성공 시 호출되는 이벤트
+ * 			var onDX5UploadCompleted = function(id) {
+ * 				
+ * 			}
+ * 
+ * 			// Dextupload X5 이미지 등록이 완료된 후 호출되는 이벤트
+ * 			var onDX5ItemsAdded = function(id, count) {
+ * 				gagaDx5.resortDX5FileList();
+ * 			}
+ *		</script>
+ */
+
+var gagaDx5 = {
+	dxId : "",
+	previewUrl : "",
+	dxProdGbn : "",
+	obj : {
+		dx5Index : $('#dx5Index'),
+		dx5CardArea: $('#dx5CardArea')
+	},
+	
+	/**
+	 * Create a dx5
+	 * @param dx5Id - dx5 ID
+	 * @param fileAddBtnId - 파일추가버튼 ID
+	 * @author gagamel
+	 * @since 2019. 6. 26
+	 */
+	createDX5 : function(dx5Id, fileAddBtnId, previewDomain, prodGbn) {
+		dxId = dx5Id;
+		previewUrl = previewDomain;
+		dxProdGbn = prodGbn;
+		dx5.create({
+			mode: "multi", id: dx5Id, parentId: dx5Id + "-container",
+			btnFile: fileAddBtnId/*, btnFolder: "btnAddFolder"*/
+		});
+	},
+	
+	/**
+	 * Dextupload X5 생성 후 호출되는 이벤트
+	 * @param dx5Id - dx5 ID
+	 * @param actionUrl - Upload URL
+	 * @author gagamel
+	 * @since 2019. 6. 26
+	 */
+	onDX5Created : function(actionUrl, imgList) {
+		var dx = dx5.get(dxId);
+		
+		dx.setUploadURL(dx5.canonicalize(actionUrl));
+		
+		// 기존 업로드된 파일을 dx5에 그리기
+		$.each(imgList, function(idx, item) {
+			//if (item.extmallImgYn == 'Y'){	//외부몰이미지구분용
+			//	dx.addVirtualFile({ vindex : item.sysImgNm, name : item.sysImgNm, size : 20000});
+			//}else{
+				dx.addVirtualFile({ vindex : item.sysImgNm, name : item.sysImgNm, size : 10000, url : item.sysImgUrl});
+			//}
+		});
+		
+		// 이미지 항목 높이 조절
+		dx.setUIStyle({ itemHeight: parseInt(24, 10) });
+		
+		// 헤더 높이 조절
+		dx.setUIStyle({ headerHeight: parseInt(28, 10) });
+		
+		// 이미지 미리보기 세팅
+		var totCnt = dx.getTotalVirtualFileCount();
+		
+		// DX5 파일 리스트 순번 재정렬
+		gagaDx5.resortDX5FileList();
+		
+		// DX5 이미지 미리보기 영역 리스트 재정렬
+		gagaDx5.drawDX5ImagePreview();
+		
+		// 이미지 미리보기 적용
+		dx.setPreviewEnable(true);
+		
+		// 이미지 미리보기 이벤트로 적용
+		dx.setPreviewMethod(2);
+	},
+	
+	// 파일 선택 삭제
+	deleteChooseFile : function() {
+		var dx = dx5.get(dxId);
+		
+		dx.removeSelected();
+		
+		// DX5 파일 리스트 순번 재정렬
+		gagaDx5.resortDX5FileList();
+		
+		// DX5 이미지 미리보기 영역 리스트 재정렬
+		gagaDx5.drawDX5ImagePreview(dxId);
+	},
+	
+	// 파일 전체 삭제
+	deleteAllFiles : function() {
+		var dx = dx5.get(dxId);
+		
+		dx.removeAll();
+		
+		// DX5 파일 리스트 순번 재정렬
+		gagaDx5.resortDX5FileList();
+		
+		// DX5 이미지 미리보기 영역 리스트 재정렬
+		gagaDx5.drawDX5ImagePreview();
+	},
+	
+	// 파일 위/아래로 이동
+	moveFile : function(isUp) {
+		var dx = dx5.get(dxId);
+		
+		var totCnt = dx.getTotalItemCount();
+		var selCnt = 0;
+		var selIdx = 0;
+		
+		// 2개 이상 선택했는지 확인
+		for (var i = 0; i < totCnt; i++) {
+			if (dx.isSelectedByIndex(i)) {
+				selIdx = i;
+				selCnt++;
+			}
+		}
+		
+		if (selCnt > 1) {
+			alert("파일이 2개 이상 선택되었습니다.");
+			return;
+		}
+		
+		var ti = parseInt(selIdx, 10);
+		
+		if (isNaN(ti)) {
+			alert("파일의 인덱스(순서)가 필요합니다.");
+			return;
+		}
+		
+		if (isUp)
+			dx.moveItemUp(ti);
+		else
+			dx.moveItemDown(ti);
+		
+		// DX5 이미지 미리보기 영역 리스트 재정렬
+		gagaDx5.drawDX5ImagePreview(dxId);
+	},
+	
+	// 이미지 미리보기
+	previewImage : function() {
+		var dx = dx5.get(dxId);
+		
+		var indices = dx.getSelectedIndices();
+		
+		if (indices.length > 1 || indices.length == 0) {
+			alert("이미지 파일을 한 개만 선택해 주세요.");
+			return;
+		}
+		
+		var idx = indices[0];
+		ti = parseInt(idx, 10);
+		
+		if (isNaN(ti)) {
+			alert("파일의 인덱스(순서)가 필요합니다.");
+			return;
+		}
+
+		dx.preview(ti);
+	},
+	
+	// DX5 파일 리스트 순번 재정렬
+	resortDX5FileList : function() {
+		var dx = dx5.get(dxId);
+		
+		this.obj.dx5Index.html('');
+		
+		var totCnt = dx.getTotalItemCount();
+		var tag = "";
+		
+		for (var i = 0; i < totCnt; i++) {
+			if (i < 16) {
+				tag += '<div>' + (i + 1) + '</div>';
+			} else {
+				break;
+			}
+		}
+		
+		this.obj.dx5Index.html(tag);
+	},
+	
+	// DX5 이미지 미리보기 영역 리스트 재정렬
+	drawDX5ImagePreview : function() {
+		var dx = dx5.get(dxId);
+		
+		this.obj.dx5CardArea.html('');
+		
+		var totCnt = dx.getTotalItemCount();
+		
+		var tag = '';
+		
+		for (var i = 0 ; i < totCnt; i++) {
+			var item = dx.getItemByIndex(i);
+			//console.log("dx.item=>" + item);
+			//console.log("dx.item.name=>" + item.name);
+			//console.log("dx.item.extmallImgYn=>" + item.extmallImgYn);
+			if (item.type == "FILE") {
+				continue;
+			}
+			if (dxProdGbn =="goods"){
+				tag += '<div class="imgCard">\n';
+				//if (item.size > 10000){
+				//	tag += '<div class="imgCard"  style="border:2px solid #8597eb">\n';	
+				//}else{
+				//	tag += '<div class="imgCard">\n';
+				//}
+				tag += '	<button type="button" class="cardClose" onclick="gagaDx5.deletePreviewImage(\'' + i + '\');">닫기</button>\n';
+				tag += '	<ul>\n';
+				tag += '		<li>\n';
+				tag += '			<img src="' + previewUrl + '/' + item.url + '/' + item.name + '" width="70" height="70"/>\n';
+				//if (item.size > 10000){
+				//	tag += '			<img src="' + previewUrl + '/upload/goods/type5/' + item.name + '" width="70" height="70"/>\n';
+				//}else{
+				//	tag += '			<img src="' + previewUrl + '/upload/goods/type4/' + item.name + '" width="70" height="70"/>\n';
+				//}
+				tag += '		</li>\n';
+				tag += '		<li>이미지' + (i + 1) + '</li>\n';
+				tag += '	</ul>\n';
+				tag += '	<p>' + item.name + '</p>\n';
+				tag += '</div>\n';
+			//}else if (dxProdGbn =="magazine"){
+			//	tag += '<div class="imgCard">\n';
+			//	tag += '	<button type="button" class="cardClose" onclick="gagaDx5.deletePreviewImage(\'' + i + '\');">닫기</button>\n';
+			//	tag += '	<ul>\n';
+			//	tag += '		<li>\n';
+			//	tag += '			<img src="' + previewUrl + '/upload/michaa/magazine/' + item.name + '" width="70" height="70"/>\n';
+			//	tag += '		</li>\n';
+			//	tag += '		<li>이미지' + (i + 1) + '</li>\n';
+			//	tag += '	</ul>\n';
+			//	tag += '	<p>' + item.name + '</p>\n';
+			//	tag += '</div>\n';
+			}else if (dxProdGbn =="lookbook"){
+				tag += '<div class="imgCard">\n';
+				tag += '	<button type="button" class="cardClose" onclick="gagaDx5.deletePreviewImage(\'' + i + '\');">닫기</button>\n';
+				tag += '	<ul>\n';
+				tag += '		<li>\n';
+				tag += '			<img src="' + previewUrl + '/lookbook/' + item.name + '" width="70" height="70"/>\n';
+				tag += '		</li>\n';
+				tag += '		<li>이미지' + (i + 1) + '</li>\n';
+				tag += '	</ul>\n';
+				tag += '	<p>' + item.name + '</p>\n';
+				tag += '</div>\n';	
+			//}else if (dxProdGbn =="collection"){
+			//	tag += '<div class="imgCard">\n';
+			//	tag += '	<button type="button" class="cardClose" onclick="gagaDx5.deletePreviewImage(\'' + i + '\');">닫기</button>\n';
+			//	tag += '	<ul>\n';
+			//	tag += '		<li>\n';
+			//	tag += '			<img src="' + previewUrl + '/upload/michaa/collection/COLLECTION_AD/' + item.name + '" width="70" height="70"/>\n';
+			//	tag += '		</li>\n';
+			//	tag += '		<li>이미지' + (i + 1) + '</li>\n';
+			//	tag += '	</ul>\n';
+			//	tag += '	<p>' + item.name + '</p>\n';
+			//	tag += '</div>\n';
+			//}else if (dxProdGbn =="mdlookbook"){
+			//	tag += '<div class="imgCard">\n';
+			//	tag += '	<button type="button" class="cardClose" onclick="gagaDx5.deletePreviewImage(\'' + i + '\');">닫기</button>\n';
+			//	tag += '	<ul>\n';
+			//	tag += '		<li>\n';
+			//	tag += '			<img src="' + previewUrl + '/michaa/campaign/md/' + item.name + '" width="70" height="70"/>\n';
+			//	tag += '		</li>\n';
+			//	tag += '		<li>이미지' + (i + 1) + '</li>\n';
+			//	tag += '	</ul>\n';
+			//	tag += '	<p>' + item.name + '</p>\n';
+			//	tag += '</div>\n';		
+			}
+		}
+		
+		this.obj.dx5CardArea.html(tag);
+	},
+	
+	// 파일 업로드
+	uploadFiles : function() {
+		var dx = dx5.get(dxId);
+		
+		var totCnt = dx.getTotalItemCount(); // 모든 항목 개수
+		
+		// 업로드 할 때 필요한  데이터를 보내지 못해 MetaData로 세팅 후 보냄
+		for (var i = 0; i < totCnt; i++) {
+			var item = dx.getItemByIndex(i);
+			if (item.type == "FILE") { // MetaData는 FILE type만 등록 가능
+				dx.setMetaDataByIndex(i, "name", item.name);
+			}
+		}
+		
+		if (dx.hasUploadableItems()) { // 업로드할 파일이 있으면
+			dx.upload("AUTO");
+		} else {
+			gagaDx5.uploadAfterProcess(dxId);
+		}
+	},
+	
+	// 업로드 후처리
+	uploadAfterProcess : function() {
+		var dx = dx5.get(dxId);
+		
+		// Biz단에서 반드시 구현해야 함
+		fnUploadAfterProcess(dxId, dx.getResponses()[0]);
+	},
+	
+	// 미리보기 영역에서 삭제 시
+	deletePreviewImage : function(idx) {
+		var dx = dx5.get(dxId);
+		
+		var ti = parseInt(idx, 10);
+		
+		if (isNaN(ti)) {
+			alert("파일의 인덱스(순서)가 필요합니다.");
+			return;
+		}
+		
+		dx.removeByIndex(ti);
+		
+		// DX5 파일 리스트 순번 재정렬
+		gagaDx5.resortDX5FileList();
+		
+		// DX5 이미지 미리보기 영역 리스트 재정렬
+		gagaDx5.drawDX5ImagePreview();
+	}
+	
+}

+ 73 - 0
src/main/webapp/ux/plugins/gaga/gaga.se2.js

@@ -0,0 +1,73 @@
+/*
+ * Smart Editor Java Script written by gagamel.
+ *
+ * Copyright (c) 2010 gagamel
+ * Dual licensed under GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2019-07-02 $
+ * 
+ * 사용 예)
+ * 		// HTML 태그는 textarea로 구성
+ * 		// id는 gagaSe2.createSmartEditor 함수 호출 시에 넘겨줘야 한다.
+ * 		<textarea name="contentKorWeb" id="contentKorWeb" rows="5" cols="50" style="width: 100%; height: 400px;"></textarea>
+ * 
+ * 		// Import할 자바스크립트 파일
+ * 		<script type="text/javascript" src="/se2/js/service/HuskyEZCreator.js?v=2019070211"></script>
+ *		<script type="text/javascript" src="/ux/plugins/gaga/gaga.se2.js?v=2019070222"></script>
+ *
+ * 		<script type="text/javascript">
+ * 			// Get a SmartEditor2 options
+ * 			var se2Options = gagaSe2.getEditorOptions();
+ * 
+ * 			$(document).ready(function() {
+ * 				// Create a SmartEditor2
+ * 				gagaSe2.createSmartEditor(se2Options, 'contentKorWeb');
+ * 			});
+ * 		</script>
+ */
+
+var gagaSe2 = {
+	obj : {
+		oEditors : []
+	},
+	
+	/**
+	 * Get a Smart Editor options
+	 */
+	getEditorOptions : function() {
+		return {
+				oAppRef: this.obj.oEditors,
+				sSkinURI: '/se2/SmartEditor2Skin.html',
+				htParams : {
+					bUseToolbar : true, // 툴바사용
+					bUseVerticalResizer : true, // 입력창크기조절바사용
+					bUseModeChanger : true, // 모드탭(Editor|HTML|TEXT)사용
+//					bSkipXssFilter : true, // client-side xss filter 무시
+//					aAdditionalFontList : aAdditionalFontSet, // 추가 글꼴 목록
+					fOnBeforeUnload : function() {
+					}
+				},
+				fCreator: 'createSEditor2'
+		};
+	},
+	
+	/**
+	 * Create a Smart Editor
+	 * @param editorId - 에디터 ID
+	 */
+	createSmartEditor : function(editorOptions, editorId) {
+		editorOptions.elPlaceHolder =editorId;
+		nhn.husky.EZCreator.createInIFrame(editorOptions);
+	},
+	
+	/**
+	 * 스마트에디터에 입력한 내용이 editorId로 지정된 textarea에 설정되고,
+	 * textarea에 설정된 값을 반환한다.
+	 * @param editorId - 에디터 ID
+	 */
+	getContents : function(editorId) {
+		this.obj.oEditors.getById[editorId].exec("UPDATE_CONTENTS_FIELD", []);
+		return document.getElementById(editorId).value;
+	}
+	
+}

+ 129 - 0
src/main/webapp/ux/plugins/gaga/gaga.smarteditor.js

@@ -0,0 +1,129 @@
+/*
+ * Smart Editor Java Script written by gagamel.
+ *
+ * Copyright (c) 2010 gagamel
+ * Dual licensed under GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2019-07-02 $
+ *
+ * 사용 예)
+ * 		// HTML 태그는 textarea로 구성
+ * 		// id는 gagaSe.createSmartEditor 함수 호출 시에 넘겨줘야 한다.
+ * 		<textarea name="contentKorWeb" id="contentKorWeb" rows="5" cols="50" style="width: 100%; height: 400px;"></textarea>
+ *
+ * 		// Import할 자바스크립트 파일
+ * 		<script type="text/javascript" src="/smartEditor/js/HuskyEZCreator.js?v=2019070303" charset="utf-8"></script>
+ * 		<script type="text/javascript" src="/ux/plugins/gaga/gaga.smarteditor.js?v=2019070301"></script>
+ *
+ * 		<script type="text/javascript">
+ * 			// Get a SmartEditor options
+ * 			var seOptions = gagaSe.getEditorOptions();
+ *
+ * 			$(document).ready(function() {
+ * 				// Create a SmartEditor
+ * 				gagaSe.createSmartEditor(seOptions, 'contentKorWeb');
+ * 			});
+ * 		</script>
+ */
+
+var gagaSe = {
+	obj : {
+		oEditors : []
+	},
+
+	/**
+	 * Get a Smart Editor options
+	 */
+	getEditorOptions : function() {
+		return {
+				oAppRef: this.obj.oEditors,
+				sSkinURI: '/smartEditor/SEditorSkin.html',
+				htParams : {
+					bUseToolbar : true, // 툴바사용
+					bUseVerticalResizer : true, // 입력창크기조절바사용
+					bUseModeChanger : true, // 모드탭(Editor|HTML|TEXT)사용
+					fOnBeforeUnload : function() {
+					}
+				},
+
+				fCreator: 'createSEditorInIFrame'
+		};
+	},
+
+	/**
+	 * Create a Smart Editor
+	 * @param editorId - 에디터 ID
+	 */
+	createSmartEditor : function(editorOptions, editorId) {
+		editorOptions.elPlaceHolder = editorId;
+		nhn.husky.EZCreator.createInIFrame(editorOptions);
+	},
+
+	/**
+	 * 스마트에디터에 입력한 내용이 editorId로 지정된 textarea에 설정되고,
+	 * textarea에 설정된 값을 반환한다.
+	 * @param editorId - 에디터 ID
+	 */
+	getContents : function(editorId) {
+		this.obj.oEditors.getById[editorId].exec("UPDATE_IR_FIELD", []);
+		return document.getElementById(editorId).value;
+	},
+
+	setContents : function(editorId, content) {
+		var newContent = "";
+		if (content != null) {
+			newContent = content.replaceAll("&lt;", "<").replaceAll("&gt;",">");
+		}
+
+		try {
+			this.obj.oEditors.getById[editorId].exec("SET_IR", [newContent]);
+		} catch(e) {
+			$('#'+editorId).val(newContent);
+		}
+	},
+
+	/**
+	 * @type   : function
+	 * @access : public
+	 * @desc   : 스마트에디터 유효성 검사
+	 * <pre>
+	 *     gagaSe.getContents('contentKorWeb');
+	 *     getContents 를 먼저 해줘야 textarea에 스마트에디터 내용이 들어간다.
+	 *     if (!gagaSe.validationCheck($('#registerwebKorViewYn') , $('#contentKorWeb'))) return;
+	 *     또는
+	 *     if (!gagaSe.validationCheck($('#detailForm input:checkbox[name=webKorViewYn]') , $('#detailForm textarea[name=contentKorWeb]'))) return;
+	 * </pre>
+	 * @param  : elTarget - 국가/디바이스 스마트에디터(필수)
+	 * @param  : elCheck - 국가/디바이스 체크박스(옵션)
+	 * @since  : 2019/08/21
+	 * @author : rladbwnd5
+	 */
+	validationCheck : function(elTarget, elCheck) {
+		if (elTarget.prop('tagName') != 'TEXTAREA') {
+			mcxDialog.alert('파라메터 입력 오류');
+			console.log('입력하신 파라메터는 ' + elTarget.prop('tagName') + '입니다. textarea를  입력하세요.');
+			return false;
+		}
+
+		var checkStr = $(elTarget).val().replaceAll("<p>", "").replaceAll("</p>","").replaceAll("<br>","").replaceAll("<span style=\"white-space:pre\">","").replaceAll("&nbsp;","").replaceAll("</span>","");
+		var checked = $(elCheck).is(":checked") ? true : false;
+
+		if (checked) {
+			if (gagajf.isNull(checkStr)) {
+				var TargetNm = $(elCheck)[0].labels[0].textContent;
+				mcxDialog.alert(TargetNm + '에 체크된 내용을 입력하세요.');
+				return false;
+			}
+		} else {
+			if (elCheck == null) {
+				if (gagajf.isNull(checkStr)) {
+					mcxDialog.alert('내용을 입력하세요.');
+					return false;
+				}
+			}
+		}
+
+		return true;
+	}
+
+}

+ 110 - 0
src/main/webapp/ux/plugins/gaga/gaga.summernote.js

@@ -0,0 +1,110 @@
+/*
+ * Summernote Java Script written by gagamel.
+ *
+ * Copyright (c) 2010 gagamel
+ * Dual licensed under GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2020-10-29 $
+ *
+ * 사용 예)
+ * 		// HTML 태그는 textarea로 구성
+ * 		// id는 gagaSn.summernote 함수 호출 시에 넘겨줘야 한다.
+ * 		<textarea class="textareaR4" name="clauseContent" id="clauseContent"></textarea>
+ *
+ * 		// Import할 자바스크립트 파일
+ * 		<script type="text/javascript" src="/ux/plugins/summernote/summernote.js?v=2020102902"></script>
+ * 		<script type="text/javascript" src="/ux/plugins/gaga/gaga.summernote.js?v=2020102902"></script>
+ *
+ * 		<script type="text/javascript">
+ * 			// Get a summernote options
+ * 			var snOptions = gagaSn.getToolbarOptions();
+ *
+ * 			$(document).ready(function() {
+ * 				// Create a summernote
+ * 				gagaSn.createSummernote(snOptions, '#clauseContent');
+ * 			});
+ * 		</script>
+ */
+
+var gagaSn = {
+	/**
+	 * Get a Toolbar options
+	 * @param type - 유형(default, media: 사진/동영상 업로드)
+	 */
+	getToolbarOptions : function(type) {
+		if (typeof(type) == 'undefined' || type == 'default') {
+			return [
+				['style', ['style']],
+				['Font Style', ['fontname']],
+				['fontsize', ['fontsize']],
+				['height', ['height']],
+				['style', ['bold', 'italic', 'underline','clear']],
+				['font', ['strikethrough', 'superscript', 'subscript']],
+				['color', ['color']],
+				['para', ['ul', 'ol', 'paragraph']],
+				['Insert', ['table']],
+				['Insert', ['link']],
+				['misc', [ 'print']], //  프린트
+				['code', ['fullscreen', 'codeview', 'help']]
+			];
+		} else if (type == 'media') {
+			return [
+				['style', ['style']],
+				['Font Style', ['fontname']],
+				['fontsize', ['fontsize']],
+				['height', ['height']],
+				['style', ['bold', 'italic', 'underline','clear']],
+				['font', ['strikethrough', 'superscript', 'subscript']],
+				['color', ['color']],
+				['para', ['ul', 'ol', 'paragraph']],
+				['Insert', ['table']],
+				['Insert', ['link', 'picture', 'video']],
+				['misc', [ 'print']], // 프린트
+				['code', ['fullscreen', 'codeview', 'help']]
+			];
+		}
+	},
+
+	/**
+	 * Create a summernote
+	 * @param toolbarOptions - 툴바옵션
+	 * @param editorId - 에디터 ID
+	 * @param editorHeight - 에디터 height
+	 */
+	createSummernote : function(toolbarOptions, editorId, editorHeight) {
+		if (typeof(editorHeight) == 'undefined') editorHeight = 300;
+		
+		$(editorId).summernote({
+			disableDragAndDrop: true, //drag&drop 사용안함
+			placeholder: '내용을 입력하세요',
+			height: editorHeight, //에디터 기본 높이
+			lang : 'ko-KR', //기본 언어 인코딩
+			fontNames: ['Malgun Gothic', 'HY견고딕', 'Helvetica', 'Verdana', 'Arial', 'Arial Black'], //폰트 스타일
+			fontNamesIgnoreCheck: ['Malgun Gothic'], //기본폰트 스타일
+			focus: false, //로드시 에디터창에 포커싱
+			fontSizes: ['8','9','10','11','12','13','14','15','16','17','18','19','20','24','30','36'],
+			toolbar: toolbarOptions,
+			callbacks: {
+				onImageUpload: function(files, editor, welEditable) { //이미지 업로드
+					for (var i = files.length - 1; i >= 0; i--) {
+						sendFile(files[i], this);
+					}
+				}
+			}
+		});
+	},
+
+	/**
+	 * Set value to summernote
+	 */
+	setContents : function(editorId, content) {
+		var content = content.replaceAll("&lt;", "<").replaceAll("&gt;",">");
+
+		try {
+			$(editorId).summernote('code', content);
+		} catch(e) {
+			// Do nothing
+		}
+	}
+
+}

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 0
src/main/webapp/ux/plugins/jquery/jquery-1.12.4.min.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 5 - 0
src/main/webapp/ux/plugins/jquery/jquery-ui.min.js


+ 1 - 0
src/main/webapp/ux/plugins/jquery/jquery.serializeObject.min.js

@@ -0,0 +1 @@
+$.fn.serializeObject=function(){"use strict";var a={},b=function(b,c){var d=a[c.name];"undefined"!=typeof d&&d!==null?$.isArray(d)?d.push(c.value):a[c.name]=[d,c.value]:a[c.name]=c.value};return $.each(this.serializeArray(),b),a};

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio