Spring

15. 관심사의 분리와 MVC패턴/원리(3) - 패스트캠퍼스 백엔드 부트캠프 3기

gkss2tpt 2025. 2. 3. 12:34

1.  ReflectAPI를 이용한 메서드 호출

public class MethodCall2 {
    public static void main(String[] args) throws Exception{

        // 1. YoilTellerMVC의 객체를 생성
        Class clazz = Class.forName("com.fastcampus.ch2.YoilTellerMVC");	// 클래스 로드
        Object obj = clazz.newInstance();	// 클래스 객체 생성
        
        // 2. main메서드의 정보를 가져온다.
        Method main = clazz.getDeclaredMethod("main", int.class, int.class, int.class, Model.class);
        
        // 3. Model을 생성(Model은 인터페이스라 생성자 호출이안됨)
        Model model = new BindingAwareModelMap();
        System.out.println("[before] model="+model);
        
        // 4. main메서드를 호출
        // String viewName = obj.main(2021, 10, 1, model); // 아래 줄과 동일
        String viewName = (String)main.invoke(obj, new Object[] { 2021, 10, 1, model }); // Reflection API를 이용한 호출     
        System.out.println("viewName="+viewName);    
        
        // Model의 내용을 출력 
        System.out.println("[after] model="+model);
                
        // 텍스트 파일을 이용한 rendering
        render(model, viewName);            
    } // main
    
    static void render(Model model, String viewName) throws IOException {
        String result = "";
        
        // 1. 뷰의 내용을 한줄씩 읽어서 하나의 문자열로 만든다.
        Scanner sc = new Scanner(new File("src/main/webapp/WEB-INF/views/"+viewName+".jsp"), "utf-8");
        
        while(sc.hasNextLine())
            result += sc.nextLine()+ System.lineSeparator();
        
        // 2. model을 map으로 변환 
        Map map = model.asMap();
        
        // 3.key를 하나씩 읽어서 template의 ${key}를 value바꾼다.
        Iterator it = map.keySet().iterator();
        
        while(it.hasNext()) {
            String key = (String)it.next();

            // 4. replace()로 key를 value 치환한다.
            result = result.replace("${"+key+"}", ""+map.get(key));
        }
        
        // 5.렌더링 결과를 출력한다.
        System.out.println(result);
    }
}

 

2. 동적으로 할당하기

public class MethodCall3 {
    public static void main(String[] args) throws Exception{
        // 1. 요청할 때 제공된 값 - request.getParameterMap();
        Map map = new HashMap();
        map.put("year", "2021");
        map.put("month", "10");
        map.put("day", "1");

        Model model = null;
        Class clazz = Class.forName("com.fastcampus.ch2.YoilTellerMVC");
        Object obj  = clazz.newInstance();
        
        // YoilTellerMVC.main(int year, int month, int day, Model model)
        Method main = clazz.getDeclaredMethod("main", int.class, int.class, int.class, Model.class);
                    
        Parameter[] paramArr = main.getParameters();    // main메서드의 매개변수 목록을 가져온다.
        Object[] argArr = new Object[main.getParameterCount()];    // 매개변수 개수와 같은 길이의 Object배열을 생성
        
        for(int i=0;i<paramArr.length;i++) {
            String paramName = paramArr[i].getName();
            Class  paramType = paramArr[i].getType();
            Object value = map.get(paramName); // map에서 못찾으면 value는 null

            // paramType중에 Model이 있으면, 생성 & 저장 
            if(paramType==Model.class) {
                argArr[i] = model = new BindingAwareModelMap(); 
            } else if(value != null) {  // map에 paramName이 있으면,
                // value와 parameter의 타입을 비교해서, 다르면 변환해서 저장  
                argArr[i] = convertTo(value, paramType);                
            } 
        }
        System.out.println("paramArr="+Arrays.toString(paramArr));
        System.out.println("argArr="+Arrays.toString(argArr));
        
        
        // Controller의 main()을 호출 - YoilTellerMVC.main(int year, int month, int day, Model model)
        String viewName = (String)main.invoke(obj, argArr);     
        System.out.println("viewName="+viewName);    
        
        // Model의 내용을 출력 
        System.out.println("[after] model="+model);
                
        // 텍스트 파일을 이용한 rendering
        render(model, viewName);            
    } // main
    
    private static Object convertTo(Object value, Class type) {
        if(type==null || value==null || type.isInstance(value)) // 타입이 같으면 그대로 반환 
            return value;

        // 타입이 다르면, 변환해서 반환
        if(String.class.isInstance(value) && type==int.class) { // String -> int
            return Integer.valueOf((String)value);
        } else if(String.class.isInstance(value) && type==double.class) { // String -> double
            return Double.valueOf((String)value);
        }
            
        return value;
    }
    
    private static void render(Model model, String viewName) throws IOException {
        String result = "";
        
        // 1. 뷰의 내용을 한줄씩 읽어서 하나의 문자열로 만든다.
        Scanner sc = new Scanner(new File("src/main/webapp/WEB-INF/views/"+viewName+".jsp"), "utf-8");
        
        while(sc.hasNextLine())
            result += sc.nextLine()+ System.lineSeparator();
        
        // 2. model을 map으로 변환 
        Map map = model.asMap();
        
        // 3.key를 하나씩 읽어서 template의 ${key}를 value바꾼다.
        Iterator it = map.keySet().iterator();
        
        while(it.hasNext()) {
            String key = (String)it.next();

            // 4. replace()로 key를 value 치환한다.
            result = result.replace("${"+key+"}", ""+map.get(key));
        }
        
        // 5.렌더링 결과를 출력한다.
        System.out.println(result);
    }
}

 

3. 웹에서 요청 받기

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.ui.Model;
import org.springframework.validation.support.BindingAwareModelMap;

// 빨간줄
// @Controller + @RequestMapping
@WebServlet("/myDispatcherServlet")  // http://localhost/ch2/myDispatcherServlet?year=2021&month=10&day=1
public class MyDispatcherServlet extends HttpServlet {
    @Override
    public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
        Map    map = request.getParameterMap();
        Model  model = null;
        String viewName = "";
        
        try {
            Class clazz = Class.forName("com.fastcampus.ch2.YoilTellerMVC");
            Object obj = clazz.newInstance();
            
                  // 1. main메서드의 정보를 얻는다.
            Method main = clazz.getDeclaredMethod("main", int.class, int.class, int.class, Model.class);
            
                  // 2. main메서드의 매개변수 목록(paramArr)을 읽어서 메서드 호출에 사용할 인자 목록(argArr)을 만든다.
            Parameter[] paramArr = main.getParameters();
            Object[] argArr = new Object[main.getParameterCount()];

            for(int i=0;i<paramArr.length;i++) {
                String paramName = paramArr[i].getName();
                Class  paramType = paramArr[i].getType();
                Object value = map.get(paramName);

                // paramType중에 Model이 있으면, 생성 & 저장 
                if(paramType==Model.class) {
                    argArr[i] = model = new BindingAwareModelMap();
                } else if(paramType==HttpServletRequest.class) {
                    argArr[i] = request;
                } else if(paramType==HttpServletResponse.class) {
                    argArr[i] = response;                    
                } else if(value != null) {  // map에 paramName이 있으면,
                    // value와 parameter의 타입을 비교해서, 다르면 변환해서 저장 
                    String strValue = ((String[])value)[0];    // getParameterMap()에서 꺼낸 value는 String배열이므로 변환 필요 
                    argArr[i] = convertTo(strValue, paramType);                
                } 
            }
            
            // 3. Controller의 main()을 호출 - YoilTellerMVC.main(int year, int month, int day, Model model)
            viewName = (String)main.invoke(obj, argArr);     
        } catch(Exception e) {
            e.printStackTrace();
        }
                
        // 4. 텍스트 파일을 이용한 rendering
        render(model, viewName, response);            
    } // main
    
    private Object convertTo(Object value, Class type) {
        if(type==null || value==null || type.isInstance(value)) // 타입이 같으면 그대로 반환 
            return value;
        
        // 타입이 다르면, 변환해서 반환
        if(String.class.isInstance(value) && type==int.class) { // String -> int
            return Integer.valueOf((String)value);
        } else if(String.class.isInstance(value) && type==double.class) { // String -> double
            return Double.valueOf((String)value);
        }
            
        return value;
    }
        
    private String getResolvedViewName(String viewName) {
        return getServletContext().getRealPath("/WEB-INF/views") +"/"+viewName+".jsp";
    }
    
    private void render(Model model, String viewName, HttpServletResponse response) throws IOException {
        String result = "";
        
        response.setContentType("text/html");
        response.setCharacterEncoding("utf-8");
        PrintWriter out = response.getWriter();
        
        // 1. 뷰의 내용을 한줄씩 읽어서 하나의 문자열로 만든다.
        Scanner sc = new Scanner(new File(getResolvedViewName(viewName)), "utf-8");
        
        while(sc.hasNextLine())
            result += sc.nextLine()+ System.lineSeparator();
        
        // 2. model을 map으로 변환 
        Map map = model.asMap();
        
        // 3.key를 하나씩 읽어서 template의 ${key}를 value바꾼다.
        Iterator it = map.keySet().iterator();
        
        while(it.hasNext()) {
            String key = (String)it.next();

            // 4. replace()로 key를 value 치환한다.
            result = result.replace("${"+key+"}", map.get(key)+"");
        }
        
        // 5.렌더링 결과를 출력한다.
        out.println(result);
    }
}
  • Tomcat 라이브러리 변경

Build Path -> Configure Build Path
Libraries -> Classpath -> add Library
Server Runtime
Tomcat Library생성

  • 결과