mirror of
https://github.com/sotam0316/brain_dogfood.git
synced 2026-04-24 19:48:35 +09:00
Fix: Final critical data loss bug in metadata parsing
This commit is contained in:
+22
-14
@@ -17,8 +17,9 @@ def parse_metadata(text, default_group=GROUP_DEFAULT):
|
||||
if group_match:
|
||||
group_name = group_match.group(1)
|
||||
|
||||
# #태그 추출 (마크다운 헤더 및 내부 링크[[#ID]] 방지)
|
||||
tag_matches = re.finditer(r'(?<!#)(?<!\[\[)#(\w+)', text)
|
||||
# #태그 추출 (마크다운 헤더 # , ## 및 내부 링크[[#ID]] 방지)
|
||||
# 태그는 반드시 # 바로 뒤에 영문/숫자/한글이 붙어 있어야 하며, 앞에 다른 문자가 없어야 함
|
||||
tag_matches = re.finditer(r'(?<!#)(?<!\[\[)(?<!\w)#([^\s\#\d\W][^\s\#]*)', text)
|
||||
for match in tag_matches:
|
||||
tags.append(match.group(1))
|
||||
|
||||
@@ -32,11 +33,12 @@ def parse_and_clean_metadata(content, ui_group=GROUP_DEFAULT, ui_tags=None):
|
||||
if not content:
|
||||
return content, ui_group, ui_tags
|
||||
|
||||
# 1. 기존에 생성된 푸터 블록(수평선 + 메타데이터)을 모두 제거
|
||||
# 전후 공백을 제거한 후, 하단의 수평선(---, ***, ___)과 메타데이터 블록을 반복적으로 탐색하여 제거합니다.
|
||||
# 1. 기존에 생성된 푸터 블록(수평선 + 메타데이터)을 제거
|
||||
# 파일의 가장 마지막에 위치한 수평선(---, ***, ___)과 그 뒤에 따라오는 메타데이터($ , #) 줄들만 식별하여 제거합니다.
|
||||
content = content.strip()
|
||||
# 패턴: (공백+수평선+공백 + (메타데이터 또는 공백))이 문자열 끝에 1회 이상 반복
|
||||
content = re.sub(r'(?:\s*[\*\-\_]{3,}\s*(?:[\$\#][\s\S]*?)?\s*)+$', '', content).strip()
|
||||
# 패턴 설명: 줄바꿈 + 수평선 + 줄바꿈 + (줄 시작이 $ 또는 #이며 뒤에 공백이 없는 줄들의 반복) + 끝
|
||||
footer_regex = r'\n+[\*\-\_]{3,}\s*\n(?:^[\$\#][^\s\#].*$(?:\n|$))*$'
|
||||
content = re.sub(footer_regex, '', content, flags=re.MULTILINE).strip()
|
||||
|
||||
# 2. 본문에서 기호 정보 추출
|
||||
content_group, content_tags = parse_metadata(content)
|
||||
@@ -44,8 +46,8 @@ def parse_and_clean_metadata(content, ui_group=GROUP_DEFAULT, ui_tags=None):
|
||||
# 3. 본문에서 기호 패턴 삭제
|
||||
# $그룹 삭제
|
||||
content = re.sub(r'\$\w+', '', content)
|
||||
# #태그 삭제 (헤더 및 내부 링크 제외)
|
||||
content = re.sub(r'(?<!#)(?<!\[\[)#\w+', '', content)
|
||||
# #태그 삭제 (헤더 및 내부 링크 제외, 태그는 # 뒤에 바로 문자가 와야 함)
|
||||
content = re.sub(r'(?<!#)(?<!\[\[)(?<!\w)#([^\s\#\d\W][^\s\#]*)', '', content)
|
||||
content = content.strip()
|
||||
|
||||
# 4. 데이터 통합
|
||||
@@ -75,16 +77,22 @@ def generate_auto_title(content):
|
||||
if not content:
|
||||
return ""
|
||||
|
||||
# 푸터 제외하고 순수 본문만 추출하여 제목 생성
|
||||
main_content = re.split(r'\n+---\n', content)[0].strip()
|
||||
# 푸터 제거 (마지막 수평선 블록 제거 로직과 동일)
|
||||
footer_regex = r'\n+[\*\-\_]{3,}\s*\n(?:^[\$\#][^\s\#].*$(?:\n|$))*$'
|
||||
main_content = re.sub(footer_regex, '', content, flags=re.MULTILINE).strip()
|
||||
if not main_content: return ""
|
||||
|
||||
lines = main_content.split('\n')
|
||||
first_line = lines[0].strip()
|
||||
# 마크다운 헤더 기호(#) 제거
|
||||
first_line = re.sub(r'^#+\s+', '', first_line).strip()
|
||||
# 실제 내용이 있는 첫 번째 줄 찾기 (헤더 기호 제외)
|
||||
title = ""
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
if not stripped: continue
|
||||
# 마크다운 헤더 기호(#) 제거
|
||||
title = re.sub(r'^#+\s+', '', stripped).strip()
|
||||
if title: break
|
||||
|
||||
return first_line[:20]
|
||||
return title[:20]
|
||||
|
||||
def extract_links(text):
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# 버그 조치 보고서: 마크다운 문서 저장 시 데이터 소실 (2026-04-18)
|
||||
|
||||
## 1. 버그 내용
|
||||
- **현상**: 마크다운 형식의 메모(가장 흔하게는 `roadmap.md`)를 저장할 때, 본문 중간에 위치한 수평선(`---`) 이후의 내용이 모두 삭제되는 현상 발생.
|
||||
- **원인**:
|
||||
- 메모 하단에 `$그룹`, `#태그`를 추출하여 푸터로 모아주는 로직이 본문 내의 마크다운 수평선(`---`)과 그 뒤에 나오는 헤더(`# 제목`)를 '자동 생성된 푸터'로 오인함.
|
||||
- 정규식이 너무 탐욕적(Greedy)이었으며, 줄 단위 구분이 명확하지 않아 파일 끝까지의 모든 내용을 푸터로 간주하고 삭제함.
|
||||
|
||||
## 2. 조치 사항
|
||||
### 푸터 식별 로직 정교화 (`app/utils/__init__.py`)
|
||||
- **수단**: 정규식을 파일의 가장 끝에 위치한 **'수평선 + 순수 메타데이터 줄'**의 조합으로만 한정하도록 수정.
|
||||
- **적용**: `re.MULTILINE` 플래그를 활용하여 줄 시작(`^`)과 끝(`$`)을 명확히 구분하고, `#` 뒤에 공백이 있는 헤더는 절대로 푸터 구성 요소로 보지 않도록 개선.
|
||||
- **결과**: 본문 중간의 수평선 및 마크다운 헤더 구조가 완벽히 보존됨.
|
||||
|
||||
### 태그 추출 정규식 개선
|
||||
- **수단**: `#` 뒤에 공백이나 숫자가 먼저 오는 경우(마크다운 헤더 등)를 제외하고, 문자로 시작하는 경우만 태그로 인식하도록 강화.
|
||||
- **적용**: `(?<!\w)#([^\s\#\d\W][^\s\#]*)` 패턴 적용.
|
||||
|
||||
### 자동 제목 생성 로직 개선
|
||||
- **수단**: 첫 번째 `---` 이전만 본문으로 보던 방식에서, 전체 내용 중 가장 상단의 유의미한 텍스트를 찾아 제목으로 사용하도록 변경.
|
||||
|
||||
## 3. 결과 및 확인
|
||||
- 재현 스크립트(`scratch/repro_bug.py`)를 통해 `roadmap.md`와 같은 복합 마크다운 구조에서도 데이터가 삭제되지 않고 정확히 보존됨을 확인.
|
||||
- 의도적으로 추가한 푸터 메타데이터는 정상적으로 제거 후 재배치됨을 확인.
|
||||
|
||||
## 4. 향후 주의사항
|
||||
- 텍스트 후처리 로직 수정 시 마크다운의 특수 기호와 충돌할 가능성을 항상 염두에 두어야 함.
|
||||
- 특히 `[\s\S]*`와 같은 광범위한 매칭은 앵커(`$`)가 있더라도 지양하고, 줄 단위 매칭을 우선할 것.
|
||||
Reference in New Issue
Block a user