DEV Community

Cover image for BizOps 워크스페이스로 대화를 모니터링하고 조정하는 방법

BizOps 워크스페이스로 대화를 모니터링하고 조정하는 방법

이 사용법 문서는 BizOps Workspace로 통칭되는 PubNub의 데이터 관리 기능에 대해 설명하는 일련의 문서 중 일부입니다:

BizOps Workspace는 애플리케이션을 관리하는 데 도움이 되는 일련의 도구입니다. 이 문서에서는 관리자가 실시간으로 진행되는 대화의 실시간 미리보기를 보고 사용자를 음소거하거나 금지하는 등 수동으로 대화를 중재하는 조치를 취할 수 있는 '채널 모니터' 기능에 대해 살펴보겠습니다.

채널 모니터의 전제 조건

채팅 애플리케이션을 개발하기 위해 PubNub에서 권장하는 방법은 타입스크립트 및 JavaScript를 지원하는 전용 채팅 SDK를 사용하는 것입니다. 채팅 SDK는 PubNub의 인프라 위에 구축되므로 '대화', '입력 표시기', '스레딩' 등의 채팅 사용 사례를 위해 설계된 API를 사용하여 PubNub의 확장성과 견고함을 활용할 수 있습니다.

채널 모니터는 앱 컨텍스트를 사용하는 모든 PubNub 애플리케이션에서 사용할 수 있지만, Chat SDK를 사용하여 개발된 애플리케이션은 스레드 보기 및 답글 달기, 사용자 감시, 뮤트 및 차단 기능과 같은 추가 기능에 액세스할 수 있습니다.

채널 모니터 구성

PubNub의 전용 Chat SDK를 사용하는 경우 메시지 구조가 미리 정의되어 있으므로 채널 모니터의 추가 구성에 대해 걱정할 필요가 없습니다.

Chat SDK를 사용하지 않는 개발자는 애플리케이션에서 메시지를 구조화하는 방법과 애플리케이션에서 메시지 편집 및 삭제를 처리하는 방법을 정의해야 합니다. 이는 채널 모니터를 구성하여 수행됩니다.

채널 모니터에 대한 키 설정 요구 사항

채널 모니터를 사용하려면 PubNub 관리자 포털의 키 집합에서 다음을 활성화해야 합니다:

  • 앱 컨텍스트. 채널 및 사용자에 대한 메타데이터를 저장합니다. 앱 컨텍스트에 대해서는 이전 글'사용자 및 채널 관리'에서 더 자세히 설명했습니다. 앱 컨텍스트 데이터가 변경될 때 앱이 업데이트를 받으려면 다음을 활성화해야 합니다: 사용자 메타데이터 이벤트, 채널 메타데이터 이벤트멤버십 이벤트.

  • 메시지 지속성: 관리자가 대화를 검토하고 편집할 수 있도록 메시지 기록을 PubNub에 저장합니다.

PubNub 요금제에 따른 가용성 등 기타 요구사항은 채널 모니터 설명서를 참조하세요.

다른 키 설정 속성은 필수는 아니지만 Chat 애플리케이션을 최대한 활용하기 위해 적극 권장됩니다:

  • 액세스 관리자. 이 문서의 뒷부분에서 설명하는 것처럼 데이터에 대한 무단 액세스를 방지하고 안전한 중재 솔루션을 만드는 데 필요합니다.

  • Chat SDK를 사용한다고 가정할 때, 구성 문서에는 사용자가 온라인 상태인지 오프라인 상태인지 추적하기 위한 프레즌스도 나열되어 있습니다.

채널 모니터란 무엇인가요?

채널 모니터를 통해 채팅 관리자는 여러 채널에서 실시간으로 일어나는 대화의 실시간 미리보기를 볼 수 있습니다. 관리자는 잘못된 행동을 하는 사용자나 불쾌한 메시지 등 불안한 것을 발견하면 즉시 조치를 취하여 문제를 완화할 수 있습니다.

매니저는 취할 수 있는 조치에 대해 많은 유연성을 가지고 있습니다:

  • 액세스 권한을 제한하지 않고 사용자를 관찰(보기)

  • 문제가 되는 메시지 수정

  • 문제가 되는 메시지 삭제

  • 사용자의 메시지 게시 기능 제한(뮤트)

  • 사용자의 메시지 읽기 또는 게시 기능 제한(금지)

'모니터링' 기능의 모든 기능은 수동으로 할 수 있습니다: 수동으로 메시지 검토하기, 수동으로 사용자 뮤트하기 등 자동 모더레이션은 이 문서에서 다루지 않습니다.

채널 모니터링하기

'채널 모니터링' 문서에서 채널 활동 보기를 시작하는 단계별 지침을 확인할 수 있습니다.

요약하면 다음과 같습니다:

  1. 관리자 포털에 로그인하고 왼쪽 탐색 패널의 BizOps 워크스페이스 섹션으로 이동하여 채널 모니터를 선택합니다. BizOps 워크스페이스 섹션이 표시되지 않으면 PubNub 요금제를 업그레이드해야 하는 경우가 대부분이지만 문제가 있는 경우 지원팀에 문의 하세요.

  2. 현재 앱 컨텍스트가 비활성화되어 있는 경우 키 집합에서 앱 컨텍스트를 활성화하라는 메시지가 표시됩니다.

  3. 모더레이션을 시작하려면 채널을 선택합니다. 채널이 표시되지 않거나 채널의 하위 집합만 표시되는 경우에는 채널 컨텍스트가 연결된 채널만 표시되기 때문입니다. Chat SDK에서 자동으로 컨텍스트를 만들거나 모든 SDK에서 사용할 수 있는 setChannelMetadata() API를 사용하여 수동으로 이 작업을 수행할 수 있습니다.

  4. 동시에 중재할 채널을 최대 5개까지 선택할 수 있습니다.

채널 메시지는 실시간으로 표시되며, 키세트에서 지속성을 사용 설정한 경우 이전 메시지도 포함됩니다.

모니터 보기의 기능에 대한 개요는'채널 모니터' 문서를 참조하세요. 이 문서의 나머지 부분에서는 샘플 구현을 참조하여 메시지 및 사용자 중재에 대해 더 자세히 설명합니다.

Chat SDK 샘플 앱

채널 모니터와 Chat SDK 개발을 담당하는 같은 바쁜 엔지니어링 팀이 Chat SDK를 사용하는 React Native로 작성된 샘플 애플리케이션도 만들었습니다.

이 샘플은 SDK의 기능과 PubNub를 사용하여 사실적이고 모든 기능을 갖춘 채팅 앱을 개발하기 위한 모범 사례를 보여줍니다. 이 샘플은 오픈 소스이며 Github의 /samples/react-native-group-chat 아래에 Chat SDK가 있는 동일한 리포지토리의 일부입니다.

이 앱은 신뢰할 수 있는 앱이므로 이 문서에서는 이 앱을 사용하여 '실제' 앱에서 채널 모니터를 사용하는 방법을 보여줄 것입니다. 또한 이 앱은 모바일용 Chat SDK 데모의 일부로 호스팅되므로 종속성(Expo, React Native, 기기 에뮬레이터)을 설치할 필요 없이 앱을 사용할 수 있습니다.이 하우투의 일부 예제를 따라 해보고 싶다면 샘플의 README에 있는 설치 지침을 따르세요(작성 시점의 최신 커밋은 0c12f3f입니다).

채팅 샘플 앱 아바타

종속성을 피하기 위해 채팅 앱에서 사용하는 아바타는 앱 내에 임베드되며 사용자의 앱 컨텍스트에 저장되지 않습니다. 즉, 앱에 표시되는 아바타는 모니터 보기에 표시되는 아바타와 일치하지 않으며, 이는 애플리케이션의 제한 사항이지 PubNub의 제한 사항이 아닙니다.

채널 모니터에서 샘플 앱 사용하기

다음 단계를 통해 샘플 앱과 채널 모니터를 실행할 수 있습니다:

  1. 애플리케이션의 사용 설명서에 제공된 지침에 따라 샘플 애플리케이션을 복제하고 빌드합니다.

  2. 두 개의 디바이스에서 애플리케이션을 실행하여 두 사람 간의 대화를 보여줄 수 있습니다. 이 도움말의 스크린샷은 Android 및 iOS 에뮬레이터를 사용합니다.

  3. 왼쪽 디바이스에 로그인한 다음 오른쪽 디바이스에 로그인합니다. 오른쪽 디바이스에서 플로팅 작업 버튼을 사용하여 대화를 시작합니다. 오른쪽 디바이스에서 메시지를 보내면 왼쪽 디바이스에 읽지 않은 메시지로 표시됩니다.

  4. 앞서 설명한 대로 채널 모니터를 시작합니다. 적절한 채널을 선택하면 'XXX와 1:1'이라는 이름이 표시되며, 여기서 XXX는 왼쪽 디바이스에서 로그인한 이름입니다.

  5. 아래 스크린샷과 비슷한 화면이 표시되면 이제 '검토 시작'을 클릭하면 됩니다.

메시지 검토하기

채널 모니터는 앱 컨텍스트를 사용하여 메시지를 편집하거나 삭제하는 모든 PubNub 애플리케이션에서 사용할 수 있지만, Chat SDK를 사용하여 개발된 애플리케이션은 사용자 보기, 뮤트 및 차단 기능과 같은 추가 기능에 액세스할 수 있습니다.

진행자로 메시지 편집하기

진행자는 모든 메시지를 편집할 수 있습니다. 이렇게 하면 관리자 UI는 물론 클라이언트에 표시되는 메시지도 업데이트됩니다. 아래 스크린샷은 "안녕하세요" 메시지를 "안녕하세요 - 진행자가 편집했습니다"로 편집한 것을 보여줍니다.

아래에서 모니터 보기는 Chat SDK 메시지 편집() 메서드를 호출했습니다. 클라이언트는 streamUpdatesOn() API를 사용하여 자동으로 업데이트를 받습니다. 샘플 앱에서는 Chat.tsx에서 이를 볼 수 있습니다.

원래 메시지는 삭제되지 않지만 편집 내용은 원래 메시지에 추가(작업)로 저장됩니다. 자세한 내용은 edit() 메시지 문서를 참조하세요.

운영자로 메시지 보내기.

관리자는 지정된 채널에 메시지를 게시할 수 있으며, 이 메시지는 모든 채널 멤버가 받게 됩니다. 다음 스크린샷은 "맞습니다, 저는 관리자입니다"라는 메시지가 모든 클라이언트에게 수신되는 모습을 보여줍니다. 관리자는 자신의 메시지를 수정하고 삭제할 수 있지만, (당연히) 자신을 뮤트하거나 차단할 수는 없습니다.

대화에 참여하기 위해 채널 모니터는 아이디가 'PUBNUB_INTERNAL_MODERATOR'로 설정된 새 사용자를 생성했으며, 이는 사용자 관리 탭에서 확인할 수 있습니다.

샘플 애플리케이션은 'PUBNUB_INTERNAL_MODERATOR' 사용자를 특별히 처리하지 않기 때문에 기본 아바타와의 대화에서 표준 메시지로만 표시됩니다. 앱에서 이 모더레이터 메시지에 배경색을 다르게 하는 등 특별한 의미를 부여하여 처리할 수 있습니다.

아주 기본적인 예로, 메시지 텍스트를 표시하는 로직을 수정하고 중재자가 보낸 메시지라는 표시를 추가할 수 있습니다:

{messageProps.currentMessage?.originalPnMessage.userId == 
  "PUBNUB_INTERNAL_MODERATOR" ? "MOD: " : ""}
Enter fullscreen mode Exit fullscreen mode

그러면 UI가 다음과 같이 표시됩니다:

진행자로서 메시지 삭제하기

메시지 옆의 '삭제' 버튼을 누르면 '모니터' 보기에서 메시지가 삭제된 것으로 표시되고 클라이언트에게 업데이트에 대한 알림이 전송됩니다.

메시지를 삭제하는 것은 Chat SDK'delete()' 메서드를 호출하는 것과 동일하며, 이는 소프트 삭제이므로 메시지가 저장소에 남아 있고 단지 '삭제됨'으로 플래그가 지정된다는 점에 유의해야 합니다. '삭제됨' 플래그의 상태를 읽고 적절한 조치를 취하는 것은 클라이언트에게 달려 있으며, 보시다시피 샘플 애플리케이션에서는 메시지 텍스트가 "(메시지 소프트 삭제됨)"으로 바뀌도록 선택되어 있습니다.

클라이언트 앱의 관련 섹션은 아래에 재현되어 있으며 Github에서도 찾을 수 있습니다.

<Text variant="body">
          {messageProps.currentMessage?.originalPnMessage.deleted
            ? "(Message softly deleted)"
            : messageElements.map((msgPart, index) =>
                renderMessagePart(
                  msgPart,
                  index,
                  messageProps.currentMessage?.originalPnMessage.userId || ""
                )
              )}
        </Text>
Enter fullscreen mode Exit fullscreen mode

비영구적 소프트 삭제를 사용하면 나중에 필요한 경우 운영자나 다른 권한이 있는 사용자가 메시지를 복원할 수 있습니다.

관리자가 메시지 복원하기

메시지를 복원 하는 것은 메시지 삭제와 정반대이며 Chat SDK'restore()' 메서드를 호출하는 것과 동일합니다.

삭제된 메시지 텍스트는 '모니터' 보기에 복원되며, 메시지 개체에서 '삭제됨' 플래그가 해제됩니다. 샘플 앱에서 '삭제됨' 플래그를 처리하여 원본 텍스트를 복원하는 위의 코드를 참조하세요.

보통 사용자

모더레이션 솔루션의 구성 요소

채널 모니터에서 모더레이션이 어떻게 작동하는지에 대해 논의하기 전에 강력하고 성숙한 모더레이션 솔루션의 다양한 구성 요소와 이러한 구성 요소가 Chat SDK에서 사용할 수 있는 것과 어떻게 매핑되는지 살펴보기로 하죠.

사용자가 수행할 수 있는 작업 제한하기

사용자가 잘못된 행동을 한 경우 해당 사용자의 게시 기능을 제한하거나(뮤트), 다른 사용자와의 상호작용을 아예 제한(금지)하고 싶을 것입니다.

서버 측에서는 운영자가 사용자를 뮤트하거나 차단할 수 있는 방법과 해당 사용자가 뮤트 해제 또는 차단 해제될 수 있는 방법이 필요합니다. 또한 메시지를 받아서는 안 되는 사용자에게 메시지를 보내거나 받을 수 없도록 하는 규칙을 설정해야 합니다. 서버에서 이 작업을 수행하는 것이 악성 사용자가 우회할 수 있는 클라이언트 측 중재에 의존하는 것보다 훨씬 더 안전합니다.

Chat SDK는 특정 채널에서 사용자를 '음소거' 또는 '차단'하고 '차단 사유'를 정의할 수 있는 setRestrictions() 메서드를 제공합니다. setRestrictions() API 자체는 클라이언트가 할 수 있는 일에 어떠한 제한도 적용하지 않으며, 단지 등록자에게 제한이 적용되었음을 알릴 뿐입니다. Chat SDK의 listenForEvents() 메서드를 통해 '중재' 이벤트를 수신하는 것은 클라이언트와 서버 모두의 몫입니다.

사용자 제한의 변경 사항을 수신 대기합니다:

사용자의 권한이 변경되면 이러한 변경 사항이 즉시 적용되어 솔루션 전체에 반영되는 것이 중요합니다. 사용자가 차단되면(예: 운영자가 웹 인터페이스를 사용하여) 사용자의 클라이언트 앱에 해당 사용자에게 차단되었다는 메시지가 표시됩니다. 사용자가 클라이언트 애플리케이션을 '해킹'하여 클라이언트 측 검사를 우회하는 등 차단을 우회하려는 모든 시도는 서버 측의 추가 검사로 인해 즉시 실패해야 합니다.

Chat SDK의 listenForEvents() 메서드는 지정된 채널에서 사용자의 권한이 업데이트되었음을 모든 관계자에게 알립니다. 클라이언트는 이러한 변경 사항을 수신하여 사용자 경험을 업데이트하고 서버에 새 액세스 토큰을 요청해야 합니다. 서버에서는 revokeToken()을 사용하여 이전 액세스 토큰을 해지하고 grantToken()을 사용하여 업데이트된 액세스 규칙으로 새 토큰을 생성해야 합니다.

어떤 제한이 적용되고 있는지 쿼리하기:

솔루션 내에서 사용자에게 어떤 제한이 적용되고 있는지 쿼리할 수 있어야 합니다. 서버에서 실행되는 모더레이션 대시보드를 만드는 경우, 모더레이터에게 이 상태를 표시하려면 사용자가 이미 유인 상태인지 또는 뮤트 상태인지 쿼리할 수 있어야 합니다.

클라이언트 측에서는 메시지 수신자가 뮤트되었는지 또는 차단되었는지 쿼리할 수 있는 기능이 있어야 앱에서 사용자에게 메시지를 수신하지 못하고 보낼 수 없음을 경고할 수 있습니다.

Chat SDK는 현재 제한 사항을 검색할 수 있는 API 제품군, 즉 사용자에 대해 작동하는 getChannelRestrictions()getUserRestrictions()와 채널에 대해 작동하는 getUsersRestrictions()를 노출합니다.

부적절한 콘텐츠에 대한 메시지 또는 사용자 신고:

모든 사용자에게 안전하고 즐거운 채팅 환경을 제공하려면 문제가 있는 사용자나 메시지를 검토 및 조치를 위해 운영자에게 신고할 수 있어야 합니다.

클라이언트 측에서는 채팅 애플리케이션에서 사용자가 문제가 있는 메시지를 강조 표시하고 몇 가지 선택적 추가 피드백을 제공할 수 있는 방법이 필요합니다. Chat SDK는 이를 위해 message.report( ) 및 user.report( ) 메서드를 제공하며, 클라이언트에서 이를 호출해야 합니다.

서버 측에서는 이러한 메시지 보고서를 수신하여 관리자가 조치를 취할 수 있도록 관리자에게 표시해야 합니다. 관리자는 메시지 자체를 편집/삭제하거나 문제가 지속되는 경우 사용자에게 뮤트나 차단 등의 조치를 취할 수 있습니다. Chat SDK는 일반적으로 서버에서 처리하는 보고된 메시지를 수신하기 위해 listenForEvents( ) 메서드를 제공합니다.

채널 모니터는 이 모더레이션 아키텍처에서 어떤 역할을 하나요?

채널 모니터는 사용자가 보내는 메시지를 수동으로 검토하여 사용자를 뮤트하거나 차단할 수 있는 대시보드를 제공합니다. 대시보드는 setRestrictions() 호출을 대신 처리하며 각 채널 내에서 차단된 사용자에 대한 최신 정보를 항상 표시해 줍니다.

개발자에게 남은 일은 이러한 금지 및 뮤트 요청을 더 넓은 애플리케이션 아키텍처에 통합하는 것입니다. 특히 프로덕션 애플리케이션을 배포한 PubNub 고객은 이미 PubNub 액세스 관리자를 사용하여 데이터에 대한 액세스를 제어하고 사용자에게 각 채널에 필요한 RW 액세스만 허용되도록 하고 있습니다. listenForEvents()에서 받은 '뮤트' 및 '금지' 업데이트를 기반으로 기존 액세스 관리자 서버 구성 요소를 호출하여 해당 사용자의 액세스 토큰을 취소하고 새로 부여해야 합니다.

또한 클라이언트 애플리케이션이 listenForEvents( ) 또는 getXRestrictions() API 제품군에서 수신한 업데이트에 따라 적절한 조치를 취하고 있는지 확인해야 합니다. 금지된 사용자에게 메시지를 보내면 절대 수신되지 않도록 설정되므로 사용자가 메시지를 보내지 못하도록 하세요.

사용자 뮤트 및 차단하기: 샘플 애플리케이션을 사용한 데모

이전 섹션에서 설명한 것처럼 채널 모니터는 강력한 관리 솔루션의 한 부분입니다.

이 문서에서는 사용자가 대시보드에서 뮤트되거나 차단되었을 때 클라이언트에서 조치를 취할 수 있는 방법을 보여드리지만, 프로덕션 애플리케이션에는 PubNub Access Manager와 통합되는 서버 로직도 포함되어 있습니다.

기본 샘플 애플리케이션은 API에 대한 listenForEvents( )를 구현하지 않으므로 약간의 변경이 필요합니다.

HomeScreen.tsx에서 그림과 같이 TimetokenUtils 및 Alert 임포트를 추가합니다:

import { Channel, Membership, TimetokenUtils } from "@pubnub/chat"
import { StyleSheet, ScrollView, TouchableHighlight, TouchableOpacity, Alert } from "react-native"
Enter fullscreen mode Exit fullscreen mode

그런 다음 "초대" 리스너가 정의된 동일한 useEffect에서 아래와 같이 "중재" 이벤트를 알림으로 표시하는 두 번째 리스너를 정의합니다:

const removeModerationListener = chat.listenForEvents({
  channel: chat.currentUser.id,
  type: "moderation",
  callback: async (event: Event<"moderation">) => {
    Alert.alert(
      "Restriction: " + event.payload.restriction,
      `Reason (bans only): ${
        event.payload.reason == null ? "" : event.payload.reason
      }\nChannel Id: ${event.channelId}\nBy: ${
        event.userId
      }\nTime: ${TimetokenUtils.timetokenToDate(event.timetoken).toLocaleString([], {
        weekday: "short",
        month: "short",
        day: "numeric",
        hour: "numeric",
        minute: "numeric",
      })}`,
      [{ text: "OK" }]
    )
  },
})
Enter fullscreen mode Exit fullscreen mode

프로덕션 코드와는 거리가 멀지만 최소한 중재 이벤트가 수신되었음을 알 수 있습니다.

채널에서 사용자 뮤트하기

두 디바이스에서 실행 중인 코드를 수정한 후 test-user-607을 뮤트하면 아래와 같이 '중재' 이벤트가 트리거됩니다:

모더레이션 이벤트의 형식은 types.ts에 지정되어 있으며, 특히 ModerationEventPayload는 "뮤트됨"이 됩니다.

해당 필드는 사용자 차단에만 적용되므로 사용자를 뮤트할 이유가 없다는 점에 유의하세요.

채널에서 사용자 차단하기

채널에서 사용자를 차단하는 것은 뮤트하는 것과 매우 유사합니다. 대시보드에서 관리자에게 차단 사유를 입력하라는 메시지가 표시되며, 위 스크린샷은 '차단 기능 테스트 중'이라는 사유로 방금 차단된 test-user-935를 보여줍니다.

뮤트할 때와 마찬가지로 모더레이션 이벤트의 형식은 types.ts에 지정되며, ModerationEventPayload는 "금지됨"이 됩니다.

사용자 뮤트 또는 차단 해제하기

테스트 사용자 607과 테스트 사용자 935의 각각의 제한이 해제된 결과를 위의 스크린샷에서 확인할 수 있습니다. 앞서 설명한 대로 모더레이션 이벤트의 형식은 types.ts에 제공됩니다.

요약

채팅 애플리케이션을 개발하는 것은 첫 번째 단계에 불과하며, 증가하는 사용자 기반을 지원하기 위해 앱을 배포한 후에 가장 어려운 문제가 발생합니다. BizOps Workspace는 채팅 애플리케이션의 모든 측면을 관리하도록 설계된 도구 모음으로, 출시 후의 문제를 간소화해 줍니다.

이 문서에서는 채널 모니터에 초점을 맞추었지만 BizOps Workspace의 기능을 계속 확장해 나가고 있습니다. 이 문서에서는 Chat SDK에만 초점을 맞추었지만 이 SDK는 채널 모니터의 전체 기능을 지원하지만 향후 추가 SDK 지원에 대한 발표를 기대해 주세요.

도움이나 지원이 필요한 경우 언제든지 전담 지원팀에 문의하거나 개발자 관계팀( devrel@pubnub.com)으로 이메일을 보내주세요.

펍넙이 어떤 도움을 드릴 수 있나요?

이 문서는 원래 PubNub.com에 게시되었습니다.

저희 플랫폼은 개발자가 웹 앱, 모바일 앱, IoT 디바이스를 위한 실시간 상호작용을 구축, 제공, 관리할 수 있도록 지원합니다.

저희 플랫폼의 기반은 업계에서 가장 크고 확장성이 뛰어난 실시간 에지 메시징 네트워크입니다. 전 세계 15개 이상의 PoP가 월간 8억 명의 활성 사용자를 지원하고 99.999%의 안정성을 제공하므로 중단, 동시 접속자 수 제한 또는 트래픽 폭증으로 인한 지연 문제를 걱정할 필요가 없습니다.

PubNub 체험하기

라이브 투어를 통해 5분 이내에 모든 PubNub 기반 앱의 필수 개념을 이해하세요.

설정하기

PubNub 계정에 가입하여 PubNub 키에 무료로 즉시 액세스하세요.

시작하기

사용 사례나 SDK에 관계없이 PubNub 문서를 통해 바로 시작하고 실행할 수 있습니다.

Top comments (0)