금일 수업에서는 실시간 채팅 기능과 스케쥴링을 활성화해보도록 하겠다.
먼저 채팅을 위한 서버 클라이언트를 설정해보도록 하겠다.
서버는 채팅 환경에 알맞도록 실시간 적용이 가능한 Rest 방식의 컨트롤러를 이용했다.
/* ---------- 클라이언트 접속했을때 수행 ---------- */
@GetMapping(value="/api/sse/subscribe")
public SseEmitter subscribe( @RequestParam(name="id") String id) {
// sse 객체 생성
SseEmitter emitter = new SseEmitter( 1000L * 1200 ); //20분 동안 접속 유지
clients.put(id, emitter);
// 클라이언트 연결 중지 및 완료 되면 clients변수에서 정보 제거
emitter.onTimeout(() -> clients.remove(id));
emitter.onCompletion(() -> clients.remove(id));
return emitter;
}
클라이언트에 접속하게되면 접속을 20분동안 유지할 수 있도록 Ssemitter의 시간을 설정해두었으며,
이용자와 클라이언트의 연결이 중지되면 클라이언트 변수에서 이용자의 id값이 삭제되도록 했다.
/* ---------- 클라이언트가 전송을 했을 때 수행 ---------- */
// {"ret":1, "abc":"def"}
@GetMapping(value="/api/sse/publish")
public void publish(@RequestParam(name="message") String message) {
// map에 보관된 개수만큼 반복하면 키값을 꺼냄
for (String id : clients.keySet()) {
try {
// map의 키를 이용해서 value값을 꺼냄
SseEmitter emitter = clients.get(id);
// 클라이언트로 메시지 전송
emitter.send(message, MediaType.APPLICATION_JSON);
}
catch(Exception e) {
clients.remove(id);
}
}
}
위의 메소드에서 클라이언트에 유저의 아이디를 저장했다면, 이번에는 클라이언트에 저장된 id값을 불러온 뒤 해당 아이디의 메세지들을 전송하는 방식이다.
위와같이 클라이언트를 설정해 준 뒤, 간단하게 컨트롤러를 생성해준 뒤, 템플릿을 만들어보겠다.
@Controller
public class ChatController {
// http://127.0.0.1:9090/ROOT/chat.do
@GetMapping(value = "/chat.do")
public String chatGET() {
return "/chat";
}
}
(진짜 간단히 만들었음.)
<div>
<input type="text" id="input" />
<button id="send" onclick="sendMessage()">send</button>
<div id="messages" style="border:1px solid #cccccc;"></div>
</div>
채팅기능을 확인하기 위한 와이어프레임을 먼저 만들고,
전송버튼을 누르면 이전에 만들어둔 클라이언트 메소드를 이용하여 클라이언트로 데이터를 전송할 수 있도록 <script>
를 생성해보도록 하겠다.
<script>
// 고유한 아이디 만들기
const id = `ds209_${new Date().getTime()}`;
// 접속하기 => 내가 전달한 아이디를 map에 변수에 보관하기 (20분)
const eventSource = new EventSource(`/ROOT/api/sse/subscribe?id=${id}`);
// callback1
eventSource.onopen = (e) => {
console.log('onopen', e);
};
// callback2
eventSource.onmessage = (e) => {
const div = document.querySelector('#messages');
div.innerHTML = `${e.data}<br />` + div.innerHTML;
console.log('onmessage', e.data);
};
// callback3
eventSource.onerror = (e) => {
console.log('onerror', e);
};
const sendMessage = () => {
const msg = document.querySelector('#input').value;
fetch(`/ROOT/api/sse/publish?message=${msg}`); // 메시지 보내기
};
</script>
클라이언트에 저장하기 위한 id값은 임의로 시간을 부여했다. (계속 바뀌기때문에 테스트할때마다 바꿀 필요가 없다 ㅎㅎ)
전송 버튼을 클릭하면 자바 스크립트의 sendMessage()
객체를 통해 메세지가 전송되는 방식이다.
크롬에서 실행 후, 개발자모드를 켜 log 기록을 확인해보면 채팅 메세지가 클라이언트와 상호작용이 잘 이루어짐을 볼 수 있다.
여기서 가물가물하기 전 JS의 기본적인 문법에 대해서 간단하게 메모해두고 넘어가도록 하겠다.
const str = "hello";
const a = "aa" + str + "aa"; // aahelloaa
const b = `aa${str}aa`; // aahelloaa
함수 생성 방법
const constName = () => {
}
function functionName() {
}
// 위 둘다 동일한 기능이지만, 현재 const방식을 많이 사용하는 상황.
스케쥴링이라함은 간단히 말해서 약속된 시간에 작업이 수행되도록 하는 기능이다.
스케쥴링을 사용하는 실생활의 예시로는 매일 새벽 12시경 은행별로 ATM기기 점검이 이루어지는 것을 예로 들 수 있다.
이러한 스케쥴링 기능을 사용하는것에 대해서 간략하게나마 필기를 해두고 넘어가고자 한다.
@Slf4j
@Component
@RequiredArgsConstructor
public class MyScheduler {
final Board1Repository board1Repository;
// cron tap
// 초, 분, 시간, 일, 월, 요일
// */5 * * * * * 5초 간격으로 동작
// ex) */20 * * * * * 20초 간격동작
// ex) (0 * * * * *) 0 초가 될때 1분간격
@Scheduled(cron = "*/5 * * * * *")
// 사용시 application에 @EnableScheduling 어노테이션 추가할 것
public void printDate() {
log.info("{}", new Date().toString());
List<Board1> list = board1Repository.findAll();
log.info("{}", list.toString());
}
}
스케쥴러의 경우에는 컨트롤러, 서비스, 매퍼 등 과는 별개의 다른 기능이기에
@Component
어노테이션을 사용해주었다.
스케쥴러를 원하는 시간에 동작하기 위해서는 cron tap
에 대해 검색하여 좀 더 자세히 다루어보아야 한다.
위의 경우에는 간략한 몇가지의 예시만을 정리했다.
Board1Repository
를 불러와 List 방식으로 전체를 조회했는데,
실제로 터미널의 로그에 초 간격으로 계속해서 표시가 된다.
추가적으로 , application.java
에 스케쥴링 어노테이션을 추가해주어야 한다!