(3)

[SpringBoot/WebSocket+SocketJS] CORS 설정 시 에러 (When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of or..

에러 메세지 When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead. 원인 CORS설정 시 allowedCredentials true와, allowedOrigins "*"를 같이 사용할 수 없다. 해결 에러 메세지에 친절하게 나와있다. allowedOrigins("*") 대신..

Websocket이란? Websocket 동작과정

4개월 학습한 비전공자의 WebSocket 포스팅글입니다.피드백 적극 환영합니다. 성장을 위해 도와주세요.   더보기14) 웹 소켓(Web Socket)을 활용한 실시간 알림 - Spring Service(OTT Service) 14) 웹 소켓(Web Socket)을 활용한 실시간 알림 - Spring Service(OTT Service)해당 프로젝트는 2023/01/25 ~ 2023/03/12 내에 진행되는 아카데미 내 수강생들끼리 팀을 나누어 진행한 모의 프로젝트입니다. 팀원은 5명이었으며, 프로젝트 리더를 맡았습니다. 이전 글 목록 1) 주어mag1c.tistory.com15) 웹소켓(WebSocket)을 활용한 관리자(1:N)와 사용자(1:1) 간의 채팅 만들기 - Spring Project(OT..

14) 웹 소켓(Web Socket)을 활용한 실시간 알림 - Spring Service(OTT Service)

해당 프로젝트는 2023/01/25 ~ 2023/03/12 내에 진행되는 아카데미 내 수강생들끼리 팀을 나누어 진행한 모의 프로젝트입니다. 팀원은 5명이었으며, 프로젝트 리더를 맡았습니다. 이전 글 목록 1) 주어진 RFP를 바탕으로 주제 선정 - Spring Project(OTT 서비스) 2) ERD 설계 - Spring Project(OTT 서비스) 3) 회원 가입 기능 구현 - Spring Project (OTT 서비스) 4) 로그인, 로그아웃 기능 구현 - Spring Project (OTT 서비스) 5) 상세 페이지 및 회원 정보 수정 - Spring Project (OTT 서비스) 6) CRUD를 한번에 → 게시판 만들기(QNA게시판) - Spring Project(Mybatis) (OTT 서..

[SpringBoot/WebSocket+SocketJS] CORS 설정 시 에러 (When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of or..

Tech/트러블슈팅 2023. 5. 24. 06:00
728x90
728x90

에러 메세지

When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead.

 

 

 

원인

CORS설정 시 allowedCredentials true와, allowedOrigins "*"를 같이 사용할 수 없다.

 

 

해결

에러 메세지에 친절하게 나와있다.

allowedOrigins("*") 대신 allowedOriginPatterns("*")을 사용하여 해결.

 

728x90
300x250
mag1c

mag1c

2년차 주니어 개발자.

Websocket이란? Websocket 동작과정

Tech/네트워크 2023. 3. 25. 18:14
728x90
728x90

 

 

4개월 학습한 비전공자의 WebSocket 포스팅글입니다.

피드백 적극 환영합니다. 성장을 위해 도와주세요.

 


 

 

더보기

 

 

스프링을 활용한 OTT 서비스 개발 프로젝트 당시 웹소켓을 활용한 실시간 알림이나 채팅 기능을 구현해 본

적이 있다. 당시에 너무 흥미를 느껴 깊이 파보고자 했으나 시간 할애를 하지 못했었다.

 

시간이 나서 깊게 공부해보고자 포스팅을 시작하게 되었다.

시작에 앞서 웹소켓에 관심을 갖게 되면서 재밌게 본 유튜브 영상이 있어 공유하려고 한다. 

https://www.youtube.com/watch?v=MPQHvwPxDUw&list=PLgXGHBqgT2TvpJ_p9L_yZKPifgdBOzdVH&index=98

 

 

 

 

WebSocket


서버와 클라이언트 사이에 데이터를 주고 받을 수 있는 프로토콜로 양방향 소통이 가능하다.

ex) 채팅, 게임, 실시간 차트 등

 

 

http에서 실시간 통신을 할 수 없는 문제를 해결하기 위해 나온 기술이다.

http는요청을 보내면 응답이 오는 단방향적 구조로 통신을 하기 때문에 TCP/IP 프로토콜을 사용하는 소켓처럼 계속 connection이 유지되는 실시간 통신을 할 수 없다.

http의 특징

단방향 통신(비연결성)
request - response의 구조
헤더의 비중이 너무 크다(실시간으로 많은 데이터를 주고 받을 경우 비경제적임)
ajax또한 http를 이용하기 때문에 요청을 통한 응답의 구조이다.
변경된 데이터를 가져오기 위해서는 이벤트를 발생시키거나, 일정 시간 주기로 요청을 보내야한다.

 

 

 

특징

1. 양방향 통신

2. 실시간 네트워킹 (연결 유지)

3. 핸드쉐이크 과정에서는 헤더의 비중이 크지만, 연결 된 후에는 간단한 메세지들만 오가기 때문에 경제적이다.

 

 

TCP와 UDP, handshake / 3-way handshake, 4-way handshake

틀린 부분이 있다면 지적해주시면 감사하겠습니다. 공부하는 데 큰 도움이 됩니다. WEB과 HTTP / (특징, 구조, 동작 과정 예시) 틀린 부분이 있다면 지적해 주시면 감사하겠습니다. 공부에 많은 도

mag1c.tistory.com

 

 

 

 

 

동작 과정

크게 세가지로 분류되는데, Openeing HandShake, Data transfer, Closing HandShake로 나뉜다

웹 소켓 동작과정

 

HandShake

악수는 두 명의 사람이 만나서, 악수를 요청하고, 악수를 받는다. 총 세 가지의 과정을 거쳐 "악수" 라는 연결이 성립되는 것이다. 이를 통해 클라이언트와 서버간의 연결을 성립하는 과정이 필요하다는 것을 알 수 있다.
핸드쉐이크란 통신이 시작되기 전 두 지점이 확립된 연결을 가지도록 설정하는 것이다.

 

 

 

HandShake

접속 요청은 http로 진행되며, HandShake가 완료되면 웹소켓 프로토콜(WS)로 변경된다.

RequestHeader

<!-- ws로 변경되기 위한 httprequestheader -->

GET /chat HTTP/1.1
Host: localhost:8080
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://localhost:9090
GET /path HTTP/1.1
Host: ip:port
<!-- 반드시 HTTP버전 1.1이상, GET을 사용 -->
<!-- ip:port/path으로의 접속 ex)localhost:8080/chat-->
Upgrade: websocket
<!-- 프로토콜을 전환하기 위해 사용하는 헤더 -->
<!-- websocket의 값이 없거나 다른 값이면 cross-protocol-attack으로 간주하여 접속 중지 -->
Connection: Upgrade
<!-- 전송이 완료된 후 네트워크 접속을 유지할 것인지에 대한 정보 -->
<!-- 웹소켓 요청 시 반드시 Upgrade -->
<!-- 위의 Upgrade 헤더와 마찬가지로 값이 없거나 다른 값이면 접속 중지 -->
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
<!-- Key : 유효한 요청인지 확인하기 위해 사용하는 키값 -->
<!-- Protocol : 사용하고자 하는 하나 이상의 웹 소켓 프로토콜 지정, 필요한 경우 사용 -->
<!-- Version : 버전 -->
Origin: http://ip:port
<!-- CORS 정책으로 만들어진 헤더 -->
<!-- CSWSH(Cross-Site Websocket Hijacking)과 같은 공격을 피하기 위한 헤더 -->
CORS(교차 출처 리소스 공유)

웹 페이지에서 실행되는 자바스크립트 XMLHttpRequest(XHR) 호출이 출처가 다른 도메인의 리소스와 상호작용할 수 있도록 허용하는 표준 메커니즘
CSWSH(Cross-Site Websocket Hijacking)

Cross domain간 사용 정책인 Origin 헤더에 대한 검증 미흡으로 발생하는 문제로 서버가 Origin을 제대로 검증하지 않는다면 어떤 도메인에서도 사용자의 세션을 통해 WebSocket을 사용할 수 있다.

 

 

ResponseHeader

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
HTTP/1.1 101 Switching Protocols
<!-- 101 : http에서 ws로 프로토콜 전환이 승인되었다는 응답코드 -->
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
<!-- 요청 헤더의 Sec-WebSocket-Key에 고유ID를 더해 SHA-1로 해싱 후 base64로 인코딩한 결과 -->
<!-- 웹소켓 연결이 개시되었음을 알림 -->

 

 

 

Data Transfer

HandShake를 통해 ws프로토콜이 생성되고 나면 진행되는 과정으로 데이터는 메세지단위로 전달된다.

메세지

여러 프레임이 모여 구성되는 하나의 논리적인 단위
프레임

통신에서 가장 작은 단위의 데이터
데이터 링크계층(이더넷)에서 주고받는 가장 작은 단위로 작은 헤더+payload로 구성되어 있다.

※ 패킷 : 전 네트워크 통신 과정에서 가장 작은 단위

 

 

 

 

웹소켓의 한계

웹소켓은 HTML5의 기술이기 때문에 오래된 버전의 웹에서는 지원하지 않는다.

 

 

Socket.io / sockJS

HTML5 이전의 기술로 구현된 서비스에서 웹 소켓처럼 사용할 수 있도록 도와주는 기술 (JS 라이브러리)

웹 소켓, 폴링, 스트리밍 등 다양한 방법을 하나의 API로 추상화한 것이다.

브라우저 종류에 상관 없이 실시간 웹을 구현할 수 있는 라이브러리이다.

 

 

또한 웹소켓은 메세지를 주고받을 뿐 어떠한 역할도 하지 않으며, 주고받은 문자열은 온전히 어플리케이션에게 해석을 맡긴다. HTTP는 형식을 정해두었기 때문에, 약속을 따르기만 한다면 해석이 가능하지만, 웹 소켓은 그렇지 않기 때문에, 해석하기가 힘들다.

때문에 웹 소켓에서는 서브 프로토콜을 사용하여 주고받는 메세지의 형태를 약속하는 경우가 많다.(STOMP)

STOMP

웹 소켓에서 동작하는 문자 기반 메세징 프로토콜로써 메세지의 유형, 형식, 내용들을 정의하는 매커니즘

 

 

 

참고 (웹 소켓 이전의 실시간 통신 기술)

웹 소켓이 나오기 전, 양방향 통신과 실시간 네트워킹을 가능하게 했던 통신 방식들이 있었다.

 

1. polling

  - 일정 주기로 서버에 요청을 보내는 방법

2. long-polling

  - 서버가 응답을 바로 보내지 않고 특정 이벤트나 타임아웃이 발생했을 때 응답을 전달하는 방법

3. streaming

  - 이벤트 발생 시 응답하지만, 응답을 완료시키지 않고 계속 연결을 유지하는 방식

 

세 방식 모두 부하가 발생할 수 있고 Header가 불필요하게 커진다.

 

 

 


 

 

참조

https://doozi0316.tistory.com/entry/WebSocket%EC%9D%B4%EB%9E%80-%EA%B0%9C%EB%85%90%EA%B3%BC-%EB%8F%99%EC%9E%91-%EA%B3%BC%EC%A0%95-socketio-Polling-Streaming

 

https://velog.io/@codingbotpark/Web-Socket-%EC%9D%B4%EB%9E%80

 

https://velog.io/@rhdmstj17/%EC%86%8C%EC%BC%93%EA%B3%BC-%EC%9B%B9%EC%86%8C%EC%BC%93-%ED%95%9C-%EB%B2%88%EC%97%90-%EC%A0%95%EB%A6%AC-1

 

https://fgh0296.tistory.com/24

 

https://velog.io/@qkrqudcks7/STOMP%EB%9E%80

 

728x90
300x250
mag1c

mag1c

2년차 주니어 개발자.

14) 웹 소켓(Web Socket)을 활용한 실시간 알림 - Spring Service(OTT Service)

삽질/사이드 프로젝트 2023. 3. 2. 17:18
728x90
728x90
해당 프로젝트는 2023/01/25 ~ 2023/03/12 내에 진행되는
아카데미 내 수강생들끼리 팀을 나누어 진행한 모의 프로젝트입니다.
팀원은 5명이었으며, 프로젝트 리더를 맡았습니다.

 

 

이전 글 목록

1) 주어진 RFP를 바탕으로 주제 선정 - Spring Project(OTT 서비스)
2) ERD 설계 - Spring Project(OTT 서비스)
3) 회원 가입 기능 구현 - Spring Project (OTT 서비스)
4) 로그인, 로그아웃 기능 구현 - Spring Project (OTT 서비스)
5) 상세 페이지 및 회원 정보 수정 - Spring Project (OTT 서비스)
6) CRUD를 한번에 → 게시판 만들기(QNA게시판) - Spring Project(Mybatis) (OTT 서비스)
7) 게시판 페이징 처리 - Spring Project (OTT 서비스)
8) 카카오 지도 API 사용하기 - Spring Project (OTT 서비스)
9) (네아로) 네이버 로그인 API 활용 사이트 로그인 및 회원가입 - SPRING Project(OTT 서비스)
10) 카카오 로그인 API 사용하기(내 사이트 로그인 및 회원가입) - Spring Project(OTT Service)
11) 아임포트(포트원) API를 이용한 결제처리 - Spring Project(OTT Service)
12) 관리자 페이지 만들기(데이터 통계 및 chart.js, 유저 알고리즘) - Spring Project(OTT Service)
13) 관리자 페이지 (영상 정보 업로드 시 여러 테이블에 insert 및 update) - Spring Project(OTT Service)

 

 

포스팅 당시 영상 상세페이지의 대댓글이 구현되지 않은 상황이었기 때문에, 사용자의 질문에 대한 관리자의 답변이 달렸을 경우 알림을 우선적으로 구현하고, 차후에 대댓글이 구현된 후 댓글 알림을 구현 할 예정이다.

 

 

설정하기


pom.xml

웹소켓의 데이터 통신은 내부적으로 JSON을 사용한다. JSON라이브러리는 기존에 추가되어 있기 때문에 생략한다.

<!-- Spring-websocket -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>${org.springframework-version}</version>
</dependency>

 

servlet-context.xml

서블릿의 최상단의 beans:beans 내부에 아래의 코드를 추가하였고

xmlns:websocket="http://www.springframework.org/schema/websocket"	
xsi:schemaLocation="http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.3.xsd

 

알림 소켓 생성 시 매핑 값을 /echo로 설정할 것이고, 해당 요청을 EchoHandler에서 처리 할 것이다.

<!-- 웹소켓 -->
<beans:bean id="echoHandler" class="com.test.test1.chat.util.EchoHandler" />        

<websocket:handlers>
    <websocket:mapping handler="echoHandler" path="/echo"/>
    <websocket:handshake-interceptors>
        <beans:bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
    </websocket:handshake-interceptors>
    <websocket:sockjs />
</websocket:handlers>

 

 

CDN추가(03-30 수정)

sockjs는 CDN을 추가하여 사용했다.

<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>

 

 

사용하기


TextWebSocketHandler를 상속받아 아래의 메서드를 사용할 수 있다.

afterConnectionEstablished - 연결 성공 시
afterConnectionClosed - 연결 종료 시
handleTextMessage - 메세지 송수신 시

답변 등록 시 ALARM 테이블에 아래와 같이 데이터가 추가된다.

등록 시 ALARM 테이블에 데이터가 입력되는 과정은 생략하도록 하겠다. 이제 이정도는 껌이다

 

 

IDX : PK
USER_ID : 알림 받을 사용자의 PK
CODE : 알림 정보 - 대댓글 알림 시 NewComment 등 CODE로 조건을 주어 VIEW를 다르게 할 수 있게 설계
CHECKED : default X이며 사용자가 알림 확인 시 자동으로 삭제처리된다.
PREFIX : 알림이 어디서 왔는지. 단순히 DB에 텍스트 형태로 입력시켜 주었다.

 

EchoHandler

package com.test.test1.chat.util;
(...생략...)
//Dao DI를위해 사용. 없을 경우 Dao = null
// -> Bean으로 등록되어 있어서 따로 스프링의 객체라고 주입시켜줘야함.
@Component
@RequestMapping("/echo")
public class EchoHandler extends TextWebSocketHandler{
	
	@Autowired
	private AlarmDao alarmDao;
	
	public void setAlarmDao(AlarmDao alarmDao) {
		this.alarmDao = alarmDao;
	}

	
	private static final Logger logger = LoggerFactory.getLogger(WebSocketHandler.class);
	//로그인 한 인원 전체
	private List<WebSocketSession> sessions = new ArrayList<WebSocketSession>();
	
	//클라이언트가 웹 소켓 생성
	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		logger.info("Socket 연결");
		//웹 소켓이 생성될 때마다 리스트에 넣어줌
		sessions.add(session);
	}
	
	//JS에서 메세지 받을 때.
	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {// 메시지
	
		for(WebSocketSession single : sessions) {
//			대댓글 때 사용
//			String msg = message.getPayload();
//			String[] str = msg.split(",");
//			//JS에서 원하는대로 send하여 해당 기능 별 알람 구현
//			//질문에 답변 달렸을 때(질문자 ID와 제목 들고옴)
//			if(str != null && str.length == 2) {
//				String id = str[0];
//				String q_subject = str[1];
//				int count = alarmDao.selectAlarmCount(id); //알람이 존재할 때
//			}		
			
			//세션아이디
			String hsid = (String) single.getAttributes().get("user_id");
			
			//세션값이 같을때, 알람보낼 것이 있을 때만 전송 -> 로그인 한 사용자가 처음으로 알람 받는 순간임
			//해당 sendMsg에 DB정보 넣어서 체크 안된 알람 전부 전송하기
			if(single.getAttributes().get("user_id").equals(session.getAttributes().get("user_id"))) {				//체크 안된 알림들만 담아서 View
				List<AlarmDto> dto = new ArrayList<>();
				dto = alarmDao.selectAlarm(hsid);
				for(AlarmDto alarm : dto) {
					int idx = alarm.getIdx();
					String prefix = alarm.getPrefix();
					String code = alarm.getCode();
					if(code.equals("NewPost")) {
						code = "답변이 등록되었습니다.";
					}
					TextMessage sendMsg = new TextMessage("("+idx+")" + prefix + "에 " + code);
					single.sendMessage(sendMsg);
				}
			}
		}
	}
	
	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {//연결 해제
		// TODO Auto-generated method stub
		logger.info("Socket 끊음");
		//웹 소켓이 종료될 때마다 리스트에서 뺀다.
		sessions.remove(session);
	}
}

 

alarm.js

/* 02.23 장재호 */
const alarmUL = document.querySelector("#alarmUL");
const alarmI = document.querySelector("#alarmI");
const alarmDiv = document.querySelector("#alarmDiv");
var sock = null;

$(document).ready(function(){
	connectWs();

});

//소켓
function connectWs(){
	var ws = new SockJS("http://localhost:8080/echo");
	sock = ws;

	ws.onopen = function() {
		console.log("연결완료");
 		ws.send($('#socketuserID').val());
	};

	ws.onmessage = function(event) {
		/* 받을 알람이 있을 때 */
		console.log(event.data);
		if(event.data.length>0){
			let newAlarm = '';
			newAlarm += '<li scope="col">' + event.data + "</li>"
			$('#alarmUL').append(newAlarm);
			alarmDiv.style.visibility = "visible";
		}
	};

	ws.onclose = function() {
	    console.log('close');
	};

};

/* 알람창 추가 */

alarmI.addEventListener('click', function(){
	alarmUL.classList.toggle('visible');
	$(this).stop(false, false);
});

alarmUL.addEventListener('click', function(e){
	var endIdx = e.target.textContent.indexOf(")");
	var idx = e.target.textContent.substr(1, endIdx-1);

	$.ajax({
		url : '/alarmDel',
		data : {"idx" : idx},
		type : 'post',
		success : function(data){
			console.log(data);
			alert("성공");
		}
	})
	
	$(e.target).remove();
	if(alarmUL.children.length == 0){
		alarmDiv.style.visibility = "hidden";
	}
	
})
/* *************************** */

 

 

실행되는 순서이다.

1. alarm.jsp를 include받은 페이지 접속 시 alarm.js에 설정된 소켓이 생성되며 EchoHandler의 아래 연결

@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    logger.info("Socket 연결");
    //웹 소켓이 생성될 때마다 리스트에 넣어줌
    sessions.add(session);
}

2. 소켓이 생성 될 때 아래의 send메서드를 통해 세션에 등록되어 있는 USER_ID가 전송된다.(input hidden)

ws.onopen = function() {
    console.log("연결완료");
    ws.send($('#socketuserID').val());
};

전송이 되면 아래의 EchoHandler의 메서드 중 handleTextMessage로 간다.

굳이 세션ID를 보내지 않고, ws.send()처리를 해도 됐다. 핸들러의 메서드를 실행만 시키면 됐었다.

왜냐, 서블릿 설정 시 HttpSession을 사용할 수 있는 인터셉터 설정을 하였기 때문이다.

message.getPayload()를 사용하여 JS에서 보낸 메세지를 출력할 수 있다. 만약 인터셉터를 사용하지 않고 세션값을 전송했을 경우, 해당 메서드를 사용하여 세션값을 뽑아 사용하면될 것이다.

//JS에서 메세지 받을 때.
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {// 메시지

    for(WebSocketSession single : sessions) {

        //세션아이디
        String hsid = (String) single.getAttributes().get("user_id");

        //세션값이 같을때, 알람보낼 것이 있을 때만 전송 -> 로그인 한 사용자가 처음으로 알람 받는 순간임
        //해당 sendMsg에 DB정보 넣어서 체크 안된 알람 전부 전송하기
	if(single.getAttributes().get("user_id").equals(session.getAttributes().get("user_id"))) {        //체크 안된 알림들만 담아서 View
            List<AlarmDto> dto = new ArrayList<>();
            dto = alarmDao.selectAlarm(hsid);
            for(AlarmDto alarm : dto) {
                int idx = alarm.getIdx();
                String prefix = alarm.getPrefix();
                String code = alarm.getCode();
                if(code.equals("NewPost")) {
                    code = "답변이 등록되었습니다.";
                }
                TextMessage sendMsg = new TextMessage("("+idx+")" + prefix + "에 " + code);
                single.sendMessage(sendMsg);
            }
        }
    }
}

여튼 인터셉터를 사용했기 때문에, single에서 getAttribute를 통해 세션에 있는 유저 아이디를 가져올 수 있다.

해당 유저아이디를 바탕으로 조회하여 ALARM 테이블에 있는 정보를 가져와서 알람이 있을 경우 다시 JS를 통해 View에 출력 할 수 있다.

 

3. 전송된 무언가가 있을 경우

ws.onmessage = function(event) {
    /* 받을 알람이 있을 때 */
    if(event.data.length>0){
        let newAlarm = '';
        newAlarm += '<li scope="col">' + event.data + "</li>"
        $('#alarmUL').append(newAlarm);
        alarmDiv.style.visibility = "visible";
    }
};

전송된 무언가가 있을 경우, newAlarm 변수 안에 리스트 형태로 알람 값을 담아주었다.

alarmDiv는 알람 버튼이 있는 디비전을 말하며, 알람이 있을 경우 종 모양 아이콘이 화면에 나타난다.

종 모양 아이콘을 클릭할 경우 알림 내역들이 보일 수 있게 설계하였다.

 

 

예시로 관리자가 질문을 등록하고 관리자가 답변을 달았을 때 알림이 생기는 것을 담았다. 로그인 로그아웃 질문등록 이런 과정들이 시간이 길어 GIF가 깨져서 그러니 양해바란다.

 

 

갑자기 난이도가 훅 올라간 느낌이여서 어려웠지만 소켓을 다루다보니 너무 재밌었다. 조금 더 심도있게 공부해도 재밌을 것 같다

728x90
300x250
mag1c

mag1c

2년차 주니어 개발자.

방명록