1. 관심사의 분리 Separation of Concerns

oop 5대 설계원칙

1 SRP ( Single Responsibility Priciple )
단일책임 원칙
하나의 메서드는 하나의 책임만 진다.
( 1개의 매서드에는 1개의 관심사만 )
2 OCP ( Open Close Principle )
개방폐쇄 원칙
변하는 것과 변하지 않는것의 분리
3 LSP ( Liskov Substitution Principle )
리스코프치환 원칙
하위클래스는 인터페이스 규약을 지키기
4 LSP ( Interface Segregation Priciple )
인터페이스분리 원칙
범용하나보다는 인터페이스 여러개로
5 DIP ( Dependecy Inversion Principle )
의존관계 역전 원칙
구체화 의존하지말고, 추상화에 의존 하도록

개발시 분리 체크 대표 3가지

1 관심사 분리했는가?
2 변하는것, 자주 변하지 않는것 분리했는가? ( common, uncommon )
3 공통중복코드 분리 했는가?

 

2. 공통 코드의 분리 - 입력의 분리

 

3. 출력(view)의 분리 - 변하는 것과 변하지 않는 것의 분리

 

 

 

4. Model


메서드 만드는 Refactor 


스프링 View 경로설정


반환타입을 주지않으면 맵핑된 주소.jsp가 열린다.


5. ModelAndView 반환타입    ( 잘사용하진 않는다고함;)

Model + View 를 합친다. ( Model model 매개변수를 받지 않고)

 

6. 컨트롤러 메서드의 반환타입

반환타입이 String이면,   View이름을 반환해야한다.
반환타입이 void이면,   맵핑주소가 View이름이 된다.
반환타입이 ModelAndView이면,  컨트롤 내부에서 ① ModelAndView 생성
                                                                                ② 작업 후 결과 ModelAndView 에 저장
                                                                                ③ setViewName() 으로 View 이름지정
                                                                                ④ ModelAndView 참조변수를 반환

 

 

 


 

MVC패턴 원리


 

매개변수이름을 얻는방법

 ① Reflection API   :  -parameters옵션을 넣고 컴파일해야함.  ( Java8, Jdk1.8 부터 가능 )

 ② Class파일 얻어오기


예제 1  ( Reflection API 사용 ) 

※ StringJoiner( 구분자, 접두사, 접미사)


예제 2

 


리플랙션이란?
 : 자바의 클래스, 인터페이스 내에 어떤 멤버들이 있는지 조사하는 것

Class 객체를 얻는 방법 3가지
                        

  메서드 용도
1 Class clazz = 클래스이름 .class;    [ 클래스로부터 얻는방법 ]
2 Class clazz = Class.forName("패키지... 클래스이름");    [ 클래스로부터 얻는방법 ]
3 Class clazz = 객체참조변수.getClass();  [ 객체로부터 얻는방법 ]



패키지와 타입정보 얻기
  메서드 용도 예시
1 Package getPackage() 패키지 정보 읽기 패키지 : ch12.com.exam01
2 String getSimpleName() 패키지를 제외한 타입이름 클래스 간단  : Car
3 String getName() 패키지를 포함한 전체 타입 이름 클래스 전체이름 : ch12.com.exam01.Car

멤버 정보얻기
  메서드 용도
1 Constructorof[] getDeclaredConstructors() 생성자 정보읽기
2 Field[] getDeclaredFields() 필드(멤버변수) 정보 읽기
3 Method[] getDeclaredMthods()  메서드 정보읽기

리소스 경로 얻기
  메서드 용도
1 URL getResouce(String name) 리소스 파일의 URL 리턴 (단순경로)
2 InputStream getResourceAsStream(String name) 리소스 파일의 InputStream 리턴
( 경로의 파일을 열어 그안에 데이터를 읽음)

Invoke 메서드 : Method를 동적으로 실행시켜 주는 메서드


 

 


예제 3 ( Refalction API 이용 )

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개, 모델   (일치필요)
		
		// 3. Model을 생성
		Model model = new BindingAwareModelMap(); // BindingAwareModelMap는 Model(인터페이스)를 구현한 것 
		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를 이용한 호출	
									     // 1번에서 만든 객체, 배열로 받게 되있음( main 메서드의 매개변수 String args~ )
		
		
		// 여기에 맞는 메서드를 찾아서 가져온다.
		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);
	}
}


예제 3

public class MethodCall3 {
	public static void main(String[] args) throws Exception{
		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을 원격프로그램으로

@WebServlet이란?
스프링의 @Controller(클래스앞) + @ReqeustMapping(메서드앞) 이 합쳐진 것

서블릿은...
① HttpServlet을 꼭 상속받아야한다.
② 메서드이름은 Service라는 이름으로 꼭 해야한다.
③ 메서드 매개변수는 (HttpServletReqeust request, HttpServlet reponse) 를 꼭 적어줘야한다.
④ 서블릿에서는 메서드단위 맵핑 x, 클래스단위로만 가능
⑤ 그래서, 서블릿은 스프링에 비해서 서블릿을 많이 만들어야하는 단점이 있음.

 

 


참고 :

남궁성, 스프링의 정석

 

+ Recent posts