JS 배열 및 객체에서 일반적인 깊은 복사 얕은 복사 개념에 대해서는 넘어가고, 

nested된 객체 및 배열에 대해 얘기한다.

 

nested는 "중첩된"이라는 뜻인데,

일반적으로 많이 사용하는 깊은 복사 방법인 스프레드 연산 (...) 를 통해 객체를 복사하면 깊은 복사가 되긴한다.

단, 객체 안 객체, 배열 안 배열은 깊은 복사 되지 않는다.

 

// 깊은 복사
let obj = {a:'a'}
let deepCopy = {...obj}

// nested object
let obj = {a:'a', nested:{b:'b'}}
let deepCopy = {...obj}
let deepCopy2 = {...obj}


deepCopy.nested.b = 'B';
deepCopy2.nested.b = 'C';

console.log(deepCopy.nested.b) // 'C' 
console.log(deepCopy2.nested.b) // 'C'

외부 라이브러리 없이 해결 방법으로는 

let deepCopy = JSON.parse(JSON.stringify(obj));

이 있다.

 

lodash 같은 라이브러리를 활용하면

import cloneDeep from 'lodash/cloneDeep';

let deepCopy = cloneDeep(obj)

도 가능하다.

'Javascript' 카테고리의 다른 글

Promise를 지원하지 않는 NPM 모듈 Promise로 변환  (0) 2023.04.14

Container / Presentation 방식

container : 데이터 조작을 다루는 컴포넌트

presentation : 화면을 다루는 컴포넌트

CRA는 NPX로

NPX는 그 순간 최신의  소스를 받아와서 설치 후 삭제함

NPM 전역으로 CRA를 설치후 CRA를 하면, 설치 당시의 버전으로 사용해야하고,

나중에 패키지가 업데이트 되면 새로 전역에 설치해야함

또, CRA의 의존성 패키지들을 로컬에 남겨두지 않음

React + type script

npx create-react-app my-app --template typescript

React에 typescript , scss 추가

yarn add -D typescript @types/node @types/react @types/react-dom @types/jest
yarn add sass

// tsconfig.js
{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
  },
  "include": [
    "src"
  ]
}

React + eslint + prettier + redux tool-kit

yarn add -D prettier eslint-config-prettier eslint-plugin-prettier eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
yarn add @reduxjs/toolkit react-redux react-router-dom

// .eslintrc.js
module.exports = {
  env: {
    browser: true,
    node: true,
  },
  extends: [
    'prettier',
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:prettier/recommended',
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 12,
    sourceType: 'module',
  },
  plugins: ['react', '@typescript-eslint'],
  rules: {
    'prettier/prettier': ['error', { endOfLine: 'auto' }],
  },
};

// .prettierrc.js
module.exports = {       
  // String is in single quotes (') 
  // 문자열은 홀따옴표(')로 
  singleQuote: true,
  // With a semicolon at the end of the code. 
  // 코드 마지막에 세미콜른이 있게 
  semi: true,
  // Do not use tabs and replace them with space bars. 
  // 탭의 사용을 금하고 스페이스바 사용으로 대체하게 
  useTabs: false,
  // Indentation width of 2 spaces 
  // 들여쓰기 너비는 2칸
  tabWidth: 2,
  // When you create an object or array, you also put a comma on the element or on the back of the key-value.
  // 객체나 배열을 작성 할 때, 원소 혹은 key-valueㅇ의 맨 뒤에 있는 것에도 쉼표를 붙임
  trailingComma: 'all',
  //One line of code is maximum 80 spaces 
  // 코드 한줄이 maximum 80칸
  printWidth: 80,
};

filter vs find

const item = productList.find((list: productListProps) => {
    return list.id === Number(id);
});
// 조건에 해당하는 요소 반환
const item2 = productList.filter((list: productListProps) => {  //
  return list.id === Number(id);
});
// 배열만들어서 만들어줌

React Query

yarn add react-query

//index.tsx
import { QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient();

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement,
);
root.render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <Provider store={store}>
        <BrowserRouter>
          <App />
        </BrowserRouter>
      </Provider>
    </QueryClientProvider>
  </React.StrictMode>,
);


// DashBoard.tsx 대쉬보드 컴포넌트
import axios from 'axios';
import { useQuery } from 'react-query';
import * as React from 'react';

const DashBoard = () => {
   const result = useQuery(
    '작명',
    () =>
      axios.get('https://codingapple1.github.io/userdata.json').then((a) => {
        return a.data;
      }),
    {
      refetchOnWindowFocus: false, // react-query는 사용자가 사용하는 윈도우가 다른 곳을 갔다가 다시 화면으로 돌아오면 이 함수를 재실행합니다. 그 재실행 여부 옵션 입니다.
      retry: 0, // 실패시 재호출 몇번 할지
      onSuccess: (data) => {
        // 성공시 호출
        console.log(data);
      },
      onError: (e) => {
        // 실패시 호출 (401, 404 같은 error가 아니라 정말 api 호출이 실패한 경우만 호출됩니다.)
        // 강제로 에러 발생시키려면 api단에서 throw Error 날립니다. (참조: https://react-query.tanstack.com/guides/query-functions#usage-with-fetch-and-other-clients-that-do-not-throw-by-default)
        console.log(e.message);
      },
    },
  );

  return (
    <div>
      {result.isLoading && '로딩중'}
      {result.error && '에러남'}
      {result.data && result.data.name}
    </div>
  );
};

export default DashBoard;

불변성 지키면서 배열 업데이트

let [users, setUsers] = useState([]);

# 배열에 추가
setUsers(users.concat(user));
# 배열에 추가 (함수형)
setUsers((prev) => prev.concat(user));

# 배열에서 삭제 
const onRemove = id => {
  // user.id 가 id 인 것을 제거
  setUsers(users.filter(user => user.id !== id));
};
 
#배열 수정
const onToggle = id => {
  setUsers(
    users.map(user =>
      user.id === id ? { ...user, active: !user.active } : user
    )
  );
};

불변성 지키면서 객체(object) 업데이트

#객체에 추가 #객체 업데이트
setUsers(state => {...state, key: value})

#객체에서 제거 #loadash
setUsers(state => {..._.omit(state, 'deleteKey')})

 

맨 아래로 Scroll 하기

messageListRef.current?.scrollTo(0, messageListRef.current.scrollHeight);

ES6에서 $ 기호를 통해 변수를 쉽게 삽입할 수 있게 되었다.

예제를 통해 보자

 

let email = "abc@naver.com";

console.log("이메일은 " + email + "입니다"); //기존 따옴표 방법

console.log(`이메일은 ${email} 입니다`)  //new


console.log(`${5+10} 입니다`) //이런 것도 가능하다.

 

결과

아주 편리하다.

오늘은 하이차트를 통해 실시간으로 그래프를 그려보고자 한다.

<div id="container"></div>
let chart = new Highcharts.Chart({
    chart: {
      renderTo: 'container',
      defaultSeriesType: 'spline',
    },
    title: {
      text: 'temper'
    },
    xAxis: {
      type: 'datetime',
      tickPixelInterval: 200,
      maxZoom: 20 * 1000
    },
    yAxis: {
      minPadding: 0.2,
      maxPadding: 0.2,
      title: {
        text: 'temper',
        margin: 80
      }
    },
    series: [{
      name: 'test_11170',
      data: []
    }]
  });

일단 기본적으로 차트 컨테이너를 그린다.

 

mqtt 는 pahomqtt를 사용했다. mqtt에 대한 설정 및 사용은 본 글의 주제를 벗어나므로

다른글을 참고하거나, 기회가 된다면 추후 작성하겠다.

 

설정법을 몰라도 아래 코드를 보면 간단하게 이해가 될 것이라고 생각한다.

this.host = hostip.substring(7); 
this.wsport = 9001;
this.clientId = "mqttclient!!!"+generate();
------------------------------------------
let mqtt = new Paho.MQTT.Client(this.host, this.wsport, this.clientId)
let connectOptions = {
	onSuccess : function()
	{
         console.log("success");
	},
	useSSL: false,
	timeout: 3,
	mqttVersion: 4,
	onFailure: function(msg) 
	{
		console.log("fail");
	},
};
mqtt.connect(connectOptions);
mqtt.subscribe("topic/topic");
mqtt.onMessageArrived = function( message )
{			
	if(message.retained==true){
		return;
	}
	mqttMessage(message.payloadString);
}.bind(this);

function mqttMessage(value) {
    let point = {x:new Date(date).getTime(), y: value, marker: {enabled : false}};
    chart.series[0].addPoint(point, true, false);
}

topic/topic 이라는 토픽을 구독하고

mqtt로 메세지를 받을때마다 mqttMessage라는 함수를 호출하고 

mqtt로 받은 값을 하이차트의 addpoint기능을 통해 그래프를 그린다.

 

필자가 개발한 실 서비스의 경우 서버단의 기계설비의 측정 데이터 등이 mqtt로 오면

해당 데이터 값을 파싱하고 실시간으로 데이터를 그리고 있다.

기존 코드를 간략하게 나타낸거라 중간중간 오타나 뎁스, 문법등이 맞지 않는 부분이 있을지도 모르겠다.

컨셉과 방법에 대한 설명이니 너그럽게 넘어가자!

 

 

'Javascript > Highchart' 카테고리의 다른 글

(javascript) Highchart 그래프 그리기  (1) 2020.12.09
<div>
  <canvas id="canvas" style="border: 1px solid black;  "></canvas>
</div>​
// 기존 canvas 코드
let canvas = new new fabric.Canvas('canvas');
let ctx = canvas.getContext("2d");
ctx.translate(ctx.canvas.width, ctx.canvas.height);
ctx.rotate(180 * Math.PI / 180);

// fabric js 코드
let canvas = new new fabric.Canvas('canvas');
let canvasCenter = new fabric.Point(canvas.getWidth() / 2, canvas.getHeight() / 2) // center of canvas
let radians = fabric.util.degreesToRadians(180)
canvas.getObjects().forEach((obj) => {
    let objectOrigin = new fabric.Point(obj.left, obj.top)
    let new_loc = fabric.util.rotatePoint(objectOrigin, canvasCenter, radians)
    obj.top = new_loc.y
    obj.left = new_loc.x
    obj.angle += 180
    obj.setCoords()
});
canvas.renderAll()

Canvas로 작성된 기존 코드를 Fabric.js 라이브러리를 추가하여 업그레이드 한적이 있다.

기존에는 캔버스를 rotate한뒤 translate로 위치를 잡아주면 되는 코드다.

 

fabric js도 canvas 이기때문에 똑같이 먹힐거라 생각했지만, 이유는 모르겠고 잘 안되더라.

방법을 찾아보니 translate 과정을 top, left로 해결한 듯 하다.

 

 

json에서 키값으로 접근하여 remove하고 싶을때가 생긴다.

왜 난 delete 문법을 모르고 지금까지

for문을 돌며 key값 뽑아내고 key값으로 다시 접근해서 remove하고 있던걸까...

 

단, 배열에서는 delete를 해도 해당 인덱스의 값이 empty로 되지만 배열의 길이는 그대로 3이다.

let json = {
    "key1" : "value1",
    "key2" : "value2",
    "key3" : "value3"
}


delete json["key1"];

console.log(json);
// {key2: "value2", key3: "value3"}
console.log(Object.keys(json).length);
// 2

//단, 배열에서는
let arr = [0,1,2]
delete arr[0]; 
console.log(arr) 
// [1,2]
console.log(arr.length) 
// 3

 

+ Recent posts