Search

웹소켓

대분류
페이지/Docs
작성자
사람
태그
Docs
3 more properties

Stomp 동작 방식

왜 웹소켓을 사용할까?
웹소켓을 사용하지 않을 때의 일반적인 CRUD 과정을 생각해봅시다. url 을 생각해보면 대략적으로 게시글 리스트, 게시글 세부내용, 게시글 작성, 게시글 삭제에 대한 url 이 존재하겠죠?
하지만 여러사람이 한 페이지에서 위의 동작을 하는 경우를 생각해봅시다. 보는것은 여러사람이 같은 페이지를 볼 수 있지만, 만약 그 페이지 에서 데이터 수정이 이루어진다면 어떻게 될까요?
A 열심히 글 작성중
B 같은 페이지에 들어옴 (여기까지는 문제가 되지 않습니다.)
B 글 작성했으니까 완료버튼 눌러야게따~
A ..? 작성한글 어디로..?
리로드가 되버리면 A 의 데이터는 어디로 갈까요? 자동저장 기능이 없다면 유실됩니다. 만약 자동 리로드를 하지 않도록 한다면 AB 둘 다 데이터 유실은 되지 않겠지만 새로고침을 하기 전까지는 내용 갱신이 되지 않습니다. 이는 웹소켓과 일반적인 http 통신의 차이 때문입니다.
아래 모식도를 보면 훨씬 이해하기 편할 거에요
http 통신의 구조
WebSocket 통신의 구조
둘의 구조 차이
특정 유저의 플래너를 방문할 때의 url 은 어떻게 되나?
백단에서 아래와 같이 Controller 를 작성해 주었습니다.
@GetMapping("detail") public String plannerDetail(Long tpIdx, Model model) { PlannerDetailResponse dto = plannerService.findPlannerBytpIdx(tpIdx); model.addAttribute("planner", dto); Member member = new Member(); Profile profile = profileService.getProfileData(UserPrincipal.getUserPrincipal().getPrincipal().getUserId()); model.addAttribute("profile", profile); return "planner/planner1"; }
Java
복사
@RequestMapping 은 planner 이므로 특정 유저의 플래너를 방문할 때에는 최종 url 이 아래와 같아집니다.
소켓은 어떻게 열리는걸까?

바닐라 자바스크립트와 타임리프를 이용하였습니다.

백단에서의 기본적인 설정은 마치고, 소켓이 열려야 하는 html 파일에 아래와 같이 작성하면 소켓이 열립니다.
var stompClient = null; function connect() { // 새로운 소켓 생성 var socket = new SockJS('/gs-guide-websocket'); stompClient= Stomp.over(socket); // 소켓 통신의 시작 stompClient.connect({}, function (frame) { console.log('Connected: ' + frame); ...
JavaScript
복사
그러면 위의 url 에 방문한 모든 사람들과 1:N 연결이 됩니다. (일반적인 http 통신은 1:1 연결방식 입니다.)
MessageBroker 는 어떻게 지정 / 매핑 되는가?
백엔드에서 설정한 웹소켓 설정을 보면 알 수 있습니다.
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { /* destination 에 /app/ 이 앞에 붙으면 아 얘는 메세지를 전달해주는 애구나 하고 생각하면 될 것 같습니다. destination 에 /topic/ 이 앞에 붙으면 아 이곳에 처리된 데이터가 오는 구나 하고 생각하면 될 것 같습니다. */ config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } // 새로운 소켓 생성시 필요한 엔드포인트 입니다. @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/gs-guide-websocket").withSockJS(); } }
Java
복사
이제 다시 화면으로 돌아가서 보겠습니다! 이제 메세지 브로커와 객체를 추가해보겠습니다.
var stompClient = null; function connect() { // 새로운 소켓 생성 var socket = new SockJS('/gs-guide-websocket'); stompClient= Stomp.over(socket); // 소켓 통신의 시작 stompClient.connect({}, function (frame) { console.log('Connected: ' + frame); // 메세지 브로커와 객체 추가 stompClient.subscribe('/topic/planner-message/' + [[${planner.tpIdx}]], function (recommand) { res = JSON.parse(recommand.body); console.log('res: ' + res);
Java
복사
이렇게 작성해주면 아래와 같은 내용이 콘솔에 찍히게 됩니다.
이 destination 을 통해 백단과 화면이 소통을 하게 됩니다.
화면에서 받은 데이터를 뒤에서 처리한 뒤, 다시 보내준다!
여행 날짜를 한번 생성해보겠습니다!
다음과 같이 날짜를 생성했을 때 개발자 도구를 열어보면 아래와 같은 내용을 확인할 수 있습니다.
위의 내용은 어떻게 나온건지 백단에서부터 보겠습니다.
@Controller @AllArgsConstructor @RequestMapping("todolist") public class TodoListController { private final TodoListService todoListService; private final SimpMessagingTemplate simpMessagingTemplate; // 메세지 브로커(/app/upload-todolist/{tpIdx}) 와 매핑을 합니다. @MessageMapping("/upload-todolist/{tpIdx}") public void upload( @DestinationVariable("tpIdx") Long tpIdx, TodoListRegistRequest dto ) throws Exception { /* 괄호 안의 destination 을 SUBSCRIBE 하고 있는 모든 사람들에게 앞에서 보내 처리된 데이터를 보내겠다는 뜻입니다. */ simpMessagingTemplate.convertAndSend("/topic/planner-message/" + tpIdx, Map.of("type","upload-todolist","msg",todoListService.createTodoList(dto, tpIdx))); } ...
Java
복사
그럼 보낸 데이터는 어떻게 받을까요? 앞단의 코드를 확인해봅시당.
var stompClient = null; // 투두리스트 생성 시 만들어져야하는 동적 요소와 백단에서의 데이터 받기 function connect() { // 새로운 소켓 생성 var socket = new SockJS('/gs-guide-websocket'); stompClient= Stomp.over(socket); // 소켓 통신의 시작 stompClient.connect({}, function (frame) { console.log('Connected: ' + frame); // 백단에선 처리된 데이터 받을 때 <<< MESSAGE 부분 stompClient.subscribe('/topic/planner-message/' + [[${planner.tpIdx}]], function (recommand) { // 백단에서 보낸 객체를 res 로 지정했습니다. res = JSON.parse(recommand.body); console.log('res: ' + res); /* 뒤에서 type 과 데이터를 바인딩 했기 때문에 조건문으로 구분해주었습니다. if 아래의 내용은 요소의 동적생성을 위한 코드이기 때문에 무시해도 됩니다. 콘솔로 res 안에 어떤 데이터가 담겨있는지 확인하고, 사용했습니다. */ if (res.type === 'upload-todolist') { // 동적으로 생성되어야 할 요소들을 넣었습니다. }) ... } // 투두 리스트 추가 function addTodoList() { let title = todolistTitleAdd.value; console.dir(title); // 메세지 브로커 입니다. 백단의 @MassageMapping 과 매핑됩니다. stompClient.send("/app/upload-todolist/" + [[${planner.tpIdx}]], {}, JSON.stringify({'title':title})); }
Java
복사

대략적으로 작성해본 동작방식입니다. 궁금한점은 댓글로 남겨주시면 바로 확인하겠습니다! 감사합니다

참고하면 좋을것 같은 자료