강의 결과물을 꾸준히 만들다 보면 어느 순간 파일보다 폴더가 더 어려워진다. 처음에는 강의명, 프로그램명, 분야명, 작업대명을 폴더로 만들면 정리가 잘되는 것처럼 보인다. 하지만 생산 흐름이 늘어나면 같은 의미를 가진 폴더가 조금씩 다른 이름으로 생기고, 새 결과물을 어디에 넣어야 하는지 매번 다시 판단해야 한다.
이번 작업의 출발점도 단순했다.
결과물을 Library에 정리하는 스킬은 무엇인가?
곧이어 더 중요한 문제가 제기됐다.
결과물을 만들 때마다 비슷한 최상위 폴더가 늘어나고 있다. Library Space 규칙과 폴더 분류 규칙이 필요하다.
이 글은 그 문제를 발견한 뒤, 새 폴더 생성을 먼저 막고, 분류 규칙을 숫자 좌표로 고정하고, 기존 파일 614개를 해시 보존 상태로 이관하고, publisher가 옛 구조를 다시 만들지 못하도록 바꾼 전 과정을 정리한 사례다.
1. 문제는 “폴더가 많다”가 아니었다
처음 보인 현상은 여러 최상위 폴더였다.
같은 프로그램에서 나온 AI·IT 강의가 서로 다른 폴더에 저장됨
일반 IT 강의와 프로그램 강의가 별도 루트로 계속 분화됨
제작 작업대 이름이 독자용 분류처럼 사용됨
법률 사례집과 제작 과정 사례글이 비슷한 이름의 위치에 섞임
한 프로그램 이름이 AI·IT와 투자·금융 양쪽에 나타남
폴더를 몇 개 합치는 것만으로는 이 문제가 해결되지 않는다. 다음 결과물이 들어올 때 publisher가 다시 새 폴더를 만들면 원상복구되기 때문이다.
따라서 문제를 세 층으로 나눴다.
탐색 문제: 독자가 어디에서 무엇을 찾아야 하는가?
분류 문제: 같은 공급선 이름이 여러 분야에 존재할 때 어떻게 구분할 것인가?
쓰기 통제 문제: 제작자가 임의의 최상위 폴더를 만들지 못하게 할 수 있는가?
핵심 결론은 다음과 같았다.
폴더를 잘 정리하는 것보다, 등록되지 않은 좌표에는 애초에 발행할 수 없도록 만드는 것이 더 중요하다.
2. 첫 대응: 이동보다 재발 차단을 먼저 했다
초기 단계에서는 기존 파일을 곧바로 옮기지 않았다. 대신 Space 규칙과 registry를 만들고 publisher를 조정해, 등록되지 않은 과정명이 새 루트로 승격되지 않도록 했다.
이때 Library는 아직 전환 상태였다.
새 Space 구조와 legacy root가 공존
미등록 과정 발행은 차단
기존 등록 alias는 당분간 옛 위치를 사용할 수 있음
실제 파일 이동은 별도 migration으로 보류
이 선택은 중요했다. 이미지 자산, manifest, receipt, index가 연결된 상태에서 폴더만 수동으로 옮기면 다음 문제가 생길 수 있기 때문이다.
문서 본문 이미지가 깨짐
manifest가 존재하지 않는 파일을 가리킴
index 링크가 오래된 경로를 유지함
publisher receipt와 실제 목적지가 달라짐
같은 파일이 새 위치와 옛 위치에 이중 존재함
초기 감사에서는 legacy root가 8개였다. 이후 다른 발행과 사례집 분리가 더해져 최종 migration 직전에는 10개가 됐다. 이 숫자 변화는 오류가 아니라, 전환 기간에도 Library가 계속 사용되고 있었다는 사실을 보여준다.
3. 폴더명이 아니라 좌표 체계를 설계했다
최종 구조는 Space → Series → Item으로 정리했다.
10_AI-IT/
11_gpters/
12_governance/
19_independent/
20_legal/
21_civil-procedure/
22_tax-practice/
29_independent/
30_business-finance/
31_gpters/
39_independent/
40_travel/
49_independent/
50_general/
51_cooking/
59_independent/
60_casebook-library/
61_legal/
62_tax/
63_business-finance/
64_AI-IT/
65_travel/
69_general/
70_case-library/
숫자는 장식이 아니라 안정적인 탐색 좌표다.
최상위 Space와 Collection은 10단위 번호를 사용한다.
Series는 그 Space의 번호 구간을 사용한다.
각 구간의 마지막 번호는
independent에 남긴다.AI,IT같은 고유 약어는 대문자로 유지한다.개별 강의나 Item에는 별도 순번을 강제하지 않는다.
이 방식은 폴더명을 바꿀 때마다 전체 정렬이 흔들리는 문제를 줄인다. 또한 새 Series가 필요하더라도 빈 번호를 사용해 기존 좌표를 보존할 수 있다.
4. 가장 어려웠던 분류 결정들
4.1 같은 gpters를 하나로 합치지 않았다
gpters라는 공급선은 AI·IT 강의에도 있고 투자·금융 강의에도 있었다. 이름만 보고 하나의 Series로 합치면 독자 탐색이 오히려 나빠진다.
그래서 Series 이름은 반복될 수 있게 하되, 좌표를 (Space, Series) 복합키로 해석했다.
10_AI-IT/11_gpters
30_business-finance/31_gpters
publisher도 course만 보고 첫 번째 alias를 선택하는 방식에서 벗어나, domain + course를 함께 사용하도록 바꿨다.
4.2 general을 기본 쓰레기통으로 쓰지 않았다
애매한 자료를 모두 general로 보내면 시간이 지나면서 가장 큰 혼합 폴더가 된다. general은 최종 예외 공간으로 남기고, 내용상 분명한 자료는 별도 Series로 보냈다.
예를 들어 요리 강의는 제작 작업대가 어디였는지가 아니라 내용에 따라 다음 좌표를 사용한다.
50_general/51_cooking
반대로 Obsidian·LLM Wiki 관련 강의는 intake 폴더명이 일반적이더라도 내용에 따라 AI·IT 독립 Series로 간다.
4.3 사례집과 사례글을 완전히 분리했다
이 작업에서 자주 혼동될 수 있는 두 유형이 있었다.
사례집: 판례·결정례·행정회신 등 여러 사례를 독자가 참고하도록 묶은 결과물
사례글: 어떤 결과물을 만들고 검증한 과정을 설명하는 제작 사례
둘은 독자 목적이 다르다. 따라서 사례집은 60_casebook-library, 제작 과정 사례글은 파일 단일 계층인 70_case-library로 분리 했다.
이 글 자체도 사례집이 아니라 제작 과정 사례글이므로 70_case-library 대상이다.
5. 실제 이동 전에 읽기 전용 감사를 했다
migration 전에 Library 전체를 읽기 전용으로 조사했다.
당시 결과는 다음과 같았다.
전체 파일: 707개
.DS_Store제외: 699개legacy content root 아래 파일: 614개
legacy 또는 미확정 최상위 content root: 10개
새 번호형 content root: 아직 0개
예상 목적지 충돌: 0개
분류별 예상 이동량도 계산했다.
목적지
활성 manifest 행
물리 파일
10_AI-IT/11_gpters
25
46
10_AI-IT/12_governance
6
6
10_AI-IT/19_independent
7
7
20_legal/21_civil-procedure
54
54
20_legal/22_tax-practice
69
365
20_legal/29_independent
6
82
30_business-finance/31_gpters
2
2
50_general/51_cooking
2
5
60_casebook-library/61_legal
4
4
70_case-library
25
39
당시 미해결 routing
4
4
합계
204
614
이 시점에는 Obsidian·LLM Wiki 관련 summary·transcript 4개가 미해결 상태였다. 이름만으로 general에 넣지 않고 내용을 다시 확인한 뒤, 최종적으로 10_AI-IT/19_independent의 같은 Item으로 분류했다. 즉, 예상 경로 충돌은 0개였지만 의미 분류가 끝나지 않은 자료는 별도의 Gate로 남겨 두었다.
이 감사에서 단순한 폴더 이동보다 더 큰 문제가 발견됐다.
이름이 바뀌었지만 manifest가 옛 파일명을 가리키는 사례글
manifest에 등록되지 않은 기존 사례글
존재하지 않는 과거 연결 자산
receipt 전체에 남아 있는 legacy destination
Obsidian workspace의 오래된 활성 경로
publisher가 legacy alias를 정상 목적지로 다시 만들 수 있는 로직
registry가 없을 때
root/course로 fallback하는 위험
즉, migration은 파일 614개를 옮기는 작업이 아니라 파일·해시·manifest·receipt·index·workspace·publisher를 하나의 트랜잭션으로 바꾸는 작업이었다.
6. migration 도구를 먼저 격리 환경에서 실 패시켰다
실 Library에 바로 적용하지 않고 strict 후보 registry와 migration 스크립트를 임시 작업대에서 만들었다. 이후 fixture Library에서 반복 실행했다.
후보 실행 과정에서는 이동 후 검증할 자산 집합을 이동 전 경로 기준으로 고정해 둔 부분과, 기존 상태에서 이미 불일치하던 자산 이력을 새 migration 실패로 혼동한 문제가 드러났다.
수리 방향은 다음과 같았다.
이동 전 자산 건강 상태를 먼저 스냅샷한다.
Library 내부 경로는 old→new mapping으로 변환한다.
외부 canonical source는 이동하지 않는다.
기존부터 누락됐던 자산은 새 migration 손상과 구분한다.
해시가 일치하는 다른 파일이 있으면 명시적 복구 대상으로 기록한다.
fixture에서는 Library 내부 절대경로만 fixture root로 rebasing한다.
active asset 처리와 precomputed issue 판정을 수정하고 fixture를 다시 구성한 뒤, strict 후보 migration의 성공 상태를 확인했다.
이 실패들은 불필요한 우회가 아니었다. 실 Library에서 같은 오류가 발생하기 전에 도구의 잘못된 전제를 드러낸 안전장치였다.
7. publisher를 바꾸지 않으면 migration은 끝나지 않는다
파일을 새 위치로 옮겨도 publisher가 옛 폴더를 다시 만들 수 있다면 구조 전환은 완료가 아니다.
공용 publisher에는 다음 변경을 적용했다.
strict schema와 strict status를 가진 registry만 허용
registry가 없거나 잘못되면 fallback하지 않고 중단
domain + course복합키로 Series 해석legacy alias는 새 번호형 좌표로만 정규화
casebook은60_casebook-library로 분배case-study,case-post,case-article은70_case-library로 분배civil-procedure의 course-flat 예외도 registry에서 읽음미등록 좌표는
needs-review또는 exit code 2로 차단차단된 요청은 inbox에 찌꺼기를 남기지 않음
감사 스크립트도 강화했다.
legacy root 존재 시 실패
활성 control 문서의 stale path 검사
manifest·destination·asset·receipt·index 정합성 검사
archive의 역사적 legacy 문자열은 활성 실패와 구분
미등록 실제 artifact 검사
이로써 구조 규칙이 문서에만 있는 것이 아니라 실행 경로에 들어갔다.
8. 실 Library migration
실행 시점에는 publication lock을 사용하고, 기존 payload와 control 파일을 archive에 먼저 백업했다. 그 뒤 다음 순서로 진행했다.
old→new mapping 확정
→ 충돌 0 확인
→ payload·manifest·index·registry·receipt 백업
→ 파일 이동
→ source/destination/copied asset 경로 재작성
→ manifest 재생성·보정
→ receipt 갱신
→ human index 재생성
→ 빈 legacy root 제거
→ strict audit
실행 결과는 다음과 같았다.
이동 파일: 614개
복구 파일: 1개
기존 미등록 artifact 신규 등록: 5개
receipt 갱신: 54개
활성 index 항목: 207개
legacy root 잔여: 0개
기존 사례글 중 manifest가 옛 이름을 가리키던 항목은 해시가 같은 실제 파일명으로 연결했다. 이미 덮어써져 원래 해시를 회복할 수 없는 과거 이력은 현재 artifact로 위장하지 않고 비활성 history로 보존했다.
기존 비활성 이력 4행도 삭제하지 않았다. 이 가운데 3행은 migration 이전부터 존재하던 연결 자산 문제였고, 1행은 이미 덮어써진 과거 destination 이력이었다. 활성 결과물의 건강 상태와 역사적 불완전 기록을 분리해, 현재 index에는 노출하지 않되 이력은 남겼다.
9. “파일이 있다”가 아니라 여덟 층을 검증했다
최종 검증은 단순한 파일 존재 검사로 끝내지 않았다.
백업 parity: 백업 payload 614개와 새 목적지의 바이트·해시 비교
destination: 활성 목적지 존재 여부
manifest hash: 목적지와 기록 해시 일치
source: 활성 source 존재 여부
assets: 연결 자산 존재·해시 일치
receipt: 옛 destination이나 누락 목적지 여부
index: 정확히 인코딩된 링크 존재 여부
publisher non-regression: 새 발행이 옛 root를 만들지 않는지
최종 결과는 STRICT_PASS였다.
백업 parity
대상 파일: 614개
새 목적지 누락: 0개
해시 불일치: 0개
strict audit
manifest 행: 214개
활성 destination: 207개
비활성 이력 행: 4개
미등록 최상위 디렉터리: 0개
legacy root: 0개
미등록 Series: 0개
활성 destination 누락: 0개
활성 hash 불일치: 0개
활성 source 누락: 0개
연결 자산 누락·불일치: 0개
unresolved asset: 0개
manifest 좌표 불일치: 0개
index 링크 누락: 0개
미등록 artifact: 0개
stale receipt: 0개
stale 활성 control reference: 0개
publisher smoke matrix
전체 등록 경로 fixture promotion: PASS
AI·IT
gpters와 투자·금융gpters분리: PASS요리 강의 경로: PASS
법률 사례집 경로: PASS
제작 과정 사례글 경로: PASS
미등록 경로 fail-closed: PASS
차단 시 inbox 불변: PASS
legacy alias 입력이 새 번호형 좌표로 staging: PASS
10. 숫자가 계속 달라졌던 이유
진행 기록을 보면 manifest와 legacy root 수가 단계마다 다르다.
초기 전환 단계: manifest 168행, legacy root 8개
최종 계획 시점: manifest 208행, 고유 destination 203개
migration 직전 감사: manifest 209행, legacy/미확정 root 10개
최종 strict 상태: manifest 214행, 활성 destination 207개, legacy root 0개
이 차이는 같은 시점의 수치를 잘못 센 것이 아니다. 전환 기간에 Library가 계속 사용됐고, migration 과정에서 기존 미등록 artifact와 복구 항목을 정식 등록했기 때문이다.
그래서 운영 중인 Library에서는 “총 행 수가 예전 보고와 같은가?”보다 다음 질문이 더 중요하다.
특정 destination이 하나의 활성 좌표로 해석되는가?
source·destination·manifest hash가 일치하는가?
index가 활성 destination만 가리키는가?
역사적 행과 현재 활성 행이 구분되는가?
11. 이 사례에서 배운 일곱 가지
1. 폴더 구조는 정보구조이자 쓰기 권한 체계다
탐색용 문서만 고쳐서는 새 폴더 증식을 막을 수 없다. publisher가 registry 밖의 좌표를 거부해야 한다.
2. 먼저 재발을 차단하고, 나중에 이동한다
전환 기간에 legacy root를 즉시 없애지 못하더라도 새 root 생성을 먼저 막으면 문제의 증가 속도를 0으로 만들 수 있다.
3. 같은 이름은 같은 의미가 아닐 수 있다
gpters처럼 공급선 이름이 같아도 분야가 다르면 다른 Series가 될 수 있다. 전역 alias 하나보다 (Space, Series) 복합키가 안전하다.
4. intake lane은 독자용 분류가 아니다
작업대명, dropzone명, 제작자명은 provenance metadata로 남기고 최종 폴더는 내용으로 결정해야 한다.
5. 사례집과 사례글은 별도 artifact class다
여러 사례를 묶은 참고자료와 제작 경험을 설명하는 글을 같은 collection에 넣으면 독자 기대가 충돌한다.
6. migration은 파일 이동이 아니라 증거 변환이다
백업, 해시, manifest, receipt, index, workspace, publisher를 함께 바꾸지 않으면 이동은 완료가 아니다.
7. 실패한 fixture는 비용이 아니라 보험이다
반복된 migration 후보 실패가 실 Library 손상을 막았다. 실패 원인을 회귀 규칙으로 바꿔야 같은 전제가 다시 들어오지 않는다.
12. 재사용 가능한 migration 체크리스트
설계
탐색 축이 하나로 일관되는가?
폴더와 metadata의 역할이 분리됐는가?
독립 Collection이 필요한 artifact class를 구분했는가?
번호와 slug가 장기적으로 안정적인가?
사전 감사
모든 물리 파일과 active manifest destination을 조사했는가?
old→new mapping을 이동 전에 계산했는가?
목적지 충돌과 hash-identical 중복을 구분했는가?
기존부터 깨진 asset과 migration이 만든 손상을 구분했는가?
실행
publisher를 잠갔는가?
payload와 control 파일을 모두 백업했는가?
source·destination·asset 경로를 함께 변환했는가?
receipt와 index를 다시 만들었는가?
legacy root는 비어 있음을 확인한 뒤 제거했는가?
완료
백업과 새 목적지의 hash parity가 통과했는가?
활성 destination·source·asset 누락이 0인가?
manifest·receipt·index가 같은 좌표를 가리키는가?
unknown route가 fail-closed 되는가?
legacy alias 입력이 옛 root를 만들지 않는가?
archive의 역사와 active 상태를 구분했는가?
맺음말
이 작업은 폴더 10개를 몇 개의 숫자 폴더로 바꾸는 정리 작업처럼 보일 수 있다. 실제로는 Library가 스스로 질서를 유지하게 만드는 운영 계약을 만든 작업이었다.
최종 구조가 안정된 이유는 이름을 예쁘게 통일했기 때문이 아니다.
좌표를 registry로 고정하고
publisher가 그 좌표만 쓰게 만들고
기존 파일을 백업과 해시로 옮기고
manifest·receipt·index를 같은 트랜잭션에서 갱신하고
알 수 없는 입력을 추측하지 않고 중단하게 했기 때문이다.
정리의 완성은 폴더가 깔끔해 보이는 순간이 아니다.
다음 결과물이 들어와도 구조가 다시 흐트러지지 않는 순간이 진짜 완료다.
검증 범위와 한계
이 사례의 수치와 PASS 판정은 migration report, final verification, manifest·receipt·index read-back에 근거한다. 개인의 탐색 시간 절감이나 생산성 향상은 별도로 측정하지 않았으므로 정량 효과로 주장하지 않는다. 보호형 웹 미러의 재배포 여부는 Library migration과 별도의 운영 상태이며, 이 글의 완료 판정에는 포함하지 않았다.