옛 블로그 글 개인정보·저작권 점검: 이미지 85개를 뺀 기록
2021년 글 67편을 다시 공개하기 전에 본문과 이미지를 점검했다. 컴파일러 경로 속 Windows 계정 이름, 예제 데이터의 실명, 퍼온 것 같은 그림. 확인 방법과 유지·교체·삭제 기준.
옛날 블로그 글을 다시 공개하거나 번역하기 전에 먼저 해야 할 점검이 있습니다. 몇 년 전에 쓴 강좌에는 그때는 아무도 신경 쓰지 않은 개인정보와 저작권 위험이 남아 있을 수 있습니다. 저희는 2021년에 쓴 프로그래밍 강좌 67편을 영어·일본어·한국어 새 글로 보강했고, 다시 공개하기 전에 모든 글의 본문과 쓰인 이미지를 하나도 빠짐없이 점검했습니다. 그 결과 실명, Windows 계정 이름, 이미지 85개를 걷어냈습니다. 이 글은 무엇을 찾았고 어떻게 확인했는지에 대한 기록입니다.
보강 작업은 병렬로 돌린 AI 서브 에이전트가 했고, 그 과정은 AI 에이전트 18개로 2021년 글 67편을 병렬 보강한 방법에 정리했습니다. 에이전트마다 공통 브리프에 따라 맡은 글을 점검했고, 배포 전에 메인 세션이 사이트 전체를 다시 확인했습니다. 당연히 아래에서는 이름과 경로를 일반화해서만 씁니다.
찾은 것
| 무엇이 | 어디에 | 조치 |
|---|---|---|
| Windows 계정 이름 | 붙여 넣은 컴파일 오류, 자기 경로를 출력하는 프로그램의 결과(C:\Users\<name>\Documents\Visual Studio …). 여러 글 |
지금 컴파일러로 새로 뽑은 출력으로 바꾸고 경로는 줄임 |
| 같은 종류의 경로가 찍힌 이미지 | 2021년 예외 처리 스크린샷 | 이미지 삭제, 글에는 .NET 10 실제 출력 |
| 서로 다른 계정 이름 두 개 | 2021년 글 전체 | 둘 다 모든 곳에서 삭제 |
| 글쓴이 이름을 예제 데이터로 | 글 2편의 예제 코드 값 | 중립적인 예제 이름으로 교체 |
| 실존 연예인 이름 | 글 1편의 예제 데이터 | 교체 |
| 로그인한 계정의 프로필 사진 | IDE 메인 창 스크린샷 | 이미지 삭제 |
| 다른 사람의 사용자 폴더 | 설치 글에 가져다 쓴 업체 홍보 이미지 | 이미지 삭제 |
| 교재 같은 그림 | 그래프, 미로, 큐, 객체 그림, "그림 3."이라는 캡션이 붙은 그림 | 직접 만든 그림으로 교체 |
| 업체 튜토리얼 화면 | 파일 이름과 빨간 상자 표시가 업체 공식 문서와 같은 2장 | 교체 |
| 스톡 사진과 상품 사진 | 줄 선 사람들의 3D 스톡 이미지, 디자이너 책장 상품 사진 | 삭제 |
| 게임 화면 | 지금은 OS에 들어 있지 않은 게임의 스크린샷 | 삭제 |
| 손으로 그린 글자 모양 | 누군가의 이니셜일 수도 있는 콘솔 그림 | 혹시 몰라 삭제 |
놀란 점이 두 가지 있었습니다. 첫째, 위험한 본문은 설명 문장이 아니라 언제나 붙여 넣은 출력이었습니다. "컴파일러가 낸 걸 그대로 붙였을 뿐"이라 글쓴이가 읽지 않고 복사하기 때문입니다. 둘째, 새로 뽑은 출력에도 로컬 경로가 들어갑니다. Win32 글을 위해 새로 받은 컴파일러 출력에 툴체인 설치 경로가 들어 있었고, 에이전트가 공개 전에 …/winuser.h로 줄였습니다. 코드를 다시 돌린다고 출력이 저절로 깨끗해지지는 않습니다.
본문: 패턴 검색과 그 한계
에이전트는 각자 맡은 글을 검사했고, 웨이브가 끝날 때마다 메인 세션이 사이트 전체를 검사했습니다. 쓴 패턴은 특정 이름이 아니라 구조를 찾기 때문에 누구 환경에서든 그대로 쓸 수 있습니다.
"""Scan Markdown posts for text that often leaks personal data. Prints counts, not the values."""
import re, sys, pathlib, collections
RULES = {
"windows-user-folder": r"[Cc]:\\+[Uu]sers\\+[^\\\s<>]+",
"unix-home-dir": r"/(?:Users|home)/[a-z][\w.-]+",
"email": r"[\w.%+-]+@[\w-]+(?:\.[\w-]+)+",
"temp-dir": r"/var/folders/\w+",
}
ALLOW = re.compile(r"example\.|/me\b|\bid@") # placeholders we wrote on purpose
root = pathlib.Path(sys.argv[1])
hits = collections.Counter(); files = collections.defaultdict(set); allowed = 0
for p in root.rglob("*.md"):
for n, line in enumerate(p.read_text(encoding="utf-8").splitlines(), 1):
for name, rx in RULES.items():
for m in re.finditer(rx, line):
if ALLOW.search(m.group(0)):
allowed += 1
continue
hits[name] += 1; files[name].add(str(p))
print("files scanned:", sum(1 for _ in root.rglob("*.md")))
for name in RULES:
print(f"{name:20} {hits[name]:3} hits in {len(files[name])} files")
print("placeholders skipped:", allowed)
오늘 블로그의 content/ 폴더에 돌린 결과입니다(Python 3.9, 표준 라이브러리만).
files scanned: 259
windows-user-folder 3 hits in 3 files
unix-home-dir 0 hits in 0 files
email 9 hits in 6 files
temp-dir 0 hits in 0 files
placeholders skipped: 27
남은 12건은 눈으로 확인했습니다. Windows 3건은 설치 글에서 "옛 스크린샷에 사용자 폴더가 찍혀 있었다"고 설명하는 부분의 C:\Users\…로, 이름 자리는 말줄임표입니다. 이메일 9건은 허용 목록이 못 거르는 대문자 Dev@Example.com과, 정규식 글에서 일부러 쓴 시험용 주소(일부는 비ASCII 도메인)였습니다. 이런 판정은 스캔이 할 수 없습니다. 스캔이 하는 일은 사람이 읽을 수 있을 만큼 목록을 줄이는 것까지입니다.
이 스캔으로는 이름을 잡을 수 없습니다. 운영자 이름을 찾던 첫 사이트 전체 검색은 단어 경계 패턴이었는데, 예제 식별자 안에서 이름이 다른 단어에 붙어 있던 글 1편을 놓쳤습니다. 그것을 찾은 것은 글을 읽던 에이전트였습니다. 패턴을 넓혀 다시 검색했고, 배포 전 마지막 전체 검사에서는 아무것도 나오지 않았습니다. 이름은 이름마다 패턴을 만들어 대소문자 구분 없이, 단어 경계 없이 찾아야 합니다. 그리고 예제 데이터는 사람이 읽어야 합니다. 대충 넣은 것 같은 예제 값이야말로 실명이 숨어드는 곳입니다.
이미지: 하나도 빠짐없이 연다
작업 환경에 OCR이 없어서 이미지 점검은 자동화할 수 없었고, 그래도 괜찮다고 판단했습니다. 브리프에서 에이전트마다 2021년 글이 참조하는 이미지를 모두 열어 보고 판단하라고 했습니다. 마지막 6편 배치를 시작할 때 옛 이미지 폴더에는 파일이 114개 있었습니다.
이미지를 열어 보니 본문 검색으로는 절대 못 찾을 문제가 나왔습니다. 프로필 사진에는 글자가 없습니다. 스크린샷 속 사용자 이름은 픽셀입니다. 교재에서 옮긴 그림에는 워터마크도 없고, 그냥 교재 같은 모양새일 뿐입니다. 에이전트들은 본 그대로 보고했습니다. "사용자 폴더 경로가 찍힌 Microsoft 홍보 이미지", "로그인한 계정의 프로필 사진이 보이는 VS2019 메인 창", "줄 선 사람들의 3D 스톡 이미지".
이미지마다 같은 규칙을 같은 순서로 적용했습니다.
개인정보를 먼저 보는 이유는 다른 장점으로 메울 수 없기 때문입니다. 정확하고 쓸모 있고 우리가 만든 스크린샷이라도 계정이 보이면 뺍니다. 그다음에는 분명히 우리 것이고 지금도 정확한지를 봅니다. 우리가 만들었다는 것만으로는 부족합니다. 제약 조건 표가 틀린 그림, 2차원 배열인데 가변 배열 같은 인덱스를 보여 주던 그림도 뺐습니다.
유지·교체·삭제 기준
| 이미지 상태 | 판단 | 이번 사례 |
|---|---|---|
| 사용자 이름, 폴더 경로, 이메일, 프로필 사진이 보인다 | 정확해도 삭제 | 로컬 경로가 찍힌 예외 화면, 프로필 사진이 보이는 IDE |
| 제3자의 것(스톡, 상품, 게임 화면, 업체 홍보) | 삭제 | 큐 스톡 이미지, 책장 사진, 업체 홍보 이미지 |
| 퍼왔을 가능성(교재 같은 모양, 업체 튜토리얼 스타일) | 직접 만든 그림으로 교체 | BFS/DFS 글의 그래프·미로 그림 |
| 우리가 만들었지만 틀렸다 | 교체 | 오류가 있는 제약 조건 표 |
| 우리가 만들었지만 낡았다(옛 설치 화면, 옛 OS) | 삭제, 개념이 필요하면 교체 | 2019년 설치 안내의 설치·IDE 화면 11장 |
| 우리가 만들었고 정확하며 라벨이 한국어뿐 | 한국어 글에만 유지 | 한국어 라벨의 제네릭 그림 |
| 우리가 만들었고 정확하며 언어와 무관 | 모든 언어에서 유지 | 1차원 배열 그림 |
아래 두 줄이 중요합니다. 전부 지우는 점검은 쉽지만, 진짜 작업물까지 잃습니다. 마지막 배치를 시작할 때 디스크에 있던 이미지 중 28개는 보강한 글에서도 계속 쓰고 있습니다.
코드로 그리는 그림으로 교체
개념 설명에 필요했던 그림은 모두 YAML로 정의하고 빌드할 때 SVG로 그리는 그림으로 바꿨습니다. 상자, 화살표, 막대그래프를 세 언어로 그리고, 다크 모드와 휴대폰 배치도 지원합니다. 보강한 67편에 이런 그림이 모두 108개 들어갔습니다. 텍스트에서 그리기 때문에 저작권 답도 간단합니다. 우리가 쓴 것입니다. 이름은 example.com, <app> 같은 자리표시자뿐이라 프로필 사진이나 경로가 섞여 들어갈 일도 없습니다.
그림이 하는 역할도 바뀌었습니다. 옛 이미지는 대부분 IDE나 설치 프로그램의 한순간을 찍은 스크린샷이라 금방 낡습니다. 새 그림은 개념을 보여 줍니다. 가변 배열이 메모리에 어떻게 놓이는지, 탐욕 알고리즘이 어디서 실패하는지. 이런 것은 낡지 않습니다.
백업, 서버 삭제, 404 확인
글에서 이미지를 뺀다고 인터넷에서 사라지지는 않습니다. 파일은 옛 주소에 남고, 그 주소가 이미 이미지 검색에 올라 있을 수도 있습니다. 그래서 삭제는 네 단계로 했습니다.
- 안 쓰는 파일 목록 만들기. 옛 이미지 폴더의 모든 파일과 글 속
/static/img/legacy/…참조를 짧은 스크립트로 맞춰 봅니다. - 지우지 말고 로컬 백업으로 옮기기. 판단이 틀렸어도 원본은 복사 한 번이면 돌아옵니다.
- prune을 붙여 배포 드라이런. 배포 스크립트는 자기가 올린 파일만, 허용 목록에 있는 경로 아래에서만 지웁니다. 드라이런이 정확한 목록을 내놓으니, 이미지뿐인지 확인했습니다.
prune would delete: 74 | non-image: []. - 배포 후 지운 주소를 요청해서 404 확인. 첫 번째는 로컬 경로가 찍힌 스크린샷, 두 번째는 프로필 사진이 보이는 IDE 스크린샷이었습니다.
404 /static/img/legacy/2021/06/20190404_171228_204.png
마지막 단계는 운영자 몫입니다. 호스팅 관리 화면에서 CDN 캐시를 비웁니다. CDN은 캐시가 만료될 때까지 지운 파일을 계속 내보낼 수 있기 때문입니다.
두 번의 배포를 합치면 첫 번째 11개, 두 번째 74개로 85개를 백업하고 서버에서 지웠습니다. 모두 로컬에 남겨 두었습니다. 그런데 지워졌어야 할 파일 하나가 남아 있었습니다. 이 글을 쓰면서 같은 미사용 이미지 스크립트를 돌린 결과입니다.
29 files, 28 referenced, 1 unused
/static/img/legacy/2021/07/st.jpg
디자이너 책장 상품 사진입니다. 안 쓰는 파일 75개 목록에는 들어 있었는데, 옮기는 셸 루프가 목록의 마지막 줄을 읽지 않았습니다. 파일 끝에 줄바꿈이 없었기 때문입니다. 어느 글에서도 쓰지 않는데 서버의 옛 주소에는 남아 있었습니다. 이 글을 올리는 배포에서 함께 지웁니다. 루프 버그 이야기는 AI 에이전트의 첫 진단이 틀렸을 때에 썼습니다. 점검의 교훈은, 목록에 올린 수, 옮긴 수, 서버가 지운 수를 비교하라는 것입니다.
솔직한 한계
- "퍼온 것 같다"는 판단이지 증명이 아닙니다. 모든 그림의 출처를 끝까지 추적하지는 않았고, 자신 있게 우리 것이라 말할 수 없는 것은 뺐습니다. 그중에는 아마 우리가 만든 것도 있었을 겁니다.
- OCR 없음. 이미지 속 글자는 한 장씩 눈으로 읽었습니다. 114장이라 가능했지, 수천 장이면 안 됩니다.
- 웹은 잊지 않습니다. 서버에서 지우면 우리 서버가 내보내는 것만 멈춥니다. 검색엔진 캐시나 웹 아카이브의 사본은 그대로입니다. 정말 민감한 정보라면 검색엔진에 삭제를 요청하는 일이 따로 필요합니다.
- 법률 자문이 아닙니다. 우리 글에 보수적인 규칙을 적용했을 뿐입니다. 특정 이미지가 정당한 인용인지는 자격 있는 사람이 답할 문제입니다.
- 스캔은 그 시점의 기록입니다. 위 본문 스캔은 글을 쓰던 시점의 파일 259개가 대상이었습니다. 새 글에도 같은 스캔이 필요해서, 지금은 모든 에이전트 브리프에 들어 있습니다.
정리하면 이렇습니다. 붙여 넣은 출력을 읽고, 이미지는 전부 열어 보고, 그림은 직접 그리고, 로컬 백업을 남기고, 삭제는 404와 개수로 증명한다.
자주 묻는 질문
옛 프로그래밍 글의 어디에 개인정보가 숨어 있나요?
대부분 붙여 넣은 도구 출력입니다. Windows 사용자 폴더가 들어간 컴파일 오류와 스택 추적, 로그인한 계정의 프로필 사진이 찍힌 IDE 스크린샷, 글쓴이가 자기 이름을 넣은 예제 데이터입니다. 쓰는 동안에는 어느 것도 개인정보처럼 보이지 않습니다.
글에서 이미지만 빼면 충분한가요?
아닙니다. 참조를 지워도 파일은 옛 주소에 남고, 검색엔진이 그 주소를 이미 알고 있을 수 있습니다. 저희는 안 쓰는 파일을 로컬 백업으로 옮기고, 드라이런을 먼저 한 뒤 서버에서 지우고, 주소가 404를 돌려주는지 확인했습니다. 마지막으로 운영자가 CDN 캐시를 비웁니다.
저작권 위험이 있는 이미지는 어떻게 판단했나요?
2021년 이미지의 출처를 하나하나 증명할 수는 없어서 규칙을 정했습니다. 분명히 우리가 만든 것이고 지금도 정확한 것만 남긴다. 교재 같은 그림, 업체 튜토리얼 화면, 스톡 사진, 상품 사진은 코드로 그리는 우리 그림으로 바꿨습니다.