Redis storage 활용
Published: 2023-10-20 01:59:04


SNS 에서 활용 예시

###개요
Redis는 고성능 읽기 및 쓰기 기능을 제공하여 소셜 앱에 기대되는 기능을 지원하므로 소규모 팀이 빠르게 출시하고 반복할 수 있습니다.
Redis는 주요 소셜 네트워크 규모로 확장되지 않을 수 있지만 상당한 사용자 증가를 통해 앱의 첫 번째 버전을 강화할 수 있습니다.

###구성
먼저 Spring Boot 프로젝트를 설정하고 필요한 종속성을 추가합니다. spring-boot-starter-data-redis를 사용하여 Redis를 통합합니다. 이렇게하면 Redis 서버와의 연결 및 상호 작용이 가능합니다.

####1. User Profile

> 각 사용자 프로필을 Redis 해시로 나타낼 수 있습니다. 여기서 키는 사용자 ID이고 해시 필드에는 프로필 속성이 포함됩니다. 
> 관계형 모델과 비교하여 Redis Hash는 나중에 데이터베이스 스키마를 수정하지 않고도 새 프로필 속성을 쉽게 추가할 수 있는 유연성을 제공합니다. 데이터베이스 스키마 변경을 거칠 필요가 없기 때문에 사용자 프로필에 더 많은 속성을 검색하고 추가하는 방법을 정의하기만 하면 됩니다.

```java
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class UserProfileService {
    private final String USER_PROFILE_KEY = "user:%s";

    private final RedisTemplate<String, String> redisTemplate;
    private final HashOperations<String, String, String> hashOperations;

    public UserProfileService(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.hashOperations = redisTemplate.opsForHash();
    }

    public void saveUserProfile(String userId, String name, String location, String interests) {
        String key = String.format(USER_PROFILE_KEY, userId);
        hashOperations.put(key, "name", name);
        hashOperations.put(key, "location", location);
        hashOperations.put(key, "interests", interests);
    }

    public UserProfile getUserProfile(String userId) {
        String key = String.format(USER_PROFILE_KEY, userId);
        String name = hashOperations.get(key, "name");
        String location = hashOperations.get(key, "location");
        String interests = hashOperations.get(key, "interests");
        return new UserProfile(name, location, interests);
    }
}

```

####2. User Relationships

> Redis는 내장된 Set 데이터 구조를 사용하여 사용자 관계를 표현하는 보다 자연스러운 방법을 제공합니다.
> Redis에서는 사용자 ID를 키로 사용하여 사용자의 친구 ID를 Set에 직접 저장할 수 있습니다. 사용자의 친구를 검색하는 것은 ZSet의 구성원을 반환하는 것만큼 간단합니다.

```java
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.stereotype.Service;

@Service
public class UserRelationshipService {
    private final String USER_FRIENDS_KEY = "user:friends:%s";

    private final RedisTemplate<String, String> redisTemplate;
    private final SetOperations<String, String> setOperations;

    public UserRelationshipService(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.setOperations = redisTemplate.opsForSet();
    }

    public void addFriend(String userId, String friendUserId) {
        String key = String.format(USER_FRIENDS_KEY, userId);
        setOperations.add(key, friendUserId);
    }

    public boolean areFriends(String userId, String friendUserId) {
        String key1 = String.format(USER_FRIENDS_KEY, userId);
        String key2 = String.format(USER_FRIENDS_KEY, friendUserId);
        return setOperations.isMember(key1, friendUserId) && setOperations.isMember(key2, userId);
    }
}

```

####3. Posts

>  사용자에 대해 타임스탬프별로 정렬된 정렬 세트에 post_ids를 저장할 수 있습니다. 키는 사용자 ID가 될 수 있으며 각각의 새로운 post_id는 Set의 멤버로 추가됩니다. 게시물 콘텐츠 자체는 post_id를 해시 키로 사용하여 해시에 별도로 저장됩니다.
> 사용자가 새 게시물을 생성하면 새 post_id가 생성되고, 게시물 콘텐츠를 나타내는 해시가 생성되며, 사용자의 게시물 정렬 집합에 post_id가 추가됩니다. 이는 게시 타임라인을 모델링하는 자연스러운 방법을 제공합니다. 새로운 post_ids가 Set의 꼬리에 추가되고 post_ids의 ZRANGE를 사용하여 시간순으로 정렬된 게시물을 페이지로 이동할 수 있습니다.

```java
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.stereotype.Service;

@Service
public class UserPostService {
    private final String USER_POSTS_KEY = "user:posts:%s";
    private final String POSTS_HASH_KEY = "post:%s";

    private final RedisTemplate<String, String> redisTemplate;
    private final ZSetOperations<String, String> zSetOperations;
    private final HashOperations<String, String, String> hashOperations;

    public UserPostService(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.zSetOperations = redisTemplate.opsForZSet();
        this.hashOperations = redisTemplate.opsForHash();
    }

    public void createPost(String userId, String postId, String message) {
        String userPostsKey = String.format(USER_POSTS_KEY, userId);
        String postHashKey = String.format(POSTS_HASH_KEY, postId);

        zSetOperations.add(userPostsKey, postId, System.currentTimeMillis());
        hashOperations.put(postHashKey, "user_id", userId);
        hashOperations.put(postHashKey, "timestamp", String.valueOf(System.currentTimeMillis()));
        hashOperations.put(postHashKey, "message", message);
    }

    public List<UserPost> getUserPosts(String userId) {
        String userPostsKey = String.format(USER_POSTS_KEY, userId);
        Set<String> postIds = zSetOperations.reverseRange(userPostsKey, 0, -1);
        List<UserPost> userPosts = new ArrayList<>();

        for (String postId : postIds) {
            String postHashKey = String.format(POSTS_HASH_KEY, postId);
            String message = hashOperations.get(postHashKey, "message");
            String timestamp = hashOperations.get(postHashKey, "timestamp");
            userPosts.add(new UserPost(postId, message, Long.parseLong(timestamp)));
        }

        return userPosts;
    }
}

```