4. Router (Dynamic Import & Create Class)

배고픈 징징이 ㅣ 2023. 2. 7. 14:01

1. 설명

URI에 따라 보여야되는 화면을 만들어주는 Router를 만든다.

URI 마다 하드코딩으로 넣어주기는 힘들기 때문에 URI를 가지고 동적으로 맞는 클래스를 찾아주고, 그 클래스를 Import까지 해주는 기능이 필요하다.

그래서 Filter에서 Js파일을 내려줄때 수정을 해서 내려주도록 한다.

또한 Router는 뒤로가기 기능 지원을 위해서 History를 관리하도록 한다.

 

2. History

뒤로가기가 아니면 URL이 변경될때 마다 History에 URL을 넣어준다.

 

router.ts

export const Router = {
  application : new Map<string, any>()
  , init : (application : Application) => {
    //나중에 다른 메소드에서 화면 갱신을할때 사용할 application 세팅
    Router.application.set("application", application);
    
    //뒤로가기 Event
    window.onpopstate = (event) => {
      Router.run(window.location.pathname);
    }
    Router.run(window.location.pathname);
  }
  //History 삽입
  , push : (data : any, url : string) => {
    history.pushState(data, "", url);
  }
  , pushAndRun : (data : any, url : string) => {
    Router.push(data, url);
    Router.run(url);
  }
  , run : (url : string) => {
    ...(new Class 및 화면 갱신)
  }
};

 

3. Dynamic Import & Create Class

Router에서 동적으로 현재 페이지의 URL에 따라 맞는 클래스 생성과 Import가 가능하게 만들어준다.

Class 명의 규칙은 맨앞글자는 대문자로 그후로는 CamelCase 표기법을 따라간다.

 

 

router.ts

//최상단에는 import문을 Java에서 만들어준다.

export const Router = {
  ...
  , run : (url : string) => {
    const splitUrl = url.split("/");
    let viewName = Router.urlConverter(splitUrl[1]);
    if(viewName == "") viewName = "MainView";
    else if(viewName == "Search") viewName = "SearchView";

    let view = new DynamicClass(viewName, splitUrl) as View;

    const application = Router.application.get("application") as Application;
    if(view) application.update(view);
  }
  //Class명 규칙에 맞게 바꿔주는 메소드
  , urlConverter(string : string){
    let url = string.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.toUpperCase());
    return url.replace(/^[a-z]/, char => char.toUpperCase());
  }
};

class DynamicClass{
  constructor(className : string, parameter? : any) {
    //Java에서 해당 부분을 수정해 준다.
    let classes = {};

    try {
      //매핑되는 Class를 parameter와 함께 생성
      return new classes[className](parameter);
    }catch (e) {
      //classes와 매핑되지 않는다면 예외가 발생해 catch문으로 와진다.
      return new ErrorView(ErrorType.ERROR_404);
    }
  }
}

 

4. Filter (Edit Js File)

Java에서는 Filter가 Js파일을 내려줄때, Js파일을 수정해준다.

export class로 시작하는 모든 Js파일의 클래스들을 가져와서 classes에 넣어주고,

그 클래스들이 정상적으로 불려올 수 있도록 클래스들을 Import 해준다.

계속 importString과 classNames를 초기화할 필요는 없고 데이터가 있다면 생략해도 된다.

현재 코드는 계속 페이지를 추가하며 개발중이라 계속 새로 가져오게 해놓은 상황이다.

 

FrontFilter.java

static String importString = "";
static String classNames = "";

...

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;
    response.setCharacterEncoding("UTF-8");
    String servletPath = request.getServletPath();

    try {
        if (servletPath.startsWith("/js/")) {
            File file = Option.ROOT.getFile(servletPath + ".js");
            response.setContentType("text/javascript");
            String jsFile = FileUtils.readFileToString(file);
            
            //라우터일 경우에 import문과 class생성 관련 로직을 실행
            if (servletPath.indexOf("router") > 0){
                importString = "";
                classNames = "";
                getRouterJs(jsFile);
                jsFile = importString + jsFile.replaceAll("classes = \\{\\}", "classes = \\{" + classNames.substring(1) + "\\}");
            }

            response.getWriter().println(jsFile);
            return;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    
    ...
    
    private void getRouterJs(String jsFile){
        File[] jsList = Option.ROOT.getFile("js").listFiles();
        getJs(jsList, jsFile);
    }

    //jsFile은 기존에 import되어있는 class는 제외하고 import 하기 위해서 넘겨준다.
    public static void getJs(File[] jsList, String jsFile){
        for (File js : jsList) {
            String parentDirectory = js.getParentFile().getName();
            
            //검색에서 제외할 디렉토리 지정
            if(js.getName().indexOf("form") < 0 && js.getName().indexOf("view") < 0){
                if (!parentDirectory.equals("js") && js.getName().endsWith(".js")){
                    String className = getClassName(js);
                    
                    //Pateern Matcher를 통해 정규식으로 기존 import문들을 가져온다.
                    Pattern pattern = Pattern.compile("import \\{.*" + className + ".*}");
                    Matcher matcher = pattern.matcher(jsFile);

                    if(!matcher.find()){
                        importString += "import {" + className + "} from './"  + parentDirectory + "/" + FilenameUtils.getBaseName(js.getName()) + "'; ";
                    }
                    classNames += "," + className;
                }else if(js.isDirectory()){
                    getJs(js.listFiles(), jsFile);
                }
            }
        }
    }

	//BufferedReader를 이용하여 파일을 읽고 Export되어있는 Class들을 뽑아온다.
    public static String getClassName(File file){
        try {
            BufferedReader bufferedReader = new BufferedReader(new FileReader(file));

            String line;
            String result = "";
            while ((line = bufferedReader.readLine()) != null){
                if(line.startsWith("export class")){
                    result += "," + line.split(" ")[2];
                }
            }
            bufferedReader.close();

            return result.substring(1);
        }catch (Exception e){
            e.printStackTrace();
        }

        return "";
    }
}
반응형

'Java > - Pure Java Project' 카테고리의 다른 글

6. Annotation  (0) 2023.02.09
5. JNDI : DB Connect  (0) 2023.02.08
3. Filter > Controller, Css  (0) 2023.02.06
2. Filter > Js  (0) 2023.02.06
1. 시작  (0) 2023.02.06