이 블로그 게시물에서는 사용자가 채팅 그룹에 참여하고 실시간으로 소통할 수 있는 간단한 실시간 채팅 애플리케이션을 만들어 보겠습니다.
우리는 대기 시간이 짧은 사용자 간의 실시간 메시징을 위해 Ably의 강력한 기능을 활용하고, 메시지를 지속적으로 저장하기 위해 Upstash Redis를, 애플리케이션 구축을 위해 Node.js를 활용할 것입니다.
에이블리
에이블리는 사용자 간 양방향 소통을 가능하게 하는 실시간 체험 플랫폼입니다.
이 채팅 애플리케이션에서는 Ably의 Pub/Sub 채널을 사용하여 사용자가 Ably 채널에 메시지를 "게시"하여 메시지를 보낼 수 있도록 하고 사용자가 "구독"을 통해 채널로 전송된 메시지를 받을 수 있도록 합니다.
Ably는 추가 기능을 갖춘 웹 소켓 위에 추상 레이어를 추가하여 이러한 Pub/Sub 채널을 제공합니다. 그 중 일부는 다음과 같습니다:
-
구독자 유무
-
인증
-
하트비트 메커니즘
-
대기열
-
게시된 순서대로 메시지를 전달하는 기능
-
Aly의 모든 이벤트로 함수를 호출하는 통합
-
메시지/현재 상태/메타데이터를 Kafka로 스트리밍
자세한 내용은 웹사이트를 방문하세요!
이 강력한 실시간 경험 허브를 활용하려면 Aly에서 애플리케이션을 만들어야 합니다.
먼저 Aly 계정을 만들어 보겠습니다.
그런 다음 '라이브 채팅'을 선택하여 애플리케이션을 만들 수 있습니다.

그게 다야!
지금 여기서는 다른 작업을 수행할 필요가 없습니다. API 키를 얻으려면 나중에 Aly 대시보드로 돌아가겠습니다.
업스태시 레디스
Upstash Redis를 사용하여 채팅 메시지를 지속적으로 저장할 예정입니다.
이 저장공간을 통해 사용자는 채팅에 참여할 때 채팅 기록을 검색할 수 있습니다.
Upstash Redis를 사용하면 채팅 메시지를 정렬된 목록으로 저장할 수도 있습니다. 이렇게 하면 메시지를 다시 정렬할 필요 없이 데이터베이스에서 클라이언트로 메시지를 보내는 데 도움이 됩니다.
Upstash Redis 데이터베이스를 생성하려면 Upstash 콘솔로 이동하여 로그인한 후 Redis 데이터베이스를 생성하세요.

인프라가 준비되었습니다. 이제 애플리케이션의 아키텍처로 넘어가겠습니다.
채팅 앱 아키텍처
이 채팅 애플리케이션의 디자인은 매우 간단할 것입니다.

이 데모 프로젝트에서는 단순함을 유지하기 위해 하나의 Aly 채널만 생성하겠습니다. 클라이언트는 해당 채널에 보내는 메시지를 게시하게 됩니다. 클라이언트가 메시지를 게시하면 다른 클라이언트는 채널 구독을 통해 즉시 메시지를 받게 됩니다.
클라이언트 간의 실시간 메시징 외에도 Upstash Redis에 메시지를 저장해야 합니다. 이를 위해 Aly 채널에는 우리 서버인 구독자가 한 명 더 추가됩니다. 이 서버는 클라이언트가 보낸 메시지를 수신하여 Upstash Redis로 보내고 거기에 저장합니다.
마지막으로 서버의 도움을 받아 Upstash Redis에 저장된 채팅 기록을 활용하겠습니다. Redis에서 채팅 기록을 반환하는 엔드포인트 "/history"를 서버 측에 생성하겠습니다. 클라이언트는 이 엔드포인트를 호출하여 앱을 로드할 때 채팅 기록을 검색할 수 있습니다.
보시다시피 이것은 데모 목적으로 만들어진 간단한 채팅 앱입니다. 이 채팅 애플리케이션은 이전에 이 블로그 게시물에서 언급한 Ably의 다른 기능을 사용하여 수정하고 확장할 수 있습니다.
시작해 보세요...
클라이언트 측
먼저 사용자를 위한 기본 채팅 UI를 만들어야 합니다. 간단한 index.html를 생성하겠습니다. 이를 수행하려면 웹페이지를 방문하세요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="index.css">
<title>Chat App</title>
</head>
<body>
<div class="container">
<p class="msg">Messages:</p>
<div id="messages" class="messages"></div>
<form id="msgForm" class="msgForm">
<input type="text" placeholder="Send message" class="input" id="inputBox" />
<input type="submit" class="btn" value="Send">
</form>
</div>
<script src="https://cdn.ably.io/lib/ably.min-1.js"></script>
<script src="app.js"></script>
</body>
</html> 사용자의 브라우저에는 메시지를 입력하기 위한 입력 필드, 메시지를 보내기 위한 제출 버튼, 이전 메시지를 표시하는 메시지 상자가 표시됩니다.
이제 웹페이지의 일부로 실행될 JavaScript 파일인 “app.js”를 작성하겠습니다.
먼저 Aly 채널을 만들어 보겠습니다. 그렇게 하려면 Aly 대시보드로 돌아가서 'API 키' 아래에 새 API 키를 생성해야 합니다.
이 API의 기능으로 "게시"와 "구독"을 선택하겠습니다.

이제 Aly 대시보드에 제공된 키를 사용하여 JavaScript 파일에 Aly 클라이언트를 생성할 수 있습니다.
const ably = new Ably.Realtime(‘<Ably API Key>’); 경고
클라이언트에게 API 키를 제공하는 것은 안전하지 않습니다. Aly는 "TokenRequest"라는 메커니즘을 제공합니다. 이것은 데모 애플리케이션이므로 API 키를 JavaScript 파일에 직접 제공하겠습니다. Ably의 클라이언트 인증에 대한 자세한 내용은 Aly의 토큰 문서를 확인하세요.
이제 클라이언트가 통신할 Aly 채널을 확보해 보겠습니다.
const channel = ably.channels.get('chat'); 채널을 만들 필요는 없습니다. 누군가 채널에 콘텐츠를 게시하면 채널이 생성됩니다.
메시징 기능을 구현하기 전에 먼저 사용자로부터 사용자 이름을 얻어야 합니다. 인증은 이 블로그의 초점이 아니기 때문에 매우 간단하게 하겠습니다.
let name = window.prompt("Please enter your name.", "Anonymous"); 이제 메시지 전송을 구현할 수 있습니다.
const form = document.getElementById('msgForm');
form.addEventListener('submit', (event) => {
event.preventDefault();
const message = document.getElementById('inputBox').value;
if (message.trim() !== '') {
const messageData = {
name: name,
message: message
}
channel.publish('message', messageData);
document.getElementById('inputBox').value = '';
}
}); 메시지를 보내는 방법은 매우 간단합니다! 사용자 이름과 메시지가 포함된 개체를 생성하고 이를 Aly 채널에 게시해야 합니다.
사용자가 메시지를 받을 수 있도록 하려면 해당 채널에 대한 구독을 생성하고 사용자 이름과 메시지가 포함된 메시지 상자를 추가해야 합니다.
channel.subscribe('message', (message) => {
console.log("Client received: ", message);
displayMessage(message.data);
});
function displayMessage(message) {
const incomingName = message.name;
const incomingMessage = message.message;
const messageElement = document.createElement('div');
const messageValue = document.createElement('div');
const messageWriter = document.createElement('div');
if(incomingName !== name){
messageElement.classList.add('msgSent');
}
else {
messageElement.classList.add('msgReceived');
}
messageWriter.classList.add('msgWriter');
messageValue.classList.add('msgValue');
messageWriter.textContent = incomingName;
messageValue.textContent = incomingMessage;
messageElement.appendChild(messageWriter);
messageElement.appendChild(messageValue);
const list = document.getElementById('messages');
list.appendChild(messageElement);
} 마지막으로 페이지가 처음 로드될 때 채팅 기록을 검색합니다.
document.addEventListener("DOMContentLoaded", function() {
fetchChatHistory();
});
function fetchChatHistory() {
fetch('/history')
.then((response) => {
if (!response.ok) {
throw new Error('Failed to fetch chat history');
} return response.json();
})
.then((data) => {
const history = data.history;
console.log(history);
if (history && history.length > 0) {
history.forEach((message) => {
displayMessage(JSON.parse(message));
});
}
})
.catch((error) => {
console.error('Error fetching chat history:', error);
});
} 서버측
이 데모 애플리케이션의 서버는 Ably 채널을 구독하고 이를 Upstash Redis 데이터베이스에 푸시하며 클라이언트 요청 시 Upstash Redis에서 채팅 기록을 반환합니다.
먼저 “app.js” 파일에서 서버를 구성하겠습니다.
var express = require('express'); var path = require('path');
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'ChatApp' });
});
app.use('/', router);
module.exports = app; 다음으로 “server.js”에 서버를 생성하고 Ably 채널을 구독한 후 Upstash Redis에 연결하겠습니다.
var app = require('../app');
var http = require('http');
const redis = require('redis');
const Ably = require('ably');
const port = process.env.PORT || '3000';
app.set('port', port);
var server = http.createServer(app);
server.listen(port);
const redisClient = redis.createClient({ url : "<Upstash Redis Endpoint>" });
redisClient.on("error", function(err) {
throw err;
});
redisClient.connect().then(r => {
console.log("Connected to Redis.")
})
// Ably configuration
const ably = new Ably.Realtime({
key: '<Ably API Key>',
});
// Define a channel
const channel = ably.channels.get('chat'); 다음 단계는 수신 메시지를 Upstash Redis 데이터베이스로 푸시하는 것입니다. 해당 작업은 에이블리 채널 구독으로 진행하겠습니다.
// Handle incoming messages
channel.subscribe('message', async (message) => {
const convertedMessage = JSON.stringify(message.data);
console.log('Received message:', convertedMessage);
// Store the message in Upstash Redis
await redisClient.LPUSH("AblyChatList",convertedMessage);
}); 마지막으로 Upstash Redis에서 채팅 기록을 검색하여 클라이언트에 반환하는 '/history' 엔드포인트를 구현하겠습니다.
// Get chat history endpoint
app.get('/history', async (req, res) => {
// Retrieve chat history from Upstash Redis
const messages = await redisClient.LRANGE("AblyChatList", 0, -1);
messages.reverse();
console.log("history api: ", messages);
res.json({ history: messages });
}); 앱 실행
“server.js” 파일 디렉터리로 이동하여 다음을 실행하세요:
node server.js
localhost:3000 열기 귀하의 브라우저에서. 먼저 사용자 이름을 묻습니다.

사용자 이름을 입력하면 채팅을 열 수 있습니다.
한 탭에서 메시지를 보내고 localhost:3000을 열면 다른 사용자 이름을 사용하는 다른 앱에서는 이전 탭에서 보낸 메시지를 볼 수 있습니다.

Upstash Redis 덕분에 채팅 애플리케이션을 열 때마다 채팅 기록을 검색할 수 있습니다.
결론
Ably는 애플리케이션 간의 실시간 통신을 향상시키기 위한 다양한 기능을 제공합니다. 강력한 실시간 세계를 다양한 용도로 활용할 수 있습니다.
이 블로그 게시물에서는 Ably를 사용하여 Pub/Sub 채널을 사용하는 실시간 채팅 애플리케이션을 구축했습니다. 이 작업을 수행하는 동안 Upstash Redis 데이터베이스에 메시지를 저장했습니다. 이 두 가지 도구를 통해 이 애플리케이션을 쉽고 빠르게 구축할 수 있었습니다.
이 프로젝트는 단지 Upstash Redis와 Ably를 사용하는 방법을 시연하기 위한 것이기 때문에 범위를 매우 단순하게 유지했습니다. 관심이 있으시면 Ably 및 Upstash Redis의 기능을 활용하여 강력하고 확장 가능하며 안전한 실시간 애플리케이션을 구축할 수 있습니다.