\ Something New :: Something New
728x90
반응형

이직 한달차~ 야근이 이어지면서 잠시 정보처리기사와 멀어지기 시작했으나

필기를 본지 얼마안된지라 공부한 기억이 그나마 생생한 타이밍!

흠,,,실기 시험 은 20문항으로 

틀리라 낸문제들을 제외하고 프로그래밍문제들을 다 맞아야 합격의 근처에 갈 수 있는 상황이라고

판단했으나

시험은 생각보다 c언어가 많이 어려웠던 것 같다.

파이썬이나 자바도 기존 기출보단 어렵게 나왔던것 같고

스케줄링 계산하라는 문제가 나올줄이야ㅎㅎ

긴장은 안하고 봤는데 3차실기는 진짜 붙도록 빡공해야겠다는 생각이 들었다.

프로그래밍 공부를 중심으로 조져야지 그나마 붙을 가능성이 조금 생기는 느낌이 들고

당연히 응집력,결합도,디자인패턴, 데이터베이스 하나씩 나왔다.

네트워크 프로토콜, 보안쪽 한문항씩 기억나고

나머진 그냥 다 프로그래밍 문제,,

언어 3개를 애매하게 찍먹하는 느낌이 좀 그랬는디,,프로그래밍 어떻게 준비해야할지 다시 생각해봐야긋따

할 일이 갑자기 사라졌는데

이제 뭐하지,,,하다 운동할까 했는데

고데기에 다리 화상을 입어서 당분간은 운동도 힘들것 같다.

파상풍주사도 맞아서 팔도 아프구,,컨디션도 그다지 안좋은데

심심하고 지루한데 누굴 만나긴 귀찮고 힘들다

일도 이제 적당히 밸런스 찾아야지 너무 힘들게 하면 금방 지칠것 같다

재밌는 거 없나~

 

728x90
728x90
반응형

abulator는 JavaScript로 작성된 고성능 그리드 테이블 라이브러리로, 다양한 기능과 유연성을 제공하여 복잡한 데이터 테이블을 쉽게 만들 수 있습니다. 이 블로그에서는 React Query와 Tabulator를 함께 사용하여 조회 및 저장 기능을 구현하는 방법을 설명하겠습니다.

Tabulator 사용 이유

  • 고성능: 대량의 데이터를 빠르게 처리하고 렌더링할 수 있습니다.
  • 유연성: 다양한 데이터 소스와 쉽게 통합할 수 있습니다.
  • 사용 편의성: 간단한 API와 다양한 설정 옵션을 제공하여 개발 시간을 절약할 수 있습니다.
  • 풍부한 기능: 정렬, 필터링, 페이지네이션, 편집 등의 다양한 기능을 기본적으로 제공합니다.

Tabulator 설치 및 설정

npm install tabulator-tables

설치 후, Tabulator 스타일시트를 추가해야 합니다. 이를 위해 index.html 파일에 다음을 추가합니다:

<link href="https://unpkg.com/tabulator-tables@4.9.3/dist/css/tabulator.min.css" rel="stylesheet">

이제 React 컴포넌트에서 Tabulator를 사용할 준비가 되었습니다.

import React, { useEffect, useRef } from 'react';
import Tabulator from 'tabulator-tables';
import 'tabulator-tables/dist/css/tabulator.min.css';

const SimpleGrid = ({ data, onAddRow, onDeleteRow }) => {
  const tableRef = useRef(null);

  useEffect(() => {
    const table = new Tabulator(tableRef.current, {
      columns: [
        { title: 'Name', 
          field: 'name', 
          editor: 'input'   
        },
        { title: 'Age', 
          field: 'age', 
          editor: 'input' 
        },
        // 필요한 다른 컬럼들을 추가합니다
      ],
    });

    // 데이터가 변경될 때마다 테이블을 업데이트합니다
    if (data) {
      table.setData(data);
    }
    
    // Cleanup function to destroy the table when the component unmounts
    return () => table.destroy();
  }, [data]);

  const handleAddRow = () => {
    onAddRow({ name: '', age: '' }); // 기본값을 가진 새 행 추가
  };

  const handleDeleteRow = () => {
    const table = tableRef.current.tabulator;
    const selectedRows = table.getSelectedRows();
    selectedRows.forEach(row => row.delete());
    onDeleteRow(selectedRows.map(row => row.getData())); // 삭제된 행 데이터 반환
  };

  return (
    <div>
      <button onClick={handleAddRow}>행 추가</button>
      <button onClick={handleDeleteRow}>행 삭제</button>
      <div ref={tableRef}></div>
    </div>
  );
};

export default SimpleGrid;
import React, { useState } from 'react';
import { useQuery } from 'react-query';
import SimpleGrid from './SimpleGrid';

// 데이터 조회 함수
const fetchData = async (params) => {
  const response = await fetch(`https://api.example.com/data?${new URLSearchParams(params)}`);
  if (!response.ok) {
    throw new Error('네트워크 응답에 문제가 있습니다');
  }
  return response.json();
};

const DataView = () => {
  const [searchParams, setSearchParams] = useState({});
  const [gridData, setGridData] = useState([]);
  
  // useQuery 훅을 사용하여 데이터를 조회합니다
  const { refetch, isFetching } = useQuery(
    ['fetchData', searchParams],
    () => fetchData(searchParams),
    {
      enabled: false,
      onSuccess: (data) => setGridData(data), // 데이터 조회 후 상태 업데이트
    }
  );

  // 검색 버튼 클릭 시 호출되는 함수
  const handleSearch = () => {
    const params = {
      key1: 'value1',
      key2: 'value2',
    };
    setSearchParams(params);
    refetch();
  };

  const handleAddRow = (newRow) => {
    setGridData([...gridData, newRow]);
  };

  const handleDeleteRow = (deletedRows) => {
    setGridData(gridData.filter(row => !deletedRows.includes(row)));
  };

  return (
    <div>
      <button onClick={handleSearch}>검색</button>
      {isFetching ? (
        <p>로딩 중...</p>
      ) : (
        <SimpleGrid 
        	data={gridData} 
        	onAddRow={handleAddRow} 
            onDeleteRow={handleDeleteRow} />
      )}
    </div>
  );
};

export default DataView;

 

728x90
728x90
반응형

데이터 저장 기능은 useMutation 훅을 사용하여 구현할 수 있습니다. 아래는 예시 코드입니다:

import React, { useState } from 'react';
import { useMutation, useQueryClient } from 'react-query';

// 데이터 저장 함수
const saveData = async (data) => {
  const response = await fetch('https://api.example.com/save', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(data),
  });
  if (!response.ok) {
    throw new Error('데이터 저장에 문제가 있습니다');
  }
  return response.json();
};

const DataSave = () => {
  const queryClient = useQueryClient();
  const [formData, setFormData] = useState({ key1: '', key2: '' });

  // useMutation 훅을 사용하여 데이터를 저장합니다
  const mutation = useMutation(saveData, {
    onSuccess: () => {
      // 저장이 성공하면 데이터를 다시 조회합니다
      queryClient.invalidateQueries('fetchData');
    },
  });

  // 폼 제출 시 호출되는 함수
  const handleSubmit = (event) => {
    event.preventDefault();
    mutation.mutate(formData);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={formData.key1}
        onChange={(e) => setFormData({ ...formData, key1: e.target.value })}
        placeholder="Key1"
      />
      <input
        type="text"
        value={formData.key2}
        onChange={(e) => setFormData({ ...formData, key2: e.target.value })}
        placeholder="Key2"
      />
      <button type="submit">저장</button>
      {mutation.isLoading ? (
        <p>저장 중...</p>
      ) : mutation.isError ? (
        <p>저장에 실패했습니다: {mutation.error.message}</p>
      ) : (
        mutation.isSuccess && <p>저장이 완료되었습니다!</p>
      )}
    </form>
  );
};

export default DataSave;

 

  • saveData 함수: 서버에 데이터를 저장하는 함수입니다.
  • useMutation 훅: React Query를 사용하여 데이터를 저장합니다.
  • handleSubmit 함수: 폼 제출 시 호출되어 데이터를 저장합니다.
  • queryClient.invalidateQueries: 데이터 저장이 성공하면 해당 쿼리를 무효화하여 데이터를 다시 조회합니다.

 

728x90
728x90
반응형

안녕하세용

저는 새로운 플젝에 들어오구 3주차에 회식을 하게 되었답니다~

사실 굉장히 어색어색한 점심이 지나고

걸을때 이따금식 대화를 하지만 아직 크게 오가는 얘기는 없었는데용

첫 회식 넘나 기대되었었다구여~~

사실 한국와서 새로운 직장뿐아니라 모든 환경이 낯설다까진 아닌데 여행온 기분이랄까 신나고 재밌어요

너무 새롭고 좋아여

회식은 고기를 구워먹었구여 1차에선 진로를 마셨슴당~~

물론~ 안드시는 분들은 절대 권유xx였구요

다른 분들은 어떤 직장에서 어떤 일을 했는지도 듣고 어떤 회사는 어떤 분위기인지 이런저런 얘기 많이 들었어요

세무부분도 추천해주신다구~~!! 정말 감사하게도ㅎㅎㅎ

최근에 프로젝트가 적어서 조금 걱정도 있지만

그래도 어찌저찌 다들 플젝 들어오신거 아니겠습니까

저는 업무시작하고 새로운 라이브러리 사용이라던가 낯설지만 공부하며 기한을 지키며 하구 있습니다!

헿ㅎㅎ

아직 부족하다고는 느끼지만 조금 적응되서 여유만만이구여

코드도 아직 손을 좀 더 봐야겠지만요

그치만 전 이런 현장이 넘 좋습니다

지난 회사가 넘 힘겨웠나봐여 그 때의 고통이 지금의 행복을 가져다 준 것 같기도하고

역시 불행은 상대적으로 작은 것에도 감사하게 만들어주나 봅니다

호호홓 

담주는 정처기 실기가 있는데욥 사실 현장적응,야근쓰로 인해 공부를 많이 못해서,,,

사알짝 기대는 안하는데 막판 열심히 해봐야져~~!!

담앤 정처기 실기 후기로 돌아올게욥

다들 화잇팅~~!!

728x90
728x90
반응형

React Query는 서버 상태 관리를 쉽게 할 수 있도록 도와주는 강력한 라이브러리입니다. 이 블로그에서는 React Query를 사용한 조회 기능과 저장 기능을 중심으로 사용법을 설명하겠습니다.

React Query 설정

먼저 React Query를 설치해야 합니다. 다음 명령어를 사용하여 설치할 수 있습니다:

npm install react-query

설치 후, QueryClient와 QueryClientProvider를 설정해야 합니다. 애플리케이션의 루트 컴포넌트에서 이를 설정합니다.

import React from 'react';
import { QueryClient, QueryClientProvider } from 'react-query';

const queryClient = new QueryClient();

const App = () => (
  <QueryClientProvider client={queryClient}>
    {/* 나머지 애플리케이션 컴포넌트 */}
  </QueryClientProvider>
);

export default App;

이제 React Query를 사용할 준비가 되었습니다.

조회 기능 구현

React Query의 useQuery 훅을 사용하여 데이터를 조회할 수 있습니다. 아래는 예시 코드입니다:

import React, { useEffect, useRef, useState } from 'react';
import { useQuery } from 'react-query';

// 데이터 조회 함수
const fetchData = async (params) => {
  const response = await fetch(`https://api.example.com/data?${new URLSearchParams(params)}`);
  if (!response.ok) {
    throw new Error('네트워크 응답에 문제가 있습니다');
  }
  return response.json();
};

const DataView = () => {
  const [searchParams, setSearchParams] = useState({});
  const gridRef = useRef();

  // useQuery 훅을 사용하여 데이터를 조회합니다
  const { data, refetch, isFetching } = useQuery(
    ['fetchData', searchParams],
    () => fetchData(searchParams),
    { enabled: false } // 초기에는 비활성화
  );

  // 검색 버튼 클릭 시 호출되는 함수
  const handleSearch = () => {
    const params = {
      key1: 'value1',
      key2: 'value2',
    };
    setSearchParams(params);
    refetch();
  };

  // 검색 파라미터가 변경될 때마다 데이터를 재조회합니다
  useEffect(() => {
    if (Object.keys(searchParams).length > 0) {
      refetch();
    }
  }, [searchParams, refetch]);

  return (
    <div>
      <button onClick={handleSearch}>검색</button>
      {isFetching ? (
        <p>로딩 중...</p>
      ) : (
        <div ref={gridRef}>
          {/* 데이터를 그리드에 표시합니다 */}
          {data?.map((item, index) => (
            <div key={index}>{item.name}</div>
          ))}
        </div>
      )}
    </div>
  );
};

export default DataView;
  • fetchData 함수: 서버로부터 데이터를 가져오는 함수입니다.
  • useQuery 훅: React Query를 사용하여 데이터를 조회합니다.
  • handleSearch 함수: 검색 버튼 클릭 시 호출되어 검색 파라미터를 설정하고 데이터를 재조회합니다.
  • useEffect 훅: 검색 파라미터가 변경될 때마다 데이터를 재조회합니다.

:

728x90
728x90
반응형

안녕하세용

출퇴근길

지인중에 블로그에 일상 포스팅을 몇 번 봤는데

나름 재밌라구요 저도 이제 소소하게 남겨보려구요

저는 7월부터 상암 DMC*디지털미디어시티로 출퇴근하고 있습니다.

한 6년가까이 일본에서 개발자로 근무하고

한국에 들어와서 이런저런일들 많았어요~

출퇴근은 한시간 반정도 걸리는데 서울구경도 하고 앉아서 출퇴근하는 걸로 위안을 느끼며 적응중이랍니다.

근황은 현재 SI로 일하고 있고 정보처리기사 실기를 준비하고 있고

직장에서 하는 업무는 Java,SpringBoot,React.js를 쓰며 키오스크 개발을 하고있습니다.

드라마에서만 본 덕수궁 돌담길과 청계천 연등축제

여름은 너무 더운데 장마는 올듯 말듯 밀당중이고

오랜만에 그리웠던 친구들을 만나고

외롭고 그립다고 느끼면서도 밖에 나가는게 힘들고 지치는 알 수 없는 하루하루가 지나가고 있습니다.

20대 초반 내 인생의 가장 큰 고민은 진로였고 대학졸업이 목표였으며

중반부터 직장인이 되어 나라는 사람을 먹여 살리는데에 자부심을 느끼며 살았던 거 같은데

30에 들어서니 1인분의 몫을 제대로 해내지 못하면 안되는 경력직의 근로자가 되었네요

근데 아직 대체 왜 사는지는 물음표를 갖고 살고 그 답은 아직 모르겠어요

평생 친구라고 여겼던 친구도 떠나가 씁쓸함과 그럼에도 웃고 떠들면서

잘 지내는 스스로가 사실 인연이나 관계란 부질없다고도 느껴집니다.

행복을 만끽하며 살진 못해도 소소하게 행복이 지나가고 있다고는 생각해요.

삶이 고통의 연속이라는 어떤 철학자의 말에 크게 공감하면서

끝나지 않을 고통이 괴롭지만은 않다는건 살만하다는 반증일테니까요

지난 6년의 일본생활에서 소소하게 혼자 퇴근길에 장봐오고 매주 테니스를 치고

만원전철에 얼굴이 찌부되던 날들 자전거를 타고 역을 오갔던 기억들 모든게 그리워집니다.

전 지금 6시 기상부터 출근길 내내 무거운 눈꺼풀을 간신히 올리며

회사에 도착하면 아이스라테를 텀블러에 담아 녹지않는 얼음들에 감사하며

카페인의 힘으로 평일을 보내고 있습니다.

막중한 업무량이 두려우면서도 지루하기 보다는 정신없음으로 견디는게

시간이 잘가는 행복이겠죠~

담엔 재밌는 에피소드 가져올게용

 

한강공원

  모두모두 행복하자구여~

728x90
728x90
반응형

디자인패턴이란?

특정 문맥에서 공통적으로 발생하는 문제에 대해

재사용 가능한 해결책

목적별로 일정한 패턴이 제시되어 있음

완전한 정답이 되는 알고리즘과 달리 현재 상황에 맞춰 최적화된 패턴을 결정하여 사용하는 것이 좋음

Gof Gang of Four 에서 제시한 총 23개의 패턴이 있음

 

디자인 패턴의 장점

개발자 간의 원활한 협업이 가능

소프트웨어의 구조를 파악하기 용이

재사용을 통해 개발 시간 단축-메소드나 클래스를 정형화함

설계 변경이 있을 경우 비교적 원활하게 조치가 가능

 

디자인 패턴의 단점

객체지향적 설계를 고려하여 진행해야함

초기 투자 비용이 많이 들어감 돈,시간 등,,,

 

디자인패턴

목적에 따른 분류

생성패턴, 구조패턴, 행동 패턴 세가지로 구분

 

  • 생성패턴;

생성 패턴은 객체의 생성과 관련된 패턴

특정 객체가 생성되거나 변경되어도 프로그램 구조에 영향을 최소화 할 수 있도록 유연성 제공

Abstract Factory 구체적인 클래스를 지정하지 않고 인터페이스를 통해 연관되는 객체들을 묶어줌
Builder 객체의 생성과 표현을 분리하여 객체를 생성
Factory Method 객체 생성을 서브클래스로 분리하여 위임 캡슐화
ProtoType 원본 객체를 복사하여 객체를 생성 클론
Singleton 한 클래스마다 인스턴스를 하나만 생성하여 어디서든 참조
  • 구조패턴;

프로그램 내 자료 구조나 인터페이스 구조 등 프로그램 구조를 설계하는데 사용되는 패턴

클래스나 객체를 조합하여 더 큰 구조를 만들 수 있게 해줌

Adaptor 비호환 인터페이스에 호환성 부여하도록 변환
Bridge 구현부에서 추상층을 분리 후 각자 독립적으로 변형/확장 가능
Composite  트리 구조로 부분/전체 계층 표현, 복합/단일 객체를 구분없이 사용
Decorator  속 사용없이 객체 간 결합을 통해 객체 기능을 동적으로 추가/확장
Façade  상위에 인터페이스 구성하여 서브클래스의 기능을 복잡하게 표현하 지 않고 단순한 인터페이스로 구현 
Flyweight  인스턴스를 공유하여 메모리 절약 (클래스 경량화)
Proxy  접근이 힘든 객체를 연결하는 인터페이스 역할 (대리 객체 수행)

 

  • 행위패턴;

반복적으로 사용되는 객체들의 커뮤니케이션을 패턴화

결합도 최소화가 주 목적

객체 사이의 알고리즘 또는 책임을 분배하는 방법에 대해 정의됨

Chain of Responsibility  처리가능한 객체가 둘 이상 존재하여 한 객체 내 처리 불가 시 다음 객체로 이관
Command  요청 명령어들을 추상/구체 클래스로 분리 후 단순화/캡슐화
Interpreter  언어에 문법 표현 정의
Iterator  접근이 빈번한 객체에 대해 동일 인터페이스 사용
Mediator  객체들간 복잡한 상호작용을 캡슐화하여 객체로 정의 후 중재 
Memento  객체를 이전의 특정 시점의 상태로 저장하고 복원 (캡슐화 유지)
Observer  한 객체 상태 변화 시 상속되어 있는 객체들에 변화 전달
State  객체의 상태에 따라 동일한 동작을 다르게 처리 
Strategy  동일 계열 알고리즘을 개별적으로 캡슐화하여 상호 교환
Template Method  여러 클래스에서 공통 사용 메서드를 상위 클래스에서 정의하고, 하위 클래스마다 다르게 구현해야하는 세부 사항를 개별 구현
Visitor   각 클래스 데이터 구조로부터 처리/연산 기능을 분리하여 별도의 클래스를 만들고, 해당 클래스 메서드가 각 클래스를 돌아다니며 특정 작업을 수행 → 객체 구조 변경 X / 새로운 연산 기능만 추가

 

각자 상황에 맞게 패턴을 적용

뭐가 더 중요한진 애매한 부분이 있음

디자인 패턴이란 필요할 때 마다 찾아보는 식으로 설계할 때 간략하게 아는걸 detail화 시켜서 코드를 작성

728x90
728x90
반응형

웹뷰에서는 브라우저와 다르게 newtab으로 페이지를 열경우 oncreate외에 코드를 추가해 주어야한다.

webChromeClient를 이용해서 새로운 창을 여는 웹뷰설정과 그안에서의 다운로드 처리도 별도로 진행된다.

 

public class MyWebChromeClient extends WebChromeClient {
 //신규탭으로 열었을때
        @Override
        public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
            WebView newWebView = new WebView(view.getContext());
            WebSettings settings = newWebView.getSettings();
            //ウェブページのjavascript使用許可
            settings.setJavaScriptEnabled(true);
            //新しくタブを使用許可
            settings.setSupportMultipleWindows(true);
            //画面zoom許可
            settings.setSupportZoom(true);
            //画面拡大縮小許可
            settings.setBuiltInZoomControls(true);
            //拡大ショートカット許可しない
            settings.setDisplayZoomControls(false);
            //GPS許可
            settings.setGeolocationEnabled(true);
            //DBアクセス許可
            settings.setDatabaseEnabled(true);
            //フォームデータを保存しない
            settings.setSaveFormData(false);
            final Dialog dialog = new Dialog(view.getContext(), R.style.Theme_DialogPage);
            dialog.setContentView(newWebView);

            //다운로드처리
            newWebView.setDownloadListener(new DownloadListener() {

                @Override
                public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) {
                    try {
                        String mFileName;
                        DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
                        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));

                        request.setMimeType(mimeType);
                        String cookies = CookieManager.getInstance().getCookie(url);

                        request.addRequestHeader("cookie", cookies);
                        request.addRequestHeader("User-Agent", userAgent);
                        request.addRequestHeader("Referer", getReferrer().getAuthority());
                        request.addRequestHeader("Content-Disposition", contentDisposition);
                        request.setDescription("Downloading file...");
                        String content = contentDisposition;
                        //アンドロイド側で読み込めるようにファイル名を編集
                        mFileName = content.replace("attachment; filename=", "").trim();
                        //WEBでエンコードされたファイル名をDecodeする
                        mFileName = URLDecoder.decode(mFileName, String.valueOf(UTF_8));
                        request.setTitle(mFileName);
                        //このダウンロードの実行中または完了時に、ダウンロード マネージャーによってシステム通知が投稿されるかどうかを制御します。
                        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
                        //다운로드폴더설정 path=\내부스토리지\Android\data\어플이름\cache
                        request.setDestinationUri(Uri.fromFile(new File(getExternalCacheDir().getAbsolutePath(), mFileName)));
                        //このダウンロードを実行できるネットワークの種類を制限します。
                        request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
                        //ダウンロード マネージャーがダウンロードを実行する準備ができ、接続が利用可能になると、ダウンロードは自動的に開始されます。
                        dm.enqueue(request);

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            newWebView.clearCache(true);
            dialog.show();
            //뒤로가기 키 설정
            dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
                @Override
                public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
                    if (keyCode == KeyEvent.KEYCODE_BACK) {
                        //다이얼로그 닫기
                        dialog.dismiss();
                    }
                    return true;
                }
            });

            newWebView.setWebViewClient(new MyWebViewClient());
            newWebView.setWebChromeClient(new MyWebChromeClient());

            //スレッド境界を越えて WebView を返すためのトランスポート オブジェクト。
            WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
            transport.setWebView(newWebView);
            resultMsg.sendToTarget();
            return true;
        }
        
  }
728x90

+ Recent posts