Ver Fonte

Merge branch 'develop' into eskim

eskim há 5 anos atrás
pai
commit
3c99de17be
100 ficheiros alterados com 5637 adições e 66 exclusões
  1. 1 0
      pom.xml
  2. 36 0
      style24.admin/src/main/java/com/style24/admin/biz/dao/TsaMarketingDao.java
  3. 22 2
      style24.admin/src/main/java/com/style24/admin/biz/dao/TsaOrderDao.java
  4. 95 0
      style24.admin/src/main/java/com/style24/admin/biz/service/TsaMarketingService.java
  5. 24 2
      style24.admin/src/main/java/com/style24/admin/biz/service/TsaOrderService.java
  6. 96 0
      style24.admin/src/main/java/com/style24/admin/biz/web/TsaMarketingController.java
  7. 48 2
      style24.admin/src/main/java/com/style24/admin/biz/web/TsaOrderController.java
  8. 38 0
      style24.admin/src/main/java/com/style24/persistence/domain/Marketing.java
  9. 21 1
      style24.admin/src/main/java/com/style24/persistence/domain/Order.java
  10. 37 0
      style24.admin/src/main/java/com/style24/persistence/mybatis/shop/TsaMarketing.xml
  11. 135 19
      style24.admin/src/main/java/com/style24/persistence/mybatis/shop/TsaOrder.xml
  12. 1 1
      style24.admin/src/main/resources/log/logback-locp.xml
  13. 221 0
      style24.admin/src/main/webapp/WEB-INF/views/marketing/FreeGoodsPromotionForm.html
  14. 7 7
      style24.admin/src/main/webapp/WEB-INF/views/order/OrderDetailCouponHst.html
  15. 37 19
      style24.admin/src/main/webapp/WEB-INF/views/order/OrderDetailForm.html
  16. 143 0
      style24.admin/src/main/webapp/WEB-INF/views/order/OrderDetailGiftcardHst.html
  17. 41 11
      style24.admin/src/main/webapp/WEB-INF/views/order/OrderDetailPointHst.html
  18. 115 0
      style24.admin/src/main/webapp/WEB-INF/views/order/OrderDetailTmtbHst.html
  19. 13 0
      style24.batch/.classpath
  20. 2 0
      style24.batch/.settings/org.eclipse.wst.common.component
  21. 0 2
      style24.core/src/main/java/com/style24/core/support/env/TscConstants.java
  22. 45 0
      style24.front/.classpath
  23. 42 0
      style24.front/.project
  24. 13 0
      style24.front/.settings/.jsdtscope
  25. 6 0
      style24.front/.settings/org.eclipse.core.resources.prefs
  26. 12 0
      style24.front/.settings/org.eclipse.jdt.core.prefs
  27. 4 0
      style24.front/.settings/org.eclipse.m2e.core.prefs
  28. 10 0
      style24.front/.settings/org.eclipse.wst.common.component
  29. 7 0
      style24.front/.settings/org.eclipse.wst.common.project.facet.core.xml
  30. 1 0
      style24.front/.settings/org.eclipse.wst.jsdt.ui.superType.container
  31. 1 0
      style24.front/.settings/org.eclipse.wst.jsdt.ui.superType.name
  32. 2 0
      style24.front/.settings/org.eclipse.wst.validation.prefs
  33. 2 0
      style24.front/.settings/org.springframework.ide.eclipse.prefs
  34. 111 0
      style24.front/pom.xml
  35. 100 0
      style24.front/src/main/java/com/style24/front/biz/dao/TsfLoginDao.java
  36. 79 0
      style24.front/src/main/java/com/style24/front/biz/dao/TsfNoticeDao.java
  37. 164 0
      style24.front/src/main/java/com/style24/front/biz/service/TsfLoginService.java
  38. 102 0
      style24.front/src/main/java/com/style24/front/biz/service/TsfNoticeService.java
  39. 21 0
      style24.front/src/main/java/com/style24/front/biz/web/TsfCustomerController.java
  40. 134 0
      style24.front/src/main/java/com/style24/front/biz/web/TsfIndexController.java
  41. 48 0
      style24.front/src/main/java/com/style24/front/support/config/TsfMybatisShopConfig.java
  42. 97 0
      style24.front/src/main/java/com/style24/front/support/config/TsfRedisSessionConfig.java
  43. 82 0
      style24.front/src/main/java/com/style24/front/support/config/TsfThymeleafConfig.java
  44. 215 0
      style24.front/src/main/java/com/style24/front/support/config/TsfWebMvcConfig.java
  45. 306 0
      style24.front/src/main/java/com/style24/front/support/controller/TsfBaseController.java
  46. 32 0
      style24.front/src/main/java/com/style24/front/support/controller/TsfCustomErrorController.java
  47. 52 0
      style24.front/src/main/java/com/style24/front/support/env/TsfConstants.java
  48. 22 0
      style24.front/src/main/java/com/style24/front/support/exception/TsfDormantAccountException.java
  49. 22 0
      style24.front/src/main/java/com/style24/front/support/exception/TsfEmailDuplicationException.java
  50. 22 0
      style24.front/src/main/java/com/style24/front/support/exception/TsfLockedAccountException.java
  51. 22 0
      style24.front/src/main/java/com/style24/front/support/exception/TsfSecedeAccountException.java
  52. 22 0
      style24.front/src/main/java/com/style24/front/support/exception/TsfSessionExpiredException.java
  53. 53 0
      style24.front/src/main/java/com/style24/front/support/interceptor/TsfAflinkInterceptor.java
  54. 146 0
      style24.front/src/main/java/com/style24/front/support/interceptor/TsfDefaultInterceptor.java
  55. 63 0
      style24.front/src/main/java/com/style24/front/support/interceptor/TsfGoodsViewInterceptor.java
  56. 61 0
      style24.front/src/main/java/com/style24/front/support/interceptor/TsfLoginCheckInterceptor.java
  57. 97 0
      style24.front/src/main/java/com/style24/front/support/interceptor/TsfRememberMeInterceptor.java
  58. 58 0
      style24.front/src/main/java/com/style24/front/support/interceptor/TsfReturnUrlInterceptor.java
  59. 128 0
      style24.front/src/main/java/com/style24/front/support/security/TsfAuthenticationProvider.java
  60. 86 0
      style24.front/src/main/java/com/style24/front/support/security/TsfLoginDetails.java
  61. 129 0
      style24.front/src/main/java/com/style24/front/support/security/config/TsfSecurityConfig.java
  62. 60 0
      style24.front/src/main/java/com/style24/front/support/security/filter/TsfAuthenticationFilter.java
  63. 67 0
      style24.front/src/main/java/com/style24/front/support/security/handler/TsfLoginFailureHandler.java
  64. 130 0
      style24.front/src/main/java/com/style24/front/support/security/handler/TsfLoginSuccessHandler.java
  65. 52 0
      style24.front/src/main/java/com/style24/front/support/security/handler/TsfLogoutSuccessHandler.java
  66. 82 0
      style24.front/src/main/java/com/style24/front/support/security/handler/TsfRememberMeSuccessHandler.java
  67. 100 0
      style24.front/src/main/java/com/style24/front/support/security/session/TsfSession.java
  68. 53 0
      style24.front/src/main/java/com/style24/front/support/startup/TsfApplication.java
  69. 19 0
      style24.front/src/main/java/com/style24/front/support/startup/TsfServletInitializer.java
  70. 120 0
      style24.front/src/main/java/com/style24/persistence/TsfPageRequest.java
  71. 43 0
      style24.front/src/main/java/com/style24/persistence/domain/Login.java
  72. 43 0
      style24.front/src/main/java/com/style24/persistence/domain/Notice.java
  73. 26 0
      style24.front/src/main/java/com/style24/persistence/domain/PersistentToken.java
  74. 192 0
      style24.front/src/main/java/com/style24/persistence/mybatis/TsfLogin.xml
  75. 265 0
      style24.front/src/main/java/com/style24/persistence/mybatis/TsfNotice.xml
  76. 11 0
      style24.front/src/main/resources/banner.txt
  77. 71 0
      style24.front/src/main/resources/config/application-dev.yml
  78. 72 0
      style24.front/src/main/resources/config/application-locd.yml
  79. 73 0
      style24.front/src/main/resources/config/application-locp.yml
  80. 72 0
      style24.front/src/main/resources/config/application-run.yml
  81. 65 0
      style24.front/src/main/resources/config/application.yml
  82. 87 0
      style24.front/src/main/resources/i18n/messages/message_ko_KR.properties
  83. 25 0
      style24.front/src/main/resources/log/logback-dev.xml
  84. 41 0
      style24.front/src/main/resources/log/logback-locd.xml
  85. 41 0
      style24.front/src/main/resources/log/logback-locp.xml
  86. 39 0
      style24.front/src/main/resources/log/logback-run.xml
  87. 23 0
      style24.front/src/main/resources/persistence/mybatis-shop-config.xml
  88. BIN
      style24.front/src/main/webapp/WEB-INF/lib/gagaframework-web-core-1.7.1-RELEASE.jar
  89. BIN
      style24.front/src/main/webapp/WEB-INF/lib/gagaframework-web-parameter-1.7-RELEASE.jar
  90. BIN
      style24.front/src/main/webapp/WEB-INF/lib/gagaframework-web-rest-1.7-RELEASE.jar
  91. BIN
      style24.front/src/main/webapp/WEB-INF/lib/gagaframework-web-security-1.7.1-RELEASE.jar
  92. BIN
      style24.front/src/main/webapp/WEB-INF/lib/gagaframework-web-util-1.7-RELEASE.jar
  93. 2 0
      style24.front/src/main/webapp/WEB-INF/robots.txt
  94. 11 0
      style24.front/target/classes/banner.txt
  95. BIN
      style24.front/target/classes/com/style24/front/biz/dao/TsfLoginDao.class
  96. BIN
      style24.front/target/classes/com/style24/front/biz/dao/TsfNoticeDao.class
  97. BIN
      style24.front/target/classes/com/style24/front/biz/service/TsfLoginService.class
  98. BIN
      style24.front/target/classes/com/style24/front/biz/service/TsfNoticeService.class
  99. BIN
      style24.front/target/classes/com/style24/front/biz/web/TsfCustomerController.class
  100. BIN
      style24.front/target/classes/com/style24/front/biz/web/TsfIndexController.class

+ 1 - 0
pom.xml

@@ -18,6 +18,7 @@
 		<module>style24.admin</module>
 		<module>style24.scm</module>
 		<module>style24.batch</module>
+		<module>style24.front</module>
 	</modules>
 	
 	<properties>

+ 36 - 0
style24.admin/src/main/java/com/style24/admin/biz/dao/TsaMarketingDao.java

@@ -0,0 +1,36 @@
+package com.style24.admin.biz.dao;
+
+import com.style24.core.support.annotation.ShopDs;
+import com.style24.persistence.domain.Itemkind;
+import com.style24.persistence.domain.Marketing;
+import org.springframework.stereotype.Component;
+
+import java.util.Collection;
+
+/**
+ * 마케팅 Dao
+ *
+ * @author xodud1202
+ * @since 2020. 12. 17
+ */
+@ShopDs
+@Component
+public interface TsaMarketingDao {
+	/* xodud1202 진행 */
+
+	/**
+	 * 사은품 프로모션 리스트
+	 * @param marketing
+	 * @return
+	 * @author xodud1202
+	 * @since 2020. 12. 17
+	 */
+	Collection<Marketing> getFreeGoodsPromotionList(Marketing marketing);
+
+	/* // xodud1202 진행 */
+
+	/* JSM 진행 */
+
+	/* // JSM 진행 */
+
+}

+ 22 - 2
style24.admin/src/main/java/com/style24/admin/biz/dao/TsaOrderDao.java

@@ -290,7 +290,7 @@ public interface TsaOrderDao {
 	Collection<Order> getOrderDetailHistoryList(Order order);
 	
 	/**
-	 * 사용 쿠폰내역 팝업 화면
+	 * 쿠폰사용 내역 팝업 화면
 	 *
 	 * @param Order - 주문 정보
 	 * @return
@@ -300,7 +300,7 @@ public interface TsaOrderDao {
 	Collection<Order> getOrderDiscountCouponList(Order order);
 	
 	/**
-	 * 사용 포인트내역 팝업 화면
+	 * 포인트사용 내역 팝업 화면
 	 *
 	 * @param Order - 주문 정보
 	 * @return
@@ -309,6 +309,26 @@ public interface TsaOrderDao {
 	 */
 	Collection<Order> getOrderDiscountPointList(Order order);
 	
+	/**
+	 * 상품권사용 내역 팝업 화면
+	 *
+	 * @param Order - 주문 정보
+	 * @return
+	 * @author jsh77b
+	 * @since 2020. 12. 16
+	 */
+	Collection<Order> getOrderGiftcardHstList(Order order);
+	
+	/**
+	 * 다다익선적용 내역 팝업 화면
+	 *
+	 * @param Order - 주문 정보
+	 * @return
+	 * @author jsh77b
+	 * @since 2020. 12. 16
+	 */
+	Collection<Order> getOrderTmtbHstList(Order order);
+	
 	
 }
 

+ 95 - 0
style24.admin/src/main/java/com/style24/admin/biz/service/TsaMarketingService.java

@@ -0,0 +1,95 @@
+package com.style24.admin.biz.service;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.gagaframework.excel.GagaExcelUtil;
+import com.gagaframework.excel.env.GagaExcelConstants;
+import com.gagaframework.web.parameter.GagaMap;
+import com.gagaframework.web.util.GagaDateUtil;
+import com.gagaframework.web.util.GagaFileUtil;
+import com.gagaframework.web.util.GagaStringUtil;
+import com.style24.admin.biz.dao.TsaGoodsDao;
+import com.style24.admin.biz.dao.TsaMarketingDao;
+import com.style24.admin.support.env.TsaConstants;
+import com.style24.admin.support.security.session.TsaSession;
+import com.style24.core.biz.thirdparty.NaverLowestPriceApi;
+import com.style24.core.biz.thirdparty.SafetyKoreaApi;
+import com.style24.core.support.message.TscMessageByLocale;
+import com.style24.persistence.domain.*;
+import io.netty.util.internal.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+
+/**
+ *상품관리 Service
+ *
+ * @author xodud1202
+ * @since 2020. 12. 17
+ */
+@Service
+@Slf4j
+public class TsaMarketingService {
+
+	@Autowired
+	private TscMessageByLocale message;
+
+	@Autowired
+	private Environment env;
+
+	@Autowired
+	private TsaMarketingDao marketingDao;
+
+	@Autowired
+	private TsaBusinessService businessService;
+
+	@Autowired
+	private TsaCommonService commonService;
+
+	@Autowired
+	private TsaRendererService rendererService;
+
+	@Autowired
+	private TsaNoticeService noticeService;
+
+	@Autowired
+	private SafetyKoreaApi safetyKoreaApi;
+
+	@Autowired
+	private NaverLowestPriceApi naverLowestPriceApi;
+
+
+	@Autowired
+	private ObjectMapper mapper;
+
+	private static final String NUMBER_PATTERN = "^[0-9]+$";
+
+	private static final String SELF_GOOODS_AFTER = "STY";
+
+	/* xodud1202 진행 */
+
+	/**
+	 * 사은품 프로모션 리스트
+	 * @param marketing
+	 * @return
+	 * @author xodud1202
+	 * @since 2020. 12. 17
+	 */
+	public Collection<Marketing> getFreeGoodsPromotionList(Marketing marketing) {
+		return marketingDao.getFreeGoodsPromotionList(marketing);
+	}
+
+	/* // xodud1202 진행 */
+
+	/* JSM 진행 */
+
+	/* // JSM 진행 */
+
+}

+ 24 - 2
style24.admin/src/main/java/com/style24/admin/biz/service/TsaOrderService.java

@@ -507,7 +507,7 @@ public class TsaOrderService {
 	}
 	
 	/**
-	 * 사용 쿠폰내역 팝업 화면
+	 * 쿠폰사용 내역 팝업 화면
 	 * @param Order
 	 * @return Order
 	 * @author jsh77b
@@ -518,7 +518,7 @@ public class TsaOrderService {
 	}
 	
 	/**
-	 * 사용 포인트내역 팝업 화면
+	 * 포인트사용 내역 팝업 화면
 	 * @param Order
 	 * @return Order
 	 * @author jsh77b
@@ -528,6 +528,28 @@ public class TsaOrderService {
 		return orderDao.getOrderDiscountPointList(order);
 	}
 	
+	/**
+	 * 상품권사용 내역 팝업 화면
+	 * @param Order
+	 * @return Order
+	 * @author jsh77b
+	 * @since 2020. 12. 16
+	 */
+	public Collection<Order> getOrderGiftcardHstList(Order order) {
+		return orderDao.getOrderGiftcardHstList(order);
+	}
+	
+	/**
+	 * 다다익선적용 내역 팝업 화면
+	 * @param Order
+	 * @return Order
+	 * @author jsh77b
+	 * @since 2020. 12. 16
+	 */
+	public Collection<Order> getOrderTmtbHstList(Order order) {
+		return orderDao.getOrderTmtbHstList(order);
+	}
+	
 	
 	
 	

+ 96 - 0
style24.admin/src/main/java/com/style24/admin/biz/web/TsaMarketingController.java

@@ -0,0 +1,96 @@
+package com.style24.admin.biz.web;
+
+import com.gagaframework.web.parameter.GagaMap;
+import com.style24.admin.biz.service.TsaCommonService;
+import com.style24.admin.biz.service.TsaMarketingService;
+import com.style24.admin.biz.service.TsaRendererService;
+import com.style24.admin.biz.service.TsaSystemService;
+import com.style24.admin.support.controller.TsaBaseController;
+import com.style24.admin.support.security.session.TsaSession;
+import com.style24.core.support.message.TscMessageByLocale;
+import com.style24.persistence.TsaPageRequest;
+import com.style24.persistence.domain.Marketing;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.ModelAndView;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 마케팅 Controller
+ * @author xodud1202
+ * @since 2020. 12.16
+ */
+@Controller
+@RequestMapping("/marketing")
+@Slf4j
+public class TsaMarketingController extends TsaBaseController {
+
+	@Autowired
+	private TscMessageByLocale message;
+
+	@Autowired
+	private Environment env;
+
+	@Autowired
+	private TsaMarketingService marketingService;
+
+	@Autowired
+	private TsaRendererService rendererService;
+
+	@Autowired
+	private TsaCommonService commonService;
+
+	@Autowired
+	private TsaSystemService systemService;
+
+	/* xodud1202 진행 */
+	/**
+	 * 사은품 프로모션 관리 화면
+	 * @return
+	 * @author xodud1202
+	 * @since 2020. 12. 16
+	 */
+	@GetMapping("/freeGoods/promotion/form")
+	public ModelAndView freeGoodsPromotionForm() {
+		ModelAndView mav = new ModelAndView();
+		mav.setViewName("marketing/FreeGoodsPromotionForm");
+		return mav;
+	}
+
+	/**
+	 * 사은품 프로모션 목록 조회
+	 *
+	 * @return
+	 * @author xodud1202
+	 * @since 2020. 10. 17
+	 */
+	@PostMapping("/freeGoodsPromotion/list")
+	@ResponseBody
+	public GagaMap getFreeGoodsPromotionList(@RequestBody Marketing marketing) {
+		GagaMap result = new GagaMap();
+
+		List<Marketing> marketingList = (ArrayList<Marketing>) marketingService.getFreeGoodsPromotionList(marketing);
+
+		marketing.setRegNo(TsaSession.getInfo().getUserNo()); // 엑셀조회시 로그인 사용자의 엑셀 상품조회시 사용
+		marketing.setPageable(new TsaPageRequest(marketing.getPageNo() - 1, marketing.getPageSize()));
+		marketing.getPageable().setTotalCount(marketingList.size());
+
+		result.set("pageing", marketing);
+		result.set("freeGoodsList", marketingList);
+
+		return result;
+	}
+
+
+	/* // xodud1202 진행 */
+
+	/* JSM 진행 */
+
+	/* // JSM 진행 */
+}

+ 48 - 2
style24.admin/src/main/java/com/style24/admin/biz/web/TsaOrderController.java

@@ -502,7 +502,7 @@ public class TsaOrderController extends TsaBaseController {
 	}
 	
 	/**
-	 * 사용 쿠폰내역 팝업 화면
+	 * 쿠폰사용 내역 팝업 화면
 	 * 
 	 * @param ordNo - 주문번호
 	 * @return ModelAndView
@@ -525,7 +525,7 @@ public class TsaOrderController extends TsaBaseController {
 	}
 	
 	/**
-	 * 사용 포인트내역 팝업 화면
+	 * 포인트사용 내역 팝업 화면
 	 * 
 	 * @param ordNo - 주문번호
 	 * @return ModelAndView
@@ -546,6 +546,52 @@ public class TsaOrderController extends TsaBaseController {
 		mav.setViewName("order/OrderDetailPointHst");
 		return mav;
 	}
+	
+	/**
+	 * 상품권사용 내역 팝업 화면
+	 * 
+	 * @param ordNo - 주문번호
+	 * @return ModelAndView
+	 * @author jsh77b
+	 * @since 2020. 12. 16
+	 */
+	@GetMapping("/detail/giftcard/hst/form/{ordNo}")
+	@ResponseBody
+	public ModelAndView detailGiftcardHstFrom(@PathVariable(value = "ordNo") int ordNo) {
+
+		Order order = new Order();
+		ModelAndView mav = new ModelAndView();
+
+		order.setOrdNo(ordNo);
+		Collection<Order> orderGiftcardHstList = orderService.getOrderGiftcardHstList(order);
+
+		mav.addObject("orderGiftcardHstList", orderGiftcardHstList);
+		mav.setViewName("order/OrderDetailGiftcardHst");
+		return mav;
+	}
+	
+	/**
+	 * 다다익선적용 내역 팝업 화면
+	 * 
+	 * @param ordNo - 주문번호
+	 * @return ModelAndView
+	 * @author jsh77b
+	 * @since 2020. 12. 16
+	 */
+	@GetMapping("/detail/tmtb/hst/form/{ordNo}")
+	@ResponseBody
+	public ModelAndView detailTmtbHstFrom(@PathVariable(value = "ordNo") int ordNo) {
+
+		Order order = new Order();
+		ModelAndView mav = new ModelAndView();
+
+		order.setOrdNo(ordNo);
+		Collection<Order> orderTmtbHstList = orderService.getOrderTmtbHstList(order);
+
+		mav.addObject("orderTmtbHstList", orderTmtbHstList);
+		mav.setViewName("order/OrderDetailTmtbHst");
+		return mav;
+	}
 }
 
 

+ 38 - 0
style24.admin/src/main/java/com/style24/persistence/domain/Marketing.java

@@ -0,0 +1,38 @@
+package com.style24.persistence.domain;
+
+import com.style24.persistence.TsaPageRequest;
+import com.style24.persistence.TscBaseDomain;
+import lombok.Data;
+
+/**
+ * 품목 Domain
+ *
+ * @author gagamel
+ * @since 2020. 10. 7
+ */
+@SuppressWarnings("serial")
+@Data
+public class Marketing extends TscBaseDomain {
+	// 사은품 프로모션
+	private int freeGiftSq;		// 프로모션ID
+	private String freeGiftName;	// 프로모션명
+	private String freeGiftStat;	// 프로모션 상태
+	private String freeGiftStdt;	// 프로모션 시작일
+	private String freeGiftEddt;	// 프로모션 종료일
+	private String promotionGubun;	// 프로모션 조회 검색 구분
+	private String searchTxt;		// 프로모션 검색 조건
+
+	// Pagination
+	private TsaPageRequest pageable;
+	private int pageNo = 1;
+	private int pageSize = 50;
+	private int pageUnit = 10;
+
+	/* xodud1202 진행 */
+
+	/* // xodud1202 진행 */
+
+	/* JSM 진행 */
+
+	/* // JSM 진행 */
+}

+ 21 - 1
style24.admin/src/main/java/com/style24/persistence/domain/Order.java

@@ -279,7 +279,27 @@ public class Order extends TscBaseDomain {
 	private int dcMval;
 	private int dcAval;
 	
-		
+	// 주문포인트
+	private int pntPrate;
+	private int pntMrate;
+	private int pntAmt;
+	private String occurGb;
+	private String occurGbNm;
+	private String occurDtlDesc;
+	
+	// 주문상품권
+	private String gfcdNm;
+	private String gfcdNo;
+	private int gfcdAmt;
+	private int chgGfcdAmt;
+	private int usGfcdAmt;
+	private int rmGfcdAmt;
+	
+	// 다다익선
+	private int tmtbSq;
+	private String tmtbNm;
+	private int tmtbDcAmt;
+
 }
 
 

+ 37 - 0
style24.admin/src/main/java/com/style24/persistence/mybatis/shop/TsaMarketing.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.style24.admin.biz.dao.TsaMarketingDao">
+
+	<!-- 품목 목록 -->
+	<select id="getFreeGoodsPromotionList" parameterType="Marketing" resultType="Marketing">
+		/* TsaMarketing.getFreeGoodsPromotionList */
+		SELECT FG.FREEGIFT_SQ
+		     , FG.FREEGIFT_NM
+		     , FG.FREEGIFT_STAT
+		     , DATE_FORMAT(FG.FREEGIFT_STDT, '%Y.%m.%d') AS FREEGIFT_STDT
+		     , DATE_FORMAT(FG.FREEGIFT_EDDT, '%Y.%m.%d') AS FREEGIFT_EDDT
+		     , (SELECT USER_NM FROM TB_USER WHERE USER_NO = FG.REG_NO) AS REG_NM
+		     , DATE_FORMAT(FG.REG_DT, '%Y.%m.%d %H:%i:%S') AS REG_DT
+		     , (SELECT USER_NM FROM TB_USER WHERE USER_NO = FG.UPD_NO) AS UPD_NM
+		     , DATE_FORMAT(FG.UPD_DT, '%Y.%m.%d %H:%i:%S') AS UPD_DT
+          FROM TB_FREEGIFT FG
+         WHERE 1=1
+		<if test="searchTxt != null and searchTxt != ''">
+			<if test="promotionGubun != null and promotionGubun == 'freeGiftSq'">
+				AND FG.FREEGIFT_SQ = #{searchTxt}
+			</if>
+			<if test="promotionGubun != null and promotionGubun == 'freeGiftName'">
+				AND FG.FREEGIFT_NAME = #{searchTxt}
+			</if>
+		</if>
+         ORDER BY REG_DT DESC
+	</select>
+
+	<!-- xodud1202 진행 -->
+
+	<!-- // xodud1202 진행 -->
+
+	<!-- JSM 진행 -->
+
+	<!-- // JSM 진행 -->
+</mapper>

+ 135 - 19
style24.admin/src/main/java/com/style24/persistence/mybatis/shop/TsaOrder.xml

@@ -634,22 +634,35 @@
 	<!-- 주문상세 > 배송정보 -->
 	<select id="getDeliveryAddrList" parameterType="Order" resultType="Order">
 		/* order.getDeliveryAddrList */
-		SELECT OD.DELV_ADDR_SQ 
-		     , DA.RECIP_NM 
-		     , DA.RECIP_PHNNO 
-		     , DA.RECIP_TELNO 
-		     , DA.RECIP_ZIP_NO 
-		     , DA.RECIP_BASE_ADDR
-		     , DA.RECIP_DTL_ADDR
-		     , O.ORD_EMAIL
-		     , DA.DELV_MEMO
-		FROM   TB_ORDER_DETAIL OD
-		INNER  JOIN TB_ORDER O
-		ON     OD.ORD_NO = O.ORD_NO
-		INNER  JOIN TB_DELIVERY_ADDR DA
-		ON     OD.DELV_ADDR_SQ  = DA.DELV_ADDR_SQ
-		WHERE  1=1
-		AND    OD.ORD_NO = #{ordNo}
+		SELECT Z.*
+		FROM   (
+			SELECT OD.DELV_ADDR_SQ 
+			     , DA.RECIP_NM 
+			     , DA.RECIP_PHNNO 
+			     , DA.RECIP_TELNO 
+			     , DA.RECIP_ZIP_NO 
+			     , DA.RECIP_BASE_ADDR
+			     , DA.RECIP_DTL_ADDR
+			     , O.ORD_EMAIL
+			     , DA.DELV_MEMO
+			FROM   TB_ORDER_DETAIL OD
+			INNER  JOIN TB_ORDER O
+			ON     OD.ORD_NO = O.ORD_NO
+			INNER  JOIN TB_DELIVERY_ADDR DA
+			ON     OD.DELV_ADDR_SQ  = DA.DELV_ADDR_SQ
+			WHERE  1=1
+			AND    OD.ORD_NO = #{ordNo}
+			GROUP  BY OD.DELV_ADDR_SQ 
+			     , DA.RECIP_NM 
+			     , DA.RECIP_PHNNO 
+			     , DA.RECIP_TELNO 
+			     , DA.RECIP_ZIP_NO 
+			     , DA.RECIP_BASE_ADDR
+			     , DA.RECIP_DTL_ADDR
+			     , O.ORD_EMAIL
+			     , DA.DELV_MEMO
+		) Z
+		ORDER  BY Z.DELV_ADDR_SQ
 	</select>
 	
 	<!-- 주문상세 > 결제정보 -->
@@ -674,6 +687,8 @@
 	<!-- 주문상세 > 배송비정보 -->
 	<select id="getDeliveryFeeList" parameterType="Order" resultType="Order">
 		/* order.getDeliveryFeeList */
+		SELECT Z.*
+		FROM   (
 		SELECT DF.DELV_FEE_GB 
 		     , FN_GET_CODE_NM('G018', DF.DELV_FEE_GB) AS DELV_FEE_GB_NM
 		     , DF.DELV_FEE
@@ -684,6 +699,14 @@
 		FROM   TB_DELIVERY_FEE DF
 		WHERE  1=1
 		AND    DF.ORD_NO = #{ordNo}
+		) Z
+		GROUP  BY Z.DELV_FEE_GB 
+		     , Z.DELV_FEE_GB_NM
+		     , Z.DELV_FEE
+		     , Z.DELV_USAC_YN 
+		     , Z.DELV_USAC_DT 
+		     , Z.SUPPLY_COMP_CD 
+		     , Z.DELV_FEE_SQ
 	</select>
 	
 	<!-- 주문상세 > 취소/반품/교환요청 정보 -->
@@ -759,6 +782,7 @@
 		FROM   TB_COUNSEL CS
 		WHERE  1=1
 		AND    CS.REL_ORD_NO = #{ordNo}
+		ORDER  BY CS.REG_DT DESC
 	</select>
 	
 	<!-- 주문상세 > 관리자메모 -->
@@ -875,6 +899,7 @@
 	<delete id="deleteEntryExcelDownTmp" parameterType="Order">
 		/* TsbOrder.deleteEntryExcelDownTmp */
 		DELETE FROM TB_ENTRY_UPLOAD_EXCEL
+		WHERE  1=1
 	</delete>
 	
 	<!-- 주문상세정보 변경 (입점) -->
@@ -1309,7 +1334,7 @@
 		ORDER  BY ODH.REG_DT DESC
 	</select>
 	
-	<!-- 주문상세 > 사용 쿠폰내역 팝업 화면 -->
+	<!-- 주문상세 > 쿠폰사용내역 팝업 화면 -->
 	<select id="getOrderDiscountCouponList" parameterType="Order" resultType="Order">
 		/* order.getOrderDiscountCouponList */
 		WITH COUPON_DATA AS (
@@ -1389,25 +1414,116 @@
 		ORDER  BY DISP_ORD
 	</select>
 	
-	<!-- 주문상세 > 사용 쿠폰내역 팝업 화면 -->
+	<!-- 주문상세 > 포인트사용내역 -->
 	<select id="getOrderDiscountPointList" parameterType="Order" resultType="Order">
 		/* order.getOrderDiscountPointList */
 		SELECT OD.ORD_NO
 		     , OD.ORD_DTL_NO
 		     , OD.GOODS_CD
+		     , G.PNT_PRATE 
+		     , G.PNT_MRATE 
+		     , OD.CURR_PRICE
+		     , OD.REAL_ORD_AMT 
 		     , CPH.PNT_AMT
 		     , CPH.OCCUR_GB 
 		     , CPH.OCCUR_DTL_DESC 
-		     , CPH.REG_DT
+		     , DATE_FORMAT(CPH.REG_DT,'%Y%m%d%H%i%S') AS REG_DT
 		FROM   TB_ORDER_DETAIL OD
 		INNER  JOIN TB_CUST_POINT_HST CPH
 		ON     OD.ORD_DTL_NO = CPH.ORD_DTL_NO
 		AND    OD.ORD_NO = CPH.ORD_NO
+		INNER  JOIN TB_GOODS G
+		ON     OD.GOODS_CD = G.GOODS_CD
 		WHERE  1=1
 		AND    OD.ORD_NO = #{ordNo}
 		ORDER  BY CPH.REG_DT DESC
 	</select>
 	
+	<!-- 주문상세 > 상품권사용내역 -->
+	<select id="getOrderGiftcardHstList" parameterType="Order" resultType="Order">
+		/* order.getOrderGiftcardHstList */
+		SELECT CG.CUST_GFCD_SQ 
+		     , CG.CHG_GFCD_AMT 
+		     , CG.GFCD_NM 
+		     , CG.GFCD_NO 
+		     , CG.CHG_GFCD_AMT 
+		     , CG.US_GFCD_AMT 
+		     , CG.RM_GFCD_AMT 
+		     , OD.ORD_NO
+		     , OD.ORD_DTL_NO
+		     , OD.GOODS_CD 
+		     , CGH.GFCD_AMT
+		     , CGH.OCCUR_GB 
+		     , CGH.OCCUR_DTL_DESC 
+		     , DATE_FORMAT(CGH.REG_DT,'%Y%m%d%H%i%S') AS REG_DT
+		     , CG.CUST_GFCD_SQ 
+		FROM   TB_ORDER_DETAIL OD
+		INNER  JOIN TB_CUST_GIFTCARD_HST CGH
+		ON     OD.ORD_DTL_NO = CGH.ORD_DTL_NO
+		AND    OD.ORD_NO = CGH.ORD_NO
+		INNER  JOIN TB_CUST_GIFTCARD CG
+		ON     CGH.CUST_GFCD_SQ  = CG.CUST_GFCD_SQ 
+		INNER  JOIN TB_GOODS G
+		ON     OD.GOODS_CD = G.GOODS_CD
+		WHERE  1=1
+		AND    OD.ORD_NO = #{ordNo}
+		ORDER  BY CUST_GFCD_SQ
+		     , OD.ORD_NO
+		     , OD.ORD_DTL_NO
+	</select>
+	
+	
+	<!-- 주문상세 > 다다익선적용내역 -->
+	<select id="getOrderTmtbHstList" parameterType="Order" resultType="Order">
+		/* order.getOrderTmtbHstList */
+		WITH TMTB_DATA AS (
+			SELECT TM1.TMTB_SQ			AS  TMTB1_SQ
+			     , TM1.TMTB_NM			AS  TMTB1_NM
+			     , OD.TMTB1_DC_AMT
+			     , OD.ORD_NO
+			     , OD.ORD_DTL_NO
+			     , OD.REG_NO
+			     , OD.REG_DT
+			     , OD.GOODS_CD
+			     , TM2.TMTB_SQ			AS  TMTB2_SQ
+			     , TM2.TMTB_NM			AS  TMTB2_NM
+			     , OD.TMTB2_DC_AMT
+			FROM   TB_ORDER_DETAIL OD
+			INNER  JOIN TB_TMTB TM1
+			ON     OD.TMTB1_SQ = TM1.TMTB_SQ 
+			INNER  JOIN TB_TMTB TM2
+			ON     OD.TMTB2_SQ = TM2.TMTB_SQ 
+			WHERE  1=1
+			AND    OD.ORD_NO = 9
+		)
+		SELECT Z.*
+		FROM (
+			SELECT 1                    AS DISP_ORD
+			     , TD.TMTB1_SQ        	AS TMTB_SQ
+			     , TD.TMTB1_NM			AS TMTB_NM
+			     , TD.TMTB1_DC_AMT      AS TMTB_DC_AMT
+			     , TD.ORD_NO
+			     , TD.ORD_DTL_NO
+			     , TD.GOODS_CD
+			     , TD.REG_NO
+			     , DATE_FORMAT(TD.REG_DT,'%Y%m%d%H%i%S') AS REG_DT
+			FROM   TMTB_DATA TD
+			UNION  ALL
+			SELECT 2                    AS DISP_ORD
+			     , TD.TMTB2_SQ        	AS TMTB_SQ
+			     , TD.TMTB2_NM			AS TMTB_NM
+			     , TD.TMTB2_DC_AMT      AS TMTB_DC_AMT
+			     , TD.ORD_NO
+			     , TD.ORD_DTL_NO
+			     , TD.GOODS_CD
+			     , TD.REG_NO
+			     , DATE_FORMAT(TD.REG_DT,'%Y%m%d%H%i%S') AS REG_DT
+			FROM   TMTB_DATA TD
+		) Z
+		WHERE  1=1
+		AND    Z.TMTB_SQ IS NOT NULL
+		ORDER  BY Z.DISP_ORD
+	</select>
 	
 	
 </mapper>

+ 1 - 1
style24.admin/src/main/resources/log/logback-locp.xml

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 <configuration scan="true">
-	<property name="LOG_HOME" value="/WIDE/workspace/logs/wivismall/admin"/>
+	<property name="LOG_HOME" value="/WIDE/workspace/logs/style24/admin"/>
 	<property name="LOG_LEVEL" value="DEBUG"/>
 	
 	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">

+ 221 - 0
style24.admin/src/main/webapp/WEB-INF/views/marketing/FreeGoodsPromotionForm.html

@@ -0,0 +1,221 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : FreeGoodsPromotionForm.html
+ * @desc    : 사은품 프로모션 관리 페이지
+ *============================================================================
+ * SISUN
+ * Copyright(C) 2020 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.12.16   xodud1202   최초 작성
+ *******************************************************************************
+ -->
+	<div id="main">
+		<!-- 메인타이틀 영역 -->
+		<div class="main-title">
+		</div>
+		<!-- //메인타이틀 영역 -->
+		
+		<!-- 메뉴 설명 -->
+		<div class="infoBox menu-desc">
+		</div>
+		<form id="searchForm" name="searchForm" action="#" th:action="@{'/marketing/freeGoodsPromotion/list'}">
+			<input type="hidden" id="searchGb" name="searchGb" />
+			<input type="hidden" id="imageViewYn" name="imageViewYn" />
+			<input type="hidden" id="goodsPriceYn" name="goodsPriceYn" value="Y"/> <!-- 즉시할인판매가 조회 -->
+
+				<!-- 패널 영역1 -->
+			<div class="panelStyle" >
+				<!-- 검색조건 영역 -->
+				<!-- TITLE -->
+				<div class="panelTitle">
+					<h3><i class="fa fa-info-circle"></i>아래 검색조건 중 <font color="red">업체, 키워드, 발생일</font>중 하나를 꼭 입력해 주세요.</h3>
+					<span class="panelControl">
+						<i class="fa fa-chevron-up"></i>
+					</span>
+				</div>
+				<!-- //TITLE -->
+				<div class="panelContent">
+					<table class="frmStyle">
+						<colgroup>
+							<col style="width: 7%;"/>
+							<col/>
+						</colgroup>
+
+						<tr>
+							<th>기간</th>
+							<td id="sellTerms"></td>
+						</tr>
+
+						<tr>
+							<th>프로모션 조회</th>
+							<td>
+								<label class="rdoBtn"><input type="radio" name="promotionGubun" id="promotionId" value="freeGiftSq"  checked/>프로모션ID</label>
+								<label class="rdoBtn"><input type="radio" name="promotionGubun" id="promotionName" value="freeGiftName"/>프로모션명</label>
+								<input type="text" class="w900" name="searchTxt" id="searchTxt" />
+							</td>
+						</tr>
+					</table>
+					<ul class="panelBar">
+						<li class="center">
+							<button type="button" class="btn btn-gray btn-lg" id="btnInit" >초기화</button>
+							<button type="button" class="btn btn-info btn-lg" id="btnSearch" >조회</button>
+						</li>
+					</ul>
+				</div>
+				<!-- //검색조건 영역 -->
+			</div>
+
+			<!-- 패널 영역1 -->
+			<div class="panelStyle">
+				<!-- 상단버튼 영역  -->
+				<ul class="panelBar">
+					<li class="right">
+						검색결과 : <strong><span id="gridRowTotalCount">0</span> 건</strong>&nbsp;
+						쪽번호 <span id="pgNo">0</span>/ <strong id="endPgNo">0</strong>&nbsp;&nbsp;
+						<select id="pageSize" name="pageSize">
+							<option value="50" selected="selected">50개씩 보기</option>
+							<option value="100">100개씩 보기</option>
+							<option value="500">500개씩 보기</option>
+							<option value="1000">1000개씩 보기</option>
+						</select>
+						<input type="hidden" name="pageNo" id="pageNo" value ="1"/>
+					</li>
+				</ul>
+
+				<!-- 검색결과 영역 -->
+				<div id="gridList" style="width: 100%; height: 700px;" class="ag-theme-balham lh60"></div>
+			</div>
+		</form>
+<script type="text/javascript" src="/ux/plugins/gaga/gaga.paging.js?v=2019072202"></script>
+<script th:inline="javascript">
+/*<![CDATA[*/
+	var columnDefs = [];
+	columnDefs = [
+		{headerName: "프로모션ID", field: "freeGiftSq", width: 80, cellClass: 'text-center'},
+		{headerName: "프로모션명", field: "freeGiftName", width: 130, cellClass: 'text-center'},
+		{headerName: "상태", field: "freeGiftStat", width: 140, cellClass: 'text-center'},
+		{headerName: "시작일", field: "freeGiftStdt", width: 140, cellClass: 'text-center'},
+		{headerName: "종료일", field: "freeGiftEddt", width: 140, cellClass: 'text-center'},
+		{headerName: "등록자", field: "regNm", width: 200, cellClass: 'text-left'
+			,cellRenderer: function(params) {
+				return params.value + "(" + params.data.regDt + ")";
+			}
+		},
+		{headerName: "최종수정자", field: "updNm", width: 200, cellClass: 'text-left'
+			,cellRenderer: function(params) {
+				return params.value + "(" + params.data.updDt + ")";
+			}
+		}
+	];
+
+	// Get GridOptions
+	var gridOptions = gagaAgGrid.getGridOptions(columnDefs);
+
+	// Row Click
+	gridOptions.onCellClicked = function(event) {
+		var goodsCd = event.data.goodsCd;
+		if (event.colDef.field == "freeGiftName"){
+			// 수정 필요
+			// cfnOpenGoodsDetailPopup('U',goodsCd);
+		}
+	}
+
+	// 어떤건지 확인 필요
+	/* gridOptions.getRowStyle = function(params) {
+		if ("G008_00" == params.data.goodsStat  || "G008_10" == params.data.goodsStat || "G008_20" == params.data.goodsStat || "G008_30" == params.data.goodsStat) {
+			return { background: '#23c6c8' };
+		}
+	} */
+
+	// 초기화 클릭시
+	$('#btnInit').on('click', function() {
+
+		$('#searchForm')[0].reset();
+		//$("#searchForm input[type=radio]").removeClass("checked");
+		$("#searchForm input[type=checkbox]").removeClass("checked");
+		//$("#searchForm input[type=radio]").parent("label").removeClass("checked");
+		$("#searchForm input[type=checkbox]").parent("label").removeClass("checked");
+		$("#searchForm input[type=radio][checked]").parent("label").addClass("checked");
+	});
+
+	// 조회클릭시
+	$('#btnSearch').on('click', function() {
+		$("#searchForm input[name=pageNo]").val('1');
+		fnFreeGoodsPromotionListSearch();
+	});
+
+	// 조회
+	var fnFreeGoodsPromotionListSearch = function() {
+		if(!fnConditionCheck()) return;
+
+		gagaPaging.init('searchForm', fnSearchCallBack, 'freeGoodsListPagination', $('#searchForm').find('#pageSize').val());
+		gagaPaging.load($("#searchForm input[name=pageNo]").val());
+	}
+
+	//검색 조건 확인
+	var fnConditionCheck = function(){
+		var fromDate = $('#searchForm input[name=stDate]').val();
+		var toDate = $('#searchForm input[name=edDate]').val();
+
+		if (!gagajf.isNull(fromDate) || !gagajf.isNull(toDate)) {
+
+			if (gagajf.isNull(fromDate) || gagajf.isNull(toDate)) {
+				mcxDialog.alertC("기간 조회시 시작일자와 종료일자를 입력하세요.", {
+					sureBtnText: "확인",
+					sureBtnClick: function() {
+						$('#searchForm input[name=stDate]').focus();
+					}
+				});
+				return false;
+			}
+
+			if (fromDate > toDate) {
+				mcxDialog.alertC("등록 시작일자는 종료일자 보다 클 수 없습니다.", {
+					sureBtnText: "확인",
+					sureBtnClick: function() {
+						$('#searchForm input[name=stDate]').focus();
+					}
+				});
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	var fnSearchCallBack = function(result){
+
+		$('#searchForm').find('#gridRowTotalCount').html(result.pageing.pageable.totalCount.addComma());
+		$('#searchForm').find('#pageNo').val(result.pageing.pageable.pageNo.addComma());
+		$('#searchForm').find('#pgNo').html(result.pageing.pageable.pageNo.addComma());
+		$('#searchForm').find('#endPgNo').html(result.pageing.pageable.totalPage.addComma());
+		gridOptions.api.setRowData(result.freeGoodsList);
+		gagaPaging.createPagination(result.pageing.pageable);
+		
+	}
+
+	//페이징
+	$('#searchForm select[name=pageSize]').on('change', function() {
+		$("#searchForm input[name=pageNo]").val('1');
+		fnFreeGoodsPromotionListSearch();
+	});
+
+	$(document).ready(function() {
+
+		cfnCreateCalendar('#sellTerms', 'stDate', 'edDate', true, '기간', 'X');
+
+		// Create a agGrid
+		gagaAgGrid.createGrid('gridList', gridOptions);
+
+	});
+
+/*]]>*/
+</script>
+	</div>
+
+</html>

+ 7 - 7
style24.admin/src/main/webapp/WEB-INF/views/order/OrderDetailCouponHst.html

@@ -13,7 +13,7 @@
  * 1.0  2020.12.16   jsh77b       최초 작성
  *******************************************************************************
  -->
-<div class="modalPopup" data-width="900">
+<div class="modalPopup" data-width="1000">
 	<div class="panelStyle">
 		<div class="panelTitle">
 			<h2>쿠폰 내역</h2>
@@ -36,14 +36,14 @@ var orderCouponHstList = [[${orderCouponHstList}]];
 //specify the columns
 var columnDefsOrderCouponInfo = [
 	{headerName: "쿠폰구분", 			field: "cpnType", 	width: 100, cellClass: 'text-center'},
-	{headerName: "상품/공급처", 		field: "targetCd1", width: 160, cellClass: 'text-center'},
+	{headerName: "상품코드/공급처코드", 	field: "targetCd1", width: 160, cellClass: 'text-center'},
 	{headerName: "주문상세/배송정책", 	field: "targetCd2", width: 160, cellClass: 'text-center'},
 	{headerName: "쿠폰ID", 			field: "cpnSq", 	width: 100, cellClass: 'text-center'},
-	{headerName: "쿠폰명", 			field: "cpnNm", 	width: 120, cellClass: 'text-center'},
+	{headerName: "쿠폰명", 			field: "cpnNm", 	width: 140, cellClass: 'text-center'},
 	{
 		headerName			: "웹 할인금액/율"
 		, field				: "dcPval"
-		, width				: 140
+		, width				: 120
 		, cellClass			: 'text-center'
 		, valueFormatter	: function(params) {
 			return (params.data.dcWay == '10') ? params.value.addComma() + '원' : params.value.addComma() + '%';}	
@@ -51,7 +51,7 @@ var columnDefsOrderCouponInfo = [
 	{
 		headerName			: "모바일 할인금액/율"
 		, field				: "dcMval"
-		, width				: 150
+		, width				: 120
 		, cellClass			: 'text-center'
 		, valueFormatter	: function(params) {
 			return (params.data.dcWay == '10') ? params.value.addComma() + '원' : params.value.addComma() + '%';
@@ -60,7 +60,7 @@ var columnDefsOrderCouponInfo = [
 	{
 		headerName			: "판매상품가격"
 		, field				: "ordAmt"
-		, width				: 120
+		, width				: 100
 		, cellClass			: 'text-center'
 		, valueFormatter	: function(params) {
 			return params.value.addComma();
@@ -69,7 +69,7 @@ var columnDefsOrderCouponInfo = [
 	{
 		headerName			: "실할인금액"
 		, field				: "cpnDcAmt"
-		, width				: 120
+		, width				: 100
 		, cellClass			: 'text-center'
 		, valueFormatter	: function(params) {
 			return params.value.addComma();

+ 37 - 19
style24.admin/src/main/webapp/WEB-INF/views/order/OrderDetailForm.html

@@ -171,7 +171,7 @@ var columnDefsOrderInfo = [
 		, field				: "siteCd"
 		, width				: 100
 		, cellClass			: 'text-center'
-		, valueFormatter: function (params) {
+		, valueFormatter	: function (params) {
 			return gagaAgGrid.lookupValue(siteCdList, params.value);
 		}
 	},
@@ -183,7 +183,7 @@ var columnDefsOrderInfo = [
 		, field				: "ordDt"
 		, width				: 130
 		, cellClass			: 'text-center' 
-		, cellRenderer: function(params) {
+		, cellRenderer		: function(params) {
 			return !gagajf.isNull(params.value) ? params.value.toDate("YYYYMMDDHHmm").format("YYYY-MM-DD HH:mm") : '';
 		}
 	},
@@ -269,23 +269,29 @@ var columnDefsGoodsInfo = [
 				, cellRenderer	: function (params) {
 					return params.value.addComma();
 				}
-			},
+			}
 		]
 	},
 	{
-		headerName		: "주문상세상태"
-		, field			: "ordDtlStatNm"
-		, width			: 120
-		, cellClass		: 'text-left'
-		, cellRenderer	: function (params) {
-			var retVal = "";
-			retVal += (params.data.cancelRequestQty > 0) ? " 취" + params.data.cancelRequestQty : "";
-			retVal += (params.data.returnRequestQty > 0) ? " 반" + params.data.returnRequestQty : "";
-			retVal += (params.data.exchangeRequestQty > 0) ? " 교" + params.data.exchangeRequestQty : "";
-			
-			if (!gagajf.isNull(retVal)) retVal = "-" + retVal;
-				return "<a href=\"javascript:void(0);\" onclick=\"fnOrderDetailChangeHst('" + params.data.ordDtlNo + "');\">" + params.value + retVal + "</a>";
-		}
+		headerName	: "주문상세",
+		children	: [
+			{headerName: "주문상세번호"	, field: "ordDtlNo"		, width: 120, cellClass: 'text-center'},
+			{
+				headerName		: "주문상세상태"
+				, field			: "ordDtlStatNm"
+				, width			: 120
+				, cellClass		: 'text-left'
+				, cellRenderer	: function (params) {
+					var retVal = "";
+					retVal += (params.data.cancelRequestQty > 0) ? " 취" + params.data.cancelRequestQty : "";
+					retVal += (params.data.returnRequestQty > 0) ? " 반" + params.data.returnRequestQty : "";
+					retVal += (params.data.exchangeRequestQty > 0) ? " 교" + params.data.exchangeRequestQty : "";
+					
+					if (!gagajf.isNull(retVal)) retVal = "-" + retVal;
+						return "<a href=\"javascript:void(0);\" onclick=\"fnOrderDetailChangeHst('" + params.data.ordDtlNo + "');\">" + params.value + retVal + "</a>";
+				}
+			}
+		]
 	},
 	{
 		headerName	: "수량",
@@ -335,7 +341,7 @@ var columnDefsGoodsInfo = [
 				, width			: 70
 				, cellClass		: 'text-right'
 				, cellRenderer	: function (params) {
-					return "<a href=\"javascript:void(0);\" onclick=\"fnOrderPointHst('" + params.data.ordNo + "');\">" + params.value.addComma() + "</a>";
+					return "<a href=\"javascript:void(0);\" onclick=\"fnOrderTmtbHst('" + params.data.ordNo + "');\">" + params.value.addComma() + "</a>";
 				}
 			},
 			{
@@ -344,7 +350,7 @@ var columnDefsGoodsInfo = [
 				, width			: 70
 				, cellClass		: 'text-right'
 				, cellRenderer	: function (params) {
-					return "<a href=\"javascript:void(0);\" onclick=\"fnOrderPointHst('" + params.data.ordNo + "');\">" + params.value.addComma() + "</a>";
+					return "<a href=\"javascript:void(0);\" onclick=\"fnOrderTmtbHst('" + params.data.ordNo + "');\">" + params.value.addComma() + "</a>";
 				}
 			}
 		]
@@ -390,7 +396,7 @@ var columnDefsGoodsInfo = [
 				, width			: 80
 				, cellClass		: 'text-right' //pntDcAmtClass
 				, cellRenderer	: function (params) {
-					return "<a href=\"javascript:void(0);\" onclick=\"fnOrderPointHst('" + params.data.ordNo + "');\">" + params.value.addComma() + "</a>";
+					return "<a href=\"javascript:void(0);\" onclick=\"fnOrderGiftcardHst('" + params.data.ordNo + "');\">" + params.value.addComma() + "</a>";
 				}
 			}
 		]
@@ -976,12 +982,24 @@ $(document).ready(function () {
 		var actionUrl = "/order/detail/coupon/hst/form/" + ordNo;
 		cfnOpenModalPopup(actionUrl, 'popupOrderDetailCouponHstForm');
 	};
+	
+	// 다다익선내역 팝업
+	var fnOrderTmtbHst = function (ordNo) {
+		var actionUrl = "/order/detail/tmtb/hst/form/" + ordNo;
+		cfnOpenModalPopup(actionUrl, 'popupOrderDetailTmtbHstForm');
+	};
 
 	// 포인트내역 팝업
 	var fnOrderPointHst = function (ordNo) {
 		var actionUrl = "/order/detail/point/hst/form/" + ordNo;
 		cfnOpenModalPopup(actionUrl, 'popupOrderDetailPointHstForm');
 	};
+	
+	// 상품권내역 팝업
+	var fnOrderGiftcardHst = function (ordNo) {
+		var actionUrl = "/order/detail/giftcard/hst/form/" + ordNo;
+		cfnOpenModalPopup(actionUrl, 'popupOrderDetailGiftcardHstForm');
+	};
 
 	// 관리자메모 등록
 	var fnCreateOrderMemo = function (ordNo, seq, mode) {

+ 143 - 0
style24.admin/src/main/webapp/WEB-INF/views/order/OrderDetailGiftcardHst.html

@@ -0,0 +1,143 @@
+<!DOCTYPE html>
+<html lang="ko" xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : OrderDetailPointHst.html
+ * @desc    : 주문상품 상세 상품권 이력 화면
+ *============================================================================
+ * Pastelmall
+ * Copyright(C) 2019 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.12.16   jsh77b       최초 작성
+ *******************************************************************************
+ -->
+<div class="modalPopup" data-width="900">
+	<div class="panelStyle">
+		<div class="giftcardTitle">
+			<h2>상품권 사용 내역</h2>
+			<button type="button" class="close" onclick="uifnPopupClose('popupOrderDetailGiftcardHstForm');"><i class="fa fa-times"></i></button>
+		</div>
+			
+		<div class="panelContent" style="overflow-y:auto;">
+			<form id="pointHstFrm">
+				<div id="gridOrderGiftcardInfo" style="width:100%; height:260px;" class="ag-theme-balham"></div>
+			</form>
+		</div>
+	</div>
+</div>
+
+<!-- data -->
+<script th:inline="javascript">
+/*<![CDATA[*/
+var orderGiftcardHstList = [[${orderGiftcardHstList}]];
+
+// specify the columns
+var columnDefsOrderGiftcardInfo = [
+		{headerName: "상품권명", 		field: "gfcdNm", 		width: 180, cellClass: 'text-center'},
+		{headerName: "상품권코드", 		field: "gfcdNo", 		width: 180, cellClass: 'text-center'},
+		{
+			headerName			: "상품권승인금액"
+			, field				: "chgGfcdAmt"
+			, width				: 130
+			, cellClass			: 'text-center'
+			, valueFormatter	: function(params) {
+				return params.value.addComma();
+			}	
+		},
+		{
+			headerName			: "상품권사용금액"
+			, field				: "usGfcdAmt"
+			, width				: 130
+			, cellClass			: 'text-center'
+			, valueFormatter	: function(params) {
+				return params.value.addComma();
+			}	
+		},
+		{
+			headerName			: "상품권남은금액"
+			, field				: "rmGfcdAmt"
+			, width				: 130
+			, cellClass			: 'text-center'
+			, valueFormatter	: function(params) {
+				return params.value.addComma();
+			}	
+		},
+		{headerName: "주문상세", 		field: "ordDtlNo", 		width: 100, cellClass: 'text-center'},
+		{headerName: "상품코드", 		field: "goodsCd", 		width: 100, cellClass: 'text-center'},
+		{
+			headerName			: "상품권사용금액"
+			, field				: "gfcdAmt"
+			, width				: 130
+			, cellClass			: 'text-center'
+			, valueFormatter	: function(params) {
+				return params.value.addComma();
+			}	
+		},
+		{headerName: "사유", 			field: "occurDtlDesc", 	width: 180, cellClass: 'text-center'},
+		{
+			headerName			: "등록일시"	
+			, field				: "regDt"
+			, width				: 130
+			, cellClass			: 'text-center' 
+			, cellRenderer: function(params) {
+				return !gagajf.isNull(params.value) ? params.value.toDate("YYYYMMDDHHmm").format("YYYY-MM-DD HH:mm") : '';
+			}
+		}
+];
+
+var gridOptionsOrderGiftcardInfo = orderAgGrid.getGridOptions(columnDefsOrderGiftcardInfo);
+
+$(document).ready(function() {	
+	// Create a agGrid
+	gagaAgGrid.createGrid('gridOrderGiftcardInfo', gridOptionsOrderGiftcardInfo);
+	gridOptionsOrderGiftcardInfo.api.setRowData(orderGiftcardHstList);
+});
+/*]]>*/
+
+</script>
+
+<!-- AgGrid 컬럼 세팅 -->
+<script>
+// 공통1. 주문상세 그리드 옵션 정보 적용
+var orderAgGrid = {
+	getGridOptions : function(colDefs) {
+		return {
+			columnDefs					: colDefs
+			, detailCellRendererParams	: {
+				detailGridOptions	: {
+					columnDefs				: []
+					, defaultColDef			: {
+						resizable: true
+					}
+					, suppressLoadingOverlay: false
+					, onGridReady			: function (params) {
+						params.api.setDomLayout('autoHeight');
+					}
+					, onFirstDataRendered	: function (params) {
+						params.api.sizeColumnsToFit();
+					}
+				}
+				, getDetailRowData: function (params) {
+					params.successCallback(params.data.orderDetailList);
+				}
+			}
+			, defaultColDef: {
+				resizable: true
+			}
+			, isRowMaster: function (dataItem) {
+				return dataItem ? dataItem.orderDetailList.length > 1 : false;
+			}
+			, suppressRowTransform: true
+			, enableRangeSelection: true
+		};
+	}
+}
+</script>
+</html>
+
+
+
+
+

+ 41 - 11
style24.admin/src/main/webapp/WEB-INF/views/order/OrderDetailPointHst.html

@@ -16,8 +16,8 @@
 <div class="modalPopup" data-width="900">
 	<div class="panelStyle">
 		<div class="panelTitle">
-			<h2>마일리지 내역</h2>
-			<button type="button" class="close" onclick="uifnPopupClose('popupOrderDetailCouponHstForm');"><i class="fa fa-times"></i></button>
+			<h2>포인트 사용 내역</h2>
+			<button type="button" class="close" onclick="uifnPopupClose('popupOrderDetailPointHstForm');"><i class="fa fa-times"></i></button>
 		</div>
 			
 		<div class="panelContent" style="overflow-y:auto;">
@@ -38,21 +38,51 @@ var columnDefsOrderPointInfo = [
 		{headerName: "상품코드", 		field: "goodsCd", 		width: 100, cellClass: 'text-center'},
 		{headerName: "적립율(PC)", 	field: "pntPrate", 		width: 100, cellClass: 'text-center'},
 		{headerName: "적립율(모바일)", 	field: "pntMrate", 		width: 100, cellClass: 'text-center'},
-		{headerName: "판매상품가격", 	field: "currPrice", 	width: 120, cellClass: 'text-center', valueFormatter: function(params) {return params.value.addComma();}	},
-		{headerName: "실결제상품가격", 	field: "realOrdAmt", 	width: 140, cellClass: 'text-center', valueFormatter: function(params) {return params.value.addComma();}	},
-		{headerName: "마일리지금액", 	field: "pntAmt", 		width: 100, cellClass: 'text-center', valueFormatter: function(params) {return params.value.addComma();}	},
-		{headerName: "사유", 			field: "occurDtlDesc", 	width: 120, cellClass: 'text-center'},
-		{headerName: "전환일자", 		field: "pntUploadDt",	width: 160, cellClass: 'text-center'},
-		{headerName: "상태", 			field: "pntUploadStat", width: 100, cellClass: 'text-center'},
-		{headerName: "등록일시", 		field: "regDt",			width: 160, cellClass: 'text-center'}
+		{
+			headerName			: "판매상품가격"
+			, field				: "currPrice"
+			, width				: 120
+			, cellClass			: 'text-center'
+			, valueFormatter	: function(params) {
+				return params.value.addComma();
+			}	
+		},
+		{
+			headerName			: "실결제상품가격"
+			, field				: "realOrdAmt"
+			, width				: 140
+			, cellClass			: 'text-center'
+			, valueFormatter	: function(params) {
+				return params.value.addComma();
+			}	
+		},
+		{
+			headerName			: "사용포인트"
+			, field				: "pntAmt"
+			, width				: 100
+			, cellClass			: 'text-center'
+			, valueFormatter	: function(params) {
+				return params.value.addComma();
+			}	
+		},
+		{headerName: "사유", 			field: "occurDtlDesc", 	width: 180, cellClass: 'text-center'},
+		{
+			headerName			: "등록일시"	
+			, field				: "regDt"
+			, width				: 130
+			, cellClass			: 'text-center' 
+			, cellRenderer: function(params) {
+				return !gagajf.isNull(params.value) ? params.value.toDate("YYYYMMDDHHmm").format("YYYY-MM-DD HH:mm") : '';
+			}
+		}
 ];
 
 var gridOptionsOrderPointInfo = orderAgGrid.getGridOptions(columnDefsOrderPointInfo);
 
 $(document).ready(function() {	
 	// Create a agGrid
-	gagaAgGrid.createGrid('gridOrderCouponInfo', gridOptionsOrderCouponInfo);
-	gridOptionsOrderCouponInfo.api.setRowData(orderCouponHstList);
+	gagaAgGrid.createGrid('gridOrderPointInfo', gridOptionsOrderPointInfo);
+	gridOptionsOrderPointInfo.api.setRowData(orderPointHstList);
 });
 /*]]>*/
 

+ 115 - 0
style24.admin/src/main/webapp/WEB-INF/views/order/OrderDetailTmtbHst.html

@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html lang="ko" xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : OrderDetailPointHst.html
+ * @desc    : 주문상품 상세 상품권 이력 화면
+ *============================================================================
+ * Pastelmall
+ * Copyright(C) 2019 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.12.16   jsh77b       최초 작성
+ *******************************************************************************
+ -->
+<div class="modalPopup" data-width="800">
+	<div class="panelStyle">
+		<div class="giftcardTitle">
+			<h2>다다익선 적용 내역</h2>
+			<button type="button" class="close" onclick="uifnPopupClose('popupOrderDetailTmtbHstForm');"><i class="fa fa-times"></i></button>
+		</div>
+			
+		<div class="panelContent" style="overflow-y:auto;">
+			<form id="pointHstFrm">
+				<div id="gridOrderTmtbInfo" style="width:100%; height:260px;" class="ag-theme-balham"></div>
+			</form>
+		</div>
+	</div>
+</div>
+
+<!-- data -->
+<script th:inline="javascript">
+/*<![CDATA[*/
+var orderTmtbHstList = [[${orderTmtbHstList}]];
+
+// specify the columns
+var columnDefsOrderGiftcardInfo = [
+		{headerName: "다다익선번호"	, field: "tmtbSq", 		width: 100, cellClass: 'text-center'},
+		{headerName: "다다익선명"	, field: "tmtbNm", 		width: 180, cellClass: 'text-center'},
+		{
+			headerName			: "할인금액"
+			, field				: "tmtbDcAmt"
+			, width				: 120
+			, cellClass			: 'text-center'
+			, valueFormatter	: function(params) {
+				return params.value.addComma();
+			}	
+		},
+		{headerName: "주문상세", 		field: "ordDtlNo",	width: 120, cellClass: 'text-center'},
+		{headerName: "상품코드", 		field: "goodsCd",	width: 120, cellClass: 'text-center'},
+		{
+			headerName			: "등록일시"	
+			, field				: "regDt"
+			, width				: 130
+			, cellClass			: 'text-center' 
+			, cellRenderer: function(params) {
+				return !gagajf.isNull(params.value) ? params.value.toDate("YYYYMMDDHHmm").format("YYYY-MM-DD HH:mm") : '';
+			}
+		}
+];
+
+var gridOptionsOrderTmtbInfo = orderAgGrid.getGridOptions(columnDefsOrderGiftcardInfo);
+
+$(document).ready(function() {	
+	// Create a agGrid
+	gagaAgGrid.createGrid('gridOrderTmtbInfo', gridOptionsOrderTmtbInfo);
+	gridOptionsOrderTmtbInfo.api.setRowData(orderTmtbHstList);
+});
+/*]]>*/
+
+</script>
+
+<!-- AgGrid 컬럼 세팅 -->
+<script>
+// 공통1. 주문상세 그리드 옵션 정보 적용
+var orderAgGrid = {
+	getGridOptions : function(colDefs) {
+		return {
+			columnDefs					: colDefs
+			, detailCellRendererParams	: {
+				detailGridOptions	: {
+					columnDefs				: []
+					, defaultColDef			: {
+						resizable: true
+					}
+					, suppressLoadingOverlay: false
+					, onGridReady			: function (params) {
+						params.api.setDomLayout('autoHeight');
+					}
+					, onFirstDataRendered	: function (params) {
+						params.api.sizeColumnsToFit();
+					}
+				}
+				, getDetailRowData: function (params) {
+					params.successCallback(params.data.orderDetailList);
+				}
+			}
+			, defaultColDef: {
+				resizable: true
+			}
+			, isRowMaster: function (dataItem) {
+				return dataItem ? dataItem.orderDetailList.length > 1 : false;
+			}
+			, suppressRowTransform: true
+			, enableRangeSelection: true
+		};
+	}
+}
+</script>
+</html>
+
+
+
+
+

+ 13 - 0
style24.batch/.classpath

@@ -28,5 +28,18 @@
 			<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/classes"/>
 		</attributes>
 	</classpathentry>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+			<attribute name="test" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+			<attribute name="test" value="true"/>
+		</attributes>
+	</classpathentry>
 	<classpathentry kind="output" path="target/classes"/>
 </classpath>

+ 2 - 0
style24.batch/.settings/org.eclipse.wst.common.component

@@ -4,6 +4,8 @@
         <wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
         <wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
         <wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
+        <wb-resource deploy-path="/WEB-INF/classes" source-path="/src/test/java"/>
+        <wb-resource deploy-path="/WEB-INF/classes" source-path="/src/test/resources"/>
         <property name="context-root" value="style24.batch"/>
         <property name="java-output-path" value="/style24.batch/target/classes"/>
     </wb-module>

+ 0 - 2
style24.core/src/main/java/com/style24/core/support/env/TscConstants.java

@@ -17,8 +17,6 @@ public class TscConstants {
 	// Mybatis Mapper 경로
 	public static final String MAPPER_LOCATION_PATH = "classpath:com/style24/persistence/mybatis";
 
-	public static final String EXCEL_FOOTER_TITLE = "Copyright(c) 2020 style24, All rights reserved.";
-
 	// 콜센터전화번호
 	public static final String CALLCENTER_TEL_NO = "1544-5336";
 

+ 45 - 0
style24.front/.classpath

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry including="**/*.java" kind="src" output="target/classes" path="src/main/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java">
+		<attributes>
+			<attribute name="test" value="true"/>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
+		<attributes>
+			<attribute name="test" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+			<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v9.0.22"/>
+	<classpathentry kind="lib" path="/style24.core/target/classes">
+		<attributes>
+			<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/classes"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>

+ 42 - 0
style24.front/.project

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>style24.front</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.validation.validationbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.springframework.ide.eclipse.boot.validation.springbootbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
+		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+		<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
+	</natures>
+</projectDescription>

+ 13 - 0
style24.front/.settings/.jsdtscope

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry excluding="**/bower_components/*|**/node_modules/*|**/*.min.js" kind="src" path="src/main/webapp"/>
+	<classpathentry kind="src" path="target/m2e-wtp/web-resources"/>
+	<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
+		<attributes>
+			<attribute name="hide" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
+	<classpathentry kind="output" path=""/>
+</classpath>

+ 6 - 0
style24.front/.settings/org.eclipse.core.resources.prefs

@@ -0,0 +1,6 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/main/resources=UTF-8
+encoding//src/test/java=UTF-8
+encoding//src/test/resources=UTF-8
+encoding/<project>=UTF-8

+ 12 - 0
style24.front/.settings/org.eclipse.jdt.core.prefs

@@ -0,0 +1,12 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.8

+ 4 - 0
style24.front/.settings/org.eclipse.m2e.core.prefs

@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1

+ 10 - 0
style24.front/.settings/org.eclipse.wst.common.component

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
+    <wb-module deploy-name="style24.front">
+        <wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
+        <wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
+        <wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
+        <wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
+        <property name="context-root" value="style24.front"/>
+        <property name="java-output-path" value="/style24.front/target/classes"/>
+    </wb-module>
+</project-modules>

+ 7 - 0
style24.front/.settings/org.eclipse.wst.common.project.facet.core.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<faceted-project>
+  <fixed facet="wst.jsdt.web"/>
+  <installed facet="java" version="1.8"/>
+  <installed facet="jst.web" version="4.0"/>
+  <installed facet="wst.jsdt.web" version="1.0"/>
+</faceted-project>

+ 1 - 0
style24.front/.settings/org.eclipse.wst.jsdt.ui.superType.container

@@ -0,0 +1 @@
+org.eclipse.wst.jsdt.launching.baseBrowserLibrary

+ 1 - 0
style24.front/.settings/org.eclipse.wst.jsdt.ui.superType.name

@@ -0,0 +1 @@
+Window

+ 2 - 0
style24.front/.settings/org.eclipse.wst.validation.prefs

@@ -0,0 +1,2 @@
+disabled=06target
+eclipse.preferences.version=1

+ 2 - 0
style24.front/.settings/org.springframework.ide.eclipse.prefs

@@ -0,0 +1,2 @@
+boot.validation.initialized=true
+eclipse.preferences.version=1

+ 111 - 0
style24.front/pom.xml

@@ -0,0 +1,111 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.style24</groupId>
+		<artifactId>root</artifactId>
+		<version>0.0.1-SNAPSHOT</version>
+	</parent>
+	<groupId>com.style24.front</groupId>
+	<artifactId>style24.front</artifactId>
+	<packaging>war</packaging>
+	<name>style24.front</name>
+	<description>STYLE24 Front</description>
+	
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-security</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.security</groupId>
+			<artifactId>spring-security-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-mobile</artifactId>
+			<version>1.5.22.RELEASE</version>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-thymeleaf</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>nz.net.ultraq.thymeleaf</groupId>
+			<artifactId>thymeleaf-layout-dialect</artifactId>
+		</dependency>
+		
+		<dependency>
+			<groupId>servlets.com</groupId>
+			<artifactId>cos</artifactId>
+			<version>05Nov2002</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-fileupload</groupId>
+			<artifactId>commons-fileupload</artifactId>
+			<version>1.4</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-io</groupId>
+			<artifactId>commons-io</artifactId>
+			<version>2.6</version>
+		</dependency>
+		
+		<!-- /// WEB-INF lib -->
+		<dependency>
+			<groupId>com.gagaframework</groupId>
+			<artifactId>gagaframework-web-core</artifactId>
+			<version>1.7.1-RELEASE</version>
+			<scope>system</scope>
+			<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/gagaframework-web-core-1.7.1-RELEASE.jar</systemPath>
+		</dependency>
+		<dependency>
+			<groupId>com.gagaframework</groupId>
+			<artifactId>gagaframework-web-security</artifactId>
+			<version>1.7.1-RELEASE</version>
+			<scope>system</scope>
+			<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/gagaframework-web-security-1.7.1-RELEASE.jar</systemPath>
+		</dependency>
+		<dependency>
+			<groupId>com.gagaframework</groupId>
+			<artifactId>gagaframework-web-parameter</artifactId>
+			<version>1.7-RELEASE</version>
+			<scope>system</scope>
+			<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/gagaframework-web-parameter-1.7-RELEASE.jar</systemPath>
+		</dependency>
+		<dependency>
+			<groupId>com.gagaframework</groupId>
+			<artifactId>gagaframework-web-rest</artifactId>
+			<version>1.7.1-RELEASE</version>
+			<scope>system</scope>
+			<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/gagaframework-web-rest-1.7-RELEASE.jar</systemPath>
+		</dependency>
+		<dependency>
+			<groupId>com.gagaframework</groupId>
+			<artifactId>gagaframework-web-util</artifactId>
+			<version>1.7-RELEASE</version>
+			<scope>system</scope>
+			<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/gagaframework-web-util-1.7-RELEASE.jar</systemPath>
+		</dependency>
+		<!-- \\\ WEB-INF lib -->
+	</dependencies>
+	
+	<build>
+		<finalName>${project.name}</finalName>
+		<resources>
+			<resource>
+				<directory>src/main/java</directory>
+				<includes>
+					<include>**/*.xml</include>
+				</includes>
+			</resource>
+			<resource>
+				<directory>src/main/resources</directory>
+				<includes>
+					<include>**/*</include>
+				</includes>
+			</resource>
+		</resources>
+	</build>
+</project>

+ 100 - 0
style24.front/src/main/java/com/style24/front/biz/dao/TsfLoginDao.java

@@ -0,0 +1,100 @@
+package com.style24.front.biz.dao;
+
+import com.style24.core.support.annotation.ShopDs;
+import com.style24.persistence.domain.Login;
+import com.style24.persistence.domain.PersistentToken;
+
+/**
+ * 로그인 Dao
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@ShopDs
+public interface TsfLoginDao {
+
+	/**
+	 * 로그인체크정보 조회
+	 * @param customer - 고객정보
+	 * @return 고객정보
+	 * @author gagamel
+	 * @date 2020. 2. 3
+	 */
+	Login getLoginCheckInfo(Login customer);
+
+	/**
+	 * 로그인실패 남기기
+	 * @param customer - 고객정보
+	 * @author gagamel
+	 * @date 2020. 2. 3
+	 */
+	void createLoginFail(Login customer);
+
+	/**
+	 * 로그인 실패건수 조회
+	 * @param customer - 고객정보
+	 * @return 로그인 실패건수
+	 * @author gagamel
+	 * @date 2020. 2. 3
+	 */
+	int getLoginFailCount(Login customer);
+
+	/**
+	 * 최종로그인일시 Update
+	 * @param custNo - 고객번호
+	 * @author gagamel
+	 * @date 2020. 2. 3
+	 */
+	void updateLastLoginDate(Integer custNo);
+
+	/**
+	 * 로그인이력 남기기
+	 * @param customer - 고객정보
+	 * @author gagamel
+	 * @date 2020. 2. 3
+	 */
+	void createLoginHistory(Login customer);
+
+//	/**
+//	 * 로그인 세션정보 조회
+//	 * @param customer - 고객정보
+//	 * @return 로그인 세션정보
+//	 * @author gagamel
+//	 * @date 2020. 2. 3
+//	 */
+//	FoLogin getLoginSessionInfo(FoLogin customer);
+
+	/**
+	 * 로그인유지토큰 생성
+	 * @param token - 토큰 정보
+	 * @author gagamel
+	 * @since 2020. 6. 17
+	 */
+	void createPersistentToken(PersistentToken token);
+
+	/**
+	 * 로그인유지토큰 가져오기
+	 * @param remembermeToken - RememberMe토큰
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 6. 17
+	 */
+	PersistentToken getPersistentToken(String remembermeToken);
+
+	/**
+	 * 로그인유지토큰 갱신
+	 * @param token - 토큰 정보
+	 * @author gagamel
+	 * @since 2020. 6. 17
+	 */
+	void updatePersistentToken(PersistentToken token);
+
+	/**
+	 * 로그인유지토큰 삭제
+	 * @param remembermeToken - RememberMe토큰
+	 * @author gagamel
+	 * @since 2020. 6. 17
+	 */
+	void deletePersistentToken(String remembermeToken);
+
+}

+ 79 - 0
style24.front/src/main/java/com/style24/front/biz/dao/TsfNoticeDao.java

@@ -0,0 +1,79 @@
+package com.style24.front.biz.dao;
+
+import java.util.Collection;
+
+import com.style24.core.support.annotation.ShopDs;
+import com.style24.persistence.domain.Notice;
+
+/**
+ * 공지사항 Dao
+ * 
+ * @author gagamel
+ * @since 2020. 11. 3
+ */
+@ShopDs
+public interface TsfNoticeDao {
+
+	/**
+	 * 공지사항 총건수
+	 * @param notice - 공지사항 정보
+	 * @return 총건수
+	 * @author gagamel
+	 * @date 2020. 11. 3
+	 */
+	int getNoticeTotalCount(Notice notice);
+
+	/**
+	 * 공지사항 목록
+	 * @param notice - 공지사항 정보
+	 * @return
+	 * @author gagamel
+	 * @date 2020. 11. 3
+	 */
+	Collection<Notice> getNoticeList(Notice notice);
+
+	/**
+	 * 공지사항 상세
+	 * @param notice - 공지사항 정보
+	 * @return
+	 * @author gagamel
+	 * @date 2020. 11. 3
+	 */
+	Notice getNoticeDetail(Notice notice);
+
+	/**
+	 * 공지사항 이전글
+	 * @param notice - 공지사항 정보
+	 * @return
+	 * @author gagamel
+	 * @date 2020. 11. 3
+	 */
+	Notice getPreviousNotice(Notice notice);
+
+	/**
+	 * 공지사항 다음글
+	 * @param notice - 공지사항 정보
+	 * @return
+	 * @author gagamel
+	 * @date 2020. 11. 3
+	 */
+	Notice getNextNotice(Notice notice);
+
+	/**
+	 * 공지사항 조회수 Update
+	 * @param notice
+	 * @author gagamel
+	 * @date 2020. 11. 3
+	 */
+	void updateNoticeReadCount(Notice notice);
+
+	/**
+	 * 공지사항 파일 목록
+	 * @param notice - 공지사항 정보
+	 * @return
+	 * @author gagamel
+	 * @date 2020. 2. 24
+	 */
+	Collection<Notice> getNoticeFileList(Notice notice);
+
+}

+ 164 - 0
style24.front/src/main/java/com/style24/front/biz/service/TsfLoginService.java

@@ -0,0 +1,164 @@
+package com.style24.front.biz.service;
+
+import java.util.Date;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.style24.core.support.env.TscConstants;
+import com.style24.front.biz.dao.TsfLoginDao;
+import com.style24.front.support.security.session.TsfSession;
+import com.style24.persistence.domain.Login;
+import com.style24.persistence.domain.PersistentToken;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 로그인 Service
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Service
+@Slf4j
+public class TsfLoginService {
+
+	@Autowired
+	private TsfLoginDao loginDao;
+
+	/**
+	 * 로그인체크정보 조회
+	 * @param customer - 고객정보
+	 * @return 고객정보
+	 * @author gagamel
+	 * @since 2020. 2. 3
+	 */
+	public Login getLoginCheckInfo(Login customer) {
+		customer.setIpAddr(TsfSession.getIpAddress());
+		customer.setSiteCd(TscConstants.Site.STYLE24.value());
+		return loginDao.getLoginCheckInfo(customer);
+	}
+
+	/**
+	 * 로그인이력 남기기
+	 * @param custNo - 고객번호
+	 * @author gagamel
+	 * @since 2020. 2. 3
+	 */
+	@Transactional("shopTxnManager")
+	public void createLoginHistory(Integer custNo) {
+		Login login = new Login();
+		login.setCustNo(custNo);
+		login.setIpAddr(TsfSession.getIpAddress());
+		login.setSiteCd(TscConstants.Site.STYLE24.value());
+		login.setFrontGb(TsfSession.getFrontGb());
+
+		loginDao.createLoginHistory(login);
+	}
+
+	/**
+	 * 최종로그인일시 Update
+	 * @param custNo - 고객번호
+	 * @author gagamel
+	 * @since 2020. 2. 3
+	 */
+	@Transactional("shopTxnManager")
+	public void updateLastLoginDate(Integer custNo) {
+		loginDao.updateLastLoginDate(custNo);
+	}
+
+	/**
+	 * 로그인실패 남기기. 로그인실패여부가 "N:성공"이면 실패건수 0으로 초기화
+	 * @param custId - 고객ID
+	 * @param loginFailYn - 로그인실패여부(Y:실패, N:성공)
+	 * @author gagamel
+	 * @since 2020. 2. 3
+	 */
+	@Transactional("shopTxnManager")
+	public void createLoginFail(String custId, String loginFailYn) {
+		Login customer = new Login();
+		customer.setCustId(custId);
+		customer.setIpAddr(TsfSession.getIpAddress());
+		customer.setSiteCd(TscConstants.Site.STYLE24.value());
+		customer.setLoginFailYn(loginFailYn);
+
+		loginDao.createLoginFail(customer);
+	}
+
+	/**
+	 * 로그인 실패건수 조회
+	 * @param custNo - 고객번호
+	 * @return 로그인 실패건수
+	 * @author gagamel
+	 * @since 2020. 2. 3
+	 */
+	public int getLoginFailCount(Integer custNo) {
+		Login login = new Login();
+		login.setCustNo(custNo);
+		login.setIpAddr(TsfSession.getIpAddress());
+		login.setSiteCd(TscConstants.Site.STYLE24.value());
+
+		return loginDao.getLoginFailCount(login);
+	}
+
+	/**
+	 * 로그인유지토큰 생성
+	 * @param custNo - 고객번호
+	 * @param password - 비밀번호
+	 * @param expiry - 만료기간
+	 * @author gagamel
+	 * @since 2020. 6. 17
+	 */
+	@Transactional("shopTxnManager")
+	public void createPersistentToken(Integer custNo, String remembermeToken, Date expiry) {
+		PersistentToken token = PersistentToken.builder()
+			.custNo(custNo)
+			.remembermeToken(remembermeToken)
+			.limitDt(expiry)
+			.build();
+
+		loginDao.createPersistentToken(token);
+	}
+
+	/**
+	 * 로그인유지토큰 가져오기
+	 * @param remembermeToken - RememberMe토큰
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 6. 17
+	 */
+	public PersistentToken getPersistentToken(String remembermeToken) {
+		return loginDao.getPersistentToken(remembermeToken);
+	}
+
+	/**
+	 * 로그인유지토큰 갱신
+	 * @param token - 토큰 정보
+	 * @author gagamel
+	 * @since 2020. 6. 17
+	 */
+	@Transactional("shopTxnManager")
+	public void updatePersistentToken(Integer custNo, String prevRemembermeToken, String remembermeToken, Date expiry) {
+		PersistentToken token = PersistentToken.builder()
+			.custNo(custNo)
+			.prevRemembermeToken(prevRemembermeToken)
+			.remembermeToken(remembermeToken)
+			.limitDt(expiry)
+			.build();
+
+		loginDao.updatePersistentToken(token);
+	}
+
+	/**
+	 * 로그인유지토큰 삭제
+	 * @param remembermeToken - RememberMe토큰
+	 * @author gagamel
+	 * @since 2020. 6. 17
+	 */
+	@Transactional("shopTxnManager")
+	public void deletePersistentToken(String remembermeToken) {
+		loginDao.deletePersistentToken(remembermeToken);
+	}
+
+}

+ 102 - 0
style24.front/src/main/java/com/style24/front/biz/service/TsfNoticeService.java

@@ -0,0 +1,102 @@
+package com.style24.front.biz.service;
+
+import java.util.Collection;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.style24.front.biz.dao.TsfNoticeDao;
+import com.style24.persistence.domain.Notice;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 공지사항 Service
+ * 
+ * @author gagamel
+ * @since 2020. 11. 3
+ */
+@Service
+@Slf4j
+public class TsfNoticeService {
+
+	@Autowired
+	private TsfNoticeDao noticeDao;
+
+	/**
+	 * 공지사항 총건수
+	 * @param notice - 공지사항 정보
+	 * @return 총건수
+	 * @author gagamel
+	 * @date 2020. 11. 3
+	 */
+	public int getNoticeTotalCount(Notice notice) {
+		return noticeDao.getNoticeTotalCount(notice);
+	}
+
+	/**
+	 * 공지사항 목록
+	 * @param notice - 공지사항 정보
+	 * @return
+	 * @author gagamel
+	 * @date 2020. 11. 3
+	 */
+	public Collection<Notice> getNoticeList(Notice notice) {
+		return noticeDao.getNoticeList(notice);
+	}
+
+	/**
+	 * 긴급 공지사항 목록
+	 * @param notice - 공지사항 정보
+	 * @return
+	 * @author gagamel
+	 * @date 2020. 11. 3
+	 */
+	public Collection<Notice> getUrgentNoticeList(Notice notice) {
+		notice.setUrgentYn("Y"); // 긴급공지
+		return noticeDao.getNoticeList(notice);
+	}
+
+	/**
+	 * 공지사항 상세
+	 * @param notice - 공지사항 정보
+	 * @return
+	 * @author gagamel
+	 * @date 2020. 11. 3
+	 */
+	public Notice getNoticeDetail(Notice notice) {
+		// 공지사항 조회건수 증가
+		noticeDao.updateNoticeReadCount(notice);
+
+		// 공지사항 상세 조회
+		Notice noticeDetail = noticeDao.getNoticeDetail(notice);
+
+		// 공지사항 이전글 조회
+		Notice prevNotice = noticeDao.getPreviousNotice(notice);
+		if (prevNotice != null) {
+			noticeDetail.setPrevNoticeSq(prevNotice.getPrevNoticeSq());
+			noticeDetail.setPrevNoticeTitle(prevNotice.getPrevNoticeTitle());
+		}
+
+		// 공지사항 다음글 조회
+		Notice nextNotice = noticeDao.getNextNotice(notice);
+		if (nextNotice != null) {
+			noticeDetail.setNextNoticeSq(nextNotice.getNextNoticeSq());
+			noticeDetail.setNextNoticeTitle(nextNotice.getNextNoticeTitle());
+		}
+
+		return noticeDetail;
+	}
+
+	/**
+	 * 공지사항 첨부파일 목록
+	 * @param notice - 공지사항 정보
+	 * @return
+	 * @author gagamel
+	 * @date 2020. 11. 3
+	 */
+	public Collection<Notice> getNoticeFileList(Notice notice) {
+		return noticeDao.getNoticeFileList(notice);
+	}
+
+}

+ 21 - 0
style24.front/src/main/java/com/style24/front/biz/web/TsfCustomerController.java

@@ -0,0 +1,21 @@
+package com.style24.front.biz.web;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import com.style24.front.support.controller.TsfBaseController;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 고객 Controller
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Controller
+@RequestMapping("/customer")
+@Slf4j
+public class TsfCustomerController extends TsfBaseController {
+
+}

+ 134 - 0
style24.front/src/main/java/com/style24/front/biz/web/TsfIndexController.java

@@ -0,0 +1,134 @@
+package com.style24.front.biz.web;
+
+import java.io.IOException;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.servlet.ModelAndView;
+
+import com.style24.core.support.message.TscMessageByLocale;
+import com.style24.front.support.controller.TsfBaseController;
+import com.style24.front.support.security.session.TsfSession;
+
+import lombok.extern.slf4j.Slf4j;
+
+import com.gagaframework.web.rest.server.GagaResponseStatus;
+
+/**
+ * Index Controller
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Controller
+@Slf4j
+public class TsfIndexController extends TsfBaseController {
+
+	@Autowired
+	private Environment env;
+
+	@Autowired
+	private TscMessageByLocale message;
+
+	/**
+	 * 에러 페이지
+	 * @return
+	 * @author gagamel
+	 * @throws IOException
+	 * @since 2020. 3. 18
+	 */
+	@GetMapping("/error")
+	public ModelAndView error(HttpServletRequest request, HttpServletResponse response) throws HttpRequestMethodNotSupportedException, IOException {
+		ModelAndView mav = new ModelAndView(super.getDeviceViewName("error/500"));
+
+		Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
+
+		if (status != null) {
+			Integer statusCode = Integer.valueOf(status.toString());
+			log.debug("statusCode: {}", statusCode);
+
+			if (statusCode == GagaResponseStatus.NOT_FOUND.getCode()) {
+				mav.addObject("status", GagaResponseStatus.NOT_FOUND.getCode());
+
+				// 404 오류 시 잘못된 링크 유입 시 몰메인으로 이동 처리
+				String prevRequestUri = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
+				log.debug("prevRequestUri: {}", prevRequestUri);
+				if (prevRequestUri.startsWith("/m")) {
+					response.sendRedirect("/");
+				}
+
+				String profiles = env.getProperty("spring.profiles.active").toLowerCase();
+				if ("locd".equals(profiles) || "locp".equals(profiles) || "dev".equals(profiles)) {
+					mav.addObject("message", "No mapping found for HTTP request with URI ["
+						+ String.valueOf(request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI)) + "]");
+				}
+
+				return mav;
+			}
+		}
+
+		mav.addObject("status", GagaResponseStatus.INTERNAL_SERVER_ERROR.getCode());
+
+		String profiles = env.getProperty("spring.profiles.active").toLowerCase();
+		if ("locd".equals(profiles) || "locp".equals(profiles) || "dev".equals(profiles)) {
+			mav.addObject("message", String.valueOf(request.getAttribute(RequestDispatcher.ERROR_MESSAGE)));
+		}
+
+		return mav;
+	}
+
+	/**
+	 * 첫 페이지
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 2. 3
+	 */
+	@GetMapping("/")
+	public String index() {
+		return "forward:/display/mall/main/form";
+	}
+
+	/**
+	 * 로그인 페이지
+	 * @param error - 로그인 오류 정보
+	 * @param session - HttpSession
+	 * @param snsType - SNS유형(NV:네이버, KK:카카오, FB:페이스북)
+	 * @return
+	 * @author gagamel
+	 * @throws IOException
+	 * @since 2020. 2. 4
+	 */
+	@GetMapping("/signin")
+	public ModelAndView signin(@RequestParam(value = "error", required = false) boolean isError, HttpSession session, HttpServletResponse response) throws IOException {
+		ModelAndView mav = new ModelAndView();
+
+		if (TsfSession.isLogin()) {
+			response.sendRedirect("/");
+		}
+
+		log.debug("isError: {}", isError);
+
+		if (isError) {
+			Exception e = (Exception)session.getAttribute("SPRING_SECURITY_LAST_EXCEPTION");
+			log.debug("e.getMessage(): {}", e.getMessage());
+			mav.addObject("error", e.getMessage());
+		}
+
+		mav.addObject("front", env.getProperty("domain.front"));
+
+		// 로그인 페이지로
+		mav.setViewName(super.getDeviceViewName("Signin"));
+
+		return mav;
+	}
+
+}

+ 48 - 0
style24.front/src/main/java/com/style24/front/support/config/TsfMybatisShopConfig.java

@@ -0,0 +1,48 @@
+package com.style24.front.support.config;
+
+import javax.sql.DataSource;
+
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.SqlSessionFactoryBean;
+import org.mybatis.spring.SqlSessionTemplate;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.style24.core.support.annotation.ShopDs;
+import com.style24.core.support.env.TscConstants;
+
+/**
+ * shopDs용 Mybatis Configuration
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Configuration
+@MapperScan(basePackages = TscConstants.BASE_PACKAGE, annotationClass = ShopDs.class, sqlSessionFactoryRef = "shopSqlSessionFactory")
+public class TsfMybatisShopConfig {
+
+	@Autowired
+	private ApplicationContext applicationContext;
+
+	@Bean(name = "shopSqlSessionFactory")
+	public SqlSessionFactory shopSqlSessionFactory(@Qualifier("shopDataSource") DataSource dataSource) throws Exception {
+		SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
+
+		sessionFactoryBean.setDataSource(dataSource);
+		sessionFactoryBean.setTypeAliasesPackage(TscConstants.DOMAIN_PACKAGE);
+		sessionFactoryBean.setConfigLocation(applicationContext.getResource("classpath:persistence/mybatis-shop-config.xml"));
+		sessionFactoryBean.setMapperLocations(applicationContext.getResources(TscConstants.MAPPER_LOCATION_PATH + "/*.xml"));
+
+		return sessionFactoryBean.getObject();
+	}
+
+	@Bean(name = "shopSqlSessionTemplate")
+	public SqlSessionTemplate shopSqlSessionTemplate(@Qualifier("shopSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
+		return new SqlSessionTemplate(sqlSessionFactory);
+	}
+
+}

+ 97 - 0
style24.front/src/main/java/com/style24/front/support/config/TsfRedisSessionConfig.java

@@ -0,0 +1,97 @@
+package com.style24.front.support.config;
+
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.security.jackson2.SecurityJackson2Modules;
+import org.springframework.session.data.redis.config.ConfigureRedisAction;
+import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
+import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
+import org.springframework.session.web.http.CookieSerializer;
+import org.springframework.session.web.http.DefaultCookieSerializer;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Spring Session을 JSON 형태로 Redis에 저장하기 위한 Configuration
+ * 		HttpSession 구현체를 대체하는 서블릿 필터를 생성
+ *
+ * 		@EnableRedisHttpSession 어노테이션은 SpringSessionRepositoryFilter(Filter의 구현체)라는 빈을 생성하는데,
+ * 		yml 파일의 "spring.session.store-type: redis"와 같다.
+ * 		이 필터는 HttpSession의 구현체를 바꾸는 역할을 한다.
+ *
+ *		@EnableRedisHttpSession 어노테이션을 하고
+ *		"spring.data.redis.repositories.enabled: true" 속성을 application.yml 파일에 추가한다.
+ *
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Configuration
+@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
+public class TsfRedisSessionConfig extends AbstractHttpSessionApplicationInitializer implements BeanClassLoaderAware {
+
+	private ClassLoader classLoader;
+
+	@Override
+	public void setBeanClassLoader(ClassLoader classLoader) {
+		this.classLoader = classLoader;
+	}
+
+	/**
+	 * Redis Keyspace Notification 역시 받을 필요가 없도록 처리
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 5. 31.
+	 */
+	@Bean
+	public static ConfigureRedisAction configureRedisAction() {
+		return ConfigureRedisAction.NO_OP;
+	}
+
+	/**
+	 * Serialize 수행 방법을 스프링에 알려준다.
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 5. 29.
+	 */
+	@Bean
+	public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
+		return new GenericJackson2JsonRedisSerializer(objectMapper());
+	}
+
+	/**
+	 * Spring Security 관련 변수 및 매개 변수를 저장하기 위해 SecurityJackson2Modules를 사용
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 5. 29.
+	 */
+	private ObjectMapper objectMapper() {
+		ObjectMapper mapper = new ObjectMapper();
+		mapper.registerModules(SecurityJackson2Modules.getModules(this.classLoader));
+		return mapper;
+	}
+
+	/**
+	 * @EnableRedisHttpSession 어노테이션을 통해 redis-session을 처리할 경우
+	 * 결제, 본인인증 후 콜백시 로그인이 풀림
+	 * SAME_SITE = NONE 그리고 SECURE 를 하기위해서 해당 메소드가 필요함
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 5. 21.
+	 */
+	@Bean
+	public CookieSerializer cookieSerializer() {
+		DefaultCookieSerializer serializer = new DefaultCookieSerializer();
+		serializer.setCookiePath("/");
+		serializer.setUseBase64Encoding(false);
+
+		// SSL인증서 적용 시 주석 제거
+//		serializer.setUseSecureCookie(true);
+//		serializer.setSameSite("NONE");
+
+		return serializer;
+	}
+
+}

+ 82 - 0
style24.front/src/main/java/com/style24/front/support/config/TsfThymeleafConfig.java

@@ -0,0 +1,82 @@
+package com.style24.front.support.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.core.env.Environment;
+import org.springframework.web.servlet.ViewResolver;
+import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect;
+import org.thymeleaf.spring5.SpringTemplateEngine;
+import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
+import org.thymeleaf.spring5.view.ThymeleafViewResolver;
+import org.thymeleaf.templatemode.TemplateMode;
+
+import nz.net.ultraq.thymeleaf.LayoutDialect;
+
+/**
+ * THYMELEAF Configuration
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Configuration
+public class TsfThymeleafConfig {
+
+	@Autowired
+	private ApplicationContext applicationContext;
+
+	@Autowired
+	private Environment env;
+
+	@Bean
+	public SpringResourceTemplateResolver templateResolver() {
+		SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
+		templateResolver.setApplicationContext(applicationContext);
+		templateResolver.setPrefix(env.getProperty("spring.thymeleaf.prefix"));
+		templateResolver.setSuffix(env.getProperty("spring.thymeleaf.suffix"));
+		templateResolver.setTemplateMode(TemplateMode.HTML);
+		templateResolver.setCharacterEncoding("UTF-8");
+		templateResolver.setCacheable(false);
+
+		return templateResolver;
+	}
+
+	@Bean
+	public SpringTemplateEngine templateEngine() {
+		SpringTemplateEngine templateEngine = new SpringTemplateEngine();
+		templateEngine.setTemplateResolver(templateResolver());
+		templateEngine.setEnableSpringELCompiler(true);
+		templateEngine.addDialect(new LayoutDialect());
+		templateEngine.addDialect(new Java8TimeDialect());
+		return templateEngine;
+	}
+
+	@Bean
+	public ViewResolver thymeleafViewResolver() {
+		ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
+
+		viewResolver.setTemplateEngine(templateEngine());
+		viewResolver.setCharacterEncoding("UTF-8");
+		viewResolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
+
+		// below views will not be handled by this reslover.
+		viewResolver.setExcludedViewNames(new String[] {"common/**/*"});
+
+		return viewResolver;
+	}
+
+//	@Bean
+//	public FoLiteDeviceDelegatingViewResolver liteDeviceAwareViewResolver() {
+//		FoLiteDeviceDelegatingViewResolver resolver = new FoLiteDeviceDelegatingViewResolver(thymeleafViewResolver());
+//
+//		resolver.setNormalPrefix("web/");
+//		resolver.setMobilePrefix("mob/");
+//		resolver.setTabletPrefix("mob/");
+//		resolver.setEnableFallback(true);
+//		
+//		return resolver;
+//	}
+
+}

+ 215 - 0
style24.front/src/main/java/com/style24/front/support/config/TsfWebMvcConfig.java

@@ -0,0 +1,215 @@
+package com.style24.front.support.config;
+
+import java.nio.charset.Charset;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.mobile.device.DeviceHandlerMethodArgumentResolver;
+import org.springframework.mobile.device.DeviceResolverHandlerInterceptor;
+import org.springframework.mobile.device.site.SitePreferenceHandlerInterceptor;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.multipart.support.MultipartFilter;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import com.style24.front.support.interceptor.TsfAflinkInterceptor;
+import com.style24.front.support.interceptor.TsfDefaultInterceptor;
+import com.style24.front.support.interceptor.TsfGoodsViewInterceptor;
+import com.style24.front.support.interceptor.TsfLoginCheckInterceptor;
+import com.style24.front.support.interceptor.TsfRememberMeInterceptor;
+import com.style24.front.support.interceptor.TsfReturnUrlInterceptor;
+
+import com.gagaframework.web.core.filter.GagaXssServletFilter;
+import com.gagaframework.web.rest.client.GagaRequestStringTrim;
+
+/**
+ * Web MVC Configuration
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Configuration
+public class TsfWebMvcConfig implements WebMvcConfigurer {
+
+	@Autowired
+	private TsfDefaultInterceptor defaultInterceptor;
+
+	@Autowired
+	private TsfAflinkInterceptor aflinkInterceptor;
+
+	@Autowired
+	private TsfReturnUrlInterceptor returnUrlInterceptor;
+
+	@Autowired
+	private TsfLoginCheckInterceptor loginCheckInterceptor;
+
+	@Autowired
+	private TsfGoodsViewInterceptor goodsViewInterceptor;
+
+	@Autowired
+	private TsfRememberMeInterceptor rememberMeInterceptor;
+
+	@Override
+	public void addInterceptors(InterceptorRegistry registry) {
+		final String[] excludePathPatterns = new String[] {
+			"/", "/index", "/signin/**",
+			"/image/**", "/ux/**",
+			"/error/**", "/data/**",
+			"/login", "/logout"
+		};
+
+		// 디바이스 해석
+		registry.addInterceptor(deviceResolverHandlerInterceptor())
+			.order(0);
+
+		// 선호 사이트 해석
+		registry.addInterceptor(sitePreferenceHandlerInterceptor())
+			.order(1);
+
+		registry.addInterceptor(defaultInterceptor)
+			.addPathPatterns("/**/*")
+			.excludePathPatterns(excludePathPatterns)
+			.order(2);
+
+		registry.addInterceptor(aflinkInterceptor)
+			.addPathPatterns("/**/form")
+			.excludePathPatterns(excludePathPatterns)
+			.order(3);
+
+		registry.addInterceptor(rememberMeInterceptor)
+			.addPathPatterns("/**/form")
+			.excludePathPatterns(excludePathPatterns)
+			.order(4);
+
+		registry.addInterceptor(returnUrlInterceptor)
+			.addPathPatterns("/**/form")
+//				.excludePathPatterns(excludeReturnUrlPathPatterns)
+			.order(10);
+
+		// 로그인 체크
+		registry.addInterceptor(loginCheckInterceptor)
+			.addPathPatterns(new String[] {"/mypage/**", "/order/**", "/planning/reply/create", "/planning/vote/create", "/customer/wish/list/put"})
+			.order(20);
+
+		// 상품뷰 이력 생성
+		registry.addInterceptor(goodsViewInterceptor)
+			.addPathPatterns(new String[] {"/goods/detail/form"})
+			.order(30);
+	}
+
+	/**
+	 * 디바이스 해석
+	 * @return
+	 */
+	@Bean
+	public DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor() {
+		return new DeviceResolverHandlerInterceptor();
+	}
+
+	/**
+	 * 선호 사이트 해석
+	 * @return
+	 */
+	@Bean
+	public SitePreferenceHandlerInterceptor sitePreferenceHandlerInterceptor() {
+		return new SitePreferenceHandlerInterceptor();
+	}
+
+	/**
+	 * Device를 Controller 메소드의 argument로 전달하기 위해 설정
+	 * Controller에서 다음과 같이 사용
+	 *
+	 * 		@RequestMapping("/")
+	 * 		public void home(Device device) {
+	 * 			if (device.isMobile()) {
+	 * 				// Mobile Device
+	 * 			} else if (device.isTablet()) {
+	 * 				// Tablet Device
+	 * 			} else {
+	 * 				// Desktop Device
+	 * 			}
+	 * 		}
+	 * @return
+	 */
+	@Bean
+	public DeviceHandlerMethodArgumentResolver deviceHandlerMethodArgumentResolver() {
+		return new DeviceHandlerMethodArgumentResolver();
+	}
+
+	/**
+	 * Device를 Controller 메소드의 argument로 전달하기 위해 설정
+	 */
+	@Override
+	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
+		argumentResolvers.add(deviceHandlerMethodArgumentResolver());
+	}
+
+	/**
+	 * @RequestBody annotation 이용 시 json data 형식의 모든 문자열의 앞, 뒤 공백을 제거
+	 */
+	@Bean
+	public GagaRequestStringTrim stringTrim() {
+		return new GagaRequestStringTrim();
+	}
+
+	/**
+	 * XSS(Cross Site Script) Prevention Filter
+	 * @return
+	 */
+	@SuppressWarnings({"rawtypes", "unchecked"})
+	@Bean
+	public FilterRegistrationBean xssFilterRegistrationBean() {
+		FilterRegistrationBean bean = new FilterRegistrationBean();
+		bean.setFilter(new GagaXssServletFilter());
+		bean.setOrder(2);
+		bean.addUrlPatterns("/*");
+		return bean;
+	}
+
+	/**
+	 * Multipart Filter
+	 * 		파일 업로드 구현 시 MultipartRequest를 처리함에 따라 해당 부분은 불필요 하나,
+	 * 		com.oreilly.servlet.MultipartRequest 같은 걸 이용 시
+	 * 		"Corrupt form data: premature ending" exception이 발생한다.
+	 * 		이를 해결하고자 Filter를 구성
+	 * @return
+	 */
+	@SuppressWarnings({"rawtypes", "unchecked"})
+	@Bean
+	public FilterRegistrationBean springMultipartRegistrationBean() {
+		FilterRegistrationBean bean = new FilterRegistrationBean();
+		bean.setName("springMultipartResolver");
+		bean.setFilter(new MultipartFilter());
+		bean.setOrder(1);
+		bean.addUrlPatterns(new String[] {
+			"/common/file/upload/**",
+			"/common/files/upload/**"
+		});
+		return bean;
+	}
+
+	/**
+	 * API 호출을 위한 RestTemplate 설정
+	 * @return
+	 */
+	@Bean
+	public RestTemplate restTemplate() {
+		HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
+		factory.setConnectTimeout(300000);
+		factory.setReadTimeout(300000);
+
+		RestTemplate restTemplate = new RestTemplate(factory);
+
+		// Convert the message to UTF-8
+		restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
+
+		return restTemplate;
+	}
+
+}

+ 306 - 0
style24.front/src/main/java/com/style24/front/support/controller/TsfBaseController.java

@@ -0,0 +1,306 @@
+package com.style24.front.support.controller;
+
+import java.sql.SQLException;
+import java.sql.SQLRecoverableException;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.apache.ibatis.binding.BindingException;
+import org.springframework.beans.TypeMismatchException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.propertyeditors.StringTrimmerEditor;
+import org.springframework.core.env.Environment;
+import org.springframework.mobile.device.Device;
+import org.springframework.mobile.device.DeviceUtils;
+import org.springframework.mobile.device.site.SitePreference;
+import org.springframework.mobile.device.site.SitePreferenceUtils;
+import org.springframework.mobile.device.util.ResolverUtils;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.util.Assert;
+import org.springframework.validation.FieldError;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.MissingServletRequestParameterException;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.InitBinder;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.style24.core.support.message.TscMessageByLocale;
+import com.style24.front.support.env.TsfConstants;
+import com.style24.front.support.security.session.TsfSession;
+import com.style24.persistence.domain.Login;
+
+import lombok.extern.slf4j.Slf4j;
+
+import com.gagaframework.web.rest.exception.GagaRestException;
+import com.gagaframework.web.rest.server.GagaResponse;
+import com.gagaframework.web.rest.server.GagaResponseStatus;
+import com.gagaframework.web.util.GagaCookieUtil;
+
+/**
+ * Controller Advice
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@ControllerAdvice
+@Slf4j
+public class TsfBaseController {
+
+	@Autowired
+	private Environment env;
+
+	@Autowired
+	private TscMessageByLocale message;
+
+	/**
+	 * Json data 형식 외의 모든 문자열의 앞, 뒤 공백 제거
+	 * Json data 형식의 모든 문자열의 앞, 뒤 공백 제거 시는 AdmStringTrim에서 처리 (@RequestBody annotation 이용 시)
+	 * @param binder
+	 */
+	@InitBinder
+	public void initBinder(WebDataBinder binder) {
+		StringTrimmerEditor stringtrimmer = new StringTrimmerEditor(true);
+//		stringtrimmer.getAsText().replaceAll("<", "&lt;").replaceAll(">", "&gt;");
+		binder.registerCustomEditor(String.class, stringtrimmer);
+	}
+
+	@ModelAttribute("env")
+	public Environment getEnvironment() {
+		return env;
+	}
+
+	/**
+	 * Get 세션 정보
+	 *
+	 * 1.View 단에서 사용법
+	 * 		th:value="${sessionInfo.userId}"
+	 * 2.Java 단에서 사용법
+	 * 		super.getSession().getUserId()
+	 * @return
+	 */
+	@ModelAttribute("sessionInfo")
+	public Login getSession() {
+		return TsfSession.getInfo();
+	}
+
+	/**
+	 * Get 프론트구분(P:PC, M:모바일)
+	 *
+	 * 1.View 단에서 사용법
+	 * 		th:value="${frontGb}"
+	 * 2.Java 단에서 사용법
+	 * 		super.getFrontGb()
+	 * @return
+	 */
+	@ModelAttribute("frontGb")
+	public String getFrontGb() {
+		return TsfSession.getFrontGb();
+	}
+
+	@ModelAttribute("snsLoginPrefix")
+	public String getSnsLoginPrefix() {
+		return TsfConstants.SNSLOGIN_PREFIX;
+	}
+
+	@ModelAttribute("osType")
+	public String getOsType() {
+		return TsfSession.getAttribute("osType");
+	}
+
+	/**
+	 * APP인지 아닌지 (true/false)
+	 * @return
+	 */
+	@ModelAttribute("isApp")
+	public String isApp() {
+		return TsfSession.getAttribute("isApp");
+	}
+
+	/**
+	 * APP명
+	 * @return
+	 */
+	@ModelAttribute("appName")
+	public String getAppName() {
+		return TsfSession.getAttribute("appName");
+	}
+
+	/**
+	 * APP버전
+	 * @return
+	 */
+	@ModelAttribute("appVersion")
+	public String getAppVersion() {
+		return TsfSession.getAttribute("appVersion");
+	}
+
+	/**
+	 * APP의 디바이스토큰
+	 * @return
+	 */
+	@ModelAttribute("deviceToken")
+	public String getDeviceToken() {
+		return TsfSession.getAttribute("deviceToken");
+	}
+
+	@ResponseBody
+	public GagaResponse ok() {
+		return GagaResponse.of(GagaResponseStatus.SUCCESS.getCode(), "SUCCESS");
+	}
+
+	@ResponseBody
+	public GagaResponse ok(String message) {
+		return GagaResponse.of(GagaResponseStatus.SUCCESS.getCode(), message);
+	}
+
+	@ResponseBody
+	public GagaResponse error(String message) {
+		return GagaResponse.error(GagaResponseStatus.FAIL.getCode(), message);
+	}
+
+	@ExceptionHandler(AccessDeniedException.class)
+	@ResponseBody
+	public GagaResponse handleForbidden(Exception e) {
+		return GagaResponse.error(GagaResponseStatus.FORBIDDEN.getCode(), e.getMessage());
+	}
+
+	@ExceptionHandler(TypeMismatchException.class)
+	@ResponseBody
+	public GagaResponse handleBadRequestException(Exception e) {
+		errorLogging(e);
+		return GagaResponse.error(GagaResponseStatus.BAD_REQUEST.getCode(), e.getMessage());
+	}
+
+	@ExceptionHandler(MissingServletRequestParameterException.class)
+	@ResponseBody
+	public GagaResponse handleRequestParameterException(MissingServletRequestParameterException e) {
+		errorLogging(e);
+		return GagaResponse.error(GagaResponseStatus.BAD_REQUEST.getCode(), e.getMessage());
+	}
+
+	/**
+	 * HttpRequestMethodNotSupportedException Handler
+	 * @param e - HttpRequestMethodNotSupportedException, SQLRecoverableException
+	 * @return forwarding URI
+	 */
+	@ExceptionHandler({HttpRequestMethodNotSupportedException.class, SQLRecoverableException.class})
+	public String handleException(HttpRequestMethodNotSupportedException e) {
+		return "forward:/error/500";
+	}
+
+	@ExceptionHandler(Throwable.class)
+	@ResponseBody
+	public GagaResponse handleException(Throwable throwable) {
+		errorLogging(throwable);
+
+		Throwable rootCause = ExceptionUtils.getRootCause(throwable);
+
+		if (rootCause != null) {
+			throwable = rootCause;
+		}
+
+		if (throwable instanceof SQLException || throwable instanceof BindingException ||
+			throwable instanceof JsonMappingException) {
+			String message = String.format("데이터 처리중 에러가 발생하였습니다. 시스템 관리자에게 문의하세요.");
+			return GagaResponse.error(GagaResponseStatus.INTERNAL_SERVER_ERROR.getCode(), message);
+		}
+
+		return GagaResponse.error(GagaResponseStatus.INTERNAL_SERVER_ERROR.getCode(), throwable.getMessage());
+	}
+
+	private void errorLogging(Throwable throwable) {
+		if (log.isErrorEnabled()) {
+			Throwable rootCause = ExceptionUtils.getRootCause(throwable);
+
+			if (rootCause != null) {
+				throwable = rootCause;
+			}
+
+			if (throwable.getMessage() != null) {
+				log.error(throwable.getMessage(), throwable);
+			} else {
+				log.error("ERROR", throwable);
+			}
+		}
+	}
+
+	@ExceptionHandler(MethodArgumentNotValidException.class)
+	@ResponseBody
+	public Object processValidationError(MethodArgumentNotValidException ex) {
+		FieldError fieldError = ex.getBindingResult().getFieldErrors().get(0);
+		GagaResponse error = GagaResponse.error(GagaResponseStatus.INTERNAL_SERVER_ERROR.getCode(), fieldError.getDefaultMessage());
+		error.getError().setRequiredKey(fieldError.getField());
+		return error;
+	}
+
+	/**
+	 * Custom Validation using group class
+	 * @param domain - Domain to validate
+	 * @param groupClass - Group class to validate
+	 */
+	public void customValidation(Object clazz, Class<?> groupClass) {
+		Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
+
+		Set<ConstraintViolation<Object>> violations = validator.validate(clazz, groupClass);
+
+		for (ConstraintViolation<?> violation : violations) {
+			String validationMessage = violation.getMessage();
+			validationMessage = StringUtils.removeStart(validationMessage, "{");
+			validationMessage = StringUtils.removeEnd(validationMessage, "}");
+			throw new GagaRestException(GagaResponseStatus.FAIL.getCode(), message.getMessage(validationMessage));
+		}
+	}
+
+	public String getDeviceViewName(String viewName) {
+		RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
+		Assert.isInstanceOf(ServletRequestAttributes.class, attrs);
+		HttpServletRequest request = ((ServletRequestAttributes)attrs).getRequest();
+
+		Device device = DeviceUtils.getCurrentDevice(request);
+		SitePreference sitePreference = SitePreferenceUtils.getCurrentSitePreference(request);
+		String resolvedViewName = viewName;
+
+		if (ResolverUtils.isNormal(device, sitePreference)) {
+			resolvedViewName = "web/" + viewName + "Web";
+		} else if (ResolverUtils.isMobile(device, sitePreference)) {
+			if (GagaCookieUtil.getCookie(request, TsfConstants.CK_PREFIX + "_site_preference").equals("normal")) {
+				resolvedViewName = "web/" + viewName + "Web";
+			} else {
+				resolvedViewName = "mob/" + viewName + "Mob";
+			}
+		} else if (ResolverUtils.isTablet(device, sitePreference)) {
+			if (GagaCookieUtil.getCookie(request, TsfConstants.CK_PREFIX + "_site_preference").equals("normal")) {
+				resolvedViewName = "web/" + viewName + "Web";
+			} else {
+				resolvedViewName = "mob/" + viewName + "Mob";
+			}
+		}
+
+		log.debug("resolvedViewName: {}", resolvedViewName);
+
+		return stripTrailingSlash(resolvedViewName);
+	}
+
+	private String stripTrailingSlash(String viewName) {
+		if (viewName.endsWith("//")) {
+			return viewName.substring(0, viewName.length() - 1);
+		}
+
+		return viewName;
+	}
+
+}

+ 32 - 0
style24.front/src/main/java/com/style24/front/support/controller/TsfCustomErrorController.java

@@ -0,0 +1,32 @@
+package com.style24.front.support.controller;
+
+import org.springframework.boot.web.servlet.error.ErrorController;
+import org.springframework.stereotype.Controller;
+
+/**
+ * Custom Error Controller
+ * 		1. Disabling the Whitelabel Error Page
+ * 			- application.yml 파일에 다음을 추가
+ * 				server.error.whitelabel.enabled=false
+ * 			
+ * 			or
+ * 
+ * 			- ~Application.java 파일에 다음을 추가
+ * 				@EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class})
+ * 
+ * 		2. Create a Custom ErrorController
+ * 			you have to create a class that implements the ErrorController interface.
+ * 			And overrides its getErrorPath() to return a custom path to call when an error occurred.
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Controller
+public class TsfCustomErrorController implements ErrorController {
+
+	@Override
+	public String getErrorPath() {
+		return "/error";
+	}
+
+}

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

@@ -0,0 +1,52 @@
+package com.style24.front.support.env;
+
+/**
+ * 변경될 소지가 있는 변수 값을 정의
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+public class TsfConstants {
+
+	// 쿠키 Prefix
+	public static final String CK_PREFIX = "st24ck";
+
+	// SNS로그인
+	public static final String SNSLOGIN_PREFIX = "SNSLOGIN!@#-";
+
+	// 자동로그인 RememberMe
+	public static final String REMEMBER_ME_KEY = "style24";
+	public static final String REMEMBER_ME_PARAMETER = "rememberMe";
+	public static final int REMEMBER_ME_LIMIT = 60 * 60 * 24 * 7; // 7일간유효
+
+//	// 카테고리구분
+//	public enum CATE_GB {
+//		BYITEM("101"), BYBRAND("102"), BYOUTLET("103");
+//
+//		private String value;
+//
+//		private CATE_GB(String value) {
+//			this.value = value;
+//		}
+//
+//		public String value() {
+//			return value;
+//		}
+//	}
+//
+//	// 대카테고리
+//	public enum TCATE_CD {
+//		WOMEN("100"), MEN("200"), LIFE("300"), OUTLET("1647");
+//
+//		private String value;
+//
+//		private TCATE_CD(String value) {
+//			this.value = value;
+//		}
+//
+//		public String value() {
+//			return value;
+//		}
+//	}
+
+}

+ 22 - 0
style24.front/src/main/java/com/style24/front/support/exception/TsfDormantAccountException.java

@@ -0,0 +1,22 @@
+package com.style24.front.support.exception;
+
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ * 휴면회원 로그인 시도시 발생하는 예외
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@SuppressWarnings("serial")
+public class TsfDormantAccountException extends AuthenticationException {
+
+	public TsfDormantAccountException(String msg) {
+		super(msg);
+	}
+
+	public TsfDormantAccountException(String msg, Throwable t) {
+		super(msg, t);
+	}
+
+}

+ 22 - 0
style24.front/src/main/java/com/style24/front/support/exception/TsfEmailDuplicationException.java

@@ -0,0 +1,22 @@
+package com.style24.front.support.exception;
+
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ * SNS용 이메일 중복 시 발생하는 예외
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@SuppressWarnings("serial")
+public class TsfEmailDuplicationException extends AuthenticationException {
+
+	public TsfEmailDuplicationException(String msg) {
+		super(msg);
+	}
+
+	public TsfEmailDuplicationException(String msg, Throwable t) {
+		super(msg, t);
+	}
+
+}

+ 22 - 0
style24.front/src/main/java/com/style24/front/support/exception/TsfLockedAccountException.java

@@ -0,0 +1,22 @@
+package com.style24.front.support.exception;
+
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ * 로그인 시에 비밀번호 5회 이상 틀려서 계정이 잠기는 경우 발생시키는 예외
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@SuppressWarnings("serial")
+public class TsfLockedAccountException extends AuthenticationException {
+
+	public TsfLockedAccountException(String msg) {
+		super(msg);
+	}
+
+	public TsfLockedAccountException(String msg, Throwable t) {
+		super(msg, t);
+	}
+
+}

+ 22 - 0
style24.front/src/main/java/com/style24/front/support/exception/TsfSecedeAccountException.java

@@ -0,0 +1,22 @@
+package com.style24.front.support.exception;
+
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ * 탈퇴회원 로그인 시도시 발생하는 예외
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@SuppressWarnings("serial")
+public class TsfSecedeAccountException extends AuthenticationException {
+
+	public TsfSecedeAccountException(String msg) {
+		super(msg);
+	}
+
+	public TsfSecedeAccountException(String msg, Throwable t) {
+		super(msg, t);
+	}
+
+}

+ 22 - 0
style24.front/src/main/java/com/style24/front/support/exception/TsfSessionExpiredException.java

@@ -0,0 +1,22 @@
+package com.style24.front.support.exception;
+
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ * 세션 만료 시 발생하는 예외
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@SuppressWarnings("serial")
+public class TsfSessionExpiredException extends AuthenticationException {
+
+	public TsfSessionExpiredException(String msg) {
+		super(msg);
+	}
+
+	public TsfSessionExpiredException(String msg, Throwable t) {
+		super(msg, t);
+	}
+
+}

+ 53 - 0
style24.front/src/main/java/com/style24/front/support/interceptor/TsfAflinkInterceptor.java

@@ -0,0 +1,53 @@
+package com.style24.front.support.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 제휴링크 Interceptor
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Component
+@Slf4j
+public class TsfAflinkInterceptor extends HandlerInterceptorAdapter {
+
+//	@Autowired
+//	private WfoCommonService commonService;
+
+	@Override
+	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+//		// Parameter
+//		GagaMap params = GagaParameterUtil.getParameterMap(request);
+//
+//		// 제휴링크코드
+//		String afLinkCd = params.getString("afLinkCd");
+//
+//		if (StringUtils.isNotBlank(afLinkCd)) {
+//			if (!afLinkCd.equals(FoSession.getAttribute("afLinkCd"))) {
+//				FoSession.setAttribute("afLinkCd", afLinkCd);
+//			}
+//		} else {
+//			afLinkCd = "AF001"; // STYLE24
+//			FoSession.setAttribute("afLinkCd", afLinkCd);
+//		}
+//
+//		commonService.createInflowHistory(afLinkCd);
+
+		return super.preHandle(request, response, handler);
+	}
+
+	@Override
+	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+		ModelAndView modelAndView) throws Exception {
+		// Do nothing
+	}
+
+}

+ 146 - 0
style24.front/src/main/java/com/style24/front/support/interceptor/TsfDefaultInterceptor.java

@@ -0,0 +1,146 @@
+package com.style24.front.support.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import com.style24.front.support.security.session.TsfSession;
+
+import lombok.extern.slf4j.Slf4j;
+
+import com.gagaframework.web.util.GagaStringUtil;
+
+/**
+ * 모든 Request에 대한 선처리
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Component
+@Slf4j
+public class TsfDefaultInterceptor extends HandlerInterceptorAdapter {
+
+	private static final String APP_PREFIX = "style24@";
+
+//	@Value("${has-ssl}")
+//	private String hasSsl;
+
+//	@Autowired
+//	private WfoPolicyService policyService;
+
+	@Override
+	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+		log.info("request.isSecure(): [{}]", request.isSecure());
+		log.info("request.getServerName(): [{}]", request.getServerName());
+		log.info("request.getRequestURI(): [{}]", request.getRequestURI());
+		log.info("request.getRequestURL(): [{}]", request.getRequestURL());
+		log.info("request.getQueryString(): [{}]", request.getQueryString());
+
+//		// 설정된 HTTPS 페이지 확인
+//		String requestUrl = request.getRequestURL().toString();
+//		boolean bHttpsPage = this.isHttpsPage(isSslServer, requestUrl, request.getRequestURI());
+//		log.debug("bHttpsPage: [{}]", bHttpsPage);
+//		if (bHttpsPage) {
+//			requestUrl = requestUrl.replace("http://", "https://");
+//			log.debug("requestUrl: [{}]", requestUrl);
+//			response.sendRedirect(requestUrl);
+//			return false;
+//		}
+//
+//		// Set Meta Info.
+//		policyService.setMetaInfo(FoConstants.SITE_CD);
+//
+//		String queryString = GagaStringUtil.convertParameterToQueryString(request.getParameterMap());
+//		if (StringUtils.isNotBlank(queryString))
+//			queryString = "?" + queryString;
+//		log.debug("queryString: [{}]", queryString);
+//
+//		// Set APP Info.
+//		this.setAppInfo(request);
+
+		return super.preHandle(request, response, handler);
+	}
+
+	@Override
+	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+		ModelAndView modelAndView) throws Exception {
+		// Do nothing
+	}
+
+	/**
+	 * HTTPS로 적용할 페이지인지 체크
+	 * @param requestUrl - Request URL
+	 * @param requestUri - Request URI
+	 * @return true/false
+	 * @author gagamel
+	 * @since 2020. 2. 3
+	 */
+	private boolean isHttpsPage(boolean isSslServer, String requestUrl, String requestUri) {
+		// https 이어야 하는 화면의 URL 목록
+		final String[] httpsUrls = {"/customer", "/mypage", "/order"};
+
+		// 1.설정된 HTTPS URL 값이 없거나
+		// 2.SSL이 적용되지 않은 서버이거나
+		// 3.https로 시작되었거나
+		// 4.Ajax 호출인 경우
+		if (!isSslServer || requestUrl.startsWith("https://"))
+			return false;
+
+		for (String httpsUrl : httpsUrls) {
+			log.debug("requestUri: {}, httpsUrl: {}", requestUri, httpsUrl);
+			if (requestUri.startsWith(httpsUrl)) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * UserAgent 값을 이용한 APP 설정
+	 * 		APP에서 접속 시 다음과 같이 UserAgent 값이 들어옴.
+	 * 		예) Mozilla/5.0 (Linux; Android 10; Android SDK built for x86 Build/QSR1.190920.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.185 Mobile Safari/537.36,wivismall@WIVIS_1.0.5
+	 * @param request
+	 */
+	private void setAppInfo(HttpServletRequest request) {
+		// User Agent
+		String userAgent = request.getHeader("User-Agent").toLowerCase();
+		log.debug("userAgent: [{}]", userAgent);
+
+		if (userAgent.indexOf("iphone") > -1 ||
+			userAgent.indexOf("ipod") > -1 ||
+			userAgent.indexOf("ipad") > -1) {
+			TsfSession.setAttribute("osType", "I");
+		} else if (userAgent.indexOf("android") > -1) {
+			TsfSession.setAttribute("osType", "A");
+		}
+
+		int offset = userAgent.lastIndexOf(APP_PREFIX);
+
+		if (offset > 0) {
+			TsfSession.setAttribute("isApp", "true");
+
+			String strAppInfo = GagaStringUtil.replace(userAgent.substring(offset), APP_PREFIX, "");
+			String[] arrApp = strAppInfo.split("_");
+			if (arrApp != null) {
+				int cnt = 0;
+				for (String oneData : arrApp) {
+					if (cnt == 0) {
+						TsfSession.setAttribute("appName", oneData);
+					} else if (cnt == 1) {
+						TsfSession.setAttribute("appVersion", oneData);
+//					} else if (cnt == 2) {
+//						WfoSession.setAttribute("deviceToken", oneData);
+					}
+					cnt++;
+				}
+			}
+		} else {
+			TsfSession.setAttribute("isApp", "false");
+		}
+	}
+
+}

+ 63 - 0
style24.front/src/main/java/com/style24/front/support/interceptor/TsfGoodsViewInterceptor.java

@@ -0,0 +1,63 @@
+package com.style24.front.support.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 상품뷰 Interceptor
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Component
+@Slf4j
+public class TsfGoodsViewInterceptor extends HandlerInterceptorAdapter {
+
+//	@Value("${has-ssl}")
+//	private String hasSsl;
+
+//	@Autowired
+//	private WfoGoodsService goodsService;
+
+	@Override
+	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+//		// Parameter
+//		GagaMap params = GagaParameterUtil.getParameterMap(request);
+//
+//		// 상품뷰이력 생성
+//		goodsService.createGoodsViewHistory(params.getString("goodsCd"), params.getString("ithrCd"));
+//
+//		// SSL Server
+//		boolean isSslServer = Boolean.parseBoolean(hasSsl);
+//		log.debug("isSslServer: [{}]", isSslServer);
+//
+//		if (isSslServer) {
+//			// 상품유입경로 쿠키 설정
+//			GagaCookieUtil.setSecureCookie(response, FoConstants.CK_PREFIX + "_ithrCd", params.getString("ithrCd"), -1);
+//
+//			// 컨텐츠위치코드 쿠키 설정
+//			GagaCookieUtil.setSecureCookie(response, FoConstants.CK_PREFIX + "_contentsLoc", params.getString("contentsLoc"), -1);
+//		} else {
+//			// 상품유입경로 쿠키 설정
+//			GagaCookieUtil.setCookie(response, FoConstants.CK_PREFIX + "_ithrCd", params.getString("ithrCd"), -1);
+//
+//			// 컨텐츠위치코드 쿠키 설정
+//			GagaCookieUtil.setCookie(response, FoConstants.CK_PREFIX + "_contentsLoc", params.getString("contentsLoc"), -1);
+//		}
+
+		return super.preHandle(request, response, handler);
+	}
+
+	@Override
+	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+		ModelAndView modelAndView) throws Exception {
+		// Do nothing
+	}
+
+}

+ 61 - 0
style24.front/src/main/java/com/style24/front/support/interceptor/TsfLoginCheckInterceptor.java

@@ -0,0 +1,61 @@
+package com.style24.front.support.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 로그인 체크 Interceptor
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Component
+@Slf4j
+public class TsfLoginCheckInterceptor extends HandlerInterceptorAdapter {
+
+//	@Value("${has-ssl}")
+//	private String hasSsl;
+
+	@Override
+	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+//		log.info("request.getServerName(): [{}]", request.getServerName());
+//
+//		// 로그인 되어 있지 않으면
+//		if (!FoSession.isLogin()) {
+//			boolean isAjaxCall = Boolean.parseBoolean(request.getHeader("AJAX"));
+//			log.info("isAjaxCall: {}", isAjaxCall);
+//
+//			if (isAjaxCall) { // ajax call
+//				// Ajax call은 status 세팅 후 ajax.status == 901일 경우 로그인 페이지로 이동 처리
+//				response.setStatus(901); // No Session
+//			} else { // Submit
+//				// SSL Server
+//				boolean isSslServer = Boolean.parseBoolean(hasSsl);
+//				log.info("isSslServer: [{}]", isSslServer);
+//
+//				String loginUrl = isSslServer ? "https://" + request.getServerName() + "/signin" : "/signin";
+//				log.info("loginUrl: [{}]", loginUrl);
+//
+//				response.sendRedirect(loginUrl);
+////				response.flushBuffer();
+//			}
+//
+//			return false;
+//		}
+
+		return super.preHandle(request, response, handler);
+	}
+
+	@Override
+	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+		ModelAndView modelAndView) throws Exception {
+		// Do nothing
+	}
+
+}

+ 97 - 0
style24.front/src/main/java/com/style24/front/support/interceptor/TsfRememberMeInterceptor.java

@@ -0,0 +1,97 @@
+package com.style24.front.support.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import com.style24.front.biz.service.TsfLoginService;
+import com.style24.front.support.security.TsfLoginDetails;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 자동로그인 RememberMe 선처리
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Component
+@Slf4j
+public class TsfRememberMeInterceptor extends HandlerInterceptorAdapter {
+
+	@Autowired
+	private TsfLoginService loginService;
+
+	@Override
+	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+//		// RememberMe Cookie
+//		String ckRememberMe = GagaCookieUtil.getCookie(request, FoConstants.CK_PREFIX + "_remember-me");
+//
+//		if (!FoSession.isLogin() && StringUtils.isNotBlank(ckRememberMe)) {
+//			// 로그인유지토큰 조회
+//			WfoPersistentToken token = loginService.getPersistentToken(ckRememberMe);
+//
+//			if (token != null) {
+//				WfoCustomer customer = new WfoCustomer();
+//				customer.setCustId(token.getCustId());
+//				WfoCustomer loginInfo = loginService.getLoginCheckInfo(customer);
+//
+//				// 로그인 성공 시 로그인실패수가 0보다 크면 로그인실패수 reset
+//				int failCnt = loginService.getLoginFailCount(token.getCustNo());
+//				if (failCnt > 0) {
+//					loginService.createLoginFail(token.getCustId(), "N");
+//				}
+//
+//				// 최종로그인일시 Update
+//				loginService.updateLastLoginDate(token.getCustNo());
+//
+//				// 로그인이력 생성
+//				loginService.createLoginHistory(token.getCustNo());
+//
+//				// 세션 생성
+//				List<SimpleGrantedAuthority> authorities = new ArrayList<>();
+//				authorities.add(new SimpleGrantedAuthority(loginInfo.getCustGb()));
+//				this.createSession(request, new FoLoginDetails(loginInfo, authorities));
+//
+//				Date expiry = new Date(System.currentTimeMillis() + (1000 * FoConstants.REMEMBER_ME_LIMIT));
+//
+////				log.debug("String to MD5: {}", token.getCustNo() + ":" + GagaDateUtil.getDate(expiry, "yyyyMMddHHmmss") + ":" + WfoSession.getSessionId());
+//				String md5HexToken = GagaCryptoUtil.encryptMD5(token.getCustNo() + ":" + GagaDateUtil.getDate(expiry, "yyyyMMddHHmmss") + ":" + FoSession.getSessionId());
+////				log.debug(md5HexToken);
+//				String remembermeToken = Base64.getEncoder().encodeToString((FoConstants.REMEMBER_ME_KEY + ":" + md5HexToken).getBytes());
+////				log.debug("remembermeToken: {}", remembermeToken);
+//
+//				// RememberMe 쿠키 생성
+//				GagaCookieUtil.setSecureCookie(response, FoConstants.CK_PREFIX + "_remember-me", remembermeToken, FoConstants.REMEMBER_ME_LIMIT);
+//
+//				// 로그인유지토큰 갱신
+//				loginService.updatePersistentToken(token.getCustNo(), token.getRemembermeToken(), remembermeToken, expiry);
+//			}
+//		}
+
+		return super.preHandle(request, response, handler);
+	}
+
+	@Override
+	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+		ModelAndView modelAndView) throws Exception {
+		// Do nothing
+	}
+
+	/**
+	 * Session 생성
+	 * @param request - HttpServletRequest
+	 * @param loginDetails - 로그인 상세 정보
+	 */
+	private void createSession(HttpServletRequest request, TsfLoginDetails loginDetails) {
+		HttpSession session = request.getSession(true);
+		session.setMaxInactiveInterval(1800);
+		session.setAttribute("session", loginDetails);
+	}
+
+}

+ 58 - 0
style24.front/src/main/java/com/style24/front/support/interceptor/TsfReturnUrlInterceptor.java

@@ -0,0 +1,58 @@
+package com.style24.front.support.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Return URL Interceptor
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Component
+@Slf4j
+public class TsfReturnUrlInterceptor extends HandlerInterceptorAdapter {
+
+//	@Value("${has-ssl}")
+//	private String hasSsl;
+
+	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+//		log.info("request.isSecure(): [{}]", request.isSecure());
+//		log.info("request.getServerName(): [{}]", request.getServerName());
+//		log.info("request.getRequestURI(): [{}]", request.getRequestURI());
+//		log.info("request.getRequestURL(): [{}]", request.getRequestURL());
+//		log.info("request.getQueryString(): [{}]", request.getQueryString());
+//
+//		String queryString = GagaStringUtil.convertParameterToQueryString(request.getParameterMap());
+//		if (StringUtils.isNotBlank(queryString))
+//			queryString = "?" + queryString;
+//		log.info("queryString: [{}]", queryString);
+//
+//		String returnUrl = request.getRequestURL() + queryString;
+//		log.info("returnUrl: [{}]", returnUrl);
+//
+//		// SSL Server
+//		boolean isSslServer = Boolean.parseBoolean(hasSsl);
+//		log.debug("isSslServer: [{}]", isSslServer);
+//
+//		if (isSslServer) {
+//			GagaCookieUtil.setSecureCookie(response, FoConstants.CK_PREFIX + "_return_url", returnUrl, -1);
+//		} else {
+//			GagaCookieUtil.setCookie(response, FoConstants.CK_PREFIX + "_return_url", returnUrl, -1);
+//		}
+
+		return super.preHandle(request, response, handler);
+	}
+
+	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+		ModelAndView modelAndView) throws Exception {
+		// Do nothing
+	}
+
+}

+ 128 - 0
style24.front/src/main/java/com/style24/front/support/security/TsfAuthenticationProvider.java

@@ -0,0 +1,128 @@
+package com.style24.front.support.security;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Component;
+
+import com.style24.core.support.message.TscMessageByLocale;
+import com.style24.front.biz.service.TsfLoginService;
+import com.style24.front.support.env.TsfConstants;
+import com.style24.front.support.exception.TsfDormantAccountException;
+import com.style24.front.support.exception.TsfEmailDuplicationException;
+import com.style24.front.support.exception.TsfLockedAccountException;
+import com.style24.front.support.exception.TsfSecedeAccountException;
+import com.style24.persistence.domain.Login;
+
+import lombok.extern.slf4j.Slf4j;
+
+import com.gagaframework.web.security.GagaPasswordEncoder;
+import com.gagaframework.web.util.GagaStringUtil;
+
+/**
+ * 로그인 인증 처리
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Component
+@Slf4j
+public class TsfAuthenticationProvider implements AuthenticationProvider {
+
+	@Autowired
+	private TsfLoginService loginService;
+
+	@Autowired
+	private TscMessageByLocale message;
+
+	@Autowired
+	private GagaPasswordEncoder passwordEncoder;
+
+	@Override
+	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+		String loginId = authentication.getName();
+
+		// 일반로그인일 때는 비밀번호이고 SNS로그인일 때는 이메일 정보임
+		String passwd = authentication.getCredentials().toString();
+		log.info("loginId: {}, passwd: {}", loginId, passwd);
+
+		// 로그인 정보
+		Login loginParam = new Login();
+
+		// SNS로그인이면
+		if (loginId.startsWith(TsfConstants.SNSLOGIN_PREFIX)) {
+			loginParam.setCustId(GagaStringUtil.replace(loginId, TsfConstants.SNSLOGIN_PREFIX, ""));
+			loginParam.setSnsType(loginId.substring(loginId.indexOf("-", 0) + 1, loginId.lastIndexOf("-") - 1));
+			loginParam.setEmail(passwd); // SNS로그인이면 이메일 정보
+		} else { // 일반로그인
+			loginParam.setCustId(loginId);
+		}
+
+		// 로그인정보 조회
+		Login loginInfo = loginService.getLoginCheckInfo(loginParam);
+		log.debug("loginInfo: {}", loginInfo);
+
+		if (loginInfo == null) {
+			log.debug(String.format("Customer with ID=%s was not found!", loginId));
+			throw new UsernameNotFoundException(message.getMessage("LOGN_0001"));
+		}
+
+		// 로그인 실패누적건수가 5이면
+		if (loginInfo.getLoginFailCnt() == 5) {
+//			throw new BadCredentialsException(message.getMessage("LOGN_0005"));
+			throw new TsfLockedAccountException(message.getMessage("LOGN_0005"));
+		}
+
+		/// SNS로그인이 아닌 일반로그인 이면
+		if (!loginId.startsWith(TsfConstants.SNSLOGIN_PREFIX)) {
+			log.debug("encoded pwd: {}", passwordEncoder.encode(passwd));
+			boolean isMatch = passwordEncoder.matches(passwd, loginInfo.getPasswd());
+			log.debug("isMatch: {}", isMatch);
+
+			if (!isMatch) {
+//				// 로그인 실패 남기기
+//				customerService.createLoginFail(loginInfo.getCustId(), "Y");
+				throw new BadCredentialsException(message.getMessage("LOGN_0002"));
+			}
+		} else {
+			// SNS로그인 시 로그인ID 값은 이메일로 처리했으므로
+			// loginId 값과 회원정보의 이메일 값을 비교해서 동일하면
+			if (StringUtils.isBlank(loginInfo.getSnsType()) && passwd.equals(loginInfo.getEmail())) {
+				throw new TsfEmailDuplicationException(message.getMessage("LOGN_0008"));
+			}
+		}
+
+		if (loginInfo.getCustStat().equals("20")) { // 휴면회원
+//			WfoSession.setDormantMemberNo(request, loginInfo.getCustId());
+			throw new TsfDormantAccountException(message.getMessage("LOGN_0006"));
+		} else if (loginInfo.getCustStat().equals("30")) { // 탈퇴회원
+			throw new TsfSecedeAccountException(message.getMessage("LOGN_0007"));
+		}
+
+		// 권한 설정
+		List<SimpleGrantedAuthority> authorities = new ArrayList<>();
+		authorities.add(new SimpleGrantedAuthority(loginInfo.getCustGb()));
+
+		// 인증 토큰 생성
+		UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
+			loginInfo.getCustNo(), loginInfo.getPasswd(), authorities);
+		authToken.setDetails(new TsfLoginDetails(loginInfo, authorities));
+
+		return authToken;
+	}
+
+	@Override
+	public boolean supports(Class<?> authentication) {
+		return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
+	}
+
+}

+ 86 - 0
style24.front/src/main/java/com/style24/front/support/security/TsfLoginDetails.java

@@ -0,0 +1,86 @@
+package com.style24.front.support.security;
+
+import java.util.Collection;
+
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.style24.persistence.domain.Login;
+
+/**
+ * 로그인 상세 정보
+ * 		@JsonSerialize 애노테이션을 지정해야 세션을 레디스에 저장할 수 있다.
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@SuppressWarnings("serial")
+@JsonSerialize
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class TsfLoginDetails implements UserDetails {
+
+	private Login loginInfo = null;
+
+	private Collection<SimpleGrantedAuthority> authorities = null;
+
+	// 세션을 JSON 형식으로 레디스에 저장하려면 기본 생성자를 반드시 명시해야 한다
+	public TsfLoginDetails() {
+
+	}
+
+	public TsfLoginDetails(Login loginInfo, Collection<SimpleGrantedAuthority> authorities) {
+		this.loginInfo = loginInfo;
+		this.authorities = authorities;
+
+//		if (authorities != null && !authorities.isEmpty()) {
+//			isLogin = true;
+//		}
+	}
+
+	@Override
+	public Collection<? extends GrantedAuthority> getAuthorities() {
+		return authorities;
+	}
+
+	@Override
+	public String getPassword() {
+		return loginInfo.getPasswd();
+	}
+
+	@Override
+	public String getUsername() {
+		return String.valueOf(loginInfo.getCustNo());
+	}
+
+	@Override
+	public boolean isAccountNonExpired() {
+		return true;
+	}
+
+	@Override
+	public boolean isAccountNonLocked() {
+		return true;
+	}
+
+	@Override
+	public boolean isCredentialsNonExpired() {
+		return true;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return true;
+	}
+
+	public boolean isLogin() {
+		return loginInfo.getCustNo() != null ? true : false;
+	}
+
+	public Login getLoginInfo() {
+		return loginInfo;
+	}
+
+}

+ 129 - 0
style24.front/src/main/java/com/style24/front/support/security/config/TsfSecurityConfig.java

@@ -0,0 +1,129 @@
+package com.style24.front.support.security.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+
+import com.style24.front.support.security.TsfAuthenticationProvider;
+import com.style24.front.support.security.filter.TsfAuthenticationFilter;
+import com.style24.front.support.security.handler.TsfLoginFailureHandler;
+import com.style24.front.support.security.handler.TsfLoginSuccessHandler;
+import com.style24.front.support.security.handler.TsfLogoutSuccessHandler;
+
+import com.gagaframework.web.security.GagaCsrfSecurityRequestMatcher;
+
+/**
+ * Java Security를 이용한 Login 처리
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Configuration
+@EnableWebSecurity
+public class TsfSecurityConfig extends WebSecurityConfigurerAdapter {
+
+	private static final String[] UNAUTHORIZED_RESOURCE_LIST = new String[] {"/*", "/index", "/signin/**"};
+
+	private static final String[] UNSECURED_RESOURCE_LIST = new String[] {"/image/**", "/ux/**", "/data/**"};
+
+	@Autowired
+	private TsfAuthenticationProvider authenticationProvider;
+
+	@Override
+	protected void configure(HttpSecurity httpSecurity) throws Exception {
+		// static resources
+		httpSecurity
+			.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
+			.authorizeRequests()
+			.antMatchers(UNAUTHORIZED_RESOURCE_LIST).permitAll()
+			.and()
+			.formLogin()
+			.usernameParameter("loginId")
+			.passwordParameter("passwd")
+			.loginPage("/signin")
+			.loginProcessingUrl("/login")
+//			.successHandler(loginSuccessHandler) // authenticationFilter() 안에 설정
+//			.failureHandler(loginFailureHandler) // authenticationFilter() 안에 설정
+//			.failureUrl("/signin?error=true")
+			.and()
+			.logout()
+			.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
+			.logoutSuccessHandler(logoutSuccessHandler())
+			.and()
+//			.csrf().disable() // CSRF(Cross Site Request Forgery) 해제
+			.csrf().requireCsrfProtectionMatcher(requireCsrfProtectionMatcher())
+			.and()
+			.exceptionHandling()
+			.accessDeniedPage("/403")
+			.and()
+			.headers().frameOptions().disable() // iframe deny 설정 제거(네이버스마트에디터 사용 때문에 추가)
+		;
+	}
+
+	@Override
+	public void configure(WebSecurity webSecurity) throws Exception {
+		webSecurity
+			.ignoring()
+			.antMatchers(UNSECURED_RESOURCE_LIST);
+	}
+
+	@Bean
+	public RequestMatcher requireCsrfProtectionMatcher() {
+		GagaCsrfSecurityRequestMatcher bean = new GagaCsrfSecurityRequestMatcher();
+
+		bean.setDisableUrls(new String[] {
+			"/",
+			"/index",
+			"/customer/post/find/form"
+		});
+
+		return bean;
+	}
+
+	/**
+	 * Register the Authentication Provider
+	 */
+	@Override
+	public void configure(AuthenticationManagerBuilder auth) throws Exception {
+		auth.authenticationProvider(authenticationProvider);
+	}
+
+	@Bean
+	public TsfLoginSuccessHandler loginSuccessHandler() {
+		return new TsfLoginSuccessHandler();
+	}
+
+	@Bean
+	public TsfLoginFailureHandler loginFailureHandler() {
+		return new TsfLoginFailureHandler();
+	}
+
+	@Bean
+	public TsfLogoutSuccessHandler logoutSuccessHandler() {
+		return new TsfLogoutSuccessHandler();
+	}
+
+	/**
+	 * Authentication Filter
+	 * @return
+	 * @throws Exception
+	 * @author gagamel
+	 * @since 2020. 2. 20
+	 */
+	public TsfAuthenticationFilter authenticationFilter() throws Exception {
+		TsfAuthenticationFilter filter = new TsfAuthenticationFilter();
+		filter.setAuthenticationManager(authenticationManagerBean());
+		filter.setAuthenticationSuccessHandler(loginSuccessHandler());
+		filter.setAuthenticationFailureHandler(loginFailureHandler());
+		return filter;
+	}
+
+}

+ 60 - 0
style24.front/src/main/java/com/style24/front/support/security/filter/TsfAuthenticationFilter.java

@@ -0,0 +1,60 @@
+package com.style24.front.support.security.filter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+import lombok.extern.slf4j.Slf4j;
+
+import com.gagaframework.web.parameter.GagaMap;
+import com.gagaframework.web.parameter.GagaParameterUtil;
+
+/**
+ * 인증 Filter
+ * 		SNS(네이버, 카카오, 페이스북 등) 로그인 처리 시 Spring Security를 이용할 경우 기본적인 로그인 처리 방식으로는 파라미터를 받을 수 없다.
+ * 		request.getParameter를 통해 Username과 Password를 얻을 수 있도록 만든 인증 Filter
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Slf4j
+public class TsfAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
+
+	@Override
+	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
+		// Parameter
+		GagaMap loginParams = GagaParameterUtil.getParameterMap(request);
+
+		if (StringUtils.isNotBlank(loginParams.getString("snsType"))) { // SNS로그인
+			// SNS로그인일 때는
+			// 로그인ID: SNS_TYPE-SNS_JOIN_ID
+			// 비밀번호: 이메일
+			loginParams.setString("loginId", loginParams.getString("snsType") + "-" + loginParams.getString("snsJoinId"));
+			loginParams.setString("passwd", loginParams.getString("email")); // SNS로그인 시 불필요해서 이메일체크를 위해 추가
+		} else { // 일반로그인
+			if (StringUtils.isNotBlank(loginParams.getString("loginId"))) {
+				loginParams.setString("loginId", loginParams.getString("loginId").trim());
+			}
+
+			if (StringUtils.isNotBlank(loginParams.getString("passwd"))) {
+				loginParams.setString("passwd", loginParams.getString("passwd").trim());
+			}
+		}
+
+		log.debug("loginParams: {}", loginParams);
+
+		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
+			loginParams.getString("loginId"), loginParams.getString("passwd"));
+
+		// Allow subclasses to set the "details" property
+		setDetails(request, authRequest);
+
+		return this.getAuthenticationManager().authenticate(authRequest);
+	}
+
+}

+ 67 - 0
style24.front/src/main/java/com/style24/front/support/security/handler/TsfLoginFailureHandler.java

@@ -0,0 +1,67 @@
+package com.style24.front.support.security.handler;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+
+import com.style24.front.biz.service.TsfLoginService;
+import com.style24.front.support.exception.TsfDormantAccountException;
+import com.style24.front.support.exception.TsfEmailDuplicationException;
+import com.style24.front.support.exception.TsfLockedAccountException;
+import com.style24.front.support.exception.TsfSecedeAccountException;
+import com.style24.front.support.exception.TsfSessionExpiredException;
+
+import lombok.extern.slf4j.Slf4j;
+
+import com.gagaframework.web.parameter.GagaMap;
+import com.gagaframework.web.util.GagaStringUtil;
+
+/**
+ * 로그인 실패 시 호출되는 Handler
+ * 		1. 비밀번호 5회 이상 실패 시
+ * 		2. 휴면회원 로그인 시
+ * 		3. 탈퇴회원 로그인 시
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Slf4j
+public class TsfLoginFailureHandler implements AuthenticationFailureHandler {
+
+	@Autowired
+	private TsfLoginService loginService;
+
+	@Override
+	public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
+		AuthenticationException exception) throws IOException, ServletException {
+
+		// 로그인 실패 남기기
+		loginService.createLoginFail(request.getParameter("custId"), "Y");
+
+		GagaMap result = new GagaMap();
+		result.setString("message", exception.getMessage());
+
+		if (exception instanceof TsfLockedAccountException) { // 로그인 시에 비밀번호 5회 이상 틀려서 계정이 잠기는 경우
+			result.setString("status", "PWD_5WRONG");
+		} else if (exception instanceof TsfDormantAccountException) { // 휴면회원 로그인 시
+			result.setString("status", "DORMANT_CUST");
+		} else if (exception instanceof TsfSecedeAccountException) { // 탈퇴회원 로그인 시
+			result.setString("status", "SECEDE_CUST");
+		} else if (exception instanceof TsfSessionExpiredException) { // 세션 만료 시
+			result.setString("status", "SESSION_EXPIRED");
+		} else if (exception instanceof TsfEmailDuplicationException) { // SNS용 이메일 중복 시
+			result.setString("status", "EMAIL_DUP");
+		} else {
+			result.setString("status", "ETC_ERROR");
+		}
+
+		GagaStringUtil.write(response, result);
+	}
+
+}

+ 130 - 0
style24.front/src/main/java/com/style24/front/support/security/handler/TsfLoginSuccessHandler.java

@@ -0,0 +1,130 @@
+package com.style24.front.support.security.handler;
+
+import java.io.IOException;
+import java.util.Base64;
+import java.util.Date;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+
+import com.style24.front.biz.service.TsfLoginService;
+import com.style24.front.support.env.TsfConstants;
+import com.style24.front.support.security.TsfLoginDetails;
+import com.style24.front.support.security.session.TsfSession;
+
+import lombok.extern.slf4j.Slf4j;
+
+import com.gagaframework.web.parameter.GagaMap;
+import com.gagaframework.web.util.GagaCookieUtil;
+import com.gagaframework.web.util.GagaCryptoUtil;
+import com.gagaframework.web.util.GagaDateUtil;
+import com.gagaframework.web.util.GagaStringUtil;
+
+/**
+ * 로그인 성공 시 호출되는 Handler
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Slf4j
+public class TsfLoginSuccessHandler implements AuthenticationSuccessHandler {
+
+	// 로그인 후 메인으로 이동할 페이지 목록
+	private final String[] pagesToMain = new String[] {
+		"/customer/join/form",				// 회원가입
+		"/customer/sns/join/form",			// SNS 회원가입
+		"/customer/id/find/form",			// 아이디찾기
+		"/customer/id/find/result/form",	// 아이디찾기
+		"/customer/pwd/find/form",			// 비밀번호찾기
+		"/customer/pwd/find/result/form",	// 비밀번호완료
+		"/customer/join/complete/form"		// 회원가입완료
+	};
+
+	@Autowired
+	private TsfLoginService loginService;
+
+//	@Autowired
+//	private FoCustomerService customerService;
+
+	@Override
+	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
+		// 로그인 상세 정보
+		TsfLoginDetails loginDetails = (TsfLoginDetails)authentication.getDetails();
+
+		// 로그인 성공 시 로그인실패수가 0보다 크면 로그인실패수 reset
+		Integer custNo = loginDetails.getLoginInfo().getCustNo();
+		int failCnt = loginService.getLoginFailCount(custNo);
+		if (failCnt > 0) {
+			loginService.createLoginFail(loginDetails.getLoginInfo().getCustId(), "N");
+		}
+
+		// 최종로그인일시 Update
+		loginService.updateLastLoginDate(custNo);
+
+		// 로그인이력 생성
+		loginService.createLoginHistory(custNo);
+
+		// 세션 생성
+		this.createSession(request, loginDetails);
+
+		// 자동로그인용 RememberMe 쿠키 및 토큰 생성
+		if (request.getParameter("rememberMe") != null && request.getParameter(TsfConstants.REMEMBER_ME_PARAMETER).equals("true")) {
+			Date expiry = new Date(System.currentTimeMillis() + (1000 * TsfConstants.REMEMBER_ME_LIMIT));
+			String md5HexToken = GagaCryptoUtil.encryptMD5(custNo + ":" + GagaDateUtil.getDate(expiry, "yyyyMMddHHmmss") + ":" + TsfSession.getSessionId());
+			String remembermeToken = Base64.getEncoder().encodeToString((TsfConstants.REMEMBER_ME_KEY + ":" + md5HexToken).getBytes());
+
+			// RememberMe 쿠키 생성
+			GagaCookieUtil.setSecureCookie(response, TsfConstants.CK_PREFIX + "_remember-me", remembermeToken, TsfConstants.REMEMBER_ME_LIMIT);
+
+			// 로그인유지토큰 생성
+			loginService.createPersistentToken(custNo, remembermeToken, expiry);
+		}
+
+//		String isApp = FoSession.getAttribute("isApp");
+//		if ("true".equals(isApp)) {
+//			customerService.giveFirstAppDownPoint(loginDetails.getUsername());
+//		}
+
+		String returnUrl = GagaCookieUtil.getCookie(request, TsfConstants.CK_PREFIX + "_return_url");
+		log.info("returnUrl: {}", returnUrl);
+
+		// 회원가입, 아이디찾기, 비밀번호찾기, 회원가입완료 페이지에서 로그인 했으면 메인으로 이동
+		// 그 외는 RETURN URL로 이동
+		if (StringUtils.isNotEmpty(returnUrl)) {
+			for (String page : pagesToMain) {
+				if (returnUrl.indexOf(page) > -1) {
+					returnUrl = "/";
+					break;
+				}
+			}
+		} else {
+			returnUrl = "/";
+		}
+
+		GagaMap result = new GagaMap();
+		result.setString("status", "OK");
+		result.setString("returnUrl", returnUrl);
+		result.setString("custNo", loginDetails.getUsername()); // 로그인 후 APP푸시SDK 연동을 위해
+
+		GagaStringUtil.write(response, result);
+	}
+
+	/**
+	 * Session 생성
+	 * @param request - HttpServletRequest
+	 * @param loginDetails - 로그인 상세 정보
+	 */
+	private void createSession(HttpServletRequest request, TsfLoginDetails loginDetails) {
+		HttpSession session = request.getSession(true);
+		session.setMaxInactiveInterval(1800);
+		session.setAttribute("session", loginDetails);
+	}
+
+}

+ 52 - 0
style24.front/src/main/java/com/style24/front/support/security/handler/TsfLogoutSuccessHandler.java

@@ -0,0 +1,52 @@
+package com.style24.front.support.security.handler;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
+
+import com.style24.front.biz.service.TsfLoginService;
+import com.style24.front.support.env.TsfConstants;
+import com.style24.front.support.security.session.TsfSession;
+
+import lombok.extern.slf4j.Slf4j;
+
+import com.gagaframework.web.util.GagaCookieUtil;
+
+/**
+ * 로그아웃 성공 시 호출되는 Handler
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Slf4j
+public class TsfLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
+
+	@Autowired
+	private TsfLoginService loginService;
+
+	@Override
+	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
+		TsfSession.invalidate(request);
+
+		// RememberMe 쿠키가 있으면
+		String ckRememberMe = GagaCookieUtil.getCookie(request, TsfConstants.CK_PREFIX + "_remember-me");
+
+		if (StringUtils.isNotBlank(ckRememberMe)) {
+			// 로그인유지토큰 삭제
+			loginService.deletePersistentToken(ckRememberMe);
+
+			// RememberMe 쿠키 삭제
+			GagaCookieUtil.deleteCookie(response, TsfConstants.CK_PREFIX + "_remember-me");
+		}
+
+		response.sendRedirect("/");
+	}
+
+}

+ 82 - 0
style24.front/src/main/java/com/style24/front/support/security/handler/TsfRememberMeSuccessHandler.java

@@ -0,0 +1,82 @@
+package com.style24.front.support.security.handler;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+
+import com.style24.front.biz.service.TsfLoginService;
+import com.style24.front.support.security.TsfLoginDetails;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * RememberMe를 통한 로그인 성공 시 호출되는 Handler
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Slf4j
+public class TsfRememberMeSuccessHandler implements AuthenticationSuccessHandler {
+
+	@Autowired
+	private TsfLoginService loginService;
+
+//	@Autowired
+//	private FoCustomerService customerService;
+
+	@Override
+	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
+		// 로그인 상세 정보
+		TsfLoginDetails loginDetails = (TsfLoginDetails)authentication.getPrincipal();
+
+		// 로그인 성공 시 로그인실패수가 0보다 크면 로그인실패수 reset
+		Integer custNo = loginDetails.getLoginInfo().getCustNo();
+		int failCnt = loginService.getLoginFailCount(custNo);
+		if (failCnt > 0) {
+			loginService.createLoginFail(loginDetails.getLoginInfo().getCustId(), "N");
+		}
+
+		// 최종로그인일시 Update
+		loginService.updateLastLoginDate(custNo);
+
+		// 로그인이력 생성
+		loginService.createLoginHistory(custNo);
+
+		// 세션 생성
+		this.createSession(request, loginDetails);
+
+//		String isApp = FoSession.getAttribute("isApp");
+//		if ("true".equals(isApp)) {
+//			customerService.giveFirstAppDownPoint(loginDetails.getUsername());
+//		}
+
+		String returnUrl = request.getRequestURL().toString();
+		log.info("returnUrl: {}", returnUrl);
+
+		if (StringUtils.isBlank(returnUrl)) {
+			returnUrl = "/";
+		}
+
+		response.sendRedirect(returnUrl);
+	}
+
+	/**
+	 * Session 생성
+	 * @param request - HttpServletRequest
+	 * @param loginDetails - 로그인 상세 정보
+	 */
+	private void createSession(HttpServletRequest request, TsfLoginDetails loginDetails) {
+		HttpSession session = request.getSession(true);
+		session.setMaxInactiveInterval(1800);
+		session.setAttribute("session", loginDetails);
+	}
+
+}

+ 100 - 0
style24.front/src/main/java/com/style24/front/support/security/session/TsfSession.java

@@ -0,0 +1,100 @@
+package com.style24.front.support.security.session;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.springframework.mobile.device.Device;
+import org.springframework.mobile.device.DeviceUtils;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import com.style24.front.support.security.TsfLoginDetails;
+import com.style24.persistence.domain.Login;
+
+import lombok.extern.slf4j.Slf4j;
+
+import com.gagaframework.web.security.GagaSession;
+
+/**
+ * Session
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@Slf4j
+public class TsfSession extends GagaSession {
+
+	/**
+	 * Get Session Info.
+	 * @return 세션 정보
+	 */
+	public static Login getInfo() {
+		TsfLoginDetails loginDetails = (TsfLoginDetails)RequestContextHolder.currentRequestAttributes().getAttribute("session", RequestAttributes.SCOPE_SESSION);
+
+		if (loginDetails == null)
+			return null;
+
+		return loginDetails.getLoginInfo();
+	}
+
+	/**
+	 * 로그인 여부
+	 * @return true/false
+	 */
+	public static boolean isLogin() {
+		TsfLoginDetails loginDetails = (TsfLoginDetails)RequestContextHolder.currentRequestAttributes().getAttribute("session", RequestAttributes.SCOPE_SESSION);
+
+		if (loginDetails == null)
+			return false;
+
+		return loginDetails.isLogin();
+	}
+
+	/**
+	 * 프론트구분 가져오기
+	 * @return P:PC, M:모바일
+	 */
+	public static String getFrontGb() {
+		HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
+		Device device = DeviceUtils.getCurrentDevice(request);
+
+		if (device == null) {
+			log.debug("device is null");
+			return "P";
+		}
+
+		log.debug("device.isMobile: {}", device.isMobile());
+		log.debug("device.isNormal: {}", device.isNormal());
+		log.debug("device.isTablet: {}", device.isTablet());
+
+		return device.isMobile() ? "M" : "P";
+	}
+
+	/**
+	 * 세션 설정
+	 * @param name - 명칭
+	 * @param value - 값
+	 * @return HttpServletResponse
+	 */
+	public static void setAttribute(String name, String value) {
+		HttpServletRequest request = getHttpServletRequest();
+		request.getSession(true).setAttribute(name, value);
+	}
+
+	/**
+	 * 세션 값 조회
+	 * @return 세션 값
+	 */
+	public static String getAttribute(String name) {
+		HttpServletRequest request = getHttpServletRequest();
+		HttpSession session = request.getSession(true);
+
+		if (session != null && session.getAttribute(name) != null) {
+			return (String)session.getAttribute(name);
+		}
+
+		return "";
+	}
+
+}

+ 53 - 0
style24.front/src/main/java/com/style24/front/support/startup/TsfApplication.java

@@ -0,0 +1,53 @@
+package com.style24.front.support.startup;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.annotation.PostConstruct;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+
+import com.style24.core.support.env.TscConstants;
+
+import lombok.extern.slf4j.Slf4j;
+
+import com.gagaframework.web.core.GagaConstants;
+
+/**
+ * Application
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@EnableCaching
+@Configuration
+@EnableAutoConfiguration(exclude = {DataSourceTransactionManagerAutoConfiguration.class, DataSourceAutoConfiguration.class})
+@ComponentScan(basePackages = {GagaConstants.GAGA_PACKAGE, TscConstants.BASE_PACKAGE})
+@Slf4j
+public class TsfApplication {
+
+	@Autowired
+	private Environment env;
+
+	@PostConstruct
+	public void initApplication() throws IOException {
+		log.info("Running with Spring Profiles: {}", Arrays.toString(env.getActiveProfiles()));
+		if (env.getActiveProfiles().length == 0) {
+			log.warn("No spring profile configured, running with default configuration.");
+		} else if (env.getActiveProfiles().length > 1) {
+			log.error("You have misconfigured your application! It should not run with both Spring Profiles at the same time.");
+		}
+	}
+
+	public static void main(String[] args) {
+		SpringApplication.run(TsfApplication.class, args);
+	}
+
+}

+ 19 - 0
style24.front/src/main/java/com/style24/front/support/startup/TsfServletInitializer.java

@@ -0,0 +1,19 @@
+package com.style24.front.support.startup;
+
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+
+/**
+ * ServletInitializer
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+public class TsfServletInitializer extends SpringBootServletInitializer {
+
+	@Override
+	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+		return application.sources(TsfApplication.class);
+	}
+
+}

+ 120 - 0
style24.front/src/main/java/com/style24/persistence/TsfPageRequest.java

@@ -0,0 +1,120 @@
+package com.style24.persistence;
+
+import java.io.Serializable;
+
+import lombok.Data;
+
+/**
+ * Paging
+ * @author gagamel
+ * @since 2020. 2. 14
+ */
+@SuppressWarnings("serial")
+@Data
+public class TsfPageRequest implements Serializable {
+
+	private final int pageNo;   // 페이지번호
+	private final int pageSize; // 조회할 row수
+	private final int pageUnit; // 그룹핑 페이지 단위
+	private int totalCount = 0; // 전체 row 건수
+
+	public TsfPageRequest(int pageNo, int pageSize) {
+		this(pageNo, pageSize, 10);
+	}
+
+	public TsfPageRequest(int pageNo, int pageSize, int pageUnit) {
+		if (pageNo < 0) {
+			throw new IllegalArgumentException("Current page index must not be less than zero!");
+		}
+
+		if (pageSize < 1) {
+			throw new IllegalArgumentException("Page size must not be less than one!");
+		}
+
+		if (pageUnit < 1) {
+			throw new IllegalArgumentException("Page unit must not be less than one!");
+		}
+
+		this.pageNo = pageNo;
+		this.pageSize = pageSize;
+		this.pageUnit = pageUnit;
+	}
+
+	public int getPageNo() {
+		return pageNo + 1;
+	}
+
+	public int getOffset() {
+		return pageNo * pageSize;
+	}
+
+	public int getStartRow() {
+		return pageNo * pageSize + 1;
+	}
+
+	public int getEndRow() {
+		return getOffset() + pageSize;
+	}
+
+	public int getPageGroup() {
+		return pageNo / pageUnit + 1;
+	}
+
+	public void setTotalCount(int totalCount) {
+		this.totalCount = totalCount;
+	}
+
+	public int getTotalPage() {
+		int totalPage = totalCount / pageSize;
+		if (totalCount % pageSize > 0) totalPage++;
+		return totalPage;
+	}
+
+	public String getGeneratedPagination() {
+		int firstCount = (getPageGroup() - 1) * pageUnit + 1;
+		int loopCount = firstCount + pageUnit;
+		if (loopCount > getTotalPage()) loopCount = getTotalPage() + 1;
+
+		StringBuffer pageTag = new StringBuffer();
+
+		if (getPageNo() == 1) {
+			pageTag.append("<a href=\"#\" class=\"icon first\" alt=\"처음 페이지\">처음 페이지</a>\n");
+		} else {
+			pageTag.append("<a href=\"#pageNo=1\" class=\"icon first\" alt=\"처음 페이지\">처음 페이지</a>\n");
+		}
+
+		if (getPageGroup() == 1) {
+			pageTag.append("<a href=\"#\" class=\"icon prev\" alt=\"이전페이지\">이전 페이지</a>\n");
+		} else {
+			pageTag.append("<a href=\"#pageNo=").append((getPageGroup() - 1) * pageUnit).append("\" class=\"icon prev\" alt=\"이전페이지\">이전 페이지</a>\n");
+		}
+
+		for (int i = firstCount; i < loopCount; i++) {
+			if (getPageNo() == i) {
+				pageTag.append("<a class=\"num on\" href=\"#\">").append(i).append("</a>\n");
+			} else {
+				pageTag.append("<a class=\"num\" href=\"#pageNo=").append(i).append("\">").append(i).append("</a>\n");
+			}
+		}
+
+		if (loopCount <= (getTotalPage() + 1)) {
+			if (getPageNo() == getTotalPage() || getTotalPage()<=(getPageGroup() * pageUnit)) {
+				//				pageTag.append("<a href=\"#\" class=\"icon next\" alt=\"다음 페이지\">다음 페이지</a>\n");
+				//				pageTag.append("<a href=\"#\" class=\"icon last\" alt=\"마지막 페이지\">마지막 페이지</a>\n");
+				pageTag.append("<a href=\"#pageNo=").append(getPageNo()).append("\" class=\"icon next\" alt=\"다음 페이지\">다음 페이지</a>\n");
+				pageTag.append("<a href=\"#pageNo=").append(getPageNo()).append("\" class=\"icon last\" alt=\"마지막 페이지\">마지막 페이지</a>\n");
+			} else {
+				pageTag.append("<a href=\"#pageNo=").append(getPageGroup() * pageUnit + 1).append("\" class=\"icon next\" alt=\"다음 페이지\">다음 페이지</a>\n");
+				pageTag.append("<a href=\"#pageNo=").append(getTotalPage()).append("\" class=\"icon last\" alt=\"마지막 페이지\">마지막 페이지</a>\n");
+			}
+		}
+
+		return pageTag.toString();
+	}
+
+	@Override
+	public String toString() {
+		return String.format("Page request [pageNo: %d, pageSize %d, pageUnit %d]", getPageNo(), pageSize, pageUnit);
+	}
+
+}

+ 43 - 0
style24.front/src/main/java/com/style24/persistence/domain/Login.java

@@ -0,0 +1,43 @@
+package com.style24.persistence.domain;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.style24.persistence.TscBaseDomain;
+
+import lombok.Data;
+
+/**
+ * 고객 Domain
+ * 		@JsonSerialize 애노테이션을 지정해야 세션을 레디스에 저장할 수 있다.
+ * @author gagamel
+ * @since 2019. 12. 4
+ */
+@SuppressWarnings("serial")
+@Data
+@JsonSerialize
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Login extends TscBaseDomain {
+
+	// 세션을 JSON 형식으로 레디스에 저장하려면 기본 생성자를 반드시 명시해야 한다
+	public Login() {
+
+	}
+
+	private Integer custNo;			// 고객번호
+	private String custId;			// 고객ID
+	private String custNm;			// 고객명
+	private String passwd;			// 비밀번호
+	private String custGb;			// 고객구분
+	private String custGbNm;		// 고객구분명
+	private String custStat;		// 고객상태
+	private String email;			// 이메일
+	private String snsType;			// SNS유형
+	private String snsJoinId;		// SNS가입ID
+	private int loginFailCnt;		// 로그인실패건수
+	private String siteCd;			// 가입사이트코드(공통코드G000)
+	private String frontGb;			// 프론트구분(P:PC, M:모바일)
+	private String ipAddr;			// IP주소
+	private String loginLdt;		// 최종로그인일시
+	private String loginFailYn;		// 로그인실패여부
+
+}

+ 43 - 0
style24.front/src/main/java/com/style24/persistence/domain/Notice.java

@@ -0,0 +1,43 @@
+package com.style24.persistence.domain;
+
+import com.style24.persistence.TscBaseDomain;
+
+import lombok.Data;
+
+/**
+ * 공지사항 Domain
+ * 
+ * @author gagamel
+ * @since 2020. 10. 30
+ */
+@SuppressWarnings("serial")
+@Data
+public class Notice extends TscBaseDomain {
+
+	private Integer noticeSq;		// 공지사항일련번호
+	private String noticeType;		// 공지사항유형
+	private String urgentYn;		// 긴급여부
+	private String noticeTitle;		// 공지사항제목
+	private String noticeContent;	// 공지사항내용
+	private int fileCnt;			// 파일건수
+	private String noticeStdt;		// 공지시작일시
+	private String noticeEddt;		// 공지종료일시
+	private int readCnt;			// 조회수
+	private String useYn;			// 사용여부
+
+	// 검색용
+	private String siteCd;			// 사이트코드
+	private String searchGb;		// 검색구분(ALL: 전체, TITLE: 제목, CONTENT: 내용)
+	private String searchTxt;		// 검색텍스트
+
+	private Integer prevNoticeSq;	// 이전 공지사항일련번호
+	private String prevNoticeTitle;	// 이전 공지제목
+	private Integer nextNoticeSq;	// 다음 공지사항일련번호
+	private String nextNoticeTitle;	// 다음 공지제목
+
+	// 첨부파일
+	private int seq;				// 일련번호
+	private String orgFileNm;		// 원본파일명
+	private String sysFileNm;		// 시스템파일명
+
+}

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

@@ -0,0 +1,26 @@
+package com.style24.persistence.domain;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * 로그인유지용 토큰
+ * 
+ * @author gagamel
+ * @since 2020. 9. 11
+ */
+@SuppressWarnings("serial")
+@Data
+@Builder
+public class PersistentToken implements Serializable {
+
+	private Integer custNo;				// 고객번호
+	private String remembermeToken;		// RememberMe토큰
+	private Date limitDt;				// 만료일시
+	private String custId;				// 고객ID
+	private String prevRemembermeToken;	// 기존RememberMe토큰
+
+}

+ 192 - 0
style24.front/src/main/java/com/style24/persistence/mybatis/TsfLogin.xml

@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.style24.front.biz.dao.TsfLoginDao">
+
+	<!-- 로그인체크 정보 조회 -->
+	<select id="getLoginCheckInfo" parameterType="Login" resultType="Login">
+		/* TsfLogin.getLoginCheckInfo */
+		SELECT CUST_NO                                      --고객번호
+		     , CUST_ID                                      --고객ID
+		     , CUST_NM                                      --고객명
+		     , PASSWD                                       --비밀번호
+		     , CUST_GB                                      --고객구분
+		     , FN_GET_CODE_NM('G100',CUST_GB) AS CUST_GB_NM --고객구분명
+		     , CUST_STAT                                    --회원상태
+		     , EMAIL                                        --이메일
+		     , SNS_TYPE                                     --SNS유형
+		     , SNS_JOIN_ID                                  --SNS가입ID
+		     , NVL((SELECT LOGIN_FAIL_CNT
+		            FROM   TB_LOGIN_FAIL
+		            WHERE  LOGIN_ID = TO_CHAR(A.CUST_NO)
+		            AND    IP_ADDR = #{ipAddr}
+		            AND    SITE_CD = #{siteCd}
+		           ),0)    AS LOGIN_FAIL_CNT                --로그인실패건수
+		FROM   TB_CUSTOMER A
+		WHERE  CUST_STAT <![CDATA[<>]]> '40'                --전환대상고객제외
+		<choose>
+		    <when test="snsType != null and snsType != ''"> <!-- SNS로그인 -->
+		AND    (
+		        SNS_TYPE||'-'||SNS_JOIN_ID = #{custId}
+		        OR
+		        EMAIL = #{email}
+		       )
+		    </when>
+		    <otherwise> <!-- 일반로그인 -->
+		AND    CUST_ID = #{custId}
+		    </otherwise>
+		</choose>
+	</select>
+
+	<!-- 로그인실패 남기기 -->
+	<insert id="createLoginFail" parameterType="Login">
+		/* TsfLogin.createLoginFail */
+		MERGE INTO TB_LOGIN_FAIL
+		USING (
+		       SELECT TO_CHAR(CUST_NO) AS CUST_NO
+		       FROM   TB_CUSTOMER
+		       WHERE  CUST_ID = #{custId}
+		      ) B
+		ON    (
+		       LOGIN_ID = B.CUST_NO
+		AND    IP_ADDR = #{ipAddr}
+		AND    SITE_CD = #{siteCd}
+		      )
+		WHEN MATCHED THEN
+		    UPDATE
+		    SET    LOGIN_FAIL_CNT = DECODE(#{loginFailYn},'Y',LOGIN_FAIL_CNT + 1,0)
+		         , UPD_NO = B.CUST_NO
+		         , UPD_DT = NOW()
+		WHEN NOT MATCHED THEN
+		    INSERT (LOGIN_ID, IP_ADDR, SITE_CD, LOGIN_FAIL_CNT, REG_ID, REG_DT, UPD_NO, UPD_DT)
+		    VALUES (B.CUST_NO, #{ipAddr}, #{siteCd}, 1, B.CUST_NO, NOW(), B.CUST_NO, NOW())
+	</insert>
+
+	<!-- 로그인 실패건수 조회 -->
+	<select id="getLoginFailCount" parameterType="Login" resultType="int">
+		/* TsfLogin.getLoginFailCount */
+		SELECT NVL((SELECT LOGIN_FAIL_CNT
+		            FROM   TB_LOGIN_FAIL
+		            WHERE  LOGIN_ID = #{custNo}
+		            AND    IP_ADDR = #{ipAddr}
+		            AND    SITE_CD = #{siteCd}
+		           ),0)
+		FROM   DUAL
+	</select>
+
+	<!-- 최종로그인일시 Update -->
+	<update id="updateLastLoginDate" parameterType="Integer">
+		/* TsfLogin.updateLastLoginDate */
+		UPDATE TB_CUSTOMER
+		SET    LOGIN_LDT = NOW()
+		     , UPD_NO = #{custNo}
+		     , UPD_DT = NOW()
+		WHERE  CUST_NO = #{custNo}
+	</update>
+
+	<!-- 로그인이력 남기기 -->
+	<insert id="createLoginHistory" parameterType="Login">
+		/* TsfLogin.createLoginHistory */
+		INSERT INTO TB_LOGIN_HST (
+		       LOGIN_HST_SQ
+		     , LOGIN_ID
+		     , IP_ADDR
+		     , SITE_CD
+		     , FRONT_GB
+		     , LOGIN_DT
+		     , REG_ID
+		     , REG_DT
+		)
+		VALUES (
+		       SEQ_LOGIN_HST.NEXTVAL
+		     , #{custNo}
+		     , #{ipAddr}
+		     , #{siteCd}
+		     , #{frontGb}
+		     , NOW()
+		     , #{custNo}
+		     , NOW()
+		)
+	</insert>
+
+	<!-- 로그인 세션정보 조회 -->
+	<select id="getLoginSessionInfo" parameterType="Login" resultType="Login">
+		/* TsfLogin.getLoginSessionInfo */
+		SELECT CUST_NO                                                --고객번호
+		     , CUST_ID                                                --고객ID
+		     , CUST_NM                                                --고객명
+		     , PASSWD                                                 --비밀번호
+		     , CELL_PHNNO                                             --휴대전화번호
+		     , EMAIL                                                  --이메일
+		     , CUST_GB                                                --회원구분코드
+		     , FN_GET_CODE_NM('G100',CUST_GB)        AS CUST_GB_NM    --회원구분명
+		     , CUST_GRADE                                             --회원등급코드
+		     , FN_GET_CODE_NM('G110',CUST_GRADE)     AS CUST_GRADE_NM --회원등급명
+		     , SITE_CD                                                --사이트코드
+		     , TO_CHAR(LOGIN_LDT,'YYYYMMDDHH24MISS') AS LOGIN_LDT     --최종로그인일시
+		     , MANAGED_RSN                                            --관리대상지정사유(공통코드G120)
+		     , CASE WHEN ADD_MONTHS(PASSWD_CHG_DT,6) <![CDATA[<]]> NOW() THEN 'Y'
+		            ELSE 'N'
+		       END                                   AS PASSWD_CHG_YN --비밀번호변경도래여부
+		FROM   TB_CUSTOMER A
+		WHERE  CUST_ID = #{custId}
+		AND    CUST_STAT = '10' --활동회원
+	</select>
+
+	<!-- 로그인유지토큰 생성 -->
+	<insert id="createPersistentToken" parameterType="PersistentToken">
+		/* TsfLogin.createPersistentToken */
+		MERGE INTO TB_PERSISTENT_TOKEN
+		USING DUAL
+		ON    (
+		       CUST_NO = #{custNo}
+		       AND
+		       REMEMBERME_TOKEN = #{remembermeToken}
+		)
+		WHEN MATCHED THEN
+		    UPDATE
+		    SET    LIMIT_DT = #{limitDt}
+		WHEN NOT MATCHED THEN
+		    INSERT (
+		            CUST_NO
+		          , REMEMBERME_TOKEN
+		          , LIMIT_DT
+		    )
+		    VALUES (
+		           #{custNo}
+		         , #{remembermeToken}
+		         , NOW()
+		    )
+	</insert>
+
+	<!-- 로그인유지토큰 조회 -->
+	<select id="getPersistentToken" parameterType="String" resultType="PersistentToken">
+		/* TsfLogin.getPersistentToken */
+		SELECT PT.CUST_NO
+		     , PT.REMEMBERME_TOKEN
+		     , PT.LIMIT_DT
+		     , C.CUST_ID
+		FROM   TB_PERSISTENT_TOKEN PT
+		     , TB_CUSTOMER C
+		WHERE  PT.CUST_NO = C.CUST_NO
+		AND    PT.REMEMBERME_TOKEN = #{remembermeToken}
+		AND    NOW() <![CDATA[<=]]> PT.LIMIT_DT
+	</select>
+
+	<!-- 로그인유지토큰 갱신 -->
+	<update id="updatePersistentToken" parameterType="PersistentToken">
+		/* TsfLogin.updatePersistentToken */
+		UPDATE TB_PERSISTENT_TOKEN
+		SET    REMEMBERME_TOKEN = #{remembermeToken}
+		     , LIMIT_DT = #{limitDt}
+		WHERE  CUST_NO = #{custNo}
+		AND    REMEMBERME_TOKEN = #{prevRemembermeToken}
+	</update>
+
+	<!-- 로그인유지토근 삭제 -->
+	<delete id="deletePersistentToken" parameterType="String">
+		/* TsfLogin.deletePersistentToken */
+		DELETE FROM TB_PERSISTENT_TOKEN
+		WHERE  REMEMBERME_TOKEN = #{remembermeToken}
+	</delete>
+
+</mapper>

+ 265 - 0
style24.front/src/main/java/com/style24/persistence/mybatis/TsfNotice.xml

@@ -0,0 +1,265 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.style24.front.biz.dao.TsfNoticeDao">
+
+	<!-- Paging -->
+	<sql id="pageSql">
+		LIMIT #{pageable.startRow}, #{pageable.pageSize}
+	</sql>
+	
+	<!-- 공지사항 전체 건수 조회 -->
+	<select id="getNoticeTotalCount" parameterType="Notice" resultType="int">
+		/* TsfNotice.getNoticeTotalCount */
+		SELECT COUNT(*)
+		FROM   TB_NOTICE A
+		WHERE  NOTICE_TYPE = #{noticeType}
+		AND    USE_YN = 'Y'
+		AND    NOW() BETWEEN NOTICE_STDT AND NOTICE_EDDT
+		<choose>
+		    <when test="urgentYn == null or urgentYn == ''">
+		        <if test="searchTxt != null and searchTxt != ''">
+		            <choose>
+		                <when test="searchGb == 'TITLE'">
+		AND    NOTICE_TITLE LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		                </when>
+		                <when test="searchGb == 'CONTENT'">
+		AND    NOTICE_CONTENT LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		                </when>
+		                <otherwise>
+		AND    (
+		        NOTICE_TITLE LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		        OR
+		        NOTICE_CONTENT LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		       )
+		                </otherwise>
+		            </choose>
+		        </if>
+		    </when>
+		    <otherwise>
+		        <if test='urgentYn == "Y"'> <!-- 최상단 공지글인 경우 -->
+		AND    URGENT_YN = 'Y'
+		        </if>
+		    </otherwise>
+		</choose>
+		AND    EXISTS (SELECT 1
+		               FROM   TB_NOTICE_RECEIVER
+		               WHERE  NOTICE_SQ = A.NOTICE_SQ
+		               AND    RECEIVER_ID = #{siteCd}
+		              )
+	</select>
+	
+	<!-- 공지사항 목록 -->
+	<select id="getNoticeList" parameterType="Notice" resultType="Notice">
+		/* TsfNotice.getNoticeList */
+		SELECT NOTICE_SQ                              /*공지사항일련번호*/
+		     , NOTICE_TITLE                           /*공지제목*/
+		     , READ_CNT                               /*조회수*/
+		     , DATE_FORMAT(REG_DT,'%Y%m%d') AS REG_DT /*등록일자*/
+		     , (SELECT COUNT(1)
+		        FROM   TB_NOTICE_FILE
+		        WHERE  NOTICE_SQ = A.NOTICE_SQ
+		       )                          AS FILE_CNT /*첨부파일건수*/
+		FROM   TB_NOTICE A
+		WHERE  NOTICE_TYPE = #{noticeType}
+		AND    USE_YN =  'Y'
+		AND    NOW() BETWEEN NOTICE_STDT AND NOTICE_EDDT
+		<choose>
+		    <when test="urgentYn == null or urgentYn == ''">
+		        <if test="searchTxt != null and searchTxt != ''">
+		            <choose>
+		                <when test="searchGb == 'TITLE'">
+		AND    NOTICE_TITLE LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		                </when>
+		                <when test="searchGb == 'CONTENT'">
+		AND    NOTICE_CONTENT LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		                </when>
+		                <otherwise>
+		AND    (
+		        NOTICE_TITLE LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		        OR
+		        NOTICE_CONTENT LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		       )
+		                </otherwise>
+		            </choose>
+		        </if>
+		    </when>
+		    <otherwise>
+		        <if test='urgentYn == "Y"'> <!-- 최상단 공지글인 경우 -->
+		AND    URGENT_YN = 'Y'
+		        </if>
+		    </otherwise>
+		</choose>
+		AND    EXISTS (SELECT 1
+		               FROM   TB_NOTICE_RECEIVER
+		               WHERE  NOTICE_SQ = A.NOTICE_SQ
+		               AND    RECEIVER_ID = #{siteCd}
+		              )
+		ORDER  BY URGENT_YN DESC, REG_DT DESC
+		<include refid="pageSql"></include>
+	</select>
+
+	<!-- 공지사항 상세 -->
+	<select id="getNoticeDetail" parameterType="Notice" resultType="Notice">
+		/* TsfNotice.getNoticeDetail */
+		SELECT NOTICE_SQ      /*공지사항일련번호*/
+		     , NOTICE_TITLE   /*공지사항제목*/
+		     , NOTICE_CONTENT /*공지사항내용*/
+		     , READ_CNT       /*조회수*/
+		     , REG_DT         /*등록일시*/
+		FROM   (
+		        SELECT NOTICE_SQ
+		             , NOTICE_TITLE
+		             , NOTICE_CONTENT
+		             , READ_CNT
+		             , DATE_FORMAT(REG_DT,'%Y.%m.%d') AS REG_DT
+		        FROM   TB_NOTICE A
+		        WHERE  NOTICE_TYPE = #{noticeType}
+		        AND    USE_YN =  'Y'
+		        AND    NOW() BETWEEN NOTICE_STDT AND NOTICE_EDDT
+		        AND    NOTICE_SQ = #{noticeSq}
+		        <choose>
+		            <when test="urgentYn == null or urgentYn == ''">
+		                <if test="searchTxt != null and searchTxt != ''">
+		                    <choose>
+		                        <when test="searchGb == 'TITLE'">
+		        AND    NOTICE_TITLE LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		                        </when>
+		                        <when test="searchGb == 'CONTENT'">
+		        AND    NOTICE_CONTENT LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		                        </when>
+		                        <otherwise>
+		        AND    (
+		                NOTICE_TITLE LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		                OR
+		                NOTICE_CONTENT LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		               )
+		                        </otherwise>
+		                    </choose>
+		                </if>
+		            </when>
+		            <otherwise>
+		                <if test='urgentYn == "Y"'> <!-- 최상단 공지글인 경우 -->
+		        AND    URGENT_YN = 'Y'
+		                </if>
+		            </otherwise>
+		        </choose>
+		        AND    EXISTS (SELECT 1
+		                       FROM   TB_NOTICE_RECEIVER
+		                       WHERE  NOTICE_SQ = A.NOTICE_SQ
+		                       AND    RECEIVER_ID = #{siteCd}
+		                      )
+		       )
+	</select>
+	
+	<!-- 공지사항 이전글 조회 -->
+	<select id="getPreviousNotice" parameterType="Notice" resultType="Notice">
+		/* TsfNotice.getPreviousNotice */
+		SELECT NOTICE_SQ    AS PREV_NOTICE_SQ
+		     , NOTICE_TITLE AS PREV_NOTICE_TITLE
+		FROM   TB_NOTICE
+		WHERE  NOTICE_SQ = (SELECT MAX(NOTICE_SQ)
+		                    FROM   TB_NOTICE N
+		                    WHERE  NOTICE_SQ <![CDATA[<]]> #{noticeSq}
+		                    AND    NOTICE_TYPE = #{noticeType}
+		                    AND    USE_YN = 'Y'
+		                    AND    NOW() BETWEEN NOTICE_STDT AND NOTICE_EDDT
+		                    <choose>
+		                        <when test="urgentYn == null or urgentYn == ''">
+		                            <if test="searchTxt != null and searchTxt != ''">
+		                                <choose>
+		                                    <when test="searchGb == 'TITLE'">
+		                    AND    NOTICE_TITLE LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		                                    </when>
+		                                    <when test="searchGb == 'CONTENT'">
+		                    AND    NOTICE_CONTENT LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		                                    </when>
+		                                    <otherwise>
+		                    AND    (
+		                            NOTICE_TITLE LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		                            OR
+		                            NOTICE_CONTENT LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		                           )
+		                                    </otherwise>
+		                                </choose>
+		                            </if>
+		                        </when>
+		                        <otherwise>
+		                            <if test='urgentYn == "Y"'> <!-- 최상단 공지글인 경우 -->
+		                    AND    URGENT_YN = 'Y'
+		                            </if>
+		                        </otherwise>
+		                    </choose>
+		                    AND    EXISTS (SELECT 1
+		                                   FROM   TB_NOTICE_RECEIVER
+		                                   WHERE  NOTICE_SQ = N.NOTICE_SQ
+		                                   AND    RECEIVER_ID = #{siteCd}
+		                                  )
+		                   )
+	</select>
+
+	<!-- 공지사항 다음글 조회 -->
+	<select id="getNextNotice" parameterType="Notice" resultType="Notice">
+		/* TsfNotice.getNextNotice */
+		SELECT NOTICE_SQ    AS NEXT_NOTICE_SQ
+		     , NOTICE_TITLE AS NEXT_NOTICE_TITLE
+		FROM   TB_NOTICE
+		WHERE  NOTICE_SQ = (SELECT MIN(NOTICE_SQ)
+		                    FROM   TB_NOTICE N
+		                    WHERE  NOTICE_SQ <![CDATA[>]]> #{noticeSq}
+		                    AND    NOTICE_TYPE = #{noticeType}
+		                    AND    USE_YN = 'Y'
+		                    AND    NOW() BETWEEN NOTICE_STDT AND NOTICE_EDDT
+		                    <choose>
+		                        <when test="urgentYn == null or urgentYn == ''">
+		                            <if test="searchTxt != null and searchTxt != ''">
+		                                <choose>
+		                                    <when test="searchGb == 'TITLE'">
+		                    AND    NOTICE_TITLE LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		                                    </when>
+		                                    <when test="searchGb == 'CONTENT'">
+		                    AND    NOTICE_CONTENT LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		                                    </when>
+		                                    <otherwise>
+		                    AND    (
+		                            NOTICE_TITLE LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		                            OR
+		                            NOTICE_CONTENT LIKE CONCAT('%',TRIM(#{searchTxt}),'%')
+		                           )
+		                                    </otherwise>
+		                                </choose>
+		                            </if>
+		                        </when>
+		                        <otherwise>
+		                            <if test='urgentYn == "Y"'> <!-- 최상단 공지글인 경우 -->
+		                    AND    URGENT_YN = 'Y'
+		                            </if>
+		                        </otherwise>
+		                    </choose>
+		                    AND    EXISTS (SELECT 1
+		                                   FROM   TB_NOTICE_RECEIVER
+		                                   WHERE  NOTICE_SQ = N.NOTICE_SQ
+		                                   AND    RECEIVER_ID = #{siteCd}
+		                                  )
+		                   )
+	</select>
+
+	<!-- 공지사항 파일 목록 -->
+	<select id="getNoticeFileList" parameterType="Integer" resultType="Notice">
+		/* TsfNotice.getNoticeFileList */
+		SELECT NOTICE_SQ
+		     , SEQ
+		     , ORG_FILE_NM
+		     , SYS_FILE_NM
+		FROM   TB_NOTICE_FILE
+		WHERE  NOTICE_SQ = #{noticeSq}
+	</select>
+	
+	<!-- 공지사항  조회건수 Update -->
+	<update id="updateNoticeReadCount" parameterType="Notice">
+		/* TsfNotice.updateNoticeReadCount */
+		UPDATE TB_NOTICE
+		SET    READ_CNT = READ_CNT + 1
+		WHERE  NOTICE_SQ = #{noticeSq}
+	</update>
+
+</mapper>

+ 11 - 0
style24.front/src/main/resources/banner.txt

@@ -0,0 +1,11 @@
+  ______   ________  __      __  __        ________   ______   __    __        ________                               __     
+ /      \ /        |/  \    /  |/  |      /        | /      \ /  |  /  |      /        |                             /  |    
+/$$$$$$  |$$$$$$$$/ $$  \  /$$/ $$ |      $$$$$$$$/ /$$$$$$  |$$ |  $$ |      $$$$$$$$/______    ______   _______   _$$ |_   
+$$ \__$$/    $$ |    $$  \/$$/  $$ |      $$ |__    $$____$$ |$$ |__$$ |      $$ |__  /      \  /      \ /       \ / $$   |  
+$$      \    $$ |     $$  $$/   $$ |      $$    |    /    $$/ $$    $$ |      $$    |/$$$$$$  |/$$$$$$  |$$$$$$$  |$$$$$$/   
+ $$$$$$  |   $$ |      $$$$/    $$ |      $$$$$/    /$$$$$$/  $$$$$$$$ |      $$$$$/ $$ |  $$/ $$ |  $$ |$$ |  $$ |  $$ | __ 
+/  \__$$ |   $$ |       $$ |    $$ |_____ $$ |_____ $$ |_____       $$ |      $$ |   $$ |      $$ \__$$ |$$ |  $$ |  $$ |/  |
+$$    $$/    $$ |       $$ |    $$       |$$       |$$       |      $$ |      $$ |   $$ |      $$    $$/ $$ |  $$ |  $$  $$/ 
+ $$$$$$/     $$/        $$/     $$$$$$$$/ $$$$$$$$/ $$$$$$$$/       $$/       $$/    $$/        $$$$$$/  $$/   $$/    $$$$/  
+                                                                                                                             
+:: (v1.0.0.RELEASE by tsinfotech.co.kr 2020) ::

+ 71 - 0
style24.front/src/main/resources/config/application-dev.yml

@@ -0,0 +1,71 @@
+spring:
+    profiles:
+        active: dev
+    cache:
+        type: redis
+    redis:
+        lettuce:
+            pool:
+                max-active: 10
+                max-idle: 10
+                min-idle: 2
+        host: 116.121.60.104
+        port: 6379
+        password: 
+
+logging.config: classpath:log/logback-dev.xml
+
+domain:
+    wivis: //116.121.60.104
+    image: //116.121.60.104:90
+    uximage: //116.121.60.104
+
+# SSL Server
+has-ssl: false
+
+upload:
+    default:
+        target.path: /home/app/www/data
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png|bmp|txt|doc|docx|ppt|pptx|xls|xlsx|hwp|pdf
+        view: //116.121.60.104:90
+    goods:
+        target.path: /home/app/www/data/goods
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png
+        view: //image.style24.com/speedy_image-wivismall/goods
+    image:
+        target.path: /home/app/www/data
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png
+        view: //116.121.60.104:90
+    excel:
+        target.path: /home/app/www/data/excel
+        max.size: 10
+        allow.extension: xls|xlsx
+        view: //116.121.60.104:90/excel
+
+download.path: /home/app/www/data
+
+# SMTP(운영서버만 설정되어 있어 작동안함)
+mail:
+    host: mail.wivis.com
+    #    port: 465
+    username: admin@wivis.com
+    password: dnlqltm1!
+    protocol: smtp
+    #    tls: true
+    auth: true
+    from: admin@wivis.com
+    wivis.url: http://116.121.60.104
+    image.url: http://116.121.60.104/image/mailing
+    template.path: /home/app/www/front/WEB-INF/mail
+
+# PG
+pg:
+    nicepay:
+        merchantId: nictest00m
+        merchantKey: 33F49GnCMS1mFYlGXisbUDzVf2ATWCl9k3R++d5hDd3Frmuos/XLx8XhXpe+LDYAbpGKZYSwtlyyLOtS/8aD7A==
+        log.path: /home/app/logs/nicepay
+        account.cert.url: https://webapi.nicepay.co.kr/api/checkBankAccountAPI.jsp
+        vbank.refund.url: https://webapi.nicepay.co.kr/v2/api/merchant/vbank_refund.jsp

+ 72 - 0
style24.front/src/main/resources/config/application-locd.yml

@@ -0,0 +1,72 @@
+spring:
+    profiles:
+        active: locd
+    cache:
+        type: redis
+    redis:
+        lettuce:
+            pool:
+                max-active: 10
+                max-idle: 10
+                min-idle: 2
+        host: localhost
+        port: 6379
+        password: 
+
+logging:
+    config: classpath:log/logback-locd.xml
+
+# SSL Server
+has-ssl: false
+
+domain:
+    wivis: //ldfront.style24.com
+    image: //ldimage.style24.com
+    uximage: //ldfront.style24.com
+
+upload:
+    default:
+        target.path: /WIDE/workspace/files/data
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png|bmp|txt|doc|docx|ppt|pptx|xls|xlsx|hwp|pdf
+        view: //ldimage.style24.com
+    goods:
+        target.path: /WIDE/workspace/files/data/goods
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png
+        view: //image.style24.com/speedy_image-wivismall/goods
+    image:
+        target.path: /WIDE/workspace/files/data
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png
+        view: //ldimage.style24.com
+    excel:
+        target.path: /WIDE/workspace/files/data/excel
+        max.size: 10
+        allow.extension: xls|xlsx
+        view: //ldimage.style24.com/excel
+
+download.path: /WIDE/workspace/files/data
+
+# SMTP(naver의 본인 username과 password, from(메일주소) 입려 후 테스트 하면 됨)
+mail:
+    host: mail.wivis.com
+    #    port: 465
+    username: admin@wivis.com
+    password: dnlqltm1!
+    protocol: smtp
+    #    tls: true
+    auth: true
+    from: admin@wivis.com
+    wivis.url: https://ldfront.style24.com
+    image.url: http://ldfront.style24.com/image/mailing
+    template.path: /WIDE/workspace/webapps/wivis/wivismall.front/src/main/webapp/WEB-INF/mail
+
+# PG
+pg:
+    nicepay:
+        merchantId: nictest00m
+        merchantKey: 33F49GnCMS1mFYlGXisbUDzVf2ATWCl9k3R++d5hDd3Frmuos/XLx8XhXpe+LDYAbpGKZYSwtlyyLOtS/8aD7A==
+        log.path: /WIDE/workspace/logs/wivis
+        account.cert.url: https://webapi.nicepay.co.kr/api/checkBankAccountAPI.jsp
+        vbank.refund.url: https://webapi.nicepay.co.kr/v2/api/merchant/vbank_refund.jsp

+ 73 - 0
style24.front/src/main/resources/config/application-locp.yml

@@ -0,0 +1,73 @@
+spring:
+    profiles:
+        active: locp
+    cache:
+        type: redis
+    redis:
+        lettuce:
+            pool:
+                max-active: 10
+                max-idle: 10
+                min-idle: 2
+        host: localhost
+        port: 6379
+        password: 
+
+logging:
+    config: classpath:log/logback-locp.xml
+
+# SSL Server
+has-ssl: false
+
+domain:
+    wivis: //lpfront.style24.com
+    image: //image.style24.com/speedy_image-wivismall
+    uximage: //lpfront.style24.com
+
+upload:
+    default:
+        target.path: /WIDE/workspace/files/data
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png|bmp|txt|doc|docx|ppt|pptx|xls|xlsx|hwp|pdf
+        view: //image.style24.com/speedy_image-wivismall
+    goods:
+        target.path: /WIDE/workspace/files/data/goods
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png
+        view: //image.style24.com/speedy_image-wivismall/goods
+    image:
+        target.path: /WIDE/workspace/files/data
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png
+        view: //image.style24.com/speedy_image-wivismall
+    excel:
+        target.path: /WIDE/workspace/files/data/excel
+        max.size: 10
+        allow.extension: xls|xlsx
+        view: //lpimage.style24.com/excel
+
+download.path: /WIDE/workspace/files/data
+
+# SMTP(naver의 본인 username과 password, from(메일주소) 입려 후 테스트 하면 됨)
+mail:
+    host: mail.wivis.com
+    #    port: 465
+    username: admin@wivis.com
+    password: dnlqltm1!
+    protocol: smtp
+    #    tls: true
+    auth: true
+    from: admin@wivis.com
+    wivis.url: http://lpfront.style24.com
+    image.url: http://lpfront.style24.com/image/mailing
+    template.path: /WIDE/workspace/webapps/wivis/wivismall.front/src/main/webapp/WEB-INF/mail
+
+# PG
+pg:
+    nicepay:
+        merchantId: wivismallm
+        merchantKey: pXTdML8rIaOVCeXQQsyRp1uCfTSTT/n80BV4LCqa+/yJM64MYIqeBCIQdH1rKhJRwSOsdCxVPa1V6hRxkkdJxg==
+        cancelPwd: wivis@2020
+        log.path: /WIDE/workspace/logs/wivis
+        account.cert.url: https://webapi.nicepay.co.kr/api/checkBankAccountAPI.jsp
+        vbank.refund.url: https://webapi.nicepay.co.kr/v2/api/merchant/vbank_refund.jsp

+ 72 - 0
style24.front/src/main/resources/config/application-run.yml

@@ -0,0 +1,72 @@
+spring:
+    profiles:
+        active: run
+    cache:
+        type: redis
+    redis:
+        lettuce:
+            pool:
+                max-active: 10
+                max-idle: 10
+                min-idle: 2
+        host: 192.186.1.30
+        port: 6379
+        password: wivismall
+
+logging.config: classpath:log/logback-run.xml
+
+domain:
+    wivis: //www.style24.com
+    image: //image.style24.com/speedy_image-wivismall
+    uximage: //www.style24.com
+
+# SSL Server
+has-ssl: true
+
+upload:
+    default:
+        target.path: /app/was/deploy/wivismall.front/data
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png|bmp|txt|doc|docx|ppt|pptx|xls|xlsx|hwp|pdf
+        view: //image.style24.com/speedy_image-wivismall
+    goods:
+        target.path: /app/was/deploy/wivismall.front/data/goods
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png
+        view: //image.style24.com/speedy_image-wivismall/goods
+    image:
+        target.path: /app/was/deploy/wivismall.front/data
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png
+        view: //image.style24.com/speedy_image-wivismall
+    excel:
+        target.path: /app/was/deploy/wivismall.front/data/excel
+        max.size: 10
+        allow.extension: xls|xlsx
+        view: //www.style24.com/excel
+
+download.path: /app/was/deploy/wivismall.front/data
+
+# SMTP
+mail:
+    host: mail.wivis.com
+    #    port: 465
+    username: admin@wivis.com
+    password: dnlqltm1!
+    protocol: smtp
+    #    tls: true
+    auth: true
+    from: admin@wivis.com
+    wivis.url: https://www.style24.com
+    image.url: http://www.style24.com/image/mailing
+    template.path: /app/was/deploy/wivismall.front/WEB-INF/mail
+
+# PG
+pg:
+    nicepay:
+        merchantId: wivismallm
+        merchantKey: pXTdML8rIaOVCeXQQsyRp1uCfTSTT/n80BV4LCqa+/yJM64MYIqeBCIQdH1rKhJRwSOsdCxVPa1V6hRxkkdJxg==
+        cancelPwd: wivis@2020
+        log.path: /app/was/applogs/nicepay
+        account.cert.url: https://webapi.nicepay.co.kr/api/checkBankAccountAPI.jsp
+        vbank.refund.url: https://webapi.nicepay.co.kr/v2/api/merchant/vbank_refund.jsp

+ 65 - 0
style24.front/src/main/resources/config/application.yml

@@ -0,0 +1,65 @@
+spring:
+    messages:
+        cache-duration: -1
+        basename: classpath:i18n/messages/message
+        encoding: UTF-8
+    thymeleaf:
+        check-template-location: true
+        prefix: /WEB-INF/views/
+        suffix: .html
+    servlet:
+        multipart:
+            max-file-size: 10MB
+            max-request-size: 10MB
+#    session.store-type: redis
+    data:
+        redis:
+            repositories:
+                enabled: true
+
+server:
+#    servlet:
+#        session:
+#            cookie:
+#                name: WSESSIONID
+#                secure: true
+    error.whitelabel.enabled: false
+
+# 네이버 API
+naver:
+    clientId: OMmbCMu7ac7GgYWgjlhv
+    clientSecret: jwRNdDbEBG
+    login.callbackUrl: /signin/snsLoginCallback?snsType=NV
+    shortUrl: https://openapi.naver.com/v1/util/shorturl
+    tokenUrl: https://nid.naver.com/oauth2.0/token
+    userInfoUrl : https://openapi.naver.com/v1/nid/me
+    authorizeUrl : https://nid.naver.com/oauth2.0/authorize
+
+# SPEEDY Image Upload
+speedy:
+    ftp:
+        host: fileupload.cdn.cloudn.co.kr
+        port: 21
+        username: speedy_image-wivismall
+        pwd: wZ31jS_!@
+
+#카카오 API
+kakao:
+    appId : 399207
+    appName : WIVISMALL
+    companyName : 위비스
+    restApiKey: 8f8db3657b60b2c83df79a37d38becd4
+    nativeAppKey : a4790e2102950309d87ad81a39c0597d
+    javascriptKey : f435c12d89ddb9cc6337f4cf0a05fd30
+    adminKey : 567e9476b15d2149c714aaecd0fee740
+    addressApiRequestUrl : https://dapi.kakao.com/v2/local/search/address.json?page=1&query=
+    login.callbackUrl: /signin/snsLoginCallback?snsType=KK
+    tokenUrl: https://kauth.kakao.com/oauth/token
+    userInfoUrl: https://kapi.kakao.com/v2/user/me
+    authorizeUrl: https://kauth.kakao.com/oauth/authorize
+    unlinkUrl : https://kapi.kakao.com/v1/user/unlink
+
+# APP 다운로드 URL
+app.down.url: 
+    ios: https://apps.apple.com/kr/app/WIVISMALL/id1517275108
+    aos: https://play.google.com/store/apps/details?id=com.wivis.wivismal

+ 87 - 0
style24.front/src/main/resources/i18n/messages/message_ko_KR.properties

@@ -0,0 +1,87 @@
+## -----------------------------------------------------------------------------
+## Message properties
+## -----------------------------------------------------------------------------
+SUCC_0001=\uC131\uACF5\uC801\uC73C\uB85C \uC800\uC7A5\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+SUCC_0002=\uC131\uACF5\uC801\uC73C\uB85C \uC218\uC815\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+SUCC_0003=\uC131\uACF5\uC801\uC73C\uB85C \uC0AD\uC81C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+SUCC_0004=\uC131\uACF5\uC801\uC73C\uB85C \uCC98\uB9AC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+SUCC_0005=\uC131\uACF5\uC801\uC73C\uB85C \uBC1C\uC1A1\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+SUCC_0006=\uC131\uACF5\uC801\uC73C\uB85C \uBC1C\uD589\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+SUCC_0007=\uC131\uACF5\uC801\uC73C\uB85C \uC5C5\uB85C\uB4DC \uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+#SUCC_0008=\uC131\uACF5\uC801\uC73C\uB85C \uB4F1\uB85D\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+SUCC_0009=\uC131\uACF5\uC801\uC73C\uB85C \uBCC0\uACBD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+
+FAIL_0001=\uC624\uB958\uB85C \uC778\uD574 \uC800\uC7A5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.
+FAIL_0002=\uC624\uB958\uB85C \uC778\uD574 \uC218\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.
+FAIL_0003=\uC624\uB958\uB85C \uC778\uD574 \uC0AD\uC81C\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.
+FAIL_0004=\uC624\uB958\uB85C \uC778\uD574 \uCC98\uB9AC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.
+FAIL_0005=\uC624\uB958\uB85C \uC778\uD574 \uBC1C\uC1A1\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.
+FAIL_0006=\uC624\uB958\uB85C \uC778\uD574 \uBC1C\uD589\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.
+FAIL_0007=\uC624\uB958\uB85C \uC778\uD574 \uC5C5\uB85C\uB4DC \uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.
+#FAIL_0008=\uC624\uB958\uB85C \uC778\uD574 \uB4F1\uB85D\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.
+FAIL_0009=\uC624\uB958\uB85C \uC778\uD574 \uBCC0\uACBD\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.
+FAIL_1001=\uC800\uC7A5\uD560 \uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.
+FAIL_1002=\uC804\uC1A1\uD560 \uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.
+
+LOGN_0001=\uC785\uB825\uD558\uC2E0 \uC815\uBCF4\uB85C \uAC00\uC785\uB41C \uB0B4\uC5ED\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.
+LOGN_0002=\uBE44\uBC00\uBC88\uD638\uAC00 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.
+LOGN_0003=\uB85C\uADF8\uC778 \uC0C1\uD0DC\uAC00 \uC544\uB2D9\uB2C8\uB2E4. \uB2E4\uC2DC \uB85C\uADF8\uC778 \uD574\uC8FC\uC138\uC694.
+LOGN_0004=\uBE44\uBC00\uBC88\uD638\uB97C \uBCC0\uACBD\uD55C \uB0A0\uB85C\uBD80\uD130 3\uAC1C\uC6D4\uC774 \uACBD\uACFC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+LOGN_0005=\uBE44\uBC00\uBC88\uD638\uAC00 5\uD68C \uC774\uC0C1 \uD2C0\uB824 \uACC4\uC815\uC774 \uC7A0\uACBC\uC2B5\uB2C8\uB2E4.
+LOGN_0006=\uD734\uBA74 \uD68C\uC6D0\uC785\uB2C8\uB2E4.
+LOGN_0007=\uD0C8\uD1F4 \uD68C\uC6D0\uC785\uB2C8\uB2E4.
+LOGN_0008=\uC774\uBBF8 \uAC00\uC785\uD558\uC2E0 \uC774\uBA54\uC77C\uC774 \uC874\uC7AC\uD569\uB2C8\uB2E4.
+
+##\uC7A5\uBC14\uAD6C\uB2C8
+CART_0001=\uC7A5\uBC14\uAD6C\uB2C8\uC5D0 \uB2F4\uACBC\uC2B5\uB2C8\uB2E4.
+
+##\uC8FC\uBB38
+ORDER_0001=\uC8FC\uBB38\uC815\uBCF4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.
+ORDER_0002=\uC8FC\uBB38\uC0C1\uD0DC\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.\n[\uACB0\uC81C\uC644\uB8CC][\uCD9C\uACE0\uC644\uB8CC] \uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uC0C1\uD488\uC900\uBE44\uC911\uC73C\uB85C \uBCC0\uACBD\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
+ORDER_0003=\uD574\uB2F9 \uC0C1\uD488 \uBE0C\uB79C\uB4DC \uC218\uC815 \uAD8C\uD55C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.
+ORDER_0004=\uC8FC\uBB38\uC0C1\uD0DC\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.\n[\uC0C1\uD488\uC900\uBE44\uC911][\uAD6C\uB9E4\uD655\uC815] \uC0C1\uD0DC\uC5D0\uB9CC \uC1A1\uC7A5\uBC88\uD638\uB97C \uC785\uB825\uD558\uC2E4\uC218 \uC788\uC2B5\uB2C8\uB2E4.
+ORDER_0005=\uC8FC\uBB38\uC0C1\uD0DC\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.\n[\uCD9C\uACE0\uC644\uB8CC]\uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uAD6C\uB9E4\uD655\uC815 \uC0C1\uD0DC\uB85C \uBCC0\uACBD\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
+ORDER_0006=\uAD6C\uB9E4\uD655\uC815\uC744 \uD558\uC2E4\uC218 \uC788\uB294 \uC0C1\uD488\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \n\uBC18\uD488\uC774\uB098 \uAD50\uD658\uC0C1\uD0DC\uB97C \uD655\uC778\uD574\uC8FC\uC2DC\uAE30 \uBC14\uB78D\uB2C8\uB2E4.
+ORDER_0007=\uC8FC\uBB38 \uC804\uCCB4\uCDE8\uC18C\uB97C \uD558\uC2E4\uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n\uAD50\uD658,\uBC18\uD488,\uCDE8\uC18C\uC911\uC778 \uC0C1\uD488\uC774 \uC788\uB294\uC9C0 \uD655\uC778 \uBC14\uB78D\uB2C8\uB2E4.
+ORDER_0008=\uD0C0 \uC5C5\uCCB4\uC758 \uC0C1\uD488\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uC804\uCCB4 \uBC18\uD488\uC744 \uC9C4\uD589\uD558\uC2E4\uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
+ORDER_0009=\uD3EC\uC778\uD2B8 \uC6D0\uBCF5 \uCC98\uB9AC\uB97C \uC2E4\uD328\uD558\uC600\uC2B5\uB2C8\uB2E4.
+ORDER_0010=\uACB0\uC81C \uCDE8\uC18C\uB97C \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.
+ORDER_0011=\uCE74\uB4DC \uACB0\uC81C\uC778 \uACBD\uC6B0\uC5D0\uB9CC \uBD80\uBD84\uCDE8\uC18C\uAC00 \uAC00\uB2A5\uD569\uB2C8\uB2E4.
+ORDER_0012=\uCDE8\uC18C\uC218\uB7C9\uC744 \uD655\uC778\uD574\uC8FC\uC138\uC694. \uCDE8\uC18C\uAC00\uB2A5 \uC218\uB7C9\uBCF4\uB2E4 \uB9CE\uC2B5\uB2C8\uB2E4.
+ORDER_0013=\uC8FC\uBB38\uC0C1\uD0DC\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.\n[\uACB0\uC81C\uC644\uB8CC][\uC0C1\uD488\uC900\uBE44\uC911]\uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uBD80\uBD84\uCDE8\uC18C\uAC00 \uAC00\uB2A5\uD569\uB2C8\uB2E4.
+ORDER_0014=\uCD9C\uACE0\uC644\uB8CC\uB97C \uD558\uC2E4\uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n\uC8FC\uBB38 \uCDE8\uC18C\uC2E0\uCCAD\uB41C \uC8FC\uBB38\uC744 \uBA3C\uC800 \uCC98\uB9AC\uD574\uC8FC\uC2DC\uAE30\uBC14\uB78D\uB2C8\uB2E4.
+ORDER_0015=\uC8FC\uBB38\uC0C1\uD0DC\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.\n[\uCD9C\uACE0\uC644\uB8CC]\uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uC8FC\uBB38\uAD50\uD658\uC774 \uAC00\uB2A5\uD569\uB2C8\uB2E4.
+ORDER_0016=\uAD50\uD658\uC218\uB7C9\uC744 \uD655\uC778\uD574\uC8FC\uC138\uC694.\uAD50\uD658\uAC00\uB2A5 \uC218\uB7C9\uBCF4\uB2E4 \uB9CE\uC2B5\uB2C8\uB2E4.
+ORDER_0017=\uC8FC\uBB38\uC0C1\uD0DC\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.\n[\uAD50\uD658\uC2E0\uCCAD]\uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uAD50\uD658\uC9C4\uD589\uC774 \uAC00\uB2A5\uD569\uB2C8\uB2E4.
+ORDER_0018=\uC8FC\uBB38\uC0C1\uD0DC\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.\n[\uAD50\uD658\uC9C4\uD589]\uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uAD50\uD658\uC644\uB8CC\uAC00 \uAC00\uB2A5\uD569\uB2C8\uB2E4.
+ORDER_0019=\uC8FC\uBB38\uC0C1\uD0DC\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.\n[\uAD50\uD658\uC2E0\uCCAD]\uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uAD50\uD658\uBC18\uB824\uAC00 \uAC00\uB2A5\uD569\uB2C8\uB2E4.
+ORDER_0020=\uC8FC\uBB38\uC0C1\uD0DC\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.\n[\uBC18\uD488\uC2E0\uCCAD]\uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uBC18\uD488\uC9C4\uD589\uC774 \uAC00\uB2A5\uD569\uB2C8\uB2E4.
+ORDER_0021=\uC8FC\uBB38\uC0C1\uD0DC\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.\n[\uBC18\uD488\uC9C4\uD589]\uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uBC18\uD488\uC644\uB8CC\uAC00 \uAC00\uB2A5\uD569\uB2C8\uB2E4.
+ORDER_0022=\uC8FC\uBB38\uC0C1\uD0DC\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.\n[\uBC18\uD488\uC2E0\uCCAD]\uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uBC18\uD488\uBC18\uB824\uAC00 \uAC00\uB2A5\uD569\uB2C8\uB2E4.
+ORDER_0023=\uC8FC\uBB38\uC0C1\uD0DC\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.\n[\uCDE8\uC18C\uC2E0\uCCAD]\uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uCDE8\uC18C\uBC18\uB824\uAC00 \uAC00\uB2A5\uD569\uB2C8\uB2E4.
+ORDER_0024=\uC8FC\uBB38\uC0C1\uD0DC\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.\n[\uCDE8\uC18C\uC2E0\uCCAD]\uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uCDE8\uC18C\uC644\uB8CC\uAC00 \uAC00\uB2A5\uD569\uB2C8\uB2E4.
+ORDER_0025=\uBC18\uD488\uC218\uB7C9\uC744 \uD655\uC778\uD574\uC8FC\uC138\uC694. \uBC18\uD488\uAC00\uB2A5 \uC218\uB7C9\uBCF4\uB2E4 \uB9CE\uC2B5\uB2C8\uB2E4.
+ORDER_0026=\uC8FC\uBB38 \uC804\uCCB4\uBC18\uD488\uC744 \uD558\uC2E4\uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n\uAD50\uD658,\uBC18\uD488,\uCDE8\uC18C\uC911\uC778 \uC0C1\uD488\uC774 \uC788\uB294\uC9C0 \uD655\uC778 \uBC14\uB78D\uB2C8\uB2E4.
+ORDER_0027=\uC8FC\uBB38\uC0C1\uD0DC\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.\n[\uC0C1\uD488\uC900\uBE44\uC911] \uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uACB0\uC81C\uC644\uB8CC \uBCC0\uACBD\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
+ORDER_0028=\uACB0\uC81C \uC644\uB8CC \uC0C1\uD0DC\uB85C \uBCC0\uACBD\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n\uCDE8\uC18C,\uBC18\uD488\uC774 \uC644\uB8CC\uB41C \uC8FC\uBB38\uC785\uB2C8\uB2E4.
+ORDER_0029=\uC8FC\uBB38 \uC0C1\uD0DC\uB97C \uD655\uC778\uD574 \uC8FC\uC138\uC694. [\uCD9C\uACE0\uC911][\uAD6C\uB9E4\uD655\uC815]\uC0C1\uD0DC\uC778 \uC8FC\uBB38\uAC74\uC774 \uC788\uC2B5\uB2C8\uB2E4.
+ORDER_0030=\uD544\uC218\uAC12\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.
+ORDER_0031=\uC8FC\uBB38\uC804\uCCB4\uCDE8\uC18C \uCC98\uB9AC\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+
+##\uD68C\uC6D0
+CUST_0001=\uC815\uC0C1\uC801\uC73C\uB85C \uD0C8\uD1F4\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+CUST_0002=\uC774\uBBF8 \uD0C8\uD1F4\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+CUST_0003=\uC9C4\uD589\uC911\uC778 \uAC70\uB798\uAC00 \uC788\uC2B5\uB2C8\uB2E4. \uC9C4\uD589\uC911\uC778 \uAC70\uB798\uB97C \uC644\uB8CC \uD6C4 \uD0C8\uD1F4 \uC2E0\uCCAD\uC744 \uD574\uC8FC\uC138\uC694.
+CUST_0004=\uD0C8\uD1F4\uAC00 \uC815\uC0C1\uC801\uC73C\uB85C \uCC98\uB9AC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.
+
+#\uC0AC\uBC29\uB137
+SABANGNET_0001=\uC131\uACF5\uC801\uC73C\uB85C \uC0C1\uD488 \uC815\uBCF4\uAC00 \uC804\uC1A1\uB418\uC5C8\uC2B5\uB2C8\uB2E4.(\uCD1D {0}\uAC74 \uC911 {1}\uAC74 \uC131\uACF5)
+SABANGNET_0002=\uC131\uACF5\uC801\uC73C\uB85C \uC1FC\uD551\uBAB0\uBCC4 DATA \uAC00 \uC804\uC1A1\uB418\uC5C8\uC2B5\uB2C8\uB2E4.(\uCD1D {0}\uAC74 \uC911 {1}\uAC74 \uC131\uACF5)
+SABANGNET_0003=\uC131\uACF5\uC801\uC73C\uB85C \uC0C1\uD488 \uC694\uC57D \uC815\uBCF4\uAC00 \uC804\uC1A1\uB418\uC5C8\uC2B5\uB2C8\uB2E4.(\uCD1D {0}\uAC74 \uC911 {1}\uAC74 \uC131\uACF5)
+SABANGNET_0004=\uC8FC\uBB38\uC774 \uC218\uC9D1\uB418\uC5C8\uC2B5\uB2C8\uB2E4.(\uCD1D {0}\uAC74 \uC911 {1}\uAC74 \uC131\uACF5, \uC2E4\uD328: {2}\uAC74)
+SABANGNET_0005=\uC131\uACF5\uC801\uC73C\uB85C \uC1A1\uC7A5\uBC88\uD638\uAC00 \uB4F1\uB85D\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+SABANGNET_0006=\uCDE8\uC18C\uC8FC\uBB38\uC774 \uC218\uC9D1\uB418\uC5C8\uC2B5\uB2C8\uB2E4.(\uCD1D {0}\uAC74 \uC911 {1}\uAC74 \uC2E4\uD328 {2}\uAC74 \uC131\uACF5)
+SABANGNET_0007=\uAD50\uD658\uC8FC\uBB38\uC774 \uC218\uC9D1\uB418\uC5C8\uC2B5\uB2C8\uB2E4.(\uCD1D {0}\uAC74 \uC911 {1}\uAC74 \uC2E4\uD328 {2}\uAC74 \uC131\uACF5)
+SABANGNET_0008=\uBC18\uD488\uC8FC\uBB38\uC774 \uC218\uC9D1\uB418\uC5C8\uC2B5\uB2C8\uB2E4.(\uCD1D {0}\uAC74 \uC911 {1}\uAC74 \uC2E4\uD328 {2}\uAC74 \uC131\uACF5)
+SABANGNET_0009=\uC720\uD6A8\uC131 \uAC80\uC99D \uC2E4\uD328\uB85C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
+SABANGNET_0010=\uCD9C\uACE0\uB97C \uC704\uD574 ERP\uB85C \uC804\uC1A1\uD558\uC600\uC2B5\uB2C8\uB2E4.(\uC131\uACF5: {0}\uAC74, \uC2E4\uD328: {1}\uAC74)

+ 25 - 0
style24.front/src/main/resources/log/logback-dev.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<configuration scan="true">
+	<property name="LOG_HOME" value="./logs/applog"/>
+	<property name="LOG_LEVEL" value="DEBUG"/>
+	
+	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<charset>utf-8</charset>
+			<pattern>[%d] [%thread] %-5level %logger{32} : %msg%n</pattern>
+		</encoder>
+	</appender>
+
+	<logger name="org.springframework" level="ERROR"/>
+	
+	<!-- SQL문과 해당 SQL을 실행시키는데 수행된 시간 정보(milliseconds)를 포함한다. -->
+	<logger name="jdbc.sqltiming" level="INFO" additivity="false">
+		<appender-ref ref="CONSOLE"/>
+	</logger>
+	
+	<root level="${LOG_LEVEL}">
+		<appender-ref ref="CONSOLE"/>
+	</root>
+
+</configuration>

+ 41 - 0
style24.front/src/main/resources/log/logback-locd.xml

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<configuration scan="true">
+	<property name="LOG_HOME" value="/WIDE/workspace/logs/style24/front"/>
+	<property name="LOG_LEVEL" value="INFO"/>
+	
+	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<charset>utf-8</charset>
+			<pattern>[%d] [%thread] %-5level %logger{32} : %msg%n</pattern>
+		</encoder>
+	</appender>
+	
+	<logger name="org.springframework" level="ERROR"/>
+	
+	<!-- SQL문만을 로그로 남기며, PreparedStatement일 경우 관련된 argument 값으로 대체된 SQL문이 보여진다. -->
+	<!-- <logger name="jdbc.sqlonly" level="${LOG_LEVEL}">
+		<appender-ref ref="CONSOLE"/>
+	</logger> -->
+	
+	<!-- SQL문과 해당 SQL을 실행시키는데 수행된 시간 정보(milliseconds)를 포함한다. -->
+	<!-- <logger name="jdbc.sqltiming" level="${LOG_LEVEL}">
+		<appender-ref ref="CONSOLE"/>
+	</logger> -->
+	
+	<!-- ResultSet을 제외한 모든 JDBC 호출 정보를 로그로 남긴다. -->
+	<!-- 많은 양의 로그가 생성되므로 특별히 JDBC 문제를 추적해야 할 필요가 있는 경우를 제외하고는 사용을 권장하지 않는다. -->
+	<!-- <logger name="jdbc.audit" level="ERROR">
+		<appender-ref ref="CONSOLE"/>
+	</logger> -->
+	
+	<!-- ResultSet을 포함한 모든 JDBC 호출 정보를 로그로 남기므로 매우 방대한 양의 로그가 생성된다. -->
+	<!-- <logger name="jdbc.resultset" level="ERROR">
+		<appender-ref ref="CONSOLE"/>
+	</logger> -->
+
+	<root level="${LOG_LEVEL}">
+		<appender-ref ref="CONSOLE"/>
+	</root>
+
+</configuration>

+ 41 - 0
style24.front/src/main/resources/log/logback-locp.xml

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<configuration scan="true">
+	<property name="LOG_HOME" value="/WIDE/workspace/logs/style24/front"/>
+	<property name="LOG_LEVEL" value="INFO"/>
+	
+	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<charset>utf-8</charset>
+			<pattern>[%d] [%thread] %-5level %logger{32} : %msg%n</pattern>
+		</encoder>
+	</appender>
+
+	<logger name="org.springframework" level="ERROR"/>
+
+	<!-- SQL문만을 로그로 남기며, PreparedStatement일 경우 관련된 argument 값으로 대체된 SQL문이 보여진다. -->
+	<!-- <logger name="jdbc.sqlonly" level="${LOG_LEVEL}">
+		<appender-ref ref="CONSOLE"/>
+	</logger> -->
+	
+	<!-- SQL문과 해당 SQL을 실행시키는데 수행된 시간 정보(milliseconds)를 포함한다. -->
+	<!-- <logger name="jdbc.sqltiming" level="${LOG_LEVEL}">
+		<appender-ref ref="CONSOLE"/>
+	</logger> -->
+	
+	<!-- ResultSet을 제외한 모든 JDBC 호출 정보를 로그로 남긴다. -->
+	<!-- 많은 양의 로그가 생성되므로 특별히 JDBC 문제를 추적해야 할 필요가 있는 경우를 제외하고는 사용을 권장하지 않는다. -->
+	<!-- <logger name="jdbc.audit" level="ERROR">
+		<appender-ref ref="CONSOLE"/>
+	</logger> -->
+	
+	<!-- ResultSet을 포함한 모든 JDBC 호출 정보를 로그로 남기므로 매우 방대한 양의 로그가 생성된다. -->
+	<!-- <logger name="jdbc.resultset" level="ERROR">
+		<appender-ref ref="CONSOLE"/>
+	</logger> -->
+
+	<root level="${LOG_LEVEL}">
+		<appender-ref ref="CONSOLE"/>
+	</root>
+
+</configuration>

+ 39 - 0
style24.front/src/main/resources/log/logback-run.xml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<configuration scan="true">
+	<property name="LOG_HOME" value="./logs/applog"/>
+	<property name="LOG_LEVEL" value="INFO"/>
+	
+	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<charset>utf-8</charset>
+			<pattern>[%d] [%thread] %-5level %logger{32} : %msg%n</pattern>
+		</encoder>
+	</appender>
+
+	<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${LOG_HOME}/wivismall_front.log</file>
+		<encoder>
+			<charset>utf-8</charset>
+			<pattern>[%d] [%thread] %-5level %logger{32} : %msg%n</pattern>
+		</encoder>
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+			<fileNamePattern>${LOG_HOME}/wivismall_front.%d{yyyy-MM-dd}.log.zip</fileNamePattern>
+			<!-- keep 30 days' worth of history -->
+			<maxHistory>30</maxHistory>
+		</rollingPolicy>
+	</appender>
+
+	<logger name="org.springframework" level="ERROR"/>
+	
+	<!-- SQL문과 해당 SQL을 실행시키는데 수행된 시간 정보(milliseconds)를 포함한다. -->
+	<logger name="jdbc.sqltiming" level="INFO" additivity="false">
+		<appender-ref ref="CONSOLE"/>
+	</logger>
+
+	<root level="${LOG_LEVEL}">
+		<appender-ref ref="CONSOLE"/>
+		<appender-ref ref="FILE" />
+	</root>
+
+</configuration>

+ 23 - 0
style24.front/src/main/resources/persistence/mybatis-shop-config.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "HTTP://mybatis.org/dtd/mybatis-3-config.dtd">
+
+<configuration>
+
+	<settings>
+		<setting name="cacheEnabled" value="false" /> <!-- 설정에서 각 매퍼에 설정된 캐시를 전역적으로 사용할지 말지에 대한 여부 (default true)-->
+		<!-- <setting name="lazyLoadingEnabled" value="true" /> --> <!-- 지연로딩을 사용할지에 대한 여부. 사용하지 않는다면 모두 즉시 로딩 (default true) -->
+		<!-- <setting name="multipleResultSetsEnabled" value="true" /> --> <!-- 한개의 구문에서 여러개의 ResultSet을 허용할지의 여부 (default true) -->
+		<setting name="useGeneratedKeys" value="true" /> <!-- 생성키를 강제로 생성 (default false) -->
+		<setting name="defaultExecutorType" value="REUSE" /> <!-- 디폴트 실행자(executor) 설정. PreparedStatement를 재사용 (default SIMPLE)-->
+		<setting name="defaultStatementTimeout" value="25" /> <!-- 데이터베이스로의 응답을 얼마나 오래 기다릴지를 판단하는 타임아웃(초)를 설정 -->
+		<setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 데이터베이스 칼럼명 형태인 A_COLUMN을 CamelCase 형태의 자바 프로퍼티명 형태인 aColumn으로 자동으로 매핑하도록 함 (default false) -->
+		<setting name="logImpl" value="NO_LOGGING" /> <!-- 마이바티스가 사용할 로깅 구현체를 명시. 이 설정을 사용하지 않으면 마이바티스가 사용할 로깅 구현체를 자동으로 찾는다. -->
+		<setting name="jdbcTypeForNull" value="NULL"/> <!-- JDBC 타입을 파라미터에 제공하지 않을 때 null 값을 처리한 JDBC 타입을 명시 -->
+		<setting name="callSettersOnNulls" value="true"/> <!-- 가져온 값이 null일때 setter나 맵의 put 메소드를 호출할지를 명시 (default false) -->
+	</settings>
+	
+	<typeAliases>
+		<typeAlias alias="paramMap" type="com.gagaframework.web.parameter.GagaMap" />
+	</typeAliases>
+
+</configuration>

BIN
style24.front/src/main/webapp/WEB-INF/lib/gagaframework-web-core-1.7.1-RELEASE.jar


BIN
style24.front/src/main/webapp/WEB-INF/lib/gagaframework-web-parameter-1.7-RELEASE.jar


BIN
style24.front/src/main/webapp/WEB-INF/lib/gagaframework-web-rest-1.7-RELEASE.jar


BIN
style24.front/src/main/webapp/WEB-INF/lib/gagaframework-web-security-1.7.1-RELEASE.jar


BIN
style24.front/src/main/webapp/WEB-INF/lib/gagaframework-web-util-1.7-RELEASE.jar


+ 2 - 0
style24.front/src/main/webapp/WEB-INF/robots.txt

@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /

+ 11 - 0
style24.front/target/classes/banner.txt

@@ -0,0 +1,11 @@
+  ______   ________  __      __  __        ________   ______   __    __        ________                               __     
+ /      \ /        |/  \    /  |/  |      /        | /      \ /  |  /  |      /        |                             /  |    
+/$$$$$$  |$$$$$$$$/ $$  \  /$$/ $$ |      $$$$$$$$/ /$$$$$$  |$$ |  $$ |      $$$$$$$$/______    ______   _______   _$$ |_   
+$$ \__$$/    $$ |    $$  \/$$/  $$ |      $$ |__    $$____$$ |$$ |__$$ |      $$ |__  /      \  /      \ /       \ / $$   |  
+$$      \    $$ |     $$  $$/   $$ |      $$    |    /    $$/ $$    $$ |      $$    |/$$$$$$  |/$$$$$$  |$$$$$$$  |$$$$$$/   
+ $$$$$$  |   $$ |      $$$$/    $$ |      $$$$$/    /$$$$$$/  $$$$$$$$ |      $$$$$/ $$ |  $$/ $$ |  $$ |$$ |  $$ |  $$ | __ 
+/  \__$$ |   $$ |       $$ |    $$ |_____ $$ |_____ $$ |_____       $$ |      $$ |   $$ |      $$ \__$$ |$$ |  $$ |  $$ |/  |
+$$    $$/    $$ |       $$ |    $$       |$$       |$$       |      $$ |      $$ |   $$ |      $$    $$/ $$ |  $$ |  $$  $$/ 
+ $$$$$$/     $$/        $$/     $$$$$$$$/ $$$$$$$$/ $$$$$$$$/       $$/       $$/    $$/        $$$$$$/  $$/   $$/    $$$$/  
+                                                                                                                             
+:: (v1.0.0.RELEASE by tsinfotech.co.kr 2020) ::

BIN
style24.front/target/classes/com/style24/front/biz/dao/TsfLoginDao.class


BIN
style24.front/target/classes/com/style24/front/biz/dao/TsfNoticeDao.class


BIN
style24.front/target/classes/com/style24/front/biz/service/TsfLoginService.class


BIN
style24.front/target/classes/com/style24/front/biz/service/TsfNoticeService.class


BIN
style24.front/target/classes/com/style24/front/biz/web/TsfCustomerController.class


BIN
style24.front/target/classes/com/style24/front/biz/web/TsfIndexController.class


Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff