2026-05-02

BBolZZim Gangseo Seoul

뽈찜. 

가게 이름은? 소문난 뽈테기 찜.탕.아구찜 이라고 간판에 써 있는데, 어디까지 가게 이름인지 모르겠음.

서울 강서구청 길 건너 있는 식당. 주소는 서울 강서구 화곡로53길 11, 1층

 

자체배달 즉 포장 집에서 뽈찜 먹음
식당에서 먹으면 기본 반찬 나오는데 포장에는 반찬 없음. 가서 먹으나 포장하나 가격은 같은데.

소문날 뽈 메뉴 2026. 5. 2.

메뉴는 이러함. 다른 블로그에 같은 식당 가격 다른 것 있는데, 아마도 지난 자료인 듯. 음식 가격은 어디가나 시간이 지나면 오름. 

반면, 전자제품 가격은 시간이 지날 수록 성능은 좋아지고 가격은 내려가는데, 그렇다고 계속 가격 내려가길 기다리다가는 전자제품 평생 못 살 수 있음.

2026-04-27

SSada Hair Sunday

싸다헤어. 5호선 우장산역 4번 출구.

싸다. 그런데, 언제부터인가 일요일에 전화 안 받는다. 일요일엔 안 하는 듯. 그러면 간판에 써 놓던지.

02-323-9882 직접 전화 해 봄.

2026년 5월에는 목,금 만 영업 한다고 한다.

일정 봐서 다시 예약 잡아야 할 듯.


2026-04-10

2026 명곡 아카데미

36년 전통, 명곡 아카데미


클래식의 품격, 최고 강사진과 함께
🎤 성악클래스(임종환 교수, 김정경 교수)
* 오페라 주역 및 세계 무대 활약 성악가 연주가
* 국내외 가곡, 건전가요, 발성법, 호흡법, 가창법, 시문학해석(노래가사), 보컬 테라피, 힐링, 마음건강
* 음악박사 피아니스트 반주, 지휘 및 건강 지도

왜 명곡아카데미인가?
성악과 음악치료, 심리상담, 심리치료 전문가
노래를 통해 정신건강과 품격 있는 삶 초대
힐링 명곡 교실

일시: 매주 금요일 15:00 ~ 16:30
장소: 서울시 강서구 우장산동 주민센터 3층
문의: 010-5416-4940, 010-8245-4931

이상은 임종환 교수님이 주신 자료. 

그런데, 위 글만 보면 너무 엄숙하고 그래서 다가가기 어려워 보인다. 내 느낌은 좀 다르다.

동네 사랑방 같은 느낌? 교수님 말씀이 웃기다. 재밌다. 그런데, 그냥 단순히 웃긴 게 아니라, 그 속에 깊은 뜻이 있다. 그래서  더욱 재미있고 유익하게 노래 수업을 들을 수 있다. 

장소가 서울 강서구 우장산동주민센터. 장소만 보면 "강서구 사람만 갈 수 있나?" 생각할 수도 있는데, 다른 곳에 사는 사람도 참가 가능. 더 자세한 것 궁금하면 위의 전화로 물어보시길. 

 

 

2026-03-30

Gangseo HairForYou

강서구에 장애인을 위한 미용실이 생겼다.

위치는? 오래 전에 정보화진흥원 있던 자리. 정보화진흥원은 지방으로 내려간지 한참되었는데 그동안 서울 정보화진흥원 있던 자리 건물은 제대로 활용 안 되고 있었던 듯. 그런데, 이번에 어울림플라자 지은 것을 보고 깜짝 놀랐다. 매우 훌륭. 어울림플라자에는 도서관도 있고, 장애인전용미용실도 있다.

거기 있는 장애인 전용 미용실 이름은 헤어포유. 이름에서 유추해서 홈페이지 주소에 hairforyou 쳤더니... 강서구 아니고 다른 곳에 있는 헤어포유 나온다. 이런 일반적인 이름이 하나뿐이라면 오히려 이상한 듯. 강서구 헤어포유 주소

(우) 07564 서울 강서구 공항대로 489 어울림플라자 3층

인터넷 주소(homepage, 홈페이지)는?

http://gshairforyou.co.kr/

약간 아쉽다. 내가 하는 일이 그러하다 보니 그런 게 잘 보인다. 다른 사람들은 잘 발견하지 못할 것들을 내가 발견.

이 홈페이지 아쉬운 점.

이용료 안내 페이지와 이용안내 페이지 두 페이지가 자동으로 넘어간다. 이건 곤란하다.

한 페이지 읽고 있는데 다 읽지도 않았는데 다른 페이지로 넘어가버리면 당황한다. 장애인은 더더욱.

마이페이지 메뉴 클릭하면 로그인한 회원만 접근할 수 있다는 메시지가 나온다.

그게 무슨 문제냐고? 로그인한 회원만 접근할 수 있는 페이지라면 로그인 전에는 마이페이지 메뉴가 안 나오게 하는 게 좋다. 로그인 한 다음에는 마이페이지 메뉴가 나오고, 로그아웃하면 마이페이지 메뉴 사라지게. 기술적으로 어려울까? 아니다. 웹페이지 개발자가 그 정도도 못하면 웹페이지 개발 하지 말아야지. 아니면 배워서 하거나.

헤어포유 운영자를 탓하는 건 아니다. 어차피 운영자가 전산에 대해 잘 알고 있다는 보장 없으니. 그래도 아쉬운 건 그걸 홈페이지 개발 전문 업체에게 맡겼을 텐데, 그 업체는 무슨 생각으로 홈페이지를 만들었을까? 저거 만들고 개발비는 얼마나 받아간 걸까? 업체를 너무 나무런 것 같은데 여기서 내가 업체의 변명도 좀 하자면(병 주고 약 주고?) 업체는 고객의 요구사항에 따라 페이지를 만드는 거다. 그런데, 고객의 요구사항이 불분명하면 그에 따라 만든 홈페이지도 그리 되기 쉽다.

앞으로 많은 발전이 있기를. 강서헤어포유도. 그곳 홈페이지도.

http://gshairforyou.co.kr

2026-03-29

20260324Big10

2026.3.24. 광화문 교보 명강의 빅10

박재연 님

2026.3.24. 박재연 

 

나는 못 갔음. 교보문고. 강연 본 사람은 많을 텐 데, 후기가 안 올라옴.

후기 올리지 않기로 담합을 한 것일까? 후기 올리지 말라고 한 걸까? 나는 모름.

사진 출처: https://cafe.naver.com/vorashow

 

2026-03-25

BTS Question

BTS 공연이 별다른 사고 없이 끝난 것 같다. 이태원 같은 사고가 나지 않은 건 다행. 그러나, 너무 심한 거 아니었냐는 의견도 있었으니. 참 어렵다.

https://www.khan.co.kr/article/202603250700001

WeHappyChurch Admin Manual

우리행복한교회 홈페이지 관리자 여러분께 알림. 관리자 홈페이지  설명서 아래 링크 참고.

http://help30.church-love.net/index_ch.html

2026-03-20

ls -lF of LFS Book korean translation

LFS Book 한국어 번역 시도. 일단 github에서 받고, ko 디렉터리 만들어서 한국어 번역 관련 내용은 ko 디렉터리에 영어는 en 디렉터리에 공통사항은 메인 디렉터리에 넣으려고 함. 시행착오 중. 일단 현재 상황은 아래와 같음.

tu@ma:~/lfs.13$ ls -lF 
합계 180
-rw-rw-r--  1 tu tu  1776  3월 15일  08:34 INSTALL
-rw-rw-r--  1 tu tu  7605  3월 15일  09:24 Makefile
-rw-rw-r--  1 tu tu   920  3월 15일  08:34 README
lrwxrwxrwx  1 tu tu    13  3월 20일  03:19 appendices -> ko/appendices/
-rwxrwxr-x  1 tu tu   744  3월 15일  08:34 aux-file-data.sh*
lrwxrwxrwx  1 tu tu    14  3월 20일  03:18 bootscripts -> en/bootscripts/
lrwxrwxrwx  1 tu tu    12  3월 19일  21:44 chapter01 -> ko/chapter01/
lrwxrwxrwx  1 tu tu    12  3월 19일  21:45 chapter02 -> ko/chapter02/
lrwxrwxrwx  1 tu tu    12  3월 19일  21:45 chapter03 -> ko/chapter03/
lrwxrwxrwx  1 tu tu    12  3월 19일  21:45 chapter04 -> ko/chapter04/
lrwxrwxrwx  1 tu tu    12  3월 19일  21:45 chapter05 -> ko/chapter05/
lrwxrwxrwx  1 tu tu    12  3월 19일  21:45 chapter06 -> ko/chapter06/
lrwxrwxrwx  1 tu tu    12  3월 19일  21:45 chapter07 -> ko/chapter07/
lrwxrwxrwx  1 tu tu    12  3월 19일  21:45 chapter08 -> ko/chapter08/
lrwxrwxrwx  1 tu tu    12  3월 19일  21:45 chapter09 -> ko/chapter09/
lrwxrwxrwx  1 tu tu    12  3월 19일  21:53 chapter10 -> ko/chapter10/
lrwxrwxrwx  1 tu tu    12  3월 19일  21:46 chapter11 -> ko/chapter11/
-rw-rw-r--  1 tu tu    60  3월 20일  04:13 conditional.ent
-rw-rw-r--  1 tu tu  2812  3월 20일  04:53 dirlist.txt
drwxrwxr-x 19 tu tu  4096  3월 16일  02:04 en/
-rwxrwxr-x  1 tu tu  3317  3월 15일  08:34 gen-changelog.py*
-rw-rw-r--  1 tu tu  1273  3월 15일  08:40 gen_conf.py
-rw-rw-r--  1 tu tu  6815  3월 15일  08:34 general.ent
-rwxrwxr-x  1 tu tu  2463  3월 15일  08:34 git-version.sh*
drwxrwxr-x  2 tu tu  4096  3월 15일  21:12 images/
lrwxrwxrwx  1 tu tu    12  3월 15일  21:15 index.xml -> ko/index.xml
drwxr-xr-x 17 tu tu  4096  3월 20일  03:07 ko/
-rw-rw-r--  1 tu tu   196  3월 20일  04:13 lfs-bootscripts-20250827.tar.xz
-rw-rw-r--  1 tu tu 17000  3월 15일  08:34 lfs-latest-git.php
lrwxrwxrwx  1 tu tu    18  3월 15일  08:34 lfs-latest.php -> lfs-latest-git.php
-rwxrwxr-x  1 tu tu   682  3월 15일  08:34 make-aux-files.sh*
-rwxrwxr-x  1 tu tu  1342  3월 15일  08:34 obfuscate.sh*
-rw-rw-r--  1 tu tu 35221  3월 15일  08:34 packages.ent
lrwxrwxrwx  1 tu tu    13  3월 20일  03:22 part3intro -> ko/part3intro/
-rw-rw-r--  1 tu tu  2663  3월 15일  08:34 patches.ent
-rwxrwxr-x  1 tu tu   450  3월 15일  08:34 pdf-fixups.sh*
drwxrwxr-x  2 tu tu  4096  3월 20일  04:10 po/
-rw-rw-r--  1 tu tu 17896  3월 16일  02:26 po4a.conf
-rwxrwxr-x  1 tu tu   822  3월 15일  08:34 process-scripts.sh*
lrwxrwxrwx  1 tu tu    11  3월 20일  03:21 prologue -> ko/prologue/
drwxrwxr-x  3 tu tu  4096  3월 15일  21:12 stylesheets/
-rw-rw-r--  1 tu tu   194  3월 15일  08:34 tidy.conf
drwxrwxr-x  3 tu tu  4096  3월 15일  08:34 udev-lfs/
-rw-rw-r--  1 tu tu   211  3월 20일  04:13 version.ent

2026-03-16

Gil Bit Media Gangseo

상호 길빛미디어 

등록신고번호 251002026000027

등록일 2026-02-06

소재지 서울특별시 강서구

전국츨판사인쇄사 검색시스템에 나타남.

YeokdaegeupPizzaMagok

발산동 포시즌 근처. 전에 카페동네 있던 자리. 동네카페 문 닫고. 역대급피자 문 열다.

2026. 3. 13.(금)

아래는 직원모집 공고.

https://www.albamon.com/jobs/detail/115301252?logpath=7&productCount=1

문 연지 얼마 안 되어서 그런가? 아래 링크에는 안 나옴.

http://yeokdaegeup.kr/franchise

2026-03-14

WeHappyC.Admin

교회 홈페이지에 서브 메뉴 추가 방법.

admin.wehappyc.org 로그인 (아이디 및 비번은 관리자에게 문의)

메뉴 설정 클릭.

1. 서브 메뉴 기본 정보

조직 및 기관 아래에 메뉴를 추가하는 예시.

조직 및 기관 클릭해서 나타난 팝업 메뉴 중 조직 및 기관 하위에 메뉴 추가

페이지명 입력.

2. 페이지 형식

콘텐츠 생성기, html, 게시판, 모듈, 링크 중에서 원하는 것을 클릭.

3. 기타 설정 열기 버튼 클릭하여 더 입력할 것 있으면 하면 됨.


2026-03-12

Gangseo Hair For You

서울시 강서구에 장애인 친화 미용실 헤어포유 문을 열다.

어디: 강서구 공항대로 489, 어울림플라자 3층

언제: 화~토 09:00-18:00 (일, 월, 공휴일 쉼)

이용대상: 서울시 강서구 거주 등록장애인

이용방법: 전화(02-6949-3491), 온라인 예약

커트: 6,000원, (기초수급자: 3,000원)

염색: 15,000원, (기초수급자:7,500원)

일반펌: 19,000원, (기초수급자: 9,500원)

열펌: 39,000원, (기초수급자: 19,500원)

강서헤어포유 홈페이지: http://www.gshairforyou.co.kr/

2026-03-07

VoraTalk20260306

2026. 3. 6.(금) 19:30- 교보문고.  보라토크.

이야기: 백희성, 책 제목: 쓰는사람 

명함

특이한 문 손잡이

계산무진

계산무진 해설

발표자. 사진 제대로 안 나옴. 실제는 잘 생겼음.

돌탑.바닥 어마어마한 돌탑. 자연을 활용

붓 없이 그린 그림

2026-02-28

202602GalBiYeChan

강서구청 길 건너편에 있는 식당. 갈비예찬. 2026년 3월 말에 문 닫는다.

그리고, 이전한다는데, 김포신라화로로 이전한다니. 이전 하면서 이름도 바꾸면? 그게 이전인가? 아예 새로 만드는 건가? 나는 모름.

현재위치 서울시 강서구 화곡로53길 10 

갈비예찬 영업종료 미리 알림

 

2026-02-27

Linux From Scratch Book Korean Translation

Linux From Scratch 설명서 한국어 번역. 아직 끝나지 않음. 언제 완성될 지 모르고, 누가 읽을지도 모르지만 아직도 진행 중. 아래 링크 맨 아래로 가면 링크 있다.

https://www.linuxfromscratch.org/lfs/read.html

내 LFS 저장소: https://github.com/sebuls/lfs-ko

2026-02-18

2026LunarNewYear

2026년 설날

2026. 2.17. 

충북 영동 황간

2026. 설날. 황간 


 

2026-02-09

Linux from Scratch Book Korean Translation

Linux From Scratch 줄여서 LFS

설명서를 한국어로 번역 중.

저장소: https://github.com/sebuls/lfs-ko

한국어 설명서: 위 저장소에 있는 파일을 받아서 설명에 따라 make 하면 html 파일을 얻을 수 있다. 아주 가끔(실시간 아니라는 뜻) https://sebul.sarang.net/lfs/ 에 올리고 있다.


2026-02-02

Jesus Loves Me piano and sheet music

원본 파일: JesusLovesMe.m4a 이 파일 처음부터 32초까지만 자르기

결과파일: jesuslovesme32.m4a

d:\opt\ffmpeg-6.1-win-64\ffmpeg -i JesusLovesMe.m4a -ss 00:00:00 -t 00:00:32 -c copy jesuslovesme32.m4a

소리파일만 유튜브에 올릴 순 없잖아? 이미지 파일 추가

d:\opt\ffmpeg-6.1-win-64\ffmpeg -loop 1 -i imgJesuslovesme.jpg -i jesuslovesme32.m4a -c:v libx264 -tune stillimage -c:a copy -shortest jsmimg.mp4

이렇게 해서 만든 영상 jsmimg.mp4 파일을 유튜브에 올리면 됨.


2026-01-30

DisabledStudentSupport

장애학생 성장 자립 지원사업 신청 알림
정보통신보조기기 보급 자부담금 지원사업

사업명: 장애학생 성장 자립 지원사업(정보통신보조기기 보급 자부담금 지원사업)

지원대상: 과학기술정보통신부 ‘2025년 정보통신보조기기 보급사업에 신청하여 최종 선정된 장애학생(초등학생, 중학생, 고등학생, 전공과 학생, 대학생, 대학원생)

지원내용: 과학기술정보통신부 ‘2025년 정보통신보조기기 보급사업선정자 대상 자부담금(개인부담금) 전액 지원

신청기간: 2026116() ~ 227()

제출서류

- 2025 정보통신보조기기 보급사업 기기 수령확인서(배송·설치업체 발급)

- 재학증명서(대학·대학원생의 경우 휴학증명서로 대체 가능)

- 통장사본(신청자 본인 통장)

- 자부담금 납부 증빙자료(이체확인증, 온라인 뱅킹 캡처 화면 등)

제출방법

- 구글폼 접수 페이지에 첨부 후 제출(https://forms.gle/94Y21JpG7LTTP1DP7)

문의처

- 담당자: 한국장애인단체총연맹 신우철 선임

- 대표 전화: 02-783-0067

- 대표 메일: mail@kofdo.kr



2026-01-27

NalMok 2026

게임 이름:날목 (python3 으로 개발)

import sys
import math # 중앙 거리를 계산하기 위해 추가
from PyQt6.QtWidgets import QApplication, QMainWindow, QMessageBox
from PyQt6.QtGui import QPainter, QPen, QColor, QBrush
from PyQt6.QtCore import Qt, QPoint

class FixedNalGomoku(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("세벌의 날목 (Fixed AI)")
        self.setFixedSize(640, 640)
        self.grid_size = 30
        self.margin = 50
        self.board = [[0] * 19 for _ in range(19)]
        self.current_player = 1  # 흑(컴퓨터) 선공
        self.directions = [(2, 1), (1, 2), (-1, 2), (-2, 1)]
        self.win_stones = []
        
        # 첫 수는 무조건 중앙(천원)
        self.board[9][9] = 1
        self.current_player = 2

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.RenderHint.Antialiasing)
        painter.fillRect(self.rect(), QColor("#E6C38A"))

        # 바둑판 선 및 화점
        painter.setPen(QPen(Qt.GlobalColor.black, 1))
        for i in range(19):
            painter.drawLine(self.margin, self.margin + i * self.grid_size, self.margin + 18 * self.grid_size, self.margin + i * self.grid_size)
            painter.drawLine(self.margin + i * self.grid_size, self.margin, self.margin + i * self.grid_size, self.margin + 18 * self.grid_size)
        
        painter.setBrush(QBrush(Qt.GlobalColor.black))
        for r in [3, 9, 15]:
            for c in [3, 9, 15]:
                painter.drawEllipse(QPoint(self.margin + c * self.grid_size, self.margin + r * self.grid_size), 3, 3)

        # 돌 그리기
        for y in range(19):
            for x in range(19):
                if self.board[y][x] != 0:
                    center = QPoint(self.margin + x * self.grid_size, self.margin + y * self.grid_size)
                    color = Qt.GlobalColor.black if self.board[y][x] == 1 else Qt.GlobalColor.white
                    painter.setBrush(QBrush(color))
                    painter.setPen(QPen(QColor("#CCCCCC") if self.board[y][x] == 2 else Qt.GlobalColor.black, 1))
                    painter.drawEllipse(center, 13, 13)

                    if (y, x) in self.win_stones:
                        painter.setBrush(QBrush(QColor("lime")))
                        painter.setPen(Qt.PenStyle.NoPen)
                        painter.drawEllipse(center, 4, 4)

    def mousePressEvent(self, event):
        if self.current_player != 2 or self.win_stones: return
        x = round((event.position().x() - self.margin) / self.grid_size)
        y = round((event.position().y() - self.margin) / self.grid_size)

        if 0 <= x < 19 and 0 <= y < 19 and self.board[y][x] == 0:
            self.board[y][x] = 2
            win, stones = self.check_win(y, x, 2)
            if win:
                self.win_stones = stones
                self.update()
                self.end_game("세벌 님 승리!")
            else:
                self.current_player = 1
                self.ai_move()
            self.update()

    def ai_move(self):
        best_score = -1
        best_moves = [] # 동점일 경우를 위해 리스트로 관리

        for y in range(19):
            for x in range(19):
                if self.board[y][x] == 0:
                    score = self.evaluate_move(y, x)
                    if score > best_score:
                        best_score = score
                        best_moves = [(y, x)]
                    elif score == best_score:
                        best_moves.append((y, x))

        if best_moves:
            # 동점일 경우 중앙에서 가장 가까운 곳 선택
            best_move = min(best_moves, key=lambda pos: math.sqrt((pos[0]-9)**2 + (pos[1]-9)**2))
            y, x = best_move
            self.board[y][x] = 1
            win, stones = self.check_win(y, x, 1)
            if win:
                self.win_stones = stones
                self.update()
                self.end_game("컴퓨터 승리!")
            self.current_player = 2
        self.update()

    def evaluate_move(self, y, x):
        score = 0
        for dy, dx in self.directions:
            # 공격 가중치와 방어 가중치를 전략적으로 배분
            score += self.get_line_score(y, x, 1, dy, dx) * 1.5 # 공격 중시
            score += self.get_line_score(y, x, 2, dy, dx) * 1.2 # 방어 필수
        
        # 중앙 선호도 점수 (0~10점 사이의 아주 작은 보너스)
        dist_from_center = math.sqrt((y-9)**2 + (x-9)**2)
        score += (15 - dist_from_center) 
        
        return score

    def get_line_score(self, y, x, player, dy, dx):
        count = 1
        open_ends = 0 # 양끝이 열려있는지 확인 (3-3 등 파악용)
        
        for direction in [1, -1]:
            ty, tx = y + dy*direction, x + dx*direction
            while 0 <= ty < 19 and 0 <= tx < 19 and self.board[ty][tx] == player:
                count += 1
                ty, tx = ty + dy*direction, tx + dx*direction
            if 0 <= ty < 19 and 0 <= tx < 19 and self.board[ty][tx] == 0:
                open_ends += 1
        
        # 가중치 대폭 상향
        if count >= 5: return 100000
        if count == 4: return 10000 if open_ends > 0 else 5000
        if count == 3: return 2000 if open_ends == 2 else 500
        if count == 2: return 100
        return 0

    def check_win(self, y, x, player):
        for dy, dx in self.directions:
            stones = [(y, x)]
            for direction in [1, -1]:
                ty, tx = y + dy*direction, x + dx*direction
                while 0 <= ty < 19 and 0 <= tx < 19 and self.board[ty][tx] == player:
                    stones.append((ty, tx))
                    ty, tx = ty + dy*direction, tx + dx*direction
            if len(stones) >= 5:
                return True, stones[:5]
        return False, []

    def end_game(self, message):
        QMessageBox.information(self, "게임 종료", message)
        self.board = [[0] * 19 for _ in range(19)]
        self.win_stones = []
        self.board[9][9] = 1 # 리셋 후에도 흑(AI)이 중앙 선공
        self.current_player = 2

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = FixedNalGomoku()
    window.show()
    sys.exit(app.exec())

sed example

.po 파일 번역 작업 중 "Installation of 어쩌구" 형식의 문장이 여러 개 나오는 것 발견. 한 두 개면 그냥 하겠는데, 여러 개 있으니 일괄 작업 하는 게 더 빠를 것 같다. 아래 스크립트 참고하면 된다.

sed -i -E '/msgid "Installation of (.*)"/{N;s/msgid "Installation of (.*)"\nmsgstr ""/msgid "Installation of \1"\nmsgstr "\1 설치"/}' lfs-ko.po

2026-01-25

LinuxFromScratchBookTransalionKo

Linux From Scratch Book https://github.com/lfs-book/lfs

L10n i18n 은 알아서 해야 된다. 그 사이트에서 기본 제공하지는 않는다는 뜻. 한국어 번역. 그 알아서 하는 것을 내가 시작.

한국어 번역. 사실 다른 사람이 한 게 있기는 하다. 현재 영어 원본은 버전 12까지 나왔는데, 한국어 버전 최근 버전은 9. 그런데, 버전 12와 버전 9 차이는 적지 않다. 한국어 버전 살펴보니 xml 파일을 직접 뜯어 고친 거라 유지관리 쉽지 않다. 기존 한국어 번역을 업데이트 하느니 최근 영어 버전 원본에서 새롭게 시작하는 게 낫다고 판단. 내가 한 작업은 .po 파일을 만들였고, 원문과 번역문 1대1로 쉽게 할 수 있도록 틀 만들었다. 또한 한국어 번역 중 영어 원본이 바뀌면 바뀐 부분을 추가적으로 한국어 버전에 반영하기 쉽게 했다. 그것을 모두 수작업으로 한 건 아니고, 관련 프로그램을 잘 활용하면 된다.

 https://github.com/sebuls/lfs-ko/

이제 시작은 했다. 번역 업데이트는 틈틈이 해 나가면 된다. 다른 사람이 관심 있다면 내 작품 fork 해서 clone 해도 되고. 그게 오픈소스 좋은 점. 그런데 할 사람이 있을지는 모르겠다.

 

2026-01-21

Linux From Scratch Book Korean Translation

Linux From Scratch Book Korean Translation 현재 한국어 버전 9. 영문 Linux From Scratch Book은 최근 버전 12. 기존 한국어 번역을 업데이트 해서 12 버전 만드는 건 포기. 영문 최근 버전 12 갖고 와서 한국어 버전 만드는 중. 배우는 게 많다. 

https://www.linuxfromscratch.org/lfs/download.html 에서 Current Development 보면서 진행 중. 아직은 잘 모르겠다. 내가 맞는 길로 가고 있는지. 그래도 좋다. 여기 저기 헤매면서 바른 길 찾아가는 과정에서 배우는 게 많다.

시작점은 어디? 바로 아래.

git clone git://git.linuxfromscratch.org/lfs.git

2026-01-15

gpdf.py

pdftk 참 좋은데 명령어 방식이라 좀 불편. PyQt 활용. 간단한 기능만 GUI 버전으로 만들어 봄.

gpdf.py

import sys

import subprocess

from PyQt6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QPushButton, 

                             QListWidget, QFileDialog, QHBoxLayout, QMessageBox)

from PyQt6.QtCore import Qt


class PDFJoiner(QWidget):

    def __init__(self):

        super().__init__()

        self.initUI()


    def initUI(self):

        self.setWindowTitle('PDF 도우미 (pdftk GUI)')

        self.setGeometry(300, 300, 500, 400)


        layout = QVBoxLayout()


        # 파일 목록 표시창

        self.file_list = QListWidget()

        layout.addWidget(self.file_list)


        # 버튼 레이아웃

        btn_layout = QHBoxLayout()

        

        btn_add = QPushButton('파일 추가')

        btn_add.clicked.connect(self.add_files)

        

        btn_remove = QPushButton('삭제')

        btn_remove.clicked.connect(self.remove_item)


        btn_up = QPushButton('위로')

        btn_up.clicked.connect(lambda: self.move_item(-1))


        btn_down = QPushButton('아래로')

        btn_down.clicked.connect(lambda: self.move_item(1))


        btn_layout.addWidget(btn_add)

        btn_layout.addWidget(btn_remove)

        btn_layout.addWidget(btn_up)

        btn_layout.addWidget(btn_down)

        layout.addLayout(btn_layout)


        # 실행 버튼

        self.btn_run = QPushButton('PDF 합치기 실행')

        self.btn_run.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold; height: 40px;")

        self.btn_run.clicked.connect(self.merge_pdfs)

        layout.addWidget(self.btn_run)


        self.setLayout(layout)


    def add_files(self):

        files, _ = QFileDialog.getOpenFileNames(self, "PDF 파일 선택", "", "PDF Files (*.pdf)")

        if files:

            self.file_list.addItems(files)


    def remove_item(self):

        for item in self.file_list.selectedItems():

            self.file_list.takeItem(self.file_list.row(item))


    def move_item(self, direction):

        current_row = self.file_list.currentRow()

        if current_row < 0: return

        

        target_row = current_row + direction

        if 0 <= target_row < self.file_list.count():

            item = self.file_list.takeItem(current_row)

            self.file_list.insertItem(target_row, item)

            self.file_list.setCurrentRow(target_row)


    def merge_pdfs(self):

        count = self.file_list.count()

        if count < 2:

            QMessageBox.warning(self, "알림", "합칠 파일이 2개 이상 필요합니다.")

            return


        save_path, _ = QFileDialog.getSaveFileName(self, "저장 경로 선택", "merged_output.pdf", "PDF Files (*.pdf)")

        

        if save_path:

            input_files = [self.file_list.item(i).text() for i in range(count)]

            

            # pdftk 명령어 구성

            # pdftk file1.pdf file2.pdf cat output combined.pdf

            command = ["pdftk"] + input_files + ["cat", "output", save_path]

            

            try:

                subprocess.run(command, check=True)

                QMessageBox.information(self, "성공", f"성공적으로 합쳐졌습니다!\n경로: {save_path}")

            except Exception as e:

                QMessageBox.critical(self, "오류", f"실행 중 오류가 발생했습니다: {e}")


if __name__ == '__main__':

    app = QApplication(sys.argv)

    ex = PDFJoiner()

    ex.show()

    sys.exit(app.exec())