Bookmark Dashboard를 온라인에서 공유 가능하게 만들기
Source: Dev.to
번역할 텍스트가 제공되지 않았습니다. 번역을 원하는 본문을 알려주시면 한국어로 번역해 드리겠습니다.
소개
최근에 팀원들과 북마크를 공유할 일이 자주 생겼습니다—코드 저장소, 문서 링크, Figma의 UI 디자인 등. 그때 아이디어가 떠올랐습니다:
그 북마크들을 대시보드로 정리하고, 한 번에 모든 것을 공유할 수 있는 공개 웹페이지를 생성한다면 어떨까?
이렇게 하면 하나 이상의 북마크 컬렉션으로 대시보드를 준비한 뒤, 해당 웹페이지를 바로 생성할 수 있습니다. 북마크를 일일이 복사·붙여넣는 대신 URL만 팀원에게 보내면, 모든 리소스가 정리된 깔끔한 정적 대시보드를 바로 볼 수 있겠죠. 정말 편리합니다!
아이디어에 흥미를 느낀 저는 바로 구현에 들어갔습니다. Bookmark Dashboard(제가 이전에 개발한 브라우저 확장) 가 이미 존재했기 때문에, 주요 작업은 “공유 링크 생성” 버튼을 추가하는 것이었습니다. 버튼을 클릭하면 다음이 수행됩니다:
- 현재 대시보드 레이아웃을 저장합니다.
- 해당 순간의 대시보드와 정확히 동일한 모습을 보여주는 고유 URL을 생성합니다.

UI를 업데이트하는 것은 쉬운 일였지만, 버튼을 클릭했을 때 실제 작업은 백엔드에서 이루어집니다. 여러 방안을 고민한 끝에, 가장 간단하고 빠르게 구현할 수 있는 방법을 선택했습니다.
아래에서는 Next.js와 MongoDB를 사용해 백엔드를 구현한 과정을 자세히 살펴보겠습니다.
구현 접근 방식
대시보드는 전달된 레이아웃이 동일한 한 같은 내용을 표시합니다. 따라서 공유 기능은 두 단계로 요약됩니다:
- Save – 공유 링크를 생성할 때 레이아웃을 저장합니다.
- 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.js와 page.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는 이제 공유 가능한 정적 대시보드 페이지 생성을 지원합니다. 워크플로는 다음과 같습니다:
- Generate Sharing Link 버튼을 클릭 → UI가
PUT /dashboard/linkAPI를 호출합니다. - API가 레이아웃을 저장하고
_id를 반환합니다. - URL
/dashboard/link/를 구성하고 공유합니다. - 해당 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.js와 page.js가 모두 서버 측에서 실행되므로 동일한 연결 로직을 공유하고 연결 재사용의 이점을 누릴 수 있습니다.
데이터베이스를 서버 측 페이지에서 직접 쿼리하는 이 패턴은 Next.js의 주요 특징 중 하나입니다. 서버 측 데이터 가져오기와 렌더링을 가능하게 하며, 이는 React나 Vue와 같은 클라이언트 측 프레임워크와 크게 다릅니다.
마무리
백엔드는 모두 설정되었습니다. 흐름은 다음과 같습니다:
- 대시보드 공유 링크 생성 – 프론트엔드(“Generate” 버튼 클릭 핸들러)에서
/dashboard/linkAPI를 호출합니다. - 레이아웃 저장 – API가 레이아웃을 저장하고
_id를 반환합니다. - 공유 링크 구성 –
_id를 사용해/dashboard/link/_id링크를 만듭니다. - 링크 열기 – 페이지가 경로에서
_id를 추출하고, 저장된 레이아웃을 가져와 대시보드 내용을 렌더링합니다.
예시

공유 링크 열기

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