Overview
binary 이놈때문에 혼자 하루종일 북치고 장구치고 다했다 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ
암튼 한번 봅시당~
- Typescript 예제
- Springboot - Config
- Springboot - Handler
- 참조
1. Typescript 예제
Typescript 프론트 예제는 그냥 파일 하나 또는 여러장 보내는? 겁니다. ㅎ...
SockJS 는 사용하지 않았습니다.
import React, { useState } from "react";
const BinaryTest = () => {
const [selectedFiles, setSelectedFiles] = useState<FileList | null>(null);
const [imageSrc, setImageSrc] = useState<string | null>(null);
const selectFile = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
setSelectedFiles(event.target.files);
}
};
const sendFilesToServer = () => {
if (selectedFiles) {
const websocket = new WebSocket("ws://localhost:8080/binary");
// Listen for messages
websocket.addEventListener("open", () => {
Array.from(selectedFiles).forEach(file => {
const reader = new FileReader();
reader.onload = (event) => {
if (event.target && event.target.result) {
const arrayBuffer = event.target.result;
websocket.send(arrayBuffer);
}
};
reader.readAsArrayBuffer(file);
});
});
websocket.onmessage = (event) => {
// 서버로부터의 응답 처리
console.log("Received message from server:", event.data);
const arrayBuffer = event.data;
const imageUrl = URL.createObjectURL(new Blob([arrayBuffer]));
setImageSrc(imageUrl);
};
websocket.onclose = () => {
console.log("WebSocket closed");
};
websocket.onerror = (error) => {
console.error("WebSocket error:", error);
};
}
};
return (
<div>
<input
type="file"
className="form-control"
id=""
onChange={selectFile}
multiple
/>
<button onClick={sendFilesToServer}>Send Files</button>
{imageSrc && <img src={imageSrc} alt="Received Image" />}
</div>
);
};
export default BinaryTest;
2. Springboot - Config
제일 중요한 부분이자 이 부분에 하루종일을 쓴 나였다고 한다. 정작 4줄때문에 얼마나 힘들었는지 흑흑...
1. 엔드 포인트 /binary 로 들어오면 Socket 으로 이어주는 역할을 합니다.
2. "/binary" 엔드포인트에 사용할 handler 를 지정해줍니다.
3. setAllowedOrigins 로 cors 문제를 해결합니다.
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry
.addHandler(getWebSocketHandler(), "/binary").setAllowedOrigins("*");
}
4. setMaxBinaryMessageBufferSize 메서드를 사용하여 버퍼 크기를 조정할 수 있습니다.
그리고 제가 못찾았던 부분입니다.
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxBinaryMessageBufferSize(1024 * 1024); // 1MB, adjust as needed
return container;
}
만약 위의 설정이 없을 경우 :
메시지 크기 초과 현상 발생 ☆
만약 데이터가 큰 메시지를 전달하고 싶다면 데이터를 나누어서 전달하거나 다른 방법을 찾아 해결해야합니다.
{} - 클라이언트 접속 : StandardWebSocketSession[id=15b0560b-2f9d-db00-3376-31778f04b22b, uri=ws://localhost:8080/binary]
{} - 클라이언트 ID : 15b0560b-2f9d-db00-3376-31778f04b22b
{} - 클라이언트 프로토클 : ws://localhost:8080/binary
1
{} - 클라이언트 접속 해제 : StandardWebSocketSession[id=15b0560b-2f9d-db00-3376-31778f04b22b, uri=ws://localhost:8080/binary]
오류 메시지(이유) : CloseStatus[code=1009, reason=No async message support and buffer too small. Buffer size: [8,192], Message size: [110,088]]
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry
.addHandler(getWebSocketHandler(), "/binary").setAllowedOrigins("*");
}
@Bean
public WebSocketHandler getWebSocketHandler() {
return new WebSocketHandler();
}
/**
* setMaxBinaryMessageBufferSize 메서드를 사용하여 버퍼 크기를 조정할 수 있습니다.
* @return
*/
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxBinaryMessageBufferSize(1024 * 1024); // 1MB, adjust as needed
return container;
}
}
2. Springboot - Handler
이번에는 Handler 부분입니다.
저는 AbstractWebSocketHandler 를 extens 받았지만 TextWebsocketHandler , BinaryWebSocketHandler 등이 있습니다.
다만, TextWebsocketHandler 는 Binary 데이터 전송을 지원 하지않고 반대로 BinaryWebSocketHandler 또한 Text 타입 데이터 전송을 지원하지 않습니다.
public class WebSocketHandler extends AbstractWebSocketHandler {
// private static List<WebSocketSession> list = new ArrayList<>();
// 얘도 많이들 사용함
private Set<WebSocketSession> sessions
= Collections.synchronizedSet(new HashSet<>());
// Client가 접속 시 호출되는 메서드
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session); // 세션 추가
System.out.println("{} - 클라이언트 접속 : " + session);
System.out.println("{} - 클라이언트 ID : " + session.getId());
System.out.println("{} - 클라이언트 프로토클 : " + session.getUri());
System.out.println(sessions.size());
}
@Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
System.out.println("이곳은 바이너리 데이터를 받는 곳입니다.");
byte[] payload = message.getPayload().array();
System.out.println("{} = payload : " + payload.length);
// 접속된 유저에게 보냄
for (WebSocketSession ws : sessions) {
ws.sendMessage(new BinaryMessage(payload));
}
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage textMessage) throws Exception {
Message message = Utils.getObject(textMessage.getPayload(), Message.class);
message.setSender(session.getId());
// 접속된 유저에게 보냄
for (WebSocketSession ws : sessions) {
ws.sendMessage(new TextMessage(Utils.getString(message)));
}
}
// Client가 접속 해제 시 호출되는 메서드
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
System.out.println("{} - 클라이언트 접속 해제 : " + session);
System.out.println("오류 메시지(이유) : " + status);
sessions.remove(session);
}
}
4. 참조
만약 Text형식 데이터를 받는다면
4.1. Model
> Message 객체
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Message {
private String type;
private String sender;
private String receiver;
private Object data;
}
4.2. Utils
import com.fasterxml.jackson.databind.ObjectMapper;
public class Utils {
private static final ObjectMapper objectMapper = new ObjectMapper();
private Utils() {
}
public static <T> T getObject(final String message, Class<T> valueType) throws Exception {
return objectMapper.readValue(message, valueType);
}
public static <T> String getString(final T message) throws Exception {
return objectMapper.writeValueAsString(message);
}
}
대충 이정도로 꾸며놓았던거 같습니다.
'SpringBoot' 카테고리의 다른 글
URI 설계 - GET , POST, PUT , DELETE 와 Path Variable vs Query Parameter (0) | 2024.01.30 |
---|---|
Springboot - session 사용 로그인 로그아웃 (0) | 2024.01.29 |
Springboot 2.x 버젼 - QueryDSL 설정 (0) | 2023.12.23 |
React , Springboot - 파일 한장 올리기 (0) | 2023.12.21 |
Springboot - 파일 여러장을 저장해보자 (1) | 2023.12.21 |