javascript 혹은 react상에서 간단한 템플릿 랭기지인 mustache 를 살펴보면 다음과 같다.

설치: npm install mustache

 

 

예제코드.

import React from "react";
import Mustache from "mustache";

const TemplateRenderer = () => {
  const template = "Hello, {{name}}! Welcome to {{platform}}.";
  const data = {
    name: "John",
    platform: "React Mustache",
  };

  const renderedText = Mustache.render(template, data);

  return <div>{renderedText}</div>;
};

export default TemplateRenderer;
Posted by yongary
,

golang 개요

golang 2025. 1. 21. 09:12

포인터가 존재: c언어와 유사
패키지명/가비지 컬렉션존재: java와 유사 

 

- 함수의 지연된 실행 허용

 

- 동시 프로그래밍을 지원하여 여러 프로세스를 동시에 실행할 수 있음

이는 채널, 고루틴 등을 사용하여 달성됩니다.

Posted by yongary
,


코드 에디터 (아래에 실행영역)이 있는 것 처럼 아래 위 두개의 영역이 있고,
중간을 마우스로 드래그하면서  둘 사이의 비율을 조절하고 싶을때
다음과 같은 방식으로 가능하다. 

동작 원리

  1. 상단 div의 높이 조절:
    • onMouseDown: 마우스 조절 시작을 감지.
    • onMouseMove: 마우스의 Y축 좌표(e.clientY)를 기준으로 height 상태를 업데이트.
    • onMouseUp: 조절 종료.
  2. 하단 div의 크기 계산:
    • 상단 div의 높이를 기준으로 나머지 공간을 자동으로 차지하도록 flex 사용.
  3. 유저 경험:
    • resizer 영역에서 마우스 커서가 변경(row-resize).
    • 조절 중 user-select: none을 사용해 텍스트 선택을 방지.

 

 

 

react 코드:

import React, { useState } from "react";
import "./ResizableDiv.css"; // 스타일 관리

const ResizableDiv = () => {
  const [height, setHeight] = useState(300); // 상단 div 초기 높이
  const [isResizing, setIsResizing] = useState(false); // 조절 상태 관리

  const handleMouseDown = () => {
    setIsResizing(true); // 마우스 드래그 시작
  };

  const handleMouseMove = (e) => {
    if (!isResizing) return;
    const newHeight = e.clientY; // 마우스 위치를 기준으로 높이 계산
    setHeight(newHeight);
  };

  const handleMouseUp = () => {
    setIsResizing(false); // 마우스 드래그 종료
  };

  return (
    <div
      className="container"
      onMouseMove={handleMouseMove}
      onMouseUp={handleMouseUp}
      onMouseLeave={handleMouseUp}
    >
      <div className="top-div" style={{ height: `${height}px` }}>
        상단 Div
      </div>
      <div className="resizer" onMouseDown={handleMouseDown}></div>
      <div className="bottom-div">하단 Div</div>
    </div>
  );
};

export default ResizableDiv;

 

 

CSS

.container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  width: 100%;
  user-select: none; /* 텍스트 선택 방지 */
}

.top-div {
  background-color: #f0f0f0;
  border-bottom: 2px solid #ccc;
  overflow: auto;
}

.resizer {
  height: 10px;
  background-color: #888;
  cursor: row-resize;
}

.bottom-div {
  background-color: #e0e0e0;
  flex: 1;
  overflow: auto;
}
Posted by yongary
,

Slack API 키를 발급받기 위해서는 Slack 앱 생성과정을 거쳐야 합니다. Slack 앱을 생성한 후, API 키(토큰)를 발급받아 다양한 Slack API 호출에 사용할 수 있습니다. 아래는 단계별 방법입니다.


1. Slack 앱 생성하기

  1. Slack API 페이지 방문:
  2. 앱 생성 시작:
    • 상단의 "Create an App" 버튼을 클릭합니다.
  3. 앱 생성 방법 선택:
    • **"From scratch"**를 선택하여 새 앱을 생성합니다.
  4. 앱 이름 및 워크스페이스 선택:
    • 앱 이름을 입력하고, 앱을 설치할 워크스페이스를 선택합니다.
    • "Create App" 버튼을 클릭하여 앱 생성을 완료합니다.

2. OAuth & Permissions 설정

  1. OAuth & Permissions 메뉴로 이동:
    • 생성된 앱의 관리 페이지에서 "OAuth & Permissions" 탭으로 이동합니다.
  2. OAuth 범위(Scope) 설정:
    • "Scopes" 섹션에서 앱이 필요로 하는 권한을 설정합니다.
    • 예를 들어:
      • chat:write: 메시지 전송 권한.
      • channels:read: 채널 정보 읽기 권한.
      • users:read: 사용자 정보 읽기 권한.
    • 필요한 범위를 선택하고 추가합니다.
  3. 앱 설치:
    • "Install App to Workspace" 버튼을 클릭하여 앱을 워크스페이스에 설치합니다.
    • 설치를 완료하면 **OAuth 토큰(Access Token)**이 생성됩니다.

3. API 키(Access Token) 확인

  1. OAuth 토큰 복사:
    • 설치 완료 후 표시되는 "Bot User OAuth Token" 또는 **"User OAuth Token"**을 복사합니다.
    • 이 토큰이 Slack API 호출 시 사용하는 API 키입니다.
  2. 보안 저장:
    • 토큰은 민감한 정보이므로 안전한 곳에 저장합니다.
    • 코드에 직접 포함시키기보다는 환경변수로 관리하는 것이 권장됩니다.

4. 토큰 사용 예제

복사한 API 키를 사용하여 Slack API를 호출할 수 있습니다. 예를 들어, 메시지 전송 API를 호출하려면 다음과 같이 작성합니다:

bash
코드 복사
curl -X POST -H "Authorization: Bearer xoxb-your-token" -H "Content-Type: application/json" \ --data '{"channel":"#general","text":"Hello, Slack!"}' \ https://slack.com/api/chat.postMessage
Posted by yongary
,

original_window = self.driver.current_window_handle
self.driver.switch_to.window(original_window)  # 메인 창으로 이동

# 새로운 창에서 실행
## child window
for window_handle in self.driver.window_handles:
    print(window_handle, original_window, window_handle != original_window)
    if window_handle != original_window:
        print("자식 창 변경")
        self.driver.switch_to.window(window_handle)
        print("current handle 2:: ", self.driver.current_window_handle)

self.driver.implicitly_wait(5)

 

 

self.driver.switch_to.window(original_window)  # 메인 창으로 이동

Posted by yongary
,

추가: ssh는 기존 pem파일 로그인, sftp는 비번 로그인으로 설정하는 법. /etc/ssh/sshd_config 파일 에 추가. 

# 공개 키 인증을 기본으로 사용
PasswordAuthentication no
PubkeyAuthentication yes

# SFTP만 비밀번호로 허용
Match Group sftpusers  (이건 맞는 그룹으로 변경필요) 
    PasswordAuthentication yes
    PubkeyAuthentication no
    ForceCommand internal-sftp

$sudo systemctl restart sshd

 

 

SFTP(Secure File Transfer Protocol)를 사용하기 위해서는 현재 설정된 vsftpd를 변경할 필요는 없습니다. SFTP는 SSH(Secure Shell) 서비스로 작동하기 때문입니다. CentOS 7에서 SFTP를 사용하도록 설정하는 방법은 다음과 같습니다:

1. SSH 설치 및 실행 확인

SFTP는 SSH를 통해 작동하므로, SSH 서버(sshd)가 설치되고 실행 중인지 확인해야 합니다.

 
sudo yum install openssh-server sudo systemctl start sshd sudo systemctl enable sshd

2. SFTP 사용자 설정

이미 cncity라는 사용자가 있습니다. 이 사용자가 유효한 쉘(예: /bin/bash)을 가지고 있고 올바른 홈 디렉토리를 가지고 있는지 확인합니다:

bash
 
sudo usermod -s /bin/bash cncity

사용자의 홈 디렉토리가 존재하고 올바른 권한을 가지고 있는지 확인하세요:

bash
 
sudo mkdir -p /home/centos/nfs/cncity sudo chown cncity:cncity /home/centos/nfs/cncity

3. SFTP만 허용하도록 사용자 제한 (선택 사항)

만약 SFTP만 허용하고 전체 SSH 접근을 제한하려면, SFTP를 위한 제한된 쉘을 설정할 수 있습니다:

SSH 설정 파일 편집:

bash
코드 복사
sudo vi /etc/ssh/sshd_config
cncity 사용자에 대한 SFTP 제한 설정 추가:

plaintext
코드 복사
Match User cncity
    ForceCommand internal-sftp
    ChrootDirectory /home/centos/nfs
    PermitTunnel no
    AllowAgentForwarding no
    AllowTcpForwarding no
    X11Forwarding no
ForceCommand internal-sftp는 사용자가 SFTP만 사용하도록 강제합니다.
ChrootDirectory /home/centos/nfs는 사용자를 지정된 디렉토리(/home/centos/nfs)로만 제한합니다.
ChrootDirectory는 root가 소유하고 사용자에게 쓰기 권한이 없어야 합니다.
Chroot 디렉토리의 권한 설정:

bash
코드 복사
sudo chown root:root /home/centos/nfs
sudo chmod 755 /home/centos/nfs
SSH 서비스 재시작:

bash
코드 복사
sudo systemctl restart sshd

 

 

4. 기본적으로는 id/비번만으로 동작함. but 키 인증기반으로 변경할 수도 있음.

1. 기본 설정 (암호 인증)
클라이언트에게 제공할 것: 사용자 이름과 비밀번호
설명: SFTP 접속 시 클라이언트는 서버의 IP 주소, 사용자 이름, 비밀번호만으로 접속할 수 있습니다. 추가적인 인증 키나 인증서는 필요하지 않습니다.
보안 수준: 기본적인 암호 인증은 충분히 안전하지만, 키 기반 인증보다 상대적으로 보안이 낮을 수 있습니다.
2. 키 기반 인증 (더 안전한 방법)
클라이언트에게 제공할 것: 개인 SSH 키 (Private Key)

설정 방법:

서버에서 SSH 키 쌍을 생성 (클라이언트가 생성할 수도 있음):

bash
코드 복사
ssh-keygen -t rsa -b 2048
이 명령을 실행하면 id_rsa(개인 키)와 id_rsa.pub(공개 키)라는 두 개의 파일이 생성됩니다.

클라이언트의 공개 키를 서버에 복사:

공개 키 파일(id_rsa.pub)을 서버의 사용자의 홈 디렉토리에 있는 ~/.ssh/authorized_keys 파일에 추가합니다:

bash
코드 복사
cat id_rsa.pub >> /home/cncity/.ssh/authorized_keys
SSH 설정 파일에서 키 인증 사용 (이미 기본적으로 활성화되어 있을 수 있음):

/etc/ssh/sshd_config 파일을 열고 PubkeyAuthentication이 yes로 설정되어 있는지 확인합니다:

plaintext
코드 복사
PubkeyAuthentication yes
SSH 서비스 재시작:

bash
코드 복사
sudo systemctl restart sshd
클라이언트 측: 클라이언트는 개인 키(id_rsa)를 SFTP 접속 시 사용해야 합니다. SFTP 클라이언트에서 개인 키를 설정하거나, 명령줄에서 다음과 같이 사용할 수 있습니다:

bash
코드 복사
sftp -i /path/to/private_key cncity@your_server_ip
보안 수준: 키 기반 인증은 암호 기반 인증보다 안전합니다. 개인 키를 분실하지 않도록 주의해야 하며, 개인 키는 암호로 보호하는 것이 좋습니다.
Posted by yongary
,

vsftpd for CentOS7

IT 2024. 9. 4. 16:44

기본 설치 방법.

sudo yum install vsftpd

sudo systemctl start vsftpd
sudo systemctl enable vsftpd

 

 

 

sudo vi /etc/vsftpd/vsftpd.conf 기본설정 항목. 

# 익명 사용자 접속 금지
anonymous_enable=NO

# 로컬 사용자 접속 허용
local_enable=YES

# 업로드 권한 부여
write_enable=YES

# 사용자 홈 디렉토리를 루트 디렉토리로 설정
chroot_local_user=YES

# FTP에 허용할 사용자 목록을 지정하는 설정
userlist_enable=YES
userlist_file=/etc/vsftpd/user_list
userlist_deny=NO

 

sudo vi /etc/vsftpd/user_list 에는
cncity
라는 유저명 한줄만 있으면 됨. 

 

 

 

FTP 루트 디렉토리를 홈 디렉토리로 설정하는 chroot_local_user=YES가 이미 활성화된지 확인합니다. 이 설정은 사용자가 자신의 홈 디렉토리(지금은 /home/centos/nfs/cncity)로만 접근하도록 제한하는 역할을 합니다.

$sudo vi /etc/vsftpd/vsftpd.conf

# 로컬 사용자 홈 디렉토리를 루트 디렉토리로 설정
chroot_local_user=YES

# 일부 사용자만 chroot 제한을 적용하려면
# chroot_list_enable=YES
# chroot_list_file=/etc/vsftpd/chroot_list
Posted by yongary
,
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 10초 동안 팝업이 나타날 때까지 기다림
try:
    popup_element = WebDriverWait(self.driver, 10).until(
        EC.presence_of_element_located((By.ID, "popup_element_id"))  # 여기서 ID는 팝업의 요소 ID
    )
    # 팝업이 나타났을 때 수행할 동작
except TimeoutException:
    print("팝업이 나타나지 않았습니다.")
Posted by yongary
,

이렇게 하면 file이 업로드 되었을때, 이벤트도 받을 수 있다.

 

<dependency>
    <groupId>org.apache.ftpserver</groupId>
    <artifactId>ftpserver-core</artifactId>
    <version>1.1.1</version>
</dependency>

 

import org.apache.ftpserver.FtpServer;
import org.apache.ftpserver.FtpServerFactory;
import org.apache.ftpserver.ftplet.Ftplet;
import org.apache.ftpserver.ftplet.FtpletContext;
import org.apache.ftpserver.ftplet.FtpletResult;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory;
import org.apache.ftpserver.usermanager.SaltedPasswordEncryptor;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Component
public class EmbeddedFtpServer {

    @PostConstruct
    public void startFtpServer() {
        FtpServerFactory serverFactory = new FtpServerFactory();
        ListenerFactory factory = new ListenerFactory();
        
        // Set the port of the listener
        factory.setPort(2221);
        serverFactory.addListener("default", factory.createListener());

        // Setup user manager
        PropertiesUserManagerFactory userManagerFactory = new PropertiesUserManagerFactory();
        userManagerFactory.setFile(new File("users.properties"));
        userManagerFactory.setPasswordEncryptor(new SaltedPasswordEncryptor());
        serverFactory.setUserManager(userManagerFactory.createUserManager());

        // Add Ftplet to handle FTP events
        Map<String, Ftplet> ftplets = new HashMap<>();
        ftplets.put("eventListenerFtplet", new EventListenerFtplet());
        serverFactory.setFtplets(ftplets);

        // Start the server
        FtpServer server = serverFactory.createServer();
        try {
            server.start();
            System.out.println("Embedded FTP server started.");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

이벤트 수신

import org.apache.ftpserver.ftplet.*;

import java.io.IOException;

public class EventListenerFtplet extends DefaultFtplet {

    @Override
    public FtpletResult onUploadStart(FtpSession session, FtpRequest request) throws FtpException, IOException {
        System.out.println("Start uploading file: " + request.getArgument());
        return FtpletResult.DEFAULT;
    }

    @Override
    public FtpletResult onUploadEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
        System.out.println("Finished uploading file: " + request.getArgument());
        return FtpletResult.DEFAULT;
    }

    @Override
    public FtpletResult onConnect(FtpSession session) throws FtpException, IOException {
        System.out.println("User connected: " + session.getUser().getName());
        return FtpletResult.DEFAULT;
    }

    @Override
    public FtpletResult onDisconnect(FtpSession session) throws FtpException, IOException {
        System.out.println("User disconnected: " + session.getUser().getName());
        return FtpletResult.DEFAULT;
    }
}

 

사용자까지 설정한 예제는:

import org.apache.ftpserver.FtpServerFactory;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory;
import org.apache.ftpserver.usermanager.impl.BaseUser;
import org.apache.ftpserver.usermanager.impl.SaltedPasswordEncryptor;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;

@Component
public class EmbeddedFtpServer {

    @PostConstruct
    public void startFtpServer() {
        FtpServerFactory serverFactory = new FtpServerFactory();
        ListenerFactory factory = new ListenerFactory();
        
        // Set the port of the listener
        factory.setPort(2221);
        serverFactory.addListener("default", factory.createListener());

        // Setup user manager
        PropertiesUserManagerFactory userManagerFactory = new PropertiesUserManagerFactory();
        userManagerFactory.setPasswordEncryptor(new SaltedPasswordEncryptor());
        var userManager = userManagerFactory.createUserManager();

        // Create a new user
        BaseUser user = new BaseUser();
        user.setName("user1");
        user.setPassword("pass1");
        user.setHomeDirectory("/path/to/root/folder1");
        user.setEnabled(true);

        // Set permissions if needed (e.g., write permission)
        userManager.save(user);

        serverFactory.setUserManager(userManager);

        // Start the server
        FtpServer server = serverFactory.createServer();
        try {
            server.start();
            System.out.println("Embedded FTP server started.");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Posted by yongary
,

Entity class에 @Document 어노테이션을 추가하고,

특정필드에 @Indexed를 추가하면 자동으로 인덱스가 생성된다.

만약에 잘 안되면, 원인분석을 해야겠지만,

임시로 코드로 하는 방법이있는데, 다음과 같다.

1.  잘 동작함 - MongoConfig 수정 방식

    //AUTO INDEX CREATION안되서 추가: 참고- https://github.com/spring-projects/spring-boot/issues/28478#issuecomment-954627106
    @Override
    protected boolean autoIndexCreation() {
        return true;
    }

    @Bean
    @Primary
    public MongoMappingContext mongoMappingContext(MongoCustomConversions conversions, MongoManagedTypes managedTypes) {
        MongoMappingContext mappingContext = super.mongoMappingContext(conversions, managedTypes);
        mappingContext.setAutoIndexCreation(true);
        return mappingContext;
    }



 

 

 

2. not TESTED. - post Construct방식

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.index.Index;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

@Service
public class IndexCreatorService {

    @Autowired
    private MongoTemplate mongoTemplate;

    @PostConstruct
    public void initIndexes() {
        IndexOperations indexOps = mongoTemplate.indexOps(ExampleEntity.class);
        indexOps.ensureIndex(new Index().on("fieldToIndex", org.springframework.data.domain.Sort.Direction.ASC));
    }
}
Posted by yongary
,