빌드가 안되어 버린다.
잘라내도 된다고 판단해서 코드가 잘라내어 지면(Trimmed) 실행중에 실제로
난독화가 되는것이 아니고 중간언어가 함께 들어가기 때문에 보안이 좋다는
착각을 해서는 안된다.
it블로그
초기 하드웨어 점검: (쿨링패드장착, IO핀, 하이닉스M.2 SSD설치)
맥주소를 확인하여 ipv4주소로 접속한다.
명령어 (nmap -p 22 --open 192.168.0.1/24)
SSH 포트(22) 활성화 확인후 putty로 접속확인
sftp://[기기아이피]로 파일질라를 통해 포트(22) 접속확인
사용자명(U): orangepi
비밀번호(P): orangepi
명령어(sudo orangepi-config)로 → 타임존, 로케일, 키보드설정
Personal settings 하위메뉴에서
Timezone: Asia / Seoul 설정
Locales: en_US.UTF-8, ko_KR.EUC-KR, ko_KR.UTF-8 설정
KeyBoard: 폰트설정이후 korean 101/104로 변경
기기 업데이트 및 폴더생성과 바로가기 만들기
sudo apt update (저장소 업데이트)
sudo apt upgrade (업그레이드 시간오래걸림)
sudo apt update (업데이트 다시)
mkdir temp (temp폴더 생성)
mkdir MainApp (MainApp폴더 생성)
mkdir SubApp (SubApp폴더 생성)
touch ~/MainApp/start_main.sh (MainApp 바로가기 만들기)
touch ~/SubApp/start_sub.sh (SubApp 바로가기 만들기)
chmod +x ~/MainApp/start_main.sh (MainApp 바로가기 실행가능 하도록 변경)
chmod +x ~/SubApp/start_sub.sh (SubApp 바로가기 실행가능 하도록 변경)
X-Server 그래픽 엔진과 경량 윈도우매니저 OpenBox 설치
# 패키지 저장소 새로고침
sudo apt update
# X11, 디스플레이 매니저, 오픈박스 설치
sudo apt install xorg lightdm openbox -y
한글폰트 및 한글입력기(fcitx5) 설치 및 환경변수
# 한글 폰트 및 fcitx5 입력기 패키지 설치
sudo apt install fonts-nanum fonts-noto-cjk im-config fcitx5 fcitx5-hangul fcitx5-config-qt fcitx5-frontend-gtk2 fcitx5-frontend-gtk3 fcitx5-frontend-qt5 -y
# 입력기 프레임워크를 fcitx5로 지정
im-config -n fcitx5
# 리눅스 시스템 인코딩 환경에 한국어 언어 팩 생성 및 고정
sudo locale-gen ko_KR.UTF-8
sudo update-locale LANG=ko_KR.UTF-8 LANGUAGE=ko_KR:ko LC_ALL=ko_KR.UTF-8
# LightDM 로그인 세션 환경 변수 설정
nano ~/.xprofile 에 아래 내용 추가 (없으면 새로 생성)
export LANG=ko_KR.UTF-8
export LC_ALL=ko_KR.UTF-8
export XMODIFIERS=@im=fcitx
export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
# 환경변수 파일 수정처리
sudo nano /etc/environment
# 제일 하단에 다음을 입력후 저장
LANG=ko_KR.UTF-8
LANGUAGE=ko_KR:ko
LC_ALL=ko_KR.UTF-8
한글입력기(fcitx5) 프로파일을 구성한다.
# 프로파일 내용진입 하여 다음 내용으로 수정
sudo nano ~/.config/fcitx5/profile
[Groups/0]
# Group Name
Name=Default
# Layout
Default Layout=us
# Default Input Method
DefaultIM=keyboard-us
[Groups/0/Items/0]
# Name
Name=keyboard-us
# Layout
Layout=
[Groups/0/Items/1]
# Name
Name=hangul
# Layout
Layout=
[GroupOrder]
0=Default
원격지 화면 통제를 위해 VNC 서버를 설치하고 비밀번호를 고정
# x11vnc 패키지 엔진 설치
sudo apt install x11vnc -y
# 계정 홈 디렉토리 내부에 보안 인증서 폴더 생성
mkdir -p ~/.vnc
# 비밀번호 생성 및 저장 (패스워드 2회입력후, 저장여부 Y)
x11vnc -storepasswd ~/.vnc/passwd
자동 로그인 orangepi로 로그인을 락인 시킴
# 구성 파일 에디터 열기
sudo nano /etc/lightdm/lightdm.conf
# 아래 내용으로 설정한후 저장
[Seat:*]
autologin-user=orangepi
autologin-user-timeout=0
user-session=openbox
콘솔 기반 구동 시 가상 모니터를 생성하여 Headless 상태에서도 해상도 및 VNC 유지
# 가상 그래픽 드라이버 패키지 설치
sudo apt install xserver-xorg-video-dummy -y
# X11 가상 디스플레이 구성 파일 생성
sudo nano /etc/X11/xorg.conf.d/99-dummy.conf
# 아래 설정 내용 복사 후 저장 (1280x1024 HD 지정)
Section "Device"
Identifier "DummyDevice"
Driver "dummy"
EndSection
Section "Monitor"
Identifier "DummyMonitor"
HorizSync 28.0-80.0
VertRefresh 48.0-75.0
EndSection
Section "Screen"
Identifier "DummyScreen"
Device "DummyDevice"
Monitor "DummyMonitor"
DefaultDepth 24
SubSection "Display"
Depth 24
Modes "1280x1024"
EndSubSection
EndSection
오픈박스가 로드될때 순차적으로 자동 실행 (이후 재부팅: sudo reboot)
# 오픈박스 스타트업 구성 파일 폴더 및 파일 생성
mkdir -p ~/.config/openbox
sudo nano ~/.config/openbox/autostart
[ 아래 내용으로 설정한 후 저장 ]
# 1. 마우스 커서 기본 모양 설정
xsetroot -cursor_name left_ptr &
# 2. VNC 원격 서버 백그라운드 실행
x11vnc -auth guess -forever -loop -noxdamage -repeat -rfbauth /home/orangepi/.vnc/passwd -rfbport 5900 -shared &
# 3. 절전모드 사용안함
xset s off &
xset -dpms &
xset s noblank &
# 4. fcitx5 한글 입력기 데몬 실행
fcitx5 -d &
# 5. 시스템 안정화 후 메인 앱 자동 구동 (1초 대기 후 실행)
sleep 1
/home/orangepi/MainApp/start_main.sh &
오픈박스가 로드될때 마우스와 키보드로 처리할 내용
# 시스템 공용 폴더에서 원본파일을 유저 폴더로 복사
cp /etc/xdg/openbox/rc.xml ~/.config/openbox/rc.xml
# 설정파일을 아래의 xml로 변경처리
# 타이핑 보다는 파일질라로 복사처리 권장
[ 아래의 사항으로 변경 ]
<?xml version="1.0" encoding="UTF-8"?>
<openbox_config xmlns="http://openbox.org/3.4/rc" xmlns:xi="http://www.w3.org/2001/XInclude">
<resistance><strength>10</strength><screen_edge_strength>20</screen_edge_strength></resistance>
<focus><focusNew>yes</focusNew><followMouse>no</followMouse><focusLast>yes</focusLast><underMouse>no</underMouse><focusDelay>200</focusDelay><raiseOnFocus>no</raiseOnFocus></focus>
<placement><policy>Smart</policy><center>yes</center><monitor>Primary</monitor><primaryMonitor>1</primaryMonitor></placement>
<theme>
<name>Clearlooks</name><titleLayout>NLIMC</titleLayout><keepBorder>yes</keepBorder><animateIconify>yes</animateIconify>
<font place="ActiveWindow"><name>sans</name><size>8</size><weight>bold</weight><slant>normal</slant></font>
<font place="InactiveWindow"><name>sans</name><size>8</size><weight>bold</weight><slant>normal</slant></font>
<font place="MenuHeader"><name>sans</name><size>9</size><weight>normal</weight><slant>normal</slant></font>
<font place="MenuItem"><name>sans</name><size>9</size><weight>normal</weight><slant>normal</slant></font>
</theme>
<desktops>
<number>1</number><firstdesk>1</firstdesk><names></names><popupTime>0</popupTime>
</desktops>
<resize><drawContents>yes</drawContents><popupShow>Nonpixel</popupShow><popupPosition>Center</popupPosition></resize>
<margins><top>0</top><bottom>0</bottom><left>0</left><right>0</right></margins>
<keyboard>
<chainQuitKey>C-g</chainQuitKey>
<keybind key="W-d"><action name="ToggleShowDesktop"/></keybind>
<keybind key="A-F4"><action name="Close"/></keybind>
<keybind key="A-Escape"><action name="Lower"/><action name="FocusToBottom"/><action name="Unfocus"/></keybind>
<keybind key="A-space"><action name="ShowMenu"><menu>client-menu</menu></action></keybind>
<keybind key="Print"><action name="Execute"><command>scrot</command></action></keybind>
</keyboard>
<mouse>
<dragThreshold>1</dragThreshold><doubleClickTime>500</doubleClickTime><screenEdgeWarpTime>0</screenEdgeWarpTime><screenEdgeWarpMouse>false</screenEdgeWarpMouse>
<context name="Frame">
<mousebind button="A-Left" action="Press"><action name="Focus"/><action name="Raise"/></mousebind>
<mousebind button="A-Left" action="Drag"><action name="Move"/></mousebind>
<mousebind button="A-Right" action="Drag"><action name="Resize"/></mousebind>
</context>
<context name="Titlebar">
<mousebind button="Left" action="Drag"><action name="Move"/></mousebind>
<mousebind button="Left" action="DoubleClick"><action name="ToggleMaximize"/></mousebind>
</context>
<context name="Client">
<mousebind button="Left" action="Press"><action name="Focus"/><action name="Raise"/></mousebind>
<mousebind button="Middle" action="Press"><action name="Focus"/><action name="Raise"/></mousebind>
<mousebind button="Right" action="Press"><action name="Focus"/><action name="Raise"/></mousebind>
</context>
<context name="Desktop">
<mousebind button="Left" action="Press"><action name="Focus"/><action name="Raise"/></mousebind>
<mousebind button="Right" action="Press"><action name="Focus"/><action name="Raise"/></mousebind>
</context>
<context name="Root">
<mousebind button="Right" action="Press"><action name="ShowMenu"><menu>root-menu</menu></action></mousebind>
</context>
</mouse>
<menu><file>menu.xml</file><hideDelay>200</hideDelay><middle>no</middle><submenuShowDelay>100</submenuShowDelay><submenuHideDelay>400</submenuHideDelay><showIcons>yes</showIcons><manageDesktops>no</manageDesktops></menu>
</openbox_config>
장비의 물리적 시간 오차방지용 타임데몬 Chrony 설치 및 락인
# 1. 크로니 패키지 설치 및 활성화
sudo apt update
sudo apt install chrony -y
sudo systemctl enable chrony
sudo systemctl start chrony
# 2. 타임 서버 설정 수정 (인터넷 가능 환경 또는 현장 NTP 서버 지정)
sudo nano /etc/chrony/chrony.conf
# [기존 pool 대신에 한국 표준 타임 서버 등록 (지우고 아래로 변경)]
pool time.bora.net iburst
pool time.kornet.net iburst
pool ntp.gwnn.rm.kr iburst
# 3. 크로니 데몬 재시작 및 동기화 상태 현장 검증
sudo systemctl restart chrony
chronyc tracking (현재 시간 동기화 추적 상태 확인)
chronyc sources -v (연결된 외부 타임 서버 리스트 및 상태 확인)
경량 유틸리티 5종 설치
# 정비용 유틸리티 (메모장, 터미널, 탐색기, 파일질라, 이미지뷰어) 설치
sudo apt update
sudo apt install -y mousepad lxterminal pcmanfm filezilla viewnior
오픈박스 바탕화면에 나올 메뉴 구성 및 sh 스크립트 연동
# 메뉴판 구성 파일 에디터 열기
nano ~/.config/openbox/menu.xml
# [아래의 내용으로 변경]
<?xml version="1.0" encoding="utf-8"?>
<openbox_menu xmlns="http://openbox.org/3.4/menu">
<menu id="root-menu" label="EQUIPMENT CONTROL">
<separator label="[ RUN PROGRAM ]" />
<item label="메인 App">
<action name="Execute"><command>/home/orangepi/MainApp/start_main.sh</command></action>
</item>
<item label="보조 App">
<action name="Execute"><command>/home/orangepi/SubApp/start_sub.sh</command></action>
</item>
<separator label="[ REPAIR TOOL ]" />
<item label="메모장">
<action name="Execute"><command>mousepad</command></action>
</item>
<item label="터미널">
<action name="Execute"><command>lxterminal</command></action>
</item>
<item label="파일 탐색기">
<action name="Execute"><command>pcmanfm</command></action>
</item>
<separator />
<item label="장비 재부팅 (Reboot)">
<action name="Execute"><command>systemctl reboot</command></action>
</item>
</menu>
</openbox_menu>
한국어 입력 자판 GUI 설정 (configtool 이용)
# 바탕화면 터미널 메뉴로 진입
# fcitx5-configtool로 키보드 설정진입
# 좌측 입력기에 다음 순서로 배치처리
키보드 - Korean - Korean (101/104)
한글(Hangul)
# 이렇게 설정하고 Apply
메인 앱 실행 스크립트 작성 (보조도 같은방식으로 처리)
# 메인 앱 디렉토리로 이동 및 스크립트 생성
cd ~/MainApp
nano start_main.sh
# [아래의 내용으로 변경]
#!/bin/bash
export DISPLAY=:0
export XMODIFIERS=@im=fcitx
export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
# 프로그램 실행 경로로 이동
cd /home/orangepi/MainApp
# 메인 애플리케이션 실행 (실제 구동할 바이너리나 파이썬 파일 지정)
# 예: python3 main_gui.py 또는 ./.NET바이너리 등
/usr/bin/python3 main_gui.py
# 작성 완료 후 저장(Ctrl+O, Enter, Ctrl+X)
.NET에서 async, await, Task가 등장한 이후 비동기 프로그래밍 방식은 크게 변화하였다.
과거에는 스레드 동기화 객체를 사용하여 작업 완료를 기다렸지만, 현재는 Task 기반 비동기 모델을 통해 보다 효율적인 구현이 가능하다.
이번 글에서는 비동기 환경에서 자주 사용되는 시그널링(Signaling) 패턴과 TaskCompletionSource를 활용한 현대적인 구현 방법을 소개한다.
다음과 같은 구조를 생각해보자.
즉, 결과를 기다리는 주체와 결과를 생성하는 주체가 서로 다른 상황이다.
private int _result;
private readonly ManualResetEvent _event = new(false);
private async Task<int> F1()
{
_event.Reset();
await Task.Run(() => _event.WaitOne());
return _result;
}
private void F2(int result)
{
_result = result;
_event.Set();
}
private TaskCompletionSource<int>? _resultTcs;
private Task<int> F1()
{
_resultTcs = new TaskCompletionSource<int>();
return _resultTcs.Task;
}
private void F2(int result)
{
_resultTcs?.TrySetResult(result);
}
int result = await F1();
실제 서비스 코드에서는 RunContinuationsAsynchronously 옵션을 함께 사용하는 것을 권장한다.
private TaskCompletionSource<int>? _resultTcs;
public Task<int> F1()
{
_resultTcs =
new TaskCompletionSource<int>(
TaskCreationOptions.RunContinuationsAsynchronously);
return _resultTcs.Task;
}
기본 TaskCompletionSource는 TrySetResult() 호출 시 await 이후 코드가 같은 스레드에서 즉시 실행될 수 있다.
int result = await F1(); DoSomething();
즉, 결과를 전달하려던 코드가 소비자 측 후속 로직까지 실행하게 될 수 있다.
ManualResetEvent는 스레드를 점유하는 블로킹 방식이다.
TaskCompletionSource는 스레드 점유 없이 결과 완료 시점을 외부에서 제어할 수 있으며, 현대적인 .NET 비동기 프로그래밍에 가장 적합한 시그널링 기법이다.
postgresql은 강력한 mvcc를 지원하고 엔진과 관리툴이 철저히 분리되어 있다.
이 때문에 하드웨어에 의존적인 순수한 속도와 규율적인 제약을 탈피할 수 있고
OS종속에서도 벗어날수 있으며 도커 컨테이너에도 자유롭게 올라가고 서버엔진과
클라이언트를 분리해서 클라이언트쪽에서 관리작업 (백업,복구)을 진행할수 있는등
혁신적인 일이 벌어진다. (어설픈 스크립트 백업이런거 아님) 또한 실시간 메모리 범위
변동이나 update나 insert시 RAM에서 우선처리하고 응답한후 스토리지에는 지연쓰기를 설정할 수 있는 레디스 db속도급 synchronous_commit = off 기능등 활용범위가
다양하고 자료형도 그래프관련 자료나 기하학관련 AI에서 쓰이는 자료형이나
json이나 xml처리등을할 수 있으며 지구상에서 sql표준을 가장 잘 구현하고 표준에 부합하는
데이터베이스다. 이런 혁신적인 구조 때문에 차세대 db엔진으로 많은 사랑을 받고 있다.
그래서 운영하는 시스템도 mssql에서 postgresql로 마이그레이션을 진행하였다.
먼저 이관 순서는 다음과 같이 진행하여야 한다. (한PC에서 두커넥션다 존재)
1. 기존 mssql자료에 접근할수 있는 로컬환경구축 (로그인과 알멩이가 같이 있어야함)
2. 새로운 postgresql에 접근할수 있는 로컬환경구축 (로그인과 껍데기 db준비)
3. sling으로 양쪽을 전부 접속해서 마이그레이션하는 명령실행
sling은 리눅스 기반이라 윈도우에서는 wsl을 이용하여 우분투를 설치한후
명령을 실행하는 방식을 취해야한다. 명령줄은 다음과 같다. (상세내역 아래참조)
./sling run --src-conn 'sqlserver://아이디:비밀번호@아이피:포트?database=DB명&encrypt=false&TrustServerCertificate=true' --src-stream 'dbo.*' --tgt-conn 'postgresql://아이디:비밀번호@아이피:포트/DB명?sslmode=disable' --tgt-object 'public.{stream_table}'
4. postgresql에 public 스키마로 백업된 데이터를 확인하고
내 입맛에 맞게 테이블을 alter작업 (이부분이 시간이 가장 많이 걸림.. 뷰나 프로시저도 한번은 거쳐야하므로 이때 이관한다. 복원된
테이블을 변경하고 인덱스도 다시 잡아줌 등등 sql파일에서 작업해서 처리할것)
5. 입맛대로 변경된 postgresql에서 db를 백업한다. (pg_dump 처리로 파일로 export)
6. 운영할 postgresql이 설치된 서버 pc에서 5번 파일로 restore 작업을 진행한다..
[마이그레이션 유의사항 - 리눅스에서 진행할것]
# 슬라잉(sling-cli) 최신 버전 다운로드
wget https://github.com/slingdata-io/sling-cli/releases/latest/download/sling_linux_amd64.tar.gz
# 3. 압축 풀기
tar -xzvf sling_linux_amd64.tar.gz
# 슬라잉을 이용하여 마이그레이션 작업
./sling run --src-conn 'sqlserver://아이디:비밀번호@아이피:포트?database=DB명&encrypt=false&TrustServerCertificate=true' --src-stream 'dbo.*' --tgt-conn 'postgresql://아이디:비밀번호@아이피:포트/DB명?sslmode=disable' --tgt-object 'public.{stream_table}'
[시간관련처리]
현재시간 | NOW() or CURRENT_TIMESTAMP | 현재시간을 추출함
형식변환(날짜) | NOW()::date | '2026-04-17' 형태만 남김
형식변환(파싱) | '2024-05-12 08:30:00'::timestamp | 문자열을 날자로변환
시간차이 | EXTRACT(EPOCH FROM (후-전))/3600 |
두시간사이 차이를 초로 반환해서 /3600을 하니까 시간단위로 바뀐것
시간더하기 | NOW() + INTERNAL '9 MINUTE' | 특정 시간의 합산처리
특정부분추출 | EXTRACT(HOUR FROM NOW()) | 시간에서 지정값을 뽑음
※ 시간(timstamp)을 문자로 변환하는것은 TO_CHAR() 하나로 끝남
TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI:SS.MS') | 지정 스타일로 변환
[기간조회 공식은 같이 먹힘]
WHERE (sb::timestamp <= pe:timestamp) AND (se::timestamp >= pb::timestamp)
포스트그리SQL만은 기간조회가 아예 함수로도 있음
WHERE (SB, SE) OVERLAPS (@PB, @PE)
[백업 및 복원] - 명령파일 pg_dump.exe 같은걸로 실행해야함
[백업]
pg_dump -h <원격서버_IP> -p <포트번호> -U <권한있는계정> -d <db_이름> -F c -b -v -f "저장할_파일명.dump"
[클린복원]
pg_restore -h <원격서버 IP> -p <포트번호> -U <권한있는계정> -d <db_이름> -c -v "파일명.dump"
테이블만 지정할때는 -t 옵션으로 지정해서 처리해야함..
[프로그래밍 방식] - DO $$로 시작해서 BEGIN .. END $$;로 감싸는 구조임
예를 들어 '2017-05-23 14:23:32' 라는 문자를 파싱해서 timestamp로 만들고
그걸 문자열 6시간과 int 변수인 초값으로 더하고 그 결과를 다시 초로 환산해서
임시테이블로 바꿔서 select하는 예시
do $$
declare
input_str text := '2017-05-23 14:23:32';
target_date timestamp;
result_seconds integer;
add_sec integer := 30;
begin
create temp table if not exists temp_tb (
ori_time text,
cvt_time timestamp,
calc_sec integer
);
truncate temp_tb;
--
target_date := input_str::timestamp;
target_date := target_date + interval '6 hours';
target_date := target_date + (add_sec || ' minutes')::interval;
result_seconds := extract(epoch from target_date)::integer;
--
insert into temp_tb (ori_time, cvt_time, calc_sec)
values (input_str, target_date, result_seconds);
end $$;
--
select ori_time, cvt_time, calc_sec from temp_tb limit 1;
[또는 with와 as의 임시 테이블을 이용하는 방법]
WITH
var_ttb AS (SELECT '2017-05-23 14:23:32' AS input_str, 30 AS add_sec),
cal_ttb AS (SELECT input_str, add_sec, input_str::timestamp + INTERVAL '6 hours' AS base_date FROM var_ttb),
res_ttb AS (SELECT input_str, base_date + (add_sec || ' minutes')::interval AS target_date
FROM cal_ttb)
--
SELECT input_str AS ori_time, target_date AS cvt_time, EXTRACT(EPOCH FROM target_date)::integer AS calc_sec FROM res_ttb;
[접속한 db의 모든 테이블 목록보기]
SELECT * FROM information_schema.tables WHERE table_schema = 'public';
[자동증가 id]
자동증가 id의 경우 자료형을 integer로 해서 초기값과 증가값을 가지는 경우..
묻지도 따지지도 말고 serial 자료형으로 처음부터 등록해야함.
자동증감기라는 오브젝트가 아예 별도로 있어서 뒤로 당겨주는 역할을 별도로 하기때문에..
자동증가 당겨주기는
SELECT setval(pg_get_serial_sequence('mytable', 'id'), COALESCE(max(id), 0)) FROM mytable;
로 처리하면 되는데
자료형을 바꺼버리면 아이디가 새로 부여된다.. 굳이 아이디가 바껴도 상관없다면..
ALTER TABLE mytable DROP COLUMN id;
ALTER TABLE mytable ADD COLUMN id SERIAL PRIMARY KEY;
이렇게 할수도 있지만 id의 값이 바껴버림 (주의)
[pgAdmin4 에서 전체 테이블 create 스크립팅 처리]
데이터베이스 -> 백업 -> 형식(Plain) -> 데이터 옵션(객체종류: Only schemas)
-> 파일경로지정(*.sql) -> 백업버튼
[*.sql 파일처리]
*.sql에 대량의 insert문이나 혹은 내용에 select가 있어서 결과셋을 헤더가 있는 csv파일로
출력할때는 다음처럼 psql 명령을 이용한다. 원래는 콘솔창에서 접속해서 쿼리작업을 위한 용도
이지만 이렇게 처리해도됨.
psql -h 127.0.0.1 -p 5432 -U postgres -d parking_db -v ON_ERROR_STOP=1 -f "D:/input.sql"
이때 결과셋을 출력하는 내용이 input.sql 파일에 다음과 같이 있다면
\copy (SELECT user_no, user_name FROM myuser) TO 'D:/output.csv' WITH CSV HEADER ENCODING 'UTF-8';
실행결과셋이 csv로 출력된다.
[주기적인 청소]
os에 따라서 윈도우 작업 스케줄러 같은게 있음.
postgresql은 바이너리로 reindexdb가 외부실행이 가능하도록 되어 있다.
따라서 그 작업 스케쥴러에 배치파일을 만들어서 다음과 같이 일주일에 한번
예를들어 매주 수요일 새벽 2시 에 한번 다음과 같은 bat파일을 만들어놓고
실행하는것이 좋다.
reindexdb.exe -U [사용자명] -d [DB이름] --concurrently
vacuumdb.exe -U [사용자명] -d [DB이름] --analyze