WebSquare5 공통 개발자 가이드 정리

김연수·2023년 2월 23일
0

websquare

목록 보기
12/12

WRM(WebSqaure5 Reference Model)

구성은 크게 3파트로 나뉘어져 있다

  • UI Template Code
  • Server Template Code
  • UI Dev Template Document

그중에 내가 가장 중점적으로 다뤄야 할 부분은

Server-Side JAVA Service

  • Spring MVC Controller/Service/Dao
    • 코드관리,권한관리,프로그램관리,메뉴관리,릴리즈관리
    • 조직관리,사원관리
  • Login & Logout / Session Check Interceptor
  • Log4J
  • Maria DB
  • Jackson JSON Convertor
  • WebSquare Engine

먼저 WAS의 -> Edit Configuration -> Arguments 탭에 추가해준다

-DWEBSQUARE_HOME="C:\WEBSQUARE_DEV_PACK\workspace\websquare_home" -Xms1024m -Xmx1024m
  • WEBSQUARE_HOME 옵션은 웹스퀘어 환경 설정 및 라이센스 파일이 저장된 HOME 경로임
  • Xms, Xmx를 통한 JAVA Heap 메모리 설정은 시스템 사용 인원과 데이터 크기에 따라서 적절한 크기를 설정해야 함

로그인(Session)

최초 로그인 시 로그인 페이지를 통해 Session 정보를 가져온다. Interceptor가 로그인을 확인하기 때문에 로그인 없이 개별 페이지에 접근할 경우에도 로그인 페이지가 출력된다. 프로세스는 다음과 같다

JSON Data Convertor

WRM은 클라이언트 <-> 서버 간의 통신 시 JSON Format으로 데이터 통신을 처리한다.

servlet-context.xml

Spring MVC Project의 Controller 단에서 @ResponseBody, @RequestBody 어노테이션을 이용해서 JSON 문자열을 자동으로 JAVA 객체로 Converting 되어서 전달받기 위해서 servlet-context.xml 파일에 아래의 설정이 추가하고, Jackson JSON Library Import가 필요함

<beans:bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" p:order="0">
</beans:bean>

<beans:bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" p:order="1">
    <beans:property name="messageConverters">
        <beans:list>
            <beans:ref bean="mappingJackson2HttpMessageConverter" />
        </beans:list>
    </beans:property>
</beans:bean>

<beans:bean id="mappingJackson2HttpMessageConverter"
    class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
    <beans:property name="supportedMediaTypes">
        <beans:list>
            <beans:value>application/json;charset=UTF-8</beans:value>
        </beans:list>
    </beans:property>
</beans:bean>

Interceptors

로그인 되지 않은 상태에서 화면에 접근할 수 없도록 SessionCheckInterceptor를 만들고, servlet-context.xml 파일에 아래와 같이 Interceptor를 등록한다.

<!-- 접근할 Resource 매핑 설정 -->
<resources mapping="/websquare/**" location="/websquare/" />
<resources mapping="/cm/**" location="/cm/" />
<resources mapping="/ui/**" location="/ui/" />
<resources mapping="/favicon.ico" location="/favicon.ico" />

<!-- 로그인 여부를 체크하기 위한 Interceptor -->  
<beans:bean id="interceptorLoginCheck" name="interceptorLoginCheck" class="com.inswave.wrm.interceptor.SessionCheckInterceptor" />

<!-- 로그인이 되지 않은 경우도 접근을 허용하기 위한 예외 처리-->  
<interceptors>
    <interceptor>
        <mapping path="/**" />
        <exclude-mapping path="/websquare/**" />
        <exclude-mapping path="/cm/**" />
        <beans:ref bean="interceptorLoginCheck" />
    </interceptor>
</interceptors>

데이터 구조

WRM은 map/json 형태로 서버와 통신한다.

  • Request
    {dma_search: {“col1”: "….",”col2”:"",”col3”: "all"}}
  • Response
    {"rsMsg": {"message":" …. 리스트가 조회되었습니다.","statusCode":"S"} ,”dlt_result.":[{"col1":"....","col2":"...","col3":"...", .....} , .....]}

Controller

클라이언트가 Submission을 통해 전송한 Request 정보를 전달 받아 지정된 서비스를 호출

  • WebSquare5 파일 (클라이언트)
<xf:submission id="sbm_commonCode" ref='data:json,dma_commonGrp' target='data:json,dlt_commonCode' action="/common/selectCommonCodeList" method="post" mediatype="application/json" encoding="UTF-8" instance="" replace="" errorHandler="" customHandler="" mode="asynchronous" processMsg="" ev:submit="" ev:submitdone="scwin.sbm_commonCode_submitdone" ev:submiterror="">
</xf:submission>
  • Java 파일 (Controller)
public @ResponseBody Map<String, Object> selectCommonCodeList(@RequestBody Map<String, Object> param) {
    Result result = new Result();
    try {
        result.setData("dlt_commonCode", commonService.selectCommonCodeList((Map) param.get("dma_CommonGrp")));
        result.setMsg(result.STATUS_SUCESS, "공통코드(" + ((Map) param.get("dma_CommonGrp")).get("GRP_CD") + ") 리스트가 조회되었습니다.");
    } catch (Exception ex) {
        ex.printStackTrace();
        result.setMsg(result.STATUS_ERROR, "공통코드 정보(" + ((Map) param.get("dma_CommonGrp")).get("GRP_CD") + ")를 가져오는 도중 오류가 발생하였습니다,", ex);
    }
    return result.getResult();
}

Service

서버 업무 로직을 실행하며 DAO 클래스를 호출

// Service 
public List commonCodeList(Map param);

// Service Impl@Override
public List commonCodeList(Map param) {
return commonDao.selectCommonCodeList(param);
}

DAO

SQL을 실행

public List selectCommonCodeList(Map param);

MyBatis

<?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.inswave.dao.CommonDao">
    
    ... 중략 ....
    <select id="selectCommonCodeList" resultType="Map">
       select * from BM_CODE A
        where  A.GRP_CD = #{GRP_CD} order by A.SORT_ORDER
    </select>
        ... 중략 ....

SessionCheckInterceptor

웹스퀘어 페이지(XML) 및 서비스 요청 시 사용자 로그인 세션이 존재하는지를 체크해서 로그인이 끊긴 경우에 대한 예외 처리를 수행

package com.inswave.wrm.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.inswave.wrm.util.UserInfo;

public class SessionCheckInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private UserInfo userInfo;

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String loginInfo = null;
        HttpSession session = request.getSession();
        String reqUrl = request.getRequestURI();
        String w2xPath = request.getParameter("w2xPath");
        boolean result = true;

        try {
            loginInfo = (String) session.getAttribute("EMP_CD");
            
            if (loginInfo != null) {
                userInfo.setUserInfo(session);
            } else {
                if (!isSkipURI(request)) {
                    if ((w2xPath != null) || (reqUrl.indexOf(".xml") > -1)) {
                        // 웹스퀘어 화면 호출 시 세션이 종료된 경우, 로그인 페이지로 Redirect 처리한다.
                        result = false;
                        response.setContentType("text/xml");
                        response.setCharacterEncoding("UTF-8");
                        response.getWriter().write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
                        response.getWriter().write("<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:ev=\"http://www.w3.org/2001/xml-events\" ");
                        response.getWriter().write("xmlns:w2=\"http://www.inswave.com/websquare\" xmlns:xf=\"http://www.w3.org/2002/xforms\">");
                        response.getWriter().write("<head>");
                        response.getWriter().write("<w2:buildDate/>");
                        response.getWriter().write("<xf:model><xf:instance><data xmlns=\"\"/></xf:instance></xf:model>");
                        response.getWriter().write("<script type=\"javascript\" lazy=\"false\"><![CDATA[ ");
                        response.getWriter().write("scwin.onpageload = function() { com.win.alert(\"Session이 종료 되었습니다. 로그인 화면으로 이동하겠습니다.\", \"com.win.goHome\", true); };");
                        response.getWriter().write("scwin.onpageunload = function() { };");
                        response.getWriter().write("]]></script>");
                        response.getWriter().write("</head>");
                        response.getWriter().write("<body ev:onpageload=\"scwin.onpageload\" ev:onpageunload=\"scwin.onpageunload\"></body>");
                        response.getWriter().write("</html>");
                    } else {
                        // 서비스 호출 시 세션이 종료된 경우, Session 종료 Alert 후, 로그인 페이지로 Redirect 처리 한다.
                        result = false;
                        response.setContentType("application/json");
                        response.setCharacterEncoding("UTF-8");
                        response.getWriter().write("{\"rsMsg\":{\"statusCode\":\"E\", \"errorCode\" : \"E0001\", \"message\":\"Session이 종료 되었습니다.\",\"status\":\"Error\"}}");
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }
    
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
response.addHeader("X-Frame-Options", "DENY");
}

    /**
     * Session 체크 대상에서 예외 URI 구성
     * 
     * @date 2020.03.06
     * @param request HttpServletRequest 객체
     * @returns <boolean> 예외처리 대상 URL 여부
     * @author Inswave Systems
     */
    private boolean isSkipURI(HttpServletRequest request) {    
        String[] skipUrl = { "/", "/I18N", "/main/login", "/favicon.ico" };
        boolean result = false;
        String uri = (request.getRequestURI()).replace(request.getContextPath(), "");

        for (int i = 0; i < skipUrl.length; i++) {
            if (uri.equals(skipUrl[i])) {
                result = true;
                break;
            }
        }
        return result;
    }
}

Util

Servlet 설정

Web.xml

  1. WAS를 구동하면서 웹스퀘어 엔진을 로딩하기 위해서 JavascriptInitializer를 Listener에 등록한다. 등록하지 않으면 WAS 구동 후 웹스퀘어 페이지가 첫번째로 호출되는 시점에 웹스퀘어 엔진이 WAS에 로딩된다.
<!-- WAS를 구동하면서 웹스퀘어 엔진을 로딩하기 위해서 JavascriptInitializer를 listener에 등록 -->     
<listener>
    <listener-class>websquare.http.controller.JavascriptInitializer</listener-class>
</listener>
<servlet>
    <servlet-name>websquareDispatcher</servlet-name>
    <servlet-class>websquare.http.DefaultRequestDispatcher</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>websquareDispatcher</servlet-name>
    <url-pattern>*.wq</url-pattern>
</servlet-mapping>

appServlet

  • application-context.xml : DAO, Service 및 Spring Property 파일 로딩 설정 파일
  • database-context.xml : db 설정 파일 (DB 계정 정보는 db.properties에서 설정함)
  • mybatis-config.xml : mybatis 설정 파일

servlet-context.xml

Controller 로딩, Resource 파일 경로 설정, Interceptor 설정, RequestMappingHandlerMapping, MappingJackson2HttpMessageConverter(JSON String <-> JAVA Object 변환) , BeanNameViewResolver, InternalResourceViewResolver, Log4J 로딩 설정을 한다.

properties

  • db.properties: db정보 설정
  • log4j.xml : Log4J 로그 저장 설정 (현재 설정은 개발 모드에 맞추어져 있기 때문에 운영 환경 적용 시에는 설정 변경 필요)
  • websquareConfig.properties: 사용하는 properties 관리

URL 숨기기

InitController.java

Root(/) 경로에 대한 처리. (movePage의 viewName 및 value 설정.)

/**
 * 기본 Root Url 처리
 */
@RequestMapping(value = "/", method = RequestMethod.GET)
public String IndexBase(HttpServletRequest request, Model model) throws Exception {
    model.addAttribute("movePage", getLoginPage(request.getParameter("w2xPath")));
    return "websquare/websquare";
}

/**
 * Popup Url 처리
 */
@RequestMapping(value = "/websquare/popup", method = RequestMethod.GET)
public String IndexWebSquare(HttpServletRequest request, Model model) throws Exception {
    model.addAttribute("movePage", getLoginPage(request.getParameter("w2xPath")));
    return "websquare/popup";
}

/**
 * 로그인 페이지 Url을 반환한다.
 * 
 * @param w2xPath w2xPath 파라미터
 * @return 로그인 페이지 Url
 */
private String getLoginPage(String w2xPath) {
    String movePage = w2xPath;

    // session이 없을 경우 login 화면으로 이동.
    if (!userInfo.isLogined()) {
        // session이 있고 w2xPath가 없을 경우 index화면으로 이동.
        movePage = PageURIUtil.getLoginPage();
    } else {
        if (movePage == null) {
            // DB 설정조회 초기 page 구성
            movePage = PageURIUtil.getIndexPageURI(userInfo.getMainLayoutCode());

            // DB에 값이 저장되어 있지 않은 경우 기본 index화면으로 이동
            if (movePage == null) {
                movePage = PageURIUtil.getIndexPageURI();
            }
        }
    }
    return movePage;
}

PageURIUtil.java

Login 및 index 페이지를 설정. InitController에서 사용


package com.inswave.util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class PageURIUtil {
    private static String DEF_INDEX_PAGE;
    private static String TAB_INDEX;
    private static String WIN_INDEX;
    private static String LOGIN_PAGE;
    private static String AUTH_CHECK;
    @Value("${w5xml.defIndex}") // websquareConfig.properties에서 설정 값 로딩
    private void setDEF_INDEX_PAGE(String def_index) {
        DEF_INDEX_PAGE = def_index;
    }
    @Value("${w5xml.main.tab}") // websquareConfig.properties에서 설정 값 로딩
    private void setTAB_INDEX(String tab_index) {
        TAB_INDEX = tab_index;
    }
    @Value("${w5xml.main.win}") // websquareConfig.properties에서 설정 값 로딩
    private void setWIN_INDEX(String win_index) {
        WIN_INDEX = win_index;
    }
    @Value("${w5xml.login}") // websquareConfig.properties에서 설정 값 로딩
    private void setLOGIN_PAGE(String login_page){
        LOGIN_PAGE = login_page;
    }
    @Value("${w5xml.auth.check}") // websquareConfig.properties에서 설정 값 로딩
    private void setAUTH_CHECK(String auth_check){
        AUTH_CHECK = auth_check;
    }
    public static String getIndexPageURI(String pageNm) {
        String rsURI =  DEF_INDEX_PAGE;
        if(pageNm != null){
            if(pageNm.equals("T")){
                rsURI = TAB_INDEX;
            }else if(pageNm.equals("W")){
                rsURI = WIN_INDEX;
            }
        }
        return rsURI;
    }
    public static String getIndexPageURI() {
        return getIndexPageURI("T");
    }
    public static String getLoginPage(){
        return LOGIN_PAGE;
    }
    public static String getAuthCheck(){
        return AUTH_CHECK;
    }
}

websquareConfig.properties

PageURIUtil.java를 사용하는 Properties를 정의

w5xml.login=/cm/main/login.xml
w5xml.defIndex=/cm/main/index_tabControl.xml
w5xml.main.tab=/cm/main/index_tabControl.xml
w5xml.main.win=/cm/main/index_windowContainer.xml
w5xml.auth.check=/cm/main/auth_check.xml
main.setting.default.layout=T
main.setting.default.favoriteLocation=D
main.setting.code.DB=D
main.setting.code.LS=L
system.admin.id=100001

websquare.jsp

Root(/) Path를 호출할 경우 웹스퀘어 엔진을 로딩하고 메인 페이지를 로딩하기 위한 처리를 담당하는 websquare.jsp 파일을 반환

<%@page contentType="text/html; charset=utf-8" language="java"%><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns='http://www.w3.org/1999/xhtml' xmlns:ev='http://www.w3.org/2001/xml-events' xmlns:w2='http://www.inswave.com/websquare' xmlns:xf='http://www.w3.org/2002/xforms'>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
        <link rel="shortcut icon" href="../../favicon.ico" />
        <title>WRM</title>
        <script type="text/javascript">
            var WebSquareExternal = {
                "baseURI": "${pageContext.request.contextPath}/websquare/", 
                "w2xPath" : "${pageContext.request.contextPath}" + "<%= (String)request.getAttribute("movePage") %>"
            };
        </script>
        <script type="text/javascript" src="${pageContext.request.contextPath}/websquare/javascript.wq?q=/bootloader"></script>
        <script type="text/javascript">
            window.onload = init;
            
            function init() {
                try{
                    gcm.CONTEXT_PATH = "${pageContext.request.contextPath}";
                    WebSquare.startApplication(WebSquareExternal.w2xPath);
                } catch(e) {
                    alert(e.message);
                }
            }
            
        </script>
    </head>
<body>
</body>
</html>

서버 통신

Submission 서비스 호출에 필요한 규약을 정의하는 컴포넌트

  • 정적 Submission

<xf:submission id="sbm_selectTemp" 
ref='data:json,dma_search' 
target='data:json,{"id":"dlt_temp","key":"dlt_temp"}' 
action="/sample/tempSelect" 
method="post" mediatype="application/json"
                encoding="UTF-8" 
instance="" replace="" errorHandler="" customHandler="" mode="asynchronous" 
processMsg="" ev:submit="" 
ev:submitdone="scwin.sbm_selectTemp_submitdone" ev:submiterror="">
</xf:submission>
  • 동적 Submission

var searchCodeGrpOption = { id : "sbm_searchCodeGrp",
                          action : " /sample/tempSelect",
                          target : 'data:json,{"id":"dlt_temp","key":"dlt_temp"}',
                          submitDoneHandler : "searchCodeGrpCallback", isShowMeg : false };
com.executeSubmission_dynamic(searchCodeGrpOption);

동적 Submission은 웹 스퀘어 스튜디오에서 제공하는 기능을 사용 할 수 없다. com.executeSubmission_dynamic은 common.js에서 확인

화면간 정보 전달

권장하는 방법 : SessionStorage 이용

  • sessionStorage.setItem("CODE", value);
  • sessionStorage.getItem("CODE");

메모리 관리

아래의 경우 메모리 누수를 유발 할 수 있다.

profile
코린이

0개의 댓글