2026년 8월 3일 월요일

리눅스 wine에서 한글깨짐 잡기

와인 설치이후 맑은고딕 ttf로 변경을 해줘야함.
아무래도 이 폰트가 한국을 대표하는 폰트이니까..


[아래 경로에 맑은고딕 폰트를 다운로드 하여 복사한다.]
 ~/.wine/drive_c/windows/Fonts/*.ttf


이후 터미널에서 레지스트리를 편집하여 맑은 고딕을 기본으로 설정함
[아래의 내용을 붙혀넣기]


---------------------------------------------------------------

cat << 'EOF' > font_fix_malgun.reg
REGEDIT4

[HKEY_CURRENT_USER\Software\Wine\FontSubstitutes]
"MS Shell Dlg"="Malgun Gothic"
"MS Shell Dlg 2"="Malgun Gothic"
"System"="Malgun Gothic"
"Tahoma"="Malgun Gothic"
EOF

regedit font_fix_malgun.reg
wineserver -k

---------------------------------------------------------------


이후 winecfg 를 실행시켜서 그래픽 탭으로 들어가보면 잘나오는걸 확인한다.




맑은고딕 다운로드





2026년 7월 23일 목요일

PostgreSQL 모든테이블의 SerialType의 인덱스를 최신으로 맞추기

DB 마이그레이션 하거나 그럴때 ID값이 안따라갈수 있음..
아래의 내용으로 인덱스를 최신으로 바꿔줘야함..
안하면 duplicate key value violates unique constraint  에러를 만나게됨..





DO $$
DECLARE
    r RECORD;
BEGIN
    FOR r IN
        SELECT
            n.nspname AS schema_name,
            c.relname AS table_name,
            a.attname AS column_name,
            pg_get_serial_sequence(
                quote_ident(n.nspname) || '.' || quote_ident(c.relname),
                a.attname
            ) AS seq_name
        FROM pg_class c
        JOIN pg_namespace n ON n.oid = c.relnamespace
        JOIN pg_attribute a ON a.attrelid = c.oid
        WHERE c.relkind = 'r'
          AND pg_get_serial_sequence(
                quote_ident(n.nspname) || '.' || quote_ident(c.relname),
                a.attname
              ) IS NOT NULL
    LOOP
        EXECUTE format(
            'SELECT setval(%L, COALESCE((SELECT MAX(%I) FROM %I.%I), 1), true)',
            r.seq_name,
            r.column_name,
            r.schema_name,
            r.table_name
        );
    END LOOP;
END $$;






ㅇㅇ

민트나 조린OS로 프로그램 바로가기 (desktop 파일) 만들기

예를들어 RDRemote라는 프로그램 실행파일이 있을경우 
다음과 같은 ~/MyLauncher/RDRemote.desktop 파일을 만든다.
내용은 다음처럼

[Desktop Entry]
Type=Application
Name=RD Remote
Comment=Remote Support Launcher
Exec=/home/username/MyApps/RDRemote/RDRemoteLauncher
Path=/home/username/MyApps/RDRemote
Icon=/home/dyflrytlf/MyApps/RDRemote/mainlogo.png
Terminal=false
Categories=Utility;



이거는 실제 실행화일은
/home/username/MyApps/RDRemote/RDRemoteLauncher
이고  실행경로는
/home/username/MyApps/RDRemote

이다..  저장하고  다음명령으로 폴더와 바로가기를 복사한다.


mkdir -p ~/.local/share/applications
cp ~/MyLauncher/RDRemote.desktop ~/.local/share/applications/
chmod +x ~/.local/share/applications/RDRemote.desktop



이렇게 하면 앱런처 화면에 아이콘이 복사되어 있을것이다.

음..  lnk 파일은 편리한거였네..



2026년 7월 10일 금요일

남자 셀프 헤어컷 정리

남자는 이제 셀프 헤어컷을 해야한다.
동네 미용실 예약제와 폭리가격에 개짜증이 나니까 이를 정리하겠다.








ㅇㅇ

2026년 7월 4일 토요일

ini-parser 만들기.. C#

누겟에 있는 ini-parser가 리눅스환경에서 정상동작하지 않는다.

윈도우에 있는 ini파일이 개행이 \r\n이거니와.

리눅스쪽으로 복사가 되면서 더미 데이터가 들어가는것 같다..

그래서 ini-parser를 쓰면 정상적으로 쪼개기가 안된다.

짜증나니까.. 나만의 ini-parser (읽기전용)을 만들었드랬다..





Public class MyIniParser
{
/// <summary>
/// 내부 자료
/// </summary>
private Dictionary<string, List<KeyValuePair<string, string>>> _iniData = new Dictionary<string, List<KeyValuePair<string, string>>>();


/// <summary>
/// 데이터 가져오기
/// </summary>
public T GetIniData<T>(string section, string key)
{
T ret_data = default;
if (!_iniData.ContainsKey(section)) { return ret_data; }
if (!_iniData[section].Exists(x => x.Key == key)) { return ret_data; }
KeyValuePair<string, string> ini_data = _iniData[section].Find(x => x.Key == key);
//
try
{
return (T)Convert.ChangeType(ini_data.Value, typeof(T));
}
catch
{
return ret_data;
}
}


/// <summary>
/// 파일경로로 파싱처리하기
/// </summary>
public bool ParseIniFile(string iniFile, Encoding encoding)
{
try
{
if (!File.Exists(iniFile)) { return false; }
string rawContent = File.ReadAllText(iniFile, encoding);

// ini를 읽기 좋게 변환처리
string result = rawContent.Replace("\r", "");
string[] lines = result.Split('\n');
var validLines = new List<string>();
foreach (string line in lines)
{
string trimmed = line.Trim();

// 빈라인 무시
if (string.IsNullOrWhiteSpace(trimmed)) continue;
// 주석으로 시작하는 라인제거 (';' 또는 '#')
if (trimmed.StartsWith(";") || trimmed.StartsWith("#")) continue;
// 깨진문자열 제외
if (trimmed.Contains("M-lM")) continue;
//
bool isSection = trimmed.StartsWith("[") && trimmed.EndsWith("]");
bool isKeyValue = trimmed.Contains("=");
if (isSection || isKeyValue) { validLines.Add(trimmed); }
}


//필터링된 라인으로 KeyValue 삽입
string lastSection = "";
foreach (string one_line in validLines)
{
bool isSection = one_line.StartsWith("[") && one_line.EndsWith("]");
bool isKeyValue = one_line.Contains("=");
//
if (isSection)
{
string sectionName = one_line.Substring(1, one_line.Length - 2);
lastSection = sectionName;
if (!_iniData.ContainsKey(sectionName))
{
_iniData.Add(sectionName, new List<KeyValuePair<string, string>>());
}
}
else if (isKeyValue)
{
string[] keyValue = one_line.Split('=');
if (keyValue.Length != 2) { continue; }
string key = keyValue[0].Trim();
string value = keyValue[1].Trim();
if (_iniData.ContainsKey(lastSection))
{
//동일한 키가 있으면 제거하고 새로 추가
var list = _iniData[lastSection];
int index = list.FindIndex(x => x.Key == key);
if (index != -1) { list.RemoveAt(index); }
//
_iniData[lastSection].Add(new KeyValuePair<string, string>(key, value));
}
}
}

//
return true;
} catch (Exception ex) {
throw ex;
}
}
}

2026년 6월 28일 일요일

모바일 삼성인터넷 브라우저에서 웹앱만들기

스마트폰에서 웹페이지 자체를 앱처럼 만들려는 시도는 계속 이어져왔다..
나도.. 자바나 코틀린으로 안드로이드 스튜디오에서 웹뷰만 가지고 처리하도록
하는 앱으로 실행해서 특정웹페이지 URL을 지정해서 apk로 빌드하곤했다. 
하나의 중앙집중식 웹서버라면 문제가 없지만 분산형 웹서버인경우 배포문제가 발생함
또한 처리과정에서 카메라,센서,전화,이메일로 전환하는 것들이 부가적으로 필요하다.
이런부분이 많이 번거롭고 또한 안드로이드 정책변화에 따라서 apk 파일설치가
제한될 수 도 있다.   그래서 그냥 브라우저 자체가 이러한 기능을 해주면 좋겠다는 생각을
했다.. 그런데.. 그걸 삼성인터넷 브라우저가 먼저 이거 해줬다..
매니페스트(아이콘과 앱이름)정보와 부가파일만 있으면 처리되고 실행시 스플레쉬 이미지를 
띄워주고 지정된 URL로 바로 진입하고.. 센서나 카메라에 접근 가능 하도록 하게되는 것이다.
pwa도 아니고 pwa의 보안요건을 충족하는것도 아니므로 https의 보안과는
관계없는 기술이다 라고 생각하고 또 그냥 http에서도 동작한다.  
그런데 알림기술만 제외하면 pwa에 준하는 형태로 보여진다.
삼성인터넷을 이용해서 설치과 되어서 실행되는 방법이 있고 그냥 홈화면 바로가기가 
그렇게 되는 경우가 있다.  이게 브라우저 버전에 따라 다른것 같은데
두개의 차이는 표면상으로는 설치는 런처아이콘에 삼성인터넷 뱃지가 없고 바로가기는 있다.
앱형태 이므로 URL주소창과 도구모음 메뉴가 없고 삼성인터넷이 띄우지면서 탭에 
추가되는 형태가 아니고 개별 실행되고 OS의 앱전환 명령에도 별로의 프로그램으로 
띄워진다.  구현을 위해 일단 서버는 아래와 같이 준비만 하고 있으면 된다.





[준비물]

1. manifest.webmanifest 파일
2. sw.js 파일
3. icon에 쓰일  192*192,  512*512 아이콘 이미지파일


[처리사항]
 
manifest.webmanifest 파일의 내용을 다음 처럼 기재한다.

{
"name": "마이어플",
"short_name": "마이어플",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#ffffff",
"icons": [
{
"src": "/myicon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/myicon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}


그 다음 sw.js 파일의 내용을 다음처럼 기재한다.

self.addEventListener("install", e=>{
self.skipWaiting();
});

self.addEventListener("activate", e=>{
self.clients.claim();
});


그 다음 특정 html페이지에서 (예: 로그인페이지)
메타링크 아래에.. 
<link rel="manifest" href="/manifest.webmanifest">
라고 기제하고

스크립트 테그에서
<script>
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/sw.js');
}
</script>


그 다음 static 폴더에 그림파일 2개와 위의 파일 2개를 복사하면 끝~



이렇게 하면 삼성 인터넷 브라우저를 통해서 홈화면 바로가기를 한다면..
그 바로가기는 핸드폰 어플처럼 동작한다.
주소창없고 task 스위처에서 별도의 어플처럼 띄워짐

웹앱 공수 안들이고  빠르게 만드는 방법임..  (땡큐 삼성~)

2026년 6월 18일 목요일

닷넷의 R2R

닷넷에 네이티브 AOT같은 경우에는 프로젝트 전체가 전부 기계어로 빌드된다.
그런데 지난번 언급처럼 이렇게 되면 프로그래밍 기법이 전부 바뀌어야 한다.
리플렉션 DI 제네릭심화 다이나믹 키워드  같은 경우는 들어가면
빌드가 안되어 버린다.

그렇다면 기계어로 구워지는게 안되는것이냐 하겠지만..
이 경우에도 기계어로 빌드될수 있도록 배포/빌드하는 기능이 하나있다.
바로 ReadyToRun (R2R) 기능이다.
일반적으로 C#닷넷은 중간언어로 빌드되어 런타임에 프레임워크가
소스코드를 해석해서 기계어로 변형되어 실행된다. 이 과정의 오버헤드가 높다.

그런데 R2R은 배포 빌드를 할때 영리한 전략을 쓰는데..
가상머신이 실행중에 JIT 컴파일러로 해야 할 일을 빌드시점에 미리 대신해서 
그 결과물인 기계어를 중간 언어(IL)와 함께 빌드해 버린다.
물론 AOT처럼 깡 기계어로 되는게 아니라 중간 언어가 노출되어 버리는건
똑같지만 이정도만 하더라도 초기 로딩속도는 나름 빨라진다.
일단 메모리에 올라가면 JIT나 AOT나 속도는 비슷비슷하다.
그래서 메모리에 올라가서 JIT가 해석하는 시간이 관건인것이다.

그리고 기계어로 변환될지 말지를 결정하는 단위는 함수단위 이다.
함수단위로 분석을 해서 파라미터 본문 리턴값을 바라볼때 자료크기나 자료형이
예측 가능하다면 기계어로 구워지고 DI나 리플렉션 제네릭처럼  런타임에 크기나
자료형이 결정되어 지거나 이러한 함수들은 그냥 JIT에 의존을 하도록 변경된다.

따라서 최초 실행되거나 실행중 동적라이브러리 (DLL) 로딩을 하거나 이러는 경우도
대상이 되므로 빌드시점에 이것들이 기계어로 바뀔지 말지가 결정이 된다.
주의 할점이 있는데 배포(Publish) 단계에서 컴파일러가 판단해서 이것은
잘라내도 된다고 판단해서 코드가 잘라내어 지면(Trimmed) 실행중에 실제로 
해당코드가 없어서 낭패를 볼 수가 있으므로 용량이 조금 늘어나더라도
*.csproj 에서 프로젝트 그룹에 다음처럼

<PublishTrimmed>false</PublishTrimmed>


배포시 Trim기능 사용안함으로 하고 빌드하는것이 정신건강에 이롭다.
아니면 명령으로 빌드할때 옵션으로 주던가..
아무튼 이렇게 해서 R2R 옵션으로 빌드하면 초기 로딩속도가 상당히 빨라지고

실행중간에 예측불가능한 부분을 만나면 JIT에 의존해서 돌아가고
나머지는 그냥 JIT가 해야할일이 기계어로 빌드된것이 실행된다.

AOT와 R2R의 근본적인 차이는
R2R은 JIT 컴파일러가 미래에 해야 할일을 빌드 시점에 미리 대신해서 찔러 넣어둔 것이고, 
AOT는 가상머신이건 뭐건 다 걷어치우고 프로그램과 함수 구조 그 자체를 순수 
C/C++ 프로그램처럼 깡기계어로 빌드한 결과물이라고 할수 있다.
따라서 R2R로 빌드해서 기계어가 일부 들어간다고 해서 AOT처럼 기계어수준의
난독화가 되는것이 아니고 중간언어가 함께 들어가기 때문에 보안이 좋다는
착각을 해서는 안된다.


해당옵션이 적용된 빌드명령으로는  - 리눅스 arm x64와  윈도우 x64 는 각각

* 리눅스 arm64 빌드 명령어: dotnet publish -c Release -r linux-arm64 -p:PublishSingleFile=true -p:PublishReadyToRun=true -p:SelfContained=true -p:IncludeNativeLibrariesForSelfContained=true -p:PublishTrimmed=false --output ./bin/publish_linux_arm64_output

* 윈도우 x64 빌드 명령어: dotnet publish -c Release -r win-x64 -p:PublishSingleFile=true -p:PublishReadyToRun=true -p:SelfContained=true -p:IncludeNativeLibrariesForSelfContained=true -p:PublishTrimmed=false --output ./bin/publish_win_x64_output
  

이렇게 하면된다..