Never give up

Typescript - Completer 본문

해왔던 삽질..

Typescript - Completer

대기만성 개발자 2023. 3. 20. 21:59
반응형

오늘은 Dart에서 사용하는 Completer를 typescript에서도 사용해봤습니다

(dart completer 링크 : https://api.flutter.dev/flutter/dart-async/Completer-class.html)

 

공식 문서에 따르면

 

A way to produce Future objects and to complete them later with a value or error

 

이 친구를 간단하게 설명하자면

 

내가 원하는 시점에 다른 곳에서 Promise를 완료시켜줄 수 있는 친구입니다

 

말로는 뭔지 조금 애매하니 예제와 함께 보도록 하겠습니다

 

본격적으로 시작하기전에 경고문구가 필요할거 같아서 한번 만들어봤습니다

(사실 예제 만들면서 이거 이렇게 쓰면 안되는데.. 하는 생각이 더 많이 들었습니다)

< 결ㅎ..이 아니라 따라하지마세요! >

먼저 웹소켓을 준비해봤는데 RestAPI와 달리 보내는 시점과 받는 시점이 다르고

 

위치 또한 다르기 때문에 이렇게도 쓸 수 있구나 하고 보시면 되겠습니다

 

server

const { WebSocketServer } = require('ws');
const util = require('./util/util');

const server = new WebSocketServer({ port: 8080, path: '/calculator' });

server.on("connection", (socket) => {
    socket.on("message", (data) => {
        const message = JSON.parse(data);

		// /^\d+$/.test(`${Math.abs(data)}
        if (util.checkNumber(message?.num1) && util.checkNumber(message?.num2)) {

            socket.send(`Result : ${message.num1 + message.num2}`);
        }
    });
});

socket io 대신 ws library를 써서 간단하게 더하기 해주는 동작만 만들어 봤습니다

 

주의 사항은 주고 받는 message는 문자열만 가능합니다

 

client/socket_controller.ts

import { Completer } from "src/util/completer";

const socketController = () => {
    const socket = new WebSocket('ws://localhost:8080/calculator');

    let isConnected = false;

    let completer: Completer<string>;

    socket.onopen = (e) => {
        console.log('[socket] connected', { e });

        isConnected = true;
    };

    socket.onerror = (e) => {
        console.log('[socket] error', { e });

        isConnected = false;
    };

    socket.onclose = (e) => {
        console.log('[socket] closed', { e });

        isConnected = false;
    };

    socket.onmessage = (e) => {
        completer.complete(`${e.data}`);
    };

    return {
        sendData: (model: object) => {
            if (isConnected) {
                completer = new Completer<string>();

                socket.send(JSON.stringify(model));
            } else {
                console.log('[socket] disconnected');
            }
        },
        getData: async () => {
            return await completer.promise;
        },
        dispose: () => {
            socket.close();
        }
    };
};

export default socketController;

기본으로 제공되는 WebSocket api를 사용해서 웹소켓에 연결을 해주고

 

각종 이벤트들과 서버로 데이터를 보내는 부분(sendData)과 받는 부분(getData)을 만들어주는데

 

여기서 completer라는 친구를 보면

 

sendData할 때 초기화를 하고 onmessage로 데이터를 받았을 때 complete를 사용해서

 

내가 원하는 시점에 받을 수 있도록 해줍니다

 

client/completer

export class Completer<T> {
    public readonly promise: Promise<T>;
    public complete!: (value: T | PromiseLike<T>) => void;
    public reject!: (reason?: any) => void;

    public constructor() {
        this.promise = new Promise<T>((res, rej) => {
            this.complete = res;
            this.reject = rej;
        });
    }
}

(링크 : https://stackoverflow.com/questions/34673902/typescript-equivalent-to-dart-completer)

 

우리의 영원한 친구(?) 스텍 오버플로우에 나온거 그대로 사용해봤는데

 

내부를 보면 별거 없습니다

 

반환되는 값: promise

완료시키기: complete

중단: reject

 

complete(원하는 값) 형태로 비동기처리를 완료하거나, reject(원하는 값)형태로 에러 처리도 가능합니다

 

client/view

import React, { FormEvent, useCallback, useEffect, useRef, useState } from 'react';
import CommonBtn from 'src/components/common_btn/common_btn';
import InputComponent, { BorderEnum } from 'src/components/input/input_component';
import socketController from 'src/controller/socket_controller';
import styles from './test.module.css';

const Test = () => {
    const [num1, setNum1] = useState<number>(0);
    const [num2, setNum2] = useState<number>(0);
    const [result, setResult] = useState<string>('');

    const controller = useRef<any>();

    useEffect(() => {
        controller.current = socketController();

        return () => {
            controller.current?.dispose();
        };
    }, []);

    const calculate = useCallback(async (e: FormEvent) => {
        e.preventDefault();

        const model = { num1, num2 };

        controller.current?.sendData(model);

        const res = await controller.current?.getData();

        setResult(res);
    }, [num1, num2]);

    return (
        <div className={styles.div_test}>
            <p>Calculator</p>
            <form onSubmit={calculate}>
                <InputComponent border={BorderEnum.outline} type={'number'} value={num1} onChange={(e) => setNum1(Number(e.target.value))} />
                <InputComponent border={BorderEnum.outline} type={'number'} value={num2} onChange={(e) => setNum2(Number(e.target.value))} />
                <CommonBtn type='submit'>
                    Cacluate
                </CommonBtn>
            </form>
            <p>{result}</p>
        </div>
    );
};

export default Test;

socket을 연결하고 submit이 동작될 때

 

서버로 데이터를 보내고, 원하는 시점(데이터가 들어오는 시점)에 complete를 해서

 

비동기 처리를 완료합니다

(예제처럼 이렇게 쓰지마시고.. store를 활용하시길..)

 

< 결과물 >

혹시나 js가 필요하신 분들을 위해 js도 준비해봤습니다

export class Completer {
    constructor() {
        this.promise = new Promise((res, rej) => {
            this.complete = res;
            this.reject = rej;
        });
    }
}

 

대부분의 상황에는 필요 없으니, 그냥 이런 방법도 있다 정도로 넘어가 주시면 될거 같습니다

 

(간단한 예제이니 전체 코드는 따로 남기지는 않겠습니다)

 

 

-- 이하 필자의 헛소리 타임이 시작될 예정이니 예제만 확인하실 분들은 스킵하시면 됩니다

 

 

간만에 드립과 짤을 적절하게 배치해봤는데, 나름 리프레싱 되고 좋았던거 같습니다

 

위에도 누누이 말했듯이.. 대부분의 상황에서는 필요가 없습니다

 

그런데.. 너무 귀찮은 부분에는 이렇게 해도 되지 않을까 하는 못된 심보(?)가 이렇게 하게 만들었습니다..

 

< 필자의 상태 >

3월 목표중 하나가 three js 처음부터 다시 공부해보는거였는데

 

어쩌다보니 시작도 못하게 됐는데 다시 열심히 해야될거 같습니다

 

< 시작하기로 마음먹었으니 이미 반 했ㄷ... >

 

반응형
Comments