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
,

springboot 버전정보를 api로 제공하는 2가지 방법.

 

1. 1 build.gradle에서 processResources 태스크를 사용하여 애플리케이션 속성 파일에 버전 정보를 주입

 

// build.gradle
processResources {
    filesMatching('application.properties') {
        expand(project: project)
    }
}

 

1.2 

# application.properties
app.version=${version}

# application.yml
app:
  version: ${version}

 

1.3

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class VersionController {

    @Value("${app.version}")
    private String appVersion;

    @GetMapping("/api/version")
    public String getVersion() {
        return appVersion;
    }
}

 

 

2. @Autowired BuildProperties buildProperties;를 사용하여 애플리케이션의 빌드 정보에 접근하는 것은 Spring Boot 2.0 이상에서 가능합니다. BuildProperties는 Spring Boot의 org.springframework.boot.info 패키지에 포함된 클래스로, 빌드 시 생성된 META-INF/build-info.properties 파일의 정보를 제공

2.1  build.gradle 1

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    // 기타 의존성들...
}

 

2.2 build.gradle 2

springBoot {
    buildInfo()
}

 

2.3 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.info.BuildProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class VersionController {

    @Autowired
    private BuildProperties buildProperties;

    @GetMapping("/api/version")
    public String getVersion() {
        return buildProperties.getVersion();
    }
}

 

 

클라이언트에서 사용 방법.

const AppVersionChecker = () => {
  const [clientVersion, setClientVersion] = useState('1.0.0'); // 클라이언트 버전-> localStorage에 기록해 놓으면 좋을듯.

  useEffect(() => {
    const checkVersion = async () => {
      try {
        // 서버에서 현재 버전을 가져옵니다.
        const response = await axios.get('https://yourserver.com/api/version');
        const serverVersion = response.data.version;

        // 서버 버전과 클라이언트 버전이 다르면 페이지를 새로고침합니다.
        if (serverVersion !== clientVersion) {
          window.location.reload();
        }
      } catch (error) {
        console.error('버전 확인 중 오류 발생:', error);
      }
    };
Posted by yongary
,

react에서 drag& drop

React.js 2024. 1. 4. 15:44

antd의 Dragger를 custom하게 바꾸는 예제. (목록을 우측으로 )

 

https://stackoverflow.com/questions/58469157/antd-how-to-display-fileuploadlist-to-the-right-of-dragger-upload 

 

 

 

 

 

 

 

 

 

<Input type={'file'} 과 함께 사용하는 drag&drop 예제.

 

import React, { useState } from 'react';

function FileUpload() {
  const [files, setFiles] = useState([]);

  const handleDrop = (e) => {
    e.preventDefault();
    const droppedFiles = [...e.dataTransfer.files];
    setFiles(droppedFiles);
  };

  const handleFileInputChange = (e) => {
    const selectedFiles = [...e.target.files];
    setFiles(selectedFiles);
  };

  const handleUpload = () => {
    // 업로드할 파일들을 처리하는 로직을 작성하세요.
    console.log(files);
  };

  return (
    <div
      onDrop={handleDrop}
      onDragOver={(e) => e.preventDefault()}
      style={{
        border: '2px dashed #ccc',
        padding: '20px',
        textAlign: 'center',
        cursor: 'pointer',
      }}
    >
      <input
        type="file"
        style={{ display: 'none' }}
        multiple
        onChange={handleFileInputChange}
        id="fileInput"
      />
      <label htmlFor="fileInput">
        드래그 앤 드롭 또는 클릭하여 파일을 업로드하세요.
      </label>
      <button onClick={handleUpload}>업로드</button>
      {files.map((file, index) => (
        <div key={index}>{file.name}</div>
      ))}
    </div>
  );
}

export default FileUpload;
Posted by yongary
,

 

Spring Security를 이용해 로그인한 관리자에게만 images를 노출할 수 있습니다.


### build.gradle
implementation 'org.springframework.boot:spring-boot-starter-security'


### 설정
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/images/**").hasRole("ADMIN") // 이미지 리소스에 대한 접근 권한 설정
                .anyRequest().permitAll() // 다른 요청은 모두 허용
                .and()
            .httpBasic(); // 기본 인증 사용
    }
}

### application.properties
spring.security.user.name=admin
spring.security.user.password=adminpassword
spring.security.user.roles=ADMIN

### WebConfig
registry.addResourceHandler("/images/**")
        .addResourceLocations("file://" + System.getProperty("user.dir") + "/my-images/");
 
 
 
### 참고 : 로그인 구현방식 중 1가지 예제:단,  {noop}는 암호화없는 평문이라는걸 알리는 키워드입니다.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/images/**").hasRole("ADMIN")
                .anyRequest().authenticated() // 모든 요청에 대해 인증을 필요로 함
                .and()
            .formLogin(); // 폼 기반 로그인 사용
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser("admin").password("{noop}adminpassword").roles("ADMIN");
    }
}
Posted by yongary
,

 

front에서 직접 이미지를 올리더라도, S3의 bucket URL 및 키를 모두 front에서 관리하는 것은 보안상 위험하므로
Backend에서 pre-signed URL을 프론트로 주면, front에서 그 URL을 이용해서 올리는 방식이 권장된다.
pre-signed URL은 설정한 시간(예: 1시간) 동안 유효하다.

java 백엔드에서 pre-signed URL을 생성하고, front에서 react를 이용해서 파일을 올리는 예제는 다음과 같다.

 



백엔드:  AWS-SDK이용.

import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.net.URL;
import java.util.Date;

@Service
public class S3Service {
    private final AmazonS3 s3Client;
    private final String bucketName;

    public S3Service(
            @Value("${aws.accessKeyId}") String accessKeyId,
            @Value("${aws.secretKey}") String secretKey,
            @Value("${aws.region}") String region,
            @Value("${aws.s3.bucketName}") String bucketName) {
        this.bucketName = bucketName;
        this.s3Client = AmazonS3ClientBuilder
                .standard()
                .withRegion(region)
                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKeyId, secretKey)))
                .build();
    }

    public URL generatePresignedUrl(String objectKey, HttpMethod httpMethod, long expirationInSeconds) {
        Date expirationDate = new Date(System.currentTimeMillis() + (expirationInSeconds * 1000));
        GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, objectKey)
                .withMethod(httpMethod)
                .withExpiration(expirationDate);

        return s3Client.generatePresignedUrl(generatePresignedUrlRequest);
    }
}

 

 

FrontEnd:

import React, { useState } from 'react';
import axios from 'axios';

function FileUpload() {
  const [selectedFile, setSelectedFile] = useState(null);

  const handleFileChange = (event) => {
    setSelectedFile(event.target.files[0]);
  };

  const handleUpload = async () => {
    if (!selectedFile) {
      console.log('Please select a file.');
      return;
    }

    try {
      // Replace with your backend API endpoint that generates the Pre-signed URL
      const response = await axios.get('http://your-backend-api/generatePresignedUrl', {
        params: {
          objectKey: 'example.jpg', // 업로드할 객체의 키
          httpMethod: 'PUT', // 업로드할 때 사용할 HTTP 메서드 (PUT 또는 POST)
          expirationInSeconds: 3600, // URL의 유효 기간 (1시간)
        },
      });

      const { url } = response.data;

      // Use the obtained Pre-signed URL to upload the file directly to S3
      await axios.put(url, selectedFile, {
        headers: {
          'Content-Type': selectedFile.type,
        },
      });

      console.log('File uploaded successfully.');
    } catch (error) {
      console.error('Error uploading file:', error);
    }
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      <button onClick={handleUpload}>Upload</button>
    </div>
  );
}

export default FileUpload;
Posted by yongary
,

selector 예제:

 - atom이 변하면 자동으로 따라서 변하는 동적인  atom 제공.

/// atom 예제
import { atom } from 'recoil';

export const userState = atom({
  key: 'userState', // 고유한 키
  default: {
    id: 1,
    name: 'John Doe',
    email: 'johndoe@example.com'
  },
});


/// selector 예제 => atom이 변하면 자동으로 따라서 변하며, 동적인 atom느낌을 제공.

import { selector } from 'recoil';
import { userState } from './userAtom'; // 위에서 정의한 atom을 가져옵니다.

export const userNameUpperState = selector({
  key: 'userNameUpperState', // 고유한 키
  get: ({get}) => {
    const user = get(userState);
    return user.name.toUpperCase();
  },
});

 

 

effects 예제

-  onSet : 바뀔때마다 호출.

- setSelf:  초기값 설정에 사용.  default:와 비슷하지만 비동기도 지원하고, 동적인 조건식 제공가능하므로 좀 더 권장. 

import { atom } from 'recoil';

export const userState = atom({
  key: 'userState',
  default: {
    id: 1,
    name: 'John Doe',
    email: 'johndoe@example.com'
  },
  effects: [
    ({ onSet }) => {
      onSet(newValue => {
        console.log('userState has changed:', newValue);
      });
    }
  ]
});

 

Posted by yongary
,

git 복사 (미러링)

기타 2023. 11. 18. 22:59

Git 저장소의 히스토리를 포함하여 복사해서 다른 저장소로 옮기는 방법

 

1. 기존 저장소를 --mirror로 받는다.
$git clone --mirror <기존_저장소_URL>

 

2. 새 저장소를 만들고 그 URL을 얻은 다음..

 

3. $cd 기존_저장소_URL
    $git push --mirror <새_저장소_URL> 

 

 

Posted by yongary
,