리액트에서 map이 아닌 반복문으로 랜딩하기
보통은 리스트를 렌더링할 때 map 메소드로 나열하는 것이 일반적이지만, 간혹 필요에 따라 반복문으로 렌더링하게 되는 경우도 있다.
import { Client } from '@notionhq/client'
// 노션 API 활성화
const notion = new Client({ auth: process.env.NOTION_SECRET_KEY })
// 피드 목록 가져오기
async function getList() {
const data = (await notion.databases.query({
database_id: '498cea7e5c4b44ba8b2b13c6a7f9e3d1',
sorts: [{ property: '생성일', direction: 'descending' }],
page_size: 5
})) as unknown as FeedList
return data
}
// 한 페이지의 모든 블록 가져오기
async function getData(id: string) {
let isFirst = true
let nextCursor: string
let data: BlockObjectResponse[] = []
while (isFirst || nextCursor) {
const { results, next_cursor } = await notion.blocks.children.list({
block_id: id,
start_cursor: nextCursor
})
nextCursor = next_cursor
if (isFirst) isFirst = false
data.push(...(results as BlockObjectResponse[]))
}
return data
}
export default async function Page() {
const { results, next_cursor } = getList()
return (
<ul className="grid gap-6 xl:gap-10">
{results.map(async (page) => {
const render = async () => {
let items = []
let orderedList = []
const list = await getData(page.id)
for (const block of list) {
if (block.type === 'numbered_list_item') {
orderedList.push(<li>블록</li>)
} else if (orderedList.length) {
items.push(<ol key={'ol' + block.id}>{orderedList}</ol>)
orderedList = []
}
}
return <>{items}</>
}
return <>{await render()}</>
})}
</ul>
)
}
설명
- 노션 페이지 콘텐츠에서 한 줄 한 줄을 Block이라고 부른다.
- List Item 태그들은 ol, ul 태그에 감싸져야 하는데 map 메소드로 돌리면 이것들을 하나의 list 태그 안에 넣을 수가 없다.
- 따라서 임의의 배열 orderedList를 만든 후 반복문을 돌려서 list item들을 집어넣고 list item 블록이 아닌 다른 블록이 오게 되었을 때 list 태그를 items에 집어넣는다.
- items에 넣은 후 다시 orderedList를 비운다.
물론 map으로 하는 방식보다 성능은 좋지 않겠지만 별다른 방법이 없는 것 같다.