1. rs.status() 로 state=2 (secondary), health=1  이면 sync가 완료된 것임.

2. 한대 추가 후, 삭제 하기

rs.add("NEW_IP:27017")

js
코드 복사
rs.status()
🔎 state: 2 = Secondary 가 될 때까지 기다리기 (sync 시간 필요)


rs.remove("OLD_IP:27017")


2-1: 중요 - application 배포 하기. (바뀐 IP 적용해서 배포하기)
위 스텝 2번 반복.

 

중요: 마지막 primary 변경전에는 primary를 다른 2번서버로 옮기기.

cfg = rs.conf()
cfg.members[0].priority = 0    // 기존 Primary
cfg.members[1].priority = 2    // 새로 Primary가 되길 원하는 노드
rs.reconfig(cfg)



3. primary는 secondary처럼  conf change하고시기고
     application 배포 하기. (바뀐 IP 적용해서 배포하기)


그 후 primary 변경 후
마지막 application 배포 하기.

Posted by yongary
,

부제: mongo-keyfile 을 설정해서 mongoDB를 restart해도 auth를 활성화 하지 않은 상태로 유지하기


0. 아래 1번부터 하면 좋지만, keyfile이 없는 서버와 있는서버간 통신이 안되는 문제가 있으므로
- security:
  keyFile: /etc/mongo/mongo-keyfile
  transitionToAuth: true
  authorization: disabled

로 설정해서 3,2,1 순서로 restart하고

다되면 , mongoDB user추가 및 application 소스에 반영한 후에

# transitionToAuth: true 로 코멘트 하고, 3,2,1 다시 restart


1. keyFile = “노드끼리 비밀 합의용 패스워드”

keyFile은 원래 replica set / sharded cluster 노드끼리 서로 인증하는 용도입니다.

  • security.keyFile: /etc/mongo-keyfile 만 설정하고
  • security.authorization: disabled 로 두면:

👉 결과

  • 서버끼리(replica set 멤버 간):
    • keyFile을 사용해서 internal authentication을 합니다.
  • 클라이언트(SPRING, mongo shell 등):
    • 여전히 user/password 없이 접속 가능
    • 기존처럼 mongosh --host ... 하면 그냥 접속

즉, keyFile만 설정했다고 해서 바로 “유저 인증 필수” 모드로 바뀌진 않습니다.
유저/비밀번호 강제를 하려면 authorization: enabled 가 결정적 스위치입니다.


2. 그래서 rollout 순서는 이렇게 가면 돼요

당신이 계획한 것처럼:

  1. 지금:
    • authorization: disabled
    • keyFile 없음
    • 아무나 접속 가능
  2. 1단계 – keyFile만 먼저 적용 (auth 여전히 꺼둔 상태)
    • 모든 노드에 /etc/mongo-keyfile 배포
    • mongod.conf에:
    •  
      security: authorization: disabled keyFile: /etc/mongo-keyfile
    • replica set이면 롤링 재시작
    • 이때도 여전히 애플리케이션은 비번 없이 접속 가능
  3. 2단계 – 유저 생성 + 스프링부트에서 계정/비번 사용하도록 변경
    • 아직 authorization: disabled라
      • 유저를 만들어도
      • 비번을 보내도 / 안 보내도
        전부 통과됩니다.
    • 이 상태에서 Spring Boot 설정을 미리:
    •  
      spring.data.mongodb.uri=mongodb://appUser:강력비번@host1,host2,host3/yourDb?replicaSet=rs0&authSource=admin
  4. 3단계 – 마지막에 auth ON
     
    security: authorization: enabled keyFile: /etc/mongo-keyfile
    로 바꾸고, 다시 replica set 롤링 재시작.
    • 이 시점부터는
      • 계정/비번 없이 접속하는 옛날 클라이언트 → 전부 접속 실패
      • 이미 계정/비번 쓰도록 바꾼 Spring Boot → 정상 동작
  5. 이제 정말로:

3. 한 줄로 요약

keyFile 설정 + 재시작만으로는 클라이언트 인증이 강제되지 않습니다.

그래서 원하는 대로
“keyFile 먼저 → 나중에 authorization: enabled”
순서로 점진적 전환(거의 무중단) 진행이 가능합니다.

 

Posted by yongary
,

🎯 요약

단계내용
1 (sudo -iu root 후에)
openssl rand -base64 756 > mongo-keyfile
2 chmod 600 & chown mongod
3 모든 노드 /etc/mongo-keyfile로 SCP
4 mongod.conf에 authorization + keyFile 설정
5 replica set 롤링 재시작
6 rs.status() 확인

3서버에 완전히 동일한 mongo-keyfile이 복사되어야 함

상세 설명:
MongoDB Replica Set에서 auth를 켜려면 노드 간 인증용 keyFile을 동일하게 만들어서 모든 서버에 배포해야 합니다.
AWS EC2(리눅스) 기준으로 MongoDB 5.0.23에서 사용할 수 있는 안전한 방법을 설명할게요.


🔐 Mongo KeyFile 생성 & 배포 절차 (EC2 / Linux)

📌 1) EC2 한 대에서 keyFile 생성

아래 명령을 실행 (지금 로그인한 서버 아무 곳이나 상관없음):

 
openssl rand -base64 756 > mongo-keyfile
  • 파일 길이는 at least 32 bytes 필요하지만
    MongoDB 권장값인 756 bytes 사용 (5.x에서도 OK)
  • 파일 이름은 아무거나 가능 (mongo-keyfile, rs.key, 등)

📌 2) 권한 설정 (반드시 600)

MongoDB는 keyFile 권한이 600 아니면 실행 실패합니다.

 
chmod 600 mongo-keyfile

(소유자는 MongoDB 사용자면 더 안전함)

 
chown mongod:mongod mongo-keyfile # 시스템마다 mongod 또는 mongodb

만약 MongoDB 프로세스가 mongod 유저가 아닌 root로 실행되고 있다면
chown 하지 않아도 됨. (권장 X → 되도록 mongod 유저로)


📌 3) keyFile을 Replica Set 모든 서버에 복사

각 EC2 인스턴스에 SSH로 복사:

 
scp mongo-keyfile ec2-user@<SERVER1_IP>:/etc/ scp mongo-keyfile ec2-user@<SERVER2_IP>:/etc/ scp mongo-keyfile ec2-user@<SERVER3_IP>:/etc/

그리고 각 서버에서 다시 권한 재확인:

 
chmod 600 /etc/mongo-keyfile chown mongod:mongod /etc/mongo-keyfile

3개 노드가 완전히 동일한 keyFile을 사용해야 합니다.
절대 각각 생성하면 안 됩니다.


📌 4) mongod.conf 수정 (각 서버 공통)

/etc/mongod.conf 파일 편집:

 
security: authorization: enabled keyFile: /etc/mongo-keyfile

또는 기존에 security: 블록이 있다면 아래처럼 통합:

 
security: authorization: enabled keyFile: /etc/mongo-keyfile

keyFile 설정 + authorization을 같이 써야 합니다.
keyFile만 넣고 auth 안 넣으면 replica set 인증만 되고 클라이언트는 무조건 실패합니다.


📌 5) 노드별 롤링 재시작

각 서버에서 한 대씩 순서대로:

 
sudo systemctl restart mongod

🚨 주의

Primary를 마지막에 재시작해야 합니다.
Secondary 2대 → Primary 순서.

Primary 먼저 끄면 다운타임 발생.


📌 6) 상태 확인

Primary에 접속한 후:

 
rs.status()

정상 replica set이면 OK.


🧠 추가 Tip (AWS EC2 운영 시 추천)

✔ KeyFile 보안을 위해 권장

 
chmod 400 /etc/mongo-keyfile

MongoDB는 600 또는 400 모두 허용합니다.

✔ EC2 UserData로 자동 배포하는 경우

  • Cloud-init에서 keyFile copy 후 chmod + chown 반드시 적용

✔ KeyFile Backup

  • keyFile은 분실하면 replica set이 절대로 부활 못합니다.
  • 안전한 S3/KMS 또는 Parameter Store에 백업 권장
Posted by yongary
,

1. 개념부터 정리

MongoDB는 크게 두 상태 중 하나입니다.

  1. authorization 꺼짐 (–-auth 없음, security.authorization: disabled)
    • 비번 여부 상관 없이 모든 연결이 풀 권한
    • 클라이언트가 username/password를 보내도 그냥 무시하고 동작
  2. authorization 켜짐 (–-auth, security.authorization: enabled)
    • 모든 요청은 인증 필수
    • 유저/롤 기반 권한 체크 수행

즉, “비번 있어도 되고 없어도 되고” 라는 중간 모드는 없어요.
하지만 auth 끄고 있는 동안에도 미리 계정을 만들어 놓고,
앱 쪽은 비번을 먼저 사용하게 바꾸는 건 가능
합니다.


2. 전체 전략 요약 (거의 무중단 버전)

  1. 현재: auth OFF 상태
  2. 관리용/앱용 사용자 미리 생성 (auth OFF 상태에서)
  3. Spring Boot 설정을 수정해서 username/password로 접속하도록 배포
  4. MongoDB 설정에 auth 켜고, replica set이면 노드별 롤링 재시작

이 순서로 하면:

  • 앱 입장:
    • 1~3단계: 비번 없어도 잘 되고, 비번 보내도 잘 됨 (auth OFF라 그냥 통과)
    • 4단계: auth ON 이후에도 이미 비번을 보내고 있으므로 정상 동작
  • 기존 “비번 안 쓰는 옛날 설정” 앱은
    • 4단계 이후부터는 접속 실패 → 이 때 이미 전부 새 설정으로 바꿔 둔 상태여야 함

3. 단계별로 조금 더 구체적으로

✅ 1단계: 관리자/앱 계정 만들기 (auth OFF 상태에서)

mongosh(또는 mongo)로 접속:

 
mongosh --host <primary-host>:27017

admin DB에서 계정 생성:

 
use admin; // 관리용 계정 (DBA용) db.createUser({ user: "adminUser", pwd: "강력한_비번", roles: [ { role: "userAdminAnyDatabase", db: "admin" }, { role: "dbAdminAnyDatabase", db: "admin" }, { role: "readWriteAnyDatabase", db: "admin" }, ] }); // 애플리케이션용 계정 (권한 최소화) db.createUser({ user: "appUser", pwd: "또_다른_강력한_비번", roles: [ { role: "readWrite", db: "yourAppDb" } // 실제 사용하는 DB명 ] });

주의: 지금은 auth OFF라 사실상 아무 제약 없이 createUser가 되지만,
나중에 auth ON 하면 이 계정들이 진짜로 쓰이게 됩니다.


✅ 2단계: Spring Boot 쪽에서 먼저 비번 사용하도록 변경

application.yml 예시 (mongoTemplate 쓰는 경우도 똑같이 설정):

 
spring: data: mongodb: uri: mongodb://appUser:또_다른_강력한_비번@host1:27017,host2:27017,host3:27017/yourAppDb?replicaSet=rs0&authSource=admin

혹은 application.properties:

 
spring.data.mongodb.uri=mongodb://appUser:또_다른_강력한_비번@host1:27017,host2:27017,host3:27017/yourAppDb?replicaSet=rs0&authSource=admin

그리고 이 설정으로 애플리케이션 배포/롤링 배포를 먼저 합니다.

  • 이 시점에는 Mongo가 auth OFF →
    비번이 틀려도 연결은 그냥 성공합니다.
  • 하지만 나중에 auth ON 하면 이 계정 정보가 실제로 사용되죠.

✅ 3단계: MongoDB에 auth 활성화 (security.authorization: enabled)

3-1. mongod.conf 수정

각 노드의 /etc/mongod.conf (또는 사용 중인 conf)에서:

 
security: authorization: enabled # replica set이면 keyFile도 같이 설정해야 함 (이미 쓰고 있을 수도 있음) # keyFile: /etc/mongo-keyfile

replica set인데 아직 keyFile 안 쓰고 있다면,
별도로 keyfile 생성해서 세 노드에 같은 파일/권한으로 배포해야 합니다.
(이건 내부 노드 간 인증용)

3-2. replica set이라면 롤링 재시작

예: 노드 3대 (primary 1 + secondary 2)라고 가정.

  1. secondary 1
    • mongod stop
    • conf 수정 (authorization: enabled, keyFile 설정)
    • mongod start
    • rs.status()로 정상 동기 확인
  2. secondary 2
    • 위와 동일하게
  3. primary
    • primary를 마지막에 재시작 → 이때 잠깐 primary 선거가 일어날 수 있지만
      Driver가 잘 잡아주면 애플리케이션은 거의 문제없이 넘어감

이 과정을 통해 전체 replica set이 auth ON 상태로 전환됩니다.


4. 진짜 “무중단”이 가능한가?

  • 싱글 인스턴스 MongoDB라면:
    • auth 켜려면 어쨌든 한 번은 mongod 재시작이 필요 → 짧은 다운은 피하기 어려움
  • 3대 replica set이라면:
    • 각 노드를 하나씩 재시작하는 롤링 방식으로
      사용자 입장에서는 거의 무중단에 가깝게 전환 가능
    • 순간적인 primary 선거 동안 짧은 hiccup 정도는 있을 수 있음
    • spring-data-mongodb와 드라이버가 replica set + 재시도를 잘 처리해주면 체감 거의 없음

5. 질문하신 포인트에 대한 답 정리

비번이 없어도 동작하고 비번이 있어도 동작하도록 했다가,
나중에 비번 없이는 안 돌게 바꾸는 게 되나요?

  • Yes, 이 순서로 가능합니다:
    1. auth OFF 상태에서 사용자 계정들 미리 생성
    2. Spring Boot를 먼저 username/password 쓰게 배포
      → 이때는 “비번 있어도/없어도” 모두 동작하는 상태 (auth OFF니까)
    3. MongoDB에 authorization: enabled 설정 후 롤링 재시작
      → 이제는 비번 없이는 안 되는 상태로 전환
Posted by yongary
,

🔥 결론: 하나의 Java 프로세스 안에 있는 thread들을 전부 분리해서 확인할 수 있다

방법은 바로 이것:


✅ 1. Linux에서 Java 프로세스의 thread를 분리해서 보는 방법

명령어

 
top -H -p <java_pid>

예:

 
top -H -p 12345

그럼 하나의 Java 프로세스(PID=12345) 안에 있는 모든 thread(TID) 가 각각 CPU를 얼마나 쓰는지 나옵니다.

예:

 
PID %CPU COMMAND 12345 10.0 java <-- 전체 12347 280.0 java <-- 문제 쓰레드(스레드 ID) 12349 12.3 java 12351 5.6 java
  • 12347: 하나의 TID(thread ID)
  • 이게 CPU 280% 먹고 있음 → 문제 쓰레드

즉, Java 한 개 프로세스 안의 각 thread CPU 사용량을 분리해서 알 수 있음.


✅ 2. 문제 thread가 “Java 내부에서 어떤 함수”를 돌고 있는지도 확인 가능

이건 Java thread dump(jstack)를 사용합니다.


Step A. thread ID(TID)를 16진수로 변환

Java thread dump는 hex 값으로 thread를 표시함.

 
printf "%x\n" 12347

출력 예:

 
303b

Step B. jstack 실행

 
jstack 12345 > /tmp/stack.txt

Step C. thread ID(hex)로 검색

 
grep -n "0x303b" /tmp/stack.txt

그럼 이렇게 나옵니다:

 
"pool-1-thread-3" #32 prio=5 tid=0x00007fda900303b nid=0x303b runnable at com.myapp.service.OrderService.process(OrderService.java:182) at com.myapp.service.OrderService.runLoop(OrderService.java:155) at java.lang.Thread.run(Thread.java:748)

여기서:

  • nid=0x303b ← 이게 바로 문제 thread
  • process() 함수에서 CPU를 많이 먹고 있음
  • runLoop() 안에서 무한 loop가 돌고 있을 수 있음

이렇게 정확한 함수 이름 + 라인번호까지 알 수 있습니다.


🔥 추가: thread 이름까지 알 수 있음

Java에서는 thread를 만들 때 이름을 붙이면

 
new Thread(() -> {...}, "Worker-1").start();

jstack에서 이렇게 보입니다:

 
"Worker-1" #25 prio=5 ...

즉,

  • thread 이름
  • thread 상태
  • thread call stack
  • CPU 점유율
  • lock(JVM monitor) 보유 여부

전부 확인 가능.


🔥 추가: thread 목록 전체 보기

 
jcmd <pid> Thread.print

또는 자동으로 thread hierarchy 확인 가능.


🔥 결론 요약

하나의 Java 프로그램이라도 내부 thread들은 개별 TID를 가지므로
CPU 사용량, 함수 위치, thread 이름 등을 전부 분리해서 확인 가능하다.

✔ top -H -p PID → thread CPU 식별
✔ printf "%x" TID → hex 변환
✔ jstack PID → thread dump
✔ grep hex → problem thread 찾기
✔ 어떤 함수인지 100% 확인 가능


원하면 지금 사용 중인 Java 서버에서
문제가 되는 thread를 실시간으로 찾는 쉘 스크립트도 만들어줄게!

나의 말:

thread ID는 찾아도 어떤 코드인지 알수는 없는건가요?

ChatGPT의 말:

알 수 있습니다.
단순히 “thread ID 숫자만 보인다 → 코드 위치는 모른다”가 아니라,
thread ID(TID) → jstack → Java 함수명 + 파일명 + 라인번호까지 정확히 찾을 수 있습니다.

즉,

thread ID만 알면 “어떤 코드(어떤 함수)가 문제인지 100% 추적 가능합니다.”

아래를 보면 왜 가능한지 이해됩니다.


🔥 핵심 개념:

리눅스에서 보이는 thread ID(TID)는 JVM 내부 스레드의 nid(native thread id)와 1:1로 매핑됩니다.

예를 들어 top에서:

 
TID = 12347

라고 CPU를 많이 먹는 스레드가 보이면,

  • 이걸 HEX로 바꾸고
  • jstack에서 해당 TID를 검색하면
  • 그 스레드가 어떤 Java 코드(메소드) 를 실행 중인지 정확히 알 수 있습니다.

실제로 이렇게 나옵니다:


🚀 실전 예제 (이게 실제로 나오는 화면)

Step1 — CPU 많이 먹는 thread 찾기

 
top -H -p <java_pid>

예:

 
12347 280% java ← 문제 스레드

Step2 — 10진수를 16진수(hex)로 변환

 
printf "%x\n" 12347

출력:

 
303b

Step3 — jstack 덤프 생성

 
jstack <java_pid> > dump.txt

Step4 — 문제 스레드 검색

 
grep -n "0x303b" dump.txt

🔥 실제로 보이는 결과

 
"pool-4-thread-1" #47 prio=5 os_prio=0 tid=0x00007fda900303b nid=0x303b runnable at com.company.payment.PaymentService.calculateFee(PaymentService.java:183) at com.company.payment.PaymentWorker.run(PaymentWorker.java:92) at java.lang.Thread.run(Thread.java:748)

즉, thread ID 하나만으로

  • 어떤 클래스인지
  • 어떤 메소드인지
  • 몇 번째 줄(line number)인지
    정확히 나온다.

여기서는:

  • calculateFee() 함수가 CPU를 폭주시키고 있음
  • 파일: PaymentService.java
  • 라인: 183

이 수준까지 바로 알 수 있습니다.


💡 정리하면

❗ thread ID 자체로는 코드 이름이 안 보이지만

❗ thread ID → jstack 검색을 하면

👉 스레드가 실행 중인 메소드
👉 메소드가 있는 클래스
👉 Java 파일 이름
👉 라인 번호
👉 Lock held 여부
👉 JVM 상태(runnable / waiting / blocked)

까지 전부 알 수 있습니다.


🎯 결론

thread ID만 알면 어떤 코드가 문제인지 100% 찾아낼 수 있습니다.
단순한 추론이 아니라, 실제 Java stack trace에 메소드/라인번호가 정확히 찍힙니다.

Posted by yongary
,

✅ 1. MongoDB 5.0 지원 종료 사실

MongoDB 5.0은 2024년 10월부로 End of Life(EOL) 입니다.
→ Security patch 포함 지원 없음, CVE 발생 시 패치 불가

현재 지원되는 LTS 버전은:

  • 6.0 LTS
  • 7.0 LTS

즉, 실질적 업그레이드 목표는:

 
5.0 → 6.0 → 7.0

(6.0을 LTS로 오래 운영해도 됨)

MongoDB는 메이저 버전 건너뛰기 업그레이드를 허용하지 않습니다.
5 → 7로 직접 업그레이드하면 ReplicaSet 자체가 깨집니다.


✅ 2. “서비스 중단 없이” 업그레이드할 수 있는 구조

현재 EC2 3대로 구동 중이라면 아마 아래 형태일 거예요:

 
Replica Set - Primary - Secondary - Secondary

이 구조에서는 롤링 업그레이드(Rolling Upgrade) 를 사용하면
서비스 중단 없이 업그레이드 가능합니다.


✅ 3. 정식 업그레이드 절차 (MongoDB 공식 절차 기반)

전체 플로우

 
1) Pre-check (호환성, 스토리지 엔진, Feature Compatibility Version) 2) Secondary 1대 업그레이드 3) Secondary 2대 업그레이드 4) Primary를 Step Down 후 업그레이드 5) FCV 변경 6) 클러스터 정상화 확인

아래에 실제 현장에서 쓰는 구체 절차를 적어볼게요.


🔥 4. 실제 상세 절차

Step 0 — 백업/스냅샷 생성

  • EC2 EBS Snapshot
  • 혹은 mongodump
    → 실패 시 즉각 롤백 가능하게

Step 1 — 사전 체크

mongo 콘솔에서:

 
db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })

FCV가 5.0이어야 정상.

또한:

 
rs.status() rs.conf()

오류 없는지 확인.


Step 2 — Secondary 한 대를 업그레이드

  1. Secondary 한 대를 선택
  2. mongod 종료
 
sudo systemctl stop mongod
  1. MongoDB 6.0 패키지 설치
    (amazon linux / ubuntu에 따라 다름 — 필요하면 상세 스크립트 제공 가능)
  2. 재기동
 
sudo systemctl start mongod
  1. 클러스터 정상 합류 확인
 
rs.status()
  1. oplog sync OK 확인

→ 이 서버가 6.0으로 잘 돌아가면 계속 진행.


Step 3 — 나머지 Secondary도 동일 절차로 업그레이드


Step 4 — Primary 업그레이드

Primary는 직접 Upgrade 하면 다운타임 위험 있으므로:

  1. Primary를 강제로 step down
 
rs.stepDown()

→ 다른 Secondary가 Primary가 됨
서비스 중단 없음

  1. 기존 Primary 서버를 mongod stop
  2. MongoDB 6.0 설치 후 start

Step 5 — FeatureCompatibilityVersion 변경

모든 노드가 6.0에서 정상적으로 올라온 뒤:

 
db.adminCommand({ setFeatureCompatibilityVersion: "6.0" })

Step 6 — 정상 동작 확인

  • rs.status()
  • replication lag 확인
  • 애플리케이션 write/read 정상 동작 확인
  • op log 증가 확인
  • mongod.log 에 error 없는지

🔁 6. (선택) 6.0 → 7.0 업그레이드

같은 방식으로 “롤링 업그레이드” 하면 됩니다.

 
5.0 → 6.0 → 안정화 후 → 7.0

바로 7.0까지 갈 필요 없으면 6.0 LTS 유지해도 안전합니다.


⚠️ “바로 5.x → 7.x or 5.x → 5.9.x” 하면 안 되는 이유

  • MongoDB는 메이저 버전 건너뛰기 허용 안함
  • WiredTiger 스토리지 메타데이터가 다름
  • FCV 매커니즘 때문에 클러스터 자체가 올라오지 않음
  • 5.9는 정식 릴리즈가 아님 (개발 프리뷰)

🧩 추가 고려사항 (실무에서 꼭 필요)

✔︎ 바뀌는 parameter / deprecated 기능

  • setParameter, replication.enableMajorityReadConcern 같은 값의 호환성 체크

✔︎ Driver 버전 업데이트

Node/Java/Python에서 MongoDB Driver도 버전 호환 필요

  • 6.x 이상에서는 5.x 드라이버가 deprecated 되는 경우 있음

✔︎ Monitoring (CloudWatch/Datadog/Prometheus)

업그레이드 후 connection pool 동작이 달라질 수 있음


🎯 최종 요약

MongoDB 5.0 → 6.0 롤링 업그레이드 절차

  1. Snapshot/백업 생성
  2. FCV 확인
  3. Secondary 1 업그레이드 → 정상 확인
  4. Secondary 2 업그레이드
  5. Primary stepDown → 업그레이드
  6. 6.0 전체 업 후 FCV 6.0 설정
  7. 정상 동작 확인
  8. (선택) 같은 방식으로 7.0까지 업그레이드 가능

👉 서비스 중단 없음 (Zero downtime 유지 가능)
👉 메이저는 반드시 계단식 업그레이드 (5 → 6 → 7)

Posted by yongary
,

AWS EC2 환경에서 데이터베이스(PostgreSQL, MySQL 등)는 보통 보안상의 이유로 퍼블릭 접근을 막고 VPC 내부에서만 접근 가능하도록 구성합니다.
이때, 개발자나 운영 담당자가 사내 네트워크(로컬 PC)에서 안전하게 DB에 접속하려면 SSH 포트포워딩(터널링) 방식을 활용하는 것이 가장 간편하고 안전한 방법입니다.


1️⃣ 준비사항

항목설명
EC2 인스턴스 DB가 설치되어 있는 서버 (또는 DB에 접근 가능한 Bastion 서버)
PEM 키 파일 AWS EC2 접속용 개인키 (예: mykey.pem)
보안그룹 EC2의 SSH(22번 포트)는 회사 IP만 허용되어 있어야 함
DB 클라이언트 psql, DBeaver, TablePlus 등 DB 접속 도구

2️⃣ SSH 포트포워딩 원리

SSH 클라이언트가 로컬 포트EC2 서버 포트 사이에 암호화된 통신 터널을 생성합니다.

 
[로컬 PC] ←(암호화 터널:SSH)→ [EC2 서버][DB 서버(Localhost:5432)]

즉, 로컬 PC에서 localhost:5433 으로 접속하면, 실제로는 EC2의 localhost:5432 (PostgreSQL 등)에 접속하는 효과가 납니다.


3️⃣ 실행 명령 예시 (PostgreSQL 기준)

 
ssh -i ~/mykey.pem -L 5433:localhost:5432 ec2-user@<EC2_Public_IP>
옵션설명
-i ~/mykey.pem 개인키 경로 지정
-L 5433:localhost:5432 로컬의 5433 포트를 EC2 내부의 5432 포트로 연결
ec2-user@<EC2_Public_IP> 접속 대상 EC2의 사용자와 공인 IP

✅ 이 명령을 실행한 터미널은 SSH 세션이 열린 상태로 유지되어야 합니다.
세션이 끊기면 포트포워딩도 함께 종료됩니다.


4️⃣ DB 클라이언트에서 접속

SSH 터널이 열린 상태에서 DB 클라이언트를 다음과 같이 설정합니다.

항목값
Host localhost
Port 5433
User postgres (또는 DB 계정명)
Password DB 비밀번호
Database 접속할 DB명 (예: appdb, vwstest 등)

5️⃣ 연결 확인

터미널에서 직접 psql로 접속해도 됩니다.

 
psql -h localhost -p 5433 -U postgres -d vwstest

정상적으로 접속되면, 포트포워딩이 성공적으로 작동하고 있는 것입니다.


6️⃣ 보안 팁

  • PEM 키는 외부 유출이 절대 금지이며, 파일 권한을 600으로 설정하세요.
  •  
    chmod 600 ~/mykey.pem
  • DB 보안그룹에서는 외부 접근(0.0.0.0/0) 을 모두 차단하고,
    EC2 내부 또는 Bastion 서버 IP 만 허용하세요.
  • 필요 시 autossh 나 ProxyCommand 를 이용해 포트포워딩 자동화를 설정할 수 있습니다.

✨ 마무리

이 방식은 별도의 VPN 없이도 AWS EC2의 SSH 터널을 활용하여
DB에 암호화된 통신 경로로 접속할 수 있는 안전한 방법입니다.
공용망을 통하지 않고 내부적으로만 접근이 가능하므로,
보안성과 편의성을 모두 확보할 수 있습니다.

Posted by yongary
,

AWS를 처음 사용할 때 가장 많이 하는 실수 중 하나가 바로 EC2 인스턴스 접속을 무조건 SSH 키페어로 해야 한다고 생각하는 것이야.
하지만 실제로는 **AWS Systems Manager(SSM)**을 이용하면 키페어 없이도 브라우저에서 바로 접속할 수 있고, 보안도 더 강력해져.

아래에서 SSM을 활용한 접속 과정과 필수 설정을 정리


1. SSM 접속의 장점

  • 키페어 분실 위험 없음
  • 22번 포트(SSH)를 열 필요가 없음 → 보안 강화
  • IAM 기반 권한 관리로 더 안전한 접근 제어
  • 브라우저 기반 Session Manager로 편하게 접속

2. SSM 사용 조건 정리

EC2가 SSM을 쓰려면 다음 3가지가 반드시 필요

① SSM Agent

  • Amazon Linux 2, Ubuntu 최신 버전은 기본 설치됨
  • 만약 설치 안 되어 있다면 아래 명령어로 가능
  •  
    sudo yum install amazon-ssm-agent -y sudo systemctl enable amazon-ssm-agent sudo systemctl start amazon-ssm-agent

② IAM Role 부착

EC2에 아래 정책을 가진 Role을 연결해야 함

  • AmazonSSMManagedInstanceCore

(콘솔 → EC2 → 인스턴스 선택 → 보안 → IAM Role 연결)

③ 네트워크 조건

  • 인터넷이 있거나
  • SSM용 VPC 엔드포인트가 존재해야 함

프라이빗 서브넷에서도 SSM 엔드포인트만 연결되어 있으면 접속 가능!


3. EC2를 SSM으로 접속하는 법

  1. AWS 콘솔 → Systems Manager → Session Manager
  2. “세션 시작” 클릭
  3. 인스턴스 선택 → Connect
  4. 브라우저 터미널이 열리면 끝

특별히 SSH 키도, 퍼블릭 IP도 필요 없다.


4. SSM 사용 시 자주 발생하는 오류 해결

🔸 1) 인스턴스 목록에 EC2가 안 보임

  • IAM Role에 AmazonSSMManagedInstanceCore 빠짐
  • SSM Agent가 실행 안 됨
  • 프라이빗 서브넷에 엔드포인트 없음

🔸 2) "세션 시작 실패"

  • CloudWatch Logs 권한 부족
  • SSM Agent 버전 오래됨
  • 보안그룹/라우팅으로 인해 SSM 서비스 접근 불가

🎯 5. SSM은 어떤 상황에서 쓰면 좋을까?

  • 운영 서버 SSH 차단하고 싶은 경우
  • 실습 환경에서 학생/팀원에게 개별 키 제공하기 싫을 때
  • Bastion 없이 내부 서버 접속해야 할 때
  • 로그 & 명령 실행을 중앙에서 관리하고 싶을 때
Posted by yongary
,

대폰 브라우저에서 캐시삭제 하면 localStorage삭제 되나요? 아니면 데이터삭제까지 해야하나요?

 

휴대폰 브라우저에서 캐시 삭제 localStorage 삭제는 별개입니다.

캐시 삭제만 하는 경우

  • localStorage는 삭제되지 않습니다
  • 이미지, CSS, JS 파일 등 캐시된 리소스만 삭제됩니다
  • 웹사이트의 로그인 상태나 설정값은 그대로 유지됩니다

localStorage를 삭제하려면

브라우저마다 다르지만, 일반적으로:

Chrome (Android)

  • "사이트 데이터 삭제" 또는 "쿠키 및 사이트 데이터" 옵션을 선택해야 합니다

Safari (iOS)

  • "방문 기록 및 웹사이트 데이터 지우기"를 선택하면 localStorage도 함께 삭제됩니다
  • 또는 설정 > Safari > 고급 > 웹사이트 데이터에서 개별 삭제 가능

Samsung Internet / 기타

  • "쿠키 및 사이트 데이터" 또는 "저장된 데이터" 항목을 선택해야 합니다

요약

단순 캐시 삭제로는 localStorage가 유지되므로, localStorage를 삭제하려면 "사이트 데이터" 또는 "쿠키 및 사이트 데이터" 항목까지 함께 삭제해야 합니다.

어떤 브라우저를 사용하시나요? 구체적인 방법을 알려드릴 수 있습니다.

Posted by yongary
,

🧱 1️⃣ 새로운 VPC 생성

  1. AWS Management Console → VPC 서비스로 이동
    검색창에 “VPC” 입력 → “VPCs” 메뉴 클릭
  2. [Create VPC] 버튼 클릭
  3. 설정 예시
    • VPC only 선택
    • Name tag: my-new-vpc
    • IPv4 CIDR block: 10.1.0.0/16 (기존 VPC와 겹치지 않게)
    • IPv6 CIDR: 비활성화(필요시만)
    • Tenancy: Default (Dedicated 필요 없으면)
  4. [Create VPC] 클릭

✅ 결과: my-new-vpc라는 완전히 독립된 VPC가 만들어집니다.


🌐 2️⃣ 서브넷 / 인터넷 게이트웨이 / 라우팅 세팅

VPC만 있으면 EC2가 외부통신 불가이므로, 기본 네트워킹 요소를 만들어야 합니다.

  1. Subnet 생성
    • VPC 대시보드 → “Subnets” → [Create subnet]
    • VPC: my-new-vpc
    • Subnet name: my-new-public-subnet
    • Availability Zone: ap-northeast-2a (또는 원하는 AZ)
    • IPv4 CIDR block: 10.1.1.0/24
    • Create subnet
  2. Internet Gateway 생성
    • 왼쪽 메뉴 “Internet Gateways” → [Create internet gateway]
    • Name tag: my-new-igw
    • Create → Attach to VPC → my-new-vpc
  3. Route Table 수정
    • “Route tables” → my-new-vpc에 자동생성된 테이블 선택
    • Routes 탭 → Edit routes → Add route
      • Destination: 0.0.0.0/0
      • Target: 방금 만든 my-new-igw
    • Save changes
    • “Subnet associations” 탭 → Edit → my-new-public-subnet 연결

✅ 결과: my-new-public-subnet은 인터넷 연결 가능한 퍼블릭 서브넷이 됨.


💻 3️⃣ 새 EC2 인스턴스 생성 (이 VPC 안에)

  1. EC2 → Instances → [Launch instances]
  2. 이름: my-new-server
  3. AMI 선택: Amazon Linux / Ubuntu 등
  4. 인스턴스 유형: t3.micro 등
  5. Key pair:
    • “Create new key pair” 클릭
    • Name: my-new-keypair
    • Type: RSA (또는 ED25519)
    • Format: .pem (Windows PuTTY면 .ppk 선택)
    • [Create key pair] → 자동으로 다운로드됨 (my-new-keypair.pem)
  6. Network settings:
    • VPC: my-new-vpc
    • Subnet: my-new-public-subnet
    • Auto-assign public IP: Enable
    • Security group: 새로 생성 (예: SSH(22), HTTP(80), HTTPS(443))
  7. Launch instance

✅ 결과: 완전히 독립된 VPC + 서브넷 + 키쌍 환경에 EC2 인스턴스 생성 완료

Posted by yongary
,

sftp 서버 연결

java8~ 2025. 6. 17. 08:19

✅ 1. Maven 의존성 추가

<dependency>
  <groupId>com.jcraft</groupId>
  <artifactId>jsch</artifactId>
  <version>0.1.55</version>
</dependency>

✅ 2. Java 코드: SFTP 연결 + 로그인 + 설정

import com.jcraft.jsch.*;

public class SftpUploader {
    public void connectSftp(String server, int port, String user, String pass) {
        Session session = null;
        ChannelSftp sftpChannel = null;

        try {
            JSch jsch = new JSch();
            session = jsch.getSession(user, server, port);
            session.setPassword(pass);

            // 호스트 키 검증 비활성화 (테스트용, 실제 배포 시에는 키 검증 로직 필요)
            session.setConfig("StrictHostKeyChecking", "no");

            session.connect();
            log.info("SFTP Server: connected");

            Channel channel = session.openChannel("sftp");
            channel.connect();
            sftpChannel = (ChannelSftp) channel;
            log.info("SFTP Server: login OK");

            // 여기에 파일 업로드/다운로드 코드 추가 가능
            // sftpChannel.put(...);
            // sftpChannel.get(...);

        } catch (JSchException | SftpException e) {
            log.error("SFTP 연결 오류", e);
        } finally {
            if (sftpChannel != null) {
                sftpChannel.exit();
                log.info("SFTP Channel: closed");
            }
            if (session != null) {
                session.disconnect();
                log.info("SFTP Session: disconnected");
            }
        }
    }
}
Posted by yongary
,

구글doc API 연동

BACK-END 2025. 6. 5. 17:47

1. Google API 설정

  google Drive API 활성화 방법

✅ 1. Google Drive API 활성화 방법
① Google Cloud Console 접속
→ 로그인 후 우측 상단에서 프로젝트 선택 or 새로 만들기

② API 및 서비스 > 라이브러리
→ Drive 검색
→ Google Drive API 클릭
→ [사용] 버튼 클릭

③ 사용자 인증 정보 (Credentials) 이동
→ 좌측 메뉴에서 “사용자 인증 정보” 클릭

✅ 2. OAuth 2.0 Client ID 또는 Service Account 발급
🔹 A. Service Account 발급 (서버 to 서버 방식, 권장)
1. “사용자 인증 정보 만들기” > 서비스 계정 선택
이름: drive-api-access

역할: 필요 없으면 “없음”으로 두어도 됨

2. 완료 후, 해당 서비스 계정 클릭
→ “키” 탭 > 새 키 만들기 > JSON 선택 → 자동 다운로드

3. 생성된 서비스 계정 이메일 확인
→ 예: drive-api-access@your-project-id.iam.gserviceaccount.com

4. Google Drive에 폴더 생성 후, 해당 서비스 계정을 공유자로 추가
Google Drive 웹에서 해당 폴더에 대해
공유 > drive-api-access@... 이메일 입력 후 편집자 권한 부여

이 폴더 아래의 하위 폴더 및 파일 생성 가능

🔹 B. OAuth 2.0 Client ID 발급 (사용자 인증 방식)
1. “사용자 인증 정보 만들기” > OAuth 클라이언트 ID 선택
→ 먼저 OAuth 동의 화면 설정 필요

⬇️ OAuth 동의 화면 설정
사용자 유형: 일반적으로 내부 (회사 내부용이면) or 외부

앱 이름, 지원 이메일 등 기본 정보 입력

범위 설정: 아래만 추가

plaintext
복사
편집
https://www.googleapis.com/auth/drive.file
✅ drive.file: 사용자의 Google Drive에서 앱이 만든 파일만 읽고 쓰기 가능
→ 꼭 필요한 최소 권한

⬇️ 클라이언트 ID 생성
애플리케이션 유형: 일반적으로 "웹 애플리케이션" 또는 "데스크톱 앱"

이름 입력 후 생성 → 클라이언트 ID / 클라이언트 비밀 확인 가능






2. 의존성 추가 (build.gradle)

implementation 'com.google.api-client:google-api-client:2.3.0'
implementation 'com.google.oauth-client:google-oauth-client-jetty:1.34.1'
implementation 'com.google.apis:google-api-services-drive:v3-rev20230825-2.0.0'

✅ 예제 코드

java
 
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.File;
import com.google.api.client.http.FileContent;

import java.io.IOException;
import java.util.Collections;

public class GoogleDriveUploader {

    private final Drive driveService;

    public GoogleDriveUploader() throws IOException {
        GoogleCredential credential = GoogleCredential.fromStream(
                getClass().getResourceAsStream("/your-service-account-key.json"))
                .createScoped(Collections.singleton(DriveScopes.DRIVE_FILE));

        driveService = new Drive.Builder(
                credential.getTransport(),
                credential.getJsonFactory(),
                credential
        ).setApplicationName("YourAppName").build();
    }

    /**
     * 특정 부모 폴더 아래에 새 폴더 생성
     */
    public String createSubFolder(String folderName, String parentFolderId) throws IOException {
        File fileMetadata = new File();
        fileMetadata.setName(folderName);
        fileMetadata.setMimeType("application/vnd.google-apps.folder");

        if (parentFolderId != null) {
            fileMetadata.setParents(Collections.singletonList(parentFolderId));
        }

        File folder = driveService.files().create(fileMetadata)
                .setFields("id")
                .execute();

        return folder.getId();
    }

    /**
     * 파일 업로드
     */
    public String uploadFileToFolder(java.io.File uploadFile, String folderId) throws IOException {
        File fileMetadata = new File();
        fileMetadata.setName(uploadFile.getName());
        fileMetadata.setParents(Collections.singletonList(folderId));

        FileContent mediaContent = new FileContent("application/octet-stream", uploadFile);

        File file = driveService.files().create(fileMetadata, mediaContent)
                .setFields("id")
                .execute();

        return file.getId();
    }
}

✅ 사용 예시

java
 
public class DriveTest {
    public static void main(String[] args) throws Exception {
        GoogleDriveUploader uploader = new GoogleDriveUploader();

        // 상위 폴더 ID (Google Drive에서 미리 만들어두거나 루트 폴더 ID 사용)
        String parentFolderId = "1XyzABCdefg1234567890";  // 예시

        // 1. 하위 폴더 생성
        String newFolderId = uploader.createSubFolder("새로운하위폴더", parentFolderId);

        // 2. 해당 폴더에 파일 업로드
        java.io.File file = new java.io.File("C:/temp/sample.txt");
        uploader.uploadFileToFolder(file, newFolderId);

        System.out.println("업로드 완료!");
    }
}

🔐 주의 사항

  • 서비스 계정 사용 시, 해당 계정 이메일을 드라이브 공유 권한에 추가해야 업로드 가능
  • 일반 사용자 OAuth 인증을 쓰려면 별도 토큰 플로우 필요
Posted by yongary
,

Vercel 배포

React.js 2025. 5. 20. 12:55

S3 + CloudFront에 react를 배포하는 것 보다 훨씬 간단하고, 싸게 front를 배포할 수 있다.

 

(1).

✅ Vercel 계정 생성 및 로그인

  • https://vercel.com 접속
  • GitHub 계정으로 로그인 (또는 GitLab, Bitbucket도 가능)

✅ 프로젝트 배포 (Vercel 대시보드)

  1. 로그인 후 + Add New → Project 클릭
  2. GitHub 저장소 목록 중에서 my-react-app 선택
  3. 빌드 설정 화면:
    • Framework Preset: Create React App
    • Build Command: npm run build
    • Output Directory: build
  4. “Deploy” 클릭

✅ GitHub 자동 배포  자동 <-> 수동 변경

1. Vercel 대시보드에서 자동 배포 끄기

  1. Vercel 대시보드 → 해당 프로젝트 클릭
  2. 상단 메뉴: Settings → Git 탭 이동
  3. 아래로 스크롤 →
    "Auto Deploy" 옵션을 끔
    🔲 Automatically deploy commits ← 이걸 체크 해제

이렇게 하면 GitHub에 푸시해도 자동으로 배포되지 않고,
수동으로 “Deploy” 버튼을 눌러야만 배포됩니다.



(2) 🔧 커스텀 도메인에 HTTPS 적용하는 방법 (요약)

  1. Vercel 프로젝트 대시보드에서 "Domains" 클릭
  2. 사용하려는 도메인 추가 (예: example.com)
  3. 도메인 네임서버 또는 CNAME 연결
  4. 연결되면 Vercel이 자동으로 SSL 인증서 발급 및 적용

✅ 예시 화면

 

 

(3)

아마존 Route53 을 활용한 Vercel https 도메인 설정.

 

✅ 설정 방법 (단계별)

1. Route53에서 test.my.com 서브도메인 등록

  • Route53에서 my.com 호스팅 영역을 엽니다
  • 다음 레코드를 추가합니다:
도메인: test.my.com
타입: CNAME
값: cname.vercel-dns.com (또는 Vercel에서 제공하는 고유 값)

주의:

  • test.my.com은 Vercel 프로젝트에 등록된 도메인과 일치해야 합니다
  • Vercel에서 DNS 레코드가 적용되면 Let's Encrypt 인증서가 자동 발급되어 HTTPS 지원됩니다

2. Vercel 프로젝트에 도메인 추가

Vercel → 프로젝트 → Settings > Domains → Add Domain:

  • test.my.com 입력
  • Vercel이 "이 도메인 소유를 증명하세요"라고 안내
  • TXT 레코드 또는 CNAME 값을 보여줌
  • 그 값을 Route53에 등록하면 소유 확인 후 자동 HTTPS 연결 완료

3. 결과적으로 이렇게 됩니다:

  • my.com의 DNS는 전부 Route53에서 관리
  • b.my.com은 ACM 인증서 사용 + AWS에서 서비스
  • test.my.com은 Vercel에서 Let's Encrypt 자동 인증서 사용 + HTTPS 서비스

 

Posted by yongary
,

구글 cloud API 연동

통신 2025. 4. 10. 15:21

1. 사전 준비 사항 

🔐 A. 구글 클라우드 프로젝트 생성 & API 사용 설정

  1. Google Cloud Console 접속
  2. 새 프로젝트 생성
  3. Google Sheets API & Google Drive API 활성화
  4. 서비스 계정(Service Account) 생성
  5. 서비스 계정에 JSON 키 발급

2. 인증 정보 등록

Spring Boot 프로젝트에서 사용할 수 있도록, 발급받은 JSON 파일을 프로젝트 리소스 폴더에 저장 (src/main/resources/credentials.json 등)

✅ 3. Google Sheet 공유 설정

스프레드시트를 서비스 계정 이메일 주소와 공유해야 합니다.

예:
서비스 계정 이메일이 my-service-account@my-project.iam.gserviceaccount.com 이라면
구글 시트 공유 설정에서 이 계정을 편집 권한으로 추가해야 합니다!


✅ 4. 코드 예시: Java에서 한 줄 추가하기

public class GoogleSheetAppender {

    private static final String APPLICATION_NAME = "My Spring Sheet Writer";
    private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
    private static final String CREDENTIALS_FILE_PATH = "/credentials.json"; // resources 경로

    public static Sheets getSheetsService() throws Exception {
        GoogleCredentials credentials;
        try (InputStream in = GoogleSheetAppender.class.getResourceAsStream(CREDENTIALS_FILE_PATH)) {
            credentials = GoogleCredentials.fromStream(in)
                    .createScoped(List.of("https://www.googleapis.com/auth/spreadsheets"));
        }

        return new Sheets.Builder(
                GoogleNetHttpTransport.newTrustedTransport(),
                JSON_FACTORY,
                new HttpCredentialsAdapter(credentials))
                .setApplicationName(APPLICATION_NAME)
                .build();
    }

    public static void appendRow(String spreadsheetId, String range, List<Object> rowData) throws Exception {
        Sheets sheetsService = getSheetsService();

        ValueRange appendBody = new ValueRange().setValues(List.of(rowData));

        sheetsService.spreadsheets().values()
                .append(spreadsheetId, range, appendBody)
                .setValueInputOption("RAW")
                .execute();
    }
}

(사용예제)
GoogleSheetAppender.appendRow(
    "your-spreadsheet-id",     // 시트 ID
    "Sheet1!A1",               // 추가할 범위
    List.of("홍길동", "테스트 메시지", "2025-04-10")
);
 

Posted by yongary
,

yum으로 설치하기 (python 3.6 까지만 가능)
$ sudo yum install -y python3

설치경로:
Python 3.x	/usr/bin/python3
pip3	/usr/bin/pip3


단점은 python버전이 3.6.x정도로 설치된다는 점.

그 후 아래 4번에 .bashrc등에 등록 권장.


==> 또 python3.6에서는 TensorFlow 2.11이하만 지원가능함.......


TensorFlow나 Keras까지 필요하다면 아래 링크 참조.

https://cntechsystems.tistory.com/105


========================= 이하 소스로 설치하는 법===========================

0단계: $free -m 으로 확인해서 swap파일 없으면 생성.

# 1GB 스왑 파일 생성 (필요시 크기 조절 가능)
sudo dd if=/dev/zero of=/swapfile bs=1M count=1024

# 권한 설정
sudo chmod 600 /swapfile

# 스왑 설정
sudo mkswap /swapfile
sudo swapon /swapfile

# 확인
free -m


1단계: 필수 패키지 설치

sudo yum update -y
sudo yum groupinstall "Development Tools" -y
sudo yum install -y gcc make zlib-devel bzip2 bzip2-devel readline-devel sqlite sqlite-devel tk-devel\
     xz xz-devel libffi-devel wget curl git ncurses-devel libuuid-devel gdbm-devel


1-1단계: openssl1.1.1을 특정위치에 설치하기.
($which openssl11  로  1.1.1의 위치가 /usr/bin/openssl11 인지  확인  후) : 이게 잘 안되서, yum에서 openssl11 openssl11-devel 제외하고

cd /usr/src
sudo wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz
sudo tar xzvf openssl-1.1.1w.tar.gz
cd openssl-1.1.1w

sudo ./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared zlib
sudo make -j$(nproc)
sudo make install

 

 

 

2단계: Python 3.x 소스 설치 

cd /usr/src
sudo wget https://www.python.org/ftp/python/3.11.8/Python-3.11.8.tgz
sudo tar xzf Python-3.11.8.tgz
cd Python-3.11.8

export LD_LIBRARY_PATH=/usr/local/ssl/lib
export CPPFLAGS="-I/usr/local/ssl/include"
export LDFLAGS="-L/usr/local/ssl/lib"
sudo ./configure --prefix=/usr/local/python3.11 \
  --with-openssl=/usr/local/ssl \
  --with-openssl-rpath=auto 
      # --enable-optimizations 이 에러나는거 같아서 뺐음.
sudo make -j$(nproc)  (cpu 여러개 이용설정)
     (혹시 make 실패시 sudo make clean 하고 ./configure 부터 다시하면됨)

sudo make install  (혹시 python2유지하고 싶을때는 altinstall)

 

3. 이미 pip3.10도 설치되었을 확률이 높으나, 확인하고 추가설치

/usr/local/python3.11/bin/pip3.11 --version  (로 확인해봄, 없다면 아래 수행)

(불필요)sudo /usr/local/bin/python3.11 -m ensurepip
(불필요)sudo /usr/local/bin/python3.11 -m pip install --upgrade pip



심볼링 링크 만들기( 아래 4번을 할꺼면 생략 가능) 

sudo ln -s /usr/local/bin/python3.10 /usr/bin/python
sudo ln -s /usr/local/bin/pip3.10 /usr/bin/pip

 

 

4. .bashrc에 등록하기

# Python 3.10 경로 등록 (~/.bashrc 편집 등록) 
export PATH="/usr/local/python3.11/bin:$PATH"


alias python="/usr/local/python3.11/bin/python3.11"
alias pip="/usr/local/python3.11/bin/pip3.11"

 

 

마지막으로 $source ~/.bashrc  수행. 

 

 

========= pip install -r ./requirements.txt 설치시 ===============

torch는 gpu 때문에, 설치 안될 수 있으므로

$pip install torch --no-deps 로 설치해보고  
나머지들만 
$pip install -r ./requirements.txt 로 해보기.

(혹시 .py가 잘 안돈다면 $pip install torch  로 설치해야할 수도 있음)
Posted by yongary
,

팀뷰어 teamViewer

기타 2025. 2. 24. 11:37

 

팀뷰어로 원격 pc를 제어하려면
host PC에 teamViewer host를 설치해야한다. (라이선스 있다고 눌러야 뜨네요)

참고링크: https://m.blog.naver.com/terry_daddy/223284278249

Posted by yongary
,

gdd

Mac 2025. 2. 13. 21:09

 

file2를 file1Copied에 붙여서 2개파일을 합치는 명령어.
brew install coreutils를 한후,

$gdd if=file2.bin of=file1Copied.bin bs=1M oflag=append conv=notrunc

Posted by yongary
,

 

 

 

 

 

[array]

리턴하는 함수들: split,map, join(문자열 리턴)

  • .some(v => v==7)   : true/false 리턴
  • .find(v => v > 7)   :첫번째 만족하는 값 반환.   없으면 undefined 반환
  • .findIndex(... ) :  첫번째로 만족하는 index반환.  없으면 -1 반환

 

 

예제:

const toCamelCase = (templCode) => {
    return templCode
        .toLowerCase() // 전체를 소문자로 변환
        .split('_') // "_" 기준으로 분할
        .map((word, index) =>
            index === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1) // 첫 단어는 소문자 유지, 이후 단어는 첫 글자만 대문자
        )
        .join(''); // 배열을 문자열로 합침
}

 

특이함수: slice - 문자열/array 모두 동작.

const str = "JavaScript";

console.log(str.slice(5)); // "cript" (start만 입력하면 끝까지)
console.log(str.slice(0, 4)); // "Java"
console.log(str.slice(-6, -3)); // "Scr" (음수 인덱스 사용 가능)

 

- slice와 splice 차이점

            ( 원본 배열 변경 여부,  반환값,     주요 용도 로 비교)

.splice() ✅ 원본 배열을 변경함 삭제된 요소를 배열로 반환 요소 추가, 삭제, 교체
.slice() ❌ 원본 배열을 변경하지 않음 새로운 배열 반환 배열의 일부 복사
[splice 사용예제] 
const index = users.findIndex(user => user.id === 2);
if (index !== -1) {
    users.splice(index, 1); // 삭제
}

 

[Object]:json

let obj = {a:1, b:2, c:3};

  • Object.keys(obj) => array리턴
  • Object.values(obj) => array리턴
  • let newObj = Object.assign(obj);   //딥 카피.  자식이 array/obj일 경우는 잘안될 수도 있으므로,
    - let newObj = JSON.parse(JSON.stringify(obj));

 

 

 

[Set]

 

let mySet = new Set([1,2]);

let newSet = mySet.add(5); //[1,2,5]; 리턴하는 함수, mySet자체도 바꿈.

mySet.delete(2); //[1,5];           //리턴없는 함수

 

  • array의 각종 좋은 함수들을 사용하기 위해 Set->array로 변환필요한 경우 많음
const mySet = new Set([1, 2, 3, 4, 5]);
const arr = Array.from(mySet);
console.log(arr); // [1, 2, 3, 4, 5]

 

 

[String] javascript도 java처럼 immutable이라서,

  • .replace( , ) : 원본은 변경하지 않음
Posted by yongary
,

chatGpt fine-tuning모델

기타 2025. 2. 4. 13:48

customGPT모델은 openAPI로 호출이 안되는 현상이 있네요.

 

그래서 자체훈련을 시키려면 fine-tuning모델이 맞고,
아래와 같이 생성하면 됩니다.

 

✅ OpenAI Fine-Tuning 모델 생성 방법 (처음부터 끝까지)

OpenAI에서 Fine-Tuning 모델을 생성하는 과정처음부터 끝까지 단계별로 설명하겠습니다. 🚀
Fine-Tuning을 통해 GPT-3.5 Turbo 또는 GPT-4를 특정한 스타일, 문체, 데이터에 맞게 최적화할 수 있습니다.


📌 1. Fine-Tuning 개요

Fine-Tuning이란?

Fine-Tuning이란 기존 OpenAI 모델(GPT-3.5-Turbo, GPT-4)을 사용자의 특정 데이터에 맞춰 재훈련하는 과정입니다.

사용 사례:

  • 특정 고객 응대 방식을 학습 (예: 콜센터 대응, 법률 상담)
  • 특정 전문 용어를 반영한 챗봇 생성 (예: 의학, 법률, 금융)
  • 기존 데이터 기반으로 맞춤형 답변 생성 (예: 기업 내부 문서 요약)

📌 2. Fine-Tuning에 필요한 사전 준비

✅ OpenAI API 키 발급

Fine-Tuning을 하려면 OpenAI API 키가 필요합니다.

  1. OpenAI API 대시보드에 접속
  2. "Create API Key"를 클릭하여 새로운 API 키 생성
  3. API 키를 복사하여 보관 (한 번만 표시됨)

📌 3. Fine-Tuning 데이터 준비

Fine-Tuning을 하려면 **훈련 데이터(training data)**를 만들어야 합니다.
OpenAI는 JSONL(JSON Lines) 형식의 데이터를 요구합니다.

✅ Fine-Tuning 데이터 형식

Fine-Tuning 데이터는 "messages" 형식으로 작성됩니다.

 

(json형식 예제)
{"messages": [
    {"role": "system", "content": "너는 친절한 금융 상담사야."},
    {"role": "user", "content": "대출 금리가 어떻게 되나요?"},
    {"role": "assistant", "content": "현재 대출 금리는 연 3.5%입니다."}
]}


jsonl형식.필요

{"messages": [{"role": "system", "content": "너는 친절한 채권 전문가야."}, {"role": "user", "content": "채권 회수 절차가 어떻게 되나요?"}, {"role": "assistant", "content": "채권 회수 절차는 1) 채무자 연락 2) 독촉장 발송 3) 법적 조치 순으로 진행됩니다."}]}
{"messages": [{"role": "system", "content": "너는 친절한 채권 전문가야."}, {"role": "user", "content": "부실채권을 매각할 수 있나요?"}, {"role": "assistant", "content": "네, 부실채권은 채권 매입 기관을 통해 매각할 수 있습니다."}]}

 

 
 

📌 JSONL 파일을 만들고, .jsonl 확장자로 저장해야 합니다.


📌 4. Fine-Tuning 데이터 업로드

OpenAI API를 사용하여 준비한 JSONL 파일을 업로드합니다.

✅ JSONL 파일 업로드 (파일 ID 얻기)

curl -X POST "https://api.openai.com/v1/files" \
  -H "Authorization: Bearer YOUR_OPENAI_API_KEY" \
  -H "Content-Type: multipart/form-data" \
  -F "file=@training_data.jsonl" \
  -F "purpose=fine-tune"



응답예시:
{
  "id": "file-abc123",
  "object": "file",
  "bytes": 12345,
  "created_at": 1700000000,
  "filename": "training_data.jsonl",
  "purpose": "fine-tune"
}
 
 

"id" 값을 저장 (file-abc123) → Fine-Tuning을 수행할 때 사용됩니다.


 

📌 5. Fine-Tuning 실행

이제 Fine-Tuning을 실행하여 맞춤형 모델을 학습시킵니다.

✅ Fine-Tuning 모델 생성 요청

curl -X POST "https://api.openai.com/v1/fine_tuning/jobs" \
  -H "Authorization: Bearer YOUR_OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-3.5-turbo",
    "training_file": "file-abc123",
    "hyperparameters": {
      "n_epochs": 3
    }
  }'


응답 예시:

{
  "id": "ftjob-xyz789",
  "object": "fine_tuning.job",
  "model": "gpt-3.5-turbo",
  "training_file": "file-abc123",
  "status": "running",
  "created_at": 1700000000
}
 

✅ "id" (ftjob-xyz789)가 Fine-Tuning 작업의 ID입니다.
✅ 훈련이 완료되면 새로운 모델 ID가 생성됩니다.


📌 6. Fine-Tuning 진행 상태 확인

Fine-Tuning이 얼마나 진행되었는지 확인하려면 아래 명령어를 실행합니다.

curl -X GET "https://api.openai.com/v1/fine_tuning/jobs/ftjob-xyz789" \
  -H "Authorization: Bearer YOUR_OPENAI_API_KEY"

응답 예시: 
{
  "id": "ftjob-xyz789",
  "status": "succeeded",
  "fine_tuned_model": "ft:gpt-3.5-turbo:your-org:custom-model"
}
 
 

Fine-Tuning이 완료되면 "fine_tuned_model" 값이 생성됩니다.
✅ "ft:gpt-3.5-turbo:your-org:custom-model"이 새로운 모델 ID입니다.


📌 7. Fine-Tuned 모델 사용 (API 호출)

Fine-Tuning이 완료되면 새로운 모델 ID를 사용하여 API 호출이 가능합니다.

✅ Fine-Tuning 모델 호출 예제

curl https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "ft:gpt-3.5-turbo:your-org:custom-model",
    "messages": [{"role": "user", "content": "채권 회수 절차가 어떻게 되나요?"}],
    "temperature": 0.7
  }'​

📌 Fine-Tuned 모델이 기존 GPT-3.5보다 더 정확하게 원하는 답변을 생성합니다.


📌 8. Fine-Tuned 모델 삭제 (필요할 경우)

Fine-Tuning 모델을 더 이상 사용하지 않으려면 삭제할 수 있습니다.

✅ 모델 삭제 요청

bash
복사편집
curl -X DELETE "https://api.openai.com/v1/models/ft:gpt-3.5-turbo:your-org:custom-model" \ -H "Authorization: Bearer YOUR_OPENAI_API_KEY"

📌 이 작업은 영구적이므로 주의해야 합니다.


🚀 결론 (Fine-Tuning 요약)

1) JSONL 형식으로 Fine-Tuning 데이터 준비
2) API를 사용하여 JSONL 파일 업로드 (/v1/files)
3) Fine-Tuning 실행 (/v1/fine_tuning/jobs)
4) 모델 학습이 완료되면 Fine-Tuning 모델 ID 확인
5) Fine-Tuned 모델을 API에서 호출하여 사용
6) 필요할 경우 Fine-Tuned 모델 삭제 가능

Posted by yongary
,

(참고: open API를 이용한 custom GPT는 불가능함, 내부 api로 외부데이터 연동은 가능)

1. OpenAI 플랫폼에서 Custom GPT 생성하기

Custom GPT를 설정하려면 OpenAI 플랫폼에서 새로운 Custom GPT를 생성해야 합니다.

1.1. OpenAI 플랫폼 접속

  1. OpenAI Custom GPT 페이지로 이동
  2. 로그인 후 상단의 My GPTs 메뉴 클릭
  3. "Create a GPT" 버튼을 클릭하여 새로운 Custom GPT 생성 시작

2. Custom GPT 설정 과정

Custom GPT는 OpenAI의 Guided Setup을 통해 설정할 수 있습니다.
이 과정에서는 GPT의 성격, 역할, 학습 데이터, API 기능 설정을 조정할 수 있습니다.

2.1. 기본 정보 입력

  • GPT의 이름 설정 → (예: "채권 전문가 GPT")
  • 설명 입력 → (예: "이 GPT는 채권 회수 및 법률 상담을 위한 맞춤형 AI입니다.")

2.2. 역할 및 동작 방식 정의 (Instructions)

Custom GPT의 동작 방식을 정의하는 가장 중요한 과정입니다.

✅ "Instructions" 탭에서 설정할 내용

  1. Assistant Instructions
    → GPT가 어떻게 행동해야 하는지 설명
  2. text
    복사편집
    당신은 금융 및 법률 전문가로서 채권 회수와 관련된 법률 상담을 제공합니다. 사용자의 질문에 대해 구체적인 법률 조항을 참조하며, 금융 기관의 실무적인 조언을 제공합니다.
  3. User Instructions
    → 사용자가 Custom GPT를 어떻게 사용할지 안내
  4. text
    복사편집
    질문을 작성할 때, 관련된 법률 조항이나 금융 용어를 포함해 주세요. 예시: "대여금 반환 청구 소송을 진행하려면 어떤 절차를 따라야 하나요?"

2.3. 추가적인 데이터 업로드 (Knowledge)

사용자가 Custom GPT에 대한 추가 정보를 제공할 수도 있습니다.

  • FAQ 데이터 업로드 (CSV, JSON 형식)
  • PDF, 텍스트 문서 업로드 (예: 회사 내부 지침, 법률 문서 등)

예시:

  • 금융 기관의 채권 회수 정책을 포함한 PDF 파일 업로드
  • 법률 상담 시 자주 묻는 질문(FAQ) 데이터 업로드

2.4. API 호출 및 도구 사용 설정 (Actions)

Custom GPT는 API 호출 및 외부 도구(Functions)와 연동할 수도 있습니다.

  • 예를 들어, Spring Boot API, Google 검색, 데이터베이스 API 등을 호출 가능

예제:

{
  "name": "채권 회수 API",
  "description": "고객의 채권 정보를 조회하고 법률 절차를 안내합니다.",
  "parameters": {
    "채권자명": {
      "type": "string",
      "description": "채권자의 이름"
    },
    "채무자명": {
      "type": "string",
      "description": "채무자의 이름"
    }
  }
}

이 기능을 활용하면 API를 직접 호출하여 최신 정보를 제공하는 Custom GPT를 만들 수 있습니다.


3. Custom GPT를 API에서 호출하는 방법

Custom GPT를 OpenAI API에서 사용하려면, API에서 model 값을 Custom GPT ID로 변경하면 됩니다.

✅ Custom GPT API 호출 예제 (Java)

import okhttp3.*;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.IOException;

@Service
public class OpenAICustomService {
    private static final String API_URL = "https://api.openai.com/v1/chat/completions";
    private final OkHttpClient client = new OkHttpClient();

    @Value("${openai.api.key}")
    private String apiKey;

    @Value("${openai.custom.gpt.id}") // Custom GPT ID (예: gpt-4-custom-XXXXX)
    private String customGptId;

    public String getCustomGptResponse(String userInput) throws IOException {
        String json = "{"
                + "\"model\":\"" + customGptId + "\","
                + "\"messages\":[{\"role\":\"user\",\"content\":\"" + userInput + "\"}],"
                + "\"temperature\":0.7"
                + "}";

        RequestBody body = RequestBody.create(json, MediaType.get("application/json"));

        Request request = new Request.Builder()
                .url(API_URL)
                .header("Authorization", "Bearer " + apiKey)
                .header("Content-Type", "application/json")
                .post(body)
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected code " + response);
            }

            ObjectMapper mapper = new ObjectMapper();
            JsonNode rootNode = mapper.readTree(response.body().string());
            return rootNode.get("choices").get(0).get("message").get("content").asText();
        }
    }
}

설정 완료 후 API에서 Custom GPT를 호출하면, 사전에 설정한 역할, 동작 방식, 지식 등을 반영한 응답을 반환합니다.


4. 결론

OpenAI API에서 Custom GPT를 사용하려면 먼저 OpenAI 플랫폼에서 설정이 필요합니다.
사전 설정을 통해 특정한 업무(법률, 금융, IT 등)에 최적화된 GPT를 만들 수 있습니다.
API에서 model 값을 Custom GPT ID로 변경하면 사전 설정이 반영된 맞춤형 응답을 받을 수 있습니다.

 

 

5. customGptId 조회 방식

curl https://api.openai.com/v1/models \
  -H "Authorization: Bearer YOUR_OPENAI_API_KEY"

 

 

 

6. custom GPT 구성(Config)

- 우측 큰화면의 상단에 GPT제목을 누르고, 편집을 누른 후, "구성"탭에서 설정.

인증형식은 API key로 하고, https://platform.openai.com/ 에서 카드설정+발급받은 키를 이용하게 되며,
인증방식은 기본이 아닌->  "Bearer" 로 설정하는게 편함.

 

심플 get용 스키마:

{
  "openapi": "3.1.0",
  "info": {
    "title": "간단한 GET API",
    "description": "GET 요청을 받아 텍스트를 반환하는 API",
    "version": "v1.0.0"
  },
  "servers": [
    {
      "url": "https://api.example.com"
    }
  ],
  "paths": {
    "/hello": {
      "get": {
        "summary": "텍스트 반환",
        "operationId": "getHello",
        "responses": {
          "200": {
            "description": "성공적인 응답",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "example": "Hello, GPT!"
                }
              }
            }
          }
        }
      }
    }
  }
}

 

 

post용 스키마:

{
  "openapi": "3.1.0",
  "info": {
    "title": "채권 조회 API",
    "description": "채권자 및 채무자 정보를 입력하면 채권 상태를 반환합니다.",
    "version": "v1.0.0"
  },
  "servers": [
    {
      "url": "https://api.example.com"
    }
  ],
  "paths": {
    "/getDebtStatus": {
      "post": {
        "summary": "채권 상태 조회",
        "operationId": "getDebtStatus",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DebtRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "채권 상태 응답",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DebtResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "DebtRequest": {
        "type": "object",
        "properties": {
          "채권자명": {
            "type": "string",
            "description": "채권자의 이름"
          },
          "채무자명": {
            "type": "string",
            "description": "채무자의 이름"
          }
        },
        "required": ["채권자명", "채무자명"]
      },
      "DebtResponse": {
        "type": "object",
        "properties": {
          "결과": {
            "type": "string",
            "description": "응답 상태 (성공 또는 실패)"
          },
          "채권 상태": {
            "type": "string",
            "description": "채권의 현재 상태 (예: 연체, 회수 완료 등)"
          },
          "회수 가능 금액": {
            "type": "number",
            "description": "현재 회수 가능한 금액 (원화 기준)"
          }
        }
      }
    }
  }
}
Posted by yongary
,

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 앱 생성하기 : 스크린샷은 https://zzang9ha.tistory.com/400#google_vignette 참고

  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" 섹션에서 앱이 필요로 하는 권한을 설정합니다.
    • 예를 들어: 특정 User가 로그인해서 마치 bot인것 처럼 메시지를  보낼때
      "User Token Scopes" 에 아래 2개 추가  필수. 
      • chat:write는 반드시 추가
      • chat::read Public 도 꼭 추가
    • 필요한 범위를 선택하고 추가합니다.
  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
,