모바일 애플리케이션을 운영하다 보면 최종 사용자에게 정보, 경고 또는 안내 사항을 전달해야 하는 경우가 자주 발생합니다. 이를 실현하는 방법 중 하나가 바로 사용자에게 인앱 공지(in-app announcement)를 보내는 것입니다.
이번 포스트에서는 서버리스 Redis를 활용해 사용자에게 공지를 전달하는 모바일 애플리케이션을 만들어 보겠습니다. 모바일 앱 개발에는 React Native를, 앱에 직접 연결되는 서버리스 Redis로는 Upstash를 사용합니다.
인앱 공지란 무엇인가?
인앱 공지는 중요한 정보를 알리거나, 사용자의 행동에 대해 알려주거나, 특정 위치로 안내하기 위해 최종 사용자에게 전송하는 메시지입니다.
이러한 공지는 코드를 수정하거나 새 버전의 앱을 배포하지 않고도 사용자와 소통할 수 있게 해주는 강력한 수단입니다.
이번 예제에서는 사용자가 딱 한 번만 읽으면 되는 메시지를 공지하는 솔루션을 구현해 보겠습니다. 이러한 구현은 다음과 같은 용도로 활용할 수 있습니다:
- 업데이트 또는 신규 릴리스 공지
- 앱의 새로운 기능 소개
- 사용자 100만 명 돌파 등 기념할 만한 순간 축하
서버리스 공지 시스템 설계
이번 사례에서는 가장 최근의 공지만 사용자에게 단 한 번 표시하고자 합니다. 이를 위해서는 각 공지에 버전을 부여해 순서를 정하고, 사용자가 아직 확인하지 않은 경우에만 최신 공지를 전달해야 합니다. 따라서 충족시켜야 할 조건은 다음과 같습니다.
- 최신 공지만 표시한다
- 모든 사용자에게 각 공지를 단 한 번만 표시한다
이 목적을 위해 필요한 것이 바로 Redis의 정렬된 집합(Sorted Set)입니다. 정렬된 집합은 공지들을 버전별로 저장하고 정렬하여 체계적으로 관리할 수 있게 해줍니다.
정렬된 집합은 맵(map)과 유사한 형태의 정렬된 데이터 타입으로, 키(key)는 문자열 메시지이고 값(value)은 해당 메시지에 할당된 점수(score)입니다. 데이터는 점수를 기준으로 정렬되며, 이번 사례에서 점수는 곧 공지의 버전을 의미합니다.
첫 번째 단계로, Upstash 콘솔에서 데이터베이스를 생성해야 합니다.
그다음 서버리스 Redis에 공지를 채워 넣을 수 있습니다. 저는 모든 작업을 코드로 자동화하는 것을 선호하기 때문에, Python 스크립트를 작성해 새 공지를 추가하는 방식을 선택했습니다.
먼저 Python 스크립트에서 Upstash Redis를 설정합니다:
import json
import redis
r = redis.Redis(
host='YOUR_REDIS_ENDPOINT',
port='YOUR_REDIS_PORT',
password='YOUR_REDIS_PASSWORD',
charset="utf-8",
decode_responses=True)
그런 다음 데이터베이스에서 최신 버전을 조회하고, 아래 형식으로 새 메시지를 추가합니다:
{"new_message": latest_version + 1}
message = "Please update the application before using it."
last_message = r.zrange("Announcements", 0, 0, withscores=True, desc=True)
new_version = 0
if len(last_message) > 0:
new_version = last_message[0][1] + 1
r.zadd("Announcements", {message: new_version})
Upstash 콘솔의 새로운 기능 덕분에 콘솔의 "Data Browser" 섹션에서 데이터를 확인하고 시각화할 수 있습니다.
이렇게 정렬된 집합에 새 공지를 추가할 수 있습니다.
물론 이것이 새 공지를 추가하는 유일한 방법은 아닙니다. 콘솔(https://console.upstash.com/)을 직접 사용하는 것이 편하다면 그 방법을 활용해도 좋습니다.
앱을 서버리스 Redis에 연결하기
이번 데모의 핵심 목표는 사용자와 데이터베이스의 공지를 연결하는 별도의 백엔드 서비스 없이, 최종 사용자를 Redis에 직접 연결하여 공지 확인 기능을 구현하는 것입니다.
모바일 앱에서 Redis의 공지를 읽기 위해, Upstash 콘솔에서 읽기 전용(read-only) 토큰을 발급받을 수 있습니다.
이제 React Native로 모바일 애플리케이션을 만들어 보겠습니다. React Native에 대한 자세한 내용은 공식 문서를 참고하세요.
React Native를 처음 사용한다면 환경 설정 가이드의 단계를 먼저 따라 하시기 바랍니다.
여기서는 인앱 공지 구현 방법 중 하나를 보여주는 것이 목적이므로, 새 메시지가 존재할 경우 알림(alert)으로 표시하는 단일 화면만 설계하겠습니다.
이 화면에서는 읽기 전용 토큰을 사용해 Redis로 HTTP 요청을 보내 새 메시지가 존재하는지 확인해야 합니다. 이를 위해서는 과거에 Redis에서 가져온 최신 버전을 저장해 두어야 합니다.
React Native의 로컬 스토리지로는 react-native-encrypted-storage를 사용할 수 있습니다.
사용자가 이 화면을 열 때마다, 휴대폰 로컬 스토리지에 저장된 버전보다 높은 버전을 가진 최신 메시지를 가져와야 합니다.
ZRANGEBYSCORE 명령어를 +inf부터 로컬에 저장된 버전까지의 범위로 사용하고, 결과를 하나로 제한하면 우리 문제에 대한 해결책이 됩니다. Redis 명령어에 대한 자세한 내용은 Redis commands 문서에서 확인할 수 있습니다.
다음 코드 세그먼트는 React Native에서 아래 단계들을 구현합니다:
- 마지막으로 표시된 메시지의 저장된 버전을 가져옵니다.
- 이전 단계에서 얻은 버전보다 높은 버전의 최신 공지를 가져오기 위해 Upstash로 HTTP 요청을 보냅니다.
- 요청 결과로 새 메시지가 반환되면 이를 표시하고 로컬 스토리지의 버전을 업데이트합니다.
var version = await EncryptedStorage.getItem("announcement_version");
if (version == null) {
version = "-1";
}
version = parseInt(version);
console.log(version);
await fetch(
"REPLACE_UPSTASH_REDIS_REST_URL/zrangebyscore/Announcements/+inf/" +
version +
"/WITHSCORES/LIMIT/0/1",
{
method: "GET",
headers: {
Authorization: "Bearer REPLACE_UPSTASH_REDIS_REST_READONLY_TOKEN",
Accept: "application/json",
"Content-Type": "application/json",
},
}
)
.then((response) => response.json())
.then((data) => {
var announcement = data["result"];
if (announcement && announcement.length > 0) {
version = parseInt(announcement[1]) + 1;
var message = announcement[0];
EncryptedStorage.setItem("announcement_version", version.toString());
Alert.alert("You have new message!", message);
}
})
.catch((err) => {
console.error(err);
});
최종적으로 최신 공지를 알림으로 표시하고, 로컬 버전을 Redis의 최신 버전과 동기화할 수 있습니다.
이 간단한 애플리케이션의 전체 소스 코드는 GitHub에서 확인할 수 있습니다.
마무리
이번 포스트에서는 새 메시지가 존재할 경우 사용자에게 단 한 번만 전송하는 인앱 공지 시스템을 개발했습니다.
사용자는 아직 확인하지 않은 최신 공지를 받게 되며, 받은 메시지의 버전은 로컬 스토리지에 저장됩니다. 사용자가 화면을 열 때마다 애플리케이션은 Redis에 새 메시지가 있는지 확인합니다.
이 솔루션은 읽기 전용 토큰을 통해 모바일 앱에서 Upstash Redis에 직접 연결함으로써, 어떠한 백엔드 서비스 없이도 구현할 수 있다는 점이 큰 장점입니다.
이 포스트가 여러분이 사용자와 더욱 쉽게 소통하는 데 도움이 되기를 바랍니다.