Spring(Day-14)

김성국·2023년 6월 30일
0
post-custom-banner

■ Chat

// websocket
// socket.io
// mqtt, xmpp
// SSE => Server send Events

@Slf4j
@RestController
public class RestSseController {
    
    // 접속하는 클라이언트의 정보를 보관할 변수
    private static final Map<String, SseEmitter> clients = new HashMap<>();

    // 클라이언트 접속했을때 수행
    @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;
    }


    // 클라이언트가 전송을 했을  때 수행
    // {"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);
            }
        }
    }
}
  • RestController폴더에 RestSseController.java를 생성한다.
  • map을 사용하여 접속하는 클라이언트의 정보를 보관할 변수를 생성한다.
  • 클라이언트 접속했을때 수행하기 위해 see객체를 생성하여 접속시간을 정해준다.
  • 클라이언트 연결 중지 및 완료되면 clients변수에서 정보를 제거한다.
  • 클라이언트가 전송했을때 map에 보관된 개수만큼 값을 꺼낸다. 그 키값으로 value값을 꺼내어 클라이언트로 메시지 전송한다.

■ ChatController

@Slf4j
@Controller
public class ChatController {
    
    @GetMapping(value="/chat.do")
    public String chatGET() {
        return "/chat";
    } 
}

■ chat.html

<body>
    <h3>chat</h3>

    <div>
        <input type="text" id="input"/>
        <button id="send" onclick="sendMessage()">send</button>
        <div id="messages" style="border:1px solid #cccccc;"></div>
    </div>

    <script>

        // 고유한 아이디 만들기
        const id = `ds200_${new Date().getTime()}`;

        // 접속하기 => 내가전달한 아이디를 map에 변수에 보관(2분간)
        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>

</body>

  • 다른 곳에서 전송을 해도 전송한 값을 받아와 콘솔에 출력한다.

■ Scheduler

@Component
@Slf4j
@RequiredArgsConstructor
public class MyScheduler {
    
    final Board1Repository board1Repository;

    // cron tab
    // 초, 분, 시간, 일, 월, 요일
    // */5 * * * * * 5초 간격으로 동작
    // */20 * * * * * 20초 간격
    // 0 * * * * * 0초가 될 때마다 (1분 간격)
    @Scheduled(cron = "*/5 * * * * *")
    public void printDate(){
        log.info("{}", new Date().toString());

        List<Board1> list = board1Repository.findAll();
        log.info("{}", list.toString());
    }
}
  • scheduler 폴더를 생성하여 MyScheduler.java를 생성한다.
@EnableScheduling

@ComponentScan(basePackages = { 
...

"com.example.scheduler" })
  • Boot20230427Application.java에 위 코드를 추가한다.

  • 5초마다 board1Repository의 값이 출력된다.
  • // @Scheduled(cron = "/5 * * * *") 이렇게 주석처리를 하면 더이상 출력되지 않는다.
post-custom-banner

0개의 댓글