1. Apps Script 컨솔 왼쪽 메뉴에서 "서비스 +" 를 눌러 Drive API 를 추가한다.

 

2. 코드를 아래와 같이 작성하고

3. 배포 -> 새 배포 를 한 후

4. 실행을 누른다.

function convertDocxToPdf() {
  var folderId = 'myFolderID-folder의 URL 뒤쪽 끝부분임.'; // 변환할 파일이 들어 있는 폴더 ID
  var folder = DriveApp.getFolderById(folderId);
  var files = folder.getFilesByType(MimeType.MICROSOFT_WORD);

  while (files.hasNext()) {
    var file = files.next();
    Logger.log('File name:' + file.getName());

    //1. Microsoft Word 파일을 Google Docs로 변환
    var resource = {
      title: file.getName(),
      mimeType: MimeType.GOOGLE_DOCS,
      parents: [{id: folderId}]
    };

    var convertedDocFile = Drive.Files.copy(resource, file.getId());

    //2. Google Docs 형식으로 문서를 연 후 PDF로 변환
    var doc = DocumentApp.openById(convertedDocFile.getId());
    var blob = doc.getBlob().getAs('application/pdf');

    // 특정 폴더에 PDF 파일 저장
    var folder = DriveApp.getFolderById(folderId);
    var pdfFile = folder.createFile(blob.setName(convertedDocFile.getName().replace(/\.[^/.]+$/, "") + ".pdf"));

    // 생성된 PDF 파일의 URL 반환
    return pdfFile.getUrl();
  }
}

 

 

원격호출이 가능한 appScript만들기 => doGet(e)과 filename 파라미터를 추가.

 

function convertDoc2PdfByFilename(filename) {
  var folderId = 'myFOlderID'; // 변환할 파일이 들어 있는 폴더 ID
  var folder = DriveApp.getFolderById(folderId);
  // var files = folder.getFilesByType(MimeType.MICROSOFT_WORD);
  var files = folder.getFilesByName(filename);

  while (files.hasNext()) {
    var file = files.next();
    Logger.log('File name:' + file.getName());

    //1. Microsoft Word 파일을 Google Docs로 변환
    var resource = {
      title: file.getName(),
      mimeType: MimeType.GOOGLE_DOCS,
      parents: [{id: folderId}]
    };

    var convertedDocFile = Drive.Files.copy(resource, file.getId());

    //2. Google Docs 형식으로 문서를 연 후 PDF로 변환
    var doc = DocumentApp.openById(convertedDocFile.getId());
    var blob = doc.getBlob().getAs('application/pdf');

    // 특정 폴더에 PDF 파일 저장
    var folder = DriveApp.getFolderById(folderId);
    var pdfFile = folder.createFile(blob.setName(convertedDocFile.getName().replace(/\.[^/.]+$/, "") + ".pdf"));

    // 생성된 PDF 파일의 URL 반환
    return pdfFile.getUrl();
  }
}

function doGet(e) {
  var filename = e.parameter.filename;
  if (!filename) {
    return ContentService.createTextOutput("Filename parameter missing")
      .setMimeType(ContentService.MimeType.TEXT);
  }

  // 파일 이름을 사용하여 DOCX를 PDF로 변환하고, 생성된 PDF의 URL을 반환
  var pdfUrl = convertDoc2PdfByFilename(filename);
  if (pdfUrl) {
    return ContentService.createTextOutput("PDF 생성됨: " + pdfUrl)
      .setMimeType(ContentService.MimeType.TEXT);
  } else {
    return ContentService.createTextOutput("해당 이름의 파일을 찾을 수 없습니다.")
      .setMimeType(ContentService.MimeType.TEXT);
  }
}
Posted by yongary
,

springboot에서 pdf로 변환하다가 어려움을 겪는다면, 시도해 볼만한 방법 중하나로
구글드라이브와 구글의 appScript를 이용하는 방법이다.

 

Spring Boot 애플리케이션에서 Google 계정에 로그인하여 Google Apps Script를 실행하고, 결과로 생성된 PDF를 가져오는 것은 가능합니다. 이 과정은 크게 세 부분으로 나눌 수 있습니다: Google 계정 인증, Google Apps Script 실행, 생성된 PDF 파일 가져오기. 이 과정은 Google Cloud Platform(GCP)의 여러 서비스와 API를 사용하며, OAuth 2.0을 통한 인증이 필요합니다.

1. Google 계정 인증 (OAuth 2.0 사용)

  1. Google Cloud Console에서 새 프로젝트를 생성합니다.
  2. API 및 서비스 대시보드로 이동하여 OAuth 동의 화면을 구성합니다.
  3. 사용자 인증 정보를 클릭하여 새 OAuth 클라이언트 ID를 생성합니다. 애플리케이션 유형으로 웹 애플리케이션을 선택합니다.
  4. 승인된 리디렉션 URI로 애플리케이션의 리디렉션 엔드포인트를 추가합니다. 예를 들어, http://localhost:8080/oauth2/callback과 같습니다.
  5. 클라이언트 ID와 클라이언트 비밀을 안전하게 저장합니다.

2. Google Apps Script 실행

  1. Google Apps Script를 사용하여 PDF를 생성하고 Google Drive에 저장하는 스크립트를 작성합니다.
  2. Apps Script 프로젝트에서 공유 및 배포 > 배포를 위한 앱 만들기 > API 실행 가능을 활성화하고, 생성된 스크립트 ID를 저장합니다.

3. Spring Boot에서 Google API 사용

  1. Spring Boot 프로젝트에 google-api-client, google-oauth-client, google-api-services-script 라이브러리를 의존성에 추가합니다.
implementation 'com.google.api-client:google-api-client:1.30.9'
implementation 'com.google.oauth-client:google-oauth-client-jetty:1.30.6'
implementation 'com.google.apis:google-api-services-script:v1-rev20200827-1.30.10'
gradleCopy code
implementation 'com.google.api-client:google-api-client:1.30.9' implementation 'com.google.oauth-client:google-oauth-client-jetty:1.30.6' implementation 'com.google.apis:google-api-services-script:v1-rev20200827-1.30.10'
  1. 사용자를 Google로 리디렉션하여 인증하고, Google로부터 리디렉션된 요청을 처리하는 컨트롤러를 구현합니다. 이 과정에서 GoogleAuthorizationCodeFlow를 사용하여 액세스 토큰을 얻습니다.
  2. 얻은 액세스 토큰을 사용하여 Apps Script API를 호출하고, 스크립트를 실행하여 PDF를 생성합니다.

4. 생성된 PDF 가져오기

  1. Google Drive API를 사용하여 Apps Script가 생성한 PDF 파일을 검색하고 다운로드합니다.
  2. 파일의 ID나 이름을 알고 있다면, Files.get 메서드로 파일 메타데이터를 가져온 후 GoogleCredential을 사용하여 인증된 Drive 서비스 객체로 파일을 다운로드할 수 있습니다.
Posted by yongary
,

springFramework의 BeanUtils말고 apache의 BeanUtils를 상속해서 NullAwareBeanUtils를 만들면 된다.

import org.apache.commons.beanutils.BeanUtilsBean;

public class NullAwareBeanUtilsBean extends BeanUtilsBean {

    @Override
    public void copyProperty(Object dest, String name, Object value) throws IllegalAccessException, InvocationTargetException {
        if(value == null) return; // null 값이면 복사를 수행하지 않음
        super.copyProperty(dest, name, value);
    }
}

사용법:
NullAwareBeanUtilsBean nullAwareBeanUtils = new NullAwareBeanUtilsBean();
nullAwareBeanUtils.copyProperties(destObject, sourceObject);
Posted by yongary
,

selenium은 웹 내부만 자동화 해주는데 웹에서 뭘 진행하다 보면 exe파일로 앱이 떠야하는 경우가 있는 사이트의 경우 앱 클릭까지 자동화하는 방법:

 

웹 애플리케이션 테스트를 위해 Selenium을 사용하는 경우, Selenium은 브라우저 내에서 동작하는 웹 페이지의 자동화에 최적화되어 있습니다. 그러나 웹 페이지에서 특정 동작을 통해 외부 애플리케이션(예: .exe 파일)을 실행해야 하는 경우, Selenium만으로는 이를 직접 제어할 수 없습니다.

웹에서 실행되는 애플리케이션(예: 파일 다운로드 후 실행 등)을 자동화하고자 한다면, 다음과 같은 방법을 고려할 수 있습니다:

1. 키보드 및 마우스 제어를 위한 도구 사용

  • AutoIt 또는 Sikuli: 이러한 도구들은 시스템 수준에서의 GUI 자동화를 가능하게 해줍니다. 예를 들어, 파일 다운로드 대화 상자의 조작, .exe 파일 실행, 애플리케이션 내의 특정 버튼 클릭 등의 작업을 스크립트를 통해 자동화할 수 있습니다.
  • 이 도구들은 스크립트로 작성된 시나리오에 따라 키보드 입력, 마우스 클릭 및 이동을 시뮬레이션할 수 있으며, 화면 상의 이미지를 기반으로 특정 요소를 인식하고 조작할 수 있는 기능도 제공합니다.
  • Selenium과 함께 사용하여, Selenium이 웹 페이지 내의 자동화를 담당하고 AutoIt 또는 Sikuli가 외부 애플리케이션의 자동화를 담당하도록 할 수 있습니다.

2. Selenium과 외부 도구의 통합

  • 웹 자동화 과정에서 특정 단계에서 외부 애플리케이션을 실행해야 한다면, Selenium 스크립트 내에서 AutoIt 또는 Sikuli 스크립트를 호출하여 실행할 수 있습니다.
  • 예를 들어, Python을 사용하는 경우, subprocess 모듈을 사용하여 AutoIt 스크립트를 실행할 수 있습니다.

예제: Python에서 AutoIt 스크립트 실행

 
import subprocess

# AutoIt 스크립트 실행
subprocess.run(['path/to/autoit/script.exe'])

 

예제:  AutoIt 스크립트

; 메모장을 실행합니다.
Run("notepad.exe")

; 메모장의 창이 활성화될 때까지 기다립니다.
WinWaitActive("제목 없음 - 메모장")

; 텍스트를 입력합니다.
Send("Hello, World!{ENTER}")
Send("이것은 AutoIt 스크립트에 의해 자동으로 입력된 텍스트입니다.")



; 사용자에게 경고창을 표시합니다.
MsgBox(0, "경고", "이것은 AutoIt 스크립트입니다!")



; 특정 응용 프로그램 실행
Run("your_application.exe")

; 응용 프로그램 창이 나타날 때까지 기다립니다.
WinWaitActive("응용 프로그램 제목")

; 'OK' 버튼을 클릭합니다.
ControlClick("응용 프로그램 제목", "", "[CLASS:Button; TEXT:OK]")

기본방법 

  1. AutoIt을 설치합니다.
  2. 파일을 .au3 확장자로 저장합니다.
  3. 저장한 스크립트 파일을 더블 클릭하거나, AutoIt 스크립트 편집기에서 직접 실행하여 테스트합니다.

AutoIt 스크립트는 GUI 기반의 애플리케이션 자동화에 매우 유용하며, 복잡한 작업을 간단한 스크립트로 자동화할 수 있습니다. 스크립트는 상황에 맞게 수정하여 사용해야 합니다. 예를 들어, "응용 프로그램 제목"이나 "your_application.exe"는 실제 사용하는 응용 프로그램의 이름이나 실행 파일 경로로 변경해야 합니다.

 

오토잇 설치 가이드: https://mypcinfo.tistory.com/23  (2019년 글) 

Selenium+오토잇:   https://testmanager.tistory.com/145  (2018년 글)

 

 

 

Python에서 Selenium과 Sikuli를 함께 사용하여 GUI 자동화 작업을 수행하는 예제.

 

Sikuli의 기능을 Python 스크립트 내에서 직접 호출하기 위해서는 Sikuli의 명령줄(Command Line) 인터페이스를 사용하거나 SikuliX API (Java 기반)를 Python과 연동하는 방법이 있습니다. 여기서는 가장 간단한 방법 중 하나인 Sikuli Script를 외부 프로세스로 실행하는 방법을 사용합니다.

이 예제에서는 Selenium을 사용하여 웹 브라우저를 자동으로 제어하고, 특정 조건에서 Sikuli 스크립트를 호출하여 브라우저 외부의 GUI 요소를 제어하는 상황을 가정합니다. 예를 들어, Selenium으로 파일 다운로드 버튼을 클릭한 후, Sikuli를 사용하여 나타나는 시스템 파일 다이얼로그를 제어할 수 있습니다.

준비사항

  1. Python, Selenium, SikuliX가 설치되어 있어야 합니다.
  2. SikuliX IDE를 사용하여 Sikuli 스크립트를 작성하고 저장합니다. 예를 들어, saveFile.sikuli이라는 이름으로 저장합니다.

Sikuli 스크립트 예제

  • saveFile.sikuli 스크립트는 다운로드 창에서 "저장" 버튼을 클릭하는 작업을 수행할 수 있습니다.
from selenium import webdriver
import subprocess
import time

# Selenium으로 웹드라이버 초기화 및 웹페이지 열기
driver = webdriver.Chrome()
driver.get('http://example.com')

# 웹 페이지에서 파일 다운로드를 시작하는 동작 수행 (Selenium 사용)
download_button = driver.find_element_by_id('download')
download_button.click()

# 파일 다운로드 다이얼로그 처리를 위해 Sikuli 스크립트 호출 (외부 프로세스 실행)
time.sleep(5)  # 다운로드 다이얼로그가 나타나기를 기다림
sikuli_script_path = 'C:\\path\\to\\saveFile.sikuli'
subprocess.call(['C:\\path\\to\\runsikulix.cmd', '-r', sikuli_script_path])

# 작업 완료 후 브라우저 닫기
driver.quit()

 

이 예제에서는 subprocess.call을 사용하여 Sikuli 스크립트를 외부 프로세스로 실행합니다. SikuliX를 설치한 디렉토리 내의 runsikulix.cmd(Windows의 경우)를 사용하여 Sikuli 스크립트를 실행합니다. Sikuli 스크립트 경로와 SikuliX 실행 파일 경로는 실제 환경에 맞게 수정해야 합니다.

주의사항

  • Sikuli 스크립트를 실행하기 전에, 필요한 경우 시스템 다이얼로그가 화면에 완전히 나타날 때까지 충분한 대기 시간을 제공해야 합니다.
  • Sikuli 스크립트의 이미지 인식은 실행 환경의 해상도, 디스플레이 설정, UI 테마 등에 영향을 받을 수 있으므로, 환경에 따라 인식률이 달라질 수 있습니다.

 

Posted by yongary
,

 

  • 설치:  pip install requests
  • 예제: 
import requests

# GET 요청 보내기
def send_get_request(url):
    try:
        response = requests.get(url)
        if response.status_code == 200:
            # 요청이 성공하면 JSON 형식으로 반환된 데이터를 파싱하여 반환합니다.
            return response.json()
        else:
            print(f"Failed to send GET request. Status code: {response.status_code}")
            return None
    except Exception as e:
        print(f"An error occurred: {e}")
        return None

# POST 요청 보내기
def send_post_request(url, data):
    try:
        response = requests.post(url, json=data)
        if response.status_code == 200:
            # 요청이 성공하면 JSON 형식으로 반환된 데이터를 파싱하여 반환합니다.
            return response.json()
        else:
            print(f"Failed to send POST request. Status code: {response.status_code}")
            return None
    except Exception as e:
        print(f"An error occurred: {e}")
        return None

# 예제 URL
url = 'https://jsonplaceholder.typicode.com/posts/1'

# GET 요청 보내기
response_data = send_get_request(url)
if response_data:
    print("GET 요청 결과:")
    print(response_data)

# POST 요청 보내기 (예제 데이터)
post_data = {'title': 'foo', 'body': 'bar', 'userId': 1}
response_data = send_post_request(url, post_data)
if response_data:
    print("POST 요청 결과:")
    print(response_data)

 

물론 flask안에서도 동작한다.

Posted by yongary
,

 

pymongo가 간단.

 

  • 설치 :  pip install pymongo
  • 예제: 
from pymongo import MongoClient

# MongoDB에 연결합니다. 호스트 및 포트는 MongoDB 서버에 맞게 수정해야 합니다.
client = MongoClient('mongodb://localhost:27017/')

# 데이터베이스와 컬렉션을 선택합니다.
db = client['mydatabase']
collection = db['mycollection']

# 예제 데이터를 삽입합니다.
data = {'name': 'John', 'age': 30, 'city': 'New York'}
collection.insert_one(data)

# 컬렉션에서 데이터를 검색합니다.
result = collection.find_one({'name': 'John'})
print(result)
Posted by yongary
,

 

flask를 이용하는 방법이 간단하다. (Django보다는 간단)

  • 아럐 예제는
    - /run_selenium 엔드포인트로 POST 요청이 들어오면 해당 요청에서 URL을 추출하여 Selenium을 사용하여 해당 URL의 웹 페이지를 가져오는 간단한 REST API 서버 구현. 

 

from flask import Flask, request, jsonify
from selenium import webdriver

app = Flask(__name__)

# 요청을 처리하는 엔드포인트를 정의합니다.
@app.route('/run_selenium', methods=['POST'])
def run_selenium():
    # 요청에서 필요한 데이터를 추출합니다.
    url = request.json.get('url')
    
    # Selenium을 실행하여 웹 페이지를 가져옵니다.
    driver = webdriver.Chrome()  # 적절한 드라이버를 선택하여 사용합니다.
    driver.get(url)
    
    # Selenium을 사용하여 추가적인 작업을 수행합니다.
    
    # 결과를 반환합니다.
    return jsonify({'message': 'Selenium 실행 완료'})

if __name__ == '__main__':
    app.run(debug=True)
Posted by yongary
,

아래 URL을 참고하세요.

 

https://velog.io/@kimsehwan96/S3-CORS-%ED%97%A4%EB%8D%94-%EA%B4%80%EB%A0%A8-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95-html2canvas-lottie

Posted by yongary
,

DatePicker 팝업 안에 '날짜 모름'을 추가하려면 DatePicker 컴포넌트를 커스텀하여 팝업 내부에 체크박스를 추가해야 합니다. 이를 위해 react-datepicker의 CustomInput prop을 사용하여 커스텀 인풋을 만들고, 해당 인풋이 클릭되었을 때 팝업이 열리도록 조정할 수 있습니다. 아래는 그 예시입니다.

import React, { useState } from 'react';
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';

const CustomDatePicker = () => {
  const [selectedDate, setSelectedDate] = useState(null);
  const [dateUnknown, setDateUnknown] = useState(false);

  const handleDateChange = (date) => {
    setSelectedDate(date);
    setDateUnknown(false);
  };

  const toggleDateUnknown = () => {
    if (dateUnknown) {
      setSelectedDate(null);
    }
    setDateUnknown(!dateUnknown);
  };

  const CustomInput = React.forwardRef(({ value, onClick }, ref) => (
    <div>
      <input
        type="text"
        value={value}
        onClick={onClick}
        ref={ref}
        readOnly
      />
      <label>
        <input
          type="checkbox"
          checked={dateUnknown}
          onChange={toggleDateUnknown}
        />
        날짜 모름
      </label>
    </div>
  ));

  return (
    <DatePicker
      selected={selectedDate}
      onChange={handleDateChange}
      dateFormat="yyyy-MM-dd"
      placeholderText="날짜 선택"
      customInput={<CustomInput />}
    />
  );
};

export default CustomDatePicker;

 

j이 코드에서 CustomInput 컴포넌트를 만들어 Datepicker의 커스텀 인풋으로 사용하고 있습니다. 이 커스텀 인풋에는 입력 필드와 '날짜 모름'을 나타내는 체크박스가 함께 포함되어 있습니다. 사용자가 팝업을 열면 이 체크박스가 팝업 안에 표시됩니다.

 

 

 

기본사용법

https://jiyumi00.tistory.com/54 

 

[React -5] ReactDatePicker 라이브러리로 기간 설정 구현

회원관리, 판매내역, 거래내역 등 테이블이 있는 페이지에 기간 설정을 추가하려고 한다 ✅ React-DatePicker 라이브러리 설치 npm install react-datepicker https://reactdatepicker.com/ React Datepicker crafted by HackerOn

jiyumi00.tistory.com

 

 

커스텀 수정 예제:

https://velog.io/@oo009pbh/react-datepicker-%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%95%98%EC%97%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

 

react-datepicker 커스텀 하여 사용하기

Date 형식을 다룰때 정말 많이 사용하는 라이브러리중에 하나이다. 주당 설치 횟수가 100만회가 너끈히 넘을 정도.무엇보다 데모사이트가 깔끔하게 되어 있어 기능을 테스트 해보기가 편하다.어

velog.io

 

커스텀 input 예제: 

https://velog.io/@bnb8419/React-Datepicker-%EC%82%AC%EC%9A%A9%EB%B2%95-1znalwg3

 

React Datepicker Custom

react date picker custom 방법

velog.io

 

css 스타일링으로 세로 text출력.

https://hanbbistory.tistory.com/59

Posted by yongary
,
  1. 고차 컴포넌트 (Higher-Order Component, HOC) 사용: GTM 스크립트를 포함한 컴포넌트를 만들고, 이 컴포넌트로 각 페이지나 레이아웃을 감싸서 GTM 스크립트를 한 번만 로드하도록 할 수 있습니다.
  2. 컨텍스트 (Context) 사용: React의 컨텍스트를 활용하여 GTM 스크립트를 한 번만 로드하고, 각 컴포넌트에서 이벤트를 트리거할 수 있습니다.
import React from 'react';

const addGTMScript = () => {
  const script = document.createElement("script");
  script.innerHTML = `
    (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
    j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
    'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
    })(window,document,'script','dataLayer','GTM-XXXXXXX');
  `;
  document.head.appendChild(script);
};

const withGTM = (WrappedComponent) => {
  return class extends React.Component {
    componentDidMount() {
      addGTMScript();
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
};

const LoginButton = () => {
  const handleLoginClick = () => {
    window.dataLayer = window.dataLayer || [];
    window.dataLayer.push({
      event: 'login'
    });
  };

  return (
    <div>
      <button onClick={handleLoginClick}>Login</button>
    </div>
  );
};

const LoginButtonWithGTM = withGTM(LoginButton);

const LoginPage = () => {
  return (
    <div>
      <h1>Login Page</h1>
      <LoginButtonWithGTM />
    </div>
  );
};

export default LoginPage;
Posted by yongary
,