목차

  1. Bear 간택 이유
  2. Bear 단점
  3. 마이그래이션을 잘 하는 방법이 없을까?
  4. enex를 이용한 마이그래이션
  5. js script
  6. enex import 결과물
  7. 마치며

하루에 한 줄이라도 일기를 쓰는 습관을 들였습니다.
그렇게 2012년 evernote부터 시작하여, dayone, pencake 등의 앱을 거쳤습니다.
문제는 이 앱들이 뭔가 조금씩 제 성에 차지 않았다는 점 입니다.

그 중 제 마음을 최종적으로 사로잡은 녀석은 바로 Bear 입니다.

Bear 간택 이유

  1. 애플 생태계 완벽 지원 (다만, 애플이 아니면 사용이 불가능)
  2. 군더더기 없는 UI
  3. Evernote, DayOne 등에서 거의 완벽히 Import 가능 (이미지도 그대로)
  4. 폴더 구조를 태그로 제어 가능 (예. #일기/2020/08 처럼 입력시 계층구조로 인식됨)
  5. iCloud 동기화를 이용, 동기화 속도가 굉장히 빠름 (프로 구독시)
  6. 프로 구독료가 싸다. 현재 기준 월 1,500원
  7. 마크다운 문법 지원
  8. 여러 포멧으로 export 가능 (md, pdf, html, docx, jpg, rtf)

첫번째 이유는 저에겐 최고의 장점입니다만, 윈도우나 안드로이드를 사용하시는 분들께는 최악의 단점이 될테니 혹시나 Bear 이용을 고려하시는 분들께는 반드시 참고하시길 바랍니다.
사실 가장 맘에 들었던 것은 3, 4번 항목입니다.

Bear 단점

  1. Create, Modified Timestamp 수정을 못함
  2. 애플 생태계 외에는 사용을 못함
  3. 테이블을 그릴 수 없음 (현재 지원되는 버전 개발 중 인듯 함)

저는 앱등이여서 2번은 노상관이기때문에 재끼겠습니다.
전 1번이 가장 맘에 안드는 포인트였습니다.
다른노트에서 수동으로 한두건씩 마이그래이션 하는 경우 타임스탬프가 그 입력시점으로 찍혀서 정렬이 틀어지는 경우가 생기는 것이죠.
하지만 해결 방법은 있습니다.

마이그래이션을 잘 하는 방법이 없을까?

아래와 같은 니즈가 있다고 가정합시다.

  1. 일기를 마이그래이션 하는데, #일기/2020/08 과 같은 태그를 넣고싶다.
  2. evernote, dayone이 아닌 다른 앱에서도 마이그래이션을 완벽히 하고싶다.
  3. create, modified timestamp를 그대로 보존하고 싶다.

위에서 언급한 간택 이유에서 언급했듯이 evernote, dayone 등에서는 정말 완벽하게 마이그래이션이 됩니다.
저는 PenCake 글쓰기 앱에서 Bear로 migration 하려고 합니다.
이 글쓰기 앱은 애정을 가지고 썼었는데.. 뭔가 저의 니즈엔 살짝 부족한면이 있어서 부득이 Bear 이주를 선택했답니다.
Bear에서 공식적으로 이 앱의 데이터를 마이그래이션 할 순 없습니다.
그래서 생각해낸 방법은 이렇습니다.

PenCake이든 뭐든 어디서든 export 한 데이터를 evernote에서 export 한 것 처럼 만든 후
그 파일을 import 하면 되지 않을까?
만약, 이게 가능하다면 3가지의 니즈 모두 해결 가능합니다.

enex를 이용한 마이그래이션

enex란, evernote 데이터의 포멧입니다. 위 이미지에서 보시다시피 Bear는 evernote 데이터를 import 할 수 있습니다.
enex 포맷에 대해 더 자세히 알아보시려면 여길 참고해주세요. How Evernote’s XML Export Format Works

더 알아보기 귀찮으니 어떻게 생겼는지 직접 눈으로 봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export3.dtd">
<en-export export-date="20200819T011503Z" application="Evernote" version="Evernote Mac 7.14 (458265)">
<note>
<title>제목</title>
<content><![CDATA[<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd"><en-note><div>내용</div><div>내용</div></en-note>]]></content>
<created>20190311T073918Z</created>
<updated>20190311T073932Z</updated>
<note-attributes>
<latitude>위도</latitude>
<longitude>경도</longitude>
<altitude>고도</altitude>
<author>작성자에버노트계정이메일</author>
<source>desktop.mac</source>
<reminder-order>0</reminder-order>
<tag>태그</tag>
</note-attributes>
</note>
</en-export>

대충 이렇게 생겼습니다. 생각보다 간단하죠?
굳이 각 항목에 대해 설명할 필요는 없어보일만큼 명확합니다.
위치정보들, author, source, reminder-order 는 무시해도 될것 같은 느낌입니다.

js script

뭐.. 파이썬이나 루비 같은거로 하셔도 되겠습니다만.. 저는 이런거 할때 js가 제일 편하더라고요
그래서 nodejs가 필요합니다. 설치는 각 os 플랫폼에 맞게 알아서 잘 준비해주시길 바라겠습니다.
PenCake에서 글들을 export 하면 아래와 같은 txt 파일이 출력됩니다.

1
2
3
4
5
글의 제목이 텍스트 최 상단에 이렇게 들어가네요

2019년 10월 2일 수 오후 8:03

여기에 글의 내용이 plain text로 들어갑니다

흠.. 뭔가 json 형식으로 되어있었다면 가공이 쉬웠을것 같은데요. 어쩔수없이 파싱을 해야합니다.
게다가 저 timestamp … 네.. 풀 수동으로 파싱해야합니다.
아무튼 위 파일을 enex로 바꿔봅시다.

convertToEnex.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
const fs = require('fs');
const directory = './Story_001/Text' // export 된 파일들이 있는 경로를 잘 지정해주세요
const filenames = fs.readdirSync(directory);

// 아래는 enex 파일 포멧의 헤더입니다. export-date는 별로 중요하지 않으니 그냥 두셔도 되고 건드리셔도 됩니다.
const xmlHeader = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export3.dtd">
<en-export export-date="20200819T011503Z" application="Evernote" version="Evernote Mac 7.14 (458265)">
`;

var noteBody = "";

for (const filename of filenames) {
const text = fs.readFileSync(`${directory}/${filename}`, 'utf8');
const timestamp = text.split("\n")[2].split(" ");
const title = text.split("\n")[0].replace("\r", "");

// 2020년 8월 5일 수 오후 10:52
// 위와 같은 포맷일때 날짜를 파싱하자
const year = parseInt(timestamp[0].substring(0, 4));
const month = parseInt(timestamp[1].substring(0, 2));
const paddedMonth = month < 10 ? "0" + month : month; // 태그를 "일기/2020/08" 처럼 만드려는 용도
const date = parseInt(timestamp[2].substring(0, 2));
const pm = timestamp[4] === "오후";
const hour = parseInt(timestamp[5].split(":")[0]) + (pm ? 12 : 0);
const minute = parseInt(timestamp[5].split(":")[1]);
const second = 0;

const createdAt = new Date();
createdAt.setYear(year);
createdAt.setMonth(month - 1);
createdAt.setDate(date)
createdAt.setHours(hour);
createdAt.setMinutes(minute);
createdAt.setSeconds(second);

// 20200819T011503Z 이런 포맷이 됩니다.
const created = createdAt.toISOString().replace(/[-:]/gi, "").substring(0, 15) + "Z";
const tag = `일기/${year}/${paddedMonth}`;

// <div></div> 블럭으로 감싸야 개행의 단위가 됩니다.
// <tag></tag> 안에 넣어야 태그로 인식됩니다. 본문에 #태그 이런식으로 하면 인식이 안됩니다.
//
noteBody += `<note><title>${title.replace("&", "&amp;")}</title><content><![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
<en-note><div>${text.replace(/\n/ig, "</div><div>")}</div></en-note>
]]></content><created>${created}</created><updated>${created}</updated><note-attributes><source>mobile.iphone</source><reminder-order>0</reminder-order><tag>${tag}</tag></note-attributes></note>
`;

}

const result = xmlHeader + noteBody + "</en-export>";

fs.writeFile('pencake.enex', result, 'utf8', function(error){ console.log('write end') });

xml builder 이런거 안써도 됩니다. 그냥 이렇게 빨리빨리 개발하는게 개발입니다. 굳이 이런 잡일에 정석을 따질일은 없겠죠?
실행은 아래와같이 합니다.

1
2
# pencake.enex 라는 파일이 생성됩니다.
$ node convertToEnex.js

enex import 결과물

Bear는 이렇게 태그를 ‘/‘로 구분한 텍스트로 지정하면 폴더구조를 만들어줍니다.
위 코드의 <tag> 항목에 잘 넣었더니 보기좋게 정리되었습니다.

캡쳐는 안했지만, 타임스탬프도 실제 작성 시간으로 잘 들어갔고요.

마치며

개발자는 이런 잡일을 할때 가끔씩 편할때가 있습니다.
저는 코딩을 반드시 배워야된다고 생각하는 사람은 아닙니다.
다만 스크립트 언어를 간단하게 다룰줄 알면 이런 삽질할때 굉장히 큰 도움이 된답니다.