익명 채팅방

package kh.spring.controller;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
	
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
	
	/**
	 * Simply selects the home view to render by returning its name.
	 */
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);
		
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		
		String formattedDate = dateFormat.format(date);
		
		model.addAttribute("serverTime", formattedDate );
		
		return "home";
	}
	
}
package kh.spring.chat;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.RemoteEndpoint.Basic;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/chat")
public class WebChat {

	private static Set<Session> clients = new HashSet<>();

	@OnOpen
	public void onConnect(Session client) {
		System.out.println(client.getId()+" 클라이언트가 접속했습니다.");
		clients.add(client);
	}

	@OnMessage
	public void onMessage(Session session, String message) {
		for(Session client : clients) {

			if(!client.getId().contentEquals(session.getId())) {

				Basic basic = client.getBasicRemote();
				try {
					basic.sendText(message);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}


	}

	@OnClose
	public void onClose(Session session) {
		clients.remove(session);
	}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>

<style>
.chat-box {
	width: 500px;
	height: 500px;
	float: left;
}

.message-area {
	width: 100%;
	height: 90%;
	border: 1px solid #616161;
	overflow-y: auto;
	word-break: break-all;
}

.input-area {
	width: 100%;
	height: 10%;
	border: 1px solid #616161;
	overflow-y: auto;
}
</style>
<script>

	
	$(function() {
		var ws = new WebSocket("ws://localhost/chat")
		
		ws.onmessage = function(e){
			console.log(e.data);
			var line = $("<div>");
			line.append(e.data);
			$(".message-area").append(line);
		}

		
		
		$(".input-area").on("keydown", function(e) {
			if (e.keyCode == 13) {

				e.preventDefault();
				var text = $(".input-area").html();
				var line = $("<div>");
				line.append("글쓴이 : " + text);
				$(".message-area").append(line);
				$(".input-area").html("");
				
				ws.send(text);
				
				$('.message-area').scrollTop($('.message-area')[0].scrollHeight);
				return false;
			}
		})

	})
</script>
</head>
<body>
	<div class="chat-box">
		<div class="message-area"></div>
		<div class="input-area" contenteditable="true"></div>
	</div>
</body>
</html>
	<!-- 웹소캣 라이브러리 추가 -->
		<dependency>
			<groupId>javax.websocket</groupId>
			<artifactId>javax.websocket-api</artifactId>
			<version>1.1</version>
		</dependency>
	

HTTP 프로토콜이 아니라 웹소캣으로 접속하기 때문에

ip 주소와 웹 정보를 가져올수없음

session configurator 

 

 

HTTPSessionConfigurator

object형을 리턴하기에 캐스팅 해줘야함

Map 방식

 

WebChat의 @ServerEndpoint 인자값 추가, 만든 httpSessionConfigurator 자료형 추가

핸드쉐이크로 만들어진 값이 config로 들어옴

 

다른 메서드에서도 쓸수 있도록

HomeController에서 session 사용 가능


서버에서 접속 url 수정 가능


package kh.spring.chat;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import javax.servlet.http.HttpSession;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.RemoteEndpoint.Basic;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import kh.spring.config.HTTPSessionConfigurator;

@ServerEndpoint(value="/chat",configurator = HTTPSessionConfigurator.class)
public class WebChat {

	private static Set<Session> clients = new HashSet<>();
	private HttpSession session;

	@OnOpen
	public void onConnect(Session client, EndpointConfig config) {
		System.out.println(client.getId()+" 클라이언트가 접속했습니다.");
		clients.add(client);
		this.session = (HttpSession) config.getUserProperties().get("session");
	}

	@OnMessage
	public void onMessage(Session session, String message) {
		
		String id = (String)this.session.getAttribute("loginId");
		
		for(Session client : clients) {
			if(!client.getId().contentEquals(session.getId())) {
				Basic basic = client.getBasicRemote();
				try {
					basic.sendText(id+ " : "+message);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	@OnClose
	public void onClose(Session session) {
		clients.remove(session);
	}
}
package kh.spring.config;

import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import javax.websocket.server.ServerEndpointConfig.Configurator;

public class HTTPSessionConfigurator extends Configurator{

	@Override
    public void modifyHandshake(
    		ServerEndpointConfig sec, 
    		HandshakeRequest request, 
    		HandshakeResponse response) {
		
		HttpSession session = (HttpSession)request.getHttpSession();
		sec.getUserProperties().put("session", session);
        
    }

}
package kh.spring.controller;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;


@Controller
public class HomeController {
	
	
	@Autowired
	private HttpSession session;
	

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {

		this.session.setAttribute("loginId", "Jack");
		
		return "home";
	}
	
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>

<style>
.chat-box {
	width: 500px;
	height: 500px;
	float: left;
}

.message-area {
	width: 100%;
	height: 90%;
	border: 1px solid #616161;
	overflow-y: auto;
	word-break: break-all;
}

.input-area {
	width: 100%;
	height: 10%;
	border: 1px solid #616161;
	overflow-y: auto;
}
</style>
<script>

	
	$(function() {
		var ws = new WebSocket("ws://192.168.60.7/chat")
		
		ws.onmessage = function(e){
			console.log(e.data);
			var line = $("<div>");
			line.append(e.data);
			$(".message-area").append(line);
		}

		
		
		$(".input-area").on("keydown", function(e) {
			if (e.keyCode == 13) {

				e.preventDefault();
				var text = $(".input-area").html();
				var line = $("<div>");
				line.append("글쓴이 : " + text);
				$(".message-area").append(line);
				$(".input-area").html("");
				
				ws.send(text);
				
				$('.message-area').scrollTop($('.message-area')[0].scrollHeight);
				return false;
			}
		})

	})
</script>
</head>
<body>
	<div class="chat-box">
		<div class="message-area"></div>
		<div class="input-area" contenteditable="true"></div>
	</div>
</body>
</html>

동시성 오류

 

동기화 작업 필요

 

 

HashSet 에 동기화 작업을 할 수 있도록 업그레이드

일반 HashSet은 동시성에 대한 취약성이 있다.

 

 


에러 처리

 

연결 끊어버리기

	@OnError
	public void onError(Session session, Throwable t) {
		clients.remove(session);
	}

 

메세지를 보내는 중 한명이 나갈경우

for문이 도는 동안 정지

없는 사람에게 보낼 수 없음

 

	@OnMessage
	public void onMessage(Session session, String message) {
		
		String id = (String)this.session.getAttribute("loginId");
		
		synchronized(clients){
			for(Session client : clients) {
				if(!client.getId().contentEquals(session.getId())) {
					Basic basic = client.getBasicRemote();
					try {
						basic.sendText(id+ " : "+message);
					} catch (IOException e) {}
				}
			}
		}

	}

굳이 나간 사람 때문에 발생한 에러 메시지를 출력할 필요 없음

HTTP 프로토콜은 클라이언트가 요청하고 서버가 응답을 보내는게 기본

 

클라이언트가 req를 쓰면 다른 클라이언트에게 전달달

2번 클라이언트 입장에서는 req없이 서버에게 받는게 먼저

 

 

1. 원초적인 방법

각각의 클라이언트가 계속 리퀘스트를 쏘도록 함

submit, href 등으로 req 보내는 경우 페이지 전환 -> AJAX 활용

-> 서버 과부하

5초정도 딜레이를 준다면? 부하는 줄어들지만 사용이 불편


HTTP 말고 실시간으로 통신을 하기 위해

 socket을 활용

 

웹소캣 라이브러리 필요

		<!-- 웹소캣 라이브러리 추가 -->
		<dependency>
			<groupId>javax.websocket</groupId>
			<artifactId>javax.websocket-api</artifactId>
			<version>1.1</version>
		</dependency>

scope는 어디에 적용될지 정하는 태그 (dependency scope를 검색하면 설명 나온다)

생략하면 전체 적용

HTTP 프로토콜이 아닌 

WS (웹소캣)


읽는 순간 소캣 연결 요청

 

소캣 연결 요청 받는 위치

@OnOpen : 접속시 실행


웹소캣 프로토콜의 세션

import javax.websocket.Session;

Spring Bean이 아니기 떄문에 Autowired 못한다

 

 

client.getId() : 16진수 숫자

F5를 누를 때마다 페이지가 다시 로딩될때 웹소캣 새로 발급


<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>
<script>
	$(function(){
		var ws = new WebSocket("ws://localhost/chat")
	})
</script>
<style>
.chat-box {
	width: 500px;
	height: 500px;
	float: left;
	position: absolute;
	margin: -250px 0 0 -250px;
	top: 50%;
	left: 50%;
}

.message-area {
	width: 100%;
	height: 90%;
	border: 1px solid #616161;
}

.input-area {
	width: 100%;
	height: 10%;
	border: 1px solid #616161;
}
</style>

</head>
<body>
	<div class="chat-box">
		<div class="message-area"></div>
		<div class="input-area" contenteditable="true"></div>
	</div>
</body>
</html>

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>

<style>
.chat-box {
	width: 500px;
	height: 500px;
	float: left;
	
}

.message-area {
	width: 100%;
	height: 90%;
	border: 1px solid #616161;
	overflow-y: auto;
	 word-break : break-all; 
}

.input-area {
	width: 100%;
	height: 10%;
	border: 1px solid #616161;
	overflow-y: auto;
}
</style>
<script>

function updateScroll(){
	var element = document.getElementById("message-area")
}
	$(function() {
		var ws = new WebSocket("ws://localhost/chat")

		$(".input-area").on("keydown", function(e) {
			if (e.keyCode == 13) {

				e.preventDefault();
				var msg = $(".input-area").html();
				var tr = $("<tr></tr");
				tr.append("<td>글쓴이 : " + msg);
				$(".message-area").append(tr);
				$(".input-area").html("");


			}
		})


	})
</script>
</head>
<body>
	<div class="chat-box">
		<div class="message-area"></div>
		<div class="input-area" contenteditable="true"></div>
	</div>
</body>
</html>

updateScroll 함수로 만들어 호출하는건 실패고 제이쿼리로 안에 넣은것 동작? 왜?


	$(function() {
		var ws = new WebSocket("ws://localhost/chat")

		$(".input-area").on("keydown", function(e) {
			if (e.keyCode == 13) {

				e.preventDefault();
				var text = $(".input-area").html();
				var line = $("<div>");
				line.append("글쓴이 : " + text);
				$(".message-area").append(line);
				$(".input-area").html("");
				
				ws.send(text);
				
				updateScroll();
				return false;
			}
		})

	})

 

@OnMessage

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>

<style>
.chat-box {
	width: 500px;
	height: 500px;
	float: left;
}

.message-area {
	width: 100%;
	height: 90%;
	border: 1px solid #616161;
	overflow-y: auto;
	word-break: break-all;
}

.input-area {
	width: 100%;
	height: 10%;
	border: 1px solid #616161;
	overflow-y: auto;
}
</style>
<script>

	
	$(function() {
		var ws = new WebSocket("ws://localhost/chat")

		$(".input-area").on("keydown", function(e) {
			if (e.keyCode == 13) {

				e.preventDefault();
				var text = $(".input-area").html();
				var line = $("<div>");
				line.append("글쓴이 : " + text);
				$(".message-area").append(line);
				$(".input-area").html("");
				
				ws.send(text);
				
				$('.message-area').scrollTop($('.message-area')[0].scrollHeight);
				return false;
			}
		})

	})
</script>
</head>
<body>
	<div class="chat-box">
		<div class="message-area"></div>
		<div class="input-area" contenteditable="true"></div>
	</div>
</body>
</html>

package kh.spring.chat;

import java.io.IOException;

import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.RemoteEndpoint.Basic;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/chat")
public class WebChat {

	@OnOpen
	public void onConnect(Session client) {
		System.out.println(client.getId()+" 클라이언트가 접속했습니다.");
	}
	
	@OnMessage
	public void onMessage(Session session, String message) {
		
//		System.out.println(session.getId()+" : "+message);
		
		Basic client = session.getBasicRemote();
		try {
			client.sendText(message);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		
	}
	
}

말한 그대로 돌아옴 : 에코

 

내가 아니라 상대방에서 보내야 함!

 

 


 

클라이언트들의 접속 정보가 서버의 램에는 저장되지만 제어할 수 있는 정보는 모름

 

매개변수로 받고 매서드가 끝나면 사라짐

정보를 저장해 놔야함

몇명이 들어올지 모르기 때문에 배열은 부적절

 

세션아이디는 중복되서는 안된다 -> set으로 중복 방지

 

모든 사용자가 공유할 수 있도록 Set<Session> clients에  static을 줘야함

package kh.spring.chat;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.RemoteEndpoint.Basic;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/chat")
public class WebChat {

	private static Set<Session> clients = new HashSet<>();
	
	@OnOpen
	public void onConnect(Session client) {
		System.out.println(client.getId()+" 클라이언트가 접속했습니다.");
		clients.add(client);
	}
	
	@OnMessage
	public void onMessage(Session session, String message) {
		for(Session client : clients) {
			Basic basic = client.getBasicRemote();
			try {
				basic.sendText(message);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}	
	}
}


<script>

	
	$(function() {
		var ws = new WebSocket("ws://localhost/chat")
		
		ws.onmessage = function(e){
			console.log(e.data);
			var line = $("<div>");
			line.append(e.data);
			$(".message-area").append(line);
		}

		
		
		$(".input-area").on("keydown", function(e) {
			if (e.keyCode == 13) {

				e.preventDefault();
				var text = $(".input-area").html();
				var line = $("<div>");
				line.append("글쓴이 : " + text);
				$(".message-area").append(line);
				$(".input-area").html("");
				
				ws.send(text);
				
				$('.message-area').scrollTop($('.message-area')[0].scrollHeight);
				return false;
			}
		})

	})
</script>

 

내가 친 채팅과 출력되는 채팅이 중복 되는 경우, 하나만 출력되도록 하기

	@OnMessage
	public void onMessage(Session session, String message) {
		for(Session client : clients) {

			if(!client.getId().contentEquals(session.getId())) {

				Basic basic = client.getBasicRemote();
				try {
					basic.sendText(message);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}


	}

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>

<style>
.chat-box {
	width: 500px;
	height: 500px;
	float: left;
}

.message-area {
	width: 100%;
	height: 90%;
	border: 1px solid #616161;
	overflow-y: auto;
	word-break: break-all;
}

.input-area {
	width: 100%;
	height: 10%;
	border: 1px solid #616161;
	overflow-y: auto;
}
</style>
<script>

	
	$(function() {
		var ws = new WebSocket("ws://localhost/chat")
		
		ws.onmessage = function(e){
			console.log(e.data);
			var line = $("<div>");
			line.append(e.data);
			$(".message-area").append(line);
		}

		
		
		$(".input-area").on("keydown", function(e) {
			if (e.keyCode == 13) {

				e.preventDefault();
				var text = $(".input-area").html();
				var line = $("<div>");
				line.append("글쓴이 : " + text);
				$(".message-area").append(line);
				$(".input-area").html("");
				
				ws.send(text);
				
				$('.message-area').scrollTop($('.message-area')[0].scrollHeight);
				return false;
			}
		})

	})
</script>
</head>
<body>
	<div class="chat-box">
		<div class="message-area"></div>
		<div class="input-area" contenteditable="true"></div>
	</div>
</body>
</html>
package kh.spring.chat;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.RemoteEndpoint.Basic;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/chat")
public class WebChat {

	private static Set<Session> clients = new HashSet<>();

	@OnOpen
	public void onConnect(Session client) {
		System.out.println(client.getId()+" 클라이언트가 접속했습니다.");
		clients.add(client);
	}

	@OnMessage
	public void onMessage(Session session, String message) {
		for(Session client : clients) {

			if(!client.getId().contentEquals(session.getId())) {

				Basic basic = client.getBasicRemote();
				try {
					basic.sendText(message);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}


	}

	@OnClose
	public void onClose(Session session) {
		clients.remove(session);
	}
}
더보기

 


<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Home</title>
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>

<style>
* {
	box-sizing: border-box;
}

.chat-box {
	width: 400px;
	height: 500px;
	margin: 100px auto;
	border: 1px solid #c9d8de;
	position: relative;
}

.message-area {
	padding: 20px;
	min-height: 400px;
	overflow-y: auto;
	background: #f0f9fd;
	width: 100%;
	height: 80%;
	word-wrap: break-word;
	word-break: break-all;
}

.input-area {
	width: 100%;
	height: 100px;
	overflow: auto;
}
</style>
<script>
		function updateScroll() {
			var element = document.getElementById("message-area");
			element.scrollTop = element.scrollHeight;
		}

		$(function() {
			
			var ws = new WebSocket("ws://175.125.124.73:11111//chat");
			ws.onmessage = function(e){
				var line = $("<div>");
				line.append(e.data);
				$(".message-area").append(line);
				updateScroll();
			}
			
			$(".input-area").on("keydown", function(e) {
				if (e.keyCode == 13) {
					
					if($(".input-area").html()=="" || $(".input-area").html()==" "){
						alert("글을 입력하세요.");
					}else {
					var text = $(".input-area").html();
					var line = $("<div>");
					line.append(text);
					$(".message-area").append(line);
					$(".input-area").html("");
					ws.send(text);
					updateScroll();
					return false;
				}
			}
			});

			

		});
	</script>


</head>
<body>
	<div class="chat-box">
		<div id="message-area" class="message-area"></div>
		<div id="input-area" class="input-area" contenteditable="true"></div>
	</div>

</body>
</html>

 

 

 

 

JoinPoint

- PointCut 후보

- Advice 가 적용 될 수 있는 메서드

 

PointCut

- JoinPoint 중에서 실제 Advice를 적용하기 위해 선별해 낸 메서드

- PointCut Expression 을 통해 선별함

 

Advice

- 선별된 PointCut 메서드에 적용될 횡단기술

 

Weaving

- Advice를 PointCut


servlet-context.xml

logAdvisor를 빈으로 만들어 놓기

HomeController의 home 메서드(매개변수는 있을수도 없을수도)

pointcut 준비해놓기

before로 적용


aop:before


 

 

 

스프링이 갖고 있는 bean에서만 HttpSession만 Autowired 할 수 있다.

 

 

 


 

 

리퀘스트를 출력할 때

셋어트리뷰트 하지 않았으므로 null이 나올 것

 

 

before가 먼저 출력 후 메서드 실행

 

매개변수의 자료형과 개수를 모르기 때문에 오브젝트형 배열로 받는다.

지나가던 request 에 값 넣어놓기

 

 

 

범용성없는 예제코드

가도있던 코드는 낚아채서 추가처리 후 보낼 수 있다.

 

AOP는 강력하지만 오류도 많이 발생시키기 때문에 사용 패턴이 정해져 있다 (로그 남기기, 트랜젝션, 시큐리티)


aop:around

 

ProceedingJoinPoint : around 만 가질 수 있는 매개변수

JoinPoint 상속받아 기능을 모두 가짐

낚아챈 시점 (38번 라인)

39번에서 실행된 메서드의 리턴값이 돌아옴

 

어드바이스가 포인트컷매서드를 콜 한다.

 

 

성능체크 시( endtime - starttime ) around 사용 적절

 

 

ex. 보드컨트롤러에 있는 모든 메서드 성능체크 하기

 

모든 메서드에 적용되므로 범용성 있는 코드로 작성 할 것.

package kh.spring.aspect;

import java.text.SimpleDateFormat;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.springframework.beans.factory.annotation.Autowired;

public class LogAdvisor {
	
	@Autowired
	private HttpSession session;

	// advice
	public void aopTest(JoinPoint jp) {
		Signature sign = jp.getSignature();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
		String currentTime = sdf.format(System.currentTimeMillis());
		
		Object[] args = jp.getArgs();
		HttpServletRequest req = (HttpServletRequest)args[0];
		req.setAttribute("request", "Hello AOP");

		System.out.println("===============");
		System.out.println(currentTime);
		System.out.println(jp.getTarget()); // Advice가 적용되어 실행되는 메서드의 클래스
		System.out.println(sign.getName());
		System.out.println("===============");
//		System.out.println(session.getAttribute("loginId"));
	}
	
	
	public Object aroundTest(ProceedingJoinPoint pjp) throws Throwable {
		
		long startTime = System.currentTimeMillis();
		
		String returnValue = (String)pjp.proceed();
		
		long endTime = System.currentTimeMillis();
		
		System.out.println(endTime-startTime);
		
		return returnValue;
		
	}
	
	
}

controller가 아니라 서비스에도 걸 수 있음

 

리턴값이 Object인 이유는

 


리턴값이 void인 경우 default return값으로 null을 리턴한다.

 


컨트롤러에서 null을 리턴하면 리퀘스트맵핑url의 jsp로 이동 동작

 

Advice는 return값을 명시하면 null을 리턴

null은 주소자료형으로 int형에 넣을 수 없다.

 


 

'디지털 컨버전스 > Spring' 카테고리의 다른 글

[Spring Framework] WebChat - 익명채팅  (0) 2020.06.12
[Spring Framework] AOP:around - 로그인 체크  (0) 2020.06.12
[Spring Framework] AOP  (0) 2020.06.10
[Spring Framework] Scheduler  (0) 2020.06.10
[Spring Framework] MyBatis  (0) 2020.06.10

OOP : Object Oriented Programming 객체 지향

AOP : Aspect Oriented Programming

- 관점 지향 개발론

- Spring Framework 를 지원하는 기술 (개발의 패러다임으로 보기 어려움)

- 횡단관심사 개발 기술

 

클래스의 기능을 묶는 방식을

 

어드바이저 클래스에 저장

일일히 넣기 어렵고 유리보수 불리

-> AOP

 

weaver 라이브러리 추가

 

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>

 


AOP

 

- JoinPoint

   : Advice를 적용할 메서드 후보

   : 사실 상 프로젝트 내의 모든 매서드가 후보가 될 수 있다.

 

- PointCut

   : JoinPoint 중에서 Advice이 적용될 대상으로 선발된 메서드

   : PointCut 표현식에 의해 선발 됨.

 

- Advice

    : pointCut 표현식에 의해 선발된 PointCut 메서드에 적용될 실제 기능

 

- Weaving

   : Advice Metod 를 PointCut 에 적용하는 과정

   : maven 에서 weaver 라이브러리를 적용해야 한다.

 


패키지 생성

Bean태그 추가

Namespace 에서 aop 추가

aop 태그 영역 생성

리턴데이터 타입

 


스프링을 어떻게 설치했고 이클립스 어디서 받았는지 체크

 

	<annotation-driven />

요청을 Mapping 해준다

 

 

	<task:annotation-driven/>

시간을 지정


 

fixedRate

작업이 주기보다 길다면 즉시 실행

작업 시작기준으로 카운트

무조건 5초마다 한번씩 실행

 

fixedDelay

작업이 끝나야 이어서 실행

작업이 끝나면 5초 후 동작

 

 

간단한 작업에서는 차이가 없어보이지만 매서드 안의 작업이 오래걸리는 경우 차이가 난다


서버의 실행 기준이 아니라

 

cron

(리눅스의 daemon)

 

시간을 표현하는 표현식을 이용해서 시간 지정

 

1: 일요일 7: 토요일

 

6월 10일 16시 35분 30초부터 5초마다 실행

 

'디지털 컨버전스 > Spring' 카테고리의 다른 글

[Spring Framework] AOP:before , AOP:around  (0) 2020.06.11
[Spring Framework] AOP  (0) 2020.06.10
[Spring Framework] MyBatis  (0) 2020.06.10
[Spring Framework] MyBatis - update  (0) 2020.06.09
[Spring Framework] MyBatis - select  (0) 2020.06.09

 

 

DTO를 매개변수로 보낼 때

 

 

복수개의 DTO를 보낼때에는 Map에 키값을 달아서 전달 한다.

 


Map 방식 외의 방법으로 전달하는 법

ㅡ mybatis multiple parameter : @Mapper @Param

 


DTO 필드명과 데이터베이스 컬럼 이름이 다른 경우

 

테이블에서는 name, DTO에서는 writer 라면

 

resultMap 태그로 mapping 정보를 만들어준다.

 

 

생략된 컬럼은 자동 mapping


동적 쿼리

상세 검색

 

selectOne과 selectAll 통합 select

if 조건이 true일 경우에만

 

 

 

 

 


Mapper의 쿼리가 동적으로 동작 하는 경우

어떻게 매개변수 존재를 구분해서 호출할 것인가?

 

컨트롤러 서비스 dao에

매개변수 seq를 무조건 넣어주자

 

- seq를 입력한 경우 정상동작

- seq 값이 없는 경우 에러 발생

 

 

int seq 대신에 검색전용DTO를 매개변수로 전달

 


 

'' 값 없음의 경우 null을 integer 할수 없으므로 받을 수 없음

 

Integer 로 받으면 null도 받을 수 있다.

 

기본자료형을 둘러싸는 자료형

Wrapper Class

 

 

 

검색용으로는 dto로 받는 것이 적합

 

검색용도 dto라면 필드 자료형 int말고 Integer로 변경

 

wrapper class는 자동으로 형변환 된다

첫글자 대문자 -> 클래스로 만들어짐

Auto Boxing 으로 인해 getter setter 말고는 따로 코드를 수정해줄 필요없음

// Auto Boxing

Integer i = 10;

Integer i = new Integer(10);

Object o = 10;

 

integer 는 값이 없을 시 null로 전달

String은 값이 없을 시 ''로 전달

-> 전달되는 값에 맞추어 조건을 설정해야 한다.

 


조건이 여러개일 경우

 

 

seq name 둘다 비어있을 경우 전체 목록

 

둘중 하나만 비어있을 경우 조건 검색

 

둘다 존재할 경우 where 이 두번?

 

trim 태그

 

trim태그 inner 영역에 어떠한 값이든 존재한다면 앞에 where을 붙인다.

 

 

prefixOverrides : 발생한 텍스트의 접두사를 삭제

생성된 구문의 가장 앞이 "and"라면 오버라이드 (지워버림)

 

 

'디지털 컨버전스 > Spring' 카테고리의 다른 글

[Spring Framework] AOP  (0) 2020.06.10
[Spring Framework] Scheduler  (0) 2020.06.10
[Spring Framework] MyBatis - update  (0) 2020.06.09
[Spring Framework] MyBatis - select  (0) 2020.06.09
[Spring Framework] MyBatis 에러  (0) 2020.06.09

 

컬럼명에서는 따옴표 쓰면 안된다.

따라서 #{}이 아닌 ${}로

 

'디지털 컨버전스 > Spring' 카테고리의 다른 글

[Spring Framework] Scheduler  (0) 2020.06.10
[Spring Framework] MyBatis  (0) 2020.06.10
[Spring Framework] MyBatis - select  (0) 2020.06.09
[Spring Framework] MyBatis 에러  (0) 2020.06.09
[Spring Framework] MyBatis - insert  (0) 2020.06.09

MyBatis는 업그레이드 된 버전

 

그 이전에 iBatis가 존재

 

데이터베이스 관련 작업에 특화된 라이브러리

코드와 쿼리문을 분리 할 수 있다.

 

쿼리문을 xml에 독립적으로 작성하기 때문에 이식성이 좋아짐

 

분리 되기 때문에 가독성도 좋다.

 

상세검색 기능 등 복잡한 쿼리 조립

조립식 동적 쿼리를 만들 때 MyBatis가 더 유리

 

정석적으로는 넘어오는 타입을 명시해야 하지만 없어도 인식가능

parameterType

 

 

 

 

select 태그를 사용할 때는 resultType을 명세해야 한다.

insert, update, delete는 어쩌피 int 리턴임

 

 

select 된 데이터 마다 DTO를 만들고 이름에 맞추어서 값 넣어줌

while이나 lowMapper 기능도 필요없다

 

쿼리에서 꺼내는 컬럼명이 resultType으로 명세한 DTO의 필드명과 동일하다는 전제 하에

 

만약 컬럼명과 DTO필드명이 다른 경우가 생긴다면 거기에 맞는 태그가 존재

 

 

출력

 


selectone

 

int 값 따옴표 안에 넣어도 검색된다.

 

 #{seq} 는 dto.getSeq로 도장

넘오는 값이 하나일 경우 #{value} 라고 작성한다.

 


value가 여러개여야 할 경우 (범위 검색)

매번 DTO를 만들기 않고

Map 사용


selectCount

 

'디지털 컨버전스 > Spring' 카테고리의 다른 글

[Spring Framework] MyBatis  (0) 2020.06.10
[Spring Framework] MyBatis - update  (0) 2020.06.09
[Spring Framework] MyBatis 에러  (0) 2020.06.09
[Spring Framework] MyBatis - insert  (0) 2020.06.09
[Spring Framework] 정리  (0) 2020.06.08

+ Recent posts