개발 노트

자료구조 변경 비교 본문

React

자료구조 변경 비교

한츄 2024. 3. 13. 09:30

기존 : mqtt 수신내역을 배열형태로 일괄적으로 담음

변경 후 : mqtt수신 시 farmCode기준으로 묶어서 내부에 필요한 정보만 저장

 

기존 호출 컴포넌트

const ResizableComponent: React.FC<ResizableProps> = () => {
  const workerRef = useRef<Worker | null>(null);
  const { messages, addMessage } = useMqttStore();

useEffect(() => {
    const URL = '/mqttWorker.built.js';
    if (!workerRef.current) {
      const worker = new Worker(URL);

      workerRef.current = worker;
      workerRef.current.onmessage = function (event) {
        // console.log(typeof event.data.message);
        
        if (event.data.message) {
          const messageTime = extractTime(event.data.message);
          const currentTime = new Date()
            .toLocaleString('ko-KR', {
              hour12: false,
              year: 'numeric',
              month: '2-digit',
              day: '2-digit',
              hour: '2-digit',
              minute: '2-digit',
              second: '2-digit',
            })
            .replace(/. \d{4}/, '')
            .replace(/(\d{4})\. (\d{2})\. (\d{2})\. (\d{2}):(\d{2}):(\d{2})/, '$1-$2-$3 $4:$5:$6');
          const now = new Date();
          const sevenDaysAgo = new Date(now.setDate(now.getDate() - 7));

          if (new Date(messageTime) >= sevenDaysAgo) {
            const splitTopic = event.data.topic.split('/');
            const farmCode = splitTopic.length > 2 ? splitTopic[2] : '';

            // 메시지 객체 생성
            const messageObject = {
              topic: event.data.topic,
              message: event.data.message,
              time: messageTime,
              currentTime: currentTime,
            };

            addMessage(payload);
          }
        }
      };

      if (workerRef.current) {
        workerRef.current.postMessage({ CMD: 'MQTT_START' });
        workerRef.current.postMessage({
          CMD: 'MQTT_SUBSCIBE',
          DATA: ['dawoon/ALIVE/#', 'dawoon/Robot/#', 'dawoon/RMAN/#', 'dawoon/Feeder/#', 'dawoon/SMARTGATE/#'],
        });
      }
    }
return function cleanup() {
      if (workerRef.current) {
        workerRef.current.terminate();
        workerRef.current = null;
      }
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

 

 

변경 호출컴포넌트 => 그룹으로 변경

const ResizableComponent: React.FC<ResizableProps> = () => {
const workerRef = useRef<Worker | null>(null);
const {groupedMessages, addGroupedMessage } = useGroupedMqttStore()

  useEffect(() => {
    const URL = '/mqttWorker.built.js';
    if (!workerRef.current) {
      const worker = new Worker(URL);

      workerRef.current = worker;
      workerRef.current.onmessage = function (event) {
        // console.log(typeof event.data.message);
        
        if (event.data.message) {
          const messageTime = extractTime(event.data.message);
          const currentTime = new Date()
            .toLocaleString('ko-KR', {
              hour12: false,
              year: 'numeric',
              month: '2-digit',
              day: '2-digit',
              hour: '2-digit',
              minute: '2-digit',
              second: '2-digit',
            })
            .replace(/. \d{4}/, '')
            .replace(/(\d{4})\. (\d{2})\. (\d{2})\. (\d{2}):(\d{2}):(\d{2})/, '$1-$2-$3 $4:$5:$6');
          const now = new Date();
          const sevenDaysAgo = new Date(now.setDate(now.getDate() - 7));

          if (new Date(messageTime) >= sevenDaysAgo) {
            const splitTopic = event.data.topic.split('/');
            const farmCode = splitTopic.length > 2 ? splitTopic[2] : '';

            // 메시지 객체 생성
            const messageObject = {
              topic: event.data.topic,
              message: event.data.message,
              time: messageTime,
              currentTime: currentTime,
            };

            // addGroupedMessage를 사용하여 메시지 추가
            addGroupedMessage(farmCode, messageObject);
          }
        }
      };

      if (workerRef.current) {
        workerRef.current.postMessage({ CMD: 'MQTT_START' });
        workerRef.current.postMessage({
          CMD: 'MQTT_SUBSCIBE',
          DATA: ['dawoon/ALIVE/#', 'dawoon/Robot/#', 'dawoon/RMAN/#', 'dawoon/Feeder/#', 'dawoon/SMARTGATE/#'],
        });
      }
    }

    return function cleanup() {
      if (workerRef.current) {
        workerRef.current.terminate();
        workerRef.current = null;
      }
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

 

 

변경 전 zustand store코드

  • 배열에 담는 구조여서 같은내용을 중복처리 하지 않기 위한 코드 필요
  • 배열에 필요한내용을 추가적으로 담는 구조로 배열의 내용자체가 많고 무거워짐
export const useMqttStore = create<Store>((set, get) => ({
  messages: [],
  addMessage: (payload: Message) => {
    const splitTopic = payload.topic.split('/');
    const parsedMessage = JSON.parse(payload.message);
    const cmd = parsedMessage.CMD;

    // 특정 조건에 따라 추가 필드 설정
    let additionalFields = {};
    
    //추가 설정 내역 생략
    
    // 이름값 가져오기
    if ('FNAME' in parsedMessage || 'FARMNAME' in parsedMessage) {
      additionalFields = { ...additionalFields, farmName: parsedMessage.FARMNAME || parsedMessage.FNAME };
    }
// 새로운 메시지를 추가하는 함수
set((state) => {
  // 이전 메시지 중 중복되지 않은 메시지만을 필터링하여 새 배열을 생성
  const noDuplicateArray = state.messages.filter((message) => {
    // 메시지의 topic을 '/'를 기준으로 분리
    const splitTopic = message.topic.split('/');
    // 분리된 topic의 세 번째 항목이 farmCode
    const farmCode = splitTopic.length > 2 ? splitTopic[2] : '';

    let parsedMessage;
    try {
      // 메시지의 내용을 JSON 형태로 파싱
      parsedMessage = JSON.parse(message.message);
    } catch (error) {
      // JSON 파싱에 실패한 경우 오류를 출력하고, 해당 메시지는 필터링에서 제외
      console.error('Invalid JSON:', message.message);
      return true; 
    }

    // 메시지의 CMD를 가져옴
    const cmd = parsedMessage.CMD;


    if (farmCode === splitTopic[2] && message.topic === payload.topic && cmd === parsedMessage.CMD) {
      // CMD가 "MAIN_ERROR" 또는 "ERROR_SMARTGATE"가 아니라면, 중복으로 판단하고 필터링
      if (cmd !== "MAIN_ERROR" && cmd !== "ERROR_SMARTGATE") {
        // array filter에 의해 noDuplicateArray에 포함하지 않음
        return false;
      }
    }
    
    // 위의 조건들에 해당하지 않는 메시지는 필터링에서 제외하지 않음
    // array filter에 의해 noDuplicateArray에 포함함
    return true;
  });

  // 필터링된 메시지 배열에 새 메시지를 추가하여 상태를 업데이트
  return {
    messages: [...noDuplicateArray, { ...payload, additionalFields }],
  };
});

  },

 

 

변경 후 zustand store 코드

  • 그룹화해서 묶어서 객체형태로 저장(farmCode를  key값으로 가짐)
  • 기존에 가져온 데이터에서 필요한 내용만 추출하여 저장
  • 저장형태를 미리 지정 후(typescript) 그 형태에 맞춰서 지정
import { extractTime } from '@/utils/extractTime';
import { create } from 'zustand';

interface Message {
  topic: string;
  time: string;
  message: any;
  currentTime: string;
  additionalFields?: {
    [key: string]: any;
  };
}
interface GroupedMessages {
  [farmCode: string]: {
    robot: { [key: string]: any };
    smartgate: { [key: string]: any };
    rman: { [key: string]: any };
    web: { [key: string]: any };
    feeder: { [key: string]: any };
    errorList: Message[];
    currentTime: string;
    time: string;
  };
}

interface State {
  groupedMessages: GroupedMessages;
  addGroupedMessage: (farmCode: string, messageObject: Message) => void;
}

const useGroupedMqttStore = create<State>((set) => ({
  groupedMessages: {},
  addGroupedMessage: (farmCode, messageObject) =>
    set((state) => {
      // cmd 필드가 있을 경우 바로 JSON.parse 적용
      if (messageObject.message) {
        try {
          // message 필드가 JSON 문자열이면 파싱
          const message = JSON.parse(messageObject.message);
          messageObject.message = message;
        } catch (e) {
          console.error('message 파싱 오류:', e);
        }
      }

      // 기존 메시지들을 가져옴
      const existingGroup = state.groupedMessages[farmCode] || {
        robot: {},
        smartgate: {},
        rman: {},
        web: {},
        errorList: [],
        feeder:{},
        currentTime: messageObject.currentTime,
        time: messageObject.time,
      };

      // 새 메시지에 대한 추가 필드 설정 로직
      const cmd = messageObject.message.CMD;
      const splitTopic = messageObject.topic.split('/');
      let additionalFields = {};
      
      //추가 필드 설정 삭제
      
            // 메시지를 관련된 객체에 추가
      const messageType = messageObject.topic.split('/')[1].toUpperCase(); 
      switch (messageType) {
        case 'ROBOT':
          existingGroup.robot = { ...existingGroup.robot, ...messageObject.additionalFields };
          if (cmd==='MAIN_ERROR') {
            existingGroup.errorList.push(messageObject)
          }
          break;
        case 'SMARTGATE':
          existingGroup.smartgate = { ...existingGroup.smartgate, ...messageObject.additionalFields };
                      if (cmd==='ERROR_SMARTGATE') {
              existingGroup.errorList.push(messageObject)
            }
          break;
        case 'RMAN':
          existingGroup.rman = { ...existingGroup.rman, ...messageObject.additionalFields };
          break;
          case 'ALIVE':
            if (messageObject.topic.split('/')[1].toUpperCase()==='RMAN'){
            existingGroup.rman = { ...existingGroup.rman, ...messageObject.additionalFields };

          }
          if (messageObject.topic.split('/')[1].toUpperCase()==='WEB'){

            existingGroup.web = { ...existingGroup.web, ...messageObject.additionalFields };
          }
          break;
        case 'FEEDER':
          existingGroup.feeder = { ...existingGroup.feeder, ...messageObject.additionalFields };
          break;
        default:
          console.error('Unknown message type:', messageType);
      }

      // 상태를 업데이트
      return {
        groupedMessages: {
          ...state.groupedMessages,
          [farmCode]: existingGroup,
        },
      };
    }),
}));

export default useGroupedMqttStore;

 

'React' 카테고리의 다른 글

modal dialog component만들기  (0) 2024.03.20
필터기능 구현  (0) 2024.03.18
Next.js에서 mysql2 연결하기 (net error 해결기)  (0) 2024.02.29
mqtt연결 시 connected반복 에러  (0) 2024.02.28
정렬기능 보완  (0) 2024.02.27