KOSA fullstack 교육(Ajax 실습, myBatis)
알고리즘을 이용한 간단한 ajax 실습을 해보았다.
먼저 최빈값 알고리즘을 이용해서 문자열 중에 가장 많은 문자열을 출력하는 알고리즘을 servlet을 통해 찍어보자
Count.java
Kickboard.java
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Java Call</title>
<script src="http://code.jquery.com/jquery-3.1.1.js"></script>
<script type="text/javascript">
</script>
<style type="text/css">
.container{
display: flex;
justify-content: space-around;
margin: 100px;
}
.quest{
border-radius: 20px;
border: 10px dotted DodgerBlue;
padding: 50px;
width: 500px; height: 350px;
background-color: rgb(234,234,234);
}
h2{
height: 50px;
color: Violet;
}
.kick{
width : 150px;
}
*{
box-sizing: border-box;
margin: 0 auto;
}
.button {
background-color:MediumSeaGreen; /* Green */
border: none;
color: white;
padding: 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin: 4px 2px;
cursor: pointer;
}
.button4 {border-radius: 12px;}
input[type=button]{
margin-top: 20px;
}
#result1, #result2 {
font-size : 1.8em;
color: red;
}
</style>
</head>
<body>
<div class="container">
<div id="q1" class="quest">
<h2>단어세기</h2>
<div><span id="ins">입력 문자열 : </span><textarea id="words" cols="45" rows="5"></textarea> </div>
<div><input type="button" id="exec1" class="button button4" value="실 행"> 가장 많이 나온 단어 : <span id="result1" ></span></div>
</div>
<div id="q2" class="quest">
<h2>Kickboard</h2>
<div><span class="kick">입력 파일명:</span><input type="text" id="fname" ></div>
<div> <input type="button" id="exec2" class="button button4" value="실 행"> 갈수 있는 경로의 수 : <span id="result2"> </span></div>
</div>
</div>
</body>
</html>
기본 코드가 있고
다음은 구현된 최빈값 알고리즘과 킥보드 알고리즘이다.
package com.algo;
import java.util.HashMap;
import java.util.HashSet;
import java.util.StringTokenizer;
public class Count {
public String execute(String book) {
book = book.toLowerCase().replaceAll("[^a-z ]", "");
String[] strings = book.split(" ");
HashMap<String, Integer> map = new HashMap<>();
int max = 0;
String s = "";
for (String word : strings) {
if (word.isEmpty()) continue;
map.put(word, map.getOrDefault(word, 0) + 1);
int count = map.get(word);
if (count > max || (count == max && word.compareTo(s) < 0)) {
max = count;
s = word;
}
}
return s;
}
public static void main(String[] args) {
String book1 ="Can Danny and his father outsmart the villainous Mr. Hazell? Danny has a life any boy would love - his home is a gypsy caravan, he's the youngest master car mechanic around, and his best friend is his dad, who never runs out of wonderful stories to tell. But one night Danny discovers a shocking secret that his father has kept hidden for years. ";
String book2 ="Soon Danny finds himself the mastermind behind the most incredible plot ever attempted against nasty Victor Hazell, a wealthy landowner with a bad attitude. Can they pull it off? If so, Danny will truly be the champion of the world.";
String book3 ="I like cat. I like cat. I like cat. ";
Count c = new Count();
System.out.println(c.execute(book1));
System.out.println(c.execute(book2));
System.out.println(c.execute(book3));
}
}
package com.algo;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class Kickboard {
static int M;
static int N;
static int[][] map;
static boolean[][] visited;
public static int execute(File path) throws FileNotFoundException {
Scanner sc = new Scanner(path);
M = sc.nextInt();
N = sc.nextInt();
map = new int[M][N];
memo = new int[M][N];
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++)
map[i][j] = sc.nextInt();
}
visited = new boolean[M][N];
int result = dfs(0,0);
//System.out.println(result);
return result;
}
static int[] dr = { -1, 1, 0, 0 };
static int[] dc = { 0, 0, -1, 1 };
static int[][] memo;
static int cnt = 0;
static int dfs(int r, int c) {
cnt++;
//System.out.println(r + " " + c);
if (r == M-1 && c == N-1)
return 1;
if (memo[r][c] != 0)
return memo[r][c];
if(!visited[r][c]) {
visited[r][c] = true;
for (int i = 0; i < 4; i++) {
int nr = r + dr[i];
int nc = c + dc[i];
if (nr < 0 || nc < 0 || nr >= M || nc >= N)
continue;
if (map[nr][nc] < map[r][c]) {
memo[r][c] += dfs(nr, nc);
}
}
}
return memo[r][c];
}
public static void main(String[] args) throws FileNotFoundException {
System.out.println(execute(new File("input.txt")));
}
}
이제 이 알고리즘을 연동시켜서 버튼을 눌렀을 때 비동기로 실행되도록 바꿔보자
$(function() {
$('#exec1').on('click', function() {
let words = $('#words').val();
$.ajax({
type: 'post',
url: 'front.do?command=count',
data: {'words': words},
success: function(result){
$('#result1').html(result);
}
});
});
$('#exec2').on('click', function() {
let fname = $('#fname').val();
$.ajax({
type: 'post',
url: 'front.do?command=kickboard',
data: {'fname': fname},
success: function(result){
$('#result2').html(result);
}
});
});
});
url의 front.do로 보낼 서블릿을 생성해보자
package com.servlet;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.algo.Count;
import com.algo.Kickboard;
@WebServlet("/front.do")
public class MainServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public MainServlet() {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doProcess(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doProcess(request, response);
}
protected void doProcess(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String command = request.getParameter("command");
if(command.equals("count")) {
wordCount(request, response);
}else if(command.equals("kickboard")) {
kickboardCount(request, response);
}
}
public void kickboardCount(HttpServletRequest request, HttpServletResponse response) throws IOException {
String fname = request.getParameter("fname");
String realPath = request.getServletContext().getRealPath("/res/"+fname);
// String path = request.getServletPath() + "/res/" + fname;
System.out.println(realPath);
File file = new File(realPath);
int result = new Kickboard().execute(file);
PrintWriter out = response.getWriter();
out.print(result);
}
private void wordCount(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String string = request.getParameter("words");
String result = new Count().execute(string);
request.setAttribute("result", result);
request.getRequestDispatcher("Result.jsp").forward(request, response);
}
}
여기서 중요한 점은 path를 ServletPath()로 만들면 /front.do 부터 시작하는 URL경로 문자열을 생성하기 때문에 File 객체를 만들 때 작동하지 않는다.
이렇게 되면 비동기로 해당 알고리즘이 작동하는 것을 볼 수 있다.
이전까지 web에 대해 배웠다.
SE와 EE의 차이를 명확히 하고 Spring에 들어가야 한다.
이전까지 했던 자바 web 프로젝트 원리를 간단히 그려보면 다음과 같다.
위와 같은 로직을 레이어로 쪼개는 것을 Architectrue Layer라고 하고,
두 레이어를 각각 Presentaion Layer / Business Logic Layer 라고 한다.
Business Logic Layer는 Service / Repository로 나뉜다.
서블릿은 컨테이너가 만들어주고, 자바는 내가 만들었다. 하지만 이제 프레임워크에 들어가면 자바도 내가 안만들어도 된다!
이러한 기술을 DI라고 하고 IOC 기법이 적용된다. IOC는 객체 생성의 주체가 컨테이너로 넘어가는 것이다
→ 이걸 가능하게 해주는 게 DI(Dependency Injection)
→ 이 개념의 기반이 되는 것이 IoC(Inversion of Control)
Spring MVC + MyBatis + IOC 를 Spring Framework라고 한다.
라이브러리는 완벽하게 모듈화 되어져있다.
그래서 앞으로 배울 기술로 갈아끼울 것이다.
Data Access는 MyBatis(JPA, Spring JDBC)로, Web (MVC)는 Spring MVC로...
Core Container 는 DI로 보면 된다.
배울 순서는 DI → MyBatis → Spring MVC로 갈 것이다. (Maven기반으로...)
최종으로는 세가지를 모두 연계하여 SpringBoot기반으로 갈 것이다.
DI실습
프로젝트 파일을 하나 생성해보자
다음과같이 add Folder로 폴더를 추가한다.
src/main/java에는 java code
src/main/resources에는 Configuraion file
src/test/java에는 UnitTest testClass
package spring.service.hello;
/*
* FileName : Hello.java
* ㅇ package / private Field(property) / Constructor / getter,setter / 필요한 Method
* ㅇ를 갖는 빈 규약을 준한 일반적인 Bean :: POJO(Plain Old Java Object)
*/
public class Hello{
///Field
private String message = "Container 란 ?????";
///Constructor Method
public Hello() {
}
public Hello(String message) {
this.message = message;
}
///Method
//==> getter / setter Method definition
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
//==> message 를 출력하는 Method
public void printMessage() {
System.out.println("\n"+getClass().getName()+"=>"+message);
}
}//end of class
여기서의 POJO(Plain Old Java Object)는 아무것도 담지 않은, 어떤 것도 제약받지 않은 java클래스 본연의 상태라고 한다.
예를들어 extends, implements를 안 한 상태..
package spring.service.hello.test;
import spring.service.hello.Hello;
/*
* FileName : HelloTestApp01.java
* ㅇ Hello.class 사용하는 Application
*/
public class HelloTestApp01 {
///main method
public static void main(String[] args) {
//==>1. instance 생성
Hello hello = new Hello();
//==> 2. printMessage() method 호출
hello.printMessage();
}
}//end of class
/*
* [ 고려할 사항 :: 다른 Bean 사용 및 출력 Message 변경 시... ]
*
* 1. new Hello() 의 instance 생성 하드코딩
* - Hello.class, 하위 class 또는 다른 class 사용을 할 경우
* ==> 다시 코딩 후 컴파일 과정 수행
*
* 2. 출력할 message 하드 코딩
* - 다른 message 를 출력코자 한다면
* ==> 다시 코딩 후 컴파일 과정 수행 ( :: setter Method 를 사용하지 않은 경우로 한정 )
*
* ==> 위의 하드코딩된 부분(1.instance 생성, 2.출력 message)을
* text file(부가정보,Meta Data) 에 저장하고 Application 은 runtime 시 text 에 저장된 내용을
* 읽어 수행하는 구조라면, 사용 할 instance/message 에 변경사항이 발생하면 text만 수정
* Application 은 text 를 통해 전달된 정보를 사용 함으로 Bean 은 수정 할 필요가 없다
* ( text/메타데이터의 활용 )
*/
package spring.service.hello;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
/*
* FileName : HelloFactory.java
* ㅇ hello.properties file(부가정보,Meta-Data) 의 내용을 읽어 Hello instance 를 생성 return.
*/
public class HelloFactory {
///Field
private Hello hello;
private Properties properties;
private static HelloFactory helloFactory;
///Constructor Method
private HelloFactory(){
}
///Method
//==> HelloFactory instance 를 return 하는 static method(SingleTon)
public synchronized static HelloFactory getInstance() {
if( helloFactory == null){
helloFactory = new HelloFactory();
}
return helloFactory;
}
//==> properties file 을 추상화,캡슐화한 java.util.Properties instance 생성
public void setConfigResource(String configResource) {
FileInputStream fis = null;
try {
fis = new FileInputStream(configResource);
properties = new Properties();
properties.load(fis);
} catch (FileNotFoundException e) {
e.printStackTrace();
throw new RuntimeException("1. hello.proerties 파일을 찾을수 없음.");
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("2. hello.proerties 파일 초기화 시 오류 발생.");
}finally{
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//==> name 에 해당하는 instance 생성:: default constructor
private void newInstanceHello(String name){
String className = properties.getProperty(name).trim(); //==> trim() 앞뒤 공백 trim
System.out.println("!! debug=>hello.properties 에서 추출한 className : "+className);
try {
Class cls = Class.forName(className);
Object obj = cls.newInstance();
//==> Hello Instance 확인
if( obj instanceof Hello){
this.hello = (Hello)obj;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new RuntimeException("1. "+className +"의 이름을 갖는 class가 없음.");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("2. "+className +"의 instance 생성시 문제 발생.");
}
}
//==> Hello instance 생성하는 newInstanceHello() 호출 및 Hello instance 를 return
public Hello getBean(String name){
this.newInstanceHello(name);
return hello;
}
}//end of class
package spring.service.hello.test;
import spring.service.hello.Hello;
import spring.service.hello.HelloFactory;
/*
* FileName : HelloTestApp02.java
* ㅇ Hello.class 를 사용하는 Application
* ㅇ [ HelloTestApp01과 비교 ]
* - 사용할 Bean 를 hello.properties(meta-data)에 text 로 저장 하고...
* - instance 를 직접 생성하지 않으며, HelloFactory 에서 return 되는 instance 사용.
*/
public class HelloTestApp02 {
///main method
public static void main(String[] args) {
// 1. 사용할 Bean 정보를 갖는 hello.properties(meta-data)를
// parsing, Bean instance를 생성할 HelloFactory instance 생성
HelloFactory helloFactory = HelloFactory.getInstance();
// 2. helloFactory instance 로 parsing 할 resource::meta-data(hello.properties) 전달
helloFactory.setConfigResource("./src/main/resources/config/hello.properties");
// 3. helloFactory instance 로 부터 instance.one 이름을 갖는 Hello instance Request
Hello hello = helloFactory.getBean("instance.one");
// 4. printMessage() 호출
hello.printMessage();
}
}//end of class
/*
* [[[[[[ HelloTestApp01 고려할 사항 ]]]]]] 과 비교
*
* [ 고려할 사항 :: 다른 Bean 사용 및 출력 Message 변경 시... ]
*
* 1. new Hello() 의 instance 생성 하드코딩
* - Hello.class, 하위 class 또는 다른 class 사용을 할 경우
* ==> 다시 코딩 후 컴파일 과정 수행
* [[[[[[ HelloTestApp02 변경 ]]]]]]
* : 다른 Bean 사용할 경우 hello.properties 의 내용을 변경한다면....
* ==>[한번더 고려...]
* ==> 현재는 Data Type Hello 임으로 Hello 만 생성 관리 가능
* ==> 다른 Data type 의 생성은....
* (HelloFactory는 Hello Type 만 생성 관리 한다....)
*
* 2. 출력할 message 하드 코딩
* - 다른 message 를 출력코자 한다면
* ==> 다시 코딩 후 컴파일 과정 수행 ( :: setter Method 를 사용하지 않은 경우로 한정 )
* [[[[[[ HelloTestApp02 변경 ]]]]]]
* : HelloTestApp02는 미적용 다음예제에서...
* ==> hello.properties 에 Message 를 저장 불러올 수 있다면....
*
*
* ==> 위의 하드코딩된 부분(1.instance 생성, 2.출력 message)을
* text file(부가정보,Meta Data) 에 저장하고 Application 은 runtime 시 text 에 저장된 내용을
* 읽어 수행하는 구조라면, 사용 할 instance/message 에 변경사항이 발생하면 text 만 수정
* Application 은 text 를 통해 전달된 정보를 사용 함으로 Bean 은 수정 할 필요가 없다
* ( text/메타데이터의 활용 )
*/
# use instance
instance.one=spring.service.hello.Hello
방금의 코드를 설명하자면 다음과 같다.
1. Hello 클래스 → 우리가 사용할 실제 객체 (Bean)
- 일반 Java 클래스. 우리가 직접 작성하는 비즈니스 로직.
2. HelloFactory → Hello 객체를 대신 만들어주는 공장 (DI 컨테이너)
- 객체를 직접 만들지 않고 대신 만들어주는 컨테이너(factory) 역할.
- 내부적으로 설정파일(.properties)을 읽고, 그에 따라 객체(Hello)를 생성함.
- getBean(“이름”) 으로 필요한 객체를 꺼내올 수 있음.
3. ~.properties 파일 → Hello 객체를 어떻게 만들지 설정한 설정파일
- 설정 파일. 어떤 클래스(Hello)를 어떤 이름으로 생성할지 지정.
4. HelloTest02 → Hello 객체가 필요한 사용자 (클라이언트)
- Hello 객체를 사용하는 주체 (ex. Controller, Service 등).
- 직접 new Hello() 하지 않고, HelloFactory의 getBean() 메서드로 객체를 받아 씀.
5. getBean(”~”) → HelloFactory에서 Hello 객체를 꺼내오는 메서드
추가로 파일을 더 넣어보자
package spring.service.hello.test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
import spring.service.hello.Hello;
/*
* FileName : HelloTestApp03UseSpring.java
* ㅇ Hello.class 을 사용하는 Application
* ㅇ Spring Framework 에서 제공하는 API를 사용하여 instance 생성
* [[[ HelloTestApp02와 비교 ]]]
* - hello.properties ==> hello.xml
* - HelloFactory ==> org.springframework.beans.factory.BeanFactory
==> Spring Framework 은 다양한 메타데이터 중 xml 을 일반적으로 사용하며,
instance 생성, 초기화, 객체의 상호 관계를 xml 에 선언적으로 기술.
( 사용자는Meta-Data 만 변경,수정 만으로 code 의 수정없이 instance 를 가능)
<bean id="hello" class="spring.services.hello.Hello"/>
==> HelloFactory : Hello Data type 만 관리 하는 Factory
==> Spring Framework :모든 Data Type(Bean/POJO) 생성관리하는 Factory
*/
public class HelloTestApp03UseSpring {
/// main method
public static void main(String[] args) {
//1. BeanFactory(Spring Container(?))을 이용 xml 에 선언적으로 기술된 instance 정보 획득
BeanFactory factory =
new XmlBeanFactory( new FileSystemResource
("./src/main/resources/config/hello.xml") );
//2. Container 로 부터 hello 의 name 을 갖는 instance return 받기
Hello hello = (Hello)factory.getBean("hello");
hello.printMessage();
}
}//end of class
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="hello" class="spring.service.hello.Hello"></bean>
<!--
ㅇ Hello instance 생성 서술적, 선언적 기술
1. bean element
- id : instance identifier
- class : instance 생성 Bean (FQCN)
-->
</beans>
**이제 객체를 내가 안만들고 Bean Factory Container에서 만든다 !!**
설정 문서도 싱글톤 패턴으로 작동한다.
설정 문서에서는 인터페이스는 언급되지 않고, 실체 클래스만 거론된다.
Hello를 DAO클래스로 이해해보면 다음과 같은 그림이 나온다