java - Project 36-b + 42 = 43-a
전통적인 Client/Server Architecture의 문제점
그나마 데이터는 DBMS에 저장되어 있어서 다행이였음.
Application Server Architecture
DBMS외부에서 직접 DBMS접근 불가 -> 보안이 강화된다
-> 기능이 업그레이드 때마다 재설치가 필요 없다.
익명이너클래스가 호출하는 생성자는 수퍼클래스 생성자이다.
java "-Dfile.encoding=utf-8" -cp bin/main com.eomcs.pms.ClientApp localhost 8888
java Project36-b + 42 = 43-a
out, in 입출력을 맵객체에 담아놓으면 한번에 여러 커맨드가 들어와서 요청할 경우를 처리할 수 없다. 그래서 Request 클래스에 커맨드 객체가 클라이언트의 데이터를 읽고 쓸 때 사용할 스트림 객체를 보관한다.
Request는 뭐하는 앤가? 바구니이다. 내가 필요한 도구를 담아둠.
Map : 주머니, commandpath : 이름, out, in : 도구
객체는 별거 아니다. 이름 붙인 그릇이라고 생각하면 됨!
현재 시스템의 가장 치명적 문제점 : 로컬에서 접속 + 원격에서 접속, 로그인을 시키면 context에 보관되어서 다른사람의 정보가 보여짐. 다른사람을 로그아웃시킬수도 있음.
각각 클라이언트 전용 보관소(전용상자)가 필요한 상태!!
클라이언트와 서버가 접속하는 유저를 식별할 수 있도록 코드를 바꾼다.
서버에 getSession메서드 추가
private static String prepareSession(String sessionInfo) {
String[] values = sessionInfo.split("=");
if(values.length == 2) { // 클라이언트에서 자신의 세션 아이디를 보내왔다면,
// 기존에 서버에서 발급한 세션 아이디를 그대로 리턴한다..
return values[1];
}
// 클라이언트에게 새 세션 아이디를 부여한다.
String sessionId = UUID.randomUUID().toString();
// 새 세션을 위한 보관소를 생성한다.
HashMap<String, Object> sessionMap = new HashMap<>();
// 필터나 커맨드가 사용할 수 있도록 context맵에 저장한다.
context.put(sessionId, sessionMap);
return sessionId;
}
Command가 필요한 객체가 늘 때마다 파라미터에 추가하고 커맨드를 수정하는 것이 매우 번거로우므로 Command 인터페이스를 Request객체를 받는 것으로 바꾼다. 대신 이제 필요한 기능은 Request클래스에 추가한다.
리플렉션 API를 사용하여 커맨드 객체를 자동으로 찾아 인스턴스를 생성한다.
package com.eomcs.pms.listener;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Parameter;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.apache.ibatis.io.Resources;
import com.eomcs.context.ApplicationContextListener;
import com.eomcs.pms.handler.Command;
import com.eomcs.pms.handler.CommandAnno;
// 클라이언트 요청을 처리할 커맨드 객체를 준비한다.
public class RequestMappingListener implements ApplicationContextListener {
Map<String, Object> context;
@Override
public void contextInitialized(Map<String,Object> context) {
try {
// 다른 메서드에서 context 맵 객체를 사용할 수 있도록 인스턴스 필드에 저장한다.
this.context = context;
// 커맨드 클래스가 있는 패키지의 파일 경로를 알아내기
// => Mybatis에서 제공하는 클래스의 도움을 받는다.
File commandPackagePath = Resources.getResourceAsFile("com/eomcs/pms/handler");
System.out.println(commandPackagePath.getCanonicalPath());
// 해당 패키지안에 있는 커맨드 클래스를 찾아 인스턴스를 생성한다.
createCommands(commandPackagePath, "com.eomcs.pms.handler");
// Command 구현체 생성 및 commandMap 객체 준비
Map<String,Command> commandMap = new HashMap<>();
// command객체만 모아놓은 상자를 큰 상자(context)에 보관한다.
context.put("commandMap", commandMap);
// 테스트 용 로그인 사용자 정보 가져오기
//Member member = memberService.get("aaa@test.com", "1111");
//context.put("loginUser", member);
} catch (Exception e) {
System.out.println("서비스 객체를 준비하는 중에 오류 발생!");
e.printStackTrace();
}
}
private Map<String,Object> createCommands(File packagePath, String packageName) {
HashMap<String,Object> commandMap = new HashMap<>();
File[] files = packagePath.listFiles((dir, name) -> name.endsWith(".class"));
for(File f : files) {
// 파일 정보를 가지고 클래스 이름을 알아낸다.
String className = String.format("%s.%s", packageName, f.getName().replace(".class", ""));
try {
// 클래스 이름(패키지명 포함)을 사용하여 .class파일을 로딩한다.
Class<?> clazz = Class.forName(className);
// 패키지에서 찾은 클래스가 Command인터페이스를 구현한 클래스가 아니라면,
// 생성자를 찾지 말고 다음 클래스로 이동한다.
Class<?>[] interfaces = clazz.getInterfaces();
boolean isCommand = false;
for(Class<?> c : interfaces) {
if(c == Command.class) {
isCommand = true;
break;
}
}
if(!isCommand) continue; // 이 클래스는 Command구현체가 아니다.
// 커맨드 클래스에 붙여 놓은 @CommandAnno 애노테이션 정보를 가져온다.
CommandAnno commandAnno = clazz.getAnnotation(CommandAnno.class);
// @CommandAnno 애노테이션이 클래스에 붙어 있지 않다면,
// 해당 커맨드를 저장할 수 없기 때문에 객체를 생성하지 않는다!
if(commandAnno == null) continue;
// 클래스의 생성자 정보를 알아낸다.
Constructor<?> constructor = clazz.getConstructors()[0];
// 생성자의 파라미터 정보를 알아낸다.
Parameter[] params = constructor.getParameters();
// 생성자를 호출할 떄 넘겨 줄 파라미터 값을 담을 배열을 준비한다.
Object[] args = new Object[params.length];
int i = 0;
for(Parameter param : params) {
args[i++] = findDependency(param.getType());
}
Object command = constructor.newInstance(args);
System.out.println(command.getClass().getName() + " 객체 생성 성공..?");
// @CommandAnno 애노테이션에 지정한 커맨드 객체의 이름을 가져와서,
// 커맨드 객체를 저장할 때 key로 사용한다!!
commandMap.put(commandAnno.value(), command);
} catch (Exception e) {
System.out.println(className + " 로딩 중 오류 발생!");
}
}
return commandMap;
}
private Object findDependency(Class<?> type) {
// context맵에서 해당 타입의 객체를 찾는다.
// 1) context맵에 보관된 모든 객체를 꺼낸다.
Collection<?> objs = context.values();
// 2) 각 객체가 파라미터로 받은 타입의 인스턴스인지 확인한다.
for(Object obj : objs) {
if(type.isInstance(obj)) {
return obj;
}
}
return null;
}
@Override
public void contextDestroyed(Map<String,Object> context) {
}
}
command의 이름을 자동으로 알 수 있도록 우리만의 주석 : annotaion을 생성한다.
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
// 커맨드 객체에 붙일 특별한 주석!
// => 커맨드의 이름을 설정하는 용도이다.
// => JVM에서 객체를 생성할 때 사용할 것이다.
@Retention(RetentionPolicy.RUNTIME)
public @interface CommandAnno {
String value();
}
command클래스에 애노테이션을 등록한다.
@CommandAnno("/board/add")
public class BoardAddCommand implements Command {}
이제 클래스 이름만 알면 인스턴스를 자동으로 생성할 수 있게 되었다!
안타깝게도 실무에서는 리플렉션 API를 만들일이 없다. 그러나 혼자서 이런걸 공부하게 된다면 실력이 쑥쑥 늘 것이다..
“API*(Application Programming Interface, 응용 프로그램 프로그래밍 인터페이스)는 응용 프로그램에서 사용할 수 있도록, 운영체제나 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만든 인터페이스를 뜻한다.”
노마드 코더에 의하면 API는 키보드같은거라고 한다. 코드와 코드가 소통하기 위해서 입력체제가 필요한데 키보드 역할을 하는 것이 API인것임.
예를들어 Daum이 지도 데이터를 공개해도 대부분의 사람들은 그 데이터를 가지고 자신에게 유용하게 사용하기가 어려울 것입니다. 호환성의 문제라던가 너무나 정보가 방대해서 다루기 어렵다든가 등이 그 이유가 되겠죠. 생각해보세요. 홈버튼과 터치가 없이 스마트폰을 조작하라고 한다면 너무나 막막하겠죠? 그래서 설계자들은 사용자들에게 스마트폰과 교감을 할 수 있도록 터치 기능과 홈버튼을 집어 넣었습니다. 같은 맥락으로 Daum에선 자사 데이터를 활용하여 사용할 수 있게끔 ‘다음지도API’라는 일종의 ‘홈버튼’을 사용자들에게 공개한 것이죠.
API란 간단하게 이해하면 내가 만든 프로그램이 개인 개발자, 기업 기관이 제공하는 기능, 프로그램 등을 활용할 수 있게끔 도와주는 중간 매개체라는 것.
앞으로 우리가 써야할 외부 로그인기능도 구글 api, 깃허브 api를 사용한다고 생각하면 된다.
톰캣서버 사용자 홈디렉토리/server/ 이곳에 압축푼다.
리눅스 유닉스는 통짜가 파일명이라 뒤의 확장자로 실행파일을 결정하지 않는다.
chmod
커맨드 사용.chmod 754 *sh
-> sh로 끝나는 것들은 다 저렇게 바꿔라.Servelet = 서버 어플리케이션 조각
그레이들 플러그인 추가
eclipse
플러그인 대신에 eclipse-wtp
을 사용한다.war
플러그인 추가클래스에 애노테이션을 붙이면 클래스 정보를 추출할 수 있구나!
그럼 우린 객체를 생성해서 자동으로 관리할 수 있구나!
코드 한줄한줄 이해하는 것 보다 전체적인 그림을 보기
주말에 진짜 공부 열심히하고싶다 -> html공부하세요