코딩, 개발에 대한 기록 저장소

python apscheduler 의 job 관리(Job Store)


## Job Store

apscheduler에서 Job Store는 스케줄러가 관리하는 작업(job)에 대한 모든 정보를 저장하고 유지하는 역할을 합니다. 여기에는 다음과 같은 정보가 포함됩니다.

- 실행할 함수 또는 콜러블 객체: 어떤 작업을 실행해야 하는지에 대한 정보
- 트리거 (Trigger): 작업이 언제, 얼마나 자주 실행되어야 하는지에 대한 스케줄링 규칙
- 작업의 상태: 작업이 일시 중지되었는지, 다음 실행 시간은 언제인지 등의 상태 정보
- 작업의 설정: 작업 ID, 실행 시 동시 실행 허용 여부 등 추가적인 설정 정보

기본적으로 apscheduler는 MemoryJobStore라는 메모리 기반의 Job Store를 사용합니다. 이 저장소는 애플리케이션이 실행되는 동안에만 작업 정보를 유지합니다.

- 장점: 설정이 간단하고 빠릅니다. 별도의 데이터베이스 설치나 설정이 필요하지 않습니다.
- 단점: 애플리케이션이 종료되면 모든 작업 정보가 사라집니다. 따라서 재시작 후에는 이전에 예약된 작업들이 유지되지 않습니다.

### 영구 저장소의 필요성

애플리케이션이 재시작되거나 여러 인스턴스(예: 웹 애플리케이션의 여러 서버)에서 스케줄러를 공유해야 하는 경우에는 메모리 기반의 저장소만으로는 충분하지 않습니다. 이때 영구 저장소를 사용하여 작업 정보를 데이터베이스와 같은 외부 저장소에 저장해야 합니다. 영구 저장소를 사용하면 다음과 같은 이점을 얻을 수 있습니다.

- 애플리케이션 재시작 후에도 작업 유지: 애플리케이션이 종료되었다가 다시 시작되더라도 이전에 예약된 작업들이 그대로 유지됩니다.
- 스케줄러 상태 공유: 여러 프로세스 또는 서버에서 동일한 데이터베이스를 공유하여 스케줄러 상태를 동기화하고, 작업을 중복 실행하는 것을 방지할 수 있습니다. (이를 위해서는 잠금(locking) 설정이 필요할 수 있습니다.)

### 주요 영구 저장소

apscheduler는 다양한 데이터베이스 백엔드를 지원하는 여러 영구 저장소를 제공합니다.

#### SQLAlchemyJobStore
SQLAlchemy ORM을 사용하여 다양한 SQL 데이터베이스 (SQLite, PostgreSQL, MySQL, Oracle 등)에 작업 정보를 저장합니다. 가장 널리 사용되고 강력한 옵션 중 하나입니다.

```python
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore

jobstores = {
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite') # SQLite 데이터베이스 사용 예시
}

scheduler = BackgroundScheduler(jobstores=jobstores)
```

#### MongoDBJobStore
MongoDB NoSQL 데이터베이스에 작업 정보를 저장합니다.

```python
from apscheduler.jobstores.mongodb import MongoDBJobStore
from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')
jobstores = {
    'default': MongoDBJobStore(database='apscheduler', client=client)
}

scheduler = BackgroundScheduler(jobstores=jobstores)
```

#### RedisJobStore
Redis 인-메모리 데이터 구조 저장소에 작업 정보를 저장합니다. 고성능 스케줄링에 적합할 수 있습니다.

```python
from apscheduler.jobstores.redis import RedisJobStore
import redis

redis_client = redis.Redis(host='localhost', port=6379, db=0)
jobstores = {
    'default': RedisJobStore(client=redis_client)
}

scheduler = BackgroundScheduler(jobstores=jobstores)
```

### 저장소를 선택 고려사항

- 간단한 테스트 또는 애플리케이션 수명이 짧은 경우: MemoryJobStore로 충분할 수 있음
- 애플리케이션 재시작 후에도 작업을 유지해야 하는 경우: SQLAlchemyJobStore, MongoDBJobStore, RedisJobStore 중 하나를 선택
- SQL 데이터베이스를 이미 사용하고 있거나 관계형 데이터 모델이 적합한 경우: SQLAlchemyJobStore
- NoSQL 데이터베이스를 선호하거나 확장성이 중요한 경우: MongoDBJobStore
- 높은 성능과 빠른 읽기/쓰기가 필요한 경우: RedisJobStore. 다만, Redis는 기본적으로 휘발성 데이터베이스이므로 영구 저장을 위한 별도의 설정을 고려해야 함



WSL2 메모리 제한 설정



### 메모리 사용량 확인
```
free -h
```

### 메모리 제한

- C:\Users\<사용자>의 경로로 이동
- windows+R 키를 누른 뒤 %USERPROFILE%를 입력하면 해당 경로로 빠르게 이동 가능
- 경로에 .wslconfig 파일을 생성하고 다음과 같은 내용을 입력

```
[wsl2]
memory=16GB
swap=4GB
```

- memory: VM 메모리 16GB로 제한
- swap: swap 메모리 4GB로 제한

다른 config는 [Windows의 공식 문서](https://github.com/MicrosoftDocs/wsl/blob/main/WSL/wsl-config.md) 참고하고 WSL 를 재시작하고 잘 적용되었는지 확인

### WSL2 재시작 명령어
```
wsl --shutdown
```



rabbitmq 큐 생성 개수 제한

RabbitMQ는 큐 생성 개수에 대해 제한은 없습니다. 다만 시스템 리소스와 RabbitMQ 서버의 설정에 따라 실질적인 제한이 존재합니다.

큐 생성 개수에 대한 고려 사항

리소스 소비:

  • 각 큐는 메모리, CPU 및 디스크 리소스를 소비하므로 큐가 많을수록 더 많은 리소스를 소비합니다.
  • RabbitMQ를 실행하는 시스템이 생성하려는 큐의 수를 처리할 수 있는 충분한 리소스를 가지고 있는지 확인 필요합니다.

설정 제한:

  • channel_max 매개변수는 연결당 최대 채널 수를 제어합니다. 기본값은 2047로, 이전 기본값인 65535보다 안전합니다.
  • 연결당 더 많은 채널을 지원하려면 RabbitMQ 설정 파일에서 channel_max 매개변수를 조정할 수 있습니다.

성능 고려:

  • 은 수의 큐는 RabbitMQ 서버의 성능에도 영향을 미칠수 있습니다.

참고 자료


맥에서 업로드한 한글 파일을 linux 파일에서 못 읽는 이유


다음의 "한"과 "한"은 시각적으로는 동일해 보이지만, 내부적으로는 다른 Unicode 인코딩을 사용하여 표현됨.

### "한" (U+D55C):

이는 단일 유니코드 문자로, "한"의 조합을 하나의 문자로 결합한 것
유니코드 값: U+D55C
UTF-8 인코딩: E D5 9C

### "한" (U+1112 U+1161 U+11AB):

이는 세 개의 개별 유니코드 문자로 구성된 조합형 문자
ᄒ (초성): U+1112
ᅡ (중성): U+1161
ᆫ (종성): U+11AB
UTF-8 인코딩: 각각 E1 84 92, E1 85 A1, E1 86 AB로 인코딩됨

### 문제 해결
이 차이는 Unicode 정규화(Normalization)를 통해 해결할 수 있음.

NFC (Normalization Form C): 조합형 문자들을 가능한 한 결합하여 하나의 문자로 만듦
NFD (Normalization Form D): 조합형 문자들을 가능한 한 분해하여 각각의 구성 요소로 나눔

아래 예제 코드 참고
```python
import unicodedata

# 조합형 문자
composed = '한'
# 분해된 문자
decomposed = '한'

# NFC 정규화
composed_nfc = unicodedata.normalize('NFC', decomposed)
# NFD 정규화
decomposed_nfd = unicodedata.normalize('NFD', composed)

print(f"Composed: {composed}, NFC: {composed_nfc}")
print(f"Decomposed: {decomposed}, NFD: {decomposed_nfd}")

# 출력 결과
# Composed: 한, NFC: 한
# Decomposed: 한, NFD: 한
```



프로그램 개발시 행동을 지칭하는 단어 비교


하나의 행동을 지칭하는 단어로는 **"Act"**, **"Task"**, **"Process"**, **"Event"**, **"Activity"**, **"Procedure"** 등이 있습니다. 각각의 의미와 차이를 살펴보겠습니다.  

### **1. Act** (행위, 행동)  
- 특정한 목적을 가진 하나의 **행위** 또는 **결정적인 행동**을 의미합니다.  
- 예시:  
  - *The act of pressing a button* (버튼을 누르는 행위)  
  - *An act of kindness* (친절한 행동)  

### **2. Task** (작업, 과업)  
- **완료해야 하는 특정한 일**을 의미하며, 일반적으로 작은 단위의 작업을 지칭합니다.  
- 예시:  
  - *Complete a task* (작업을 완료하다)  
  - *Assign a task* (작업을 할당하다)  

### **3. Process** (과정, 절차)  
- **일련의 단계로 이루어진 행동의 흐름**을 의미하며, 하나의 행동이라기보다는 연속적인 절차를 강조할 때 사용합니다.  
- 예시:  
  - *The process of authentication* (인증 과정)  
  - *A learning process* (학습 과정)  

### **4. Event** (사건, 이벤트)  
- **특정 시점에서 발생하는 하나의 사건**을 의미하며, 소프트웨어 개발에서는 주로 UI 이벤트나 시스템 이벤트를 의미합니다.  
- 예시:  
  - *Click event* (클릭 이벤트)  
  - *A scheduled event* (예약된 이벤트)  

### **5. Activity** (활동, 행위)  
- **특정한 목표를 가지고 수행하는 행동**을 의미하며, 비교적 범위가 넓습니다.  
- 예시:  
  - *User activity* (사용자 활동)  
  - *Physical activity* (신체 활동)  

### **6. Procedure** (절차, 순서)  
- **어떤 행동을 수행하는 공식적인 방법이나 절차**를 의미합니다.  
- 예시:  
  - *Safety procedure* (안전 절차)  
  - *Login procedure* (로그인 절차)  

### **어떤 단어를 선택해야 할까?**  
|  | **의미** | **사용 예시** |
|---|---|---|
| **Act** | 단순한 행동 | 문을 열다, 버튼을 누르다 |
| **Task** | 할당된 작업 | 문서를 작성하는 작업 |
| **Process** | 여러 단계를 거치는 과정 | 로그인 과정, 데이터 처리 |
| **Event** | 특정 시점에서 발생하는 사건 | 클릭 이벤트, 시스템 알림 |
| **Activity** | 지속적으로 수행하는 활동 | 사용자 활동 기록 |
| **Procedure** | 정해진 방법에 따라 수행하는 절차 | 보안 절차, 데이터 백업 절차 |

용도에 따라 적절한 변수명을 사용하면 선택하면 됩니다!



WSL 리눅스 설치 경로 변경 방법


WSL 설치 경로를 C 드라이브에서 D 드라이브로 이동하는 방법 설명

### 기본 설치 경로

`C:\Users\<유저명>\AppData\Local\Packages\<리죽스이름>\localState` 아래 `ext4.vhdx` 파일로 설치됨

### export
```
# wsl --export <리눅스 이름> <파일 경로>
wsl --export Ubuntu-18.04 D:\ubuntu-18-04.tar
```

### 기존 버전 삭제
```
# wsl --unregister <리눅스 이름>
wsl --unregister Ubuntu-18.04
```

### import
```
# wsl --import <설정해 줄 리눅스 이름> <설치경로> <추출한 파일 경로>
wsl --import Ubuntu-18.04 D:\wsl\ubuntu-18-04\ D:\ubuntu-18-04.tar
```

### 기본 사용자 설정
처음 실행하면 root 로 실행됨. wsl 안에서 wsl.conf 파일 생성후 아래 옵션 추가
```
$ touch /etc/wsl.conf
$ vi /etc/wsl.conf
[user]
default=<userid>
```

wsl 재시작



git remote branch 삭제 및 상태 업데이트


### remote branch 삭제
```
# git push <원격 저장소 이름> -d <원격 브랜치 이름>
$ git push origin -d test_branch
```

### remote branch 상태 업데이트
- A 에서 remote branch 를 삭제했지만 B 에서는 remote branch 가 계속 있는 것처럼 보이는 경우가 있음
- 이럴땐 아래 명령으로 remote branch 정보를 리셋 혹은 갱신 해야 함

```
# git remote prune <원격 저장소 이름>
$ git remote prune origin
```

### local branch 삭제
```
# git branch -D <로컬 브랜치 이름>
$ git branch -D test_branch
```

### 요점
- remote branch 삭제는 git push
- local branch 삭제는 -D