노션 API로 멜론 원터치 플레이리스트 생성하기

2025년 4월 12일

1. 서론

지난 글에 이어, 이번엔 노션 데이터베이스에서 가져온 음악 정보를 바탕으로 멜론에서 바로 실행할 수 있는 플레이리스트 링크를 자동으로 생성하는 방법을 소개합니다.

노션 API로 데이터베이스를 연결하는 과정이 궁금하시면 이 글을 읽어주세요.

노래 데이터베이스에는 이름, 앨범, 키워드, 멜론Id 속성이 있습니다.

Image

멜론 Id란, 멜론 음원 사이트에서 각 곡에 부여된 고유 식별자입니다. 플레이리스트를 만들려면 이 Id가 필요해요. ( 같은 곡이어도 음원 사이트마다 Id는 달라요. )

예를 들어, ONF의 "Beautiful Beautiful"의 멜론 ID는 33313442입니다. 아래는 해당 곡의 멜론 링크에서 Id를 확인하는 방법이에요.

Image

기본적으로 노션 데이터베이스에서 데이터를 가져오면 다음과 같은 형식입니다.

[
...
    {
        "object": "page",
        "id": "1b78f2a4-7f13-8040-be20-c6cbf2ae133f",
        "created_time": "2025-03-15T09:40:00.000Z",
        "last_edited_time": "2025-03-17T05:28:00.000Z",
        "created_by": {
            "object": "user",
            "id": "397fb8f1-4f0a-4f04-8807-c68f4e82f46a"
        },
        "last_edited_by": {
            "object": "user",
            "id": "397fb8f1-4f0a-4f04-8807-c68f4e82f46a"
        },
        "cover": null,
        "icon": null,
        "parent": {
            "type": "database_id",
            "database_id": "id"
        },
        "archived": false,
        "in_trash": false,
        "properties": {
            "앨범": {
                "id": "CQVX",
                "type": "select",
                "select": {
                    "id": "]aIS",
                    "name": "ONF : MY NAME",
                    "color": "blue"
                }
            },
            "멜론Id": {
                "id": "_%7BfD",
                "type": "rich_text",
                "rich_text": [
                    {
                        "type": "text",
                        "text": {
                            "content": "33313442",
                            "link": null
                        },
                        "annotations": {
                            "bold": false,
                            "italic": false,
                            "strikethrough": false,
                            "underline": false,
                            "code": false,
                            "color": "default"
                        },
                        "plain_text": "33313442",
                        "href": null
                    }
                ]
            },
            "키워드": {
                "id": "~G%7Ci",
                "type": "multi_select",
                "multi_select": [
                    {
                        "id": "4c575e77-e345-47b7-87e8-8ab5f08670eb",
                        "name": "청량한",
                        "color": "blue"
                    },
                    {
                        "id": "38844f4f-08f3-450c-b09c-98209729ce30",
                        "name": "신나는",
                        "color": "purple"
                    },
                    {
                        "id": "bb64899a-57fb-42f6-9d3e-8d292655c760",
                        "name": "설레는",
                        "color": "yellow"
                    },
                    {
                        "id": "b86fa446-5a60-41b1-9ee5-76f0a3cfcd36",
                        "name": "기분 좋은",
                        "color": "orange"
                    },
                    {
                        "id": "b88f9969-a2e2-4d6b-bdf7-e1b7283d35e0",
                        "name": "강렬한",
                        "color": "default"
                    },
                    {
                        "id": "a4bcfbc4-5cae-4c99-835b-dccdc117a43f",
                        "name": "중독적인",
                        "color": "brown"
                    }
                ]
            },
            "이름": {
                "id": "title",
                "type": "title",
                "title": [
                    {
                        "type": "text",
                        "text": {
                            "content": "Beautiful  Beautiful ",
                            "link": null
                        },
                        "annotations": {
                            "bold": false,
                            "italic": false,
                            "strikethrough": false,
                            "underline": false,
                            "code": false,
                            "color": "default"
                        },
                        "plain_text": "Beautiful  Beautiful ",
                        "href": null
                    }
                ]
            }
        },
        "url": "url",
        "public_url": null
    },
    {
        "object": "page",
        "id": "1b98f2a4-7f13-8038-a29f-ca5c24780e58",
        "created_time": "2025-03-17T00:39:00.000Z",
        "last_edited_time": "2025-03-17T11:30:00.000Z",
        "created_by": {
            "object": "user",
            "id": "397fb8f1-4f0a-4f04-8807-c68f4e82f46a"
        },
        "last_edited_by": {
            "object": "user",
            "id": "397fb8f1-4f0a-4f04-8807-c68f4e82f46a"
        },
        "cover": null,
        "icon": null,
        "parent": {
            "type": "database_id",
            "database_id": "id"
        },
        "archived": false,
        "in_trash": false,
        "properties": {
            "앨범": {
                "id": "CQVX",
                "type": "select",
                "select": {
                    "id": "}_uj",
                    "name": "ONF : MY IDENTITY",
                    "color": "red"
                }
            },
            "멜론Id": {
                "id": "_%7BfD",
                "type": "rich_text",
                "rich_text": [
                    {
                        "type": "text",
                        "text": {
                            "content": "38565923",
                            "link": null
                        },
                        "annotations": {
                            "bold": false,
                            "italic": false,
                            "strikethrough": false,
                            "underline": false,
                            "code": false,
                            "color": "default"
                        },
                        "plain_text": "38565923",
                        "href": null
                    }
                ]
            },
            "키워드": {
                "id": "~G%7Ci",
                "type": "multi_select",
                "multi_select": [
                    {
                        "id": "4c575e77-e345-47b7-87e8-8ab5f08670eb",
                        "name": "청량한",
                        "color": "blue"
                    },
                    {
                        "id": "38844f4f-08f3-450c-b09c-98209729ce30",
                        "name": "신나는",
                        "color": "purple"
                    },
                    {
                        "id": "b88f9969-a2e2-4d6b-bdf7-e1b7283d35e0",
                        "name": "강렬한",
                        "color": "default"
                    },
                    {
                        "id": "8dc26355-b2c9-4143-8d9d-3fce775f4165",
                        "name": "힙한",
                        "color": "purple"
                    }
                ]
            },
            "이름": {
                "id": "title",
                "type": "title",
                "title": [
                    {
                        "type": "text",
                        "text": {
                            "content": "The Stranger",
                            "link": null
                        },
                        "annotations": {
                            "bold": false,
                            "italic": false,
                            "strikethrough": false,
                            "underline": false,
                            "code": false,
                            "color": "default"
                        },
                        "plain_text": "The Stranger",
                        "href": null
                    }
                ]
            }
        },
        "url": "url",
        "public_url": null
    },
    ...
]

노션에서 받아온 데이터를 바로 활용하기에는 구조가 매우 복잡하죠. 데이터를 가공하여 원하는 형태로 만들고 멜론 플레이리스트 주소를 만드는 방법은 다음과 같습니다.

2. 본론

1) 데이터 가공

노션 데이터에서 이름, 앨범, 키워드, 멜론Id 데이터 요소를 추출합니다.

이때 가중치(songWeight) 속성을 추가했습니다. 사용자가 입력한 키워드가 해당 곡과 얼마나 잘 맞는지 판단하기 위해, 해당곡에 사용자가 입력한 키워드가 있을때마다 가중치를 1씩 증가합니다.

notionSongList.forEach((song) => {
  const songName = song.properties["이름"].title[0]?.plain_text;
  const songAlbum = song.properties["앨범"].select.name;
  const songMelonId = song.properties["멜론Id"].rich_text[0]?.plain_text;
  let songWeight = 0;
  let songKeyword: string[] = [];
  song.properties["키워드"].multi_select.forEach(
    (value: { color: string, id: string, name: string }) => {
      if (selected.includes(value.name)) {
        songWeight++;
      }
      songKeyword.push(value.name);
    }
  );

  playlist.push({
    songName,
    songAlbum,
    songMelonId,
    songKeyword,
    songWeight,
  });
});

2) 가중치 별 정렬 및 선별

가중치가 높은 순서대로 정렬하고, 대략 1시간 분량의 재생시간을 갖게 하기 위해 15~20곡을 선별합니다.

let sortedPlaylist = playlist.sort((a, b) => b.songWeight - a.songWeight);
if (sortedPlaylist.length >= 20) {
  sortedPlaylist = sortedPlaylist.slice(0, 20);
} else if (sortedPlaylist.length >= 15) {
  sortedPlaylist = sortedPlaylist.slice(0, 15);
}

3) 선별한 곡 순서 랜덤하게 돌리기

매번 동일한 플레이리스트가 나오면 재미없으니까 마지막으로 곡 순서를 랜덤하게 섞어줍니다.

const shuffledSongs = [...sortedPlaylist].sort(() => Math.random() - 0.5);

4) 멜론 플레이리스트 주소 만들기

사용자가 버튼을 클릭하면 각 노래별 멜론Id를 join하여 멜론Id 주소를 만듭니다. 멜론은 111,222,333 과 같이 곡 Id를 ,(쉼표)로 구분해요.

중요한 점은 아이폰, 안드로이드에 들어가는 url이 다릅니다. url을 확인해주세요.

const iosMelon = "meloniphone://play/?ctype=1&menuid=0&cid";
const androidMelon = "melonapp://play?ctype=1&menuid=1000002721&cid";

const melonIdList: string[] = [];
shuffledSongs.forEach((list) => {
  melonIdList.push(list.songMelonId);
});

setMelonId(melonIdList.join(","));

다음과 같이 재생목록 url을 만들 수 있어요.

아이폰
meloniphone://play/?ctype=1&menuid=0&cid=36838796,36838797,31118050,31060382,32647490,32675501,35529361,38172760,31283825,32082017,32849073,34364635,32082015,33004757,33313443,33780723,38565927,37390567,38565923,33313442

안드로이드
melonapp://play?ctype=1&menuid=1000002721&cid=36838796,36838797,31118050,31060382,32647490,32675501,35529361,38172760,31283825,32082017,32849073,34364635,32082015,33004757,33313443,33780723,38565927,37390567,38565923,33313442

기기별 멜론 url에 멜론Id 주소를 붙여 최종 플레이리스트 주소를 생성합니다.

<div className="flex flex-row items-center mt-4">
  <a href={`${iosMelon}${melonId}`}>
    <button className="rounded-md flex flex-row items-center px-[12px] py-2 bg-[#bc2a31] text-[#f5f3ee] mr-4">
      <Image
        className="mr-1 rounded-lg"
        src={icon_melon}
        alt=""
        width={18}
        height={18}
      />
      아이폰으로 듣기
    </button>
  </a>

  <a href={`${androidMelon}${melonId}`}>
    <button className="rounded-md flex flex-row items-center px-[12px] py-2 bg-[#bc2a31] text-[#f5f3ee]">
      <Image
        className="mr-1 rounded-lg"
        src={icon_melon}
        alt=""
        width={18}
        height={18}
      />
      안드로이드로 듣기
    </button>
  </a>
</div>

다음과 같이 동작합니다! 🚀

Image

이렇게 각 사이트별로 재생목록을 만드는 것을 ‘원클릭 스밍리스트’라고 하는데요. 원하는 곡 ID만 알고 있다면 친구에게 재생목록을 공유할때도 사용할 수 있을 것 같아요.

+ (추가) 플레이리스트 이미지 저장 기능

플레이리스트 내용이 길다보니 캡쳐했을때 한페이지안에 다 안담겼어요. 사용자들이 자신의 키워드와 플레이리스트를 공유할때 여러번 캡쳐해야하는 번거로움을 해소하기 위해 이미지 저장 기능을 추가했어요.

html-to-image 라이브러리로 플레이리스트 영역 dom을 이미지로 저장합니다.

import { toPng } from "html-to-image";

export function useImageDownload() {
  const downloadImage = async (captureRef: React.RefObject<HTMLDivElement>) => {
    if (captureRef.current === null) {
      return;
    }

    try {
      const dataUrl = await toPng(captureRef.current, { cacheBust: true });
      const link = document.createElement("a");
      link.download = "ONF_playlist.png";
      link.href = dataUrl;
      link.click();
      return true;
    } catch (err) {
      console.error(err);
      return false;
    }
  };

  return { downloadImage };
}

사용자가 입력한 닉네임, 선택한 키워드, 플레이리스트가 포함된 이미지가 생성됩니다.

ONF_Playlist

결론

노션 데이터베이스에서 가져온 데이터를 활용하여 멜론 플레이리스트를 만들어보았습니다. 곡에 대한 데이터만 있으면 재생목록 url을 만드는건 어렵지 않아요. 배포 후 이미지 저장까지 원하는 사용자의 요구에 따라 추가 기능을 개발한 경험은 매우 재밌었어요. 사용자의 피드백을 받고 홈페이지를 개선해나아가는 경험이 개발자로서 유의미했습니다.

이 기능을 확장할 수 있는 다양한 방안도 고민중입니다. 그 기능을 상용화 하게 되면 또 공유할게요!

🙇‍♂️ 참고문헌

[원클릭 슴리] 원클릭 스밍리스트 제작 가이드

멜론/지니/벅스/바이브 - 원클릭 스밍리스트 만드는 법 / 플레이리스트 /재생목록/ 링크 / 원클릭/노래 고유번호/음원총공/단축 링크 복구/pc 멜론