# 내 사이트와 앱에서 문의받기

**문의 페이지**는 헬프 센터의 `/contact`에 있는 페이지로, 사람들이 팀에 글을 쓰고 답변을 읽는 곳입니다. 문의 하나가 Messenger 대화 하나입니다. 앱에서 시작한 대화처럼 프로젝트의 **Inbox**에 도착하고, 팀은 같은 방법으로 답변합니다. 방문자는 카테고리를 고르고, 글을 쓰고, 스크린샷을 첨부할 수 있으며, 같은 페이지로 돌아와 답변을 읽습니다.

브라우저와 앱의 인앱 브라우저 모두에서 동작하므로, 앱이 화면을 직접 그리지 않고도 **문의하기**를 제공할 수 있습니다. 앱에서 화면을 직접 만들고 싶다면 SDK를 쓰세요([Mocco Messenger로 앱 안에서 문의받기](https://www.mocco.club/docs/ko/articles/ky2kfv-contact-us)). 두 방법은 함께 쓸 수 있습니다. 인박스, 카테고리, 사람을 같이 씁니다.

## 준비할 것

- 프로젝트에 **Messenger**가 설정되어 있어야 합니다. **Inbox** 페이지의 **Set up messenger**가 프로젝트의 identity secret을 만듭니다([Mocco Messenger로 앱 안에서 문의받기](https://www.mocco.club/docs/ko/articles/ky2kfv-contact-us#2-set-up-the-messenger)).
- 프로젝트의 **헬프 센터**([헬프 센터 게시하기](https://www.mocco.club/docs/ko/articles/im56b3-help-center)). 문의 페이지는 헬프 센터의 한 부분입니다.
- 문의 페이지를 켜려면 워크스페이스의 소유자나 관리자여야 합니다.
- 앱에 로그인한 사람을 위해서는 [로그인 URL](https://www.mocco.club/docs/ko/articles/eaut9t-sign-in)이나 앱 서버가 만든 토큰, 또는 둘 다가 필요합니다. 둘 다 없으면 게스트만 글을 쓸 수 있습니다.

## 1. 문의 페이지 켜기

프로젝트의 **Inbox** 페이지에서 설정을 열고 **Contact page**를 찾습니다. **Take inquiries on a page of your help center**를 체크합니다.

![인박스의 Contact page 설정: 스위치와 문의 페이지의 주소](https://fi-workers-mocco-storage.s3.ap-northeast-2.amazonaws.com/pub/w/93e11fe8-0494-4e3d-b1cb-fe4dffd3ee7e/p/d64785b8-9a23-4fa1-a81d-550af30e203a/helpcenter/642272d8-67b8-4a7b-95aa-b47aedd139a0/contact-page-settings.png)

스위치 아래의 **It is at**에 문의 페이지의 주소가 복사 버튼과 함께 나옵니다. 첫 번째 주소를 공유하면 됩니다. 켜기 전에는 같은 목록이 어디에 생길지 보여 줍니다. 문의 페이지는 헬프 센터의 기본 주소인 `https://<address>.help.mocco.club/contact`에서 시작하고, 헬프 센터를 내 사이트에서 제공하면 그 위치를 따라갑니다. `https://example.com/docs`에 있는 헬프 센터라면 문의 페이지는 `https://example.com/docs/contact`에 있습니다.

프로젝트에 아직 헬프 센터가 없으면, 이 섹션에는 대신 헬프 센터 설정으로 가는 링크가 나옵니다. 워크스페이스의 멤버는 설정을 볼 수 있지만 바꿀 수는 없습니다. 모든 변경은 [감사 로그](https://www.mocco.club/docs/ko/articles/2yiscf-audit-log)에 남습니다.

문의 페이지를 켜면 헬프 센터의 페이지가 바로 갱신되어 헤더에 **문의하기**가 생깁니다. 끄면 문의 페이지가 내려갑니다. `404`를 응답하고, 사람들이 열어 두었던 세션도 더 이상 동작하지 않습니다. 대화는 인박스에 그대로 남고, 인박스에서 계속 답변할 수 있습니다.

## 2. 내 사이트에서 제공하기

내 사이트의 경로에서 제공하는 헬프 센터에는 이미 그 경로 아래에 문의 페이지가 있습니다. 문의 페이지에 `https://example.com/contact`처럼 따로 경로를 줄 수도 있습니다. 그러면 주소가 짧고, 헬프 센터가 어디에 있든 바뀌지 않습니다.

프로젝트의 **Help center** 페이지에서 **Your domain**의 **Part of the site**로 **Contact page**를 고르고, 공개 URL을 입력한 뒤 **Add path**를 누릅니다. 다른 경로와 마찬가지로 콘솔이 복사할 설정을 보여 줍니다([내 사이트에서 헬프 센터 제공하기](https://www.mocco.club/docs/ko/articles/a2t8mn-own-site)). 주소가 `orbit`인 헬프 센터라면 Next.js rewrite는 다음과 같습니다.

```ts
// next.config.ts
async rewrites() {
  return [
    { source: '/contact', destination: 'https://www.mocco.club/_proxy/orbit/contact' },
    { source: '/contact/:path*', destination: 'https://www.mocco.club/_proxy/orbit/contact/:path*' },
  ];
},
```

배포한 뒤 **Check now**를 누릅니다. 경로가 **Active**가 되면 어디서나 그 경로가 문의 페이지의 주소가 되고, 헬프 센터의 기본 주소는 방문자를 그곳으로 보냅니다.

문의 페이지를 켜기 전에도 경로를 추가할 수 있습니다. 켜기 전까지 콘솔에는 **Contact page (off)**로 표시됩니다.

알아 둘 것:

- 같은 웹사이트의 다른 경로 안에 둘 수 없습니다. 보드가 `https://example.com/help`에 있으면 `https://example.com/help/contact`는 쓸 수 없습니다.
- 내 사이트가 `/contact`를 Mocco로 보내므로, [로그인 엔드포인트](https://www.mocco.club/docs/ko/articles/eaut9t-sign-in)는 `/contact` 아래에 둘 수 없습니다. `/api/help/enter`가 적당합니다.
- 경로가 활성화되면 **Signing in**의 **Return addresses to allow**에 추가됩니다. 로그인 엔드포인트에도 추가하세요. 그렇지 않으면 문의 페이지에서 로그인한 사람이 대체 페이지로 가게 됩니다.

## 3. 링크하기

문의 페이지가 켜져 있으면 헬프 센터와 피드백 보드의 모든 페이지 헤더에 **문의하기**가 나옵니다. 내 사이트의 헤더나 푸터, 고객 지원 페이지 같은 곳에서도 링크하세요.

주소에 언어가 들어가지 않으므로 이메일이나 앱에 넣어도 됩니다. 페이지의 문구는 헬프 센터를 쓰는 언어를 따릅니다. 한국어 헬프 센터면 한국어, 그 밖에는 영어입니다. 카테고리는 인박스 설정에서 붙인 이름 그대로 나옵니다.

![휴대폰 브라우저에서 본 영어 헬프 센터의 문의 페이지](https://fi-workers-mocco-storage.s3.ap-northeast-2.amazonaws.com/pub/w/93e11fe8-0494-4e3d-b1cb-fe4dffd3ee7e/p/d64785b8-9a23-4fa1-a81d-550af30e203a/helpcenter/bd6801a6-8da6-4121-bc46-05e21ef80e98/contact-page-guest.png)

검색 엔진은 이 페이지를 색인하지 않습니다. 누군가 글을 쓰기 전에는 읽을 내용이 없기 때문입니다.

## 4. 앱에서 열기

앱은 문의 페이지를 인앱 브라우저로 엽니다. React Native에서는 `expo-web-browser`를 씁니다. 주소에는 두 가지를 함께 보냅니다.

- **누구인지**, `token`으로 보냅니다. 앱 서버가 만든 수명이 짧은 토큰입니다. [로그인 엔드포인트](https://www.mocco.club/docs/ko/articles/eaut9t-sign-in#the-endpoint-contract)가 서명하는 것과 같은 토큰입니다. 프로젝트의 identity secret으로 서명한 HS256이고, `sub`는 사용자 ID, `exp`는 지금부터 최대 한 시간이며, 선택으로 `name`과 `email`을 넣으면 팀이 인박스에서 볼 수 있습니다. 아무도 로그인하지 않았으면 빼세요.
- **기기 정보**, 팀이 사용자가 무엇을 실행 중이었는지 알 수 있게 보냅니다. `appVersion`, `build`, `platform`, `os`, `locale`입니다. 인박스의 사용자 패널은 보낸 값을 그대로 보여 주므로, 플랫폼은 `ios`나 `android`로 보내세요.

```tsx
import * as Application from 'expo-application';
import * as WebBrowser from 'expo-web-browser';
import { Platform } from 'react-native';

const CONTACT_PAGE = 'https://example.com/contact';

export async function openContactPage() {
  const params: Record<string, string> = {
    appVersion: Application.nativeApplicationVersion ?? '',
    build: Application.nativeBuildVersion ?? '',
    platform: Platform.OS === 'ios' ? 'ios' : 'android',
    os: String(Platform.Version),
    locale: Intl.DateTimeFormat().resolvedOptions().locale,
  };
  const user = await getSignedInUser();
  if (user !== null) {
    params.token = await api.helpToken(); // your server signs it, for 10 minutes
  }
  const query = Object.entries(params)
    .filter(([, value]) => value !== '')
    .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
    .join('&');
  await WebBrowser.openBrowserAsync(`${CONTACT_PAGE}?${query}`);
}
```

React Native의 `URL`은 모든 메서드를 구현하지 않으므로 쿼리를 직접 만듭니다.

문의 페이지는 열리자마자 주소에서 토큰을 빼내고, 그 브라우저 탭에서만 보관하며, Messenger 세션으로 바꿉니다. 주소를 다른 사이트로 넘기지 않습니다. 다만 내 프록시와 Mocco의 요청 로그에는 쿼리가 남을 수 있으므로 토큰의 수명은 짧게 하세요. 10분이면 충분합니다.

페이지를 열 때마다 새 토큰을 넘기세요. 인앱 브라우저는 앱의 로그인을 공유하지 않으므로, 페이지가 혼자서 다시 로그인시킬 수 없습니다.

토큰은 프래그먼트의 `#token=`이 아니라 쿼리의 `token`으로 보내세요. 문의 페이지는 그 브라우저 탭이 로그인 URL에 직접 요청한 경우에만 `#token=`을 받습니다. 링크 하나로 방문자를 다른 사람의 계정에 로그인시킬 수 없게 하기 위해서입니다. 쿼리로 받은 토큰은 그 기기에서 게스트로 쓴 내용을 계정으로 옮기지 않습니다. 페이지의 **로그인**으로 로그인하면 옮겨집니다.

## 게스트

인박스 설정에서 **Let people who aren't signed in write**를 켜면, 로그인하지 않은 사람에게 짧은 양식이 나옵니다. 이메일과, 원하면 이름을 입력합니다. **로그인 없이 계속하기**를 누르면 그 사람의 문의 목록이 열립니다. 팀에게는 그 대화가 게스트의 것으로 표시되고, 남긴 이메일이 함께 나옵니다.

![로그인하지 않은 방문자: 로그인 버튼과 게스트로 글을 쓰는 양식](https://fi-workers-mocco-storage.s3.ap-northeast-2.amazonaws.com/pub/w/93e11fe8-0494-4e3d-b1cb-fe4dffd3ee7e/p/d64785b8-9a23-4fa1-a81d-550af30e203a/helpcenter/3cd174e4-c446-4dad-b50c-57fd9705e75d/contact-page-guest-ko.png)

게스트를 끄면 문의 페이지는 글을 쓰려면 로그인하라고 안내합니다.

브라우저는 게스트를 기억합니다. 문의 페이지가 페이지를 제공하는 사이트의 브라우저 저장소에 게스트 토큰을 보관합니다. 같은 브라우저로 돌아와 양식을 다시 채우면 이전 문의와 답변이 보입니다. 다른 브라우저나 기기에서는 처음부터 시작합니다. 일부 인앱 브라우저는 열 때마다 저장소를 유지하지 않으므로, 앱 안의 게스트는 이전 문의를 찾지 못할 수도 있습니다.

게스트가 페이지의 **로그인**으로 로그인하면, 그 브라우저에서 게스트로 쓴 내용이 계정으로 옮겨지고 브라우저는 게스트 토큰을 지웁니다.

## 로그인

로그인 URL을 설정하면 문의 페이지의 헤더에 **로그인**이 나옵니다. 피드백 보드와 똑같이 내 앱의 로그인을 거쳐 로그인한 상태로 페이지에 돌아옵니다([헬프 센터에서 로그인하게 하기](https://www.mocco.club/docs/ko/articles/eaut9t-sign-in)). **로그아웃**은 그 브라우저에서 그 사람을 잊습니다.

로그인한 방문자의 세션은 30일 동안 유효하지만, 문의 페이지는 그 브라우저 탭에서만 보관하므로 공용 컴퓨터에 남지 않습니다. 전에 로그인했던 사람이 다시 오면, 페이지가 로그인 URL을 조용히 확인해서 내 사이트가 아직 그 사람을 알면 로그인한 상태로 돌아옵니다.

![앱에서 연 문의와 팀의 답변](https://fi-workers-mocco-storage.s3.ap-northeast-2.amazonaws.com/pub/w/93e11fe8-0494-4e3d-b1cb-fe4dffd3ee7e/p/d64785b8-9a23-4fa1-a81d-550af30e203a/helpcenter/28e8a730-ed8d-4bed-947f-9b8e26a9ee19/contact-page-conversation-ko.png)

## 첨부 파일

메시지 하나에 이미지(PNG, JPEG, WebP, GIF)나 PDF를 최대 세 개, 각 10MB까지 첨부할 수 있습니다. Mocco는 앱에서 온 파일과 같은 방식으로 각 파일을 확인합니다. 실제로는 주장하는 이미지나 PDF가 아닌 파일은 거부됩니다. 파일을 올리지 못하면 문의 페이지가 파일 이름과 함께 알려 주고, 그 파일 없이 메시지를 보냅니다.

팀이 답변에 첨부한 파일은 문의 페이지의 스레드에 나옵니다. 이미지는 바로 보이고, PDF는 내려받습니다.

## 답변

팀은 인박스에서, 또는 에이전트에서 `mocco_messenger_reply`로 답변합니다. 문의를 열어 두면 페이지가 15초마다 새 답변을 확인합니다. 목록에서는 읽지 않은 답변이 있는 문의에 **새 답변**이 표시됩니다.

Mocco는 아직 답변을 이메일로 알리지 않습니다. 페이지로 다시 와 달라고 안내하거나, 남긴 이메일로 직접 연락하세요.

## 에이전트에서

[MCP로 연결한 에이전트](https://www.mocco.club/docs/ko/articles/frhj3w-connect)는 `mocco_messenger_settings_get`(`contactPage`, `contactPageUrls`)으로 문의 페이지가 켜져 있는지와 주소를 읽고, `mocco_messenger_contact_page_set`으로 켜거나 끕니다. 변경은 먼저 클라이언트에서 소유자나 관리자의 확인을 받습니다. `mocco_help_site_mount_add`에 `sitePath: '/contact'`를 주면 `/contact` 경로를 추가합니다.
