2020-11-17
Narkdown 프로젝트
notion-py 를 사용해서 Python 기반으로진행했었지만 Python을 잘 사용하지 않다보니 유지 보수하기 힘들었다.
ts
기반 notion-api-worker 가 있어서 이를 사용하려고 한다.
목표
CLI로 page url 또는 database url을 입력 받고 notion의 컨텐츠를 파싱하여 md 파일을 생성하자. (CMS를 위해)
이후 웹 페이지를 만들 수 있으면 좋을 듯.
fetch vs axios
프론트엔드에서 fetch, axios는 자주 사용했는데, node 환경에서 api 요청을 해보는것은 처음이라, 어떤 것을 사용할지 고민했다.
axios는 node 환경에서도 동일하게 사용하는 것이 가능하다.
fetch는 node-fetch 라는 라이브러리를 사용해야 한다. 이 라이브러리는 NodeJS 런타임 환경에서
window.fetch
를 사용하기 위한 최소한의 코드를 목표로 한다고 한다.프론트엔드 환경에서 fetch 가 라이브러리 설치 없이 사용할 수 있는게 장점이라고생각하는데, 굳이 node-fetch를 사용할 필요는 없을 것 같아서, axios를 사용하기로결정했다. (보안적인 옵션도 axios가 많이 제공한다.)
https://www.geeksforgeeks.org/difference-between-fetch-and-axios-js-for-making-http-requests/
ts-node
tsconfig
생성 명령어
tsc --init
tsc --init
tsconfig
옵션 정리
8 Best Practices for Future-Proofing Your TypeScript Code
[TS / Node] TS + Node.js + Express + Babel(option) + eslint로 개발환경 세팅하기
notion-api-worker
get page 리턴
기본 페이지
json{ "847c0e9b-15a1-42c9-9392-ca2f817c4eac": { "role": "editor", "value": { "id": "847c0e9b-15a1-42c9-9392-ca2f817c4eac", "version": 23, "type": "page", "properties": { "title": [["hello"]] }, "content": ["cc1dee2e-269d-4cd0-a5a3-b1c3d72edf61"], "permissions": [ { "role": "editor", "type": "user_permission", "user_id": "b21a69b3-a19b-438c-b599-e850190836a3" } ], "created": 1605604740000, "last_edited_time": 1605604740000, "parent_id": "ee7c0178-18cf-474e-a665-83f2432f545f", "parent_table": "space", "alive": true, "created_by_table": "notion_user", "created_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "last_edited_by_table": "notion_user", "last_edited_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "shard_id": 1004639, "space_id": "ee7c0178-18cf-474e-a665-83f2432f545f" } }, "cc1dee2e-269d-4cd0-a5a3-b1c3d72edf61": { "role": "editor", "value": { "id": "cc1dee2e-269d-4cd0-a5a3-b1c3d72edf61", "version": 12, "type": "text", "properties": { "title": [["1234"]] }, "created": 1605604740000, "last_edited_time": 1605604740000, "parent_id": "847c0e9b-15a1-42c9-9392-ca2f817c4eac", "parent_table": "block", "alive": true, "created_by_table": "notion_user", "created_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "last_edited_by_table": "notion_user", "last_edited_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "shard_id": 1004639, "space_id": "ee7c0178-18cf-474e-a665-83f2432f545f" } } }
{ "847c0e9b-15a1-42c9-9392-ca2f817c4eac": { "role": "editor", "value": { "id": "847c0e9b-15a1-42c9-9392-ca2f817c4eac", "version": 23, "type": "page", "properties": { "title": [["hello"]] }, "content": ["cc1dee2e-269d-4cd0-a5a3-b1c3d72edf61"], "permissions": [ { "role": "editor", "type": "user_permission", "user_id": "b21a69b3-a19b-438c-b599-e850190836a3" } ], "created": 1605604740000, "last_edited_time": 1605604740000, "parent_id": "ee7c0178-18cf-474e-a665-83f2432f545f", "parent_table": "space", "alive": true, "created_by_table": "notion_user", "created_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "last_edited_by_table": "notion_user", "last_edited_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "shard_id": 1004639, "space_id": "ee7c0178-18cf-474e-a665-83f2432f545f" } }, "cc1dee2e-269d-4cd0-a5a3-b1c3d72edf61": { "role": "editor", "value": { "id": "cc1dee2e-269d-4cd0-a5a3-b1c3d72edf61", "version": 12, "type": "text", "properties": { "title": [["1234"]] }, "created": 1605604740000, "last_edited_time": 1605604740000, "parent_id": "847c0e9b-15a1-42c9-9392-ca2f817c4eac", "parent_table": "block", "alive": true, "created_by_table": "notion_user", "created_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "last_edited_by_table": "notion_user", "last_edited_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "shard_id": 1004639, "space_id": "ee7c0178-18cf-474e-a665-83f2432f545f" } } }
Database 하위 페이지
json{ "15afa14b-8f9c-4b6c-97cc-46375c775cc5": { "role": "reader", "value": { "id": "15afa14b-8f9c-4b6c-97cc-46375c775cc5", "version": 25, "type": "page", "properties": [Object], "created": 1605517260000, "last_edited_time": 1605602760000, // 부모 Id // 데이터베이스의 경우 collection_id "parent_id": "e543505f-be64-46cd-9c55-07117dc85a92", // 부모 "parent_table": "collection", "alive": true, "created_by_table": "notion_user", // user_id "created_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "last_edited_by_table": "notion_user", "last_edited_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "shard_id": 1004639, "space_id": "ee7c0178-18cf-474e-a665-83f2432f545f" } }, // 부모가 space가 아닌 경우 부모 "acc3dfd0-339e-4cac-b5ba-ae8673fddfad": { "role": "reader", "value": { "id": "acc3dfd0-339e-4cac-b5ba-ae8673fddfad", "version": 106, "type": "collection_view_page", "view_ids": [Array], "collection_id": "e543505f-be64-46cd-9c55-07117dc85a92", "format": [Object], "permissions": [Array], "created": 1600223639505, "last_edited_time": 1605594780000, "parent_id": "ee7c0178-18cf-474e-a665-83f2432f545f", "parent_table": "space", "alive": true, "created_by_table": "notion_user", "created_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "last_edited_by_table": "notion_user", "last_edited_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "shard_id": 1004639, "space_id": "ee7c0178-18cf-474e-a665-83f2432f545f" } } }
{ "15afa14b-8f9c-4b6c-97cc-46375c775cc5": { "role": "reader", "value": { "id": "15afa14b-8f9c-4b6c-97cc-46375c775cc5", "version": 25, "type": "page", "properties": [Object], "created": 1605517260000, "last_edited_time": 1605602760000, // 부모 Id // 데이터베이스의 경우 collection_id "parent_id": "e543505f-be64-46cd-9c55-07117dc85a92", // 부모 "parent_table": "collection", "alive": true, "created_by_table": "notion_user", // user_id "created_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "last_edited_by_table": "notion_user", "last_edited_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "shard_id": 1004639, "space_id": "ee7c0178-18cf-474e-a665-83f2432f545f" } }, // 부모가 space가 아닌 경우 부모 "acc3dfd0-339e-4cac-b5ba-ae8673fddfad": { "role": "reader", "value": { "id": "acc3dfd0-339e-4cac-b5ba-ae8673fddfad", "version": 106, "type": "collection_view_page", "view_ids": [Array], "collection_id": "e543505f-be64-46cd-9c55-07117dc85a92", "format": [Object], "permissions": [Array], "created": 1600223639505, "last_edited_time": 1605594780000, "parent_id": "ee7c0178-18cf-474e-a665-83f2432f545f", "parent_table": "space", "alive": true, "created_by_table": "notion_user", "created_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "last_edited_by_table": "notion_user", "last_edited_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "shard_id": 1004639, "space_id": "ee7c0178-18cf-474e-a665-83f2432f545f" } } }
메타데이터가 있는 페이지
json{ "1c7c8eb6-ec3b-42fb-b950-63abd3c9bd61": { "role": "reader", "value": { "id": "1c7c8eb6-ec3b-42fb-b950-63abd3c9bd61", "version": 6, "type": "page", // 데이터베이스 하위에 있는 페이지의 메타데이터 부모 데이터베이스에 접근해서 가져와야 함. "properties": { "RXKJ": [ // 여러 태그 목록이 문자열 ',' 로 구분되어 옴. ["hello,bye"] ], "fb_;": [["🛠 In Progress"]], "qS^H": [["Test"]], "title": [["asdf"], ["의 사본"]] }, // 전체 페이지, 작은 페이지 사이즈일 때 함께 옴, 없으면 안옴. "format": { "page_icon": "🚡", // link인 경우는 링크 "page_cover": "https://user-images.githubusercontent.com/1440854/79684011-6c948280-822e-11ea-9e23-1644903796fb.png", // 업로드인 경우 https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d53a69fd-a3e1-4914-b014-63158a1078a2/blue.png // 기본 제공인 경우 /images/blue.png => https://notion.so/images/blue.png로 접근 가능 "page_cover_position": 0.5, "page_full_width": true, "page_small_text": true }, "created": 1605594821014, "last_edited_time": 1605603000000, "parent_id": "e543505f-be64-46cd-9c55-07117dc85a92", "parent_table": "collection", "alive": true, "copied_from": "15afa14b-8f9c-4b6c-97cc-46375c775cc5", "created_by_table": "notion_user", "created_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "last_edited_by_table": "notion_user", "last_edited_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "shard_id": 1004639, "space_id": "ee7c0178-18cf-474e-a665-83f2432f545f" } }, "acc3dfd0-339e-4cac-b5ba-ae8673fddfad": { "role": "reader", "value": { "id": "acc3dfd0-339e-4cac-b5ba-ae8673fddfad", "version": 106, "type": "collection_view_page", "view_ids": [ "be43c1c8-dd64-4cfb-9df9-efd97d8af60a", "cfabb574-6051-47ed-9c14-ea3a1b6aead7", "87cdd007-d8d6-464c-82f2-c7a4153bab0d", "c09c2c36-0419-4bff-8195-bf6c2b897d6f", "e0d39abd-4d7b-4c5c-9ce9-4984a3315932", "83b3d2a6-6f63-4940-987d-1142e51da175" ], "collection_id": "e543505f-be64-46cd-9c55-07117dc85a92", "format": { "page_cover_position": 0.6 }, "permissions": [ { "role": "editor", "type": "user_permission", "user_id": "b21a69b3-a19b-438c-b599-e850190836a3" }, { "role": "reader", "type": "public_permission" } ], "created": 1600223639505, "last_edited_time": 1605594780000, "parent_id": "ee7c0178-18cf-474e-a665-83f2432f545f", "parent_table": "space", "alive": true, "created_by_table": "notion_user", "created_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "last_edited_by_table": "notion_user", "last_edited_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "shard_id": 1004639, "space_id": "ee7c0178-18cf-474e-a665-83f2432f545f" } } }
{ "1c7c8eb6-ec3b-42fb-b950-63abd3c9bd61": { "role": "reader", "value": { "id": "1c7c8eb6-ec3b-42fb-b950-63abd3c9bd61", "version": 6, "type": "page", // 데이터베이스 하위에 있는 페이지의 메타데이터 부모 데이터베이스에 접근해서 가져와야 함. "properties": { "RXKJ": [ // 여러 태그 목록이 문자열 ',' 로 구분되어 옴. ["hello,bye"] ], "fb_;": [["🛠 In Progress"]], "qS^H": [["Test"]], "title": [["asdf"], ["의 사본"]] }, // 전체 페이지, 작은 페이지 사이즈일 때 함께 옴, 없으면 안옴. "format": { "page_icon": "🚡", // link인 경우는 링크 "page_cover": "https://user-images.githubusercontent.com/1440854/79684011-6c948280-822e-11ea-9e23-1644903796fb.png", // 업로드인 경우 https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d53a69fd-a3e1-4914-b014-63158a1078a2/blue.png // 기본 제공인 경우 /images/blue.png => https://notion.so/images/blue.png로 접근 가능 "page_cover_position": 0.5, "page_full_width": true, "page_small_text": true }, "created": 1605594821014, "last_edited_time": 1605603000000, "parent_id": "e543505f-be64-46cd-9c55-07117dc85a92", "parent_table": "collection", "alive": true, "copied_from": "15afa14b-8f9c-4b6c-97cc-46375c775cc5", "created_by_table": "notion_user", "created_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "last_edited_by_table": "notion_user", "last_edited_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "shard_id": 1004639, "space_id": "ee7c0178-18cf-474e-a665-83f2432f545f" } }, "acc3dfd0-339e-4cac-b5ba-ae8673fddfad": { "role": "reader", "value": { "id": "acc3dfd0-339e-4cac-b5ba-ae8673fddfad", "version": 106, "type": "collection_view_page", "view_ids": [ "be43c1c8-dd64-4cfb-9df9-efd97d8af60a", "cfabb574-6051-47ed-9c14-ea3a1b6aead7", "87cdd007-d8d6-464c-82f2-c7a4153bab0d", "c09c2c36-0419-4bff-8195-bf6c2b897d6f", "e0d39abd-4d7b-4c5c-9ce9-4984a3315932", "83b3d2a6-6f63-4940-987d-1142e51da175" ], "collection_id": "e543505f-be64-46cd-9c55-07117dc85a92", "format": { "page_cover_position": 0.6 }, "permissions": [ { "role": "editor", "type": "user_permission", "user_id": "b21a69b3-a19b-438c-b599-e850190836a3" }, { "role": "reader", "type": "public_permission" } ], "created": 1600223639505, "last_edited_time": 1605594780000, "parent_id": "ee7c0178-18cf-474e-a665-83f2432f545f", "parent_table": "space", "alive": true, "created_by_table": "notion_user", "created_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "last_edited_by_table": "notion_user", "last_edited_by_id": "b21a69b3-a19b-438c-b599-e850190836a3", "shard_id": 1004639, "space_id": "ee7c0178-18cf-474e-a665-83f2432f545f" } } }
get table 리턴
table로 된 페이지
json[ { "id": "11acfd54-2ee8-4640-b3fb-1782ce9b8caa", "Status": "🖨 Published", "Category": "Test", "Name": "Basic Blocks" }, { "id": "084bbefe-7f25-481a-bfbb-e8aff2152e4f", "Status": "🖨 Published", "Category": "Test", "Name": "Copy of Basic Blocks" }, { "id": "24786a8a-3d7d-4dfd-854d-ac40559c9f82", "Status": "🖨 Published", "Category": "Test", "Name": "Code Blocks" }, { "id": "74bbb810-9a68-499f-8f12-25dcce846f02", "Status": "🖨 Published", "Category": "Test", "Name": "Embed Blocks" }, { "id": "2df7176f-d58f-4c42-921b-55e9bbf0e92e", "Status": "🖨 Published", "Category": "Test", "Name": "Table Blocks" }, { "id": "30894478-96e6-4f95-9095-d84be27a82a9", "Status": "🖨 Published", "Category": "Test", "Name": "Page Blocks" }, { "id": "cee84696-242a-4f6e-953a-2c7ecb8b1603", "Status": "🖨 Published", "Category": "Test", "Name": "Advanced Blocks" }, { "id": "64c69eaf-268a-4076-bf48-d8ee5f2ca8c8", "Status": "🖨 Published", "Category": "Test", "Name": "Linked Page" }, { "id": "6a8383c0-0a12-4859-9edc-2fe41e9cbe75", "Status": "🖨 Published", "Category": "Test", "Name": "Recursive embed Image" }, { "id": "d10a7885-58f0-4ba9-b9a4-fb357ab796e8", "Status": "🖨 Published", "Category": "Example", "Name": "Example Pages" }, { "id": "1c7c8eb6-ec3b-42fb-b950-63abd3c9bd61", "Tags": ["hello", "bye"], "Status": "🛠 In Progress", "Category": "Test", "Name": "asdf의 사본" }, // 완전히 안지웠을 때는 빈문자열로 옴. { "id": "e8b450e4-f686-4d0b-9212-6241e6099a0b", "Tags": [""], "Status": "", "Category": "" } ]
[ { "id": "11acfd54-2ee8-4640-b3fb-1782ce9b8caa", "Status": "🖨 Published", "Category": "Test", "Name": "Basic Blocks" }, { "id": "084bbefe-7f25-481a-bfbb-e8aff2152e4f", "Status": "🖨 Published", "Category": "Test", "Name": "Copy of Basic Blocks" }, { "id": "24786a8a-3d7d-4dfd-854d-ac40559c9f82", "Status": "🖨 Published", "Category": "Test", "Name": "Code Blocks" }, { "id": "74bbb810-9a68-499f-8f12-25dcce846f02", "Status": "🖨 Published", "Category": "Test", "Name": "Embed Blocks" }, { "id": "2df7176f-d58f-4c42-921b-55e9bbf0e92e", "Status": "🖨 Published", "Category": "Test", "Name": "Table Blocks" }, { "id": "30894478-96e6-4f95-9095-d84be27a82a9", "Status": "🖨 Published", "Category": "Test", "Name": "Page Blocks" }, { "id": "cee84696-242a-4f6e-953a-2c7ecb8b1603", "Status": "🖨 Published", "Category": "Test", "Name": "Advanced Blocks" }, { "id": "64c69eaf-268a-4076-bf48-d8ee5f2ca8c8", "Status": "🖨 Published", "Category": "Test", "Name": "Linked Page" }, { "id": "6a8383c0-0a12-4859-9edc-2fe41e9cbe75", "Status": "🖨 Published", "Category": "Test", "Name": "Recursive embed Image" }, { "id": "d10a7885-58f0-4ba9-b9a4-fb357ab796e8", "Status": "🖨 Published", "Category": "Example", "Name": "Example Pages" }, { "id": "1c7c8eb6-ec3b-42fb-b950-63abd3c9bd61", "Tags": ["hello", "bye"], "Status": "🛠 In Progress", "Category": "Test", "Name": "asdf의 사본" }, // 완전히 안지웠을 때는 빈문자열로 옴. { "id": "e8b450e4-f686-4d0b-9212-6241e6099a0b", "Tags": [""], "Status": "", "Category": "" } ]
blocks
Accept vs Content-Type
Accept : 클라이언트가 받을 수 있는 응답 형식을 지정함.
Content-Type : 클라이언트가 보내는 콘텐츠의 형식