Bookmark Dashboard를 온라인에서 공유 가능하게 만들기

발행: (2025년 12월 17일 오후 07:40 GMT+9)
11 min read
원문: Dev.to

Source: Dev.to

번역할 텍스트가 제공되지 않았습니다. 번역을 원하는 본문을 알려주시면 한국어로 번역해 드리겠습니다.

소개

최근에 팀원들과 북마크를 공유할 일이 자주 생겼습니다—코드 저장소, 문서 링크, Figma의 UI 디자인 등. 그때 아이디어가 떠올랐습니다:

그 북마크들을 대시보드로 정리하고, 한 번에 모든 것을 공유할 수 있는 공개 웹페이지를 생성한다면 어떨까?

이렇게 하면 하나 이상의 북마크 컬렉션으로 대시보드를 준비한 뒤, 해당 웹페이지를 바로 생성할 수 있습니다. 북마크를 일일이 복사·붙여넣는 대신 URL만 팀원에게 보내면, 모든 리소스가 정리된 깔끔한 정적 대시보드를 바로 볼 수 있겠죠. 정말 편리합니다!

아이디어에 흥미를 느낀 저는 바로 구현에 들어갔습니다. Bookmark Dashboard(제가 이전에 개발한 브라우저 확장) 가 이미 존재했기 때문에, 주요 작업은 “공유 링크 생성” 버튼을 추가하는 것이었습니다. 버튼을 클릭하면 다음이 수행됩니다:

  1. 현재 대시보드 레이아웃을 저장합니다.
  2. 해당 순간의 대시보드와 정확히 동일한 모습을 보여주는 고유 URL을 생성합니다.

대시보드 공유 UI

UI를 업데이트하는 것은 쉬운 일였지만, 버튼을 클릭했을 때 실제 작업은 백엔드에서 이루어집니다. 여러 방안을 고민한 끝에, 가장 간단하고 빠르게 구현할 수 있는 방법을 선택했습니다.

아래에서는 Next.jsMongoDB를 사용해 백엔드를 구현한 과정을 자세히 살펴보겠습니다.

구현 접근 방식

대시보드는 전달된 레이아웃이 동일한 한 같은 내용을 표시합니다. 따라서 공유 기능은 두 단계로 요약됩니다:

  1. Save – 공유 링크를 생성할 때 레이아웃을 저장합니다.
  2. Restore – 링크가 열릴 때 레이아웃을 복원합니다.

백엔드 구현은 간단합니다:

  • Create an API – 대시보드 레이아웃을 받아 데이터베이스에 저장하는 API를 만듭니다.
  • Create a page – 저장된 레이아웃을 가져와 방문 시 렌더링하는 페이지를 만듭니다.
    이 페이지의 URL이 공유 링크가 됩니다.

Source:

대시보드 링크 API

내 백엔드 서비스는 Next.js App Router를 사용해 구축되었습니다. API 엔드포인트를 정의하려면 폴더 기반 라우팅 규칙만 따르면 됩니다.

API 경로를 /dashboard/link 로 만들고 싶었기 때문에 다음과 같은 폴더 구조를 만들고 그 안에 route.js 파일을 배치했습니다:

src/app/dashboard
└── link
    └── route.js

route.js

import { NextResponse } from 'next/server';
// database util
import connectMongo from '@/db/mongo';
// database model for dashboard layout data
import LinkData from '@/db/entity/link_data';

export async function PUT(req) {
  // TODO: read current user's account (e.g., from auth token)
  const account = /* ... */;

  // Read the dashboard layout from the request body
  const { content } = await req.json();

  // Connect to MongoDB and upsert the layout
  await connectMongo();
  const item = await LinkData.findOneAndUpdate(
    { account },
    { content },
    { upsert: true, new: true }
  );

  // Return the generated document ID
  return NextResponse.json(
    { re: item._id },
    { status: 200 }
  );
}

PUT 함수는 /dashboard/link 로 들어오는 PUT 요청을 자동으로 처리합니다. MongoDB에 연결한 뒤, 레이아웃(content)을 사용자의 계정 아래에 저장합니다. 프론트엔드(“Generate” 버튼 클릭 핸들러)에서 이 API를 호출할 수 있으며, 반환된 _id를 사용해 공유 링크를 생성합니다.

Source:

대시보드 페이지

대시보드를 _id 로 참조하려면 공유 링크가 /dashboard/link/[id] 형태여야 합니다. app/dashboard/link/[id] 아래에 page.js 를 만들었습니다:

src/app/dashboard
└── link
    ├── [id]
    │   ├── Dashboard.js
    │   └── page.js
    └── route.js

page.js

// database util
import connectMongo from '@/db/mongo';
// database model for dashboard layout data
import LinkData from '@/db/entity/link_data';
// the component that actually renders the dashboard
import Dashboard from './Dashboard';

async function loadData(id) {
  await connectMongo();
  if (!id) return null;
  return await LinkData.findById(id).exec();
}

export default async function DashboardLinkPage({ params }) {
  const { id } = params;
  const data = await loadData(id);

  let layout = undefined;
  if (data?.content) {
    try {
      const parsed = JSON.parse(data.content);
      layout = parsed?.layout;
    } catch (e) {
      console.error('Failed to parse layout JSON', e);
    }
  }

  // This is a server‑side component (no "use client" directive)
  return <Dashboard layout={layout} />;
}

이 페이지는 라우트 파라미터에서 id 를 추출하고, MongoDB에 저장된 레이아웃을 가져와 파싱한 뒤 React‑Grid‑Layout 으로 구축된 Dashboard 컴포넌트에 전달합니다(이전에 작성한 post 를 참고).

/dashboard/link/[id] 에서 [id] 를 API 로부터 반환된 실제 _id 값으로 교체하면, 해당 URL이 바로 다른 사람에게 보낼 수 있는 공유 링크가 됩니다. 사용자가 이 링크를 열면, 링크를 생성할 때 사용된 대시보드와 동일한 정적 대시보드를 확인할 수 있습니다.

MongoDB 연결에 대한 참고

route.jspage.js 모두 동일한 connectMongo 헬퍼를 사용하여 동시 요청 및 핫 리로드 시 단일 연결을 재사용합니다:

import mongoose from 'mongoose';

// Reuse the connection across requests (module re‑imports)
let cached = global.mongoose;
if (!cached) {
  cached = global.mongoose = { conn: null, promise: null };
}

async function connectMongo() {
  if (cached.conn) {
    return cached.conn;
  }

  if (!cached.promise) {
    const uri = process.env.MONGODB_URI;
    const opts = {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    };
    cached.promise = mongoose.connect(uri, opts).then((mongoose) => mongoose);
  }

  cached.conn = await cached.promise;
  return cached.conn;
}

export default connectMongo;

이 패턴은 MongoDB 클라이언트가 서버 인스턴스당 한 번만 인스턴스화되도록 보장하여 성능을 향상시키고 연결 고갈 오류를 방지합니다.

결론

몇 줄의 백엔드 코드만으로 Bookmark Dashboard는 이제 공유 가능한 정적 대시보드 페이지 생성을 지원합니다. 워크플로는 다음과 같습니다:

  1. Generate Sharing Link 버튼을 클릭 → UI가 PUT /dashboard/link API를 호출합니다.
  2. API가 레이아웃을 저장하고 _id를 반환합니다.
  3. URL /dashboard/link/를 구성하고 공유합니다.
  4. 해당 URL에 접속하는 누구든지 정확한 대시보드 레이아웃을 서버 측에서 렌더링된 모습을 볼 수 있습니다.

이 솔루션은 가볍고, Next.js의 App Router를 활용하며, 영구 저장을 위해 MongoDB를 사용합니다—팀원과 선별된 리소스 세트를 빠르게 공유하기에 완벽합니다.

if (!cached.promise) {
  cached.promise = mongoose
    .connect(MONGO_URI, {
      serverApi: { version: '1', strict: true, deprecationErrors: true },
    })
    .then((mongoose) => {
      return mongoose;
    });
}

cached.conn = await cached.promise;
await mongoose.connection.db.admin().command({ ping: 1 });
console.log('Successfully connected to db!');
} catch (e) {
  cached.promise = null;
  throw e;
}

return cached.conn;
}

전체 화면 제어

  • 전체 화면 모드 진입
  • 전체 화면 모드 종료

route.jspage.js가 모두 서버 측에서 실행되므로 동일한 연결 로직을 공유하고 연결 재사용의 이점을 누릴 수 있습니다.

데이터베이스를 서버 측 페이지에서 직접 쿼리하는 이 패턴은 Next.js의 주요 특징 중 하나입니다. 서버 측 데이터 가져오기와 렌더링을 가능하게 하며, 이는 React나 Vue와 같은 클라이언트 측 프레임워크와 크게 다릅니다.

마무리

백엔드는 모두 설정되었습니다. 흐름은 다음과 같습니다:

  1. 대시보드 공유 링크 생성 – 프론트엔드(“Generate” 버튼 클릭 핸들러)에서 /dashboard/link API를 호출합니다.
  2. 레이아웃 저장 – API가 레이아웃을 저장하고 _id를 반환합니다.
  3. 공유 링크 구성_id를 사용해 /dashboard/link/_id 링크를 만듭니다.
  4. 링크 열기 – 페이지가 경로에서 _id를 추출하고, 저장된 레이아웃을 가져와 대시보드 내용을 렌더링합니다.

예시

Dashboard generation preview

공유 링크 열기

Shared dashboard view

이 공유 가능한 대시보드 기능은 최신 버전의 **Bookmark Dashboard**에 이제 라이브되었습니다. 다른 사람과 리소스를 공유할 때 이미 많은 시간을 절약하고 있습니다. 무료이며, 더 많은 사람들이 컬렉션을 빠르고 쉽게 공유하는 데 도움이 되길 바랍니다.

읽어 주셔서 감사합니다!

Back to Blog

관련 글

더 보기 »