소개
옵시디언을 공부를 하면서 일상 생활과 공부에 관한 것들을 하나씩 정리를 하고 있습니다.
그런데 지피터스에서 발송하는 메일의 내용을 하나씩 따로 저장을 하는 것이 귀찮더라구요.
마침 템플릿에 대해 공부를 하다가 AI로 만들어보기로 했습니다.
생각보다 결과물이 잘 나온 것 같아서 소개 겸 공유를 하려고 사례글 작성합니다.
진행 방법
지금 구독을 하고 있는 서비스가 클로드라서 그냥 클로드로 시도를 했습니다.
제일 처음에는
1단계: 요구사항 분석
입력 형식: 18기 AI창업패키지\nhttps://www.youtube.com/watch?v=t123123(혹시 몰라 실제 주소에서 변경)
분해 결과: 18기 (폴더명) + AI창업패키지 (노트명) + URL
출력 형식:
폴더: 18기
노트: AI창업패키지.md
내용: - 1주차 \n
중복 처리: 같은 노트가 있으면 다음 주차 번호로 추가라는 형식으로 클로드가 요구사항 정리를 하도록 프롬프트를 입력했고 추가로
1단계: 요구사항 재분석
입력 형식: 여러 개의 항목이 한번에 입력됨
18기 AI창업패키지
https://www.youtube.com/watch?v=tP7C7Nc7KPg
18기 블로그글쓰기
https://www.youtube.com/watch?v=BsAMwyfoYjA
18기 후킹영상
https://www.youtube.com/watch?v=N90CVH21i-s
...
각 항목 처리:
18기 (공통 폴더명) + AI창업패키지 (노트명) + URL
18기 (공통 폴더명) + 블로그글쓰기 (노트명) + URL
18기 (공통 폴더명) + 후킹영상 (노트명) + URL
결과: 18기 폴더 안에 여러 개의 노트들이 생성되거나 기존 노트에 주차가 추가됨라는 형식으로 변경을 했습니다. 그런데 생기는 문제가 입력이 한 줄로 붙어서 들어가는데 제대로 처리가 안 되어서 파싱이 안 되더라구요. 그래서 다시 코드를 수정했습니다.
이 후로는 노트명 수정/ 저장 위치 변경 등을 하다가 예외 사항으로 몇 주차인지 지정을 해서 만약 노트에는 2주차가 저장이 되어 있는데 1주차를 나중에 추가를 할 때 문제가 생길 것 같아서 처음에 주차를 입력을 하도록 변경했습니다. 그리고 혹시 중복된 내용이 있을 것 같아서 이 부분도 같이 처리했습니다.
아래는 최종 템플릿 코드입니다.
<%*
// 주차 선택 입력받기
const weekInput = await tp.system.prompt("몇 주차인가요? (숫자만 입력):");
if (!weekInput) {
new Notice("주차 입력이 취소되었습니다.");
return "";
}
const selectedWeek = parseInt(weekInput);
if (isNaN(selectedWeek) || selectedWeek < 1) {
new Notice("올바른 주차 번호를 입력해주세요.");
return "";
}
// 강의 목록 입력받기
const input = await tp.system.prompt("강의 목록을 붙여넣기 하세요:");
if (!input) {
new Notice("입력이 취소되었습니다.");
return "";
}
// 기본 저장 경로 설정
const basePath = "10. Collections/77. Online Class/771. Gpters";
// 입력 텍스트 전처리 - 공백으로 구분된 항목들을 분리
const text = input.trim();
// 정규식으로 패턴 매칭: "숫자기 과목명 URL" 형태
const pattern = /(\d+기)\s+([^\s]+)\s+(https?:\/\/[^\s]+)/g;
const items = [];
let match;
while ((match = pattern.exec(text)) !== null) {
const folder = match[1]; // 예: "18기"
const subject = match[2]; // 예: "AI창업패키지"
const url = match[3]; // URL
items.push({ folder, subject, url });
}
let processedCount = 0;
let skippedCount = 0;
// 각 항목 처리
for (const item of items) {
const folderPath = `${basePath}/${item.folder}`;
const fileName = `${item.folder}_${item.subject}`; // 18기_노트명 형식으로 변경
const filePath = `${folderPath}/${fileName}.md`;
try {
// 기본 경로가 없으면 생성
const basePathExists = app.vault.getAbstractFileByPath(basePath);
if (!basePathExists) {
await app.vault.createFolder(basePath);
}
// 기수 폴더가 없으면 생성
const folderExists = app.vault.getAbstractFileByPath(folderPath);
if (!folderExists) {
await app.vault.createFolder(folderPath);
}
// 파일이 있는지 확인
const existingFile = app.vault.getAbstractFileByPath(filePath);
if (existingFile) {
// 기존 파일이 있으면 내용을 읽어서 처리
const content = await app.vault.read(existingFile);
// 중복 체크: URL이 이미 존재하는지 확인
if (content.includes(item.url)) {
console.log(`Skipped: ${item.subject} - URL already exists`);
skippedCount++;
continue;
}
// Properties 부분과 본문 분리
const propertiesMatch = content.match(/^---[\s\S]*?---\n\n/);
const properties = propertiesMatch ? propertiesMatch[0] : '';
const bodyContent = propertiesMatch ? content.replace(propertiesMatch[0], '') : content;
// 기존 주차들을 찾아서 배열로 만들기
const lines = bodyContent.split('\n');
const weekBlocks = [];
let currentBlock = null;
for (const line of lines) {
const weekMatch = line.match(/^- #### (\d+)주차/);
if (weekMatch) {
if (currentBlock) {
weekBlocks.push(currentBlock);
}
const weekNum = parseInt(weekMatch[1]);
currentBlock = { weekNum, content: [line] };
} else if (currentBlock) {
currentBlock.content.push(line);
} else if (line.trim() !== '') {
// Properties 외 기타 내용
}
}
if (currentBlock) {
weekBlocks.push(currentBlock);
}
// 새 주차 블록 추가
weekBlocks.push({
weekNum: selectedWeek,
content: [`- #### ${selectedWeek}주차`, `\t`]
});
// 주차 순서대로 정렬
weekBlocks.sort((a, b) => a.weekNum - b.weekNum);
// 최종 내용 구성
const sortedContent = weekBlocks.map(block => block.content.join('\n')).join('\n');
const finalContent = properties + sortedContent;
await app.vault.modify(existingFile, finalContent);
processedCount++;
} else {
// 새 파일 생성 - Properties와 태그 포함
const newContent = `---
tags:
- Gpters
- 지피터스
- ${item.folder}
---
- #### ${selectedWeek}주차
\t`;
await app.vault.create(filePath, newContent);
processedCount++;
}
} catch (error) {
console.error(`Error processing ${item.subject}:`, error);
}
}
new Notice(`${processedCount}개 항목 처리 완료, ${skippedCount}개 항목 중복으로 스킵됨`);
return "";
%>원하시는데로 기본 저장 위치만 변경해서 사용하시면 될 듯 합니다.
결과와 배운 점
생각보다 많은 부분의 반복 작업이 조금만 고민을 한다면 해결이 될 수 있겠다라는 생각이 들었습니다. 제 스스로 체계를 정리를 한다는 느낌으로 정리를 해 보기 위해서 옵시디언을 공부하고 있지만 재미있는 작업이 될 것 같습니다.