Amazon IVS Chat Client Messaging SDK: JavaScript 자습서 1부: 채팅룸 - Amazon IVS

Amazon IVS Chat Client Messaging SDK: JavaScript 자습서 1부: 채팅룸

본 문서는 두 파트로 구성된 자습서 중 첫 번째 파트에 해당하는 자습서입니다. 이 자습서에서는 JavaScript/TypeScript를 통해 완전한 기능을 갖춘 앱을 구축하여 Amazon IVS Chat Client Messaging JavaScript SDK로 작업하기 위한 필수 사항을 설명하고 있습니다. 여기에서 지칭하는 앱은 Chatterbox라고 합니다.

이 자습서는 숙련된 개발자이나, Amazon IVS Chat Messaging SDK를 처음 접하는 사용자를 위해 작성되었습니다. 사용자는 이미 JavaScript/TypeScript 프로그래밍 언어와 React 라이브러리에 대한 친숙도가 있어야 합니다.

간략하게 나타내자면 Amazon IVS Chat Client Messaging JavaScript SDK를 Chat JS SDK라고 합니다.

참고: 경우에 따라 JavaScript와 TypeScript의 코드 예제가 동일하므로 서로 합쳐져 있습니다.

이 자습서의 첫 번째 부분은 여러 섹션으로 나뉩니다.

전체 SDK 설명서를 보려면 우선 Amazon IVS 챗 클라이언트 메시징 SDK(Amazon IVS 챗 사용 설명서에서 참조) 및 Chat Client Messaging: SDK for JavaScript Reference(GitHub)를 참조하세요.

사전 조건 

  • 사용자는 JavaScript/TypeScript 및 React 라이브러리에 익숙하여야 합니다. React에 익숙하지 않다면 React 시작하기에서 기본 사항을 검토해 보시기 바랍니다.

  • Amazon IVS 챗 시작하기를 읽고 이해합니다.

  • 기존 IAM 정책에 정의된 CreateChatToken 및 CreateRoom 기능을 사용하여 AWS IAM 사용자를 생성합니다. (Amazon IVS 챗 시작하기를 참조하세요.)

  • 이 사용자의 비밀/액세스 키가 AWS 보안 인증 파일에 저장되어 있는지 확인합니다. 지침은 AWS CLI 사용 설명서(특히 구성 및 보안 인증 파일 설정)를 참조합니다.

  • 채팅룸을 생성하고 ARN을 저장합니다. Amazon IVS 챗 시작하기를 참조하세요. (ARN을 저장하지 않은 경우 나중에 콘솔이나 Chat API를 사용하여 조회할 수 있습니다.)

  • NPM 또는 Yarn 패키지 관리자를 사용하여 Node.js 14+ 환경을 설치합니다.

로컬 인증/권한 부여 서버 설정

백엔드 애플리케이션은 채팅룸을 생성하고 Chat JS SDK가 채팅룸의 클라이언트를 인증하고 권한을 부여하는 데 필요한 채팅 토큰을 생성하는 일을 맡습니다. AWS 키는 모바일 앱에 안전하게 저장할 수 없으므로 자체 백엔드를 사용해야 합니다. 실력 좋은 공격자는 이러한 키를 추출하여 AWS 계정에 액세스할 수 있습니다.

Amazon IVS 채팅 시작하기에서 채팅 토큰 생성을 참조하세요. 순서도에서 볼 수 있듯이 서버 측 애플리케이션은 채팅 토큰 생성을 담당합니다. 즉, 앱은 서버 측 애플리케이션에서 채팅 토큰을 요청하여 채팅 토큰을 생성하는 자체 수단을 제공해야 합니다.

이 섹션에서는 백엔드에서 토큰 공급자를 생성하는 기본 사항에 대해 알아봅니다. AWS 환경을 사용하여 채팅 토큰 생성을 관리하는 로컬 서버를 만들기 위해 Express 프레임워크를 사용합니다.

NPM을 사용하여 빈 npm 프로젝트를 생성합니다. 애플리케이션을 보관할 디렉터리를 생성하고 이를 작업 디렉터리로 만듭니다.

$ mkdir backend & cd backend

npm init을 사용하여 애플리케이션의 package.json 파일을 생성합니다.

$ npm init

이 명령은 애플리케이션의 이름 및 버전을 비롯한 여러 항목을 입력하라는 메시지를 표시합니다. 지금은 RETURN 키를 눌러 대부분의 기본값을 그대로 사용할 수 있습니다. 단, 다음은 예외입니다.

entry point: (index.js)

RETURN 키를 눌러 제안된 기본 파일 이름 index.js를 그대로 사용하거나 주 파일의 이름을 원하는 대로 입력합니다.

이제 필요한 종속 항목을 설치합니다.

$ npm install express aws-sdk cors dotenv

aws-sdk에는 루트 디렉터리에 있는 .env라는 파일에서 자동으로 로드되는 구성 환경 변수가 필요합니다. 이를 구성하려면 .env라는 새 파일을 생성하고 누락된 구성 정보를 입력합니다.

# .env # The region to send service requests to. AWS_REGION=us-west-2 # Access keys use an access key ID and secret access key # that you use to sign programmatic requests to AWS. # AWS access key ID. AWS_ACCESS_KEY_ID=... # AWS secret access key. AWS_SECRET_ACCESS_KEY=...

이제 npm init 명령에서 위에 입력한 이름으로 루트 디렉터리에 진입점(entry-point) 파일을 생성합니다. 이 경우 index.js를 사용하고 필요한 모든 패키지를 가져옵니다.

// index.js import express from 'express'; import AWS from 'aws-sdk'; import 'dotenv/config'; import cors from 'cors';

이제 express의 새 인스턴스를 생성합니다.

const app = express(); const port = 3000; app.use(express.json()); app.use(cors({ origin: ['http://127.0.0.1:5173'] }));

그런 다음 토큰 공급자를 위한 첫 번째 엔드포인트 POST 메서드를 생성할 수 있습니다. 요청 본문에서 필수 파라미터(roomId, userId, capabilitiessessionDurationInMinutes)를 가져옵니다.

app.post('/create_chat_token', (req, res) => { const { roomIdentifier, userId, capabilities, sessionDurationInMinutes } = req.body || {}; });

필수 필드의 유효성 검사를 추가합니다.

app.post('/create_chat_token', (req, res) => { const { roomIdentifier, userId, capabilities, sessionDurationInMinutes } = req.body || {}; if (!roomIdentifier || !userId) { res.status(400).json({ error: 'Missing parameters: `roomIdentifier`, `userId`' }); return; } });

POST 메서드를 준비한 후 인증/권한 부여의 핵심 기능을 위해 aws-sdkcreateChatToken을 통합합니다.

app.post('/create_chat_token', (req, res) => { const { roomIdentifier, userId, capabilities, sessionDurationInMinutes } = req.body || {}; if (!roomIdentifier || !userId || !capabilities) { res.status(400).json({ error: 'Missing parameters: `roomIdentifier`, `userId`, `capabilities`' }); return; } ivsChat.createChatToken({ roomIdentifier, userId, capabilities, sessionDurationInMinutes }, (error, data) => { if (error) { console.log(error); res.status(500).send(error.code); } else if (data.token) { const { token, sessionExpirationTime, tokenExpirationTime } = data; console.log(`Retrieved Chat Token: ${JSON.stringify(data, null, 2)}`); res.json({ token, sessionExpirationTime, tokenExpirationTime }); } }); });

파일 끝에 express 앱의 포트 리스너를 추가합니다.

app.listen(port, () => { console.log(`Backend listening on port ${port}`); });

이제 프로젝트의 루트에서 다음 명령으로 서버를 실행할 수 있습니다.

$ node index.js

: 이 서버는 https://localhost:3000에서 URL 요청을 수락합니다.

Chatterbox 프로젝트 생성

먼저 chatterbox라는 React 프로젝트를 생성합니다. 다음 명령을 실행합니다.

npx create-react-app chatterbox

Node 패키지 관리자 또는 Yarn 패키지 관리자를 통해 Chat Client Messaging JS SDK를 통합할 수 있습니다.

  • Npm: npm install amazon-ivs-chat-messaging

  • Yarn: yarn add amazon-ivs-chat-messaging

채팅 룸에 연결

여기서는 ChatRoom을 생성하고 비동기 메서드를 사용하여 연결합니다. ChatRoom 클래스는 Chat JS SDK에 대한 사용자 연결을 관리합니다. 채팅룸에 성공적으로 연결하려면 React 애플리케이션 내에서 ChatToken의 인스턴스를 제공해야 합니다.

기본 chatterbox 프로젝트에서 생성한 App 파일로 이동하여 두 <div> 태그 사이의 모든 내용을 삭제합니다. 미리 입력된 코드는 필요하지 않습니다. 이 시점에서 App은 거의 비어 있습니다.

// App.jsx / App.tsx import * as React from 'react'; export default function App() { return <div>Hello!</div>; }

ChatRoom 인스턴스를 생성하고 useState 후크를 사용하여 상태로 전달합니다. 이를 위해서는 regionOrUrl(채팅룸이 호스팅되는 AWS 리전) 및tokenProvider(이후 단계에서 생성되는 백엔드 인증/권한 부여 흐름에 사용됨)를 전달해야 합니다.

중요: Amazon IVS Chat 시작하기에서 방을 생성한 리전과 동일한 AWS 리전을 사용해야 합니다. API는 AWS 리전 서비스입니다. 지원되는 리전 및 Amazon IVS Chat HTTPS 서비스 엔드포인트 목록은 Amazon IVS Chat 리전 페이지를 참조하세요.

// App.jsx / App.tsx import React, { useState } from 'react'; import { ChatRoom } from 'amazon-ivs-chat-messaging'; export default function App() { const [room] = useState(() => new ChatRoom({ regionOrUrl: process.env.REGION as string, tokenProvider: () => {}, }), ); return <div>Hello!</div>; }

토큰 공급자 구축

다음 단계로 ChatRoom 생성자에 필요한 파라미터 없는tokenProvider 함수를 구축해야 합니다. 먼저, 로컬 인증/권한 부여 서버 설정에서 설정한 백엔드 애플리케이션에 POST 요청을 하는 fetchChatToken 함수를 생성하겠습니다. 채팅 토큰은 SDK가 성공적으로 채팅룸 연결을 설정하는 데 필요한 정보를 포함합니다. Chat API는 사용자의 ID, 채팅룸 내 기능 및 세션 기간을 검증하는 안전한 방법으로 이러한 토큰을 사용합니다.

프로젝트 탐색기에서 fetchChatToken이라는 새 TypeScript/JavaScript 파일을 생성합니다. backend 애플리케이션에 가져오기 요청을 구축하고 응답에서 ChatToken 객체를 반환합니다. 채팅 토큰을 생성하는 데 필요한 요청 본문 속성을 추가합니다. Amazon 리소스 이름(ARN)에 정의된 규칙을 사용합니다. 이러한 속성은 CreateChatToken 엔드포인트에 문서화되어 있습니다.

참고: 여기서 사용하는 URL은 백엔드 애플리케이션을 실행했을 때 로컬 서버에서 생성한 URL과 동일한 URL입니다.

TypeScript
// fetchChatToken.ts import { ChatToken } from 'amazon-ivs-chat-messaging'; type UserCapability = 'DELETE_MESSAGE' | 'DISCONNECT_USER' | 'SEND_MESSAGE'; export async function fetchChatToken( userId: string, capabilities: UserCapability[] = [], attributes?: Record<string, string>, sessionDurationInMinutes?: number, ): Promise<ChatToken> { const response = await fetch(`${process.env.BACKEND_BASE_URL}/create_chat_token`, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ userId, roomIdentifier: process.env.ROOM_ID, capabilities, sessionDurationInMinutes, attributes }), }); const token = await response.json(); return { ...token, sessionExpirationTime: new Date(token.sessionExpirationTime), tokenExpirationTime: new Date(token.tokenExpirationTime), }; }
JavaScript
// fetchChatToken.js export async function fetchChatToken( userId, capabilities = [], attributes, sessionDurationInMinutes) { const response = await fetch(`${process.env.BACKEND_BASE_URL}/create_chat_token`, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ userId, roomIdentifier: process.env.ROOM_ID, capabilities, sessionDurationInMinutes, attributes }), }); const token = await response.json(); return { ...token, sessionExpirationTime: new Date(token.sessionExpirationTime), tokenExpirationTime: new Date(token.tokenExpirationTime), }; }

연결 업데이트 관찰

채팅 앱을 생성하기 위해서는 채팅룸 연결 상태 변화에 대해 필수적으로 대응해야 합니다. 관련 이벤트 구독부터 시작해 보겠습니다.

// App.jsx / App.tsx import React, { useState, useEffect } from 'react'; import { ChatRoom } from 'amazon-ivs-chat-messaging'; import { fetchChatToken } from './fetchChatToken'; export default function App() { const [room] = useState( () => new ChatRoom({ regionOrUrl: process.env.REGION as string, tokenProvider: () => fetchChatToken('Mike', ['SEND_MESSAGE']), }), ); useEffect(() => { const unsubscribeOnConnecting = room.addListener('connecting', () => {}); const unsubscribeOnConnected = room.addListener('connect', () => {}); const unsubscribeOnDisconnected = room.addListener('disconnect', () => {}); return () => { // Clean up subscriptions. unsubscribeOnConnecting(); unsubscribeOnConnected(); unsubscribeOnDisconnected(); }; }, [room]); return <div>Hello!</div>; }

다음으로 연결 상태를 읽을 수 있는 기능을 제공해야 합니다. useState 후크를 사용하여 App에서 로컬 상태를 생성하고 각 리스너 내에서 연결 상태를 설정합니다.

TypeScript
// App.tsx import React, { useState, useEffect } from 'react'; import { ChatRoom, ConnectionState } from 'amazon-ivs-chat-messaging'; import { fetchChatToken } from './fetchChatToken'; export default function App() { const [room] = useState( () => new ChatRoom({ regionOrUrl: process.env.REGION as string, tokenProvider: () => fetchChatToken('Mike', ['SEND_MESSAGE']), }), ); const [connectionState, setConnectionState] = useState<ConnectionState>('disconnected'); useEffect(() => { const unsubscribeOnConnecting = room.addListener('connecting', () => { setConnectionState('connecting'); }); const unsubscribeOnConnected = room.addListener('connect', () => { setConnectionState('connected'); }); const unsubscribeOnDisconnected = room.addListener('disconnect', () => { setConnectionState('disconnected'); }); return () => { unsubscribeOnConnecting(); unsubscribeOnConnected(); unsubscribeOnDisconnected(); }; }, [room]); return <div>Hello!</div>; }
JavaScript
// App.jsx import React, { useState, useEffect } from 'react'; import { ChatRoom } from 'amazon-ivs-chat-messaging'; import { fetchChatToken } from './fetchChatToken'; export default function App() { const [room] = useState( () => new ChatRoom({ regionOrUrl: process.env.REGION, tokenProvider: () => fetchChatToken('Mike', ['SEND_MESSAGE']), }), ); const [connectionState, setConnectionState] = useState('disconnected'); useEffect(() => { const unsubscribeOnConnecting = room.addListener('connecting', () => { setConnectionState('connecting'); }); const unsubscribeOnConnected = room.addListener('connect', () => { setConnectionState('connected'); }); const unsubscribeOnDisconnected = room.addListener('disconnect', () => { setConnectionState('disconnected'); }); return () => { unsubscribeOnConnecting(); unsubscribeOnConnected(); unsubscribeOnDisconnected(); }; }, [room]); return <div>Hello!</div>; }

연결 상태를 구독한 후 연결 상태를 표시하고 useEffect 후크 내의 room.connect 메서드를 사용하여 채팅룸에 연결합니다.

// App.jsx / App.tsx // ... useEffect(() => { const unsubscribeOnConnecting = room.addListener('connecting', () => { setConnectionState('connecting'); }); const unsubscribeOnConnected = room.addListener('connect', () => { setConnectionState('connected'); }); const unsubscribeOnDisconnected = room.addListener('disconnect', () => { setConnectionState('disconnected'); }); room.connect(); return () => { unsubscribeOnConnecting(); unsubscribeOnConnected(); unsubscribeOnDisconnected(); }; }, [room]); // ... return ( <div> <h4>Connection State: {connectionState}</h4> </div> ); // ...

채팅룸 연결을 성공적으로 구현했습니다.

전송 버튼 구성 요소 생성

이 섹션에서는 연결 상태 별로 각기 다른 디자인의 전송(send) 버튼을 생성합니다. 전송 버튼을 사용하면 채팅룸에서 메시지를 쉽게 보낼 수 있습니다. 또한 연결이 끊겼거나 채팅 세션이 만료 등과 같이 메시지를 전송 가능 여부 및 시기를 시각적으로 표시하는 역할을 합니다.

먼저, Chatterbox 프로젝트의 src 디렉터리에 새 파일을 생성하고 이름을 SendButton로 지정합니다. 그런 다음 채팅 애플리케이션용 버튼을 표시할 구성 요소를 생성합니다. SendButton을 내보내고 App으로 가져옵니다. 비어 있는 <div></div> 사이에 <SendButton />을 추가합니다.

TypeScript
// SendButton.tsx import React from 'react'; interface Props { onPress?: () => void; disabled?: boolean; } export const SendButton = ({ onPress, disabled }: Props) => { return ( <button disabled={disabled} onClick={onPress}> Send </button> ); }; // App.tsx import { SendButton } from './SendButton'; // ... return ( <div> <div>Connection State: {connectionState}</div> <SendButton /> </div> );
JavaScript
// SendButton.jsx import React from 'react'; export const SendButton = ({ onPress, disabled }) => { return ( <button disabled={disabled} onClick={onPress}> Send </button> ); }; // App.jsx import { SendButton } from './SendButton'; // ... return ( <div> <div>Connection State: {connectionState}</div> <SendButton /> </div> );

다음으로 App에서 onMessageSend라는 함수를 정의하고 이를 SendButton onPress 속성에 전달합니다. isSendDisabled라는 다른 변수를 정의하여(방이 연결되어 있지 않을 때 메시지를 보내지 못하도록 함) SendButton disabled 속성에 전달합니다.

// App.jsx / App.tsx // ... const onMessageSend = () => {}; const isSendDisabled = connectionState !== 'connected'; return ( <div> <div>Connection State: {connectionState}</div> <SendButton disabled={isSendDisabled} onPress={onMessageSend} /> </div> ); // ...

메시지 입력 생성

Chatterbox 메시지 표시줄은 채팅룸에 메시지를 보내기 위해 상호 작용하는 구성 요소입니다. 일반적으로 메시지 작성을 위한 텍스트 입력과 메시지를 보내기 위한 버튼을 포함합니다.

MessageInput 구성 요소를 생성하려면 먼저 src 디렉터리에 새 파일을 생성하고 이름을 MessageInput로 지정합니다. 그런 다음 채팅 애플리케이션용 입력을 표시할 통제된 입력 구성 요소를 생성합니다. MessageInput을 내보내고 App로 가져옵니다(<SendButton /> 위로).

기본값으로 빈 문자열이 있는 useState 후크를 사용하여 messageToSend라는 새 상태를 생성합니다. 앱 본문에서 messageToSendMessageInputvalue로 전달하고 onMessageChange 속성에 setMessageToSend를 전달합니다.

TypeScript
// MessageInput.tsx import * as React from 'react'; interface Props { value?: string; onValueChange?: (value: string) => void; } export const MessageInput = ({ value, onValueChange }: Props) => { return ( <input type="text" value={value} onChange={(e) => onValueChange?.(e.target.value)} placeholder="Send a message" /> ); }; // App.tsx // ... import { MessageInput } from './MessageInput'; // ... export default function App() { const [messageToSend, setMessageToSend] = useState(''); // ... return ( <div> <h4>Connection State: {connectionState}</h4> <MessageInput value={messageToSend} onMessageChange={setMessageToSend} /> <SendButton disabled={isSendDisabled} onPress={onMessageSend} /> </div> );
JavaScript
// MessageInput.jsx import * as React from 'react'; export const MessageInput = ({ value, onValueChange }) => { return ( <input type="text" value={value} onChange={(e) => onValueChange?.(e.target.value)} placeholder="Send a message" /> ); }; // App.jsx // ... import { MessageInput } from './MessageInput'; // ... export default function App() { const [messageToSend, setMessageToSend] = useState(''); // ... return ( <div> <h4>Connection State: {connectionState}</h4> <MessageInput value={messageToSend} onMessageChange={setMessageToSend} /> <SendButton disabled={isSendDisabled} onPress={onMessageSend} /> </div> );

다음 단계

이제 Chatterbox용 메시지 표시줄 구축을 완료했으므로 이 JavaScript 자습서의 2부인 메시지 및 이벤트로 이동하세요.