Info

옵시디언 tag 를 자동으로 달아주는 방법을 기술한 페이지입니다.

개요

필자는 그래프 뷰를 활용하고 싶어서 고민하던 와중 ‘Tags’ 를 실제 목차별로 달아주고 싶었는데, 다른 블로그를 열심히 보던 와중 python 스크립트를 활용하면 좋을 것 같다는 생각이 들었습니다.

환경설정

필자는 macbook 에서 Homebrew 을 활용하기 때문에, “외부 관리 환경(Externally Managed Environment)“으로 보호되고 있어서 직접 시스템 전역에 패키지를 설치하지 못하게 막아둔 상태라

python 가상환경을 활용하였습니다.

가상환경 생성

pip3 download

curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python3 get-pip.py
 
# 확인
pip3 --version
python3 -m venv ~/venvs/obsidian-tagger
 
# 가상환경 진입
source ~/venvs/obsidian-tagger/bin/activate
 
# 나오기
deactivate

스크립트

tags 제거

import os
import re
 
def remove_tags_from_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as f:
        lines = f.readlines()
 
    in_yaml = False
    new_lines = []
    skip_tags = False
 
    for line in lines:
        if line.strip() == '---':
            in_yaml = not in_yaml
            new_lines.append(line)
            continue
 
        if in_yaml:
            if skip_tags:
                if re.match(r'\s{2,}- ', line):  # 들여쓰기된 태그 항목
                    continue
                else:
                    skip_tags = False
 
            if line.strip().startswith('tags:'):
                skip_tags = True
                continue
 
        new_lines.append(line)
 
    with open(file_path, 'w', encoding='utf-8') as f:
        f.writelines(new_lines)
 
def process_folder(folder_path):
    for root, dirs, files in os.walk(folder_path):
        for file in files:
            if file.endswith('.md'):
                file_path = os.path.join(root, file)
                remove_tags_from_file(file_path)
 
# 사용 경로 (Obsidian 노트 폴더 위치)
folder_path = '비밀입니다'
process_folder(folder_path)

tags 생성

import os
import re
 
def sanitize_tag(tag):
    tag = tag.replace(' ', '_')                     # 공백 → 언더스코어
    tag = re.sub(r'[0-9]', '', tag)                 # 숫자 제거
    tag = re.sub(r'[^\w가-힣_]', '', tag)           # 영문, 한글, 언더스코어 외 제거
    tag = re.sub(r'^_+', '', tag)                   # 맨 앞 언더스코어 제거
    return tag
 
def generate_tags_from_path(file_path, base_folder):
    # 파일 경로에서 base_folder 이후만 추출
    relative_path = os.path.relpath(file_path, base_folder)
    path_parts = relative_path.split(os.sep)
 
    # content가 맨 앞이면 제거
    if path_parts[0] == 'content':
        path_parts = path_parts[1:]
 
    # 파일명 제외 (상위 폴더들만)
    path_parts = path_parts[:-1]
 
    # 정리된 태그 반환
    sanitized_tags = [sanitize_tag(tag) for tag in path_parts]
    return [tag for tag in sanitized_tags if tag]  # 빈 태그 제거
 
def process_file_with_path_tags(file_path, base_folder):
    tags = generate_tags_from_path(file_path, base_folder)
 
    metadata = ['---\n']
    metadata.append('tags:\n')
    metadata.extend([f'  - {tag}\n' for tag in tags])
    metadata.append('---\n')
 
    with open(file_path, 'r', encoding='utf-8') as f:
        content = f.readlines()
 
    # 기존 YAML 메타데이터 제거
    if '---\n' in content:
        start = content.index('---\n')
        try:
            end = content.index('---\n', start + 1)
            content = content[end + 1:]  # 메타데이터 이후 내용만
        except ValueError:
            content = content[start + 1:]  # 끝 --- 없으면 그냥 잘라냄
 
    # 새 내용으로 덮어쓰기
    with open(file_path, 'w', encoding='utf-8') as f:
        f.writelines(metadata + content)
 
# 사용 경로 (Obsidian 노트 폴더 위치)
base_folder = '비밀입니다'
 
for root, dirs, files in os.walk(base_folder):
    for file in files:
        if file.endswith('.md'):
            file_path = os.path.join(root, file)
            process_file_with_path_tags(file_path, base_folder)