Android

[안드로이드]Retrofit(okhttp)의 WebSocket에 대하여

나맘임 2024. 1. 7. 17:47

웹소켓(WebSocket)

양방향 통신을 지원하는 프로토콜로, 클라이언트와 서버 간 실시간 데이터 전송을 가능케 한다. HTTP 기반의 통신과 달리 지속적인 연결을 제공하며, 이를 통해 실시간으로 데이터를 주고받을 수 있다.

 

안드로이드에선 Okhttps 라이브러리의 WebSocket 모듈을 사용하여 구현할 수 있다.

 

최근엔 Okhttps의 구현을 쉽게 추상화를 한 Retrofit를 주로 사용한다.

 

이 글에선 Retrofit를 이용할 예정이다.

(뭘 사용해도 상관 없다)

1. 의존성 추가

    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.11.0'
    
    //okhttp만 쓰고 싶다면
    implementation 'com.squareup.okhttp3:okhttp:4.9.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.11.0'

2. 권한 설정

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />

네트워크와의 통신이므로 인터넷 권한 설정이 필요하다.

 

3. 사용법

먼저 서버로 보내는 Json 데이터는 다음과 같다고 가정한다.

{
  "senderNickName": "Tester",
  "message": "Hi",
  "date": "2024-01-01",
}

WebSocket 생성

class HttpWebSocket @Inject constructor(private val listener: ChattingWebSocketListener) {

    private val client = OkHttpClient()
    private val webSocket: WebSocket
    private val chatUrl = "http://10.0.2.2:8090/chat"

    init {
        val chatRequest = Request.Builder().url(chatUrl).build()
        webSocket = client.newWebSocket(chatRequest, listener)
    }

    fun sendJsonMessage(message: Message) {

        val jsonMessage = JSONObject()
            .put("senderNickname", message.senderNickname)
            .put("message", message.message)
            .put("date", message.date)
            .toString()
        webSocket.send(jsonMessage)
    }

    fun closeWebSocket() {
        webSocket.close(1000, "Connection closed")
    }

}

 

WebSocket 객체를 생성하기 위해선

 

    init {
        val chatRequest = Request.Builder().url(chatUrl).build()
        webSocket = client.newWebSocket(chatRequest, listener)
    }

 

먼저 OkHttpClient 인스턴스를 생성하고

 

서버 주소로 Request로 보낸 결과를 가진 인스턴스와 WebSocketListener가 필요하다.

 

그러면 WebSocketListener가 뭘까?

WebSocketListener

class ChattingWebSocketListener @Inject constructor() : WebSocketListener() {

    override fun onMessage(webSocket: WebSocket, text: String) {
        super.onMessage(webSocket, text)
        Log.d("listener", text)
    }

    override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
        super.onClosed(webSocket, code, reason)
    }

    override fun onOpen(webSocket: WebSocket, response: Response) {
        super.onOpen(webSocket, response)
    }

    override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
        super.onFailure(webSocket, t, response)
    }

}

간단하다.

 

WebSocket에서 이벤트가 발생했을 때 어떻게되는지를 나타낸다.

 

onMessage는 서버 측에서 BroadCast를 했을 때를,

 

onClosed는 WebSocket이 닫힐 경우를,

 

onOpen는 WebSocket이 열릴 경우를,

 

onFailure는 WebSocket 통신을 서버로 실행했는데 실패했을 때를

 

나타낸다.

 

다시 WebSocket으로 돌아와 서버로 데이터를 전달하기 위해선

 

    fun sendJsonMessage(message: Message) {

        val jsonMessage = JSONObject()
            .put("senderNickname", message.senderNickname)
            .put("message", message.message)
            .put("date", message.date)
            .toString()
        webSocket.send(jsonMessage)
    }

다음과 같은 메소드를 만들어서하면 전달하면 된다.

 

주요 메소드는 webSocket.send()로 매개변수로 String 값을 전달하면 된다.

 

WebSocket 통신을 종료하고 싶다면 

 

    fun closeWebSocket() {
        webSocket.close(1000, "Connection closed")
    }

 

webSocket.close() 메소드를 사용하면 된다.

 

매개변수는 시간, 종료 메시지를 받는다.

4. 서버 측에서 데이터를 처리하기

이는 서버가 어떤 프레임워크를 사용하는지에 따라 다르다.

 

많이 사용하는 스프링의 경우로 예시를 들자면

ChatController

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String payload = message.getPayload();

        JsonNode jsonNode = objectMapper.readTree(payload);

        String username = jsonNode.get("senderNickname").asText();
        String userMessage = jsonNode.get("message").asText();
        String date = jsonNode.get("date").asText();

        byte[] encryptedMessage = Base64.getDecoder().decode(userMessage);
        String decryptedMessage = decryptMessage(encryptedMessage);

        System.out.println("user :" + username + ", msg : " + decryptedMessage
                + ", date : " + date);

        session.sendMessage(message);
    }

(이 예시는 암호화된 메시지를 클라이언트로부터 받았기 때문에 복호화하는 과정이 담겨있다)

 

스프링 프레임워크 중에 WebSocket 모듈이 있다.

 

이를 사용해주면 정상적으로 통신을 할 수 있고

 

session.sendMessage()와 같이 BroadCast를 해줄 수 있는 메소드가 존재하니 이용하면 된다.

 

Nodejs의 경우

const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 3000 });

server.on('connection', (socket) => {
    console.log('클라이언트가 연결되었습니다.');

    // 메시지 수신 이벤트
    socket.on('message', (message) => {
        console.log(`수신한 메시지: ${message}`);

        // 클라이언트에 메시지 전송
        socket.send('서버에서 메시지를 전송합니다.');
    });

    // 연결 종료 이벤트
    socket.on('close', () => {
        console.log('클라이언트와의 연결이 종료되었습니다.');
    });
});

 

ws라는 라이브러리가 존재한다. 이를 사용하거나, socket.io를 사용해도 된다.

 

위 예시에선 ws 라이브러리를 사용하여 메시지 수신을 처리하는 과정을 볼 수 있다.

 

출처

OkHttp - WebSocket (velog.io)

 

OkHttp - WebSocket

이번 글에서는 Android 앱에서 WebSocket 통신을 구현하는 방법에 대해서 작성해보려고 합니다. WebSocket이란? WebSocket은 웹 브라우저와 서버 간에 양방향 실시간 통신을 가능하게 하는 프로토콜입니다

velog.io

웹 소켓(Web Socket) 이용하기(with. ws 모듈, Socket.IO) (velog.io)

 

웹 소켓(Web Socket) 이용하기(with. ws 모듈, Socket.IO)

1. 웹 소켓(Web Socket)이란?, 2. ws 모듈로 웹 소켓 사용하기, 3. Socket.IO로 웹 소켓 사용하기

velog.io