앱에서 아이디어 모으기
사용자는 웹사이트를 둘러볼 때가 아니라 앱을 쓰는 동안 아이디어가 떠오릅니다. 피드백 시트는 보드를 앱 안으로 가져옵니다. 득표 수가 많은 아이디어를 나열하고, 로그인한 사용자가 투표하게 하고, 새 아이디어를 받습니다. 제목을 입력하는 동안 비슷한 아이디어가 먼저 나타나므로, 자기 아이디어를 찾은 사용자는 새로 올리는 대신 투표합니다.

1. 시작하기 전에
공개 피드백 보드가 필요합니다(공개 피드백 보드 열기 참고). 앱은
ideas같은 슬러그로 보드를 읽습니다.프로젝트의 publishable 키(Settings → API keys)가 필요하고,
feedback:read와feedback:write권한이 있어야 합니다. publishable 키는 앱에 넣어 배포해도 안전합니다.서버가 서명하는 토큰으로 구분되는 로그인 사용자가 필요합니다. 프로젝트의 identity secret으로 서명한 HS256 JWT이며, 사용자 ID를
sub에 넣고 한 시간 안에 만료됩니다. 공개 보드와 "What's new" 배지에서 쓰는 것과 같은 토큰입니다. 토큰이 없으면 시트는 아이디어 목록만 보여 주고, 투표하거나 글을 올리려면 로그인해야 한다고 안내합니다.웹 앱이라면 그 주소를 프로젝트의 웹 앱 web origin(Settings → Apps)으로 등록해야 합니다.
2. 웹에서
브라우저 SDK를 설치하고 페이지의 버튼에 시트를 마운트합니다. 버튼을 누르면 페이지 옆에 시트가 열리고, Escape 키나 닫기 버튼으로 닫습니다.
npm install @mocco/jsimport { createFeedbackSheet } from '@mocco/js/feedback-sheet';
const ideas = createFeedbackSheet({
publishableKey: 'mk_pub_…',
board: 'ideas',
// Return null while nobody is signed in: the sheet is then read-only.
endUserToken: async () => {
const response = await fetch('/api/mocco-token');
return response.ok ? ((await response.json()) as { token: string }).token : null;
},
});
ideas.mount('#ideas-button');로그인한 사용자는 각 아이디어 옆의 버튼으로 투표하고(다시 누르면 투표가 취소됩니다), 맨 위의 입력란으로 아이디어를 올립니다. 새 아이디어는 작성자의 표와 함께 under review 상태로 시작하고 목록 맨 위에 올라갑니다.

토큰이 없는 방문자에게도 같은 목록이 읽기 전용으로 보입니다.

mount의 옵션은 다음과 같습니다.
| 옵션 | 동작 |
|---|---|
labels | 시트에 쓰는 문구: title(기본값은 보드 이름), submit, similar, signIn, statuses(게시물 상태별 하나씩), votes(스크린 리더용으로 개수를 말해 주는 함수) 등 |
categoryId | 새 아이디어를 보드의 카테고리 중 하나에 올림 |
onOpenChange | 시트가 열리거나 닫힐 때 true 또는 false와 함께 호출됨. 분석 용도 등에 사용 |
mount는 시트를 다시 제거하는 함수를 반환합니다. 색을 바꾸려면 페이지에 --mocco-fs-accent(투표와 게시 버튼), --mocco-fs-bg, --mocco-fs-fg, --mocco-fs-border를 설정합니다. 시트는 기본적으로 시스템의 다크 모드를 따릅니다. 사용자가 쓴 글을 포함해 모든 텍스트는 텍스트로만 표시되므로, 게시물 안의 내용이 페이지에서 코드로 실행되는 일은 없습니다.
시트와 필요한 클라이언트는 번들된 앱에서 gzip 기준 약 6 KB이고, Mocco의 검사는 25 KB 미만을 유지합니다.
3. React Native에서
React Native SDK를 설치합니다(Expo Go에서 실행됩니다).
npx expo install @mocco/react-native앱에서 시트를 컴포넌트 바깥에 하나만 만들고, 아무 버튼에서나 <FeedbackSheet />를 엽니다. 모달 페이지 시트이며, 열 때마다 보드를 불러옵니다.
import { useState } from 'react';
import { Button } from 'react-native';
import { createFeedbackSheet, FeedbackSheet } from '@mocco/react-native/feedback';
export const ideas = createFeedbackSheet({
publishableKey: 'mk_pub_…',
board: 'ideas',
endUserToken: async () => await fetchMoccoTokenFromYourServer(),
});
export function IdeasButton() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<Button title="Ideas" onPress={() => setIsOpen(true)} />
<FeedbackSheet sheet={ideas} visible={isOpen} onClose={() => setIsOpen(false)} accentColor="#4f46e5" />
</>
);
}FeedbackSheet은 labels(웹과 같은 문구), accentColor, categoryId를 받습니다. 화면을 직접 디자인해서 그리려면 useFeedbackSheet(ideas)가 같은 상태와 동작을 제공합니다. posts, votes, isSignedIn, draft(similar 포함), toggleVote(postId), setDraft({ title, body }), submit()입니다. "Help & feedback" 화면에서 메신저와 나란히 두기 좋습니다. 둘 다 같은 로그인 사용자를 쓰기 때문입니다.
4. 직접 그리거나 더 나아가기
시트는 FeedbackClient 위에 만들어져 있고, 이 클라이언트를 직접 사용할 수 있습니다(웹에서는 @mocco/js/feedback, React Native에서는 @mocco/react-native/feedback의 FeedbackClient).
import { createFeedback } from '@mocco/js/feedback';
const feedback = createFeedback({ publishableKey: 'mk_pub_…', endUserToken: fetchMoccoToken });
const board = feedback.board('ideas');
const similar = await board.similar('Dark theme'); // up to 5 posts like the title
const post = await board.createPost({ title: 'Dark theme', body: 'For late nights.' });
await feedback.comment(post.id, 'Also for the mobile app, please.');
const { comments } = await feedback.comments(post.id); // isMine marks the user's own
await feedback.unfollow(post.id); // voting or posting follows a post; this opts out
const roadmap = await board.roadmap(); // { planned, in_progress, shipped }모든 쓰기 작업에는 로그인한 사용자가 필요합니다. 토큰이 없으면 아무것도 전송되기 전에 MoccoError로 거부되며, 이 오류의 code는 missing_end_user_token입니다.
5. 예제 앱
웹. Mocco 저장소의
examples/web-feedback은 "What's new" 배지와 "Ideas" 시트가 있는 정적 페이지이고, 의존성이 없는 작은 Node 서버가 서버처럼 사용자 토큰에 서명합니다. 내 프로젝트에 대고 실행하는 방법은 README에 있습니다.Expo. 3절의 코드가 통합의 전부입니다. Expo 앱의 아무 화면에나
IdeasButton을 두고(npx create-expo-app을 실행한 뒤npx expo install @mocco/react-native),fetchMoccoTokenFromYourServer가 서버의 토큰 엔드포인트를 호출하게 하세요.
6. 한도
로그인한 사용자는 한 시간에 아이디어를 5개 올리고, 투표를 30번 하고, 댓글을 20개 달 수 있습니다. 비슷한 아이디어 검색은 한 주소에서 분당 최대 60번 실행됩니다. 시트는 입력이 멈출 때까지 기다리므로 시트에서 입력하는 정도로는 이 한도에 닿지 않습니다. 목록에는 득표 수가 많은 아이디어 20개가 나옵니다.
로그인한 사용자에게는 시트가 열리는 즉시, 이 시트에서든 앱의 다른 곳에서든 이전에 투표한 모든 아이디어에 Voted가 표시됩니다. 누르면 투표가 취소됩니다. 로그인하지 않은 방문자에게는 자기 표로 표시되는 아이디어가 없습니다.