gagamel 5 gadi atpakaļ
revīzija
8f73620242
100 mainītis faili ar 9954 papildinājumiem un 0 dzēšanām
  1. 17 0
      .project
  2. 4 0
      .settings/org.eclipse.m2e.core.prefs
  3. 140 0
      pom.xml
  4. 45 0
      style24.admin/.classpath
  5. 42 0
      style24.admin/.project
  6. 13 0
      style24.admin/.settings/.jsdtscope
  7. 6 0
      style24.admin/.settings/org.eclipse.core.resources.prefs
  8. 12 0
      style24.admin/.settings/org.eclipse.jdt.core.prefs
  9. 4 0
      style24.admin/.settings/org.eclipse.m2e.core.prefs
  10. 10 0
      style24.admin/.settings/org.eclipse.wst.common.component
  11. 7 0
      style24.admin/.settings/org.eclipse.wst.common.project.facet.core.xml
  12. 1 0
      style24.admin/.settings/org.eclipse.wst.jsdt.ui.superType.container
  13. 1 0
      style24.admin/.settings/org.eclipse.wst.jsdt.ui.superType.name
  14. 2 0
      style24.admin/.settings/org.eclipse.wst.validation.prefs
  15. 2 0
      style24.admin/.settings/org.springframework.ide.eclipse.prefs
  16. 84 0
      style24.admin/pom.xml
  17. 68 0
      style24.admin/src/main/java/com/style24/admin/biz/dao/TsaLoginDao.java
  18. 211 0
      style24.admin/src/main/java/com/style24/admin/biz/dao/TsaRendererDao.java
  19. 212 0
      style24.admin/src/main/java/com/style24/admin/biz/dao/TsaSystemDao.java
  20. 111 0
      style24.admin/src/main/java/com/style24/admin/biz/service/TsaLoginService.java
  21. 465 0
      style24.admin/src/main/java/com/style24/admin/biz/service/TsaRendererService.java
  22. 394 0
      style24.admin/src/main/java/com/style24/admin/biz/service/TsaSystemService.java
  23. 55 0
      style24.admin/src/main/java/com/style24/admin/biz/web/TsaIndexController.java
  24. 379 0
      style24.admin/src/main/java/com/style24/admin/biz/web/TsaSystemController.java
  25. 48 0
      style24.admin/src/main/java/com/style24/admin/support/config/TsaMybatisShopConfig.java
  26. 100 0
      style24.admin/src/main/java/com/style24/admin/support/config/TsaRedisSessionConfig.java
  27. 70 0
      style24.admin/src/main/java/com/style24/admin/support/config/TsaThymeleafConfig.java
  28. 119 0
      style24.admin/src/main/java/com/style24/admin/support/config/TsaWebMvcConfig.java
  29. 49 0
      style24.admin/src/main/java/com/style24/admin/support/controller/TsaBaseController.java
  30. 65 0
      style24.admin/src/main/java/com/style24/admin/support/controller/TsaErrorController.java
  31. 14 0
      style24.admin/src/main/java/com/style24/admin/support/env/TsaConstants.java
  32. 64 0
      style24.admin/src/main/java/com/style24/admin/support/interceptor/TsaDefaultInterceptor.java
  33. 48 0
      style24.admin/src/main/java/com/style24/admin/support/interceptor/TsaPosInterceptor.java
  34. 90 0
      style24.admin/src/main/java/com/style24/admin/support/security/TsaAuthenticationProvider.java
  35. 97 0
      style24.admin/src/main/java/com/style24/admin/support/security/TsaLoginDetails.java
  36. 117 0
      style24.admin/src/main/java/com/style24/admin/support/security/config/TsaSecurityConfig.java
  37. 73 0
      style24.admin/src/main/java/com/style24/admin/support/security/handler/TsaLoginSuccessHandler.java
  38. 33 0
      style24.admin/src/main/java/com/style24/admin/support/security/handler/TsaLogoutSuccessHandler.java
  39. 85 0
      style24.admin/src/main/java/com/style24/admin/support/security/session/TsaSession.java
  40. 54 0
      style24.admin/src/main/java/com/style24/admin/support/startup/TsaApplication.java
  41. 19 0
      style24.admin/src/main/java/com/style24/admin/support/startup/TsaServletInitializer.java
  42. 44 0
      style24.admin/src/main/java/com/style24/persistence/TsaBaseDomain.java
  43. 123 0
      style24.admin/src/main/java/com/style24/persistence/TsaPageRequest.java
  44. 81 0
      style24.admin/src/main/java/com/style24/persistence/domain/Brand.java
  45. 26 0
      style24.admin/src/main/java/com/style24/persistence/domain/Color.java
  46. 29 0
      style24.admin/src/main/java/com/style24/persistence/domain/CommonCode.java
  47. 41 0
      style24.admin/src/main/java/com/style24/persistence/domain/DeliveryLoc.java
  48. 27 0
      style24.admin/src/main/java/com/style24/persistence/domain/Itemkind.java
  49. 45 0
      style24.admin/src/main/java/com/style24/persistence/domain/Menu.java
  50. 23 0
      style24.admin/src/main/java/com/style24/persistence/domain/MenuAccessHst.java
  51. 21 0
      style24.admin/src/main/java/com/style24/persistence/domain/MenuRole.java
  52. 90 0
      style24.admin/src/main/java/com/style24/persistence/domain/Pos.java
  53. 42 0
      style24.admin/src/main/java/com/style24/persistence/domain/SupplyCompany.java
  54. 60 0
      style24.admin/src/main/java/com/style24/persistence/domain/User.java
  55. 25 0
      style24.admin/src/main/java/com/style24/persistence/domain/UserHst.java
  56. 24 0
      style24.admin/src/main/java/com/style24/persistence/domain/UserMenu.java
  57. 212 0
      style24.admin/src/main/java/com/style24/persistence/mybatis/shop/TsaLogin.xml
  58. 352 0
      style24.admin/src/main/java/com/style24/persistence/mybatis/shop/TsaRenderer.xml
  59. 569 0
      style24.admin/src/main/java/com/style24/persistence/mybatis/shop/TsaSystem.xml
  60. 8 0
      style24.admin/src/main/resources/banner.txt
  61. 82 0
      style24.admin/src/main/resources/config/application-dev.yml
  62. 66 0
      style24.admin/src/main/resources/config/application-locd.yml
  63. 82 0
      style24.admin/src/main/resources/config/application-locp.yml
  64. 82 0
      style24.admin/src/main/resources/config/application-run.yml
  65. 64 0
      style24.admin/src/main/resources/config/application.yml
  66. 78 0
      style24.admin/src/main/resources/i18n/messages/message_ko_KR.properties
  67. 25 0
      style24.admin/src/main/resources/log/logback-dev.xml
  68. 41 0
      style24.admin/src/main/resources/log/logback-locd.xml
  69. 55 0
      style24.admin/src/main/resources/log/logback-locp.xml
  70. 39 0
      style24.admin/src/main/resources/log/logback-run.xml
  71. 23 0
      style24.admin/src/main/resources/persistence/mybatis-shop-config.xml
  72. BIN
      style24.admin/src/main/webapp/WEB-INF/lib/gagaframework-web-core-1.7-RELEASE.jar
  73. BIN
      style24.admin/src/main/webapp/WEB-INF/lib/gagaframework-web-parameter-1.7-RELEASE.jar
  74. BIN
      style24.admin/src/main/webapp/WEB-INF/lib/gagaframework-web-rest-1.7-RELEASE.jar
  75. BIN
      style24.admin/src/main/webapp/WEB-INF/lib/gagaframework-web-util-1.7-RELEASE.jar
  76. 2 0
      style24.admin/src/main/webapp/WEB-INF/robots.txt
  77. 25 0
      style24.admin/src/main/webapp/WEB-INF/views/common/fragments/footer.html
  78. 134 0
      style24.admin/src/main/webapp/WEB-INF/views/common/fragments/gnb.html
  79. 60 0
      style24.admin/src/main/webapp/WEB-INF/views/common/fragments/header.html
  80. 95 0
      style24.admin/src/main/webapp/WEB-INF/views/common/fragments/lnb.html
  81. 34 0
      style24.admin/src/main/webapp/WEB-INF/views/common/fragments/variables.html
  82. 46 0
      style24.admin/src/main/webapp/WEB-INF/views/common/layout/dashboard.html
  83. 46 0
      style24.admin/src/main/webapp/WEB-INF/views/common/layout/default.html
  84. 23 0
      style24.admin/src/main/webapp/WEB-INF/views/common/layout/error.html
  85. 23 0
      style24.admin/src/main/webapp/WEB-INF/views/common/layout/login.html
  86. 65 0
      style24.admin/src/main/webapp/WEB-INF/views/dashboard.html
  87. 53 0
      style24.admin/src/main/webapp/WEB-INF/views/error/500.html
  88. 107 0
      style24.admin/src/main/webapp/WEB-INF/views/signin.html
  89. 266 0
      style24.admin/src/main/webapp/WEB-INF/views/system/BasicAnswerForm.html
  90. 735 0
      style24.admin/src/main/webapp/WEB-INF/views/system/BasicEnvsetForm.html
  91. 141 0
      style24.admin/src/main/webapp/WEB-INF/views/system/BizdayForm.html
  92. 206 0
      style24.admin/src/main/webapp/WEB-INF/views/system/ClauseDetailForm.html
  93. 147 0
      style24.admin/src/main/webapp/WEB-INF/views/system/ClauseListForm.html
  94. 247 0
      style24.admin/src/main/webapp/WEB-INF/views/system/CommoncodeForm.html
  95. 229 0
      style24.admin/src/main/webapp/WEB-INF/views/system/EnvsetHistoryForm.html
  96. 164 0
      style24.admin/src/main/webapp/WEB-INF/views/system/HilandDeliveryFeeDetailForm.html
  97. 104 0
      style24.admin/src/main/webapp/WEB-INF/views/system/HilandDeliveryFeeListForm.html
  98. 443 0
      style24.admin/src/main/webapp/WEB-INF/views/system/MenuForm.html
  99. 559 0
      style24.admin/src/main/webapp/WEB-INF/views/system/PointEnvsetForm.html
  100. 91 0
      style24.admin/src/main/webapp/WEB-INF/views/system/PopupPasswordChangeForm.html

+ 17 - 0
.project

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>STYLE24</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+	</natures>
+</projectDescription>

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

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

+ 140 - 0
pom.xml

@@ -0,0 +1,140 @@
+<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>org.springframework.boot</groupId>
+		<artifactId>spring-boot-starter-parent</artifactId>
+		<version>2.3.3.RELEASE</version>
+		<relativePath/> <!-- lookup parent from repository -->
+	</parent>
+	<groupId>com.style24</groupId>
+	<artifactId>root</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<packaging>pom</packaging>
+	<name>com.style24.root</name>
+	<description>STYLE24 ROOT</description>
+	<modules>
+		<module>style24.core</module>
+		<module>style24.admin</module>
+	</modules>
+	
+	<properties>
+		<java.version>1.8</java.version>
+		<skipTests>true</skipTests>
+	</properties>
+	
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>com.style24.core</groupId>
+				<artifactId>style24.core</artifactId>
+				<version>1.0.0</version>
+				<type>jar</type>
+				<scope>compile</scope>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-cache</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-jdbc</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-validation</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web</artifactId>
+			<exclusions>
+				<exclusion>
+					<groupId>org.springframework.boot</groupId>
+					<artifactId>spring-boot-starter-tomcat</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web-services</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.data</groupId>
+			<artifactId>spring-data-commons</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.mybatis.spring.boot</groupId>
+			<artifactId>mybatis-spring-boot-starter</artifactId>
+			<version>2.1.3</version>
+		</dependency>
+		<dependency>
+			<groupId>org.projectlombok</groupId>
+			<artifactId>lombok</artifactId>
+			<optional>true</optional>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-tomcat</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+			<exclusions>
+				<exclusion>
+					<groupId>org.junit.vintage</groupId>
+					<artifactId>junit-vintage-engine</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-lang3</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.httpcomponents</groupId>
+			<artifactId>httpclient</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.googlecode.json-simple</groupId>
+			<artifactId>json-simple</artifactId>
+			<version>1.1.1</version>
+		</dependency>
+		
+		<!-- /// Redis -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-redis</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.session</groupId>
+			<artifactId>spring-session-data-redis</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-pool2</artifactId>
+		</dependency>
+		<!-- \\\ Redis -->
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<configuration>
+					<skipTests>${skipTests}</skipTests>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+</project>

+ 45 - 0
style24.admin/.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.admin/.project

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>style24.admin</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.admin/.settings/.jsdtscope

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src/main/webapp"/>
+	<classpathentry excluding="**/bower_components/*|**/node_modules/*|**/*.min.js" 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.admin/.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.admin/.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.admin/.settings/org.eclipse.m2e.core.prefs

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

+ 10 - 0
style24.admin/.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.admin">
+        <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.admin"/>
+        <property name="java-output-path" value="/style24.admin/target/classes"/>
+    </wb-module>
+</project-modules>

+ 7 - 0
style24.admin/.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.admin/.settings/org.eclipse.wst.jsdt.ui.superType.container

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

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

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

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

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

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

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

+ 84 - 0
style24.admin/pom.xml

@@ -0,0 +1,84 @@
+<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.admin</groupId>
+	<artifactId>style24.admin</artifactId>
+	<packaging>war</packaging>
+	<name>style24.admin</name>
+	<description>STYLE24 Admin</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-thymeleaf</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>nz.net.ultraq.thymeleaf</groupId>
+			<artifactId>thymeleaf-layout-dialect</artifactId>
+		</dependency>
+		
+		<!-- /// WEB-INF lib -->
+		<dependency>
+			<groupId>com.gagaframework</groupId>
+			<artifactId>gagaframework-web-core</artifactId>
+			<version>1.7-RELEASE</version>
+			<scope>system</scope>
+			<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/gagaframework-web-core-1.7-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>

+ 68 - 0
style24.admin/src/main/java/com/style24/admin/biz/dao/TsaLoginDao.java

@@ -0,0 +1,68 @@
+package com.style24.admin.biz.dao;
+
+import java.util.Collection;
+
+import com.style24.core.support.annotation.ShopDs;
+import com.style24.persistence.domain.Menu;
+import com.style24.persistence.domain.User;
+
+/**
+ * 운영관리 Dao
+ * @author gagamel
+ * @since 2019. 12. 4
+ */
+@ShopDs
+public interface TsaLoginDao {
+
+	/**
+	 * ID로 사용자 정보 조회
+	 * @param userId - 사용자ID
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 5
+	 */
+	User getUserById(String userId);
+
+	/**
+	 * 로그인실패 남기기
+	 * @param user - 사용자정보
+	 * @author gagamel
+	 * @date 2020. 10. 5
+	 */
+	void createLoginFail(User user);
+
+	/**
+	 * 로그인 실패건수 조회
+	 * @param user - 사용자정보
+	 * @return 로그인 실패건수
+	 * @author gagamel
+	 * @date 2020. 10. 5
+	 */
+	int getLoginFailCount(User user);
+
+	/**
+	 * 최종로그인일시 Update
+	 * @param userNo - 사용자번호
+	 * @author gagamel
+	 * @since 2020. 10. 5
+	 */
+	void updateLastLoginDate(Integer userNo);
+
+	/**
+	 * 로그인이력 남기기
+	 * @param user - 사용자정보
+	 * @author gagamel
+	 * @date 2020. 10. 5
+	 */
+	void createLoginHistory(User user);
+
+	/**
+	 * 로그인 메뉴 목록
+	 * @param userNo - 사용자번호
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 5
+	 */
+	Collection<Menu> getLoginMenuList(Integer userNo);
+
+}

+ 211 - 0
style24.admin/src/main/java/com/style24/admin/biz/dao/TsaRendererDao.java

@@ -0,0 +1,211 @@
+package com.style24.admin.biz.dao;
+
+import java.util.Collection;
+
+import com.style24.core.support.annotation.ShopDs;
+import com.style24.persistence.domain.CommonCode;
+import com.style24.persistence.domain.SupplyCompany;
+
+/**
+ * 콤보박스, 체크박스, 라디오버튼 구성 시 필요한 Renderer Dao
+ * 
+ * @author gagamel
+ * @since 2020. 10. 7
+ */
+@ShopDs
+public interface TsaRendererDao {
+
+	/**
+	 * 공급업체 목록
+	 * @param supplyComp
+	 * @return 공급업체 목록
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	Collection<CommonCode> getSupplyCompanyList(SupplyCompany supplyComp);
+
+	/**
+	 * 공통코드 목록
+	 * @param commoncode - 공통코드 정보
+	 * @return 공통코드 목록
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	Collection<CommonCode> getCommonCodeList(CommonCode commoncode);
+
+	/**
+	 * 최상위메뉴 목록
+	 * @param pmenuId - 상위메뉴ID
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	Collection<CommonCode> getTopMenuList(String pmenuId);
+
+	/**
+	 * 전체 메뉴 목록
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	Collection<CommonCode> getAllMenuList();
+
+//	/**
+//	 * 벤더외부몰 목록
+//	 * @param vendorId - 벤더ID
+//	 * @return 벤더외부몰 목록
+//	 * @author gagamel
+//	 * @since 2019. 12. 6
+//	 */
+//	Collection<CommonCode> getVendorExtmallList(String vendorId);
+//
+//	/**
+//	 * 벤더외부몰판매매장 목록
+//	 * @param vendorId - 벤더ID
+//	 * @return
+//	 * @author gagamel
+//	 * @since 2020. 4. 28
+//	 */
+//	Collection<CommonCode> getVendorExtmallSellStoreList(String vendorId);
+//
+//	/**
+//	 * 브랜드 목록
+//	 * @param brand --브랜드 정보
+//	 * @return 브랜드 목록
+//	 * @author qkwlstktma
+//	 * @since 2019. 12. 6
+//	 */
+//	Collection<CommonCode> getBrandList(AdmBrand brand);
+//
+//	/**
+//	 * 권한별 브랜드 목록
+//	 * @param userId - 사용자ID
+//	 * @return
+//	 * @author eskim
+//	 * @since 2019. 6.17
+//	 */
+//	Collection<CommonCode> getAuthBrandList(String userId);
+//
+//	/**
+//	 * 출고처 목록
+//	 * @param params - 출고처 정보
+//	 * @return 출고처 목록
+//	 * @author sasa004
+//	 * @since 2019. 12. 12
+//	 */
+//	Collection<CommonCode> getDeliveryLocList(AdmDeliveryLoc params);
+//
+//	/**
+//	 * 정보고시 목록
+//	 * @param goodsCd
+//	 * @return
+//	 * @author jaewonHo
+//	 * @param goodsCd
+//	 * @since 2019. 12. 6
+//	 */
+//	Collection<CommonCode> getCateInfoList(String goodsCd);
+//
+//	/**
+//	 * 품목 목록
+//	 * @param itemkind
+//	 * @return
+//	 * @author eskim
+//	 * @since 2019. 12. 10
+//	 */
+//	Collection<CommonCode> getItemkindList(AdmItemkind itemkind);
+//
+//	/**
+//	 * 브랜드그룹 목록
+//	 * @return
+//	 * @author eskim
+//	 * @since 2019. 12. 10
+//	 */
+//	Collection<CommonCode> getBrandGroupList();
+//
+//	/**
+//	 * 컬러 목록
+//	 * @param color
+//	 * @return
+//	 * @author eskim
+//	 * @since 2019. 12. 11
+//	 */
+//	Collection<CommonCode> getColorList(AdmColor color);
+//
+//	/**
+//	 * 사용중 대카테고리 목록
+//	 * @param cateGb
+//	 * @return
+//	 * @author sasa004
+//	 * @since 2020. 01. 02
+//	 */
+//	Collection<CommonCode> getTCategoryList(String cateGb);
+//
+//	/**
+//	 * 브랜드에 등록된 MD 목록
+//	 * @return MD 목록
+//	 * @author eskim
+//	 * @since 2020. 01. 07
+//	 */
+//	Collection<CommonCode> getBrandMdList();
+//
+//	/**
+//	 * 택배사명 목록 조회
+//	 * @return CommonCode
+//	 * @author DaeHyoung
+//	 * @since 2020. 02. 04
+//	 */
+//	Collection<CommonCode> getShipCompanyList();
+//
+//	/**
+//	 * MD별 브랜드 목록 조회
+//	 * @param mdId
+//	 * @return CommonCode
+//	 * @author jaewonHo
+//	 * @since 2020. 02. 04
+//	 */
+//	Collection<CommonCode> getMdBrandList(String mdId);
+//
+//	/**
+//	 * MD별 브랜드 목록 조회
+//	 * @param mdId
+//	 * @return CommonCode
+//	 * @author jaewonHo
+//	 * @since 2020. 02. 04
+//	 */
+//	Collection<CommonCode> getMdBrandGrpList(String mdId);
+//
+//	/**
+//	 * 인스타그램 계정리스트 조회
+//	 * @return Collection<CommonCode>
+//	 * @author 이명철
+//	 * @since 2020. 3. 18.
+//	 */
+//	Collection<CommonCode> getInstaAccount();
+//
+//	/**
+//	 * 판매몰조회
+//	 * @return
+//	 * @author swkim
+//	 * @since 2020. 03. 19
+//	 */
+//	Collection<CommonCode> getSellStoreList();
+//
+//	/**
+//	 * 기본답변문구 제목 조회
+//	 * @param ansClsf
+//	 * @return
+//	 * @author yujung
+//	 * @since 2020. 04. 02
+//	 */
+//	Collection<CommonCode> getBasicAnsTitleList(String ansClsf);
+//
+//	/**
+//	 * 제휴링크 목록
+//	 * @param afChannel - 제휴채널
+//	 * @return
+//	 * @author gagamel
+//	 * @since 2020. 5. 4
+//	 */
+//	Collection<CommonCode> getAflinkList(String afChannel);
+
+}

+ 212 - 0
style24.admin/src/main/java/com/style24/admin/biz/dao/TsaSystemDao.java

@@ -0,0 +1,212 @@
+package com.style24.admin.biz.dao;
+
+import java.util.Collection;
+
+import com.style24.core.support.annotation.ShopDs;
+import com.style24.persistence.domain.CommonCode;
+import com.style24.persistence.domain.Menu;
+import com.style24.persistence.domain.MenuAccessHst;
+import com.style24.persistence.domain.MenuRole;
+import com.style24.persistence.domain.User;
+import com.style24.persistence.domain.UserHst;
+import com.style24.persistence.domain.UserMenu;
+
+/**
+ * 시스템 Dao
+ * 
+ * @author gagamel
+ * @since 2020. 10. 7
+ */
+@ShopDs
+public interface TsaSystemDao {
+
+	/**
+	 * 사용자 목록
+	 * @param user - 사용자 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	Collection<User> getUserList(User user);
+
+	/**
+	 * 사용자 정보 조회
+	 * @param userNo - 사용자번호
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	User getUser(Integer userNo);
+
+	/**
+	 * 사용자 삭제
+	 * @param user - 사용자 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	void deleteUser(User user);
+
+	/**
+	 * 사용자ID 건수 조회
+	 * @param userId - 사용자ID
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	int getUserIdCount(String userId);
+
+	/**
+	 * 사용자 정보 저장 처리
+	 * @param user - 사용자 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	void saveUser(User user);
+
+	/**
+	 * 임시비밀번호 조회
+	 * @param length - 비밀번호 자릿수
+	 * @return
+	 * @since 2020. 10. 7
+	 */
+	String getTemporaryPassword(int length);
+
+	/**
+	 * 사용자 비밀번호 수정
+	 * @param user - 사용자 정보
+	 * @since 2020. 10. 7
+	 */
+	void updateUserPassword(User user);
+
+	/**
+	 * 사용자 메뉴 목록
+	 * @param userNo - 사용자번호
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	Collection<UserMenu> getUserMenuList(Integer userNo);
+
+	/**
+	 * 사용자정보변경이력 생성
+	 * @param userHst - 사용자이력 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	void createUserInfoChangeHistory(UserHst userHst);
+
+	/**
+	 * 메뉴 목록
+	 * @param menu - 메뉴정보
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	Collection<Menu> getMenuList(Menu menu);
+
+	/**
+	 * 메뉴 등록/수정
+	 * @param menu - 메뉴 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	void saveMenu(Menu menu);
+
+	/**
+	 * 메뉴권한 삭제
+	 * @param menuId - 메뉴ID
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	void deleteMenuRole(String menuId);
+
+	/**
+	 * 메뉴권한 생성
+	 * @param menuRole - 메뉴권한 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	void createMenuRole(MenuRole menuRole);
+
+	/**
+	 * 전체 사용자 메뉴 삭제
+	 * @param menuRole - 메뉴권한 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	void deleteAllUserMenu(MenuRole menuRole);
+
+	/**
+	 * 전체 사용자 메뉴 생성
+	 * @param menuRole - 메뉴권한 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	void createAllUserMenu(MenuRole menuRole);
+
+	/**
+	 * 사용자 전체 메뉴 삭제
+	 * @param userNo - 사용자번호
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	void deleteUserAllMenu(Integer userNo);
+
+	/**
+	 * 사용자 전체 메뉴 생성
+	 * @param user - 사용자 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	void createUserAllMenu(User user);
+
+	/**
+	 * 사용자 메뉴 삭제
+	 * @param userMenu - 사용자 메뉴 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	void deleteAdminUserMenu(UserMenu userMenu);
+
+	/**
+	 * 사용자 메뉴 생성
+	 * @param userMenu - 사용자 메뉴 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	void createUserMenu(UserMenu userMenu);
+
+	/**
+	 * 공통코드 목록
+	 * @param commoncode - 공통코드 정보
+	 * @return 공통코드 목록
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	Collection<CommonCode> getCommonCodeList(CommonCode commoncode);
+
+	/**
+	 * 메뉴접속이력 생성
+	 * @param menuAccessHst - 메뉴접속 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	void createMenuAccessHistory(MenuAccessHst menuAccessHst);
+
+	/**
+	 * 공통코드 등록/수정
+	 * @param commoncode - 공통코드 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	void saveCommonCode(CommonCode commoncode);
+
+	/**
+	 * 공통코드 삭제
+	 * @param commoncode - 공통코드 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	void deleteCommonCode(CommonCode commoncode);
+
+}

+ 111 - 0
style24.admin/src/main/java/com/style24/admin/biz/service/TsaLoginService.java

@@ -0,0 +1,111 @@
+package com.style24.admin.biz.service;
+
+import java.util.Collection;
+
+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 com.style24.admin.biz.dao.TsaLoginDao;
+import com.style24.admin.support.security.session.TsaSession;
+import com.style24.persistence.domain.Menu;
+import com.style24.persistence.domain.User;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 운영관리 Service
+ *
+ * @author gagamel
+ * @since 2019. 12. 4
+ */
+@Service
+@Slf4j
+public class TsaLoginService {
+
+	@Autowired
+	private TsaLoginDao loginDao;
+
+	@Autowired
+	private Environment env;
+
+	/**
+	 * 사용자ID로 사용자 정보 조회
+	 * @param userId - 사용자ID
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 5
+	 */
+	public User getUserById(String userId) {
+		return loginDao.getUserById(userId);
+	}
+
+	/**
+	 * 로그인실패 남기기. 로그인실패여부가 "N:성공"이면 실패건수 0으로 초기화
+	 * @param userNo - 사용자번호
+	 * @param loginFailYn - 로그인실패여부(Y:실패, N:성공)
+	 * @author gagamel
+	 * @since 2020. 10. 5
+	 */
+	@Transactional("shopTxnManager")
+	public void createLoginFail(Integer userNo, String loginFailYn) {
+		User user = new User();
+		user.setUserNo(userNo);
+		user.setIpAddr(TsaSession.getIpAddress());
+		user.setLoginFailYn(loginFailYn);
+		loginDao.createLoginFail(user);
+	}
+
+	/**
+	 * 로그인 실패건수 조회
+	 * @param userNo - 사용자번호
+	 * @return 로그인 실패건수
+	 * @author gagamel
+	 * @since 2020. 10. 5
+	 */
+	public int getLoginFailCount(Integer userNo) {
+		User user = new User();
+		user.setUserNo(userNo);
+		user.setIpAddr(TsaSession.getIpAddress());
+		return loginDao.getLoginFailCount(user);
+	}
+
+	/**
+	 * 최종로그인일시 Update
+	 * @param userNo - 사용자번호
+	 * @author gagamel
+	 * @since 2020. 10. 5
+	 */
+	@Transactional("shopTxnManager")
+	public void updateLastLoginDate(Integer userNo) {
+		loginDao.updateLastLoginDate(userNo);
+	}
+
+	/**
+	 * 로그인이력 남기기
+	 * @param userNo - 사용자번호
+	 * @author gagamel
+	 * @date 2020. 10. 5
+	 */
+	@Transactional("shopTxnManager")
+	public void createLoginHistory(Integer userNo) {
+		User user = new User();
+		user.setUserNo(userNo);
+		user.setIpAddr(TsaSession.getIpAddress());
+
+		loginDao.createLoginHistory(user);
+	}
+
+	/**
+	 * 로그인 메뉴 목록
+	 * @param userNo - 사용자번호
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 5
+	 */
+	public Collection<Menu> getLoginMenuList(Integer userNo) {
+		return loginDao.getLoginMenuList(userNo);
+	}
+
+}

+ 465 - 0
style24.admin/src/main/java/com/style24/admin/biz/service/TsaRendererService.java

@@ -0,0 +1,465 @@
+package com.style24.admin.biz.service;
+
+import java.util.Collection;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.style24.admin.biz.dao.TsaRendererDao;
+import com.style24.persistence.domain.CommonCode;
+import com.style24.persistence.domain.SupplyCompany;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 콤보박스, 체크박스, 라디오버튼 구성 시 필요한 Renderer Service
+ *
+ * @author gagamel
+ * @since 2020. 10. 7
+ */
+@Service
+@Slf4j
+public class TsaRendererService {
+
+	@Autowired
+	private TsaRendererDao rendererDao;
+
+	/**
+	 * 공급업체 목록
+	 * @param supplyCompany - 공급업체 정보
+	 * @return 공급업체 목록
+	 * @author Daehyoung
+	 * @since 2020. 10. 7
+	 */
+	public Collection<CommonCode> getSupplyCompanyList(SupplyCompany supplyCompany) {
+		return rendererDao.getSupplyCompanyList(supplyCompany);
+	}
+
+	/**
+	 * 공급업체 목록
+	 * @return 공급업체 목록
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<CommonCode> getSupplyCompanyList() {
+		return this.getSupplyCompanyList(new SupplyCompany());
+	}
+
+	/**
+	 * 공급업체 목록
+	 * @param supplyCompCd - 공급업체코드
+	 * @return 공급업체 목록
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<CommonCode> getSupplyCompanyList(String supplyCompCd) {
+		SupplyCompany supplyCompany = new SupplyCompany();
+		supplyCompany.setSupplyCompCd(supplyCompCd);
+		return this.getSupplyCompanyList(supplyCompany);
+	}
+
+	/**
+	 * 공급업체 목록
+	 * @param supplyCompCd - 공급업체코드
+	 * @param selfYn - 자사여부
+	 * @return 공급업체 목록
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<CommonCode> getSupplyCompanyList(String supplyCompCd, String selfYn) {
+		SupplyCompany supplyCompany = new SupplyCompany();
+		supplyCompany.setSupplyCompCd(supplyCompCd);
+		supplyCompany.setSelfYn(selfYn);
+		return this.getSupplyCompanyList(supplyCompany);
+	}
+
+	/**
+	 * 자사공급업체 목록
+	 * @return 자사공급업체 목록
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<CommonCode> getSelfSupplyCompanyList() {
+		SupplyCompany supplyCompany = new SupplyCompany();
+		supplyCompany.setSelfYn("Y");
+		return this.getSupplyCompanyList(supplyCompany);
+	}
+
+	/**
+	 * 공통코드 목록 - 어드민 공통코드 캐시 미적용 - 상품등록시 시즌년도 캐쉬로 인한 정보 오류로 수정 2020.06.02
+	 * @param commoncode - 공통코드 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+//	@Cacheable(value = "common", key = "'commoncode-'.concat(#commoncode.cdGb)")
+	public Collection<CommonCode> getCommonCodeList(CommonCode commoncode) {
+		return rendererDao.getCommonCodeList(commoncode);
+	}
+
+	/**
+	 * 공통코드 목록
+	 * @param cdGb - 코드구분
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<CommonCode> getCommonCodeList(String cdGb) {
+		CommonCode commoncode = new CommonCode();
+		commoncode.setCdGb(cdGb);
+		return this.getCommonCodeList(commoncode);
+	}
+
+	/**
+	 * 공통코드 목록
+	 * @param grpCodeId - 코드구분
+	 * @param useYn     - 사용여부
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<CommonCode> getCommonCodeList(String cdGb, String useYn) {
+		CommonCode commoncode = new CommonCode();
+		commoncode.setCdGb(cdGb);
+		commoncode.setUseYn(useYn);
+		return this.getCommonCodeList(commoncode);
+	}
+
+	/**
+	 * 현재 유효한 공통코드 목록
+	 * @param cdGb - 코드구분
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<CommonCode> getAvailCommonCodeList(String cdGb) {
+		return this.getCommonCodeList(cdGb, "Y");
+	}
+
+	/**
+	 * 공통코드 목록
+	 * @param cdGb   -코드구분
+	 * @param useYn  - 사용여부
+	 * @param cdDesc - 코드설명
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<CommonCode> getCommonCodeList(String cdGb, String useYn, String cdDesc) {
+		CommonCode commoncode = new CommonCode();
+		commoncode.setCdGb(cdGb);
+		commoncode.setUseYn(useYn);
+		commoncode.setCdDesc(cdDesc);
+		return this.getCommonCodeList(commoncode);
+	}
+
+	/**
+	 * 공통코드 목록
+	 * @param cdGb      -코드구분
+	 * @param useYn     - 사용여부
+	 * @param exceptCds - 제외코드배열
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<CommonCode> getCommonCodeList(String cdGb, String useYn, String[] exceptCds) {
+		CommonCode commoncode = new CommonCode();
+		commoncode.setCdGb(cdGb);
+		commoncode.setUseYn(useYn);
+		commoncode.setExceptCds(exceptCds);
+		return this.getCommonCodeList(commoncode);
+	}
+
+	/**
+	 * 최상위메뉴 목록
+	 * @param menuId - 메뉴ID
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<CommonCode> getTopMenuList(String menuId) {
+		return rendererDao.getTopMenuList(menuId);
+	}
+
+	/**
+	 * 전체 메뉴 목록
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<CommonCode> getAllMenuList() {
+		return rendererDao.getAllMenuList();
+	}
+
+//	/**
+//	 * 벤더외부몰 목록
+//	 * @param vendorId - 벤더ID
+//	 * @return 벤더외부몰 목록
+//	 * @author gagamel
+//	 * @since 2019. 8. 11
+//	 */
+//	public Collection<CommonCode> getVendorExtmallList(String vendorId) {
+//		return rendererDao.getVendorExtmallList(vendorId);
+//	}
+//
+//	/**
+//	 * 벤더외부몰판매매장 목록
+//	 * @param vendorId - 벤더ID
+//	 * @return
+//	 * @author gagamel
+//	 * @since 2020. 4. 28
+//	 */
+//	public Collection<CommonCode> getVendorExtmallSellStoreList(String vendorId) {
+//		return rendererDao.getVendorExtmallSellStoreList(vendorId);
+//	}
+//
+//	//
+//	//	/**
+//	//	 * 유효한 브랜드 목록
+//	//	 * @param
+//	//	 * @return
+//	//	 * @author qkwlstktma
+//	//	 * @since 2019. 8. 25
+//	//	 */
+//	//	public Collection<CommonCode> getAvailableBrandList() {
+//	//		AdmBrand brand = new AdmBrand();
+//	//		brand.setUseYn("Y");
+//	//		return rendererDao.getBrandList(brand);
+//	//	}
+//	//
+//	/**
+//	 * 공급업체 브랜드 목록
+//	 * @param supplyCompCd - 공급업체코드
+//	 * @return
+//	 * @author gagamel
+//	 * @since 2019. 10. 2
+//	 */
+//	public Collection<CommonCode> getSupplyCompanyBrandList(String supplyCompCd) {
+//		AdmBrand brand = new AdmBrand();
+//		brand.setSupplyCompCd(supplyCompCd);
+//		return rendererDao.getBrandList(brand);
+//	}
+//
+//	/**
+//	 * 브랜드그룹별 브랜드 목록
+//	 * @param brandGrpNm - 브랜드그룹명
+//	 * @return
+//	 * @author gagamel
+//	 * @since 2020. 1. 10
+//	 */
+//	public Collection<CommonCode> getGroupBrandList(String brandGrpNm) {
+//		AdmBrand brand = new AdmBrand();
+//		brand.setBrandGrpNm(brandGrpNm);
+//		return rendererDao.getBrandList(brand);
+//	}
+//
+//	/**
+//	 * 권한별 브랜드 목록
+//	 * @param adminId - 사용자ID
+//	 * @return
+//	 * @author eskim
+//	 * @since 2019. 6. 17
+//	 */
+//	public Collection<CommonCode> getAuthBrandList(String userId) {
+//		return rendererDao.getAuthBrandList(userId);
+//	}
+//
+//	/**
+//	 * 출고처 목록
+//	 * @param params - 출고처 정보
+//	 * @return 출고처 목록
+//	 * @author gagamel
+//	 * @since 2020. 5. 4
+//	 */
+//	public Collection<CommonCode> getDeliveryLocList(AdmDeliveryLoc params) {
+//		return rendererDao.getDeliveryLocList(params);
+//	}
+//
+//	/**
+//	 * 출고처 목록
+//	 * @return 출고처 목록
+//	 * @author gagamel
+//	 * @since 2019. 12. 12
+//	 */
+//	public Collection<CommonCode> getDeliveryLocList() {
+//		AdmDeliveryLoc params = new AdmDeliveryLoc();
+//		return this.getDeliveryLocList(params);
+//	}
+//
+//	/**
+//	 * 출고처 목록
+//	 * @param supplyCompCd - 공급업체코드
+//	 * @return 출고처 목록
+//	 * @author gagamel
+//	 * @since 2019. 12. 12
+//	 */
+//	public Collection<CommonCode> getDeliveryLocList(String supplyCompCd) {
+//		AdmDeliveryLoc params = new AdmDeliveryLoc();
+//		params.setSupplyCompCd(supplyCompCd);
+//		return this.getDeliveryLocList(params);
+//	}
+//
+//	/**
+//	 * 직송매장 목록
+//	 * @return
+//	 * @author gagamel
+//	 * @since 2020. 5. 4
+//	 */
+//	public Collection<CommonCode> getDirectStoreList() {
+//		AdmDeliveryLoc params = new AdmDeliveryLoc();
+//		params.setDelvLocClsf("21"); // 직송매장
+//		return this.getDeliveryLocList(params);
+//	}
+//
+//	//	/**
+//	//	 * 정보고시 목록
+//	//	 * @param goodsCd - 상품코드
+//	//	 * @return
+//	//	 * @author jaewonHo
+//	//	 * @param supplyCompCd
+//	//	 * @since 2019. 10. 24
+//	//	 */
+//	//	public Collection<CommonCode> getCateInfoList(String goodsCd) {
+//	//		return rendererDao.getCateInfoList(goodsCd);
+//	//	}
+//
+//	/**
+//	 * 품목 목록
+//	 * @return 품목 목록
+//	 * @author eskim
+//	 * @since 2019. 12. 10
+//	 */
+//	public Collection<CommonCode> getAllItemkindList() {
+//		AdmItemkind itemkind = new AdmItemkind();
+//		return this.getItemkindList(itemkind);
+//	}
+//
+//	/**
+//	 * 품목 목록
+//	 * @param itemkind - 품목 정보
+//	 * @return 품목 목록
+//	 * @author eskim
+//	 * @since 2019. 12. 10
+//	 */
+//	public Collection<CommonCode> getItemkindList(AdmItemkind itemkind) {
+//		return rendererDao.getItemkindList(itemkind);
+//	}
+//
+//	/**
+//	 * 브랜드그룹 목록
+//	 * @return 브랜드그룹 목록
+//	 * @author eskim
+//	 * @since 2019. 12. 10
+//	 */
+//	public Collection<CommonCode> getBrandGroupList() {
+//		return rendererDao.getBrandGroupList();
+//	}
+//
+//	/**
+//	 * 컬러 목록
+//	 * @param color - 색상 정보
+//	 * @return 컬러 목록
+//	 * @author eskim
+//	 * @since 2019. 12. 11
+//	 */
+//	public Collection<CommonCode> getColorList(AdmColor color) {
+//		return rendererDao.getColorList(color);
+//	}
+//
+//	/**
+//	 * 사용중 대카테고리 목록
+//	 * @param cateGb - 카테고리구분
+//	 * @return 대카테고리 목록
+//	 * @author sasa004
+//	 * @since 2020. 01. 02
+//	 */
+//	public Collection<CommonCode> getTCategoryList(String cateGb) {
+//		return rendererDao.getTCategoryList(cateGb);
+//	}
+//
+//	/**
+//	 * 브랜드에 등록된 MD 목록
+//	 * @return MD 목록
+//	 * @author eskim
+//	 * @since 2020. 01. 07
+//	 */
+//	public Collection<CommonCode> getBrandMdList() {
+//		return rendererDao.getBrandMdList();
+//	}
+//
+//	/**
+//	 * 택배사명 목록 조회
+//	 * @return CommonCode
+//	 * @author Daehyoung
+//	 * @since 2020. 02. 04
+//	 */
+//	public Collection<CommonCode> getShipCompanyList() {
+//		return rendererDao.getShipCompanyList();
+//	}
+//
+//	/**
+//	 * MD 별 브랜드 목록 조회
+//	 * @param mdId - 담당MD아이디
+//	 * @return CommonCode
+//	 * @author jaewonHo
+//	 * @since 2020. 02. 11
+//	 */
+//	public Collection<CommonCode> getMdBrandList(String mdId) {
+//		return rendererDao.getMdBrandList(mdId);
+//	}
+//
+//	/**
+//	 * MD 별 브랜드 목록 조회
+//	 * @param mdId - 담당MD아이디
+//	 * @return CommonCode
+//	 * @author jaewonHo
+//	 * @since 2020. 02. 11
+//	 */
+//	public Collection<CommonCode> getMdBrandGrpList(String mdId) {
+//		return rendererDao.getMdBrandGrpList(mdId);
+//	}
+//
+//	/**
+//	 * 인스타그램 계정리스트 조회
+//	 * @return
+//	 * @since : 2020. 3. 18.
+//	 * @authur 이명철
+//	 */
+//	public Collection<CommonCode> getInstaAccount() {
+//		return rendererDao.getInstaAccount();
+//	}
+//
+//	/**
+//	 * 판매몰조회
+//	 * @param
+//	 * @return CommonCode
+//	 * @author swkim
+//	 * @since 2020. 03. 19
+//	 */
+//	public Collection<CommonCode> getSellStoreList() {
+//		return rendererDao.getSellStoreList();
+//	}
+//
+//	/**
+//	 * 기본답변문구 제목 조회
+//	 * @param
+//	 * @return CommonCode
+//	 * @author yujing
+//	 * @since 2020. 04. 02
+//	 */
+//	public Collection<CommonCode> getBasicAnsTitleList(String ansClsf) {
+//		return rendererDao.getBasicAnsTitleList(ansClsf);
+//	}
+//
+//	/**
+//	 * 제휴링크 목록
+//	 * @param afChannel - 제휴채널
+//	 * @return
+//	 * @author gagamel
+//	 * @since 2020. 5. 4
+//	 */
+//	public Collection<CommonCode> getAflinkList(String afChannel) {
+//		return rendererDao.getAflinkList(afChannel);
+//	}
+
+}

+ 394 - 0
style24.admin/src/main/java/com/style24/admin/biz/service/TsaSystemService.java

@@ -0,0 +1,394 @@
+package com.style24.admin.biz.service;
+
+import java.util.Collection;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.style24.admin.biz.dao.TsaSystemDao;
+import com.style24.admin.support.security.session.TsaSession;
+import com.style24.core.support.message.TscMessageByLocale;
+import com.style24.persistence.domain.CommonCode;
+import com.style24.persistence.domain.Menu;
+import com.style24.persistence.domain.MenuRole;
+import com.style24.persistence.domain.User;
+import com.style24.persistence.domain.UserHst;
+import com.style24.persistence.domain.UserMenu;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 시스템 Service
+ *
+ * @author gagamel
+ * @since 2020. 10. 7
+ */
+@Service
+@Slf4j
+public class TsaSystemService {
+
+	@Autowired
+	private TscMessageByLocale message;
+
+	@Autowired
+	private TsaSystemDao systemDao;
+
+	/**
+	 * 사용자 목록
+	 * @param user - 사용자 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<User> getUserList(User user) {
+		return systemDao.getUserList(user);
+	}
+
+	/**
+	 * 사용자 목록 삭제 처리
+	 * @param userList - 사용자 목록
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@Transactional("shopTxnManager")
+	public void deleteUserList(Collection<User> userList) {
+		if (userList == null || userList.isEmpty())
+			throw new IllegalStateException(message.getMessage("FAIL_1001"));
+
+		for (User user : userList) {
+			user.setUpdNo(TsaSession.getInfo().getUserNo());
+			systemDao.deleteUser(user);
+			this.deleteUserAccount(user); // 계정말소
+		}
+	}
+
+	/**
+	 * 사용자 정보 조회
+	 * @param userNo - 사용자번호
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public User getUser(Integer userNo) {
+		return systemDao.getUser(userNo);
+	}
+
+	/**
+	 * 사용자ID 건수 조회
+	 * @param userId - 사용자ID
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public int getUserIdCount(String userId) {
+		return systemDao.getUserIdCount(userId);
+	}
+
+	/**
+	 * 사용자 정보 저장 처리
+	 * @param user - 사용자 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@Transactional("shopTxnManager")
+	public void saveUser(User user) {
+		user.setRegNo(TsaSession.getInfo().getUserNo());
+		user.setUpdNo(TsaSession.getInfo().getUserNo());
+
+		// 사용자 저장 처리
+		systemDao.saveUser(user);
+
+		// 등록된 사용자번호 값 가져오기
+		Integer userNo = user.getUserNo();
+		log.info("userNo: {}", userNo);
+		user.setUserNo(userNo);
+
+		if (user.getMode().equals("N")) {
+			this.createUserAccount(user); // 계정생성
+		} else if (user.getMode().equals("U")) {
+			if (user.getUseYn().equals("N")) {
+				this.deleteUserAccount(user); // 계정말소
+			} else {
+				this.restoreUserAccount(user); // 계정복원
+			}
+		}
+
+		// 권한이 변경되었으면
+		if (user.getRoleChangeYn().equals("Y")) {
+			// 사용자 전체메뉴 삭제
+			systemDao.deleteUserAllMenu(user.getUserNo());
+
+			// 사용자 전체메뉴 생성
+			systemDao.createUserAllMenu(user);
+
+			if (user.getMode().equals("U")) {
+				this.createUserRoleChange(user); // 권한변경
+			}
+		}
+	}
+
+	/**
+	 * 사용자정보변경이력 생성 - 계정생성
+	 * @param user - 사용자 정보
+	 * @author gagamel
+	 * @since 2020. 10. 8
+	 */
+	private void createUserAccount(User user) {
+		UserHst userHst = new UserHst();
+		userHst.setUserNo(user.getUserNo());
+		userHst.setChgGb("G060_10"); // 계정생성
+		userHst.setRegNo(TsaSession.getInfo().getUserNo());
+		systemDao.createUserInfoChangeHistory(userHst);
+	}
+
+	/**
+	 * 사용자정보변경이력 생성 - 계정말소
+	 * @param user - 사용자 정보
+	 * @author gagamel
+	 * @since 2020. 10. 8
+	 */
+	private void deleteUserAccount(User user) {
+		UserHst userHst = new UserHst();
+		userHst.setUserNo(user.getUserNo());
+		userHst.setChgGb("G060_11"); // 계정말소
+		userHst.setRegNo(TsaSession.getInfo().getUserNo());
+		systemDao.createUserInfoChangeHistory(userHst);
+	}
+
+	/**
+	 * 사용자정보변경이력 생성 - 계정복원
+	 * @param user - 사용자 정보
+	 * @author gagamel
+	 * @since 2020. 10. 8
+	 */
+	private void restoreUserAccount(User user) {
+		UserHst userHst = new UserHst();
+		userHst.setUserNo(user.getUserNo());
+		userHst.setChgGb("G060_12"); // 계정복원
+		userHst.setRegNo(TsaSession.getInfo().getUserNo());
+		systemDao.createUserInfoChangeHistory(userHst);
+	}
+
+	/**
+	 * 사용자정보변경이력 생성 - 권한변경
+	 * @param user - 사용자 정보
+	 * @author gagamel
+	 * @since 2020. 10. 8
+	 */
+	private void createUserRoleChange(User user) {
+		UserHst userHst = new UserHst();
+		userHst.setUserNo(user.getUserNo());
+		userHst.setChgGb("G060_20"); // 권한변경
+		userHst.setOrgRoleCd(TsaSession.getInfo().getRoleCd());
+		userHst.setRoleCd(user.getRoleCd());
+		userHst.setRegNo(TsaSession.getInfo().getUserNo());
+		systemDao.createUserInfoChangeHistory(userHst);
+	}
+
+	/**
+	 * 임시비밀번호 조회
+	 * @param length - 비밀번호 자릿수
+	 * @return 임시비밀번호
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public String getTemporaryPassword(int length) {
+		return systemDao.getTemporaryPassword(length);
+	}
+
+	/**
+	 * 사용자 비밀번호 수정
+	 * @param user - 사용자 정보
+	 * @since 2020. 10. 7
+	 */
+	public void updateUserPassword(User user) {
+		systemDao.updateUserPassword(user);
+
+		// 사용자정보변경이력 생성 - 비밀번호변경
+		UserHst userHst = new UserHst();
+		userHst.setUserNo(user.getUserNo());
+		userHst.setChgGb("G060_30"); // 비밀번호변경
+		userHst.setRegNo(TsaSession.getInfo().getUserNo());
+		systemDao.createUserInfoChangeHistory(userHst);
+	}
+
+	/**
+	 * 사용자 메뉴 목록
+	 * @param userNo - 사용자번호
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<UserMenu> getUserMenuList(Integer userNo) {
+		return systemDao.getUserMenuList(userNo);
+	}
+
+	/**
+	 * 사용자 메뉴 목록 저장 처리
+	 * @param userMenuList - 사용자 메뉴 목록
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@Transactional("shopTxnManager")
+	public void saveUserMenuList(Collection<UserMenu> userMenuList) {
+		if (userMenuList == null || userMenuList.isEmpty())
+			throw new IllegalStateException(message.getMessage("FAIL_1001"));
+
+		int cnt = 0;
+
+		for (UserMenu adminMenu : userMenuList) {
+			if (cnt++ == 0) {
+				// 사용자 전체메뉴 삭제
+				systemDao.deleteUserAllMenu(adminMenu.getUserNo());
+			}
+
+			adminMenu.setRegNo(TsaSession.getInfo().getUserNo());
+			adminMenu.setUpdNo(TsaSession.getInfo().getUserNo());
+
+			// 사용자 메뉴 생성
+			systemDao.createUserMenu(adminMenu);
+		}
+	}
+
+	/**
+	 * 메뉴 목록
+	 * @param pmenuId - 상위메뉴ID
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<Menu> getMenuList(String pmenuId) {
+		Menu menu = new Menu();
+		menu.setPmenuId(pmenuId);
+		return systemDao.getMenuList(menu);
+	}
+
+	/**
+	 * 메뉴 목록 저장 처리
+	 * @param menuList - 메뉴 목록
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@Transactional("shopTxnManager")
+	public void saveMenuList(Collection<Menu> menuList) {
+		if (menuList == null || menuList.isEmpty())
+			throw new IllegalStateException(message.getMessage("FAIL_1001"));
+
+		for (Menu menu : menuList) {
+			menu.setRegNo(TsaSession.getInfo().getUserNo());
+			menu.setUpdNo(TsaSession.getInfo().getUserNo());
+			systemDao.saveMenu(menu);
+		}
+	}
+
+	/**
+	 * 메뉴 등록/수정 및 메뉴권한 생성
+	 * @param menu - 메뉴 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@Transactional("shopTxnManager")
+//	@CacheEvict(value = "menu", allEntries = true)
+	public void saveMenu(Menu menu) {
+		menu.setRegNo(TsaSession.getInfo().getUserNo());
+		menu.setUpdNo(TsaSession.getInfo().getUserNo());
+
+		// 메뉴 등록/수정
+		systemDao.saveMenu(menu);
+
+		if (StringUtils.isNotBlank(menu.getRoleCds())) {
+			// 메뉴권한 삭제
+			systemDao.deleteMenuRole(menu.getMenuId());
+
+			String[] roleCds = menu.getRoleCds().split(",");
+
+			for (String roleCd : roleCds) {
+				MenuRole menuRole = new MenuRole();
+				menuRole.setRoleCd(roleCd);
+				menuRole.setMenuId(menu.getMenuId());
+				menuRole.setRegNo(TsaSession.getInfo().getUserNo());
+				menuRole.setUpdNo(TsaSession.getInfo().getUserNo());
+
+				// 메뉴권한 생성
+				systemDao.createMenuRole(menuRole);
+
+				// 전체 어드민사용자 메뉴 생성
+				systemDao.deleteAllUserMenu(menuRole);
+
+				// 전체 어드민사용자 메뉴 생성
+				systemDao.createAllUserMenu(menuRole);
+			}
+		}
+	}
+
+	/**
+	 * 공통코드 목록
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<CommonCode> getCommonCodeList() {
+		return this.getCommonCodeList(new CommonCode());
+	}
+
+	/**
+	 * 공통코드 목록
+	 * @param commoncode - 공통코드 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public Collection<CommonCode> getCommonCodeList(CommonCode commoncode) {
+		return systemDao.getCommonCodeList(commoncode);
+	}
+
+	/**
+	 * 공통코드 목록 저장 처리
+	 * @param dataList - 공통코드 목록
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@Transactional("shopTxnManager")
+	public void saveCommonCodeList(Collection<CommonCode> dataList) {
+		if (dataList == null || dataList.isEmpty())
+			throw new IllegalStateException(message.getMessage("FAIL_1001"));
+
+		for (CommonCode commoncode : dataList) {
+			commoncode.setRegNo(TsaSession.getInfo().getUserNo());
+			commoncode.setUpdNo(TsaSession.getInfo().getUserNo());
+			commoncode.setCdNm(commoncode.getCdNm().replace("&gt;", ">"));
+			systemDao.saveCommonCode(commoncode);
+		}
+	}
+
+	/**
+	 * 공통코드 저장 처리
+	 * @param commoncode - 공통코드 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@Transactional("shopTxnManager")
+	public void saveCommonCode(CommonCode commoncode) {
+		commoncode.setRegNo(TsaSession.getInfo().getUserNo());
+		commoncode.setUpdNo(TsaSession.getInfo().getUserNo());
+		commoncode.setCdNm(commoncode.getCdNm().replace("&gt;", ">"));
+		systemDao.saveCommonCode(commoncode);
+	}
+
+	/**
+	 * 공통코드 삭제처리
+	 * @param commoncode - 공통코드 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@Transactional("shopTxnManager")
+	public void deleteCommonCode(CommonCode commoncode) {
+		systemDao.deleteCommonCode(commoncode);
+	}
+
+}

+ 55 - 0
style24.admin/src/main/java/com/style24/admin/biz/web/TsaIndexController.java

@@ -0,0 +1,55 @@
+package com.style24.admin.biz.web;
+
+import javax.servlet.http.HttpSession;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.servlet.ModelAndView;
+
+import com.style24.admin.support.controller.TsaBaseController;
+import com.style24.admin.support.security.session.TsaSession;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Index Controller
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@Controller
+@Slf4j
+public class TsaIndexController extends TsaBaseController {
+
+	/**
+	 * 첫 페이지. 로그인 된 상태이면 대쉬보드 화면으로 아니면 로그인 페이지로
+	 * @param error - 로그인 오류 정보
+	 * @param session - HttpSession
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 5
+	 */
+	@GetMapping({"/", "/index"})
+	public ModelAndView index(@RequestParam(value = "error", required = false) boolean isError, HttpSession session) {
+		ModelAndView mav = new ModelAndView();
+
+		if (TsaSession.isLogin()) {
+			mav.setViewName("dashboard");
+			return mav;
+		}
+
+		log.info("isError: {}", isError);
+		if (isError) {
+			Exception e = (Exception)session.getAttribute("SPRING_SECURITY_LAST_EXCEPTION");
+			log.info("e.getMessage(): {}", e.getMessage());
+			mav.addObject("error", e.getMessage());
+		}
+
+		// 로그인 페이지로
+		mav.setViewName("signin");
+
+		return mav;
+	}
+
+}

+ 379 - 0
style24.admin/src/main/java/com/style24/admin/biz/web/TsaSystemController.java

@@ -0,0 +1,379 @@
+package com.style24.admin.biz.web;
+
+import java.util.Collection;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.ModelAndView;
+
+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.domain.CommonCode;
+import com.style24.persistence.domain.Menu;
+import com.style24.persistence.domain.User;
+import com.style24.persistence.domain.UserMenu;
+
+import lombok.extern.slf4j.Slf4j;
+
+import com.gagaframework.web.core.security.GagaPasswordEncoder;
+import com.gagaframework.web.rest.server.GagaResponse;
+
+/**
+ * 운영관리 Controller
+ *
+ * @author gagamel
+ * @since 2020. 10. 7
+ */
+@Controller
+@RequestMapping("/system")
+@Slf4j
+public class TsaSystemController extends TsaBaseController {
+
+	@Autowired
+	private TscMessageByLocale message;
+
+	@Autowired
+	private Environment env;
+
+	@Autowired
+	private TsaSystemService systemService;
+
+	@Autowired
+	private TsaRendererService rendererService;
+
+	@Autowired
+	private GagaPasswordEncoder passwordEncoder;
+
+	@Value("${upload.default.target.path}")
+	private String uploadTargetPath;
+
+	/**
+	 * 사용자관리 화면
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@GetMapping("/user/form")
+	public ModelAndView userForm() {
+		ModelAndView mav = new ModelAndView();
+
+		// 공급업체 콤보박스 목록
+		mav.addObject("supplyCompList", rendererService.getSupplyCompanyList(TsaSession.getInfo().getSupplyCompCd()));
+
+		// 권한 콤보박스 목록
+		mav.addObject("roleList", rendererService.getCommonCodeList("G001"));
+
+		mav.setViewName("system/UserForm");
+
+		return mav;
+	}
+
+	/**
+	 * 사용자 목록
+	 * @param user - 사용자 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@PostMapping("/user/list")
+	@ResponseBody
+	public Collection<User> getUserList(@RequestBody User user) {
+		return systemService.getUserList(user);
+	}
+
+	/**
+	 * 사용자 목록 팝업
+	 * @param user - 사용자 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@GetMapping("/user/list/form")
+	public ModelAndView userListForm(User user) {
+		ModelAndView mav = new ModelAndView();
+
+		mav.addObject("userList", systemService.getUserList(user));
+
+		mav.addObject("params", user);
+
+		mav.setViewName("system/UserListForm");
+
+		return mav;
+	}
+
+	/**
+	 * 사용자 목록 삭제 처리
+	 * @param userList - 사용자 목록
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@PostMapping("/user/list/delete")
+	@ResponseBody
+	public GagaResponse deleteUserList(@RequestBody Collection<User> userList) {
+		systemService.deleteUserList(userList);
+		return super.ok(message.getMessage("SUCC_0003"));
+	}
+
+	/**
+	 * 사용자 정보 조회
+	 * @param mode   - 모드(N:신규, U:상세/수정, C:복사)
+	 * @param userNo - 사용자번호
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@GetMapping("/user/info/form")
+	public ModelAndView getUser(@RequestParam(value = "mode") String mode,
+		@RequestParam(value = "userNo", required = false) Integer userNo) {
+		ModelAndView mav = new ModelAndView();
+
+		// 권한 콤보박스 목록
+		if (!TsaSession.getInfo().getRoleCd().equals("0000")) {
+			mav.addObject("roleList", rendererService.getCommonCodeList("G001", "Y", new String[] {"0000"}));
+		} else {
+			mav.addObject("roleList", rendererService.getAvailCommonCodeList("G001"));
+		}
+
+		// 공급업체 콤보박스 목록
+		mav.addObject("supplyCompList", rendererService.getSupplyCompanyList(TsaSession.getInfo().getSupplyCompCd()));
+
+		// 외부몰벤더 콤보박스 목록
+		mav.addObject("vendorList", rendererService.getCommonCodeList("G003", "Y", TsaSession.getInfo().getSupplyCompCd()));
+
+		if (mode.equals("U")) {
+			// 어드민사용자정보
+			mav.addObject("userInfo", systemService.getUser(userNo));
+		} else {
+			mav.addObject("userInfo", new User());
+		}
+
+		mav.addObject("mode", mode);
+
+		mav.setViewName("system/UserDetailForm");
+
+		return mav;
+	}
+
+	/**
+	 * 사용자ID 조회
+	 * @param userId - 사용자ID
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@GetMapping("/user/id/{userId}")
+	@ResponseBody
+	public int getUserIdCount(@PathVariable("userId") String userId) {
+		return systemService.getUserIdCount(userId);
+	}
+
+	/**
+	 * 사용자 저장 처리
+	 * @param user - 사용자 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@PostMapping("/user/save")
+	@ResponseBody
+	public GagaResponse saveUser(@RequestBody User user) {
+		if (user.getMode().equals("N")) { // 신규등록
+			user.setPasswd(passwordEncoder.encode(user.getPasswd()));
+		}
+
+		systemService.saveUser(user);
+
+		return super.ok(message.getMessage("SUCC_0001"));
+	}
+
+	/**
+	 * 사용자 비밀번호 변경
+	 * @param user - 사용자 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@PostMapping("/user/password/change")
+	@ResponseBody
+	public GagaResponse sendUserTemporaryPassword(@RequestBody User user) {
+		user.setPasswd(passwordEncoder.encode(user.getPasswd()));
+		user.setUpdNo(TsaSession.getInfo().getUserNo());
+		log.debug("user: {}", user);
+
+		// 어드민사용자 비밀번호 수정
+		systemService.updateUserPassword(user);
+
+		return super.ok(message.getMessage("SUCC_0009"));
+	}
+
+	/**
+	 * 사용자 메뉴 관리 화면
+	 * @param userNo - 사용자번호
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@GetMapping("/user/menu/{userNo}")
+	public ModelAndView getAdminUserMenu(@PathVariable("userNo") Integer userNo) {
+		ModelAndView mav = new ModelAndView();
+
+		// 사용자 메뉴 목록
+		mav.addObject("userMenuList", systemService.getUserMenuList(userNo));
+
+		mav.setViewName("system/UserMenuForm");
+
+		return mav;
+	}
+
+	/**
+	 * 사용자 메뉴 목록 저장 처리
+	 * @param menuList - 사용자 메뉴 목록
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@PostMapping("/user/menu/list/save")
+	@ResponseBody
+	public GagaResponse saveUserMenuList(@RequestBody Collection<UserMenu> menuList) {
+		systemService.saveUserMenuList(menuList);
+		return super.ok(message.getMessage("SUCC_0001"));
+	}
+
+	/**
+	 * 메뉴관리 화면
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@GetMapping("/menu/form")
+	public ModelAndView menuListForm() {
+		ModelAndView mav = new ModelAndView();
+
+		// 최상위메뉴 콤보박스 목록
+		mav.addObject("topMenuList", rendererService.getTopMenuList(""));
+
+		// 전체메뉴 콤보박스 목록
+		mav.addObject("allMenuList", rendererService.getAllMenuList());
+
+		// 권한 콤보박스 목록
+		mav.addObject("roleList", rendererService.getCommonCodeList("G001"));
+
+		mav.setViewName("system/MenuForm");
+
+		return mav;
+	}
+
+	/**
+	 * 메뉴 목록
+	 * @param pmenuId - 상위메뉴ID
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@GetMapping("/menu/list")
+	@ResponseBody
+	public Collection<Menu> getMenuList(@RequestParam(value = "pmenuId", required = false) String pmenuId) {
+		return systemService.getMenuList(pmenuId);
+	}
+
+	/**
+	 * 메뉴 사용안함 처리
+	 * @param menuList - 메뉴 목록
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@PostMapping("/menu/list/save")
+	@ResponseBody
+	public GagaResponse saveMenuList(@RequestBody Collection<Menu> menuList) {
+		systemService.saveMenuList(menuList);
+		return super.ok(message.getMessage("SUCC_0001"));
+	}
+
+	/**
+	 * 메뉴 등록/수정 처리
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@PostMapping("/menu/save")
+	@ResponseBody
+	public GagaResponse saveMenu(@RequestBody Menu menu) {
+		systemService.saveMenu(menu);
+		return super.ok(message.getMessage("SUCC_0001"));
+	}
+
+	/**
+	 * 공통코드관리 화면
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@GetMapping("/commoncode/form")
+	public ModelAndView commoncodeListForm() {
+		ModelAndView mav = new ModelAndView();
+
+//		// 코드구분 콤보박스 목록
+//		mav.addObject("cdGbList", rendererService.getCommonCodeList("ROOT"));
+
+		mav.setViewName("system/CommoncodeForm");
+
+		return mav;
+	}
+
+	/**
+	 * 공통코드 목록
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@GetMapping("/commoncode/list")
+	@ResponseBody
+	public Collection<CommonCode> getCommonCodeList(@RequestParam(value = "cdGb", required = false) String cdGb) {
+		CommonCode commoncode = new CommonCode();
+		commoncode.setCdGb(cdGb);
+		return systemService.getCommonCodeList(commoncode);
+	}
+
+	/**
+	 * 공통코드 목록 저장 처리
+	 * @param commonCodeList - 공통코드 목록
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@PostMapping("/commoncode/list/save")
+	@ResponseBody
+	public GagaResponse saveCommonCodeList(@RequestBody Collection<CommonCode> commonCodeList) {
+		systemService.saveCommonCodeList(commonCodeList);
+		return super.ok(message.getMessage("SUCC_0001"));
+	}
+
+	/**
+	 * 공통코드 저장 처리
+	 * @param commonCode - 공통코드 목록
+	 * @return
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	@PostMapping("/commoncode/save")
+	@ResponseBody
+	public GagaResponse saveCommonCode(@RequestBody CommonCode commonCode) {
+		systemService.saveCommonCode(commonCode);
+		return super.ok(message.getMessage("SUCC_0001"));
+	}
+
+}

+ 48 - 0
style24.admin/src/main/java/com/style24/admin/support/config/TsaMybatisShopConfig.java

@@ -0,0 +1,48 @@
+package com.style24.admin.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. 10. 5
+ */
+@Configuration
+@MapperScan(basePackages = TscConstants.BASE_PACKAGE, annotationClass = ShopDs.class, sqlSessionFactoryRef = "shopSqlSessionFactory")
+public class TsaMybatisShopConfig {
+
+	@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 + "/shop/*.xml"));
+
+		return sessionFactoryBean.getObject();
+	}
+
+	@Bean(name = "shopSqlSessionTemplate")
+	public SqlSessionTemplate shopSqlSessionTemplate(@Qualifier("shopSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
+		return new SqlSessionTemplate(sqlSessionFactory);
+	}
+
+}

+ 100 - 0
style24.admin/src/main/java/com/style24/admin/support/config/TsaRedisSessionConfig.java

@@ -0,0 +1,100 @@
+package com.style24.admin.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. 10. 5
+ */
+@Configuration
+@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
+public class TsaRedisSessionConfig
+	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;
+	}
+
+}

+ 70 - 0
style24.admin/src/main/java/com/style24/admin/support/config/TsaThymeleafConfig.java

@@ -0,0 +1,70 @@
+package com.style24.admin.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. 10. 5
+ */
+@Configuration
+public class TsaThymeleafConfig {
+
+	@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;
+	}
+
+}

+ 119 - 0
style24.admin/src/main/java/com/style24/admin/support/config/TsaWebMvcConfig.java

@@ -0,0 +1,119 @@
+package com.style24.admin.support.config;
+
+import java.nio.charset.Charset;
+
+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.web.client.RestTemplate;
+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.admin.support.interceptor.TsaDefaultInterceptor;
+import com.style24.admin.support.interceptor.TsaPosInterceptor;
+
+import com.gagaframework.web.core.filter.GagaXssServletFilter;
+import com.gagaframework.web.rest.client.GagaRequestStringTrim;
+
+/**
+ * Web MVC Configuration
+ *
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@Configuration
+public class TsaWebMvcConfig implements WebMvcConfigurer {
+
+	@Autowired
+	private TsaDefaultInterceptor defaultInterceptor;
+
+	@Autowired
+	private TsaPosInterceptor posInterceptor;
+
+	@Override
+	public void addInterceptors(InterceptorRegistry registry) {
+		final String[] excludePathPatterns = new String[] {
+			"/", "/index", "/signin", "/image/**", "/ux/**", "/smartEditor/**",
+			"/error/**", "/data/**", "/login", "/logout",
+			"/pos/**", "/goods/naverEP/download", "/shoplinker/**"
+		};
+
+		registry.addInterceptor(defaultInterceptor)
+			.addPathPatterns("/**/*")
+			.excludePathPatterns(excludePathPatterns);
+
+		registry.addInterceptor(posInterceptor)
+			.addPathPatterns("/pos/**")
+			.excludePathPatterns("/pos/login/**");
+	}
+
+	/**
+	 * @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;
+	}
+
+}

+ 49 - 0
style24.admin/src/main/java/com/style24/admin/support/controller/TsaBaseController.java

@@ -0,0 +1,49 @@
+package com.style24.admin.support.controller;
+
+import java.util.Collection;
+
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ModelAttribute;
+
+import com.style24.admin.support.security.session.TsaSession;
+import com.style24.core.support.controller.TscBaseController;
+import com.style24.persistence.domain.Menu;
+import com.style24.persistence.domain.User;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Controller Advice
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@ControllerAdvice
+@Slf4j
+public class TsaBaseController extends TscBaseController {
+
+	/**
+	 * Get 세션 정보
+	 * 1.View 단에서 사용법
+	 * th:value="${sessionInfo.userId}"
+	 * 2.Java 단에서 사용법
+	 * super.getSession().getUserId()
+	 * 
+	 * @return
+	 */
+	@ModelAttribute("sessionInfo")
+	public User getSession() {
+		return TsaSession.getInfo();
+	}
+
+	/**
+	 * Get 로그인 메뉴 목록
+	 * 
+	 * @return
+	 */
+	@ModelAttribute("loginMenuList")
+	public Collection<Menu> getLoginMenuList() {
+		return TsaSession.getLoginMenuList();
+	}
+
+}

+ 65 - 0
style24.admin/src/main/java/com/style24/admin/support/controller/TsaErrorController.java

@@ -0,0 +1,65 @@
+package com.style24.admin.support.controller;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.servlet.ModelAndView;
+
+import lombok.extern.slf4j.Slf4j;
+
+import com.gagaframework.web.rest.server.GagaResponseStatus;
+
+/**
+ * Error Controller
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@Controller
+@Slf4j
+@RequestMapping(value = "/error")
+public class TsaErrorController extends TsaBaseController {
+
+	/**
+	 * Error
+	 */
+	@GetMapping("/500")
+	public ModelAndView error(HttpServletRequest request) throws HttpRequestMethodNotSupportedException {
+		ModelAndView mav = new ModelAndView("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());
+				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());
+		mav.addObject("message", String.valueOf(request.getAttribute(RequestDispatcher.ERROR_MESSAGE)));
+
+		return mav;
+	}
+
+	@GetMapping("/noSession")
+	public ModelAndView noSession(HttpServletRequest request) throws HttpRequestMethodNotSupportedException {
+		ModelAndView mav = new ModelAndView("error/500");
+
+		mav.addObject("status", GagaResponseStatus.UNAUTHORIZED.getCode());
+		mav.addObject("message", String.valueOf(request.getAttribute(RequestDispatcher.ERROR_MESSAGE)));
+
+		return mav;
+	}
+
+}

+ 14 - 0
style24.admin/src/main/java/com/style24/admin/support/env/TsaConstants.java

@@ -0,0 +1,14 @@
+package com.style24.admin.support.env;
+
+/**
+ * 변경될 소지가 있는 변수 값을 정의
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+public class TsaConstants {
+
+	// 사이트코드
+	public static final String SITE_CD = "10";
+
+}

+ 64 - 0
style24.admin/src/main/java/com/style24/admin/support/interceptor/TsaDefaultInterceptor.java

@@ -0,0 +1,64 @@
+package com.style24.admin.support.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+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.admin.support.security.session.TsaSession;
+import com.style24.core.support.exception.TscNoSessionException;
+import com.style24.core.support.message.TscMessageByLocale;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 모든 Request에 대해 세션 확인
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@Component
+@Slf4j
+public class TsaDefaultInterceptor extends HandlerInterceptorAdapter {
+
+	@Autowired
+	private TscMessageByLocale message;
+
+	@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());
+
+		boolean isLogin = TsaSession.isLogin();
+		log.info("isLogin: [{}]", isLogin);
+
+		if (!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
+				throw new TscNoSessionException(message.getMessage("LOGN_0003"));
+			}
+
+			return false;
+		}
+
+		return super.preHandle(request, response, handler);
+	}
+
+	@Override
+	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+		ModelAndView modelAndView) throws Exception {
+		// Do nothing
+	}
+
+}

+ 48 - 0
style24.admin/src/main/java/com/style24/admin/support/interceptor/TsaPosInterceptor.java

@@ -0,0 +1,48 @@
+package com.style24.admin.support.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+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.admin.support.security.session.TsaSession;
+import com.style24.core.support.exception.TscNoSessionException;
+import com.style24.core.support.message.TscMessageByLocale;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 매장POS 접근 시 매장로그인 세션 확인
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@Component
+@Slf4j
+public class TsaPosInterceptor extends HandlerInterceptorAdapter {
+
+	@Autowired
+	private TscMessageByLocale message;
+
+	@Override
+	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+		boolean isStoreLogin = TsaSession.isStoreLogin();
+		log.info("isStoreLogin: [{}]", isStoreLogin);
+
+		if (!isStoreLogin) {
+			throw new TscNoSessionException(message.getMessage("LOGN_0003"));
+		}
+
+		return super.preHandle(request, response, handler);
+	}
+
+	@Override
+	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+		ModelAndView modelAndView) throws Exception {
+		// Do nothing
+	}
+
+}

+ 90 - 0
style24.admin/src/main/java/com/style24/admin/support/security/TsaAuthenticationProvider.java

@@ -0,0 +1,90 @@
+package com.style24.admin.support.security;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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.stereotype.Component;
+
+import com.style24.admin.biz.service.TsaLoginService;
+import com.style24.core.support.message.TscMessageByLocale;
+import com.style24.persistence.domain.User;
+
+import lombok.extern.slf4j.Slf4j;
+
+import com.gagaframework.web.core.security.GagaPasswordEncoder;
+import com.gagaframework.web.util.GagaCryptoUtil;
+
+/**
+ * 로그인 인증 처리
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@Component
+@Slf4j
+public class TsaAuthenticationProvider implements AuthenticationProvider {
+
+	@Autowired
+	private TsaLoginService loginService;
+
+	@Autowired
+	private TscMessageByLocale message;
+
+	@Autowired
+	private GagaPasswordEncoder passwordEncoder;
+
+	@Override
+	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+		String userId = authentication.getName();
+		String passwd = authentication.getCredentials().toString();
+		log.info("userId: {}, passwd: {}", userId, passwd);
+
+		// 로그인 정보
+		User loginInfo = loginService.getUserById(authentication.getName());
+		log.info("loginInfo: {}", loginInfo);
+
+		if (loginInfo == null) {
+			log.error(String.format("User with ID=%s was not found!", userId));
+			throw new BadCredentialsException(message.getMessage("LOGN_0001"));
+		}
+
+		// 로그인 실패누적건수가 5이면
+		if (loginInfo.getLoginFailCnt() == 5) {
+			throw new BadCredentialsException(message.getMessage("LOGN_0005"));
+		}
+
+		log.info("encoded password: {}", GagaCryptoUtil.encryptSha512(passwd));
+		boolean isMatch = passwordEncoder.matches(passwd, loginInfo.getPasswd());
+		log.info("isMatch: {}", isMatch);
+
+		if (!isMatch) {
+			// 로그인 실패 남기기
+			loginService.createLoginFail(loginInfo.getUserNo(), "Y");
+			throw new BadCredentialsException(message.getMessage("LOGN_0002"));
+		}
+
+		// 권한 설정
+		List<SimpleGrantedAuthority> authorities = new ArrayList<>();
+		authorities.add(new SimpleGrantedAuthority(loginInfo.getRoleCd()));
+
+		// 인증 토큰 생성
+		UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
+			loginInfo.getUserId(), loginInfo.getPasswd(), authorities);
+		authToken.setDetails(new TsaLoginDetails(loginInfo, authorities));
+
+		return authToken;
+	}
+
+	@Override
+	public boolean supports(Class<?> authentication) {
+		return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
+	}
+
+}

+ 97 - 0
style24.admin/src/main/java/com/style24/admin/support/security/TsaLoginDetails.java

@@ -0,0 +1,97 @@
+package com.style24.admin.support.security;
+
+import java.util.Collection;
+
+import org.apache.commons.lang3.StringUtils;
+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.Menu;
+import com.style24.persistence.domain.User;
+
+/**
+ * 로그인 상세 정보
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@SuppressWarnings("serial")
+@JsonSerialize
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class TsaLoginDetails implements UserDetails {
+
+	private User loginInfo = null;
+
+	private Collection<SimpleGrantedAuthority> authorities = null;
+
+	// 메뉴 목록 (로그인 시 설정)
+	private Collection<Menu> menuList;
+
+	public TsaLoginDetails() {
+
+	}
+
+	public TsaLoginDetails(User 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 loginInfo.getUserId();
+	}
+
+	@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 (StringUtils.isNotBlank(loginInfo.getUserId())) ? true : false;
+	}
+
+	public User getLoginInfo() {
+		return loginInfo;
+	}
+
+	public void setLoginMenuList(Collection<Menu> menuList) {
+		this.menuList = menuList;
+	}
+
+	public Collection<Menu> getLoginMenuList() {
+		return menuList;
+	}
+
+}

+ 117 - 0
style24.admin/src/main/java/com/style24/admin/support/security/config/TsaSecurityConfig.java

@@ -0,0 +1,117 @@
+package com.style24.admin.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.util.matcher.AntPathRequestMatcher;
+
+import com.style24.admin.support.security.TsaAuthenticationProvider;
+import com.style24.admin.support.security.handler.TsaLoginSuccessHandler;
+import com.style24.admin.support.security.handler.TsaLogoutSuccessHandler;
+
+/**
+ * Java Security를 이용한 Login 처리
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@Configuration
+@EnableWebSecurity
+//@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
+//@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
+public class TsaSecurityConfig extends WebSecurityConfigurerAdapter {
+
+	private static final String[] UNAUTHORIZED_RESOURCE_LIST = new String[] {"/*", "/index"};
+
+	private static final String[] UNSECURED_RESOURCE_LIST = new String[] {"/image/**", "/ux/**", "/data/**", "/smartEditor/**"};
+
+	@Autowired
+	private TsaAuthenticationProvider authenticationProvider;
+
+	/**
+	 * Register the Authentication Provider
+	 */
+	@Override
+	public void configure(AuthenticationManagerBuilder auth) throws Exception {
+		auth.authenticationProvider(authenticationProvider);
+	}
+
+	@Override
+	protected void configure(HttpSecurity httpSecurity) throws Exception {
+		// static resources
+		httpSecurity.authorizeRequests()
+			.antMatchers(UNAUTHORIZED_RESOURCE_LIST).permitAll()
+//			.anyRequest().authenticated()
+//				.antMatchers("/backoffice/**").hasAnyAuthority(FccConstants.ROLE.ADMIN.value())
+//				.antMatchers(new String[] { "/backoffice/*", "/backoffice/chairman/**" }).hasAnyAuthority(FccConstants.ROLE.CC_CHIEF.value())
+//				.antMatchers(new String[] { "/backoffice/*", "/backoffice/evaluator/**" }).hasAnyAuthority(FccConstants.ROLE.COUNSELOR.value())
+			.and()
+			.formLogin()
+			.usernameParameter("userId")
+			.passwordParameter("passwd")
+			.loginPage("/signin")
+			.loginProcessingUrl("/login")
+			.successHandler(loginSuccessHandler())
+			.failureUrl("/?error=true")
+			.and()
+			.logout()
+			.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
+			.logoutSuccessHandler(logoutSuccessHandler())
+			.and()
+			.csrf().disable() // CSRF(Cross Site Request Forgery) 해제
+//			.csrf().requireCsrfProtectionMatcher(requireCsrfProtectionMatcher())
+			.exceptionHandling()
+			.accessDeniedPage("/403")
+			.and()
+			.headers().frameOptions().disable() // iframe deny 설정 제거(네이버스마트에디터 사용 때문에 추가)
+//			.and()
+//			.rememberMe().tokenRepository(persistentTokenRepository())
+//				.tokenValiditySeconds(60 * 60 * 24 * 10) // 10 days
+//				.useSecureCookie(true)
+//				.key("sisunTsa")
+//			.and()
+//			.sessionManagement()
+//				.maximumSessions(1)
+//				.maxSessionsPreventsLogin(false)
+//				.maxSessionsPreventsLogin(true)
+//				.sessionRegistry(sessionRegistry())
+//				.expiredUrl("/index")
+		;
+	}
+
+	@Override
+	public void configure(WebSecurity webSecurity) throws Exception {
+		webSecurity
+			.ignoring()
+			.antMatchers(UNSECURED_RESOURCE_LIST);
+	}
+
+	@Bean
+	public TsaLoginSuccessHandler loginSuccessHandler() {
+		return new TsaLoginSuccessHandler();
+	}
+
+	@Bean
+	public TsaLogoutSuccessHandler logoutSuccessHandler() {
+		return new TsaLogoutSuccessHandler();
+	}
+
+//	@Bean
+//	public RequestMatcher requireCsrfProtectionMatcher() {
+//		GagaCsrfSecurityRequestMatcher bean = new GagaCsrfSecurityRequestMatcher();
+//
+//		bean.setDisableUrls(new String[] {
+//				"/",
+//				"/index",
+//				"/customer/post/find/form"
+//		});
+//
+//		return bean;
+//	}
+
+}

+ 73 - 0
style24.admin/src/main/java/com/style24/admin/support/security/handler/TsaLoginSuccessHandler.java

@@ -0,0 +1,73 @@
+package com.style24.admin.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.Authentication;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.stereotype.Component;
+
+import com.style24.admin.biz.service.TsaLoginService;
+import com.style24.admin.support.security.TsaLoginDetails;
+import com.style24.admin.support.security.session.TsaSession;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 로그인 성공 시 호출되는 Handler
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@Component
+@Slf4j
+public class TsaLoginSuccessHandler implements AuthenticationSuccessHandler {
+
+	@Autowired
+	private TsaLoginService loginService;
+
+	@Override
+	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
+		// 로그인 상세 정보
+		TsaLoginDetails loginDetails = (TsaLoginDetails)authentication.getDetails();
+
+		Integer userNo = loginDetails.getLoginInfo().getUserNo();
+		log.info("userNo: {}", userNo);
+
+		// 로그인 성공 시 로그인실패수가 0보다 크면 로그인실패수 reset
+		int failCnt = loginService.getLoginFailCount(userNo);
+		if (failCnt > 0) {
+			loginService.createLoginFail(userNo, "N");
+		}
+
+		// 로그인 메뉴 목록
+		loginDetails.setLoginMenuList(loginService.getLoginMenuList(userNo));
+
+		// 최종로그인일시 Update
+		loginService.updateLastLoginDate(userNo);
+
+		// 로그인이력 남기기
+		loginService.createLoginHistory(userNo);
+
+		// 세션 생성
+		this.createSession(request, loginDetails);
+
+		response.sendRedirect("/");
+	}
+
+	/**
+	 * Session 생성
+	 * 
+	 * @param request - HttpServletRequest
+	 * @param loginDetails - 로그인 상세 정보
+	 */
+	private void createSession(HttpServletRequest request, TsaLoginDetails loginDetails) {
+		TsaSession.getNewSess(request, -1);
+		TsaSession.setAttribute(request, loginDetails);
+	}
+
+}

+ 33 - 0
style24.admin/src/main/java/com/style24/admin/support/security/handler/TsaLogoutSuccessHandler.java

@@ -0,0 +1,33 @@
+package com.style24.admin.support.security.handler;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
+import org.springframework.stereotype.Component;
+
+import com.style24.admin.support.security.session.TsaSession;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 로그아웃 성공 시 호출되는 Handler
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@Component
+@Slf4j
+public class TsaLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
+
+	@Override
+	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
+		TsaSession.invalidate(request);
+		response.sendRedirect("/");
+	}
+
+}

+ 85 - 0
style24.admin/src/main/java/com/style24/admin/support/security/session/TsaSession.java

@@ -0,0 +1,85 @@
+package com.style24.admin.support.security.session;
+
+import java.util.Collection;
+
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+
+import com.style24.admin.support.security.TsaLoginDetails;
+import com.style24.core.support.session.TscSession;
+import com.style24.persistence.domain.Menu;
+import com.style24.persistence.domain.Pos;
+import com.style24.persistence.domain.User;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Session
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@Slf4j
+public class TsaSession extends TscSession {
+
+	/**
+	 * Get Session Info.
+	 * @return 세션 정보
+	 */
+	public static User getInfo() {
+		TsaLoginDetails loginDetails = (TsaLoginDetails)RequestContextHolder.currentRequestAttributes().getAttribute("session", RequestAttributes.SCOPE_SESSION);
+
+		if (loginDetails == null)
+			return null;
+
+		return loginDetails.getLoginInfo();
+	}
+
+	/**
+	 * 로그인 여부
+	 * @return true/false
+	 */
+	public static boolean isLogin() {
+		TsaLoginDetails loginDetails = (TsaLoginDetails)RequestContextHolder.currentRequestAttributes().getAttribute("session", RequestAttributes.SCOPE_SESSION);
+
+		if (loginDetails == null)
+			return false;
+
+		return loginDetails.isLogin();
+	}
+
+	/**
+	 * Get the login menu list.
+	 * @return 로그인 메뉴 목록
+	 */
+	public static Collection<Menu> getLoginMenuList() {
+		TsaLoginDetails loginDetails = (TsaLoginDetails)RequestContextHolder.currentRequestAttributes().getAttribute("session", RequestAttributes.SCOPE_SESSION);
+
+		if (loginDetails == null)
+			return null;
+
+		return loginDetails.getLoginMenuList();
+	}
+
+	/**
+	 * 매장 세션 정보
+	 * @return 매장 세션 정보
+	 */
+	public static Pos getPosInfo() {
+		return (Pos)RequestContextHolder.currentRequestAttributes().getAttribute("sessionPosInfo", RequestAttributes.SCOPE_SESSION);
+	}
+
+	/**
+	 * 매장로그인 여부
+	 * @return true/false
+	 */
+	public static boolean isStoreLogin() {
+		Pos posInfo = (Pos)RequestContextHolder.currentRequestAttributes().getAttribute("sessionPosInfo", RequestAttributes.SCOPE_SESSION);
+
+		if (posInfo == null)
+			return false;
+
+		return true;
+	}
+
+}

+ 54 - 0
style24.admin/src/main/java/com/style24/admin/support/startup/TsaApplication.java

@@ -0,0 +1,54 @@
+package com.style24.admin.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. 10. 5
+ */
+@EnableCaching
+@Configuration
+@EnableAutoConfiguration(exclude = {DataSourceTransactionManagerAutoConfiguration.class, DataSourceAutoConfiguration.class})
+@ComponentScan(basePackages = {GagaConstants.GAGA_PACKAGE, TscConstants.BASE_PACKAGE})
+@Slf4j
+public class TsaApplication {
+
+	@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(TsaApplication.class, args);
+	}
+
+}

+ 19 - 0
style24.admin/src/main/java/com/style24/admin/support/startup/TsaServletInitializer.java

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

+ 44 - 0
style24.admin/src/main/java/com/style24/persistence/TsaBaseDomain.java

@@ -0,0 +1,44 @@
+package com.style24.persistence;
+
+import java.io.Serializable;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import lombok.Data;
+
+/**
+ * Domain의 기본 Super class
+ * Domain 클래스 생성 시 이 클래스를 상속받아서 처리한다.
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@SuppressWarnings("serial")
+@Data
+public class TsaBaseDomain implements Serializable {
+
+	@JsonInclude(JsonInclude.Include.NON_EMPTY)
+	private Integer regNo;
+
+	@JsonInclude(JsonInclude.Include.NON_EMPTY)
+	private String regDt;
+
+	@JsonInclude(JsonInclude.Include.NON_EMPTY)
+	private Integer updNo;
+
+	@JsonInclude(JsonInclude.Include.NON_EMPTY)
+	private String updDt;
+
+	// 등록/수정화면 제어용(N:신규, U:상세/수정, C:복사)
+	private String mode;
+
+	// CRUD
+	private String crud;
+
+	// Pagination
+	private TsaPageRequest pageable;
+	private int pageNo = 1;
+	private int pageSize = 50;
+	private int pageUnit = 10;
+
+}

+ 123 - 0
style24.admin/src/main/java/com/style24/persistence/TsaPageRequest.java

@@ -0,0 +1,123 @@
+package com.style24.persistence;
+
+import java.io.Serializable;
+
+import lombok.Data;
+
+/**
+ * Paging
+ *
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@SuppressWarnings("serial")
+@Data
+public class TsaPageRequest implements Serializable {
+
+	private final int pageNo;   // 페이지번호
+	private final int pageSize; // 조회할 row수
+	private final int pageUnit; // 그룹핑 페이지 단위
+	private int totalCount = 0; // 전체 row 건수
+
+	public TsaPageRequest(int pageNo, int pageSize) {
+		this(pageNo, pageSize, 10);
+	}
+
+	public TsaPageRequest(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 (!(firstCount == 1 && loopCount == 1)) {
+			if (getPageNo() == 1) {
+				pageTag.append("<a class=\"arrow\" href=\"#\"><i class=\"fa fa-angle-double-left\" alt=\"맨처음\"></i></a>\n");
+			} else {
+				pageTag.append("<a class=\"arrow\" href=\"#pageNo=1\"><i class=\"fa fa-angle-double-left\" alt=\"맨처음\"></i></a>\n");
+			}
+
+			if (getPageGroup() == 1) {
+				pageTag.append("<a class=\"arrow\" href=\"#\"><i class=\"fa fa-angle-left\" alt=\"이전페이지\"></i></a>\n");
+			} else {
+				pageTag.append("<a class=\"arrow\" href=\"#pageNo=").append((getPageGroup() - 1) * pageUnit).append("\"><i class=\"fa fa-angle-left\" alt=\"이전페이지\"></i></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()) {
+					pageTag.append("<a class=\"arrow\" href=\"#\"><i class=\"fa fa-angle-right\" alt=\"다음페이지\"></i></a>\n");
+					pageTag.append("<a class=\"arrow\" href=\"#\"><i class=\"fa fa-angle-double-right\" alt=\"맨마지막\"></i></a>\n");
+				} else {
+					pageTag.append("<a class=\"arrow\" href=\"#pageNo=").append(getPageGroup() * pageUnit + 1).append("\"><i class=\"fa fa-angle-right\" alt=\"다음페이지\"></i></a>\n");
+					pageTag.append("<a class=\"arrow\" href=\"#pageNo=").append(getTotalPage()).append("\"><i class=\"fa fa-angle-double-right\" alt=\"맨마지막\"></i></a>\n");
+				}
+			}
+		}
+		return pageTag.toString();
+	}
+
+	@Override
+	public String toString() {
+		return String.format("Page request [pageNo: %d, pageSize %d, pageUnit %d]", getPageNo(), pageSize, pageUnit);
+	}
+
+}

+ 81 - 0
style24.admin/src/main/java/com/style24/persistence/domain/Brand.java

@@ -0,0 +1,81 @@
+package com.style24.persistence.domain;
+
+import com.style24.persistence.TsaBaseDomain;
+
+import lombok.Data;
+
+/**
+ * 브랜드 Domain
+ * 
+ * @author gagamel
+ * @since 2020. 10. 7
+ */
+@SuppressWarnings("serial")
+@Data
+public class Brand extends TsaBaseDomain {
+
+//	//HTML
+//	private String cpnId;		//쿠폰ID
+//	private String cpnType;		//쿠폰유형
+//	private String cpnApplyYn;	//쿠폰적용여부
+//	private String refVal;		//관련값(쿠폰유형이 "10:상품쿠폰"일 때는 상품코드, "11:브랜드쿠폰"일 때는 브랜드코드, "12:카테고리쿠폰"일 때는 카테고리코드)
+//	private String delYn;		//삭제여부
+
+	private String brandGb;		// 브랜드구분(자사(S) 입점(E))
+	private String brandCd;		// 브랜드코드
+	private String brandEnm;	// 브랜드영문명
+	private String brandKnm;	// 브랜드한글명
+	private String brandGrpNm;	// 브랜드그룹명
+	private String delvLocCd;	// 출고처코드
+	private String delvLocNm;	// 출고처이름
+	private String erpBrandCd;	// ERP브랜드코드
+	private String brandImg;	// 브랜드이미지
+	private float pntPrate10;	// 포인트적립율(PC정상)
+	private float pntMrate10;	// 포인트적립율(모바일정상)
+	private float pntPrate20;	// 포인트적립율(PC이월)
+	private float pntMrate20;	// 포인트적립율(모바일이월)
+	private float dispOrd;		// 표시순서
+
+//	//TB_SUPPLY_COMPANY
+//	private String supplyCompCd; /*공급업체코드*/
+//	private String supplyCompNm; /*공급업체명*/
+//	private String bizGb; /*사업자구분(C:법인, P:개인)*/
+//	private String bizNo; /*사업자등록번호*/
+//	private String bizKind; /*업종*/
+//	private String bizType; /*업태*/
+//	private String ownerNm; /*대표자명*/
+//	private String bizZipcode; /*사업장우편번호*/
+//	private String bizBaseAddr; /*사업장기본주소*/
+//	private String bizDtlAddr; /*사업장상세주소*/
+//	private String mainTelno; /*대표전화번호*/
+//	private String mainFaxno; /*대표팩스번호*/
+//	private String homepageUrl; /*홈페이지URL*/
+//	private String selfYn; /*자사여부(Y:자사, N:입점)*/
+//	private String supplyStat; /*입점상태(공통코드G010)*/
+//	private int minOrdAmt; /*무료배송비최소주문금액*/
+//	private int delvFee; /*배송비*/
+//	private int sellFeeRate; /*판매수수료율*/
+//	private String settleDay; /*정산일(매월)*/
+//	private String bankNm; /*은행명*/
+//	private String accountNo; /*계좌번호*/
+//	private String depositorNm; /*예금주명*/
+//	private String remarks; /*비고*/
+//	private String useYn; /*사용여부(Y:사용)*/
+//
+//	//TB_DELIVERY_LOC
+//	private String delvLocClsf; /*출고처분류(공통코드G024)*/
+//	private String delvAssignOrd; /*출고지정순서(출고처유형이 "21:직송매장"일 때만 사용)*/
+//	private String delvAssignGrade; /*출고지정등급(출고처유형이 "21:직송매장"일 때만 사용)*/
+//	private String delvPossibQty; /*출고가능수량(출고처유형이 "21:직송매장"일 때만 사용)*/
+//	private String delvFeeRate; /*출고수수료율*/
+//	private String delvLocPostNo; /*출고처우편번호*/
+//	private String delvLocBaseAddr; /*출고처기본주소*/
+//	private String delvLocDtlAddr; /*출고처상세주소*/
+//	private String rtnLocPostNo; /*반품처우편번호*/
+//	private String rtnLocBaseAddr; /*반품처기본주소*/
+//	private String rtnLocDtlAddr; /*반품처상세주소*/
+//	private String rtnLocTelno; /*반품처전화번호*/
+//	private String rtnLocNm; /*반품처명*/
+//	private String invoicePrintType; /*송장출력형태(공통코드G025)*/
+
+}

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

@@ -0,0 +1,26 @@
+package com.style24.persistence.domain;
+
+import com.style24.persistence.TsaBaseDomain;
+
+import lombok.Data;
+
+/**
+ * 색상 Domain
+ * 
+ * @author gagamel
+ * @since 2020. 10. 7
+ */
+@SuppressWarnings("serial")
+@Data
+public class Color extends TsaBaseDomain {
+
+	private String supplyCompCd;	// 공급업체코드
+	private String colorCd;			// 색상코드
+	private String colorEnm;		// 색상영문명
+	private String colorKnm;		// 색상한글명
+	private String colorFile;		// 색상파일
+	private String colorGrpNm;		// 색상그룹명
+	private String colorGrpFile;	// 색상그룹파일
+	private String useYn;			// 사용여부
+
+}

+ 29 - 0
style24.admin/src/main/java/com/style24/persistence/domain/CommonCode.java

@@ -0,0 +1,29 @@
+package com.style24.persistence.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.style24.persistence.TsaBaseDomain;
+
+import lombok.Data;
+
+/**
+ * 공통코드 Domain
+ * 
+ * @author gagamel
+ * @since 2020. 10. 7
+ */
+@SuppressWarnings("serial")
+@Data
+public class CommonCode extends TsaBaseDomain {
+
+	private String cdGb;
+	private String cd;
+	private String cdNm;
+	private String cdDesc;
+	private int dispOrd;
+	private String useYn;
+	private String mdId;
+
+	@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+	private String[] exceptCds;
+
+}

+ 41 - 0
style24.admin/src/main/java/com/style24/persistence/domain/DeliveryLoc.java

@@ -0,0 +1,41 @@
+package com.style24.persistence.domain;
+
+import com.style24.persistence.TsaBaseDomain;
+
+import lombok.Data;
+
+/**
+ * 출고처 Domain
+ * 
+ * @author gagamel
+ * @since 2020. 10. 7
+ */
+@SuppressWarnings("serial")
+@Data
+public class DeliveryLoc extends TsaBaseDomain {
+
+	private String delvLocCd;			// 출고처코드
+	private String delvLocNm;			// 출고처명
+	private String supplyCompCd;		// 공급업체코드
+	private String supplyCompNm;		// 공급업체명
+	private String delvLocClsf;			// 출고처종류
+	private String delvLocClsfNm;		// 출고처종류명
+	private Integer delvAssignOrd;		// 출고지정순서
+	private String delvAssignGrade;		// 출고지정등급
+	private float stockApplRate;		// 재고적용율
+	private float delvFeeRate;			// 출고수수료율
+	private String delvLocZipcode;		// 출고처우편번호
+	private String delvLocBaseAddr;		// 출고처기본주소
+	private String delvLocDtlAddr;		// 출고처상세주소
+	private String rtnLocZipcode;		// 반품처우편번호
+	private String rtnLocBaseAddr;		// 반품처기본주소
+	private String rtnLocDtlAddr;		// 반품처상세주소
+	private String invoicePrintType;	// 송장출력형태(공통코드G025)
+	private String useYn;				// 사용여부
+
+//	private String delvLocType;
+//	private String delvBrandCode;
+//
+//	private String minStockLinkQty;
+
+}

+ 27 - 0
style24.admin/src/main/java/com/style24/persistence/domain/Itemkind.java

@@ -0,0 +1,27 @@
+package com.style24.persistence.domain;
+
+import com.style24.persistence.TsaBaseDomain;
+
+import lombok.Data;
+
+/**
+ * 품목 Domain
+ * 
+ * @author gagamel
+ * @since 2020. 10. 7
+ */
+@SuppressWarnings("serial")
+@Data
+public class Itemkind extends TsaBaseDomain {
+
+	private String supplyCompCd;	// 공급업체코드
+	private String itemkindCd;		// 품목코드
+	private String itemkindNm;		// 품목명
+	private String itemkindEnm;		// 품목영문명
+	private String erpItemkindCd;	// ERP품목코드
+	private String niClsfCd;		// 고시분류코드
+	private String useYn;			// 사용여부
+
+//	private String index;
+
+}

+ 45 - 0
style24.admin/src/main/java/com/style24/persistence/domain/Menu.java

@@ -0,0 +1,45 @@
+package com.style24.persistence.domain;
+
+import java.util.Collection;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.style24.persistence.TsaBaseDomain;
+
+import lombok.Data;
+
+/**
+ * 메뉴 Domain
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@SuppressWarnings("serial")
+@Data
+@JsonSerialize
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Menu extends TsaBaseDomain {
+
+	public Menu() {
+
+	}
+
+	private String menuId;
+	private String menuNm;
+	private String menuGb;
+	private String menuDesc;
+	private String menuUrl;
+	private int dispOrd;
+	private String useYn;
+	private String pmenuId;
+	private String pmenuNm;
+	private String navigation;
+	private int menuLvl;
+	private int leafLvl;
+	private String treePath; // AG-GRID트리패스(ag-Grid에서 트리를 표시하기 위한 패스. ag-Grid미사용시 필요 없음)
+
+	private Collection<MenuRole> menuRoleList;
+	private String roleCds;
+	private String roleNms;
+
+}

+ 23 - 0
style24.admin/src/main/java/com/style24/persistence/domain/MenuAccessHst.java

@@ -0,0 +1,23 @@
+package com.style24.persistence.domain;
+
+import com.style24.persistence.TsaBaseDomain;
+
+import lombok.Data;
+
+/**
+ * 메뉴접속이력 Domain
+ * 
+ * @author gagamel
+ * @since 2020. 10. 7
+ */
+@SuppressWarnings("serial")
+@Data
+public class MenuAccessHst extends TsaBaseDomain {
+
+	private Integer menuAccessSq;
+	private String userNo;
+	private String menuId;
+	private String reqUrl;
+	private String params;
+
+}

+ 21 - 0
style24.admin/src/main/java/com/style24/persistence/domain/MenuRole.java

@@ -0,0 +1,21 @@
+package com.style24.persistence.domain;
+
+import com.style24.persistence.TsaBaseDomain;
+
+import lombok.Data;
+
+/**
+ * 메뉴권한 Domain
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@SuppressWarnings("serial")
+@Data
+public class MenuRole extends TsaBaseDomain {
+
+	private String roleCd;
+	private String roleNm;
+	private String menuId;
+
+}

+ 90 - 0
style24.admin/src/main/java/com/style24/persistence/domain/Pos.java

@@ -0,0 +1,90 @@
+package com.style24.persistence.domain;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.style24.persistence.TsaBaseDomain;
+
+import lombok.Data;
+
+/**
+ * 매장 Domain
+ * 
+ * @author jsshin
+ * @since 2020. 10. 5
+ */
+@SuppressWarnings("serial")
+@Data
+@JsonSerialize
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Pos extends TsaBaseDomain {
+
+	// 세션을 JSON 형식으로 레디스에 저장하려면 기본 생성자를 반드시 명시해야 한다
+	public Pos() {
+
+	}
+
+	private String supplyCompCd;
+	private double sellFeeRate;
+	private Long ordNo;
+	private Long ordDtlNo;
+	private String orderNm;
+	private String ordDtlStat;
+	private String recipNm;
+	private String recipPhnno;
+	private String recipTelno;
+	private String recipPostNo;
+	private String recipBaseAddr;
+	private String recipDtlAddr;
+	private String delvMemo;
+	private String goodsCd;
+	private String goodsNm;
+	private String goodsNum;
+	private String colorCd;
+	private String sizeCd;
+	private String ordQty;
+	private String ordDt;
+	private String imgPath1;
+	private String imgType;
+	private String delvArGb;
+	private String delvArGbY;
+	private String delvArGbN;
+	private String invoiceNo;
+	private String occurDt;
+	private String delvStdt;
+	private String usacGbNm;
+	private String delvLocCd;   //출고처코드
+	private String delvLocNm;   //출고처명
+	private String delvLocClsf; //매장유형
+	private Double delvFeeRate;
+	private int realAmt;
+	private int delvFeeAmt;
+	private String rsrvtGb;
+	private String schdlCldt;
+	private String recipAddr;
+	private String boxQty;
+	private String boxType;
+	private String fee;
+	private String delvMemo2;
+
+	private String delvAssignDt;
+	private String ordStDate;
+	private String ordEdDate;
+	private String usacStDate;
+	private String usacEdDate;
+	private String waitOrdStDate;
+	private String waitOrdEdDate;
+	private String deliveryStDate;
+	private String deliveryEdDate;
+
+	private String ordDtlNos;
+	//@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+	private String[] ordDtlNoArr;
+
+	private String goodsType;
+
+	private String shipCompCd;
+
+	private String rejectReason;
+	private String delvArGbCnl;
+
+}

+ 42 - 0
style24.admin/src/main/java/com/style24/persistence/domain/SupplyCompany.java

@@ -0,0 +1,42 @@
+package com.style24.persistence.domain;
+
+import com.style24.persistence.TsaBaseDomain;
+
+import lombok.Data;
+
+/**
+ * 공급업체 Domain
+ * 
+ * @author gagamel
+ * @since 2020. 10. 7
+ */
+@SuppressWarnings("serial")
+@Data
+public class SupplyCompany extends TsaBaseDomain {
+
+	private String supplyCompCd;	// 공급업체코드
+	private String supplyCompNm;	// 공급업체명
+	private String bizGb;			// 사업자구분
+	private String bizNo;			// 사업자번호
+	private String bizKind;			// 업종
+	private String bizType;			// 업태
+	private String ownerNm;			// 대표자명
+	private String bizZipcode;		// 사업장우편번호
+	private String bizBaseAddr;		// 사업장기본주소
+	private String bizDtlAddr;		// 사업장상세주소
+	private String mainTelno;		// 대표전화번호
+	private String mainFaxno;		// 대표팩스번호
+	private String homepageUrl;		// 홈페이지URL
+	private String selfYn;			// 자사여부(Y:자사, N:입점)
+	private String supplyStat;		// 입점상태(공통코드G010)
+	private String minOrdAmt;		// 무료배송비최소주문금액
+	private String delvFee;			// 배송비
+	private String sellFeeRate;		// 판매수수료율
+	private String settleDay;		// 정산일(매월)
+	private String bankNm;			// 은행명
+	private String accountNo;		// 계좌번호
+	private String depositorNm;		// 예금주명
+	private String remarks;			// 비고
+	private String useYn;			// 사용여부
+
+}

+ 60 - 0
style24.admin/src/main/java/com/style24/persistence/domain/User.java

@@ -0,0 +1,60 @@
+package com.style24.persistence.domain;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.style24.persistence.TsaBaseDomain;
+
+import lombok.Data;
+
+/**
+ * 사용자 Domain
+ * 
+ * @author gagamel
+ * @since 2020. 10. 5
+ */
+@SuppressWarnings("serial")
+@Data
+@JsonSerialize
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class User extends TsaBaseDomain {
+
+	// 세션을 JSON 형식으로 레디스에 저장하려면 기본 생성자를 반드시 명시해야 한다
+	public User() {
+
+	}
+
+	private Integer userNo;
+	private String userId;
+	private String userNm;
+	private String passwd;
+	private String roleCd;
+	private String roleNm;
+	private String roleRefVal;
+	private String supplyCompCd;
+	private String supplyCompNm;
+	private String vendorId;
+	private String vendorNm;
+	private String afLinkCd;
+	private String afLinkNm;
+	private String afChannel;
+	private String afChannelNm;
+	private String email;
+	private String cellPhnno;
+	private int pntAssignAmt;
+	private String ipAddr;
+	private String ipChkYn;
+	private String loginLdt;
+	private String passwdChgDt;
+	private String useYn;
+	private int loginFailCnt;
+	private String loginFailYn;
+
+	// 사용자 목록 검색조건
+	private String searchGb;
+	private String searchTxt;
+	private String callBackFun;
+
+	// 권한변경여부
+	private String roleChangeYn;
+
+}

+ 25 - 0
style24.admin/src/main/java/com/style24/persistence/domain/UserHst.java

@@ -0,0 +1,25 @@
+package com.style24.persistence.domain;
+
+import com.style24.persistence.TsaBaseDomain;
+
+import lombok.Data;
+
+/**
+ * 사용자이력 Domain
+ * 
+ * @author gagamel
+ * @since 2020. 10. 7
+ */
+@SuppressWarnings("serial")
+@Data
+public class UserHst extends TsaBaseDomain {
+
+	private Integer chgHstSq;	// 변경이력일련번호
+	private Integer userNo;		// 사용자번호
+	private String chgGb;		// 변경구분
+	private String chgContent;	// 변경내용
+
+	private String orgRoleCd;	// 변경전권한코드
+	private String roleCd;		// 변경후권한코드
+
+}

+ 24 - 0
style24.admin/src/main/java/com/style24/persistence/domain/UserMenu.java

@@ -0,0 +1,24 @@
+package com.style24.persistence.domain;
+
+import com.style24.persistence.TsaBaseDomain;
+
+import lombok.Data;
+
+/**
+ * 사용자메뉴 Domain
+ * 
+ * @author gagamel
+ * @since 2020. 10. 7
+ */
+@SuppressWarnings("serial")
+@Data
+public class UserMenu extends TsaBaseDomain {
+
+	private Integer userNo;	// 사용자번호
+	private String menuId;	// 메뉴ID
+	private String menuNm;	// 메뉴명
+	private String menuGb;	// 메뉴구분
+	private int menuLvl;	// 메뉴레벨
+	private String useYn;	// 사용여부
+
+}

+ 212 - 0
style24.admin/src/main/java/com/style24/persistence/mybatis/shop/TsaLogin.xml

@@ -0,0 +1,212 @@
+<?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.TsaLoginDao">
+
+	<!-- ID로 사용자 조회 -->
+	<select id="getUserById" parameterType="String" resultType="User">
+		/* TsaLogin.getUserById */
+		SELECT USER_NO                                                     /*사용자명*/
+		     , USER_ID                                                     /*사용자ID*/
+		     , USER_NM                                                     /*사용자명*/
+		     , PASSWD                                                      /*비밀번호*/
+		     , ROLE_CD                                                     /*권한코드*/
+		     , FN_GET_CODE_NM('G001',ROLE_CD)            AS ROLE_NM        /*권한명*/
+		     , ROLE_REF_VAL                                                /*권한관련값*/
+		     , CASE WHEN SUBSTR(ROLE_CD,1,1) IN ('A','B','E') THEN
+		                ROLE_REF_VAL
+		            END                                  AS SUPPLY_COMP_CD /*공급업체코드
+		     , CASE WHEN SUBSTRING(ROLE_CD,1,1) IN ('A','B','E') THEN
+		                (SELECT SUPPLY_COMP_NM
+		                 FROM   TB_SUPPLY_COMPANY
+		                 WHERE  SUPPLY_COMP_CD = A.ROLE_REF_VAL
+		                )
+		       END                                       AS SUPPLY_COMP_NM /*공급업체명*/
+		     , CASE WHEN SUBSTRING(ROLE_CD,1,1) = 'C' THEN
+		                ROLE_REF_VAL
+		       END                                       AS VENDOR_ID      /*벤더ID*/
+		     , CASE WHEN SUBSTRING(ROLE_CD,1,1) = 'C' THEN
+		                FN_GET_CODE_NM('G003',ROLE_REF_VAL)
+		       END                                       AS VENDOR_NM      /*벤더명*/
+		     , CASE WHEN SUBSTRING(ROLE_CD,1,1) = 'D' THEN
+		                ROLE_REF_VAL
+		       END                                       AS AF_LINK_CD     /*제휴링크코드*/
+		     , CASE WHEN SUBSTRING(ROLE_CD,1,1) = 'D' THEN
+		                (SELECT AF_LINK_NM
+		                 FROM   TB_AF_LINK
+		                 WHERE  AF_LINK_CD = A.ROLE_REF_VAL
+		                )
+		       END                                       AS AF_LINK_NM     /*제휴링크명*/
+		     , CASE WHEN SUBSTRING(ROLE_CD,1,1) = 'D' THEN
+		                (SELECT AF_CHANNEL
+		                 FROM   TB_AF_LINK
+		                 WHERE  AF_LINK_CD = A.ROLE_REF_VAL
+		                )
+		       END                                       AS AF_CHANNEL     /*제휴채널*/
+		     , CASE WHEN SUBSTRING(ROLE_CD,1,1) = 'D' THEN
+		                (SELECT FN_GET_CODE_NM('G053',AL.AF_CHANNEL)
+		                 FROM   TB_AF_LINK AL
+		                 WHERE  AL.AF_LINK_CD = A.ROLE_REF_VAL
+		                )
+		       END                                       AS AF_CHANNEL_NM  /*제휴채널명*/
+		     , EMAIL                                                       /*이메일*/
+		     , CELL_PHNNO                                                  /*휴대전화번호*/
+		     , PNT_ASSIGN_AMT                                              /*포인트부여가능금액*/
+		     , IP_ADDR                                                     /*IP주소*/
+		     , IP_CHK_YN                                                   /*IP체크여부*/
+		     , DATE_FORMAT(PASSWD_CHG_DT,'%Y%m%d%H%i%S') AS PASSWD_CHG_DT  /*비밀번호변경일시*/
+		FROM   TB_USER A
+		WHERE  USER_ID = #{userId}
+		AND    USE_YN = 'Y'
+	</select>
+	
+	<!-- 로그인실패 남기기 -->
+	<insert id="createLoginFail" parameterType="User">
+		/* TsaLogin.createLoginFail */
+		INSERT INTO TB_USER_LOGIN_FAIL (
+		       USER_NO
+		     , IP_ADDR
+		     , LOGIN_FAIL_CNT
+		     , REG_NO
+		     , REG_DT
+		     , UPD_NO
+		     , UPD_DT
+		)
+		VALUES (
+		       #{userNo}
+		     , #{ipAddr}
+		     , 1
+		     , #{userNo}
+		     , NOW()
+		     , #{userNo}
+		     , NOW()
+		)
+		ON DUPLICATE KEY UPDATE
+		       LOGIN_FAIL_CNT = CASE WHEN #{loginFailYn} = 'Y' THEN LOGIN_FAIL_CNT + 1 ELSE 0 END
+		     , UPD_NO = #{userNo}
+		     , UPD_DT = NOW()
+	</insert>
+
+	<!-- 로그인 실패건수 조회 -->
+	<select id="getLoginFailCount" parameterType="User" resultType="int">
+		/* TsaLogin.getLoginFailCount */
+		SELECT IFNULL((SELECT LOGIN_FAIL_CNT
+		               FROM   TB_USER_LOGIN_FAIL
+		               WHERE  USER_NO = #{userNo}
+		               AND    IP_ADDR = #{ipAddr}
+		              ),0) AS CNT
+		FROM   DUAL
+	</select>
+
+	<!-- 최종로그인일시 Update -->
+	<update id="updateLastLoginDate" parameterType="Integer">
+		/* TsaLogin.updateLastLoginDate */
+		UPDATE TB_USER
+		SET    LOGIN_LDT = NOW()
+		     , UPD_NO = #{userNo}
+		     , UPD_DT = NOW()
+		WHERE  USER_NO = #{userNo}
+	</update>
+
+	<!-- 로그인이력 남기기 -->
+	<insert id="createLoginHistory" parameterType="User">
+		/* TsaLogin.createLoginHistory */
+		INSERT INTO TB_USER_LOGIN_HST (
+		       USER_LOGIN_SQ
+		     , USER_NO
+		     , IP_ADDR
+		     , LOGIN_DT
+		     , REG_NO
+		     , REG_DT
+		)
+		VALUES (
+		       NULL
+		     , #{userNo}
+		     , #{ipAddr}
+		     , NOW()
+		     , #{userNo}
+		     , NOW()
+		)
+	</insert>
+
+	<!-- 로그인 메뉴 목록 -->
+	<select id="getLoginMenuList" parameterType="Integer" resultType="Menu">
+		/* TsaLogin.getLoginMenuList */
+		WITH RECURSIVE CONNECTBY2 AS (
+		    WITH RECURSIVE CONNECTBY1 AS (
+		        SELECT 1                                                  AS MENU_LVL
+		             , PMENU_ID
+		             , MENU_ID
+		             , MENU_NM
+		             , MENU_NM                                            AS NAVIGATION
+		             , MENU_GB
+		             , MENU_DESC
+		             , MENU_URL
+		             , CONCAT(MENU_ID,LPAD(CAST(DISP_ORD AS CHAR),4,'0')) AS DISP_ORD
+		        FROM   TB_MENU
+		        WHERE  PMENU_ID = 'ROOT'
+		        AND    USE_YN = 'Y'
+		        
+		        UNION ALL
+		        
+		        SELECT A.MENU_LVL + 1                                          AS MENU_LVL
+		             , B.PMENU_ID
+		             , B.MENU_ID
+		             , B.MENU_NM
+		             , CONCAT(A.NAVIGATION,' > ',B.MENU_NM)                    AS NAVIGATION
+		             , B.MENU_GB
+		             , B.MENU_DESC
+		             , B.MENU_URL
+		             , CONCAT(A.DISP_ORD,LPAD(CAST(B.DISP_ORD AS CHAR),4,'0')) AS DISP_ORD
+		        FROM   CONNECTBY1 A
+		        INNER JOIN TB_MENU B ON A.MENU_ID = B.PMENU_ID
+		                            AND B.USE_YN = 'Y'
+		    )
+		    SELECT MENU_LVL
+		         , PMENU_ID
+		         , MENU_ID
+		         , MENU_NM
+		         , NAVIGATION
+		         , MENU_GB
+		         , MENU_DESC
+		         , MENU_URL
+		         , DISP_ORD
+		    FROM   CONNECTBY1
+		    WHERE  MENU_ID IN (SELECT MENU_ID
+		                       FROM   TB_USER_MENU
+		                       WHERE  USER_NO = #{userNo}
+		                       AND    USE_ROLE != 'NNNN'
+		                      )
+		    
+		    UNION ALL
+		    
+		    SELECT B.MENU_LVL
+		         , B.PMENU_ID
+		         , B.MENU_ID
+		         , B.MENU_NM
+		         , B.NAVIGATION
+		         , B.MENU_GB
+		         , B.MENU_DESC
+		         , B.MENU_URL
+		         , B.DISP_ORD
+		    FROM   CONNECTBY2 A
+		    INNER JOIN CONNECTBY1 B ON A.PMENU_ID = B.MENU_ID
+		)
+		SELECT DISTINCT
+		       PMENU_ID               /*상위메뉴ID*/
+		     , MENU_ID                /*메뉴ID*/
+		     , MENU_NM                /*메뉴명*/
+		     , NAVIGATION             /*네비게이션*/
+		     , MENU_URL               /*메뉴URL*/
+		     , MENU_LVL               /*메뉴레벨*/
+		     , CASE WHEN IFNULL((SELECT MAX(MENU_LVL)
+		                         FROM   CONNECTBY2
+		                         WHERE  PMENU_ID = A.MENU_ID
+		                        ),0) > 0 THEN 1
+		            ELSE 0
+		       END        AS LEAF_LVL /*말단레벨*/
+		FROM   CONNECTBY2 A
+		WHERE  MENU_ID NOT LIKE 'P%' /*팝업메뉴제외*/
+		ORDER  BY DISP_ORD
+	</select>
+
+</mapper>

+ 352 - 0
style24.admin/src/main/java/com/style24/persistence/mybatis/shop/TsaRenderer.xml

@@ -0,0 +1,352 @@
+<?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.TsaRendererDao">
+
+	<!-- 공급업체 목록 -->
+	<select id="getSupplyCompanyList" parameterType="SupplyCompany" resultType="CommonCode">
+		/* TsaRenderer.getSupplyCompanyList */
+		SELECT SUPPLY_COMP_CD AS CD
+		     , SUPPLY_COMP_NM AS CD_NM
+		FROM   TB_SUPPLY_COMPANY
+		WHERE  USE_YN = 'Y'
+		<if test='supplyCompCd != null and supplyCompCd != ""'>
+		AND    SUPPLY_COMP_CD = #{supplyCompCd}
+		</if>
+		<if test='selfYn != null and selfYn != ""'>
+		AND    SELF_YN = #{selfYn}
+		</if>
+		ORDER  BY SUPPLY_COMP_NM
+	</select>
+	
+	<!-- 공통코드 목록 -->
+	<select id="getCommonCodeList" parameterType="CommonCode" resultType="CommonCode">
+		/* TsaRenderer.getCommonCodeList */
+		SELECT CD
+		     , CD_NM
+		FROM   TB_COMMON_CODE
+		WHERE  1 = 1
+		<if test="useYn != null and useYn != ''">
+		AND    USE_YN = #{useYn}
+		</if>
+		AND    CD_GB = #{cdGb}
+		<if test="cd != null and cd != ''">
+		AND    CD = #{cd}
+		</if>
+		<if test="cdNm != null and cdNm != ''">
+		AND    CD_NM = #{cdNm}
+		</if>
+		<if test="cdDesc != null and cdDesc != ''">
+		AND    CD_DESC = #{cdDesc}
+		</if>
+		<if test="exceptCds != null and exceptCds != ''">
+		AND    CD NOT IN
+		    <foreach collection="exceptCds" item="item" index="index"  open="(" close=")" separator=",">
+		    #{item}
+		    </foreach>
+		</if>
+		ORDER  BY CD_GB, DISP_ORD
+	</select>
+	
+	<!-- 최상위메뉴 목록 -->
+	<select id="getTopMenuList" parameterType="String" resultType="CommonCode">
+		/* TsaRenderer.getTopMenuList */
+		WITH RECURSIVE CONNECTBY AS (
+		    SELECT 1                                    AS LVL
+		         , MENU_ID
+		         , MENU_NM
+		         , CONCAT(MENU_ID,LPAD(DISP_ORD,4,'0')) AS ORDBY
+		    FROM   TB_MENU
+		    WHERE  USE_YN = 'Y'
+		    <choose>
+		        <when test="pmenuId != null and pmenuId != ''">
+		    AND    PMENU_ID = #{pmenuId}
+		        </when>
+		        <otherwise>
+		    AND    PMENU_ID = 'ROOT'
+		        </otherwise>
+		    </choose>
+		    
+		    UNION ALL
+		    
+		    SELECT A.LVL + 1                              AS LVL
+		         , B.MENU_ID
+		         , B.MENU_NM
+		         , CONCAT(A.ORDBY,LPAD(B.DISP_ORD,4,'0')) AS ORDBY
+		    FROM   CONNECTBY A
+		    INNER JOIN TB_MENU B ON A.MENU_ID = B.PMENU_ID
+		    WHERE  B.USE_YN = 'Y'
+		)
+		SELECT MENU_ID AS CD
+		     , MENU_NM AS CD_NM
+		FROM   CONNECTBY
+		WHERE  LVL = 1
+		ORDER  BY ORDBY
+	</select>
+
+	<!-- 전체메뉴 목록 -->
+	<select id="getAllMenuList" resultType="CommonCode">
+		/* TsaRenderer.getAllMenuList */
+		WITH RECURSIVE CONNECTBY AS (
+		    SELECT 1                                    AS LVL
+		         , MENU_ID
+		         , MENU_NM
+		         , MENU_GB
+		         , CONCAT(MENU_ID,LPAD(DISP_ORD,4,'0')) AS ORDBY
+		    FROM   TB_MENU
+		    WHERE  USE_YN = 'Y'
+		    AND    PMENU_ID = 'ROOT'
+		    
+		    UNION ALL
+		    
+		    SELECT A.LVL + 1                              AS LVL
+		         , B.MENU_ID
+		         , B.MENU_NM
+		         , B.MENU_GB
+		         , CONCAT(A.ORDBY,LPAD(B.DISP_ORD,4,'0')) AS ORDBY
+		    FROM   CONNECTBY A
+		           INNER JOIN TB_MENU B ON A.MENU_ID = B.PMENU_ID
+		    WHERE  B.USE_YN = 'Y'
+		)
+		SELECT MENU_ID                                                 AS CD
+		     , CONCAT(REPEAT('--',(LVL - 1)),'[',MENU_ID,'] ',MENU_NM) AS CD_NM
+		FROM   CONNECTBY
+		WHERE  MENU_GB = 'M'
+		ORDER  BY ORDBY
+	</select>
+
+	<!-- 벤더별 외부몰 목록 -->
+	<select id="getVendorExtmallList" parameterType="String" resultType="CommonCode">
+		/* TsaRenderer.getVendorExtmallList */
+		SELECT DISTINCT
+		       EXTMALL_ID AS CD
+		     , EXTMALL_NM AS CD_NM
+		FROM   TB_EXTMALL
+		WHERE  VENDOR_ID = #{vendorId}
+		AND    USE_YN = 'Y'
+		ORDER  BY EXTMALL_ID
+	</select>
+
+	<!-- 벤더별 외부몰판매매장 목록 -->
+	<select id="getVendorExtmallSellStoreList" parameterType="String" resultType="CommonCode">
+		/* TsaRenderer.getVendorExtmallSellStoreList */
+		SELECT A.SELL_STORE_CD AS CD
+		     , B.SELL_STORE_NM AS CD_NM
+		FROM   TB_EXTMALL A
+		     , TB_SELL_STORE B
+		WHERE  A.SELL_STORE_CD = B.SELL_STORE_CD
+		AND    A.VENDOR_ID = #{vendorId}
+		AND    A.USE_YN = 'Y'
+		AND    B.USE_YN = 'Y'
+		GROUP  BY A.SELL_STORE_CD, B.SELL_STORE_NM
+		ORDER  BY B.SELL_STORE_NM
+	</select>
+
+	<!-- 브랜드 목록 -->
+	<select id="getBrandList" parameterType="Brand" resultType="CommonCode">
+		/* TsaRenderer.getBrandList */
+		SELECT BRAND_CD  AS CD
+		     , BRAND_ENM AS CD_NM
+		FROM   TB_BRAND
+		WHERE  1 = 1
+		<if test='supplyCompCd != null and supplyCompCd != ""'>
+		AND    SUPPLY_COMP_CD = #{supplyCompCd}
+		</if>
+		<if test='brandGrpNm != null and brandGrpNm != ""'>
+		AND    BRAND_GRP_NM = #{brandGrpNm}
+		</if>
+		<if test='useYn != null and useYn != ""'>
+		AND    USE_YN = #{useYn}
+		</if>
+		ORDER  BY SUPPLY_COMP_CD, DISP_ORD
+	</select>
+
+	<!-- 권한별 브랜드 목록 -->
+	<select id="getAuthBrandList" parameterType="String" resultType="CommonCode">
+		/* TsaRenderer.getAuthBrandList */
+		SELECT A.BRAND_CD  AS CD
+		     , A.BRAND_ENM AS CD_NM
+		FROM   TB_BRAND A
+		INNER JOIN TB_BRAND_MD B ON A.BRAND_CD = B.BRAND_CD
+		WHERE  A.USE_YN = 'Y'
+		AND    B.MD_ID = #{userId}
+		GROUP  BY A.BRAND_CD, A.BRAND_ENM
+		ORDER  BY A.BRAND_CD
+	</select>
+
+	<!-- 출고처 목록 -->
+	<select id="getDeliveryLocList" parameterType="DeliveryLoc" resultType="CommonCode">
+		/* TsaRenderer.getDeliveryLocList */
+		SELECT DELV_LOC_CD AS CD
+		     , DELV_LOC_NM AS CD_NM
+		FROM   TB_DELIVERY_LOC
+		WHERE  USE_YN = 'Y'
+		<if test='supplyCompCd != null and supplyCompCd != ""'>
+		AND    SUPPLY_COMP_CD = #{supplyCompCd}
+		</if>
+		<if test='delvLocClsf != null and delvLocClsf != ""'>
+		AND    DELV_LOC_CLSF = #{delvLocClsf}
+		</if>
+		ORDER  BY DELV_LOC_NM
+	</select>
+
+	<!-- 정보고시 목록 -->
+	<select id="getCateInfoList" parameterType="String" resultType="CommonCode">
+		/* TsaRenderer.getCateInfoList */
+		SELECT NI_CLSF_CD AS CD
+		     , B.CD_NM
+		FROM   TB_NOTI_INFO A
+		INNER JOIN TB_GOODS G ON G.SUPPLY_COMP_CD = A.SUPPLY_COMP_CD
+		INNER JOIN TB_COMMON_CODE B ON A.NI_CLSF_CD = B.CD
+		WHERE  G.GOODS_CD = #{goodsCd}
+		AND    B.CD_GB = 'G004'
+		AND    B.USE_YN = 'Y'
+		GROUP  BY NI_CLSF_CD, B.CD_NM
+		ORDER  BY NI_CLSF_CD
+	</select>
+
+	<!-- 품목 목록 -->
+	<select id="getItemkindList" parameterType="Itemkind" resultType="CommonCode">
+		/* TsaRenderer.getItemkindList */
+		SELECT ITEMKIND_CD AS CD
+		     , ITEMKIND_NM AS CD_NM
+		FROM   TB_ITEMKIND
+		WHERE  USE_YN = 'Y'
+		<if test='itemkindCd != null and itemkindCd != ""'>
+		AND    ITEMKIND_CD = #{itemkindCd}
+		</if>
+		ORDER  BY ITEMKIND_CD
+	</select>
+
+	<!-- 브랜드그룹 목록 -->
+	<select id="getBrandGroupList" resultType="CommonCode">
+		/* TsaRenderer.getBrandGroupList */
+		SELECT BRAND_GRP_NM AS CD
+		     , BRAND_GRP_NM AS CD_NM
+		FROM   TB_BRAND
+		WHERE  USE_YN = 'Y'
+		AND    BRAND_GRP_NM IS NOT NULL
+		GROUP  BY BRAND_GRP_NM
+		ORDER  BY BRAND_GRP_NM
+	</select>
+
+	<!-- 컬러 목록 -->
+	<select id="getColorList" parameterType="Color" resultType="CommonCode">
+		/* TsaRenderer.getColorList */
+		SELECT COLOR_CD  AS CD
+		     , COLOR_ENM AS CD_NM
+		     , COLOR_KNM AS CD_DESC
+		     , USE_YN
+		FROM   TB_COLOR
+		WHERE  SUPPLY_COMP_CD = #{supplyCompCd}
+		AND    USE_YN = 'Y'
+		<if test='colorCd != null and colorCd != ""'>
+		AND    COLOR_CD = #{colorCd}
+		</if>
+		ORDER  BY COLOR_CD
+	</select>
+
+	<!-- 사용중 대카테고리 목록 -->
+	<select id="getTCategoryList" parameterType="String" resultType="CommonCode">
+		/* TsaRenderer.getTCategoryList */
+		SELECT TCATE_CD AS CD
+		     , TCATE_NM AS CD_NM
+		FROM   TB_CATEGORY_4SRCH
+		WHERE  1 = 1
+		<if test='cateGb != null and cateGb != ""'>
+		AND    CATE_GB = #{cateGb}
+		</if>
+		GROUP  BY TCATE_CD, TCATE_NM
+		ORDER  BY TCATE_CD
+	</select>
+
+	<!-- 브랜드에 등록된 MD 목록 -->
+	<select id="getBrandMdList" resultType="CommonCode">
+		/* TsaRenderer.getBrandMdList */
+		SELECT DISTINCT
+		       A.MD_ID   AS CD
+		     , C.USER_NM AS CD_NM
+		FROM   TB_BRAND_MD A
+		INNER JOIN TB_BRAND B ON A.BRAND_CD = B.BRAND_CD
+		INNER JOIN TB_USER C ON A.MD_ID = C.USER_ID
+		ORDER  BY C.USER_NM
+	</select>
+
+	<!-- 택배사명 목록 조회 -->
+	<select id="getShipCompanyList" resultType="CommonCode">
+		/* TsaDelivery.getShipCompanyList */
+		SELECT SHIP_COMP_CD  AS CD
+		     , SHIP_COMP_NM  AS CD_NM
+		FROM   TB_SHIP_COMPANY
+		WHERE  1 = 1
+		<if test='useYn != null and useYn != ""'>
+		AND    USE_YN = #{useYn}
+		</if>
+	</select>
+
+	<!-- MD별 브랜드 목록 조회 -->
+	<select id="getMdBrandList" resultType="CommonCode">
+		/* TsaRenderer.getMdBrandList*/
+		SELECT DISTINCT
+		       A.BRAND_CD AS CD
+		     , B.BRAND_ENM AS CD_NM
+		FROM   TB_BRAND_MD A
+		     , TB_BRAND B
+		WHERE  A.BRAND_CD = B.BRAND_CD
+		<if test='mdId != null and mdId != "" and mdId != "all"'>
+		AND    A.MD_ID = #{mdId}
+		</if>
+	</select>
+
+	<!-- MD별 브랜드그룹조회 -->
+	<select id="getMdBrandGrpList" resultType="CommonCode">
+		/* TsaRenderer.getMdBrandGrpList*/
+		SELECT DISTINCT
+		       B.BRAND_GRP_NM AS CD
+		     , B.BRAND_GRP_NM AS CD_NM
+		FROM   TB_BRAND_MD A
+		      ,TB_BRAND B
+		WHERE  A.BRAND_CD = B.BRAND_CD
+		<if test='mdId != null and mdId != "" and mdId != "all"'>
+		AND    A.MD_ID = #{mdId}
+		</if>
+		GROUP  BY BRAND_GRP_NM
+	</select>
+
+	<select id="getInstaAccount" resultType="CommonCode">
+		/* TsaRenderer.getInstaAccount*/
+		SELECT DISTINCT
+		       IT.INSTA_ACCOUNT AS CD
+		     , IT.INSTA_ACCOUNT AS CD_NM
+		FROM   TB_INSTAGRAM_TOKEN IT
+	</select>
+
+	<!-- 판매몰조회 -->
+	<select id="getSellStoreList" resultType="CommonCode">
+		/* TsaRenderer.getSellStoreList*/
+		SELECT DISTINCT
+		       SELL_STORE_CD AS CD
+		     , SELL_STORE_NM AS CD_NM
+		FROM   TB_SELL_STORE
+	</select>
+
+	<!-- 기본답변문구 제목 목록 -->
+	<select id="getBasicAnsTitleList" parameterType="String" resultType="CommonCode">
+		/* TsaRenderer.getBasicAnsTitleList */
+		SELECT ANS_SQ     AS CD
+		     , ANS_TITLE  AS CD_NM
+		FROM   TB_BASIC_ANS
+		WHERE  ANS_CLSF = #{ansClsf}
+		AND    USE_YN = 'Y'
+	</select>
+
+	<!-- 제휴링크 목록 -->
+	<select id="getAflinkList" parameterType="String" resultType="CommonCode">
+		/* TsaRenderer.getAflinkList */
+		SELECT AF_LINK_CD AS CD
+		     , AF_LINK_NM AS CD_NM
+		FROM   TB_AF_LINK
+		WHERE  AF_CHANNEL = #{afChannel}
+		AND    USE_YN = 'Y'
+	</select>
+
+</mapper>

+ 569 - 0
style24.admin/src/main/java/com/style24/persistence/mybatis/shop/TsaSystem.xml

@@ -0,0 +1,569 @@
+<?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.TsaSystemDao">
+
+	<!-- 사용자 목록 -->
+	<select id="getUserList" parameterType="User" resultType="User">
+		/* TsaSystem.getUserList */
+		SELECT A.USER_NO                                                     /*사용자번호*/
+		     , A.USER_ID                                                     /*사용자ID*/
+		     , A.USER_NM                                                     /*사용자명*/
+		     , A.ROLE_CD                                                     /*권한코드*/
+		     , A.ROLE_REF_VAL                                                /*권한관련값*/
+		     , CASE WHEN SUBSTRING(A.ROLE_CD,1,1) IN ('A','B','E') THEN
+		                A.ROLE_REF_VAL
+		            END                                    AS SUPPLY_COMP_CD /*공급업체코드*/
+		     , CASE WHEN SUBSTRING(A.ROLE_CD,1,1) IN ('A','B','E') THEN
+		                (SELECT SUPPLY_COMP_NM
+		                 FROM   TB_SUPPLY_COMPANY
+		                 WHERE  SUPPLY_COMP_CD = A.ROLE_REF_VAL
+		                )
+		       END                                         AS SUPPLY_COMP_NM /*공급업체명*/
+		     , CASE WHEN SUBSTRING(A.ROLE_CD,1,1) = 'C' THEN
+		                A.ROLE_REF_VAL
+		       END                                         AS VENDOR_ID      /*벤더ID*/
+		     , CASE WHEN SUBSTRING(A.ROLE_CD,1,1) = 'C' THEN
+		                FN_GET_CODE_NM('G003',A.ROLE_REF_VAL)
+		       END                                         AS VENDOR_NM      /*벤더명*/
+		     , A.EMAIL                                                       /*이메일*/
+		     , A.CELL_PHNNO                                                  /*휴대전화번호*/
+		     , A.PNT_ASSIGN_AMT                                              /*포인트부여가능금액*/
+		     , A.IP_ADDR                                                     /*IP주소*/
+		     , A.IP_CHK_YN                                                   /*IP체크여부*/
+		     , DATE_FORMAT(A.LOGIN_LDT,'%Y%m%d%H%i%S')     AS LOGIN_LDT      /*최종로그인일시*/
+		     , DATE_FORMAT(A.PASSWD_CHG_DT,'%Y%m%d%H%i%S') AS PASSWD_CHG_DT  /*비밀번호변경일시*/
+		     , A.USE_YN                                                      /*사용여부*/
+		FROM   TB_USER A
+		WHERE  1 = 1
+		<choose>
+		    <when test="searchGb == 'userId'">
+		AND    LOWER(A.USER_ID) LIKE CONCAT('%',LOWER(#{searchTxt}),'%')
+		    </when>
+		    <when test="searchGb == 'userNm'">
+		AND    LOWER(A.USER_NM) LIKE CONCAT('%',LOWER(#{searchTxt}),'%')
+		    </when>
+		</choose>
+		<if test="useYn != null and useYn != ''">
+		AND    A.USE_YN  = #{useYn}
+		</if>
+		<if test="supplyCompCd != null and supplyCompCd != ''">
+		AND    A.ROLE_REF_VAL = #{supplyCompCd}
+		</if>
+		<if test="roleCd != null and roleCd != ''">
+		AND    A.ROLE_CD  = #{roleCd}
+		</if>
+		ORDER  BY A.USER_NM
+	</select>
+
+	<!-- 사용자 조회 -->
+	<select id="getUser" parameterType="Integer" resultType="User">
+		/* TsaSystem.getUser */
+		SELECT USER_NO                                                         /*사용자번호*/
+		     , USER_ID                                                         /*사용자ID*/
+		     , USER_NM                                                         /*사용자명*/
+		     , ROLE_CD                                                         /*권한코드*/
+		     , (SELECT CD_NM
+		        FROM   TB_COMMON_CODE
+		        WHERE  CD_GB = 'G001'
+		        AND    CD = A.ROLE_CD
+		       )                                              AS ROLE_NM       /*권한명*/
+		     , ROLE_REF_VAL                                                    /*권한관련값*/
+		     , EMAIL                                                           /*이메일*/
+		     , CELL_PHNNO                                                      /*휴대전화번호*/
+		     , PNT_ASSIGN_AMT                                                  /*포인트부여가능금액*/
+		     , IP_ADDR                                                         /*IP주소*/
+		     , IP_CHK_YN                                                       /*IP체크여부*/
+		     , DATE_FORMAT(LOGIN_LDT,'%Y-%m-%d %H:%i:%S')     AS LOGIN_LDT     /*최종로그인일시*/
+		     , DATE_FORMAT(PASSWD_CHG_DT,'%Y-%m-%d %H:%i:%S') AS PASSWD_CHG_DT /*비밀번호변경일시*/
+		     , USE_YN                                                          /*사용여부*/
+		FROM   TB_USER A
+		WHERE  USER_NO = #{userNo}
+	</select>
+
+	<!-- 사용자 삭제 -->
+	<update id="deleteUser" parameterType="User">
+		/* TsaSystem.deleteUser */
+		UPDATE TB_USER
+		SET    USE_YN = 'N'
+		     , UPD_NO = #{updNo}
+		     , UPD_DT = NOW()
+		WHERE  USER_NO = #{userNo}
+	</update>
+
+	<!-- 사용자ID 건수 -->
+	<select id="getUserIdCount" parameterType="String" resultType="int">
+		/* TsaSystem.getUserIdCount */
+		SELECT COUNT(*) AS CNT
+		FROM   TB_USER
+		WHERE  USER_ID = #{userId}
+	</select>
+
+	<!-- 사용자 등록/수정 -->
+	<insert id="saveUser" parameterType="User" keyProperty="userNo">
+		/* TsaSystem.saveUser */
+		INSERT INTO TB_USER (
+		       USER_NO
+		     , USER_ID
+		     , USER_NM
+		     , PASSWD
+		     , ROLE_CD
+		     , ROLE_REF_VAL
+		     , EMAIL
+		     , CELL_PHNNO
+		     , PNT_ASSIGN_AMT
+		     , IP_ADDR
+		     , IP_CHK_YN
+		     , USE_YN
+		     , REG_NO
+		     , REG_DT
+		     , UPD_NO
+		     , UPD_DT
+		)
+		VALUES (
+		       #{userNo}
+		     , #{userId}
+		     , #{userNm}
+		     , #{passwd}
+		     , #{roleCd}
+		     , #{roleRefVal}
+		     , #{email}
+		     , #{cellPhnno}
+		     , IFNULL(#{pntAssignAmt},0)
+		     , #{ipAddr}
+		     , #{ipChkYn}
+		     , #{useYn}
+		     , #{regNo}
+		     , NOW()
+		     , #{updNo}
+		     , NOW()
+		)
+		ON DUPLICATE KEY UPDATE
+		       USER_NM = #{userNm}
+		     , ROLE_CD = #{roleCd}
+		     , ROLE_REF_VAL = #{roleRefVal}
+		     , EMAIL = #{email}
+		     , CELL_PHNNO = #{cellPhnno}
+		     , PNT_ASSIGN_AMT = IFNULL(#{pntAssignAmt},0)
+		     , IP_ADDR = #{ipAddr}
+		     , IP_CHK_YN = #{ipChkYn}
+		     , USE_YN = #{useYn}
+		     , UPD_NO = #{updNo}
+		     , UPD_DT = NOW()
+	</insert>
+
+	<!-- 임시비밀번호 조회 -->
+	<select id="getTemporaryPassword" parameterType="int" resultType="String">
+		/* TsaSystem.getTemporaryPassword */
+		SELECT CONVERT(TRUNCATE(A.VAL * CAST(CONCAT(1,LPAD(0,(#{length} - 1),'0')) AS UNSIGNED),0),CHAR) AS PASSWD FROM DUAL
+	</select>
+
+	<!-- 사용자 비밀번호 수정 -->
+	<update id="updateUserPassword" parameterType="User">
+		/* TsaSystem.updateUserPassword */
+		UPDATE TB_USER
+		SET    PASSWD = #{passwd}
+		     , UPD_NO = #{updNo}
+		     , UPD_DT = NOW()
+		WHERE  USER_NO = #{userNo}
+	</update>
+
+	<!-- 사용자 메뉴 목록 -->
+	<select id="getUserMenuList" parameterType="Integer" resultType="UserMenu">
+		/* TsaSystem.getUserMenuList */
+		WITH RECURSIVE CONNECTBY AS (
+		    SELECT 1                                    AS LVL
+		         , PMENU_ID
+		         , MENU_ID
+		         , MENU_NM
+		         , MENU_GB
+		         , CONCAT(MENU_ID,LPAD(DISP_ORD,4,'0')) AS ORDBY
+		    FROM   TB_MENU
+		    WHERE  PMENU_ID = 'ROOT'
+		    AND    USE_YN = 'Y'
+		    
+		    UNION ALL
+		    
+		    SELECT A.LVL + 1                              AS LVL
+		         , B.PMENU_ID
+		         , B.MENU_ID
+		         , B.MENU_NM
+		         , B.MENU_GB
+		         , CONCAT(A.ORDBY,LPAD(B.DISP_ORD,4,'0')) AS ORDBY
+		    FROM   CONNECTBY A
+		    INNER JOIN TB_MENU B ON A.MENU_ID = B.PMENU_ID
+		)
+		SELECT #{userNo}                       AS USER_NO  /*사용자번호*/
+		     , A.PMENU_ID                                  /*상위메뉴ID*/
+		     , A.MENU_ID                                   /*메뉴ID*/
+		     , A.MENU_NM                                   /*메뉴명*/
+		     , A.MENU_GB                                   /*메뉴구분*/
+		     , CASE WHEN B.USER_NO IS NULL OR B.USE_ROLE = 'NNNN' THEN
+		                'N'
+		            ELSE
+		                'Y'
+		       END                             AS USE_YN   /*사용여부*/
+		     , A.LVL                           AS MENU_LVL /*메뉴레벨*/
+		     , IFNULL(B.USE_ROLE,'NNNN')       AS USE_ROLE /*사용권한*/
+		FROM   CONNECTBY A
+		LEFT OUTER JOIN TB_USER_MENU B ON A.MENU_ID = B.MENU_ID
+		                              AND B.USER_NO = #{userNo}
+		ORDER  BY A.ORDBY
+	</select>
+	
+	<!-- 사용자정보변경이력 생성 -->
+	<insert id="createUserInfoChangeHistory" parameterType="UserHst">
+		/* TsaSystem.createUserInfoChangeHistory */
+		INSERT INTO TB_USER_HST (
+		       CHG_HST_SQ
+		     , USER_NO
+		     , CHG_GB
+		     , CHG_CONTENT
+		     , REG_NO
+		     , REG_DT
+		)
+		VALUES (
+		       NULL
+		     , #{userNo}
+		     , #{chgGb}
+		     , CASE WHEN #{chgGb} = 'G060_20' THEN
+		                CONCAT(FN_GET_CODE_NM('G001',#{orgRoleCd}),' -> ',FN_GET_CODE_NM('G001',#{roleCd}))
+		            ELSE
+		                NULL
+		       END
+		     , #{regNo}
+		     , NOW()
+		)
+	</insert>
+
+	<!-- 메뉴 목록 -->
+	<select id="getMenuList" parameterType="Menu" resultType="Menu">
+		/* TsaSystem.getMenuList */
+		WITH RECURSIVE CONNECTBY1 AS (
+		    SELECT 1                                    AS MENU_LVL  /*메뉴레벨*/
+		         , MENU_ID                                           /*메뉴ID*/
+		         , MENU_NM                                           /*메뉴명*/
+		         , MENU_GB                                           /*메뉴구분*/
+		         , MENU_DESC                                         /*메뉴상세명*/
+		         , MENU_URL                                          /*메뉴URL*/
+		         , PMENU_ID                                          /*상위메뉴ID*/
+		         , DISP_ORD                                          /*표시순서*/
+		         , USE_YN                                            /*사용여부*/
+		         , MENU_ID                              AS TREE_PATH /*AG-GRID트리패스(ag-Grid미사용시 필요 없음)*/
+		         , CONCAT(MENU_ID,LPAD(DISP_ORD,4,'0')) AS ORDBY
+		    FROM   TB_MENU A
+		    WHERE  1 = 1
+		    <choose>
+		        <when test="pmenuId != null and pmenuId != ''">
+		    AND    PMENU_ID = #{pmenuId}
+		        </when>
+		        <otherwise>
+		    AND    PMENU_ID = 'ROOT'
+		        </otherwise>
+		    </choose>
+		    
+		    UNION ALL
+		    
+		    SELECT A.MENU_LVL + 1                         AS MENU_LVL  /*메뉴레벨*/
+		         , B.MENU_ID                                           /*메뉴ID*/
+		         , B.MENU_NM                                           /*메뉴명*/
+		         , B.MENU_GB                                           /*메뉴구분*/
+		         , B.MENU_DESC                                         /*메뉴상세명*/
+		         , B.MENU_URL                                          /*메뉴URL*/
+		         , B.PMENU_ID                                          /*상위메뉴ID*/
+		         , B.DISP_ORD                                          /*표시순서*/
+		         , B.USE_YN                                            /*사용여부*/
+		         , CONCAT(A.TREE_PATH,'/',B.MENU_ID)      AS TREE_PATH /*AG-GRID트리패스(ag-Grid미사용시 필요 없음) */
+		         , CONCAT(A.ORDBY,LPAD(B.DISP_ORD,4,'0')) AS ORDBY
+		    FROM   CONNECTBY1 A
+		    INNER JOIN TB_MENU B ON A.MENU_ID = B.PMENU_ID
+		), TAB_MENU_ROLE AS (
+		    SELECT A.MENU_ID
+		         , GROUP_CONCAT(B.ROLE_CD ORDER BY B.ROLE_CD SEPARATOR ',')                        AS ROLE_CDS /*다중메뉴권한코드*/
+		         , GROUP_CONCAT(FN_GET_CODE_NM('G001',B.ROLE_CD) ORDER BY B.ROLE_CD SEPARATOR ',') AS ROLE_NMS /*다중메뉴권한명*/
+		    FROM   TB_MENU A
+		    INNER JOIN TB_MENU_ROLE B ON A.MENU_ID = B.MENU_ID
+		    GROUP  BY A.MENU_ID
+		)
+		SELECT MENU_LVL
+		     , MENU_ID
+		     , MENU_NM
+		     , MENU_GB
+		     , MENU_DESC
+		     , MENU_URL
+		     , PMENU_ID
+		     , DISP_ORD
+		     , USE_YN
+		     , TREE_PATH
+		     , (SELECT ROLE_CDS
+		        FROM   TAB_MENU_ROLE
+		        WHERE  MENU_ID = A.MENU_ID) AS ROLE_CDS  /*다중메뉴권한코드*/
+		     , (SELECT ROLE_NMS
+		        FROM   TAB_MENU_ROLE
+		        WHERE  MENU_ID = A.MENU_ID) AS ROLE_NMS  /*다중메뉴권한명*/
+		FROM   CONNECTBY1 A
+		ORDER  BY ORDBY
+	</select>
+
+	<!-- 메뉴 등록/수정 -->
+	<insert id="saveMenu" parameterType="Menu">
+		/* TsaSystem.saveMenu */
+		INSERT INTO TB_MENU (
+		       MENU_ID
+		     , MENU_NM
+		     , MENU_GB
+		     , MENU_DESC
+		     , MENU_URL
+		     , DISP_ORD
+		     , USE_YN
+		     , PMENU_ID
+		     , REG_NO
+		     , REG_DT
+		     , UPD_NO
+		     , UPD_DT
+		)
+		VALUES (
+		       #{menuId}
+		     , #{menuNm}
+		     , #{menuGb}
+		     , #{menuDesc}
+		     , #{menuUrl}
+		     , #{dispOrd}
+		     , #{useYn}
+		     , IFNULL(NULLIF(#{pmenuId},''),'ROOT')
+		     , #{regNo}
+		     , NOW()
+		     , #{updNo}
+		     , NOW()
+		)
+		ON DUPLICATE KEY UPDATE
+		       MENU_NM = #{menuNm}
+		     , MENU_GB = #{menuGb}
+		     , MENU_DESC = #{menuDesc}
+		     , MENU_URL = #{menuUrl}
+		     , DISP_ORD = #{dispOrd}
+		     , USE_YN = #{useYn}
+		     , PMENU_ID = IFNULL(NULLIF(#{pmenuId},''),'ROOT')
+		     , UPD_NO = #{updNo}
+		     , UPD_DT = NOW()
+	</insert>
+
+	<!-- 메뉴권한 삭제 -->
+	<delete id="deleteMenuRole" parameterType="String">
+		/* TsaSystem.deleteMenuRole */
+		DELETE FROM TB_MENU_ROLE
+		WHERE  MENU_ID = #{menuId}
+	</delete>
+
+	<!-- 메뉴권한 생성 -->
+	<insert id="createMenuRole" parameterType="MenuRole">
+		/* TsaSystem.createMenuRole */
+		INSERT INTO TB_MENU_ROLE (
+		       ROLE_CD
+		     , MENU_ID
+		     , USE_ROLE
+		     , REG_NO
+		     , REG_DT
+		)
+		VALUES (
+		       #{roleCd}
+		     , #{menuId}
+		     , IFNULL(NULLIF(#{useRole},''),'RCUD')
+		     , #{regNo}
+		     , NOW()
+		)
+	</insert>
+
+	<!-- 전체 사용자 메뉴 삭제 -->
+	<delete id="deleteAllUserMenu" parameterType="MenuRole">
+		/* TsaSystem.deleteAllUserMenu */
+		DELETE
+		FROM   TB_USER_MENU
+		WHERE  USER_NO IN (SELECT USER_NO
+		                   FROM   TB_USER
+		                   WHERE  ROLE_CD = #{roleCd}
+		                  )
+		AND    MENU_ID = #{menuId}
+	</delete>
+
+	<!-- 전체 사용자 메뉴 생성-->
+	<insert id="createAllUserMenu" parameterType="MenuRole">
+		/* TsaSystem.createAllUserMenu */
+		INSERT INTO TB_USER_MENU (
+		       USER_NO
+		     , MENU_ID
+		     , USE_ROLE
+		     , REG_NO
+		     , REG_DT
+		     , UPD_NO
+		     , UPD_DT
+		)
+		SELECT B.USER_NO
+		     , A.MENU_ID
+		     , A.USE_ROLE
+		     , #{regNo}
+		     , NOW()
+		     , #{updNo}
+		     , NOW()
+		FROM   TB_MENU_ROLE A
+		INNER JOIN TB_USER B ON A.ROLE_CD = B.ROLE_CD
+		WHERE  A.ROLE_CD = #{roleCd}
+		AND    A.MENU_ID = #{menuId}
+		AND    B.USE_YN = 'Y'
+		ON DUPLICATE KEY UPDATE
+		       USE_ROLE = A.USE_ROLE
+		     , UPD_NO = #{updNo}
+		     , UPD_DT = NOW()
+	</insert>
+
+	<!-- 사용자 전체 메뉴 삭제 -->
+	<delete id="deleteUserAllMenu" parameterType="Integer">
+		/* TsaSystem.deleteUserAllMenu */
+		DELETE
+		FROM   TB_USER_MENU
+		WHERE  USER_NO = #{userNo}
+	</delete>
+
+	<!-- 사용자 전체 메뉴 생성 -->
+	<insert id="createUserAllMenu" parameterType="User">
+		/* TsaSystem.createUserAllMenu */
+		INSERT INTO TB_USER_MENU (
+		       USER_NO
+		     , MENU_ID
+		     , USE_ROLE
+		     , REG_NO
+		     , REG_DT
+		     , UPD_NO
+		     , UPD_DT
+		)
+		SELECT A.USER_NO
+		     , B.MENU_ID
+		     , B.USE_ROLE
+		     , #{regNo}     AS REG_NO
+		     , NOW()        AS REG_DT
+		     , #{updNo}     AS UPD_NO
+		     , NOW()        AS UPD_DT
+		FROM   TB_USER A
+		INNER JOIN TB_MENU_ROLE B ON A.ROLE_CD = B.ROLE_CD
+		WHERE  A.USER_NO = #{userNo}
+		AND    A.ROLE_CD = #{roleCd}
+	</insert>
+
+	<!-- 사용자 메뉴 삭제 -->
+	<delete id="deleteUserMenu" parameterType="UserMenu">
+		/* TsaSystem.deleteUserMenu */
+		DELETE
+		FROM   TB_USER_MENU
+		WHERE  USER_NO = #{userNo}
+		AND    MENU_ID = #{menuId}
+	</delete>
+
+	<!-- 사용자 메뉴 Insert/Update -->
+	<insert id="createUserMenu" parameterType="UserMenu">
+		/* TsaSystem.createUserMenu */
+		INSERT INTO TB_USER_MENU (
+		       USER_NO
+		     , MENU_ID
+		     , USE_ROLE
+		     , REG_NO
+		     , REG_DT
+		     , UPD_NO
+		     , UPD_DT
+		)
+		VALUES (
+		       #{userNo}
+		     , #{menuId}
+		     , #{useRole}
+		     , #{regNo}
+		     , NOW()
+		     , #{updNo}
+		     , NOW()
+		)
+		ON DUPLICATE KEY UPDATE
+		       USE_ROLE = #{useRole}
+		     , UPD_NO = #{updNo}
+		     , UPD_DT = NOW()
+	</insert>
+	
+	<!-- 메뉴접속이력 생성 -->
+	<insert id="createMenuAccessHistory" parameterType="MenuAccessHst">
+		/* TsaSystem.createMenuAccessHistory */
+		INSERT INTO TB_MENU_ACCESS_HST (
+		       MENU_ACCESS_SQ
+		     , USER_NO
+		     , MENU_ID
+		     , REQ_URL
+		     , PARAMS
+		     , REG_NO
+		     , REG_DT
+		)
+		VALUES (
+		       NULL
+		     , #{userNo}
+		     , #{menuId}
+		     , #{reqUrl}
+		     , #{params}
+		     , #{userNo}
+		     , NOW()
+		)
+	</insert>
+
+	<!-- 공통코드 목록 -->
+	<select id="getCommonCodeList" parameterType="CommonCode" resultType="CommonCode">
+		/* TsaSystem.getCommonCodeList */
+		SELECT CD_GB
+		     , CD
+		     , CD_NM
+		     , CD_DESC
+		     , DISP_ORD
+		     , USE_YN
+		     , DISP_ORD
+		FROM   TB_COMMON_CODE
+		WHERE  CD_GB = #{cdGb}
+		ORDER  BY DISP_ORD
+	</select>
+
+	<!-- 공통코드 등록/수정 -->
+	<insert id="saveCommonCode" parameterType="CommonCode">
+		/* TsaSystem.saveCommonCode */
+		INSERT INTO TB_COMMON_CODE (
+		       CD_GB
+		     , CD
+		     , CD_NM
+		     , CD_DESC
+		     , DISP_ORD
+		     , USE_YN
+		     , REG_NO
+		     , REG_DT
+		     , UPD_NO
+		     , UPD_DT
+		)
+		VALUES (
+		       #{cdGb}
+		     , #{cd}
+		     , #{cdNm}
+		     , #{cdDesc}
+		     , #{dispOrd}
+		     , #{useYn}
+		     , #{regNo}
+		     , NOW()
+		     , #{updNo}
+		     , NOW()
+		)
+		ON DUPLICATE KEY UPDATE
+		       CD_NM = #{cdNm}
+		     , CD_DESC = #{cdDesc}
+		     , DISP_ORD = #{dispOrd}
+		     , USE_YN = #{useYn}
+		     , UPD_NO = #{updNo}
+		     , UPD_DT = NOW()
+	</insert>
+
+	<!-- 공통코드 삭제 -->
+	<delete id="deleteCommonCode" parameterType="CommonCode">
+		/* TsaSystem.deleteCode */
+		DELETE FROM TB_COMMON_CODE
+		WHERE  CD_GB = #{cdGb}
+		AND    CD = #{cd}
+	</delete>
+
+</mapper>

+ 8 - 0
style24.admin/src/main/resources/banner.txt

@@ -0,0 +1,8 @@
+   _____ _________     ___      ______ ___  _  _                 _           _       
+  / ____|__   __\ \   / / |    |  ____|__ \| || |       /\      | |         (_)      
+ | (___    | |   \ \_/ /| |    | |__     ) | || |_     /  \   __| |_ __ ___  _ _ __  
+  \___ \   | |    \   / | |    |  __|   / /|__   _|   / /\ \ / _` | '_ ` _ \| | '_ \ 
+  ____) |  | |     | |  | |____| |____ / /_   | |    / ____ \ (_| | | | | | | | | | |
+ |_____/   |_|     |_|  |______|______|____|  |_|   /_/    \_\__,_|_| |_| |_|_|_| |_|
+                                                                                     
+:: (v1.0.0.RELEASE by tsinfotech.co.kr 2020) ::

+ 82 - 0
style24.admin/src/main/resources/config/application-dev.yml

@@ -0,0 +1,82 @@
+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:
+    admin: //dev.admin.wivismall.com
+    wivis: //dev.wivismall.com
+    #image: //dev.image.wivismall.com
+    image: //116.121.60.104:90
+    uximage: //dev.wivismall.com
+
+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
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png
+        view: //image.wivismall.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
+        max.size: 10
+        allow.extension: xls|xlsx
+        view: //116.121.60.104:90/excel
+    sample:
+        target.path: /home/app/www/data
+        max.size: 10
+        allow.extension: txt|doc|docx|ppt|pptx|xls|xlsx|hwp|pdf
+        view: //116.121.60.104:90/sample
+        
+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://dev.wivismall.com
+    image.url: http://dev.wivismall.com/image/mailing
+    template.path: /home/app/www/admin/WEB-INF/mail
+
+# 사방넷 API
+sabangnet:
+    xml:
+        path: /home/app/www/data/sabangnet
+        url: http://dev.image.wivismall.com
+
+# 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
+

+ 66 - 0
style24.admin/src/main/resources/config/application-locd.yml

@@ -0,0 +1,66 @@
+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
+    
+domain:
+    admin: //ldadmin.style24.com
+    front: //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
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png
+        view: //image.style24.com/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
+        max.size: 10
+        allow.extension: xls|xlsx
+        view: //ldimage.style24.com/data/excel
+    sample:
+        target.path: /WIDE/workspace/files/data
+        max.size: 10
+        allow.extension: txt|doc|docx|ppt|pptx|xls|xlsx|hwp|pdf
+        view: //ldimage.style24.com/data/sample
+
+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/style24/STYLE24/style24.admin/src/main/webapp/WEB-INF/mail

+ 82 - 0
style24.admin/src/main/resources/config/application-locp.yml

@@ -0,0 +1,82 @@
+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
+    
+domain:
+    admin: //lpadmin.wivismall.com
+    wivis: //lpfront.wivismall.com
+    image: //image.wivismall.com/speedy_image-wivismall
+    uximage: //lpfront.wivismall.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.wivismall.com/speedy_image-wivismall
+    goods:
+        target.path: /WIDE/workspace/files/data
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png
+        view: //image.wivismall.com/speedy_image-wivismall/goods
+    image:
+        target.path: /WIDE/workspace/files/data
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png
+        view: //image.wivismall.com/speedy_image-wivismall
+    excel:
+        target.path: /WIDE/workspace/files/data
+        max.size: 10
+        allow.extension: xls|xlsx
+        view: //locp.image.wivismall.com/data/excel
+    sample:
+        target.path: /WIDE/workspace/files/data
+        max.size: 10
+        allow.extension: txt|doc|docx|ppt|pptx|xls|xlsx|hwp|pdf
+        view: //locp.image.wivismall.com/data/sample
+
+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: //lpfront.wivismall.com
+    image.url: //lpfront.wivismall.com/image/mailing
+    template.path: /WIDE/workspace/webapps/wivis/wivismall.admin/src/main/webapp/WEB-INF/mail
+# 사방넷 API
+sabangnet:
+    xml:
+        path: /WIDE/workspace/webapps/wivis/wivismall.admin/src/main/webapp/sabangnet
+        url: http://112.172.147.34:88
+
+# 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
+

+ 82 - 0
style24.admin/src/main/resources/config/application-run.yml

@@ -0,0 +1,82 @@
+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:
+    admin: //admin.wivismall.com
+    wivis: //www.wivismall.com
+    image: //image.wivismall.com/speedy_image-wivismall
+    uximage: //www.wivismall.com
+
+upload:
+    default:
+        target.path: /app/was/deploy/admin/data
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png|bmp|txt|doc|docx|ppt|pptx|xls|xlsx|hwp|pdf
+        view: //image.wivismall.com/speedy_image-wivismall
+    goods:
+        target.path: /app/was/deploy/admin/data
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png
+        view: //image.wivismall.com/speedy_image-wivismall/goods
+    image:
+        target.path: /app/was/deploy/admin/data
+        max.size: 10
+        allow.extension: jpg|gif|jpeg|png
+        view: //image.wivismall.com/speedy_image-wivismall
+    excel:
+        target.path: /app/was/deploy/admin/data
+        max.size: 10
+        allow.extension: xls|xlsx
+        view: //admin.wivismall.com/data/excel
+    sample:
+        target.path: /app/was/deploy/admin/data
+        max.size: 10
+        allow.extension: txt|doc|docx|ppt|pptx|xls|xlsx|hwp|pdf
+        view: //admin.wivismall.com/data/sample
+
+download.path: /app/was/deploy/admin/data
+
+# 사방넷 API
+sabangnet:
+    xml:
+        path: /app/was/deploy/api/data/sabangnet
+        url: http://api.wivismall.com/data/sabangnet
+
+# 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.wivismall.com
+    image.url: http://www.wivismall.com/image/mailing
+    template.path: /app/was/deploy/admin/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
+

+ 64 - 0
style24.admin/src/main/resources/config/application.yml

@@ -0,0 +1,64 @@
+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: 30MB
+            max-request-size: 30MB
+
+server.error.whitelabel.enabled: false
+
+# 사방넷 API
+sabangnet:
+    wivis:
+        admin.id: hohomomo00
+        auth.key: TMYP5CEPHyuEx7K4xFPTAXPZd65NT88MyV
+    flyingtiger:
+        admin.id: js5353
+        auth.key: 36XBSHE2KW4MPBr3ZuT5FRF34MK9NK4uP
+
+# Instagram
+instagram:
+    api.url: https://api.instagram.com/v1
+    api.method: https://graph.instagram.com/[userId]/media?fields=id,media_type,media_url,permalink,thumbnail_url,username,caption,timestamp
+    token.url: https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token
+
+# 네이버 API
+naver:
+    clientId: OMmbCMu7ac7GgYWgjlhv
+    clientSecret: jwRNdDbEBG
+    shortUrl: https://openapi.naver.com/v1/util/shorturl
+
+# 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=
+  
+# CJ대한통운
+cj:
+    logistic:
+        custid: 30321070
+    call:
+        tel1: 02
+        tel2: 1544
+        tel3: 4097

+ 78 - 0
style24.admin/src/main/resources/i18n/messages/message_ko_KR.properties

@@ -0,0 +1,78 @@
+## -----------------------------------------------------------------------------
+## 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.
+FAIL_1003=\uC5D1\uC140\uB2E4\uC6B4\uB85C\uB4DC\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.
+
+##\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.
+
+#\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)
+
+#\uBC30\uC1A1
+DELIVERY_0001=\uB4F1\uB85D \uC2E4\uD328\uD558\uC600\uC2B5\uB2C8\uB2E4. \uC774\uBBF8 \uB4F1\uB85D\uB41C \uCD9C\uACE0\uAE08\uC9C0 \uC0C1\uD488\uC774 \uC788\uC2B5\uB2C8\uB2E4.

+ 25 - 0
style24.admin/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="/home/app/logs/admin"/>
+	<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.admin/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/admin"/>
+	<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>

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

@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<configuration scan="true">
+	<property name="LOG_HOME" value="/WIDE/workspace/logs/wivismall/admin"/>
+	<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>
+
+	<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${LOG_HOME}/wivismall_admin.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_admin.%d{yyyy-MM-dd}.log.zip</fileNamePattern>
+			<!-- keep 60 days' worth of history -->
+			<maxHistory>60</maxHistory>
+		</rollingPolicy>
+	</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"/>
+		<appender-ref ref="FILE"/>
+	</root>
+
+</configuration>

+ 39 - 0
style24.admin/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/admin"/>
+	<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_admin.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_admin.%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.admin/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.admin/src/main/webapp/WEB-INF/lib/gagaframework-web-core-1.7-RELEASE.jar


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


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


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


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

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

+ 25 - 0
style24.admin/src/main/webapp/WEB-INF/views/common/fragments/footer.html

@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : footer.html
+ * @desc    : Footer
+ *============================================================================
+ * STYLE24
+ * Copyright(C) 2020 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.10.06   gagamel     최초 작성
+ *******************************************************************************
+ -->
+<body>
+
+<footer th:fragment="footer">
+	<span class="f-left"><strong>Copyright</strong> STYLE24 © 2020</span>
+	<span class="f-right">style24.com - <strong> Admin</strong></span>
+</footer>
+	
+</body>
+</html>

+ 134 - 0
style24.admin/src/main/webapp/WEB-INF/views/common/fragments/gnb.html

@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : gnb.html
+ * @desc    : Top Menu
+ *============================================================================
+ * STYLE24
+ * Copyright(C) 2020 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.10.06   gagamel     최초 작성
+ *******************************************************************************
+ -->
+<div th:fragment="gnb">
+	<!-- HEADER AREA -->
+	<header>
+		<!-- 로고, 전체화면 버튼 -->
+		<div class="header-logo">
+			<a href="/">
+				<img src="/image/gnb_logo.png" alt="로고"/>
+			</a>
+			<button type="button" class="lnbClose">
+				<i class="fa fa-bars"></i>
+			</button>
+		</div>
+		<!-- //로고, 전체화면 버튼 -->
+		
+		<!-- 상단 메뉴 -->
+		<div class="header-menu">
+			<span class="menu" th:each="item, index : ${loginMenuList}" th:if="${item.menuLvl == 1}">
+				<a href="#" th:href="@{javascript:void(0);}" th:attr="data-menu-id=@{${item.menuId}}" th:text="${item.menuNm}">메뉴명</a>
+			</span>
+		</div>
+		<!-- //상단 메뉴 -->
+		
+		<!-- 사용자,권한,로그아웃-->
+		<div class="header-info">
+			<i class="heart">&#10084;</i>
+			<a><strong th:text="${sessionInfo.userNm}">홍길동</strong></a>
+			<i class="dot">&bull;</i>
+			<a th:text="${sessionInfo.roleNm}">Director</a>
+			<i class="dot">&bull;</i>
+			<a th:onclick="|cfnPasswordChange();|">비밀번호 변경</a>
+			<i class="dot">&bull;</i>
+			<a href="#" th:href="@{/logout}">Log out</a>
+		</div>
+		<!-- //사용자,권한,로그아웃-->
+		
+		<!-- 반응형 : 사용자,권한,로그아웃-->
+		<div class="header-info-sm">
+			<a class="tooltip">
+				<i class="fa fa-user"></i>
+				<span class="tooltiptext" th:text="${sessionInfo.userNm + '(' + sessionInfo.roleNm + ')'}">홍길동 (Director)</span>
+			</a>
+			<a href="#" th:href="@{/logout}" class="tooltip">
+				<i class="fa fa-chain-broken"></i>
+				<span class="tooltiptext logout">로그아웃</span>
+			</a>
+		</div>
+		<!-- //반응형 : 사용자,권한,로그아웃-->
+	</header>
+	<!-- //HEADER AREA -->
+	
+<script th:inline="javascript">
+/*<![CDATA[*/
+	var loginMenuList = [[${loginMenuList}]];
+	
+	// Header 메뉴 클릭
+	$('.header-menu > span > a').on('click', function() {
+		// 햄버거 버튼 보이기
+		$('.header-logo').find('.lnbClose').show();
+		
+		var a = $(this);
+		
+		// active/inactive
+		var parentTag = $(a).parent().parent();
+		$.each($(parentTag).find('a'), function(idx) {
+			$(this).removeClass('on');
+		});
+		$(a).addClass('on');
+		
+		var gnbMenuId = $(a).data('menuId');
+		
+		var lnbTag = '<li>\n';
+		
+		$.each(loginMenuList, function(idx1, item1) {
+			if (gnbMenuId == item1.pmenuId) {
+				if (item1.leafLvl == 0) {
+					//lnbTag += '	<a class="dep2" attr="data-url=' + item1.menuUrl + '">\n';
+					lnbTag += '	<a class="dep2" href="javascript:void(0);" onclick="fnClickLnb(\'' + item1.menuUrl + '\', \'' + item1.navigation + '\', this);">\n';
+				} else {
+					lnbTag += '	<a class="dep2">\n';
+				}
+				
+				/* lnbTag += '		<i class="fa fa-shopping-cart"></i>\n';
+				lnbTag += '		<span class="nav-label"> ' + item1.menuNm + '</span>\n';
+				lnbTag += '		<i class="mnArrow fa fa-angle-down"></i>\n'; */
+				lnbTag += item1.menuNm + '\n';
+				lnbTag += '	</a>\n';
+				
+				if (item1.leafLvl > 0) {
+					lnbTag += '	<ul class="dep3">\n';
+					
+					$.each(loginMenuList, function(idx2, item2) {
+						if (item1.menuId == item2.pmenuId) {
+							lnbTag += '		<li>\n';
+							//lnbTag += '			<a href="javascript:void(0);" attr="data-url=' + item2.menuUrl + '"><span>' + item2.menuNm + '</span></a>\n';
+							lnbTag += '			<a href="javascript:void(0);" onclick="fnClickLnb(\'' + item2.menuUrl + '\', \'' + item2.navigation + '\', this);"><span>' + item2.menuNm + '</span></a>\n';
+							lnbTag += '		</li>\n';
+						}
+					});
+					
+					lnbTag += '	</ul>\n';
+				}
+			}
+		});
+		
+		lnbTag += '</li>\n';
+		
+		$('#lnb').html(lnbTag);
+	});
+	
+	$(document.body).ready(function() {
+		
+	});
+/*]]>*/
+</script>
+
+</div>
+
+</html>

+ 60 - 0
style24.admin/src/main/webapp/WEB-INF/views/common/fragments/header.html

@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : header.html
+ * @desc    : Header
+ *============================================================================
+ * STYLE24
+ * Copyright(C) 2020 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.10.06   gagamel     최초 작성
+ *******************************************************************************
+ -->
+<head th:fragment="header">
+	<!-- <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"/>
+	<meta http-equiv="Pragma" content="no-cache"/>
+	<meta http-equiv="Expires" content="-1"/> -->
+	<meta charset="utf-8"/>
+	<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
+	<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+	<title>:: STYLE24 Admin ::</title>
+	<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"/>
+	<link rel="shortcut icon" type="image/x-icon" href="/image/favicon.ico"/>
+
+	<!-- Include Common CSS -->
+	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"/>
+	<link rel="stylesheet" href="/ux/plugins/agGrid/ag-grid.css"/>
+	<link rel="stylesheet" href="/ux/plugins/agGrid/ag-theme-balham.css"/>
+	<link rel="stylesheet" th:href="@{'/ux/plugins/mcxdialog/mcxdialog_ui.css?v=' + ${#calendars.format(#calendars.createNow(), 'yyyyMMddHHmmss')}}" href="/ux/plugins/mcxdialog/mcxdialog_ui.css"/>
+	<link rel="stylesheet" href="/ux/plugins/dropzone/dropzone.css"/>
+	<link rel="stylesheet" href="/ux/plugins/c3/c3.css"/>
+
+	<!-- Custom Common CSS -->
+	<link rel="stylesheet" th:href="@{'/ux/css/admin.ui.css?v=' + ${#calendars.format(#calendars.createNow(), 'yyyyMMddHHmmss')}}" href="/ux/css/admin.ui.css"/>
+	<link rel="stylesheet" href="/ux/plugins/gaga/gaga.agGrid.css"/>
+
+	<!-- Include JS library -->
+	<script type="text/javascript" src="/ux/plugins/jquery/jquery-1.12.4.min.js"></script>
+	<script type="text/javascript" src="/ux/plugins/jquery/jquery-ui.min.js"></script>
+	<script type="text/javascript" src="/ux/plugins/jquery/jquery.serializeObject.min.js"></script>
+	<script type="text/javascript" src="/ux/plugins/mcxdialog/mcxdialog_ui.js"></script>
+	<script type="text/javascript" src="/ux/plugins/agGrid/ag-grid-enterprise.min.noStyle.js"></script>
+	<script type="text/javascript" src="/ux/plugins/dropzone/dropzone.js"></script>
+	<script type="text/javascript" src="/ux/plugins/c3/d3.v5.js"></script>
+	<script type="text/javascript" src="/ux/plugins/c3/c3.js"></script>
+	
+	<!-- Custom Common JS library -->
+	<script type="text/javascript" th:src="@{'/ux/js/admin.ui.js?v=' + ${#calendars.format(#calendars.createNow(), 'yyyyMMddHHmmss')}}" src="/ux/js/admin.ui.js"></script>
+	<script type="text/javascript" th:src="@{'/ux/js/admin.common.js?v=' + ${#calendars.format(#calendars.createNow(), 'yyyyMMddHHmmss')}}" src="/ux/js/admin.common.js"></script>
+	<script type="text/javascript" th:src="@{'/ux/js/admin.popup.js?v=' + ${#calendars.format(#calendars.createNow(), 'yyyyMMddHHmmss')}}" src="/ux/js/admin.popup.js"></script>
+	<script type="text/javascript" th:src="@{'/ux/plugins/gaga/gaga.common.js?v=' + ${#calendars.format(#calendars.createNow(), 'yyyyMMddHHmmss')}}" src="/ux/plugins/gaga/gaga.common.js"></script>
+	<script type="text/javascript" th:src="@{'/ux/plugins/gaga/gaga.validation.js?v=' + ${#calendars.format(#calendars.createNow(), 'yyyyMMddHHmmss')}}" src="/ux/plugins/gaga/gaga.validation.js"></script>
+	<script type="text/javascript" th:src="@{'/ux/plugins/gaga/gaga.agGrid.js?v=' + ${#calendars.format(#calendars.createNow(), 'yyyyMMddHHmmss')}}" src="/ux/plugins/gaga/gaga.agGrid.js"></script>
+	<script type="text/javascript" th:src="@{'/ux/plugins/gaga/gaga.alert.js?v=' + ${#calendars.format(#calendars.createNow(), 'yyyyMMddHHmmss')}}" src="/ux/plugins/gaga/gaga.alert.js"></script>
+</head>
+
+</html>

+ 95 - 0
style24.admin/src/main/webapp/WEB-INF/views/common/fragments/lnb.html

@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : lnb.html
+ * @desc    : Left Menu
+ *============================================================================
+ * STYLE24
+ * Copyright(C) 2020 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.10.06   gagamel     최초 작성
+ *******************************************************************************
+ -->
+<body>
+
+<div th:fragment="lnb" id="lnb-wrapper">
+	<ul id="lnb" class="lnb">
+
+	</ul>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	// LNB 메뉴 클릭 시
+	var fnClickLnb = function(actionUrl, navigation, obj) {
+		// data-url 속성이 없으면 skip
+		if (gagajf.isNull(actionUrl)) {
+			return;
+		}
+
+// 		$.each($('#lnb').find('li > a'), function(idx) {
+// 			$(this).removeClass('on');
+// 		});
+
+// 		$(obj).addClass('on');
+
+		$('#content').load(actionUrl, function(){
+			// Content Main Title
+			var arrNavigation = navigation.split(' > ');
+			var contentTitle = '';
+			contentTitle += '<h1>' + (arrNavigation.length > 2 ? arrNavigation[2] : (arrNavigation.length > 1 ? arrNavigation[1] : arrNavigation[0])) + '</h1>\n';
+			contentTitle += '<ol>\n';
+			contentTitle += '	<li><i class="fa fa-home"></i> <a>' + arrNavigation[0] + '</a></li>\n';
+			if (arrNavigation.length > 1) {
+				contentTitle += '	<li><a>' + arrNavigation[1] + '</a></li>\n';
+			}
+			if (arrNavigation.length > 2) {
+				contentTitle += '	<li><a>' + arrNavigation[2] + '</a></li>\n';
+			}
+			contentTitle += '</ol>\n';
+			$('.main-title').html(contentTitle);
+			
+			$('.schDate').datepicker({
+				changeMonth: true,
+				changeYear: true,
+				defaultDate: $('.schDate').val()
+			});
+
+			var currentYear = (new Date()).getFullYear();
+			var startYear = currentYear - 10;
+			var finalYear = currentYear + 10;
+			var options = {
+				startYear: startYear,
+				finalYear: finalYear,
+				pattern: 'yyyy-mm',
+				monthNames: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월']
+			};
+			$('.schMonth').monthpicker(options);
+
+			//조회폼에서 엔터키 입력시 자동조회
+			if ($('#btnSearch').length == 1) {
+				$("#main").find("form:first").on('keyup', function(e) {
+					if (e.keyCode == 13) {
+						if ($('#btnSearch').length == 1) {
+							$('#btnSearch').click();
+						}
+					}
+				})
+			}
+		});
+		$(window).scrollTop(0);
+	}
+
+	$(document.body).ready(function() {
+
+	});
+/*]]>*/
+</script>
+
+</div>
+
+</body>
+</html>

+ 34 - 0
style24.admin/src/main/webapp/WEB-INF/views/common/fragments/variables.html

@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : variables.html
+ * @desc    : Global variables 설정
+ *            prefix를 _(underbar)로 시작한다.
+ *============================================================================
+ * STYLE24
+ * Copyright(C) 2020 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.10.06   gagamel     최초 작성
+ *******************************************************************************
+ -->
+<th:block th:fragment="variables">
+<script th:inline="javascript">
+/*<![CDATA[*/
+var _adminUrl = [[${@environment.getProperty('domain.admin')}]];
+var _frontUrl = [[${@environment.getProperty('domain.front')}]];
+var _uximgUrl = [[${@environment.getProperty('domain.uximage')}]];
+var _imgUrl = [[${@environment.getProperty('domain.image')}]];
+var _sampleUrl = [[${@environment.getProperty('upload.sample.view')}]];
+var _uploadDefaultUrl = [[${@environment.getProperty('upload.default.view')}]];
+
+var _today = [[${#calendars.format(#calendars.createNow(), 'yyyy-MM-dd')}]];
+var _thisYear = [[${#calendars.format(#calendars.createNow(), 'yyyy')}]];
+/*]]>*/
+</script>
+</th:block>
+
+</html>

+ 46 - 0
style24.admin/src/main/webapp/WEB-INF/views/common/layout/dashboard.html

@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org"
+	xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
+
+<head th:replace="~{common/fragments/header :: header}"></head>
+
+<body>
+
+<th:block th:replace="~{common/fragments/variables :: variables}"></th:block>
+
+<div id="wrapper" class="dashboard">
+	<!-- GNB -->
+	<div th:replace="~{common/fragments/gnb :: gnb}"></div>
+	<!--// GNB -->
+	
+	<!-- CONTENT AREA -->
+	<div id="container" class="gray-bg">
+		<!-- LNB -->
+		<div th:replace="~{common/fragments/lnb :: lnb}"></div>
+		<!--// LNB -->
+		
+		<!-- MAIN AREA -->
+		<div id="main-wrapper">
+			<div id="content" layout:fragment="content"></div>
+			
+			<!-- Footer -->
+			<footer th:replace="~{common/fragments/footer :: footer}"></footer>
+			<!--// Footer -->
+		</div>
+		<!-- // MAIN AREA -->
+	</div>
+	<!-- // CONTENT AREA -->
+	
+</div>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	$(document).ready(function() {
+		
+	});
+/*]]>*/
+</script>
+
+</body>
+</html>

+ 46 - 0
style24.admin/src/main/webapp/WEB-INF/views/common/layout/default.html

@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org"
+	xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
+
+<head th:replace="~{common/fragments/header :: header}"></head>
+
+<body>
+
+<th:block th:replace="~{common/fragments/variables :: variables}"></th:block>
+
+<div id="wrapper" class="color-pastel">
+	<!-- GNB -->
+	<div th:replace="~{common/fragments/gnb :: gnb}"></div>
+	<!--// GNB -->
+	
+	<!-- CONTENT AREA -->
+	<div id="container" class="gray-bg">
+		<!-- LNB -->
+		<div th:replace="~{common/fragments/lnb :: lnb}"></div>
+		<!--// LNB -->
+		
+		<!-- MAIN AREA -->
+		<div id="main-wrapper">
+			<div id="content" layout:fragment="content"></div>
+			
+			<!-- Footer -->
+			<footer th:replace="~{common/fragments/footer :: footer}"></footer>
+			<!--// Footer -->
+		</div>
+		<!-- // MAIN AREA -->
+	</div>
+	<!-- // CONTENT AREA -->
+	
+</div>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	$(document).ready(function() {
+		
+	});
+/*]]>*/
+</script>
+
+</body>
+</html>

+ 23 - 0
style24.admin/src/main/webapp/WEB-INF/views/common/layout/error.html

@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org"
+	xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
+
+<head th:replace="~{common/fragments/header :: header}"></head>
+
+<body class="loginBg">
+
+<div id="errPage">
+	<div layout:fragment="content"></div>
+</div>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	$(document).ready(function() {
+		
+	});
+/*]]>*/
+</script>
+
+</body>
+</html>

+ 23 - 0
style24.admin/src/main/webapp/WEB-INF/views/common/layout/login.html

@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org"
+	xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
+
+<head th:replace="~{common/fragments/header :: header}"></head>
+
+<body class="loginBg">
+
+<div class="loginWrap login_black">
+	<th:block layout:fragment="content"></th:block>
+</div>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	$(document).ready(function() {
+		
+	});
+/*]]>*/
+</script>
+
+</body>
+</html>

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 65 - 0
style24.admin/src/main/webapp/WEB-INF/views/dashboard.html


+ 53 - 0
style24.admin/src/main/webapp/WEB-INF/views/error/500.html

@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org"
+	xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+	layout:decorator="common/layout/error">
+<!--
+ *******************************************************************************
+ * @source  : 500.html
+ * @desc    : 500 Error Page
+ *============================================================================
+ * STYLE24
+ * Copyright(C) 2020 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.10.06   gagamel     최초 작성
+ *******************************************************************************
+ -->
+<head>
+	<title>500 Error</title>
+</head>
+
+<body>
+
+<div layout:fragment="content">
+	<div class="errImg">
+		<div>
+			<i class="fa fa-television" aria-hidden="true"></i>
+			<i class="fa fa-info" aria-hidden="true"></i>
+		</div>
+	</div>
+	<ul class="errTxt">
+		<li class="ttl" th:text="${message}">Internal Server Error</li>
+		<li class="cont">
+			서버가 요청을 완료할 수 없는 예기치 않은 오류가 발생했습니다.
+		</li>
+		<li class="tel">
+			문의 : 스타일24(주) 고객센터 (1544-5336)
+		</li>
+	</ul>
+	<div class="errBtn">
+		<a href="#" th:href="@{/}" class="btn">Home</a>
+	</div>
+
+<script type="text/javascript">
+/*<![CDATA[*/
+/*]]>*/
+</script>
+
+</div>
+
+</body>
+</html>

+ 107 - 0
style24.admin/src/main/webapp/WEB-INF/views/signin.html

@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org"
+	xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+	layout:decorator="common/layout/login">
+<!--
+ *******************************************************************************
+ * @source  : signin.html
+ * @desc    : Signin Page
+ *============================================================================
+ * STYLE24
+ * Copyright(C) 2020 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.10.05   gagamel     최초 작성
+ *******************************************************************************
+ -->
+<head>
+	<title>Signin</title>
+</head>
+
+<body>
+
+<th:block layout:fragment="content">
+	<form name="loginForm" id="loginForm" th:action="@{/login}" method="post">
+		<div class="loginCont">
+			<div class="logo"><img src="/image/login_logo.png" alt="logo"/></div>
+			<ul>
+				<li>
+					<ul class="loginBox">
+						<li th:style="${error == null ? 'display: none;' : ''}">
+							<div class="alertBox alert-danger" id="loginFailAlert">
+								<strong th:text="${error}">사용자ID 또는 비밀번호가 일치하지 않습니다.</strong>
+								<button type="button" class="alertClose" onclick="uifnAlertClose('loginFailAlert');">닫기</button>
+							</div>
+						</li>
+						<li>
+							<input type="text" autofocus="autofocus" name="userId" placeholder="아이디" maxlength="20" required="required" data-valid-name="아이디"/>
+						</li>
+						<li>
+							<input type="password" name="passwd" placeholder="비밀번호" maxlength="20" required="required" data-valid-name="비밀번호"/>
+						</li>
+						<li>
+							<label class="chkBox"><input type="checkbox" id="saveId"/>아이디 저장</label>
+							<!-- <input type="submit" class="btn btn-black btn-lg" value="Login"/> -->
+							<button type="submit" class="btn btn-black btn-lg">Login</button>
+						</li>
+						<!--li>
+							<a href="#" th:href="@{javascript:void(0);}" id="findId" class="forget-password">아이디 찾기</a>
+							<i class="dot">&bull;</i>
+							<a href="#" th:href="@{javascript:void(0);}" id="findPassword">비밀번호 찾기</a>
+							<i class="dot">&bull;</i>
+							<a>회원가입</a>
+						</li-->
+					</ul>
+				</li>
+				<li>
+					<div class="loginInfo">
+						<p><em><i class="fa fa-info-circle" aria-hidden="true"></i>안내문</em></p>
+						<p>이 시스템은 <b>STYLE24 Admin</b> 전용 시스템입니다.<br/>
+						인증되지 않은 사용자의 다른 접근은 허용하지 않습니다.<br/>
+						본 시스템으로 접근을 시도한 것은 기록에 남게 되며, 반복되는 접근은 불법적인 침입을 하려는 시도로 간주하겠습니다.<br/><br/>
+						본 시스템의 사용과 관련된 문의는<br/>
+						<strong>스타일24(주) 고객센터 (1544-5336)</strong>으로 연락 주시기 바랍니다.</p>
+					<div>
+				</li>
+			</ul>
+		</div>
+	</form>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	//주석 추가
+	var ckAdminId = "CK_ADMIN_ID";
+	var ckDefaultExpireDays = 90;
+
+	// Save ID
+	$('#saveId').on('click', function() {
+		if ($('#saveId').is(":checked")) {
+			if (!gagajf.isNull($('#loginForm input[name=userId]').val())) {
+				gagajf.setCookie(ckAdminId, $('#loginForm input[name=userId]').val(), ckDefaultExpireDays);
+			}
+		} else {
+			if (!gagajf.isNull(getCookie(ckAdminId)) && (getCookie(ckAdminId) === $('#loginForm input[name=userId]').val())) {
+				gagajf.setCookie(ckAdminId, $('#loginForm input[name=userId]').val(), -1);
+			}
+		}
+	});
+
+	$(document).ready(function() {
+		$('#loginForm input[name=userId]').val(gagajf.getCookie(ckAdminId));
+		if (gagajf.isNull($('#loginForm input[name=userId]').val())) {
+			$('#loginForm input[name=userId]').focus();
+			$('#saveId').prop('checked', false);
+		} else {
+			$('#loginForm input[name=passwd]').focus();
+			$('#saveId').prop('checked', true);
+		}
+	});
+/*]]>*/
+</script>
+
+</th:block>
+
+</body>
+</html>

+ 266 - 0
style24.admin/src/main/webapp/WEB-INF/views/system/BasicAnswerForm.html

@@ -0,0 +1,266 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : BasicAnswerForm.html
+ * @desc    : 기본답변문구관리 Page
+ *============================================================================
+ * Wivismall
+ * Copyright(C) 2019 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2019.12.13   jaewonHo    최초 작성
+ *******************************************************************************
+ -->
+	<div id="main">
+		<div class="main-title">
+		</div>
+		<!-- 검색조건 영역 -->
+		<div class="panelStyle2">
+			<form id="searchForm" name="searchForm" action="#" th:action="@{'/system/basic/answer/list'}" onsubmit="$('#btnSearch').trigger('click'); return false;">
+				<table class="frmStyle">
+					<colgroup>
+						<col style="width:10%;"/>
+						<col style="width:20%;"/>
+						<col style="width:10%;"/>
+						<col style="width:20%;"/>
+						<col style="width:10%;"/>
+						<col style="width:20%;"/>
+						<col/>
+					</colgroup>
+					<tr>
+						<th>사이트</th>
+						<td>
+							<select name="siteCd">
+								<option value="">[전체]</option>
+								<option th:if="${siteList}" th:each="oneData, status : ${siteList}"th:value="${oneData.cd}" th:text="${'[' + oneData.cd + '] ' + oneData.cdNm}"></option>
+							</select>
+						</td>
+						<th>답변종류</th>
+						<td>
+							<select name="ansClsf">
+								<option value="">[전체]</option>
+								<option th:if="${ansClsfList}" th:each="oneData, status : ${ansClsfList}" th:value="${oneData.cd}" th:text="${'[' + oneData.cd + '] ' + oneData.cdNm}"></option>
+							</select>
+						</td>
+						<th>답변제목</th>
+						<td>
+							<input type="text" name="ansTitle" class="w200"/>
+						</td>
+						<th>사용여부</th>
+						<td>
+							<select name="useYn">
+								<option value="">[전체]</option>
+								<option value="Y">[Y] Yes</option>
+								<option value="N">[N] No</option>
+							</select>
+						</td>
+					</tr>
+				</table>
+				<div class="panelBtnB">
+					<input type="button" value="조회" class="btn btn-base btn-lg" id="btnSearch" onclick="fnSearch();"/>
+				</div>
+			</form>
+		</div>
+		<!-- 검색조건 영역 -->
+		
+		<!-- 리스트 영역 -->
+		<div class="panelStyle2">
+			<ul class="lrStyle">
+				<li>
+					<button type="button" class="btn btn-default btn-lg" id="btnExcelDown" onclick="fnExcelDown();">엑셀다운로드</button>
+				</li>
+			</ul>
+			
+			<div class="panelContent" style="overflow: hidden;">
+				<div id="gridList" style="width: 100%; height: 450px;" class="ag-theme-balham"></div>
+			</div>
+		</div>
+		<!-- //리스트 영역 -->
+
+		<!-- 등록/수정 -->
+		<div class="panelStyle2">
+			<form id="detailForm" name="detailForm" action="#" th:action="@{'/system/basic/answer/save'}">
+				<input type="hidden" name="ansSq" value=""/>
+				
+				<table class="frmStyle">
+					<colgroup>
+						<col style="width:10%;"/>
+						<col style="width:20%;"/>
+						<col style="width:10%;"/>
+						<col style="width:20%;"/>
+						<col style="width:10%;"/>
+						<col style="width:20%;"/>
+						<col/>
+					</colgroup>
+					<tr>
+						<th>사이트<i class="star"></i></th>
+						<td>
+							<select name="siteCd" required="required" data-valid-name="사이트">
+								<option>[전체]</option>
+								<option th:if="${siteList}" th:each="oneData, status : ${siteList}" th:value="${oneData.cd}" th:text="${'[' + oneData.cd + '] ' + oneData.cdNm}"></option>
+							</select>
+						</td>
+						<th>답변종류<i class="star"></i></th>
+						<td>
+							<select name="ansClsf" required="required" data-valid-name="답변종류">
+								<option value="">[선택]</option>
+								<option th:if="${ansClsfList}" th:each="oneData, status : ${ansClsfList}" th:value="${oneData.cd}" th:text="${'[' + oneData.cd + '] ' + oneData.cdNm}"></option>
+							</select>
+						</td>
+						<th>등록자</th>
+						<td>
+							<label th:text="${sessionInfo.userId}" id="regId"></label>
+						</td>
+					</tr>
+					<tr>
+						<th>답변제목<i class="star"></i></th>
+						<td colspan="3">
+							<input type="text" name="ansTitle" required="required" data-valid-name="답변제목"/>
+						</td>
+						<th>사용여부</th>
+						<td>
+							<input type="hidden" name="useYn" value="Y"/>
+							<label><input type="checkbox" name="chkUseYn" checked="checked"/>사용</label>
+						</td>
+					</tr>
+					<tr>
+						<th>답변내용<i class="star"></i></th>
+						<td colspan="5">
+							<textarea name="ansContent" class="textareaR3" required="required"  data-valid-name="답변내용" ></textarea>
+						</td>
+					</tr>
+				</table>
+
+				<div class="panelBtnB">
+					<button type="button" class="btn btn-success btn-lg" id="btnNew">신규</button>
+					<button type="button" class="btn btn-success btn-lg" id="btnSave">저장</button>
+				</div>
+			</form>
+		</div>
+		<!-- 등록/수정 -->
+	</div>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	var siteList = cfnConvertToArray([[${siteList}]]);
+	var ansClsfList = cfnConvertToArray([[${ansClsfList}]]);
+
+	var columnDefs = [
+		{headerName: "답변번호", field: "ansSq", width: 150, cellClass: 'text-center'},
+		{
+			headerName: "사이트", field: "siteCd", width: 150, cellClass: 'text-center',
+			valueFormatter: function (params) { return gagaAgGrid.lookupValue(siteList, params.value); }
+		},
+		{
+			headerName: "답변종류", field: "ansClsf", width: 150, cellClass: 'text-center',
+			valueFormatter: function (params) { return gagaAgGrid.lookupValue(ansClsfList, params.value); }
+		},
+		{
+			headerName: "답변제목", field: "ansTitle", width: 350,
+			cellRenderer: function(params) { return '<a href="javascript:void(0);">' + params.value + '</a>'; }
+		},
+		{headerName: "사용여부", field: "useYn", width: 80, cellClass: 'text-center'},
+		{headerName: "등록자ID", field: "regId", width: 150, cellClass: 'text-center'},
+		{
+			headerName: "등록일", field: "regDt", width: 100, cellClass: 'text-center',
+			cellRenderer: function(params) { return !gagajf.isNull(params.value) ? params.value.toDate("YYYYMMDD").format("YYYY-MM-DD") : ''; }
+		}
+	];
+
+	var gridOptions = gagaAgGrid.getGridOptions(columnDefs);
+
+	// 셀 클릭 이벤트
+	gridOptions.onCellClicked = function(event) {
+		if (event.colDef.field != 'ansTitle')
+			return;
+		
+		$('#detailForm input[name=ansSq]').val(event.data.ansSq);								// 답변일련번호
+		$('#detailForm select[name=siteCd]').val(event.data.siteCd);							// 사이트
+		$('#detailForm select[name=ansClsf]').val(event.data.ansClsf);							// 답변종류
+		$('#regId').html(event.data.regId);														// 등록자ID
+		$('#detailForm input[name=ansTitle]').val(event.data.ansTitle.replaceAll('&gt;','>'));	// 답변제목
+		
+		if (event.data.useYn == 'Y') {
+			$("#detailForm input:checkbox[name=chkUseYn]").prop('checked', true);
+			$("#detailForm input:checkbox[name=chkUseYn]").parent().addClass('checked');
+		} else {
+			$("#detailForm input:checkbox[name=chkUseYn]").prop('checked', false);
+			$("#detailForm input:checkbox[name=chkUseYn]").parent().removeClass('checked');
+		}
+		
+		$('#detailForm textarea[name=ansContent]').val(event.data.ansContent);	// 답변내용
+	}
+
+	// 신규
+	$('#btnNew').on('click', function() {
+		$('#detailForm')[0].reset();
+		$('#detailForm input[name=ansSq]').val('');
+		$('#detailForm input[name=ansTitle]').focus();
+	});
+	
+	// 검색
+	var fnSearch = function() {
+		gagaAgGrid.fetch($('#searchForm').prop('action'), gridOptions, '#searchForm');
+	}
+
+	// 저장/수정
+	$("#btnSave").on("click", function(formId){
+		var formId = '#detailForm';
+
+		var selectedData = gridOptions.api.getSelectedRows();
+
+		//필수값 체크
+		if (!gagajf.validation(formId))
+			return false;
+
+		var msg = '저장하시겠습니까?';
+		if (selectedData.length == 0) {
+			msg = '등록하시겠습니까?';
+		}
+		var ansSq = $(formId + " input[name=ansSq]").val();
+		var siteCd = $(formId + " select[name=siteCd").val();									// 사이트
+		var ansClsf = $(formId + " select[name=ansClsf]").val();								// 답변종류
+		var ansTitle = $(formId + " input[name=ansTitle]").val();								// 답변제목
+		var ansContent = $(formId + " textarea[name=ansContent]").val();						// 답변내용
+		var useYn = $(formId + ' input:checkbox[name=chkUseYn]').is(":checked") ? 'Y' : 'N';	// 사용여부
+
+		var data = {
+				ansSq : ansSq,
+				siteCd : siteCd,
+				ansClsf : ansClsf,
+				ansTitle : ansTitle,
+				ansContent : ansContent,
+				useYn : useYn
+		};
+
+		mcxDialog.confirm(msg, {
+			cancelBtnText: "취소",
+			sureBtnText: "확인",
+			sureBtnClick: function() {
+				var jsonData = JSON.stringify(data);
+				gagajf.ajaxJsonSubmit($(formId).prop("action"), jsonData, fnSaveCallback);
+			}
+		});
+	});
+
+	//저장후 콜백
+	var fnSaveCallback = function() {
+		fnSearch();
+		$("#btnNew").click();
+	}
+
+	$(document).ready(function() {
+		gagaAgGrid.createGrid('gridList', gridOptions);
+
+		// Grid 높이 조정
+		uifnFitGrid();
+		
+		fnSearch();
+	});
+/*]]>*/
+</script>
+
+</html>

+ 735 - 0
style24.admin/src/main/webapp/WEB-INF/views/system/BasicEnvsetForm.html

@@ -0,0 +1,735 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : BasicEnvsetForm.html
+ * @desc    : 기본환경설정 Page
+ *============================================================================
+ * Wivismall
+ * Copyright(C) 2019 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.03.13   gagamel     최초 작성
+ *******************************************************************************
+ -->
+
+	<div id="main">
+		<!-- 메인타이틀 영역 -->
+		<div class="main-title">
+		</div>
+		<!-- //메인타이틀 영역 -->
+
+		<!-- 검색조건 영역 -->
+		<div class="panelStyle2">
+			<table class="frmStyle" aria-describedby="검색조건">
+				<colgroup>
+					<col style="width:10%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>사이트</th>
+						<td>
+							<select name="siteCd" class="w150" onchange="fnSearch();">
+								<option th:if="${siteList}" th:each="oneData, status : ${siteList}" th:value="${oneData.cd}" th:text="${'[' + oneData.cd + '] ' + oneData.cdNm}"></option>
+							</select>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+			<div class="panelBtnB"></div>
+		</div>
+		<!-- //검색조건 영역 -->
+		<!-- 테이블 영역 -->
+		<div class="panelStyle2">
+			<h4>쇼핑몰Meta정보</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('B10');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('B10', '쇼핑몰Meta정보');">이력보기</button>
+			<table class="frmStyle" aria-describedby="쇼핑몰Meta정보">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>웹브라우저Title</th>
+						<td>
+							<input name="b10StrSetVal1" type="text" class="w500" maxlength="200"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>웹 브라우저 상단에 노출되는 사이트 소개 문구입니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>쇼핑몰Title(og:title)</th>
+						<td>
+							<input name="b10StrSetVal2" type="text" class="w500" maxlength="200"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>오픈그래프 Title입니다. 영문/한글/숫자만 입력하세요.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>쇼핑몰설명(og:description)</th>
+						<td>
+							<input name="b10StrSetVal3" type="text" class="w500" maxlength="200"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>오픈그래프 Description입니다. 200자 이내의 영문/한글/숫자만 입력하세요.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>쇼핑몰이미지(og:image)</th>
+						<td>
+							<div class="uFile w500">
+								<input type="file" id="b10StrSetVal4" name="b10StrSetVal4" class="uFileInput"/>
+								<label for="b10StrSetVal4" class="uFileLabel">쇼핑몰이미지 선택</label>
+								<input type="hidden" name="b10StrSetVal4OrgFileNm"/>
+								<input type="hidden" name="b10StrSetVal4SysFileNm"/>
+							</div>
+							<a id="b10StrSetVal4FileDownload" href="#" style="display: none;" onclick="fnDownloadFile('b10StrSetVal4');"></a>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>오픈그래프 Image입니다</span>
+						</td>
+					</tr>
+					<tr>
+						<th>키워드(Keywords)</th>
+						<td>
+							<input name="b10StrSetVal5" type="text" class="w500" maxlength="70"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>70자 이내로 작성하시고 같은 단어는 3회 이상 반복하시면 안 됩니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>파비콘이미지</th>
+						<td>
+							<div class="uFile w500">
+								<input type="file" id="b10StrSetVal6" name="b10StrSetVal6" class="uFileInput"/>
+								<label for="b10StrSetVal6" class="uFileLabel">파비콘이미지 선택</label>
+								<input type="hidden" name="b10StrSetVal6OrgFileNm"/>
+								<input type="hidden" name="b10StrSetVal6SysFileNm"/>
+							</div>
+							<a id="b10StrSetVal6FileDownload" href="#" style="display: none;" onclick="fnDownloadFile('b10StrSetVal6');"></a>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>16x16 사이즈의 아이콘 이미지(확장자:ico)</span>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+
+			<h4>기본설정</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('B11');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('B11', '기본설정');">이력보기</button>
+			<table class="frmStyle" aria-describedby="기본설정">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>탈퇴후재가입불가기간(월)</th>
+						<td><input name="b11NumSetVal1" type="text" class="w50 aR" maxlength="2" data-valid-type="integer" data-valid-name="탈퇴후재가입불가기간"/><span class="cRed">개월</span> 동안 탈퇴 후 재가입 불가합니다.</td>
+					</tr>
+					<tr>
+						<th>휴면회원선정기간(일)</th>
+						<td><input name="b11NumSetVal2" type="text" class="w50 aR" maxlength="3" data-valid-type="integer" data-valid-name="휴면회원선정기간"/><span class="cRed">일</span> 동안 사이트에 로그인 하지 않은 회원을 휴면회원으로 선정합니다.</td>
+					</tr>
+					<tr>
+						<th>휴면회원전환기간(일)</th>
+						<td><input name="b11NumSetVal3" type="text" class="w50 aR" maxlength="3" data-valid-type="integer" data-valid-name="휴면회원전환기간"/><span class="cRed">일</span> 동안 사이트에 로그인 하지 않은 회원을 휴면회원으로 전환합니다.</td>
+					</tr>
+					<tr>
+						<th>회원등급산정기간</th>
+						<td>직전월 이전 <input name="b11NumSetVal4" type="text" class="w50 aR" maxlength="3" data-valid-type="integer" data-valid-name="회원등급산정기간"/><span class="cRed">개월</span> 기간동안의 매출금액을 기준으로 월초에 산정합니다. <span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>0으로 설정하면 회원별등급산정을 하지 않습니다.</span></td>
+					</tr>
+					<tr>
+						<th>무료배송비최소주문금액</th>
+						<td>최소주문금액이 <input name="b11NumSetVal5" type="text" class="w100 aR" maxlength="6" data-valid-type="integer" data-valid-name="무료배송비최소주문금액"/> 이상이면 배송비가 무료입니다. <span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>이 값은 기본값이며 공급업체별로 다르게 설정할 수도 있습니다.</span></td>
+					</tr>
+				</tbody>
+			</table>
+
+			<h4>회원혜택안내(PC)</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('B12');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('B12', '회원혜택안내');">이력보기</button>
+			<table class="frmStyle" aria-describedby="회원혜택안내(PC)">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>가입혜택안내배너(A)</th>
+						<td>
+							<div class="uFile w500">
+								<input type="file" id="b12StrSetVal1" name="b12StrSetVal1" class="uFileInput"/>
+								<label for="b12StrSetVal1" class="uFileLabel">가입혜택안내배너 선택</label>
+								<input type="hidden" name="b12StrSetVal1OrgFileNm"/>
+								<input type="hidden" name="b12StrSetVal1SysFileNm"/>
+							</div>
+							<a id="b12StrSetVal1FileDownload" href="#" style="display: none;" onclick="fnDownloadFile('b12StrSetVal1');"></a>
+						</td>
+					</tr>
+					<tr>
+						<th>가입혜택안내배너(B)</th>
+						<td>
+							<div class="uFile w500">
+								<input type="file" id="b12StrSetVal2" name="b12StrSetVal2" class="uFileInput"/>
+								<label for="b12StrSetVal2" class="uFileLabel">가입혜택안내배너 선택</label>
+								<input type="hidden" name="b12StrSetVal2OrgFileNm"/>
+								<input type="hidden" name="b12StrSetVal2SysFileNm"/>
+							</div>
+							<a id="b12StrSetVal2FileDownload" href="#" style="display: none;" onclick="fnDownloadFile('b12StrSetVal2');"></a>
+						</td>
+					</tr>
+					<tr>
+						<th>회원혜택안내배너</th>
+						<td>
+							<div class="uFile w500">
+								<input type="file" id="b12StrSetVal3" name="b12StrSetVal3" class="uFileInput"/>
+								<label for="b12StrSetVal3" class="uFileLabel">회원혜택안내배너 선택</label>
+								<input type="hidden" name="b12StrSetVal3OrgFileNm"/>
+								<input type="hidden" name="b12StrSetVal3SysFileNm"/>
+							</div>
+							<a id="b12StrSetVal3FileDownload" href="#" style="display: none;" onclick="fnDownloadFile('b12StrSetVal3');"></a>
+						</td>
+					</tr>
+					<tr>
+						<th>상품평혜택안내배너(상품상세)</th>
+						<td>
+							<div class="uFile w500">
+								<input type="file" id="b12StrSetVal4" name="b12StrSetVal4" class="uFileInput"/>
+								<label for="b12StrSetVal4" class="uFileLabel">상품평혜택안내배너 선택(상품상세페이지)</label>
+								<input type="hidden" name="b12StrSetVal4OrgFileNm"/>
+								<input type="hidden" name="b12StrSetVal4SysFileNm"/>
+							</div>
+							<a id="b12StrSetVal4FileDownload" href="#" style="display: none;" onclick="fnDownloadFile('b12StrSetVal4');"></a>
+						</td>
+					</tr>
+					<tr>
+						<th>상품평혜택안내배너(마이페이지)</th>
+						<td>
+							<div class="uFile w500">
+								<input type="file" id="b12StrSetVal5" name="b12StrSetVal5" class="uFileInput"/>
+								<label for="b12StrSetVal5" class="uFileLabel">상품평혜택안내배너 선택(마이페이지)</label>
+								<input type="hidden" name="b12StrSetVal5OrgFileNm"/>
+								<input type="hidden" name="b12StrSetVal5SysFileNm"/>
+							</div>
+							<a id="b12StrSetVal5FileDownload" href="#" style="display: none;" onclick="fnDownloadFile('b12StrSetVal5');"></a>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+			<h4>회원혜택안내(MOBILE)</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('B16');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('B16', '회원혜택안내');">이력보기</button>
+			<table class="frmStyle" aria-describedby="회원혜택안내(MOBILE)">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>가입혜택안내배너(A)</th>
+						<td>
+							<div class="uFile w500">
+								<input type="file" id="b16StrSetVal1" name="b16StrSetVal1" class="uFileInput"/>
+								<label for="b16StrSetVal1" class="uFileLabel">가입혜택안내배너 선택</label>
+								<input type="hidden" name="b16StrSetVal1OrgFileNm"/>
+								<input type="hidden" name="b16StrSetVal1SysFileNm"/>
+							</div>
+							<a id="b16StrSetVal1FileDownload" href="#" style="display: none;" onclick="fnDownloadFile('b16StrSetVal1');"></a>
+						</td>
+					</tr>
+					<tr>
+						<th>가입혜택안내배너(B)</th>
+						<td>
+							<div class="uFile w500">
+								<input type="file" id="b16StrSetVal2" name="b16StrSetVal2" class="uFileInput"/>
+								<label for="b16StrSetVal2" class="uFileLabel">가입혜택안내배너 선택</label>
+								<input type="hidden" name="b16StrSetVal2OrgFileNm"/>
+								<input type="hidden" name="b16StrSetVal2SysFileNm"/>
+							</div>
+							<a id="b16StrSetVal2FileDownload" href="#" style="display: none;" onclick="fnDownloadFile('b16StrSetVal2');"></a>
+						</td>
+					</tr>
+					<tr>
+						<th>회원혜택안내배너</th>
+						<td>
+							<div class="uFile w500">
+								<input type="file" id="b16StrSetVal3" name="b16StrSetVal3" class="uFileInput"/>
+								<label for="b16StrSetVal3" class="uFileLabel">회원혜택안내배너 선택</label>
+								<input type="hidden" name="b16StrSetVal3OrgFileNm"/>
+								<input type="hidden" name="b16StrSetVal3SysFileNm"/>
+							</div>
+							<a id="b16StrSetVal3FileDownload" href="#" style="display: none;" onclick="fnDownloadFile('b16StrSetVal3');"></a>
+						</td>
+					</tr>
+					<tr>
+						<th>상품평혜택안내배너</th>
+						<td>
+							<div class="uFile w500">
+								<input type="file" id="b16StrSetVal4" name="b16StrSetVal4" class="uFileInput"/>
+								<label for="b16StrSetVal4" class="uFileLabel">상품평혜택안내배너 선택(상품상세, 마이페이지)</label>
+								<input type="hidden" name="b16StrSetVal4OrgFileNm"/>
+								<input type="hidden" name="b16StrSetVal4SysFileNm"/>
+							</div>
+							<a id="b16StrSetVal4FileDownload" href="#" style="display: none;" onclick="fnDownloadFile('b16StrSetVal4');"></a>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+
+			<h4>상품노출</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('B13');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('B13', '상품노출');">이력보기</button>
+			<table class="frmStyle" aria-describedby="상품노출">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>상품평노출여부</th>
+						<td>
+							<label class="rdoBox"><input type="radio" name="b13StrSetVal1" value="Y" checked="checked"/>노출</label>
+							<label class="rdoBox"><input type="radio" name="b13StrSetVal1" value="N">미노출</label>
+							<span class="infoTxt cBlue"><i class="fa fa-info-circle" aria-hidden="true"></i>미노출로 설정 시 상품상세의 상품평 영역이 노출되지 않습니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>품절상품노출여부</th>
+						<td>
+							<label class="rdoBox"><input type="radio" name="b13StrSetVal2" value="Y"/>노출</label>
+							<label class="rdoBox"><input type="radio" name="b13StrSetVal2" value="N" checked="checked">미노출</label>
+							<span class="infoTxt cBlue"><i class="fa fa-info-circle" aria-hidden="true"></i>품절된 상품을 사이트에 노출할지 말지를 설정합니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>특가세일노출기준</th>
+						<td>
+							상품 썸네일 리스트의 가격은 할인율이 <input name="b13NumSetVal1" type="text" class="w20 aR" maxlength="3" data-valid-type="integer" data-valid-name=""/><span class="cRed">%</span> 이상일 경우 TAG가와 할인율 대신 <span class="infoTxt cBlue">'특가세일'</span>로 표기됩니다.
+						</td>
+					</tr>
+				</tbody>
+			</table>
+
+			<h4>상품보관</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('B17');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('B17', '상품보관');">이력보기</button>
+			<table class="frmStyle" aria-describedby="상품보관">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>장바구니보관기간(일)</th>
+						<td>
+							장바구니 보관기간은 <input name="b17NumSetVal1" type="text" class="w20 aR" maxlength="3" data-valid-type="integer" data-valid-name="장바구니보관기간"/><span class="cRed">일</span> 입니다. 보관기간 경과 시 자동 삭제됩니다.
+							<span class="infoTxt cBlue"><i class="fa fa-info-circle marL20" aria-hidden="true"></i>0으로 설정하면 자동으로 삭제 처리를 하지 않습니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>위시리스트보관기간(일)</th>
+						<td>
+							위시리스트 보관기간은 <input name="b17NumSetVal2" type="text" class="w20 aR" maxlength="3" data-valid-type="integer" data-valid-name="위시리스트보관기간"/><span class="cRed">일</span> 입니다. 보관기간 경과 시 자동 삭제됩니다.
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>0으로 설정하면 자동으로 삭제 처리를 하지 않습니다.</span>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+
+			<h4>품절안내</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('B14');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('B14', '품절안내');">이력보기</button>
+			<table class="frmStyle" aria-describedby="품절안내">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>품절안내방법</th>
+						<td>
+							<label class="rdoBox"><input type="radio" name="b14StrSetVal1" value="A" checked="checked"/>발송</label>
+							<label class="rdoBox"><input type="radio" name="b14StrSetVal1" value="M">미발송</label>
+							<p>결제완료 후 <input name="b14NumSetVal1" type="text" class="w20 aR" maxlength="3" data-valid-type="integer" data-valid-name="품절안내메시지발송기간"/><span class="cRed">일</span> 동안 출고되지 않으면 품절안내 메시지를 발송합니다.</p>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+
+			<h4>배송/구매확정</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('B15');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('B15', '배송/구매확정');">이력보기</button>
+			<table class="frmStyle" aria-describedby="배송/구매확정">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>배송완료처리기간(일)</th>
+						<td>
+							주문상세상태가 배송중 이후 <input name="b15NumSetVal1" type="text" class="w20 aR" maxlength="3" data-valid-type="integer" data-valid-name="배송완료처리기간"/><span class="cRed">일</span> 경과한 기간동안 배송완료로 처리하지 않으면 자동으로 배송완료로 처리합니다.
+							<span class="infoTxt cBlue"><i class="fa fa-info-circle marL20" aria-hidden="true"></i>0으로 설정하면 자동으로 배송완료 처리를 하지 않습니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>구매확정처리기간(일)</th>
+						<td>
+							주문상세상태가 배송완료 후 <input name="b15NumSetVal2" type="text" class="w20 aR" maxlength="3" data-valid-type="integer" data-valid-name="구매확정처리기간"/><span class="cRed">일</span> 경과한 기간동안 구매확정으로 처리를 하지 않으면 자동으로 구매확정으로 처리합니다.
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>0으로 설정하면 자동으로 구매확정 처리를 하지 않습니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>반품가능기간(일)</th>
+						<td>
+							주문상세상태가 배송완료 후 <input name="b15NumSetVal3" type="text" class="w20 aR" maxlength="3" data-valid-type="integer" data-valid-name="반품가능기간"/> <span class="cRed">일</span> 동안 반품요청할 수 있습니다.
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>0으로 설정하면 언제든 반품요청을 할 수 있습니다.</span>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+
+			<h4>모바일앱</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('M10');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('M10', '모바일앱');">이력보기</button>
+			<table class="frmStyle" aria-describedby="모바일앱">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>AOS앱버전</th>
+						<td>
+							<input name="m10StrSetVal1" type="text" class="w100" maxlength="20" data-valid-name="AOS앱버전"/>
+						</td>
+					</tr>
+					<tr>
+						<th>IOS앱버전</th>
+						<td>
+							<input name="m10StrSetVal2" type="text" class="w100" maxlength="20" data-valid-name="IOS앱버전"/>
+						</td>
+					</tr>
+					<tr>
+						<th>스플래시이미지</th>
+						<td>
+							<div class="uFile w500">
+								<input type="file" id="m10StrSetVal3" name="m10StrSetVal3" class="uFileInput"/>
+								<label for="m10StrSetVal3" class="uFileLabel">스플래시 이미지 선택</label>
+								<input type="hidden" name="m10StrSetVal3OrgFileNm"/>
+								<input type="hidden" name="m10StrSetVal3SysFileNm"/>
+							</div>
+							<a id="m10StrSetVal3FileDownload" href="#" style="display: none;" onclick="fnDownloadFile('m10StrSetVal3');"></a>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>스플래시 이미지입니다</span>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+			<div class="panelBtnB"></div>
+		</div>
+	</div>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	// 조회
+	var fnSearch = function() {
+		var siteCd = $('select[name=siteCd]').val();
+		var actionUrl = '/system/envset/' + siteCd;
+
+		// 쇼핑몰Meta정보
+		$.getJSON(actionUrl + '/B10'
+				, function(result, status) {
+					if (status === 'success') {
+						if (!gagajf.isNull(result)) {
+							$('input[name=b10StrSetVal1]').val(result.strSetVal1);
+							$('input[name=b10StrSetVal2]').val(result.strSetVal2);
+							$('input[name=b10StrSetVal3]').val(result.strSetVal3);
+							
+							if (!gagajf.isNull(result.strSetVal4)) {
+								$('input[name=b10StrSetVal4SysFileNm]').val(result.strSetVal4);
+								$('#b10StrSetVal4FileDownload').html(result.strSetVal4);
+								$('#b10StrSetVal4FileDownload').show();
+							}
+							
+							$('input[name=b10StrSetVal5]').val(result.strSetVal5);
+							
+							if (!gagajf.isNull(result.strSetVal6)) {
+								$('input[name=b10StrSetVal6SysFileNm]').val(result.strSetVal6);
+								$('#b10StrSetVal6FileDownload').html(result.strSetVal6);
+								$('#b10StrSetVal6FileDownload').show();
+							}
+						}
+					}
+				});
+
+		// 기본설정
+		$.getJSON(actionUrl + '/B11'
+				, function(result, status) {
+					if (status === 'success') {
+						if (!gagajf.isNull(result)) {
+							$('input[name=b11NumSetVal1]').val(result.numSetVal1);
+							$('input[name=b11NumSetVal2]').val(result.numSetVal2);
+							$('input[name=b11NumSetVal3]').val(result.numSetVal3);
+							$('input[name=b11NumSetVal4]').val(result.numSetVal4);
+							$('input[name=b11NumSetVal5]').val(result.numSetVal5.addComma());
+						}
+					}
+				});
+
+		// 회원혜택안내
+		$.getJSON(actionUrl + '/B12'
+				, function(result, status) {
+					if (status === 'success') {
+						if (!gagajf.isNull(result.strSetVal1)) {
+							$('input[name=b12StrSetVal1SysFileNm]').val(result.strSetVal1);
+							$('#b12StrSetVal1FileDownload').html(result.strSetVal1);
+							$('#b12StrSetVal1FileDownload').show();
+						}
+
+						if (!gagajf.isNull(result.strSetVal2)) {
+							$('input[name=b12StrSetVal2SysFileNm]').val(result.strSetVal2);
+							$('#b12StrSetVal2FileDownload').html(result.strSetVal2);
+							$('#b12StrSetVal2FileDownload').show();
+						}
+
+						if (!gagajf.isNull(result.strSetVal3)) {
+							$('input[name=b12StrSetVal3SysFileNm]').val(result.strSetVal3);
+							$('#b12StrSetVal3FileDownload').html(result.strSetVal3);
+							$('#b12StrSetVal3FileDownload').show();
+						}
+
+						if (!gagajf.isNull(result.strSetVal4)) {
+							$('input[name=b12StrSetVal4SysFileNm]').val(result.strSetVal4);
+							$('#b12StrSetVal4FileDownload').html(result.strSetVal4);
+							$('#b12StrSetVal4FileDownload').show();
+						}
+
+						if (!gagajf.isNull(result.strSetVal5)) {
+							$('input[name=b12StrSetVal5SysFileNm]').val(result.strSetVal5);
+							$('#b12StrSetVal5FileDownload').html(result.strSetVal5);
+							$('#b12StrSetVal5FileDownload').show();
+						}
+					}
+				});
+
+		// 상품노출
+		$.getJSON(actionUrl + '/B13'
+				, function(result, status) {
+					if (status === 'success') {
+						if (!gagajf.isNull(result)) {
+							$("input:radio[name=b13StrSetVal1]").parents('td').find('label').removeClass('checked');
+							if (result.strSetVal1 == 'Y') {
+								$("input:radio[name=b13StrSetVal1]").eq(0).prop('checked', true);
+								$("input:radio[name=b13StrSetVal1]").eq(0).parent().addClass('checked');
+							} else {
+								$("input:radio[name=b13StrSetVal1]").eq(1).prop('checked', true);
+								$("input:radio[name=b13StrSetVal1]").eq(1).parent().addClass('checked');
+							}
+
+							$("input:radio[name=b13StrSetVal2]").parents('td').find('label').removeClass('checked');
+							if (result.strSetVal2 == 'Y') {
+								$("input:radio[name=b13StrSetVal2]").eq(0).prop('checked', true);
+								$("input:radio[name=b13StrSetVal2]").eq(0).parent().addClass('checked');
+							} else {
+								$("input:radio[name=b13StrSetVal2]").eq(1).prop('checked', true);
+								$("input:radio[name=b13StrSetVal2]").eq(1).parent().addClass('checked');
+							}
+							
+							$('input[name=b13NumSetVal1]').val(result.numSetVal1);
+						}
+					}
+				});
+
+		// 상품보관
+		$.getJSON(actionUrl + '/B17'
+				, function(result, status) {
+					if (status === 'success') {
+						if (!gagajf.isNull(result)) {
+							$('input[name=b17NumSetVal1]').val(result.numSetVal1);
+							$('input[name=b17NumSetVal2]').val(result.numSetVal2);
+						}
+					}
+				});
+
+		// 품절안내
+		$.getJSON(actionUrl + '/B14'
+				, function(result, status) {
+					if (status === 'success') {
+						if (!gagajf.isNull(result)) {
+							$("input:radio[name=b14StrSetVal1]").parents('td').find('label').removeClass('checked');
+							if (result.strSetVal1 == 'A') {
+								$("input:radio[name=b14StrSetVal1]").eq(0).prop('checked', true);
+								$("input:radio[name=b14StrSetVal1]").eq(0).parent().addClass('checked');
+							} else if (result.strSetVal1 == 'M') {
+								$("input:radio[name=b14StrSetVal1]").eq(1).prop('checked', true);
+								$("input:radio[name=b14StrSetVal1]").eq(1).parent().addClass('checked');
+							}
+
+							$('input[name=b14NumSetVal1]').val(result.numSetVal1);
+						}
+					}
+				});
+
+		// 배송/구매확정
+		$.getJSON(actionUrl + '/B15'
+				, function(result, status) {
+					if (status === 'success') {
+						if (!gagajf.isNull(result)) {
+							$('input[name=b15NumSetVal1]').val(result.numSetVal1);
+							$('input[name=b15NumSetVal2]').val(result.numSetVal2);
+							$('input[name=b15NumSetVal3]').val(result.numSetVal3);
+						}
+					}
+				});
+
+		// 회원혜택안내(MOBILE)
+		$.getJSON(actionUrl + '/B16'
+				, function(result, status) {
+					if (status === 'success') {
+						if (!gagajf.isNull(result.strSetVal1)) {
+							$('input[name=b16StrSetVal1SysFileNm]').val(result.strSetVal1);
+							$('#b16StrSetVal1FileDownload').html(result.strSetVal1);
+							$('#b16StrSetVal1FileDownload').show();
+						}
+
+						if (!gagajf.isNull(result.strSetVal2)) {
+							$('input[name=b16StrSetVal2SysFileNm]').val(result.strSetVal2);
+							$('#b16StrSetVal2FileDownload').html(result.strSetVal2);
+							$('#b16StrSetVal2FileDownload').show();
+						}
+
+						if (!gagajf.isNull(result.strSetVal3)) {
+							$('input[name=b16StrSetVal3SysFileNm]').val(result.strSetVal3);
+							$('#b16StrSetVal3FileDownload').html(result.strSetVal3);
+							$('#b16StrSetVal3FileDownload').show();
+						}
+
+						if (!gagajf.isNull(result.strSetVal4)) {
+							$('input[name=b16StrSetVal4SysFileNm]').val(result.strSetVal4);
+							$('#b16StrSetVal4FileDownload').html(result.strSetVal4);
+							$('#b16StrSetVal4FileDownload').show();
+						}
+					}
+				});
+
+		// 모바일앱
+		$.getJSON(actionUrl + '/M10'
+				, function(result, status) {
+					if (status === 'success') {
+						if (!gagajf.isNull(result)) {
+							$('input[name=m10StrSetVal1]').val(result.strSetVal1);
+							$('input[name=m10StrSetVal2]').val(result.strSetVal2);
+						}
+						
+						if (!gagajf.isNull(result.strSetVal3)) {
+							$('input[name=m10StrSetVal3SysFileNm]').val(result.strSetVal3);
+							$('#m10StrSetVal3FileDownload').html(result.strSetVal3);
+							$('#m10StrSetVal3FileDownload').show();
+						}
+					}
+				});
+	}
+
+	// 파일첨부 선택 시
+	$('#b10StrSetVal4').on('change', function() { fnChooseFile(this); });
+	$('#b10StrSetVal6').on('change', function() { fnChooseFile(this); });
+	$('#b12StrSetVal1').on('change', function() { fnChooseFile(this); });
+	$('#b12StrSetVal2').on('change', function() { fnChooseFile(this); });
+	$('#b12StrSetVal3').on('change', function() { fnChooseFile(this); });
+	$('#b12StrSetVal4').on('change', function() { fnChooseFile(this); });
+	$('#b12StrSetVal5').on('change', function() { fnChooseFile(this); });
+	$('#b16StrSetVal1').on('change', function() { fnChooseFile(this); });
+	$('#b16StrSetVal2').on('change', function() { fnChooseFile(this); });
+	$('#b16StrSetVal3').on('change', function() { fnChooseFile(this); });
+	$('#b16StrSetVal4').on('change', function() { fnChooseFile(this); });
+	$('#m10StrSetVal3').on('change', function() { fnChooseFile(this); });
+
+	var fnChooseFile = function(obj) {
+		// multiple 속성이 있으면 files에는 다수의 객체가 할당됨
+		var file = obj.files[0];
+
+		// 파일 업로드
+		gagajf.ajaxFileUpload('/common/file/upload?subDir=/envset'
+				, file
+				, function(result) {
+					// 업로드한 파일명 설정
+					$('input[name=' + obj.name + 'OrgFileNm]').val(result.oldFileName);
+					$('input[name=' + obj.name + 'SysFileNm]').val(result.newFileName);
+				}
+		);
+	}
+
+	// 파일다운로드
+	var fnDownloadFile = function(id) {
+		$('#' + id + 'FileDownload').attr({
+			href : _uploadDefaultUrl + '/envset/' + $('input[name=' + id + 'SysFileNm]').val(),
+			target: '_blank'
+		}).get(0).click();
+	}
+
+	// 저장 처리
+	var fnSave = function(envsetType) {
+		var params = new Object();
+		params.siteCd = $('select[name=siteCd]').val();
+		params.envsetType = envsetType;
+
+		if (envsetType == 'B10') { // 쇼핑몰Meta정보
+			params.strSetVal1 = $('input[name=b10StrSetVal1]').val();
+			params.strSetVal2 = $('input[name=b10StrSetVal2]').val();
+			params.strSetVal3 = $('input[name=b10StrSetVal3]').val();
+			params.strSetVal4 = $('input[name=b10StrSetVal4SysFileNm]').val();
+			params.strSetVal5 = $('input[name=b10StrSetVal5]').val();
+			params.strSetVal6 = $('input[name=b10StrSetVal6SysFileNm]').val();
+		} else if (envsetType == 'B11') { // 기본설정
+			params.numSetVal1 = $('input[name=b11NumSetVal1]').val();
+			params.numSetVal2 = $('input[name=b11NumSetVal2]').val();
+			params.numSetVal3 = $('input[name=b11NumSetVal3]').val();
+			params.numSetVal4 = $('input[name=b11NumSetVal4]').val();
+			params.numSetVal5 = $('input[name=b11NumSetVal5]').val().removeComma();
+		} else if (envsetType == 'B12') { // 회원혜택안내
+			params.strSetVal1 = $('input[name=b12StrSetVal1SysFileNm]').val();
+			params.strSetVal2 = $('input[name=b12StrSetVal2SysFileNm]').val();
+			params.strSetVal3 = $('input[name=b12StrSetVal3SysFileNm]').val();
+			params.strSetVal4 = $('input[name=b12StrSetVal4SysFileNm]').val();
+			params.strSetVal5 = $('input[name=b12StrSetVal5SysFileNm]').val();
+		} else if (envsetType == 'B13') { // 상품노출
+			params.strSetVal1 = $('input:radio[name=b13StrSetVal1]:checked').val();
+			params.strSetVal2 = $('input:radio[name=b13StrSetVal2]:checked').val();
+			params.numSetVal1 = $('input[name=b13NumSetVal1]').val();
+		} else if (envsetType == 'B17') { // 상품보관
+			params.numSetVal1 = $('input[name=b17NumSetVal1]').val();
+			params.numSetVal2 = $('input[name=b17NumSetVal2]').val();
+		} else if (envsetType == 'B14') { // 품절안내
+			params.strSetVal1 = $('input:radio[name=b14StrSetVal1]:checked').val();
+			params.numSetVal1 = $('input[name=b14NumSetVal1]').val();
+		} else if (envsetType == 'B15') { // 배송/구매확정
+			params.numSetVal1 = $('input[name=b15NumSetVal1]').val();
+			params.numSetVal2 = $('input[name=b15NumSetVal2]').val();
+			params.numSetVal3 = $('input[name=b15NumSetVal3]').val();
+		} else if (envsetType == 'B16') { // 회원혜택안내(MOBILE)
+			params.strSetVal1 = $('input[name=b16StrSetVal1SysFileNm]').val();
+			params.strSetVal2 = $('input[name=b16StrSetVal2SysFileNm]').val();
+			params.strSetVal3 = $('input[name=b16StrSetVal3SysFileNm]').val();
+			params.strSetVal4 = $('input[name=b16StrSetVal4SysFileNm]').val();
+		} else if (envsetType == 'M10') { // 모바일앱
+			params.strSetVal1 = $('input[name=m10StrSetVal1]').val();
+			params.strSetVal2 = $('input[name=m10StrSetVal2]').val();
+			params.strSetVal3 = $('input[name=m10StrSetVal3SysFileNm]').val();
+		}
+
+		var jsonData = JSON.stringify(params);
+		gagajf.ajaxJsonSubmit('/system/envset/create', jsonData);
+	}
+
+	// 환경설정 이력보기 팝업
+	var fnOpenEnvsetPopup = function(envsetType, envsetTypeNm) {
+		var actionUrl = '/system/envset/history/form'
+				+ '?siteCd=' + $('select[name=siteCd]').val()
+				+ '&envsetType=' + envsetType
+				+ '&envsetTypeNm=' + encodeURIComponent(envsetTypeNm);
+		cfnOpenModalPopup(actionUrl, 'popupEnvset');
+	}
+
+	$(document).ready(function() {
+		fnSearch();
+		uifnFitGrid();
+	});
+/*]]>*/
+</script>
+</html>

+ 141 - 0
style24.admin/src/main/webapp/WEB-INF/views/system/BizdayForm.html

@@ -0,0 +1,141 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : BizdayForm.html
+ * @desc    : 영업일관리 Page
+ *============================================================================
+ * SISUN
+ * Copyright(C) 2019 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2019.12.13   jaewonHo   최초 작성
+ *******************************************************************************
+ -->
+ <form id="calendarForm" name="calendarForm">
+ 	<input type="hidden" name="startDt" id="startDt"/>
+	<input type="hidden" name="endDt" id="endDt"/>
+	<input type="hidden" name="bizday" id="bizDay"/>
+	<div id="main-wrapper">
+		<div id="main">
+			<div class="main-title"></div>
+			<div class="panelStyle2">
+				<ul class="notice">
+					<li><strong class="cBlue">영업일 : </strong>휴일을 Click 하시면 영업일로 변경 가능 합니다.</li>
+					<li><strong class="cRed">휴무일 : </strong>영업일을 Click 하시면 휴무일로 변경 가능 합니다.</li>
+				</ul>
+				<div id="calendar" style="margin: 5px;"></div>
+			</div>
+		</div>
+	</div>
+</form>
+
+<script class="cssdesk" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.11.0/moment.min.js" type="text/javascript"></script>
+<script src="/ux/js/fullcalendar-4.2.0.js"></script>
+<script th:inline="javascript">
+/*<![CDATA[*/
+
+	$(document).ready(function() {
+		fnstartCalendar();
+		uifnFitGrid();
+	})
+
+	var fnstartCalendar = function() {
+		var calendarEl = document.getElementById('calendar');
+		calendar = new FullCalendar.Calendar(calendarEl, {
+			plugins: [ 'interaction', 'dayGrid' ],
+
+			locale: 'ko', //한글화
+			timeZone: 'local',
+
+			selectable: true,
+			editable: false,
+			eventLimit: true,
+
+			select: function(arg) {
+				if ((new Date(arg.end)).format('YYYYMMDD') - (new Date(arg.start)).format('YYYYMMDD') > 1 ) return;
+				var title ='휴일';
+				
+				mcxDialog.confirm("휴일로 변경하시겠습니까?", {
+					cancelBtnText: "취소",
+					sureBtnText: "확인",
+					sureBtnClick: function() {
+						fnUpdateDay('Holiday' , arg);
+					}
+				});
+				calendar.unselect();
+			},
+
+			//영업일로 변경 -----
+			eventClick: function(arg) {
+				mcxDialog.confirm("영업일로 변경하시겠습니까?", {
+					cancelBtnText: "취소",
+					sureBtnText: "확인",
+					sureBtnClick: function() {
+						fnUpdateDay('Bizday' , arg);
+					}
+				});
+			},
+
+			events: function(info, callback){
+				var dt = setViewDate(info);
+				$('#calendarForm input[name=startDt]').val(dt[0]);
+				$('#calendarForm input[name=endDt]').val(dt[1]);
+				$.get('/system/bizday/list' + '?' + $('#calendarForm').serialize()
+				, function(data) {
+					var events = [];
+					for(var i = 0 ; i < data.length ; i++ ) {
+						events.push( {
+							title : data[i].restdayDesc,
+							start : data[i].bizday } );
+					}
+					callback(events);
+				});
+			}
+		});
+		calendar.render();
+	}
+
+	//해당월 시작일, 마지막일설정
+	var setViewDate = function(date) {
+		var dt = [];
+		dt[0] = (new Date(date.startStr)).format('YYYYMMDD');
+		dt[1] = (new Date(date.endStr)).format('YYYYMMDD');
+		return dt;
+	}
+	
+	// 휴일,영업일 업데이트(저장)
+	var fnUpdateDay = function(day ,arg) {
+		var formId = '#calendarForm';
+		var start="";
+		var end = "";
+		$('#calendarForm input[name=bizDay]').val("");
+		if( day == 'Holiday' ) {
+			bizday = (new Date(arg.start)).format('YYYYMMDD');
+			$('#calendarForm input[name=bizday]').val(bizday);
+			gagajf.ajaxFormSubmit('system/bizday/save', formId, fnSaveCallback);
+				calendar.addEvent({
+					title: '휴일',
+					start: arg.start,
+					end: arg.end,
+					allDay: arg.allDay
+				});
+		}
+		else {
+			bizday = (new Date(arg.event.start)).format('YYYYMMDD');
+			$('#calendarForm input[name=bizday]').val(bizday);
+			gagajf.ajaxFormSubmit('system/bizday/save', formId, fnSaveCallback);
+			arg.event.remove();
+		}
+	}
+
+	// 저장 후 콜백
+	fnSaveCallback = function() {
+	}
+	
+/*]]>*/
+</script>
+
+</html>

+ 206 - 0
style24.admin/src/main/webapp/WEB-INF/views/system/ClauseDetailForm.html

@@ -0,0 +1,206 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : ClauseDetailForm.html
+ * @desc    :  약관관리상세 Page
+ *=============================================================
+ * SISUN
+ * Copyright(C) 2019 TSIT, All rights reserved.
+ *=============================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  ==================================
+ * 1.0  2020.03.09   Daehyoung     최초 작성
+ *******************************************************************************
+ -->
+	<ul class="popup modal" id="popupClauseDetail" data-width="1200" data-height="800">
+		<li class="mdPopTitle">
+			<strong>약관상세</strong>
+ 			<button type="button" id="closeBtn" class="close" onclick="uifnPopClose('popupClauseDetail')"><i class="fa fa-times" aria-hidden="true"></i></button>
+		</li>
+		<li class="mdPopContent">
+			<form id="clauseDetailForm" name="clauseDetailForm" action="#" >
+				<div class="cardArea" style="height: 620px;">
+					<table class="frmStyle" aria-describedby="상세">
+					<colgroup>
+						<col style="width:15%;"/>
+						<col style="width:25%;"/>
+						<col style="width:15%;"/>
+						<col style="width:45%;"/>
+					</colgroup>
+					<tbody>
+						<tr>
+							<th>약관일련번호</th>
+							<td>
+								<input type="text" name="clauseSq" id="clauseSq" maxlength="20" readonly="readonly"/>
+							</td>
+							<th>사이트</th>
+							<td>
+								<select name="siteCd" id="siteCd">
+									<option th:if="${siteList}" th:each="oneData, status : ${siteList}" th:value="${oneData.cd}" th:text="|[${oneData.cd}] ${oneData.cdNm}|"></option>
+								</select>
+							</td>
+						</tr>
+						<tr>
+							<th>약관유형</th>
+							<td>
+								<select name="clauseType" id="clauseType">
+									<option th:if="${clauseTypeList}" th:each="oneData, status : ${clauseTypeList}" th:value="${oneData.cd}" th:text="|[${oneData.cd}] ${oneData.cdNm}|"></option>
+								</select>
+							</td>
+							<th>표시여부</th>
+							<td>
+								<label><input type="checkbox" name="chkYn" value="Y"/>표시</label>
+								<span class="cBlue" style="font-size:11px;"><i class="fa fa-info-circle" aria-hidden="true"></i> 표시여부를 체크 할 경우, 현재  약관으로 노출됩니다.</span>
+								<input type="hidden" name="dispYn" id="dispYn"/>
+							</td>
+						</tr>
+						<tr id="dateTableRow">
+							<th>등록일시</th>
+							<td>
+								<input type="text" class="" name="regDt" id="regDt" placeholder="" maxlength="20" readonly="readonly"/>
+							</td>
+							<th>수정일시</th>
+							<td>
+								<input type="text" class="" name="updDt" id="updDt" placeholder="" maxlength="20" readonly="readonly"/>
+							</td>
+						</tr>
+						<tr>
+							<th>약관 제목<i class="star" aria-hidden="true"></i></th>
+							<td colspan="3">
+								<input type="text" name="clauseTitle" id="clauseTitle" placeholder="" maxlength="20"/>
+							</td>
+						</tr>
+						<tr>
+							<th>약관 내용<i class="star" aria-hidden="true"></i></th>
+							<td colspan="3">
+								<textarea class="textareaR4" name="clauseContent" id="clauseContent" style="height:350px;"></textarea>
+							</td>
+						</tr>
+						</tbody>
+					</table>
+				</div>
+			</form>
+		</li>
+		<li class="mdPopBtnB btnRight">
+			<button type="button" class="btn btn-success btn-lg" id="btnSave">저장</button>
+		</li>
+	</ul>
+	
+<script type="text/javascript" src="/smartEditor/js/HuskyEZCreator.js?v=2019122801" charset="utf-8"></script>
+<script type="text/javascript" src="/ux/plugins/gaga/gaga.smarteditor.js?v=2019122801"></script>
+<script th:inline="javascript">
+/*<![CDATA[*/
+	
+	var mode = [[${mode}]];	
+	var clauseInfo;
+	
+	$('#btnSave').on('click', function() {
+		if(mode == "N"){
+			fnRegist();
+		} else {
+			fnUpdate();
+		}
+	});
+	
+	// 약관 등록
+	var fnRegist = function() {
+		gagaSe.getContents('clauseContent');
+		
+		var clauseTitle = $('#clauseTitle').val();
+		var clauseContent = $('#clauseContent').val();
+		
+		if (gagajf.isNull(clauseTitle)) {
+			mcxDialog.alert("약관 제목을 입력해주세요.");
+			return;
+		}
+		if (gagajf.isNull(clauseContent)) {
+			mcxDialog.alert("약관 내용을 입력해주세요.");
+			return;
+		}
+		$('#clauseDetailForm input[name=dispYn]').val($('#clauseDetailForm input:checkbox[name=chkYn]').is(":checked") ? 'Y' : 'N');
+		
+		mcxDialog.confirm("등록하시겠습니까?", {
+			cancelBtnText: "취소",
+			sureBtnText: "확인",
+			sureBtnClick: function() {
+				var jsonData = JSON.stringify($('#clauseDetailForm').serializeObject());
+				gagajf.ajaxJsonSubmit('/system/clause/detail/create', jsonData, saveCallback);
+			}
+		});
+	}
+	
+	// 약관 수정
+	var fnUpdate = function() {
+		gagaSe.getContents('clauseContent');
+		
+		var clauseTitle = $('#clauseTitle').val();
+		var clauseContent = $('#clauseContent').val();
+		
+		if (gagajf.isNull(clauseTitle)) {
+			mcxDialog.alert("약관 제목을 입력해주세요.");
+			return;
+		}
+		if (gagajf.isNull(clauseContent)) {
+			mcxDialog.alert("약관 내용을 입력해주세요.");
+			return;
+		}
+		$('#clauseDetailForm input[name=dispYn]').val($('#clauseDetailForm input:checkbox[name=chkYn]').is(":checked") ? 'Y' : 'N');
+		
+		mcxDialog.confirm("수정하시겠습니까?", {
+			cancelBtnText: "취소",
+			sureBtnText: "확인",
+			sureBtnClick: function() {
+				var jsonData = JSON.stringify($('#clauseDetailForm').serializeObject());
+				gagajf.ajaxJsonSubmit('/system/clause/detail/update', jsonData, saveCallback);
+			}
+		});
+	}
+	
+	// 저장 콜백
+	var saveCallback = function() {
+		$('#closeBtn').trigger('click');
+		fnSearch();
+	}	
+	
+	var seOptions = gagaSe.getEditorOptions();
+	// 초기세팅
+	var fnInit = function(mode){
+		gagaSe.createSmartEditor(seOptions, 'clauseContent');
+		if(mode == "N"){
+			// 등록
+			$('#clauseSq').val('자동생성');
+			$('#dateTableRow').remove();
+			
+		} else {
+			// 수정
+			clauseInfo = [[${admClause}]];
+			
+			$('#clauseSq').val(clauseInfo.clauseSq);
+			$('#siteCd').val(clauseInfo.siteCd);
+			$('#clauseType').val(clauseInfo.clauseType);
+			
+			$('#regDt').val(clauseInfo.regDt.toDate("YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss"));
+			$('#updDt').val(clauseInfo.updDt.toDate("YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss"));
+			
+			$('#clauseTitle').val(clauseInfo.clauseTitle);
+			
+			if(clauseInfo.dispYn == 'Y'){
+				$('input[type=checkbox]').attr('checked', true);
+			} else {
+				
+			}
+			gagaSe.setContents('clauseContent', clauseInfo.clauseContent); //공지내용
+			
+		}
+	}
+	
+	$(document).ready(function() {
+		fnInit(mode);
+	});
+	
+/*]]>*/
+</script>
+
+</html>

+ 147 - 0
style24.admin/src/main/webapp/WEB-INF/views/system/ClauseListForm.html

@@ -0,0 +1,147 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : ClauseListForm.html
+ * @desc    : 약관관리 화면 Page
+ *=============================================================
+ * Wivismall
+ * Copyright(C) 2019 TSIT, All rights reserved.
+ *=============================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  ==================================
+ * 1.0  2020.03.09  Daehyoung   최초 작성
+ *******************************************************************************
+ -->
+	<div id="main">
+		<div class="main-title">
+		</div>
+
+		<div class="panelStyle2">
+			<form id="searchForm" name="searchForm" action="#" th:action="@{'/system/clause/list'}" onsubmit="$('#btnSearch').trigger('click'); return false;">
+				<table class="frmStyle" aria-describedby="검색조건">
+					<colgroup>
+						<col style="width:15%;"/>
+						<col style="width:35%;"/>
+						<col style="width:15%;"/>
+						<col style="width:35%;"/>
+					</colgroup>
+					<tbody>
+						<tr>
+							<th class="dashR">사이트</th>
+							<td class="dashR">
+								<select name="siteCd">
+									<option th:if="${siteList}" th:each="oneData, status : ${siteList}" th:value="${oneData.cd}" th:text="|[${oneData.cd}] ${oneData.cdNm}|"></option>
+								</select>
+							</td>
+							<th class="dashR">약관유형</th>
+							<td class="dashR">
+								<select name="clauseType">
+									<option value="">전체</option>
+									<option th:if="${clauseTypeList}" th:each="oneData, status : ${clauseTypeList}" th:value="${oneData.cd}" th:text="|[${oneData.cd}] ${oneData.cdNm}|"></option>
+								</select>
+							</td>
+						</tr>
+					</tbody>
+				</table>
+				<div class="panelBtnB">
+					<input type="button" value="초기화" class="btn btn-gray btn-lg" id="btnReset" onclick="$('#searchForm')[0].reset();"/>
+					<input type="button" value="조회" class="btn btn-base btn-lg" id="btnSearch" onclick="fnSearch();"/>
+				</div>
+			</form>
+		</div>
+		<!-- 리스트 영역 -->
+		<div class="panelStyle2">
+			<ul class="lrStyle">
+				<li>
+					<span class="btnLeft">
+						<p class="infoTxt cBlue"><i class="fa fa-info-circle" aria-hidden="true"></i>표시여부를 체크하시면 약관유형별 가장 최근 약관으로 프론트에 노출됩니다.</p>
+					</span>
+				</li>
+				<li class="aR">
+					<button type="button" id="btnRegistForm" class="btn btn-success btn-lg btnRight" onclick="btnRegistForm();">등록</button>
+				</li>
+			</ul>
+			<div class="panelContent" style="overflow: hidden;">
+				<div id="gridList" style="width:100%;" class="ag-theme-balham"></div>
+			</div>
+		</div>
+		
+	</div>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	// 사이트목록
+	var siteList = cfnConvertToArray([[${siteList}]]);
+	var clauseTypeList = cfnConvertToArray([[${clauseTypeList}]]);
+	
+	var columnDefs = [
+		{headerName: "약관번호",		field:'clauseSq',		width:80,	cellClass: 'text-center'},
+		{
+			headerName: "사이트",		field:'siteCd',			width:150,	cellClass: 'text-center', 
+			valueFormatter: function (params) { return gagaAgGrid.lookupValue(siteList, params.value); }
+		},
+		{
+			headerName: "약관유형",	field:'clauseType',	width:200,	cellClass: 'text-center',
+			valueFormatter: function (params) { return gagaAgGrid.lookupValue(clauseTypeList, params.value); }
+		},
+		{
+			headerName: "약관제목",		field:'clauseTitle',	width:300,	cellClass: 'text-center',
+			cellRenderer: function(params) { return '<a href="javascript:cfnClauseDetailPopup('+params.data.clauseSq+')">' + params.value + '</a>'; }
+		},
+		{headerName: "표시여부",		field:'dispYn',			width:80,	cellClass: 'text-center'},
+		{headerName: "등록자",		field:'regId',			width:120,	cellClass: 'text-center'},
+		{headerName: "등록일시",		field:'regDt',			width:150,	cellClass: 'text-center',
+			cellRenderer: function(params) {
+				return !gagajf.isNull(params.value) ? params.value.toDate("YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss") : '';
+			}	
+		},
+		{headerName: "수정자",		field:'updId',			width:120,	cellClass: 'text-center'},
+		{headerName: "수정일시",		field:'updDt',			width:150,	cellClass: 'text-center',
+			cellRenderer: function(params) {
+				return !gagajf.isNull(params.value) ? params.value.toDate("YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss") : '';
+			}	
+		}
+	];
+
+	var gridOptions = gagaAgGrid.getGridOptions(columnDefs);
+
+	// 셀 클릭 이벤트
+	gridOptions.onCellClicked = function(event) {
+		if (event.colDef.field != 'custNm')
+			return;
+		fnCustDetailPopup(event.data);
+	}
+
+	// 검색
+	var fnSearch = function() {
+		var formId = '#searchForm';
+		gagaAgGrid.fetch($(formId).prop('action'), gridOptions, formId);
+	}
+	// 약관등록팝업
+	var btnRegistForm = function(){
+		var actionUrl = '/system/clause/detail/form/N/null';
+		cfnOpenModalPopup(actionUrl, 'popupClauseDetail');
+	}
+	// 약관상세팝업
+	var cfnClauseDetailPopup = function(clauseSq){
+		var actionUrl = '/system/clause/detail/form/U/' + clauseSq;
+		cfnOpenModalPopup(actionUrl, 'popupClauseDetail');
+	}
+	
+	$(document).ready(function() {
+		cfnCreateCalendar('#terms', 'regStdt', 'regEddt', false);
+		
+		gagaAgGrid.createGrid('gridList', gridOptions);
+		
+		// Grid 높이 자동 조정
+		uifnFitGrid('auto');
+		
+		fnSearch();
+	});
+
+/*]]>*/
+</script>
+
+</html>

+ 247 - 0
style24.admin/src/main/webapp/WEB-INF/views/system/CommoncodeForm.html

@@ -0,0 +1,247 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : CommoncodeForm.html
+ * @desc    : 공통코드관리 Page
+ *============================================================================
+ * STYLE24
+ * Copyright(C) 2020 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.10.08   gagamel     최초 작성
+ *******************************************************************************
+ -->
+	<div id="main">
+		<!-- 메인타이틀 영역 -->
+		<div class="main-title">
+		</div>
+		<!-- //메인타이틀 영역 -->
+		
+		<!-- 검색조건 영역 -->
+		<div class="panelStyle">
+			<!-- Search -->
+			<form id="searchForm" name="searchForm" action="#" th:action="@{'/system/commoncode/list'}" onsubmit="$('#btnSearch').trigger('click'); return false;">
+				<input type="hidden" name="cdGb" value="ROOT"/>
+				<ul class="panelBar">
+					<li class="center">
+						<button type="button" class="btn btn-base btn-lg" id="btnSearch">조회</button>
+					</li>
+				</ul>
+			</form>
+		</div>
+		<!-- //검색조건 영역 -->
+		
+		<div class="panelStyle">
+			<!-- 다중 TABLE 배치 -->
+			<ul class="division">
+				<li style="width: 20%">
+					<div class="panelBar">
+						<h4>코드구분</h4>
+					</div>
+					<div id="gridList1" style="width: 100%; height: 570px" class="ag-theme-balham"></div>
+				</li>
+				<li style="width: 50%">
+					<div class="panelBar">
+						<h4>상세코드</h4>
+					</div>
+					<div id="gridList2" style="width: 100%; height: 570px" class="ag-theme-balham"></div>
+				</li>
+				<li style="width: 30%">
+					<div class="panelBar">
+						<h4>코드정보</h4>
+					</div>
+					<form id="detailForm" name="detailForm" action="#" th:action="@{'/system/commoncode/save'}">
+						<table class="frmStyle" aria-describedby="등록/상세">
+							<colgroup>
+								<col style="width:30%;"/>
+								<col/>
+							</colgroup>
+							<tr>
+								<th>코드구분<i class="required" title="필수" aria-hidden="true"></i></th>
+								<td>
+									<input type="text" name="cdGb" class="w200" placeholder="" maxlength="4" required="required" data-valid-type="alphaNumeric" data-valid-name="코드구분" onkeyup="$(this).val($(this).val().toUpperCase());"/>
+								</td>
+							</tr>
+							<tr>
+								<th>코드<i class="required" title="필수" aria-hidden="true"></i></th>
+								<td>
+									<input type="text" name="cd" class="w200" placeholder="" maxlength="20" required="required" data-valid-name="코드"/>
+								</td>
+							</tr>
+							<tr>
+								<th>코드명<i class="required" title="필수" aria-hidden="true"></i></th>
+								<td>
+									<input type="text" name="cdNm" class="w200" placeholder="" maxlength="50" required="required" data-valid-name="코드명"/>
+								</td>
+							</tr>
+							<tr>
+								<th>코드설명</th>
+								<td>
+									<textarea class="textareaR4" name="cdDesc"></textarea>
+								</td>
+							</tr>
+							<tr>
+								<th>표시순서<i class="required" title="필수" aria-hidden="true"></i></th>
+								<td>
+									<input type="text" class="w100 text-right" name="dispOrd" placeholder="" maxlength="5" required="required" data-valid-type="numeric" data-valid-name="표시순서" />
+								</td>
+							</tr>
+							<tr>
+								<th>사용여부<i class="required" title="필수" aria-hidden="true"></i></th>
+								<td>
+									<input type="hidden" name="useYn"/>
+									<label class="chkBox"><input type="checkbox" name="chkUseYn" value="Y"/>사용</label>
+								</td>
+							</tr>
+						</table>
+					</form>
+					
+					<!-- 버튼 배치 영역 -->
+					<ul class="panelBar">
+						<li class="right">
+							<button type="button" class="btn btn-info btn-lg" id="btnNew">신규</button>
+							<input type="button" value="저장" class="btn btn-base btn-lg" id="btnSave"/>
+						</li>
+					</ul>
+					<!-- //버튼 배치 영역 -->
+				</li>
+			</ul>
+			<!--//다중 TABLE 배치 -->
+		</div>
+	</div>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	// specify the columns
+	let columnDefs1 = [
+// 		{width: 40, minWidth: 40, cellClass: 'text-center', headerCheckboxSelection: true, checkboxSelection: true, filter: false},
+// 		{headerName: "CRUD", field: "crud", width: 75, minWidth: 75, hide: true},
+		{
+			headerName: "코드구분", field: "cd", width: 80, cellClass: 'text-center',
+			cellRenderer: function(params) {
+				return '<a href="javascript:void(0);">' + params.value + '</button>';
+			}
+		},
+		{headerName: "코드구분명", field: "cdNm", width: 150},
+// 		{headerName: "코드설명", field: "cdDesc", width: 300},
+// 		{headerName: "표시순서", field: "dispOrd", width: 80, cellClass: 'text-right'},
+// 		{headerName: "사용여부", field: "useYn", width: 80, cellClass: 'text-center'}
+	];
+	
+	let columnDefs2 = [
+// 		{width: 40, minWidth: 40, cellClass: 'text-center', headerCheckboxSelection: true, checkboxSelection: true, filter: false},
+// 		{headerName: "CRUD", field: "crud", width: 75, minWidth: 75, hide: true},
+		{headerName: "코드구분", field: "cdGb", width: 80, cellClass: 'text-center'},
+		{
+			headerName: "코드", field: "cd", width: 80, cellClass: 'text-center',
+			cellRenderer: function(params) {
+				return '<a href="javascript:void(0);">' + params.value + '</button>';
+			}
+		},
+		{headerName: "코드명", field: "cdNm", width: 150},
+		{headerName: "코드설명", field: "cdDesc", width: 250},
+		{headerName: "표시순서", field: "dispOrd", width: 80, cellClass: 'text-right'},
+		{headerName: "사용여부", field: "useYn", width: 80, cellClass: 'text-center'}
+	];
+	
+	// Get GridOptions
+	let gridOptions1 = gagaAgGrid.getGridOptions(columnDefs1);
+	let gridOptions2 = gagaAgGrid.getGridOptions(columnDefs2);
+	
+	// 중복 선택 가능
+// 	gridOptions1.rowSelection = 'multiple';
+// 	gridOptions2.rowSelection = 'multiple';
+
+	// Cell Click
+	gridOptions1.onCellClicked = function(event) {
+		if (event.colDef.field != 'cd')
+			return;
+
+		fnBindDetail(event.data, 1);
+	}
+	
+	gridOptions2.onCellClicked = function(event) {
+		if (event.colDef.field != 'cd')
+			return;
+
+		fnBindDetail(event.data, 2);
+	}
+	
+	// 목록 > row 클릭 시
+	let lvl = 1;
+	let fnBindDetail = function(rowData, flag) {
+		lvl = flag;
+		
+		$('#detailForm input[name=cdGb]').val(rowData.cdGb);
+		$('#detailForm input[name=cd]').val(rowData.cd);
+		$('#detailForm input[name=cdNm]').val(rowData.cdNm);
+		$('#detailForm input[name=cdDesc]').val(rowData.cdDesc);
+		$('#detailForm input[name=dispOrd]').val(rowData.dispOrd);
+		
+		$("#detailForm input[name=useYn]").val(rowData.useYn);
+		if (rowData.useYn == 'Y') {
+			$("#detailForm input:checkbox[name=chkUseYn]").prop('checked', true);
+			$("#detailForm input:checkbox[name=chkUseYn]").parent().addClass('checked');
+		} else {
+			$("#detailForm input:checkbox[name=chkUseYn]").prop('checked', false);
+			$("#detailForm input:checkbox[name=chkUseYn]").parent().removeClass('checked');
+		}
+		
+		if (flag == 1) {
+			fnGetDetailCode(rowData.cd);
+		}
+	}
+	
+	// 코드구분 조회
+	$('#btnSearch').on('click', function() {
+		let actionUrl = $('#searchForm').prop('action') + '?' + $('#searchForm').serialize();
+		
+		// Fetch data
+		gagaAgGrid.fetch(actionUrl, gridOptions1);
+	});
+	
+	// 상세코드 조회
+	let gCd = '';
+	let fnGetDetailCode = function(cd) {
+		gCd = cd;
+		let actionUrl = $('#searchForm').prop('action') + '?cdGb=' + cd;
+		gagaAgGrid.fetch(actionUrl, gridOptions2);
+	}
+	
+	// 신규
+	$('#btnNew').on('click', function() {
+		$('#detailForm')[0].reset();
+		$('#detailForm input:checkbox[name=chkUseYn]').attr('checked', true);
+		$('#detailForm input:checkbox[name=chkUseYn]').attr('disabled', true);
+		$('#detailForm input[name=cdGb]').focus();
+	});
+	
+	// 저장
+	$('#btnSave').on('click', function() {
+		// 입력 값 체크
+		if (!gagajf.validation('#detailForm'))
+			return false;
+		
+		$('#detailForm input[name=useYn]').val($('#detailForm input:checkbox[name=chkUseYn]').is(":checked") ? 'Y' : 'N');
+		
+		gagajf.ajaxFormSubmit('/system/commoncode/save', '#detailForm', function(result) {
+			if (lvl == 1) {
+				$('#btnSearch').trigger('click');
+			} else {
+				fnGetDetailCode(gCd);
+			}
+		});
+	});
+	
+	$(document).ready(function() {
+		// Create a agGrid
+		gagaAgGrid.createGrid('gridList1', gridOptions1);
+		gagaAgGrid.createGrid('gridList2', gridOptions2);
+	});
+/*]]>*/
+</script>
+
+</html>

+ 229 - 0
style24.admin/src/main/webapp/WEB-INF/views/system/EnvsetHistoryForm.html

@@ -0,0 +1,229 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : EnvsetHistoryForm.html
+ * @desc    : 환경설정이력 팝업 Page
+ *============================================================================
+ * Wivismall
+ * Copyright(C) 2019 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.03.16   gagamel     최초 작성
+ *******************************************************************************
+ -->
+<ul class="popup modal" data-width="1200">
+	<li class="mdPopTitle">
+		<strong th:text="${envsetTypeNm + ' 이력'}">쇼핑몰Meat정보 이력</strong>
+		<button type="button" class="close" onclick="uifnPopClose('popupEnvset');"><i class="fa fa-times" aria-hidden="true"></i></button>
+	</li>
+
+	<li class="mdPopContent">
+		<div class="panelContent" style="overflow: hidden;">
+			<div id="gridEnvsetHistoryList" style="width: 100%; height: 330px;" class="ag-theme-balham"></div>
+		</div>
+	</li>
+</ul>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	var envsetType = [[${envsetType}]];
+	var pointGiveType = { "A":"승인후지급", "D":"즉시지급" };
+	var pointUseType = { "A":"정액", "R":"정율" };
+
+	// 쇼핑몰Meta정보
+	var columnDefsB10 = [
+		{headerName: 'No', width: 60, cellClass: 'text-center', valueGetter: function(params) { return params.node.rowIndex + 1 }},
+		{headerName: "웹브라우저Title", field: "strSetVal1", width: 200},
+		{headerName: "쇼핑몰Title(og:title)", field: "strSetVal2", width: 200},
+		{headerName: "쇼핑몰설명(og:description)", field: "strSetVal3", width: 200},
+		{headerName: "쇼핑몰이미지(og:image)", field: "strSetVal4", width: 200},
+		{headerName: "키워드(Keywords)", field: "strSetVal5", width: 200},
+		{headerName: "파비콘이미지", field: "strSetVal6", width: 200},
+		{headerName: "등록자ID", field: "regId", width: 150, cellClass: 'text-center'},
+		{headerName: "등록일시", field: "regDt", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }}
+	];
+
+	// 기본설정
+	var columnDefsB11 = [
+		{headerName: 'No', width: 60, cellClass: 'text-center', valueGetter: function(params) { return params.node.rowIndex + 1 }},
+		{headerName: "탈퇴후재가입불가기간(월)", field: "numSetVal1", width: 150, cellClass: 'text-center'},
+		{headerName: "휴면회원선정기간(일)", field: "numSetVal2", width: 150, cellClass: 'text-center'},
+		{headerName: "휴면회원전환기간(일)", field: "numSetVal3", width: 150, cellClass: 'text-center'},
+		{headerName: "회원등급산정기간(월)", field: "numSetVal4", width: 150, cellClass: 'text-center'},
+		{headerName: "무료배송비최소주문금액", field: "numSetVal5", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toAddComma(params.value);}},
+		{headerName: "등록자ID", field: "regId", width: 100, cellClass: 'text-center'},
+		{headerName: "등록일시", field: "regDt", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }}
+	];
+
+	// 회원혜택안내
+	var columnDefsB12 = [
+		{headerName: 'No', width: 60, cellClass: 'text-center', valueGetter: function(params) { return params.node.rowIndex + 1 }},
+		{headerName: "가입혜택안내배너(A)", field: "strSetVal1", width: 180},
+		{headerName: "가입혜택안내배너(B)", field: "strSetVal2", width: 180},
+		{headerName: "회원혜택안내배너", field: "strSetVal3", width: 180},
+		{headerName: "상품평혜택안내배너(상품상세)", field: "strSetVal4", width: 210},
+		{headerName: "상품평혜택안내배너(마이페이지)", field: "strSetVal5", width: 210},
+		{headerName: "등록자ID", field: "regId", width: 100, cellClass: 'text-center'},
+		{headerName: "등록일시", field: "regDt", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }}
+	];
+
+	// 상품노출
+	var columnDefsB13 = [
+		{headerName: 'No', width: 60, cellClass: 'text-center', valueGetter: function(params) { return params.node.rowIndex + 1 }},
+		{headerName: "상품평노출여부", field: "strSetVal1", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return params.value == 'Y' ? '노출' : '미노출'; }},
+		{headerName: "품절상품노출여부", field: "strSetVal2", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return params.value == 'Y' ? '노출' : '미노출'; }},
+		{headerName: "특가세일노출기준", field: "numSetVal1", width: 150, cellClass: 'text-center'},
+		{headerName: "등록자ID", field: "regId", width: 100, cellClass: 'text-center'},
+		{headerName: "등록일시", field: "regDt", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }}
+	];
+
+	// 상품보관
+	var columnDefsB17 = [
+		{headerName: 'No', width: 60, cellClass: 'text-center', valueGetter: function(params) { return params.node.rowIndex + 1 }},
+		{headerName: "장바구니보관기간", field: "numSetVal1", width: 150, cellClass: 'text-center'},
+		{headerName: "위시리스트보관기간", field: "numSetVal2", width: 150, cellClass: 'text-center'},
+		{headerName: "등록자ID", field: "regId", width: 100, cellClass: 'text-center'},
+		{headerName: "등록일시", field: "regDt", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }}
+	];
+
+	// 품절안내
+	var columnDefsB14 = [
+		{headerName: 'No', width: 60, cellClass: 'text-center', valueGetter: function(params) { return params.node.rowIndex + 1 }},
+		{headerName: "품절안내방법", field: "strSetVal1", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return params.value == 'A' ? '발송' : '미발송'; }},
+		{headerName: "품절안내메시지발송기간", field: "numSetVal1", width: 150, cellClass: 'text-center'},
+		{headerName: "등록자ID", field: "regId", width: 100, cellClass: 'text-center'},
+		{headerName: "등록일시", field: "regDt", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }}
+	];
+
+	// 배송/구매확정
+	var columnDefsB15 = [
+		{headerName: 'No', width: 60, cellClass: 'text-center', valueGetter: function(params) { return params.node.rowIndex + 1 }},
+		{headerName: "배송완료처리기간", field: "numSetVal1", width: 150, cellClass: 'text-center'},
+		{headerName: "구매확정처리기간", field: "numSetVal2", width: 150, cellClass: 'text-center'},
+		{headerName: "등록자ID", field: "regId", width: 100, cellClass: 'text-center'},
+		{headerName: "등록일시", field: "regDt", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }}
+	];
+
+	// 회원쿠폰
+	var columnDefsC10 = [
+		{headerName: 'No', width: 60, cellClass: 'text-center', valueGetter: function(params) { return params.node.rowIndex + 1 }},
+		{headerName: "가입축하쿠폰", field: "strSetVal1", width: 150, cellClass: 'text-center'},
+		{headerName: "앱푸쉬수신동의쿠폰", field: "strSetVal2", width: 150, cellClass: 'text-center'},
+		{headerName: "생일축하쿠폰", field: "strSetVal2", width: 150, cellClass: 'text-center'},
+		{headerName: "생일쿠폰발급도래시점", field: "numSetVal1", width: 150, cellClass: 'text-center'},
+		{headerName: "등록자ID", field: "regId", width: 100, cellClass: 'text-center'},
+		{headerName: "등록일시", field: "regDt", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }}
+	];
+
+	// 회원포인트
+	var columnDefsP10 = [
+		{headerName: 'No', width: 60, cellClass: 'text-center', valueGetter: function(params) { return params.node.rowIndex + 1 }},
+		{headerName: "가입축하기본포인트", field: "numSetVal1", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toAddComma(params.value); }},
+		{headerName: "가입축하추가포인트", field: "numSetVal6", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toAddComma(params.value); }},
+		{headerName: "생일축하포인트", field: "numSetVal2", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toAddComma(params.value); }},
+		{headerName: "앱설치후최초로그인포인트", field: "numSetVal3", width: 200, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toAddComma(params.value); }},
+		{headerName: "출석체크1일포인트", field: "numSetVal4", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toAddComma(params.value); }},
+		{headerName: "등록자ID", field: "regId", width: 100, cellClass: 'text-center'},
+		{headerName: "등록일시", field: "regDt", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }}
+	];
+
+	// 구매포인트
+	var columnDefsP11 = [
+		{headerName: 'No', width: 60, cellClass: 'text-center', valueGetter: function(params) { return params.node.rowIndex + 1 }},
+		{headerName: "자사상품정상포인트(PC)", field: "numSetVal1", width: 150, cellClass: 'text-center'},
+		{headerName: "자사상품정상포인트(모바일)", field: "numSetVal2", width: 150, cellClass: 'text-center'},
+		{headerName: "자사상품이월포인트(PC)", field: "numSetVal3", width: 150, cellClass: 'text-center'},
+		{headerName: "자사상품이월포인트(모바일)", field: "numSetVal4", width: 150, cellClass: 'text-center'},
+		{headerName: "입점상품정상포인트(PC)", field: "numSetVal5", width: 150, cellClass: 'text-center'},
+		{headerName: "입점상품정상포인트(모바일)", field: "numSetVal6", width: 150, cellClass: 'text-center'},
+		{headerName: "입점상품이월포인트(PC)", field: "numSetVal7", width: 150, cellClass: 'text-center'},
+		{headerName: "입점상품이월포인트(모바일)", field: "numSetVal8", width: 150, cellClass: 'text-center'},
+		{headerName: "등록자ID", field: "regId", width: 100, cellClass: 'text-center'},
+		{headerName: "등록일시", field: "regDt", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }}
+	];
+
+	// 상품평포인트
+	var columnDefsP12 = [
+		{headerName: 'No', width: 60, cellClass: 'text-center', valueGetter: function(params) { return params.node.rowIndex + 1 }},
+		{headerName: "포인트지급방법", field: "strSetVal1", width: 150, cellClass: 'text-center',
+			valueFormatter: function (params) { return gagaAgGrid.lookupValue(pointGiveType, params.value); }
+		},
+		{headerName: "텍스트상품평포인트", field: "numSetVal1", width: 150, cellClass: 'text-center'},
+		{headerName: "포토상품평포인트", field: "numSetVal2", width: 150, cellClass: 'text-center'},
+		{headerName: "최초텍스트상품평배율", field: "numSetVal3", width: 150, cellClass: 'text-center'},
+		{headerName: "최초포토상품평포인트배율", field: "numSetVal4", width: 150, cellClass: 'text-center'},
+		{headerName: "등록자ID", field: "regId", width: 100, cellClass: 'text-center'},
+		{headerName: "등록일시", field: "regDt", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }}
+	];
+
+	// 클레임포인트
+	var columnDefsP13 = [
+		{headerName: 'No', width: 60, cellClass: 'text-center', valueGetter: function(params) { return params.node.rowIndex + 1 }},
+		{headerName: "배송지연보상기준일수", field: "numSetVal1", width: 150, cellClass: 'text-center'},
+		{headerName: "배송지연보상포인트", field: "numSetVal2", width: 150, cellClass: 'text-center'},
+		{headerName: "품절보상기준일수", field: "numSetVal3", width: 150, cellClass: 'text-center'},
+		{headerName: "품절보상포인트", field: "numSetVal4", width: 150, cellClass: 'text-center'},
+		{headerName: "등록자ID", field: "regId", width: 100, cellClass: 'text-center'},
+		{headerName: "등록일시", field: "regDt", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }}
+	];
+
+	// 가용포인트전환
+	var columnDefsP20 = [
+		{headerName: 'No', width: 60, cellClass: 'text-center', valueGetter: function(params) { return params.node.rowIndex + 1 }},
+		{headerName: "가용포인트전환대기일수", field: "numSetVal1", width: 150, cellClass: 'text-center'},
+		{headerName: "가용포인트추가적립율", field: "numSetVal2", width: 150, cellClass: 'text-center'},
+		{headerName: "등록자ID", field: "regId", width: 100, cellClass: 'text-center'},
+		{headerName: "등록일시", field: "regDt", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }}
+	];
+
+	// 포인트사용
+	var columnDefsP30 = [
+		{headerName: 'No', width: 60, cellClass: 'text-center', valueGetter: function(params) { return params.node.rowIndex + 1 }},
+		{headerName: "사용방법", field: "strSetVal1", width: 80, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.lookupValue(pointUseType, params.value); }},
+		{
+			headerName: "사용단위", field: "strSetVal2", width: 100, cellClass: 'text-center',
+			cellRenderer: function (params) {
+				var value = '';
+				if (params.value == '10') { value = '일원'; }
+				else if (params.value == '100') { value = '십원'; }
+				else if (params.value == '1000') { value = '백원'; }
+				return value + '단위절사';
+			}
+		},
+		{headerName: "사용가능보유포인트금액", field: "numSetVal1", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toAddComma(params.value); }},
+		{headerName: "사용가능최소포인트금액", field: "numSetVal2", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toAddComma(params.value); }},
+		{headerName: "사용가능최대포인트율", field: "numSetVal3", width: 150, cellClass: 'text-center'},
+		{headerName: "즉시사용최대포인트금액", field: "numSetVal5", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toAddComma(params.value); }},
+		{headerName: "최소주문제한금액", field: "numSetVal4", width: 120, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toAddComma(params.value); }},
+		{headerName: "등록자ID", field: "regId", width: 100, cellClass: 'text-center'},
+		{headerName: "등록일시", field: "regDt", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }}
+	];
+
+	// 모바일앱
+	var columnDefsM10 = [
+		{headerName: 'No', width: 60, cellClass: 'text-center', valueGetter: function(params) { return params.node.rowIndex + 1 }},
+		{headerName: "AOS버전", field: "strSetVal1", width: 100, cellClass: 'text-center'},
+		{headerName: "IOS버전", field: "strSetVal2", width: 100, cellClass: 'text-center'},
+		{headerName: "등록자ID", field: "regId", width: 100, cellClass: 'text-center'},
+		{headerName: "등록일시", field: "regDt", width: 150, cellClass: 'text-center', cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }}
+	];
+
+	// Get GridOptions
+	var gridOptions = gagaAgGrid.getGridOptions(eval('columnDefs' + envsetType));
+
+	$(document).ready(function() {
+		// Create a agGrid
+		gagaAgGrid.createGrid('gridEnvsetHistoryList', gridOptions);
+
+		var actionUrl = '/system/envset/history/' + [[${siteCd}]] + '/' + envsetType;
+
+		// Fetch data
+		gagaAgGrid.fetch(actionUrl, gridOptions);
+	});
+/*]]>*/
+</script>
+
+</html>

+ 164 - 0
style24.admin/src/main/webapp/WEB-INF/views/system/HilandDeliveryFeeDetailForm.html

@@ -0,0 +1,164 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *****************************************************************************
+ * @source  : HilandDeliveryFeeDetailForm.html
+ * @desc    : 도서산간배송비 상세
+ *============================================================================
+ * WIVIS
+ * Copyright(C) 2019 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.03.16   Daehyoung   최초 작성
+ *****************************************************************************
+ -->
+	<ul class="popup modal" id="hilandDeliveryFeeDetail" data-width="600" >
+		<li class="mdPopTitle">
+			<strong>도서산간 배송비 설정</strong>
+			<button type="button" id="closeBtn" class="close" onclick="uifnPopClose('hilandDeliveryFeeDetail')"><i class="fa fa-times" aria-hidden="true"></i></button>
+		</li>
+		<li class="mdPopContent">
+			<form name="hilandDeliveryFeeDetailForm" id="hilandDeliveryFeeDetailForm" action="#">
+				<input type="hidden" id="originPostFno" name="originPostFno"/>
+				<input type="hidden" id="originPostTno" name="originPostTno"/>
+				
+				<table class="frmStyle" aria-describedby="상세">
+					<colgroup>
+						<col style="width:20%;"/>
+					</colgroup>
+					<tbody>
+						<tr>
+							<th class="dashR">공급업체</th>
+							<td class="dashR">
+								<select id="supplyCompCd" name="supplyCompCd" required="required" data-valid-name="공급업체">
+									<option value="">[선택]</option>
+									<option th:if="${supplyCompList}" th:each="oneData, status : ${supplyCompList}" th:value="${oneData.cd}" th:text="${oneData.cdNm}"></option>
+								</select>
+							</td>
+						</tr>
+						<tr>
+							<th class="dashR">지역명(도서, 산간 등)</th>
+							<td class="dashR">
+								<input type="text" class="w300" id="hilandNm" name="hilandNm" required="required" data-valid-name="지역명"/>
+							</td>
+						</tr>
+						<tr>
+							<th class="dashR">우편번호 범위</th>
+							<td class="dashR">
+								<input type="text" id="postFno" name="postFno"  class="w50"  required="required" data-valid-name="우편번호" readonly="readonly">
+								<button type="button" class="btn btn-info btn-sm" onclick="cfnOpenPostFindPopup('postFno');">우편번호찾기</button>부터 
+								<input type="text" id="postTno" name="postTno"  class="w50"  required="required" data-valid-name="우편번호" readonly="readonly"/>
+								<button type="button" class="btn btn-info btn-sm" onclick="cfnOpenPostFindPopup('postTno');">우편번호찾기</button>까지
+							</td>
+						</tr>
+						<tr>
+							<th class="dashR">추가배송비</th>
+							<td class="dashR">
+								<input type="text" id="addDelvFee" name="addDelvFee" class="w70 text-right" data-valid-type="integer" data-valid-name="추가배송비" required="required"/>원
+							</td>
+						</tr>
+					</tbody>
+				</table>
+			</form>
+		</li>
+		<li class="boxContentBtnB">
+			<button type="button" class="btn btn-success btn-lg " id="btnSave">저장</button>
+		</li>
+	</ul>
+<script th:inline="javascript">
+/*<![CDATA[*/
+	
+	var mode = [[${mode}]];	
+	var admHilandDeliveryFee;
+	
+	$('#btnSave').on('click', function() {
+		var formId = '#hilandDeliveryFeeDetailForm';
+		if (!gagajf.validation(formId))
+			return false;
+		
+		// 우편번호 범위 유효성
+		if ($('#postTno').val() < $('#postFno').val()) {
+			mcxDialog.alert("우편번호 뒷범위가 앞범위보다 크거나 같게 해주세요.");
+			return;
+		}
+		
+		gagajf.removeCommaAtNumberFormattedInput('#hilandDeliveryFeeDetailForm');
+		if(mode == "N"){
+			fnRegist();
+		} else {
+			fnUpdate();
+		}
+	});
+	
+	// 도서산간배송비 등록
+	var fnRegist = function() {
+		mcxDialog.confirm("등록하시겠습니까?", {
+			cancelBtnText: "취소",
+			sureBtnText: "확인",
+			sureBtnClick: function() {
+				var jsonData = JSON.stringify($('#hilandDeliveryFeeDetailForm').serializeObject());
+				gagajf.ajaxJsonSubmit('/system/hiland/deliveryfee/detail/create', jsonData, saveCallback);
+			}
+		});
+	}
+	
+	// 도서산간배송비 수정
+	var fnUpdate = function() {
+		mcxDialog.confirm("수정하시겠습니까?", {
+			cancelBtnText: "취소",
+			sureBtnText: "확인",
+			sureBtnClick: function() {
+				var jsonData = JSON.stringify($('#hilandDeliveryFeeDetailForm').serializeObject());
+				gagajf.ajaxJsonSubmit('/system/hiland/deliveryfee/detail/update', jsonData, saveCallback);
+			}
+		});
+	}
+	
+	// 저장 콜백
+	var saveCallback = function() {
+		$('#closeBtn').trigger('click');
+		fnSearch();
+	}
+	
+	// 초기세팅
+	var fnInit = function(mode){
+		
+		if(mode == "N"){
+			// 등록
+			
+		} else {
+			// 수정
+			admHilandDeliveryFee = [[${admHilandDeliveryFee}]];
+			
+			$('#originPostFno').val(admHilandDeliveryFee.postFno);
+			$('#originPostTno').val(admHilandDeliveryFee.postTno);
+			
+			$('#supplyCompCd').val(admHilandDeliveryFee.supplyCompCd);
+			$('#hilandNm').val(admHilandDeliveryFee.hilandNm);
+			$('#addDelvFee').val(admHilandDeliveryFee.addDelvFee);
+			
+			$('input[name=postFno]').val(admHilandDeliveryFee.postFno);
+			$('input[name=postTno]').val(admHilandDeliveryFee.postTno);
+		}
+		gagajf.addCommaAtNumberFormattedInput('#hilandDeliveryFeeDetailForm');
+	}
+	
+	// 우편번호 콜백 함수
+	var fnSetPostValue = function(zipNo, baseRoadAddr1, baseRoadAddr2, etcAddr, id) {
+		if(id == 'postFno') {
+			$('input[name=postFno]').val(zipNo);
+		}
+		if(id == 'postTno') {
+			$('input[name=postTno]').val(zipNo);
+		}
+	}
+	
+	$(document).ready(function() {
+		fnInit(mode);
+	});
+
+/*]]>*/
+</script>
+</html>

+ 104 - 0
style24.admin/src/main/webapp/WEB-INF/views/system/HilandDeliveryFeeListForm.html

@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org"        >
+<!--
+ *****************************************************************************
+ * @source  : HilandDeliveryFeeListForm.html
+ * @desc    : 도서산간배송비
+ *============================================================================
+ * SISUN
+ * Copyright(C) 2019 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.03.16   Daehyoung       최초 작성
+ *****************************************************************************
+ -->
+	<div id="main">
+		<!-- 메인타이틀 영역 -->
+		<div class="main-title">
+		</div>
+		<!-- //메인타이틀 영역 -->
+		
+		<!-- 검색조건 영역 -->
+		<form id="hilandDeliveryFeeForm" name="hilandDeliveryFeeForm" action="#" th:action="@{'/system/hiland/deliveryfee/list'}" onsubmit="$('#btnSearch').trigger('click'); return false;">
+			<div class="panelStyle2">
+				<ul class="lrStyle">
+					<li>
+						<p class="dot">사이트에서 사용할 도서산간지역을 설정합니다.</p>
+					</li>
+				</ul>
+			</div>
+			<div class="panelStyle2">
+				<ul class="lrStyle">
+					<li class="aR">
+						<button type="button" id="btnRegist" class="btn btn-success btn-lg btnRight">등록</button>
+					</li>
+				</ul>
+				<!-- 검색결과 영역 -->
+				<div class="panelContent">
+					<div id="gridList" style="width: 100%;" class="ag-theme-balham"></div>
+				</div>
+				<!-- 검색결과 영역 -->
+			</div>
+			<!-- //검색조건 영역 -->
+		</form>
+	</div>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	var supplyCompList = cfnConvertToArray([[${supplyCompList}]]);
+	
+	var columnDefs = [
+		//{width: 100, minWidth: 100, cellClass: 'text-center', headerCheckboxSelection: true, checkboxSelection: true, filter: false},
+		{headerName: "공급업체명",			field: "supplyCompCd",	width: 200, cellClass: 'text-center',
+			valueFormatter: function (params) { return gagaAgGrid.lookupValue(supplyCompList, params.value); }
+		},
+		{headerName: "지역명(도서, 산간 등)",	field: "hilandNm",		width: 400, cellClass: 'text-center'},
+		{headerName: "우편번호 범위",		field: "postFno",		width: 350, cellClass: 'text-center',
+			cellRenderer: function(params) {
+				return '[' + params.data.postFno + '] 부터 [' + params.data.postTno + '] 까지';
+			}	
+		},
+		{headerName: "배송비",			field: "addDelvFee",	width: 150, cellClass: 'text-center',
+			valueFormatter: function(params) {return params.value.addComma();}	
+		},
+		{headerName: "관리",				field: "", 				width: 120, cellClass: 'text-center',
+			cellRenderer: function(params) {
+				return '<button type="button" class="btn btn-danger btn-sm" onclick="fnHilandDeliveryDetailPopup(\'' + params.data.postFno + '\', \'' + params.data.postTno + '\');">&nbsp;&nbsp;상세&nbsp;&nbsp;</button>';
+			}
+		}
+	];
+
+	// Get GridOptions
+	var gridOptions = gagaAgGrid.getGridOptions(columnDefs);
+	
+	// 검색
+	var fnSearch = function() {
+		var formId = '#hilandDeliveryFeeForm';
+		gagaAgGrid.fetch($(formId).prop('action'), gridOptions, formId);
+	}
+	
+	// 등록
+	$('#btnRegist').on('click', function() {
+		var actionUrl = '/system/hiland/deliveryfee/detail/N/null/null';
+		cfnOpenModalPopup(actionUrl, 'hilandDeliveryFeeDetail');
+	});
+	
+	// 상세
+	var fnHilandDeliveryDetailPopup = function(postFno, postTno) {
+		var actionUrl = '/system/hiland/deliveryfee/detail/U/' + postFno + '/'+ postTno;
+		cfnOpenModalPopup(actionUrl, 'hilandDeliveryFeeDetail');
+	}
+	
+	$(document).ready(function() {
+		gagaAgGrid.createGrid('gridList', gridOptions);
+		
+		// Grid 높이 자동 조정
+		uifnFitGrid('auto');
+		
+		fnSearch();
+	});
+/*]]>*/
+</script>
+</html>

+ 443 - 0
style24.admin/src/main/webapp/WEB-INF/views/system/MenuForm.html

@@ -0,0 +1,443 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : MenuForm.html
+ * @desc    : 메뉴관리 Page
+ *============================================================================
+ * STYLE24
+ * Copyright(C) 2020 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.10.08   gagamel     최초 작성
+ *******************************************************************************
+ -->
+	<div id="main">
+		<!-- 메인타이틀 영역 -->
+		<div class="main-title">
+		</div>
+		<!-- //메인타이틀 영역 -->
+		
+		<!-- 검색조건 영역 -->
+		<div class="panelStyle">
+			<!-- Search -->
+			<form id="searchForm" name="searchForm" action="#" th:action="@{'/system/menu/list'}" onsubmit="$('#btnSearch').trigger('click'); return false;">
+				<table class="frmStyle" aria-describedby="검색조건">
+					<colgroup>
+						<col style="width:10%;"/>
+						<col style="width:90%;"/>
+					</colgroup>
+					<tr>
+						<th>상위메뉴</th>
+						<td>
+							<select name="pmenuId" class="w150">
+								<option value="">[전체]</option>
+								<option th:if="${topMenuList}" th:each="oneData, status : ${topMenuList}" th:value="${oneData.cd}" th:text="${'[' + oneData.cd + '] ' + oneData.cdNm}"></option>
+							</select>
+						</td>
+					</tr>
+				</table>
+				
+				<ul class="panelBar">
+					<li class="center">
+						<button type="button" class="btn btn-success btn-lg" id="btnSearch">조회</button>
+					</li>
+				</ul>
+			</form>
+		</div>
+		<!-- //검색조건 영역 -->
+		
+		<!-- TABS AREA -->
+		<div class="tabs">
+			<ul class="tabsNav">
+				<li class="on"><a href="#tab-1">목록</a></li>
+				<li><a href="#tab-2">신규</a></li>
+				<li class="off"><a href="#tab-3">상세</a></li>
+			</ul>
+			
+			<ul class="tabsCont">
+				<li class="tab on" id="tab-1">
+					<div class="panelStyle">
+						<!-- 버튼 배치 영역 -->
+						<ul class="panelBar">
+							<li class="right">
+								<button type="button" class="btn btn-danger btn-lg btnDelete">삭제</button>
+							</li>
+						</ul>
+						<!-- //버튼 배치 영역 -->
+						
+						<div id="gridList" style="width: 100%; height: 570px;" class="ag-theme-balham"></div>
+					</div>
+				</li>
+				
+				<!-- 신규 -->
+				<li class="tab" id="tab-2">
+					<div class="panelStyle">
+						<form id="registerForm" name="registerForm" action="#" th:action="@{'/system/menu/save'}">
+							<table class="frmStyle" aria-describedby="신규">
+								<colgroup>
+									<col style="width:10%"/>
+									<col style="width:40%;"/>
+									<col style="width:10%;"/>
+									<col/>
+								</colgroup>
+								<tr>
+									<th>메뉴ID<i class="star" aria-hidden="true"></i></th>
+									<td>
+										<input type="text" name="menuId" placeholder="" maxlength="20" required="required" data-valid-type="alphaNumeric" data-valid-name="메뉴ID" onkeyup="$(this).val($(this).val().toUpperCase());"/>
+									</td>
+									<th>메뉴명<i class="star" aria-hidden="true"></i></th>
+									<td>
+										<input type="text" name="menuNm" placeholder="" maxlength="50" required="required" data-valid-name="메뉴명"/>
+									</td>
+								</tr>
+								<tr>
+									<th>메뉴구분<i class="star" aria-hidden="true"></i></th>
+									<td>
+										<label class="rdoBtn"><input type="radio" name="menuGb" value="M"/>메뉴</label>
+										<label class="rdoBtn"><input type="radio" name="menuGb" value="P"/>프로그램</label>
+									</td>
+									<th>상위메뉴</th>
+									<td>
+										<select name="pmenuId">
+											<option value="">[선택]</option>
+											<option th:if="${allMenuList}" th:each="oneData, status : ${allMenuList}" th:value="${oneData.cd}" th:text="${oneData.cdNm}"></option>
+										</select>
+									</td>
+								</tr>
+								<tr class="menu-role">
+									<th>메뉴권한</th>
+									<td colspan="3">
+										<!-- <select class="chosen-select" name="roleCd" multiple="multiple" data-placeholder="[선택]" data-valid-name="메뉴권한">
+											<option th:if="${roleList}" th:each="oneData, status : ${roleList}" th:value="${oneData.cd}" th:text="${'[' + oneData.cd + '] ' + oneData.cdNm}"></option>
+										</select> -->
+										<div class="mSelectWrap">
+											<select name="roleCd" multiple="multiple" tabindex="-1" data-valid-name="메뉴권한">
+												<option th:if="${roleList}" th:each="oneData, status : ${roleList}" th:attr="data-index=${status.index + 1}" th:value="${oneData.cd}" th:text="${'[' + oneData.cd + '] ' + oneData.cdNm}"></option>
+											</select>
+											<div class="mSelectBox">
+												<ul class="mSelected">
+													<li class="off" th:if="${roleList}" th:each="oneData, status : ${roleList}" th:attr="data-index=${status.index + 1}" th:utext="${'[' + oneData.cd + '] ' + oneData.cdNm + '&lt;a&gt;닫기&lt;/a&gt;'}"></li>
+													<li class="srchFld"><input type="text" autocomplete="off"/></li>
+												</ul>
+												<div class="mSelecting">
+													<ul>
+														<li th:if="${roleList}" th:each="oneData, status : ${roleList}" th:attr="data-index=${status.index + 1}" th:text="${'[' + oneData.cd + '] ' + oneData.cdNm}"></li>
+													</ul>
+												</div>
+											</div>
+										</div>
+										<input type="hidden" name="roleCds"/>
+									</td>
+								</tr>
+								<tr>
+									<th>메뉴URL</th>
+									<td colspan="3">
+										<input type="text" name="menuUrl" placeholder="" maxlength="100"/>
+									</td>
+								</tr>
+								<tr>
+									<th>메뉴설명</th>
+									<td colspan="3">
+										<textarea class="textareaR4" name="menuDesc"></textarea>
+									</td>
+								</tr>
+								<tr>
+									<th>표시순서<i class="star" aria-hidden="true"></i></th>
+									<td>
+										<input type="text" class="w100 text-right" name="dispOrd" placeholder="" maxlength="5" required="required" data-valid-type="numeric" data-valid-name="표시순서" />
+									</td>
+									<th>사용여부</th>
+									<td>
+										<input type="hidden" name="useYn"/>
+										<label class="chkBox"><input type="checkbox" name="chkUseYn" value="Y" checked="checked" disabled="disabled"/>사용</label>
+									</td>
+								</tr>
+							</table>
+						</form>
+						
+						<!-- 버튼 배치 영역 -->
+						<ul class="panelBar">
+							<li class="right">
+								<input type="button" value="초기화" class="btn btn-gray btn-lg" onclick="$('#registerForm')[0].reset();"/>
+								<input type="button" value="저장" class="btn btn-base btn-lg" onclick="fnSave('#registerForm');"/>
+							</li>
+						</ul>
+						<!-- //버튼 배치 영역 -->
+					</div>
+				</li>
+				<!-- //신규 -->
+				
+				<!-- 상세 -->
+				<li class="tab" id="tab-3">
+					<div class="panelStyle">
+						<form id="detailForm" name="detailForm" action="#" th:action="@{'/system/menu/save'}">
+							<table class="frmStyle" aria-describedby="상세">
+								<colgroup>
+									<col style="width:10%"/>
+									<col style="width:40%;"/>
+									<col style="width:10%;"/>
+									<col/>
+								</colgroup>
+								<tr>
+									<th>메뉴ID</th>
+									<td>
+										<input type="text" name="menuId" placeholder="" maxlength="20" readonly="readonly"/>
+									</td>
+									<th>메뉴명<i class="star" aria-hidden="true"></i></th>
+									<td>
+										<input type="text" name="menuNm" placeholder="" maxlength="50" required="required" data-valid-name="메뉴명"/>
+									</td>
+								</tr>
+								<tr>
+									<th>메뉴구분<i class="star" aria-hidden="true"></i></th>
+									<td>
+										<label class="rdoBtn"><input type="radio" name="menuGb" value="M"/>메뉴</label>
+										<label class="rdoBtn"><input type="radio" name="menuGb" value="P"/>프로그램</label>
+									</td>
+									<th>상위메뉴</th>
+									<td>
+										<select name="pmenuId">
+											<option value="">[선택]</option>
+											<option th:if="${allMenuList}" th:each="oneData, status : ${allMenuList}" th:value="${oneData.cd}" th:text="${oneData.cdNm}"></option>
+										</select>
+									</td>
+								</tr>
+								<tr class="menu-role">
+									<th>메뉴권한</th>
+									<td colspan="3">
+										<!-- <select class="chosen-select" name="roleCd" multiple="multiple" data-placeholder="[선택]" data-valid-name="메뉴권한">
+											<option th:if="${roleList}" th:each="oneData, status : ${roleList}" th:value="${oneData.cd}" th:text="${'[' + oneData.cd + '] ' + oneData.cdNm}"></option>
+										</select> -->
+										<div class="mSelectWrap">
+											<select name="roleCd" multiple="multiple" tabindex="-1" data-valid-name="메뉴권한">
+												<option th:if="${roleList}" th:each="oneData, status : ${roleList}" th:attr="data-index=${status.index + 1}" th:value="${oneData.cd}" th:text="${'[' + oneData.cd + '] ' + oneData.cdNm}"></option>
+											</select>
+											<div class="mSelectBox">
+												<ul class="mSelected">
+													<li class="off" th:if="${roleList}" th:each="oneData, status : ${roleList}" th:attr="data-index=${status.index + 1}" th:utext="${'[' + oneData.cd + '] ' + oneData.cdNm + '&lt;a&gt;닫기&lt;/a&gt;'}"></li>
+													<li class="srchFld"><input type="text" autocomplete="off"/></li>
+												</ul>
+												<div class="mSelecting">
+													<ul>
+														<li th:if="${roleList}" th:each="oneData, status : ${roleList}" th:attr="data-index=${status.index + 1}" th:text="${'[' + oneData.cd + '] ' + oneData.cdNm}"></li>
+													</ul>
+												</div>
+											</div>
+										</div>
+										<input type="hidden" name="roleCds"/>
+									</td>
+								</tr>
+								<tr>
+									<th>메뉴URL</th>
+									<td colspan="3">
+										<input type="text" name="menuUrl" placeholder="" maxlength="100"/>
+									</td>
+								</tr>
+								<tr>
+									<th>메뉴설명</th>
+									<td colspan="3">
+										<textarea class="textareaR4" name="menuDesc"></textarea>
+									</td>
+								</tr>
+								<tr>
+									<th>표시순서<i class="star" aria-hidden="true"></i></th>
+									<td>
+										<input type="text" class="w100 text-right" name="dispOrd" placeholder="" maxlength="5" required="required" data-valid-type="numeric" data-valid-name="표시순서" />
+									</td>
+									<th>사용여부<i class="star" aria-hidden="true"></i></th>
+									<td>
+										<input type="hidden" name="useYn"/>
+										<label class="chkBox"><input type="checkbox" name="chkUseYn" value="Y"/>사용</label>
+									</td>
+								</tr>
+							</table>
+						</form>
+						
+						<!-- 버튼 배치 영역 -->
+						<ul class="panelBar">
+							<li class="right">
+								<input type="button" value="초기화" class="btn btn-gray btn-lg" onclick="$('#detailForm')[0].reset();"/>
+								<input type="button" value="저장" class="btn btn-base btn-lg" onclick="fnSave('#detailForm');"/>
+							</li>
+						</ul>
+						<!-- //버튼 배치 영역 -->
+					</div>
+				</li>
+				<!-- //상세 -->
+			</ul>
+			<!-- //TAB CONTENT -->
+		</div>
+		<!-- //TABS AREA -->
+	</div>
+	
+<script th:inline="javascript">
+/*<![CDATA[*/
+	var roleList = [[${roleList}]];
+	
+	// specify the columns
+	var columnDefs = [
+		{headerName: "메뉴명", field: "menuNm", width: 200,
+			cellRenderer: function(params) {
+				return '<a href="javascript:void(0);">' + params.value + '</a>';
+			}
+		},
+		{headerName: "메뉴구분", field: "menuGb", width: 100, cellClass: 'text-center',
+			cellRenderer: function(params) { return params.value == 'M' ? '메뉴' : '프로그램'; }
+			/* valueFormatter: function(params) { return params.value == 'M' ? '메뉴' : '프로그램'; } */
+		},
+		{headerName: "메뉴권한코드", field: "roleCds", hide: true},
+		{headerName: "메뉴권한", field: "roleNms", width: 400, cellClass: 'text-center'},
+		{headerName: "메뉴URL", field: "menuUrl", width: 300},
+		{headerName: "메뉴설명", field: "menuDesc", hide: true},
+		{headerName: "표시순서", field: "dispOrd", width: 100, cellClass: 'text-right'},
+		{headerName: "사용여부", field: "useYn", width: 100, cellClass: 'text-center'}
+	];
+	
+	// Get GridOptions
+	var gridOptions = gagaAgGrid.getGridOptions(columnDefs);
+	
+	// Add on options
+	gridOptions.suppressRowClickSelection = true;
+	gridOptions.rowSelection = 'multiple';
+	//gridOptions.groupSelectsChildren = true; // Tree Data mode에서는 작동 안 함
+	gridOptions.treeData = true; // enable Tree Data mode
+	gridOptions.groupDefaultExpanded = -1; // expand all groups by default
+	gridOptions.getDataPath = function(data) { // just return the hierarchy, no conversion required
+		return data.treePath.split("/");
+	};
+	gridOptions.autoGroupColumnDef = {
+		headerName: "메뉴ID", field: "menuId", width: 200,
+		cellRendererParams: {
+			suppressCount: true, // 하위의 항목 건수 표시 안 함
+			checkbox: true
+		}
+	};
+	
+	// Cell Click
+	gridOptions.onCellClicked = function(event) {
+		if (event.colDef.field != 'menuNm')
+			return;
+
+		fnBindDetail(event.data);
+	}
+	
+	// 조회
+	$('#btnSearch').on('click', function() {
+		var actionUrl = $('#searchForm').prop('action') + '?' + $('#searchForm').serialize();
+		
+		// Fetch data
+		gagaAgGrid.fetch(actionUrl, gridOptions);
+	});
+	
+	// 목록 > row 클릭 시
+	var fnBindDetail = function(rowData) {
+		$('#detailForm input[name=menuId]').val(rowData.menuId);
+		$('#detailForm input[name=menuNm]').val(rowData.menuNm);
+		
+		$("#detailForm input:radio[name=menuGb]").parents('td').find('label').removeClass('checked');
+		if (rowData.menuGb == 'M') {
+			$("#detailForm input:radio[name=menuGb]").eq(0).prop('checked', true);
+			$("#detailForm input:radio[name=menuGb]").eq(0).parent().addClass('checked');
+			$('#detailForm').find('.menu-role').hide();
+		} else {
+			$("#detailForm input:radio[name=menuGb]").eq(1).prop('checked', true);
+			$("#detailForm input:radio[name=menuGb]").eq(1).parent().addClass('checked');
+			$('#detailForm').find('.menu-role').show();
+		}
+		
+		$('#detailForm select[name=pmenuId]').val(rowData.pmenuId);
+		
+		// 다중Select에 권한 표시
+		cfnShowMultiSelectedValue($('#detailForm select[name=roleCd]'), rowData.roleCds);
+		
+		$('#detailForm input[name=menuDesc]').val(rowData.menuDesc);
+		$('#detailForm input[name=menuUrl]').val(rowData.menuUrl);
+		$('#detailForm input[name=dispOrd]').val(rowData.dispOrd);
+		
+		$("#detailForm input[name=useYn]").val(rowData.useYn);
+		if (rowData.useYn == 'Y') {
+			$("#detailForm input:checkbox[name=chkUseYn]").prop('checked', true);
+			$("#detailForm input:checkbox[name=chkUseYn]").parent().addClass('checked');
+		} else {
+			$("#detailForm input:checkbox[name=chkUseYn]").prop('checked', false);
+			$("#detailForm input:checkbox[name=chkUseYn]").parent().removeClass('checked');
+		}
+		
+		$('.tabsNav li').eq(2).removeClass('off');
+		$('.tabsNav li').eq(2).trigger('click');
+		
+		$('#detailForm input[name=menuNm]').focus();
+	}
+	
+	// 저장 처리
+	var fnSave = function(formId) {
+		// 입력 값 체크
+		if (!gagajf.validation(formId))
+			return false;
+		
+		$(formId + ' input[name=useYn]').val($(formId + ' input:checkbox[name=chkUseYn]').is(":checked") ? 'Y' : 'N');
+		$(formId + ' input[name=roleCds]').val($(formId + ' select[name=roleCd]').val());
+		
+		gagajf.ajaxFormSubmit($(formId).prop('action'), formId, fnSaveCallback);
+	}
+	
+	// 등록/수정 처리 후 콜백함수
+	var fnSaveCallback = function(result) {
+		// 초기화
+		$('#registerForm')[0].reset();
+		
+		// 상세의 다중Select 초기화
+		var mSelected = '';
+		var mSelecting = '';
+		$.each(roleList, function(idx, item) {
+			mSelected += '<li class="off" data-index="' + (idx + 1) + '">[' + item.cd + '] ' + item.cdNm + '<a>닫기</a></li>\n';
+			mSelecting += '<li data-index="' + (idx + 1) + '">[' + item.cd + '] ' + item.cdNm + '</li>\n';
+		});
+		mSelected += '<li class="srchFld"><input type="text" autocomplete="off"/></li>\n';
+		$('#detailForm').find('.mSelected').html(mSelected);
+		$('#detailForm').find('.mSelecting > ul').html(mSelecting);
+		
+		// 목록 재조회
+		$('.tabsNav li').eq(0).trigger('click');
+		
+		$('#btnSearch').trigger('click');
+	}
+	
+	// 삭제 버튼 클릭 시
+	$('.btnDelete').on('click', function() {
+		var removedData = gagaAgGrid.removeRowData(gridOptions);
+		
+		if (removedData.length == 0) {
+			alert('선택된 행이 없습니다.');
+			return;
+		}
+		
+		mcxDialog.confirm("정말 삭제하시겠습니까?", {
+			cancelBtnText: "취소",
+			sureBtnText: "확인",
+			sureBtnClick: function() {
+				// delete 대신 update 처리해야 하므로 다음과 같이 useYn 값을 변환
+				var updatedData = [];
+				
+				$.each(removedData, function(idx, item) {
+					item.useYn = 'N';
+					updatedData.push(item);
+				});
+				
+				var jsonData = JSON.stringify(updatedData);
+				gagajf.ajaxJsonSubmit('/system/menu/list/save', jsonData, fnSaveCallback);
+			}
+		});
+	});
+	
+	$(document).ready(function() {
+		// Create a agGrid
+		gagaAgGrid.createGrid('gridList', gridOptions);
+	});
+/*]]>*/
+</script>
+
+</html>

+ 559 - 0
style24.admin/src/main/webapp/WEB-INF/views/system/PointEnvsetForm.html

@@ -0,0 +1,559 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *****************************************************************************
+ * @source  : PointEnvsetForm.html
+ * @desc    : 포인트설정관리 Page
+ *============================================================================
+ * Wivismall
+ * Copyright(C) 2019 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.03.17   Daehyoung     최초 작성
+ *****************************************************************************
+ -->
+
+	<div id="main">
+		<!-- 메인타이틀 영역 -->
+		<div class="main-title">
+		</div>
+		<!-- //메인타이틀 영역 -->
+
+		<!-- 검색조건 영역 -->
+		<div class="panelStyle2">
+			<table class="frmStyle" aria-describedby="검색조건">
+				<colgroup>
+					<col style="width:10%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>사이트</th>
+						<td>
+							<select name="siteCd" class="w150" onchange="fnSearch();">
+								<option th:if="${siteList}" th:each="oneData, status : ${siteList}" th:value="${oneData.cd}" th:text="${'[' + oneData.cd + '] ' + oneData.cdNm}"></option>
+							</select>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+			<div class="panelBtnB"></div>
+		</div>
+		<!-- //검색조건 영역 -->
+		<!-- 테이블 영역 -->
+		<div class="panelStyle2">
+			<h4>회원쿠폰</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('C10');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('C10', '회원쿠폰');">이력보기</button>
+			<table class="frmStyle" aria-describedby="회원쿠폰">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>가입축하쿠폰</th>
+						<td>
+							<input name="c10StrSetNm1" type="text" class="w300" onkeypress="if (window.event.keyCode == 13) { fnOpenCouponRetrievePopup('input[name=c10StrSetVal1]', 'input[name=c10StrSetNm1]'); }"/>
+							<button type="button" class="btn icn" onclick="fnOpenCouponRetrievePopup('input[name=c10StrSetVal1]', 'input[name=c10StrSetNm1]');"><i class="fa fa-search cpn" aria-hidden="true"></i></button>
+							<input name="c10StrSetVal1" type="text" class="w100" maxlength="20" readonly/>
+							<button type="button" class="btn icn" onclick="$('input[name=c10StrSetVal1], input[name=c10StrSetNm1]').val('');"><i class="fa fa-eraser" aria-hidden="true"></i></button>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>회원가입 시 발급해 줄 쿠폰입니다. 값이 없으면 쿠폰을 발급하지 않습니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>앱푸쉬수신동의쿠폰</th>
+						<td>
+							<input name="c10StrSetNm2" type="text" class="w300" onkeypress="if (window.event.keyCode == 13) { fnOpenCouponRetrievePopup('input[name=c10StrSetVal2]', 'input[name=c10StrSetNm2]'); }"/>
+							<button type="button" class="btn icn" onclick="fnOpenCouponRetrievePopup('input[name=c10StrSetVal2]', 'input[name=c10StrSetNm2]');"><i class="fa fa-search cpn" aria-hidden="true"></i></button>
+							<input name="c10StrSetVal2" type="text" class="w100" maxlength="20" readonly/>
+							<button type="button" class="btn icn" onclick="$('input[name=c10StrSetVal2], input[name=c10StrSetNm2]').val('');"><i class="fa fa-eraser" aria-hidden="true"></i></button>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>회원가입 시 앱푸쉬 수신동의를 한 경우 최초 한 번만 발급해 주는 쿠폰입니다. 값이 없으면 쿠폰을 발급하지 않습니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>생일축하쿠폰</th>
+						<td>
+							<input name="c10StrSetNm3" type="text" class="w300" onkeypress="if (window.event.keyCode == 13) { fnOpenCouponRetrievePopup('input[name=c10StrSetVal3]', 'input[name=c10StrSetNm3]'); }"/>
+							<button type="button" class="btn icn" onclick="fnOpenCouponRetrievePopup('input[name=c10StrSetVal3]', 'input[name=c10StrSetNm3]');"><i class="fa fa-search cpn" aria-hidden="true"></i></button>
+							<input name="c10StrSetVal3" type="text" class="w100" maxlength="20" readonly/>
+							<button type="button" class="btn icn" onclick="$('input[name=c10StrSetVal3], input[name=c10StrSetNm3]').val('');"><i class="fa fa-eraser" aria-hidden="true"></i></button>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>회원의 생일 도래 </span><input name="c10NumSetVal1" type="text" class="w20 aR" maxlength="2" data-valid-type="integer" data-valid-name="생일도래시점"/><span class="cRed">일</span><span class="cBlue">전 발급해 줄 쿠폰입니다. 값이 없으면 쿠폰을 발급하지 않습니다.</span>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+
+			<h4>회원포인트</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('P10');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('P10', '회원포인트');">이력보기</button>
+			<table class="frmStyle" aria-describedby="회원포인트">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>가입축하포인트</th>
+						<td>
+							<input name="p10NumSetVal1" type="text" class="w100 aR" maxlength="6" data-valid-type="integer" data-valid-name="가입축하포인트"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>회원가입 시 적립해 줄 기본 포인트입니다.</span>
+							<span class="cRed">단, 성별, 생년월일을 입력하고 SMS 및 E-MAIL 수신동의 시 </span><input name="p10NumSetVal6" type="text" class="w100 aR" maxlength="6" data-valid-type="integer" data-valid-name="가입축하추가포인트"/><span class="cRed">포인트를 추가로 적립해 줍니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>생일축하포인트</th>
+						<td>
+							<input name="p10NumSetVal2" type="text" class="w100 aR" maxlength="6" data-valid-type="integer" data-valid-name="생일축하포인트"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>회원의 생일 도래 </span><input name="p10NumSetVal3" type="text" class="w20 aR" maxlength="2" data-valid-type="integer" data-valid-name="생일도래시점"/><span class="cRed">일</span><span class="cBlue">전 적립해 줄 포인트입니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>앱설치후최초로그인포인트</th>
+						<td>
+							<input name="p10NumSetVal4" type="text" class="w100 aR" maxlength="6" data-valid-type="integer" data-valid-name="앱설치후최초로그인포인트"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>앱 설치 후 앱을 통해서 로그인을 했을 때 최초 한 번만 적립해 줄 포인트입니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>출석체크1일포인트</th>
+						<td>
+							<input name="p10NumSetVal5" type="text" class="w100 aR" maxlength="6" data-valid-type="integer" data-valid-name="출석체크1일포인트"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>출석체크 이벤트 하루 체크 시 적립해 줄 포인트입니다.</span>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+
+			<h4>구매포인트</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('P11');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('P11', '구매포인트');">이력보기</button>
+			<table class="frmStyle" aria-describedby="구매포인트">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>자사상품정상포인트(PC)</th>
+						<td>
+							<input name="p11NumSetVal1" type="text" class="w100 aR" maxlength="6" data-valid-type="integer" data-valid-name="자사상품정상포인트(PC)"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>자사 정상상품을 PC웹에서 구매 시 적립해 줄 포인트입니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>자사상품정상포인트(모바일)</th>
+						<td>
+							<input name="p11NumSetVal2" type="text" class="w100 aR" maxlength="6" data-valid-type="integer" data-valid-name="자사상품정상포인트(모바일)"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>자사 정상상품을 모바일에서 구매 시 적립해 줄 포인트입니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>자사상품이월포인트(PC)</th>
+						<td>
+							<input name="p11NumSetVal3" type="text" class="w100 aR" maxlength="6" data-valid-type="integer" data-valid-name="자사상품이월포인트(PC)"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>자사 이월상품을 PC웹에서 구매 시 적립해 줄 포인트입니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>자사상품이월포인트(모바일)</th>
+						<td>
+							<input name="p11NumSetVal4" type="text" class="w100 aR" maxlength="6" data-valid-type="integer" data-valid-name="자사상품이월포인트(모바일)"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>자사 이월상품을 모바일에서 구매 시 적립해 줄 포인트입니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>입점상품정상포인트(PC)</th>
+						<td>
+							<input name="p11NumSetVal5" type="text" class="w100 aR" maxlength="6" data-valid-type="integer" data-valid-name="입점상품정상포인트(PC)"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>입점 정상상품을 PC웹에서 구매 시 적립해 줄 포인트입니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>입점상품정상포인트(모바일)</th>
+						<td>
+							<input name="p11NumSetVal6" type="text" class="w100 aR" maxlength="6" data-valid-type="integer" data-valid-name="입점상품정상포인트(모바일)"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>입점 정상상품을 모바일에서 구매 시 적립해 줄 포인트입니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>입점상품이월포인트(PC)</th>
+						<td>
+							<input name="p11NumSetVal7" type="text" class="w100 aR" maxlength="6" data-valid-type="integer" data-valid-name="입점상품이월포인트(PC)"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>입점 이월상품을 PC웹에서 구매 시 적립해 줄 포인트입니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>입점상품이월포인트(모바일)</th>
+						<td>
+							<input name="p11NumSetVal8" type="text" class="w100 aR" maxlength="6" data-valid-type="integer" data-valid-name="입점상품이월포인트(모바일)"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>입점 이월상품을 모바일에서 구매 시 적립해 줄 포인트입니다.</span>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+
+			<h4>상품평포인트</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('P12');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('P12', '상품평포인트');">이력보기</button>
+			<table class="frmStyle" aria-describedby="상품평포인트">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>포인트지급방법</th>
+						<td>
+							<label class="rdoBox"><input type="radio" name="p12StrSetVal1" value="A" checked="checked"/>승인후지급</label>
+							<label class="rdoBox"><input type="radio" name="p12StrSetVal1" value="D">즉시지급</label>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>승인후지급 선택 시 <a href="javascript:void(0);" onclick="fnClickLnb('/marketing/review/list/form', '회원/마케팅 > 마케팅 > 상품평관리', this);"><span class="cRed">"회원/마케팅 > 마케팅 > 상품평관리"</span></a> 메뉴에서 승인 시 지급됩니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>텍스트상품평포인트</th>
+						<td>
+							<input name="p12NumSetVal1" type="text" class="w100 aR" maxlength="4" data-valid-type="integer" data-valid-name="텍스트상품평포인트"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>작성한 상품평이 텍스트일 경우 적립해 줄 포인트입니다. 최초등록시 </span>
+							<input name="p12NumSetVal3" type="text" class="w50 aR" maxlength="4" data-valid-type="integer" data-valid-name="일반상품평배율"/><span class="infoTxt cBlue">배를 지급합니다.</span>
+
+						</td>
+					</tr>
+					<tr>
+						<th>포토상품평포인트</th>
+						<td>
+							<input name="p12NumSetVal2" type="text" class="w100 aR" maxlength="4" data-valid-type="integer" data-valid-name="포토상품평포인트"/>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>작성한 상품평이 포토일 경우 적립해 줄 포인트입니다.. 최초등록시 </span>
+							<input name="p12NumSetVal4" type="text" class="w50 aR" maxlength="4" data-valid-type="integer" data-valid-name="포토상품평배율"/><span class="infoTxt cBlue">배를 지급합니다.</span>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+
+			<h4>클레임포인트</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('P13');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('P13', '클레임포인트');">이력보기</button>
+			<table class="frmStyle" aria-describedby="클레임포인트">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>배송지연보상포인트</th>
+						<td>
+							결제완료 후 <input name="p13NumSetVal1" type="text" class="w100 aR" maxlength="3" data-valid-type="integer" data-valid-name="배송지연보상기준일수"/> <span class="cRed">일</span> 동안 배송완료 처리가 되지 않으면
+							포인트 <input name="p13NumSetVal2" type="text" class="w100 aR" maxlength="8" data-valid-type="integer" data-valid-name="배송지연보상포인트"/>를 적립해 줍니다.
+							<p class="infoTxt cBlue"><i class="fa fa-info-circle" aria-hidden="true"></i>배송이 지연되는 경우 적립해 줄 포인트입니다. 0으로 설정하면 배송지연보상 정책을 사용하지 않습니다.</p>
+						</td>
+					</tr>
+					<tr>
+						<th>품절보상포인트</th>
+						<td>
+							결제완료 후 <input name="p13NumSetVal3" type="text" class="w100 aR" maxlength="8" data-valid-type="integer" data-valid-name="품절보상기준일수"/> <span class="cRed">일</span> 동안 출고 진행이 되지 않으면
+							포인트 <input name="p13NumSetVal4" type="text" class="w100 aR" maxlength="8" data-valid-type="integer" data-valid-name="품절보상포인트"/>를 적립해 줍니다.
+							<p class="infoTxt cBlue"><i class="fa fa-info-circle" aria-hidden="true"></i>출고가 지연되는 경우 적립해 줄 포인트입니다. 0으로 설정하면 품절보상 정책을 사용하지 않습니다.</p>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+
+			<h4>가용포인트전환</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('P20');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('P20', '가용포인트전환');">이력보기</button>
+			<table class="frmStyle" aria-describedby="가용포인트전환">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>가용포인트전환대기일수</th>
+						<td>
+							결제 시 적립된 예정포인트가 <input name="p20NumSetVal1" type="text" class="w20 aR" maxlength="2" data-valid-type="integer" data-valid-name="가용포인트전환대기일수"/> <span class="cRed">일</span> 이후 가용포인트로 전환됩니다.
+						</td>
+					</tr>
+					<tr>
+						<th>가용포인트추가적립율</th>
+						<td>
+							<span class="cRed">회원가입 시 성별, 생년월일을 입력하고 SMS 및 E-MAIL 수신동의를 한 경우</span> 결제 시 적립된 예정포인트 외에 결제금액의 <input name="p20NumSetVal2" type="text" class="w20 aR" maxlength="2" data-valid-type="integer" data-valid-name="가용포인트추가적립율"/><span class="cRed">%</span>가 추가로 적립됩니다.
+						</td>
+					</tr>
+				</tbody>
+			</table>
+
+			<h4>포인트/쿠폰사용</h4>
+			<button type="button" class="btn btn-success btn-ssm" onclick="fnSave('P30');">저장</button>
+			<button type="button" class="btn btn-info btn-ssm" onclick="fnOpenEnvsetPopup('P30', '포인트/쿠폰사용');">이력보기</button>
+			<table class="frmStyle" aria-describedby="포인트사용">
+				<colgroup>
+					<col style="width: 15%;"/>
+					<col style="width: 85%;"/>
+				</colgroup>
+				<tbody>
+					<tr>
+						<th>사용방법</th>
+						<td>
+							<label class="rdoBox"><input type="radio" name="p30StrSetVal1" value="A" checked="checked"/>정액</label>
+							<label class="rdoBox"><input type="radio" name="p30StrSetVal1" value="R">정율</label>
+						</td>
+					</tr>
+					<tr>
+						<th>사용단위(사용포인트/쿠폰단위)</th>
+						<td>
+							<select name="p30StrSetVal2">
+								<option value="10">일원단위절사</option>
+								<option value="100">십원단위절사</option>
+								<option value="1000">백원단위절사</option>
+							</select>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>쿠폰적용금액도 적용됩니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>사용가능보유포인트금액</th>
+						<td>
+							포인트 <input name="p30NumSetVal1" type="text" class="w100 aR" maxlength="7" data-valid-type="integer" data-valid-name="사용가능보유포인트금액"/> 이상 보유 시 사용 가능합니다.
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>포인트가 얼마 이상이어야 사용 가능한지를 설정합니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>사용가능최소포인트금액</th>
+						<td>
+							포인트 <input name="p30NumSetVal2" type="text" class="w100 aR" maxlength="7" data-valid-type="integer" data-valid-name="사용가능최소포인트금액"/> 이상 사용 가능합니다.
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>한 번의 결제 시 사용할 수 있는 포인트의 최소금액을 설정합니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>사용가능최대포인트율</th>
+						<td>
+							포인트 <input name="p30NumSetVal3" type="text" class="w100 aR" maxlength="3" data-valid-type="integer" data-valid-name="사용가능보유포인트율"/> <span class="cRed">%</span> 이하로 사용 가능합니다.
+							<span class="infoTxt cBlue marL20">
+								<i class="fa fa-info-circle" aria-hidden="true"></i>한 번의 결제 시 <span class="cRed">결제금액 기준</span>으로 사용할 수 있는 최대 포인트율을 설정합니다.
+								<i class="fa fa-info-circle" aria-hidden="true"></i><span class="cRed">사이트설정 > 약관관리 > 구매이용약관</span>을 수정을 하셔야 합니다.
+							</span>
+						</td>
+					</tr>
+					<tr>
+						<th>즉시사용최대포인트율</th>
+						<td>
+							포인트 <input name="p30NumSetVal5" type="text" class="w100 aR" maxlength="3" data-valid-type="integer" data-valid-name="즉시사용최대포인트율"/> <span class="cRed">%</span> 이하로 즉시 사용할 수 있습니다.
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>한 번의 결제 시 <span class="cRed">적립금액 기준</span>으로 즉시 사용할 수 있는 최대 포인트율을 설정합니다.</span>
+						</td>
+					</tr>
+					<tr>
+						<th>최소주문제한금액</th>
+						<td>
+							<input name="p30NumSetVal4" type="text" class="w100 aR" maxlength="7" data-valid-type="integer" data-valid-name="최소주문제한금액"/> <span class="cRed">원</span>
+							<span class="infoTxt cBlue marL20"><i class="fa fa-info-circle" aria-hidden="true"></i>한 번의 결제 시 상품의 판매가 합산금액이 설정 금액 보다 적을 경우 포인트 사용이 불가능 합니다.</span>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+			<div class="panelBtnB"></div>
+		</div>
+	</div>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	// 조회
+	var fnSearch = function() {
+		var siteCd = $('select[name=siteCd]').val();
+		var actionUrl = '/system/envset/' + siteCd;
+
+		// 회원쿠폰
+		$.getJSON(actionUrl + '/C10'
+				, function(result, status) {
+					if (status === 'success') {
+						if (!gagajf.isNull(result)) {
+							$('input[name=c10StrSetVal1]').val(result.strSetVal1);
+							$('input[name=c10StrSetNm1]').val(result.strSetNm1);
+							$('input[name=c10StrSetVal2]').val(result.strSetVal2);
+							$('input[name=c10StrSetNm2]').val(result.strSetNm2);
+							$('input[name=c10StrSetVal3]').val(result.strSetVal3);
+							$('input[name=c10StrSetNm3]').val(result.strSetNm3);
+							$('input[name=c10NumSetVal1]').val(result.numSetVal1.addComma());
+						}
+					}
+				});
+
+		// 회원포인트
+		$.getJSON(actionUrl + '/P10'
+				, function(result, status) {
+					if (status === 'success') {
+						if (!gagajf.isNull(result)) {
+							$('input[name=p10NumSetVal1]').val(result.numSetVal1.addComma());
+							$('input[name=p10NumSetVal2]').val(result.numSetVal2.addComma());
+							$('input[name=p10NumSetVal3]').val(result.numSetVal3.addComma());
+							$('input[name=p10NumSetVal4]').val(result.numSetVal4.addComma());
+							$('input[name=p10NumSetVal5]').val(result.numSetVal5.addComma());
+							$('input[name=p10NumSetVal6]').val(result.numSetVal6.addComma());
+						}
+					}
+				});
+
+		// 구매포인트
+		$.getJSON(actionUrl + '/P11'
+				, function(result, status) {
+					if (status === 'success') {
+						if (!gagajf.isNull(result)) {
+							$('input[name=p11NumSetVal1]').val(result.numSetVal1.addComma());
+							$('input[name=p11NumSetVal2]').val(result.numSetVal2.addComma());
+							$('input[name=p11NumSetVal3]').val(result.numSetVal3.addComma());
+							$('input[name=p11NumSetVal4]').val(result.numSetVal4.addComma());
+							$('input[name=p11NumSetVal5]').val(result.numSetVal5.addComma());
+							$('input[name=p11NumSetVal6]').val(result.numSetVal6.addComma());
+							$('input[name=p11NumSetVal7]').val(result.numSetVal7.addComma());
+							$('input[name=p11NumSetVal8]').val(result.numSetVal8.addComma());
+						}
+					}
+				});
+
+		// 상품평포인트
+		$.getJSON(actionUrl + '/P12'
+				, function(result, status) {
+					if (status === 'success') {
+						$("input:radio[name=p12StrSetVal1]").parents('td').find('label').removeClass('checked');
+						if (result.strSetVal1 == 'A') {
+							$("input:radio[name=p12StrSetVal1]").eq(0).prop('checked', true);
+							$("input:radio[name=p12StrSetVal1]").eq(0).parent().addClass('checked');
+						} else {
+							$("input:radio[name=p12StrSetVal1]").eq(1).prop('checked', true);
+							$("input:radio[name=p12StrSetVal1]").eq(1).parent().addClass('checked');
+						}
+
+						$('input[name=p12NumSetVal1]').val(result.numSetVal1.addComma());
+						$('input[name=p12NumSetVal2]').val(result.numSetVal2.addComma());
+						$('input[name=p12NumSetVal3]').val(result.numSetVal3.addComma());
+						$('input[name=p12NumSetVal4]').val(result.numSetVal4.addComma());
+					}
+				});
+
+		// 클레임포인트
+		$.getJSON(actionUrl + '/P13'
+				, function(result, status) {
+					if (status === 'success') {
+						if (!gagajf.isNull(result)) {
+							$('input[name=p13NumSetVal1]').val(result.numSetVal1.addComma());
+							$('input[name=p13NumSetVal2]').val(result.numSetVal2.addComma());
+							$('input[name=p13NumSetVal3]').val(result.numSetVal3.addComma());
+							$('input[name=p13NumSetVal4]').val(result.numSetVal4.addComma());
+						}
+					}
+				});
+
+		// 가용포인트전환
+		$.getJSON(actionUrl + '/P20'
+				, function(result, status) {
+					if (status === 'success') {
+						if (!gagajf.isNull(result)) {
+							$('input[name=p20NumSetVal1]').val(result.numSetVal1.addComma());
+							$('input[name=p20NumSetVal2]').val(result.numSetVal2.addComma());
+						}
+					}
+				});
+
+		// 표인트사용
+		$.getJSON(actionUrl + '/P30'
+				, function(result, status) {
+					if (status === 'success') {
+						if (!gagajf.isNull(result)) {
+							$("input:radio[name=p30StrSetVal1]").parents('td').find('label').removeClass('checked');
+							if (result.strSetVal1 == 'A') {
+								$("input:radio[name=p30StrSetVal1]").eq(0).prop('checked', true);
+								$("input:radio[name=p30StrSetVal1]").eq(0).parent().addClass('checked');
+							} else {
+								$("input:radio[name=p30StrSetVal1]").eq(1).prop('checked', true);
+								$("input:radio[name=p30StrSetVal1]").eq(1).parent().addClass('checked');
+							}
+
+							$('select[name=p30StrSetVal2]').val(result.strSetVal2);
+							$('input[name=p30NumSetVal1]').val(result.numSetVal1.addComma());
+							$('input[name=p30NumSetVal2]').val(result.numSetVal2.addComma());
+							$('input[name=p30NumSetVal3]').val(result.numSetVal3.addComma());
+							$('input[name=p30NumSetVal4]').val(result.numSetVal4.addComma());
+							$('input[name=p30NumSetVal5]').val(result.numSetVal5.addComma());
+						}
+					}
+				});
+	}
+
+	// 저장 처리
+	var fnSave = function(envsetType) {
+		var params = new Object();
+		params.siteCd = $('select[name=siteCd]').val();
+		params.envsetType = envsetType;
+
+		if (envsetType == 'C10') { // 회원쿠폰
+			params.strSetVal1 = $('input[name=c10StrSetVal1]').val();
+			params.strSetVal2 = $('input[name=c10StrSetVal2]').val();
+			params.strSetVal3 = $('input[name=c10StrSetVal3]').val();
+			params.numSetVal1 = $('input[name=c10NumSetVal1]').val().removeComma();
+		} else if (envsetType == 'P10') { // 회원포인트
+			params.numSetVal1 = $('input[name=p10NumSetVal1]').val().removeComma();
+			params.numSetVal2 = $('input[name=p10NumSetVal2]').val().removeComma();
+			params.numSetVal3 = $('input[name=p10NumSetVal3]').val().removeComma();
+			params.numSetVal4 = $('input[name=p10NumSetVal4]').val().removeComma();
+			params.numSetVal5 = $('input[name=p10NumSetVal5]').val().removeComma();
+			params.numSetVal6 = $('input[name=p10NumSetVal6]').val().removeComma();
+		} else if (envsetType == 'P11') { // 구매포인트
+			params.numSetVal1 = $('input[name=p11NumSetVal1]').val();
+			params.numSetVal2 = $('input[name=p11NumSetVal2]').val();
+			params.numSetVal3 = $('input[name=p11NumSetVal3]').val();
+			params.numSetVal4 = $('input[name=p11NumSetVal4]').val();
+			params.numSetVal5 = $('input[name=p11NumSetVal5]').val();
+			params.numSetVal6 = $('input[name=p11NumSetVal6]').val();
+			params.numSetVal7 = $('input[name=p11NumSetVal7]').val();
+			params.numSetVal8 = $('input[name=p11NumSetVal8]').val();
+		} else if (envsetType == 'P12') { // 상품평포인트
+			params.strSetVal1 = $('input:radio[name=p12StrSetVal1]:checked').val();
+			params.numSetVal1 = $('input[name=p12NumSetVal1]').val().removeComma();
+			params.numSetVal2 = $('input[name=p12NumSetVal2]').val().removeComma();
+			params.numSetVal3 = $('input[name=p12NumSetVal3]').val().removeComma();
+			params.numSetVal4 = $('input[name=p12NumSetVal4]').val().removeComma();
+		} else if (envsetType == 'P13') { // 클레임포인트
+			params.numSetVal1 = $('input[name=p13NumSetVal1]').val();
+			params.numSetVal2 = $('input[name=p13NumSetVal2]').val().removeComma();
+			params.numSetVal3 = $('input[name=p13NumSetVal3]').val();
+			params.numSetVal4 = $('input[name=p13NumSetVal4]').val().removeComma();
+		} else if (envsetType == 'P20') { // 가용포인트전환
+			params.numSetVal1 = $('input[name=p20NumSetVal1]').val();
+			params.numSetVal2 = $('input[name=p20NumSetVal2]').val();
+		} else if (envsetType == 'P30') { // 포인트사용
+			params.strSetVal1 = $('input:radio[name=p30StrSetVal1]:checked').val();
+			params.strSetVal2 = $('select[name=p30StrSetVal2]').val();
+			params.numSetVal1 = $('input[name=p30NumSetVal1]').val().removeComma();
+			params.numSetVal2 = $('input[name=p30NumSetVal2]').val().removeComma();
+			params.numSetVal3 = $('input[name=p30NumSetVal3]').val().removeComma();
+			params.numSetVal4 = $('input[name=p30NumSetVal4]').val().removeComma();
+			params.numSetVal5 = $('input[name=p30NumSetVal5]').val().removeComma();
+		}
+
+		var jsonData = JSON.stringify(params);
+		gagajf.ajaxJsonSubmit('/system/envset/create', jsonData);
+	}
+
+	// 환경설정 이력보기 팝업
+	var fnOpenEnvsetPopup = function(envsetType, envsetTypeNm) {
+		var actionUrl = '/system/envset/history/form'
+				+ '?siteCd=' + $('select[name=siteCd]').val()
+				+ '&envsetType=' + envsetType
+				+ '&envsetTypeNm=' + encodeURIComponent(envsetTypeNm);
+		cfnOpenModalPopup(actionUrl, 'popupEnvset');
+	}
+
+	// 쿠폰조회 팝업
+	var fnOpenCouponRetrievePopup = function(strReturnCode, strReturnName) {
+		var oParam = new Object();
+		oParam.returnCode = strReturnCode;
+		oParam.returnName = strReturnName;
+		cfnOpenCouponRetrievePopup($(strReturnName).val(), oParam);
+	}
+
+	$(document).ready(function() {
+		fnSearch();
+		uifnFitGrid();
+	});
+/*]]>*/
+</script>
+</html>

+ 91 - 0
style24.admin/src/main/webapp/WEB-INF/views/system/PopupPasswordChangeForm.html

@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : PopupPasswordChangeForm.html
+ * @desc    : 비밀번호 변경 팝업
+ *============================================================================
+ * SISUN
+ * Copyright(C) 2019 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2020.01.16   rladbwnd5   최초 작성
+ *******************************************************************************
+ -->
+	<ul class="popup modal" data-width="500" >
+		<li class="mdPopTitle">
+			<strong>비밀번호 변경</strong>
+			<button type="button" class="close" onclick="uifnPopClose('popupPassword')"><i class="fa fa-times"></i></button>
+		</li>
+		<li class="mdPopContent">
+			<form name="popupPasswordForm" id="popupPasswordForm" action="#" th:action="@{'/system/user/password/change'}" th:method="post">
+				<input type="hidden" name="userId" th:value="${userId}"/>
+				<table class="frmStyle" aria-describedby="수정">
+					<colgroup>
+						<col style="width:10%;"/>
+					</colgroup>
+					<tbody>
+						<tr>
+							<th class="dashR">관리자ID<i class="star" aria-hidden="true"></i></th>
+							<td class="dashR" th:text="${userId}">
+							</td>
+						</tr>
+						<tr>
+							<th class="dashR">비밀번호<i class="star" aria-hidden="true"></i></th>
+							<td class="dashR">
+								<input type="password" class="w300" name="passwd" data-valid-type="password" required="required" data-valid-name="비밀번호"/>
+							</td>
+						</tr>
+						<tr>
+							<th class="dashR">비밀번호 확인<i class="star" aria-hidden="true"></i></th>
+							<td class="dashR">
+								<input type="password" class="w300" name="passwdConfirm" data-valid-type="password" required="required" data-valid-name="비밀번호 확인"/>
+							</td>
+						</tr>
+					</tbody>
+				</table>
+				<p class="dot cBlue">대문자, 소문자, 특수문자, 숫자로 구성해야 합니다.</p>
+				<p class="dot cBlue">이 중 3개 이상은 8 ~ 20자, 2개 이상은 10 ~ 20자로 설정해야 합니다.</p>
+			</form>
+		</li>
+		<li class="boxContentBtnB">
+			<button type="button" class="btn btn-success btn-lg " id="btnPasswordChange">저장</button>
+		</li>
+	</ul>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	$('#btnPasswordChange').on('click', function() {
+		var formId = '#popupPasswordForm';
+		
+		if (!gagajf.validation(formId))
+			return false;
+		
+		var pwd = $('#popupPasswordForm input[name="passwd"]').val();
+		var pwdConfirm = $('#popupPasswordForm input[name="passwdConfirm"]').val();
+		
+		if (pwd != pwdConfirm) {
+			mcxDialog.alert('비밀번호를 확인해주세요.');
+			return;
+		}
+		
+		mcxDialog.confirm("비밀번호를 변경 하시겠습니까?", {
+			cancelBtnText: "취소",
+			sureBtnText: "확인",
+			sureBtnClick: function() {
+				gagajf.ajaxFormSubmit($(formId).prop('action'), formId, function() {
+					uifnPopClose('popupPassword');
+				});
+			}
+		});
+	});
+	
+	$(document).ready(function() {
+
+	});
+/*]]>*/
+</script>
+
+</html>

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels