베어_
TechBear
베어_
전체 방문자
오늘
어제
  • 분류 전체보기 (336)
    • Spring (33)
      • 개념 (13)
      • Security (5)
      • 실습 (1)
      • 토비 스프링 (11)
    • JPA (6)
    • 프로젝트 기록 (24)
    • DB (13)
    • JAVA (18)
    • 알고리즘 (50)
      • 유형정리 (8)
      • Baekjoon (21)
      • LeetCode (18)
    • 디자인패턴 (0)
    • 개발서적 (79)
      • Effective Java (78)
      • 객체지향의 사실과 오해 (1)
    • 독후감 (4)
    • 보안 (2)
    • 운영체제(OS) (53)
      • 공룡책 (53)
    • 컴퓨터 네트워크 (28)
      • 컴퓨터 네트워크 하향식 접근 (23)
    • 자료구조 (1)
    • DevOps (2)
    • 앱 개발 (20)
      • 안드로이드 스튜디오 (20)

블로그 메뉴

    공지사항

    인기 글

    태그

    • 자바
    • 스레드
    • 알고리즘
    • 스프링시큐리티
    • dfs
    • 스프링
    • java
    • C++
    • 이펙티브자바
    • 백준
    • Spring
    • leetcode
    • BFS
    • 토비스프링
    • jpa
    • 자바8
    • 운영체제
    • 코드업
    • 함수형인터페이스
    • 데이터베이스

    최근 댓글

    최근 글

    티스토리

    hELLO · Designed By 정상우.
    베어_

    TechBear

    [프로젝트 기록] Redis와 직렬화/역직렬화
    프로젝트 기록

    [프로젝트 기록] Redis와 직렬화/역직렬화

    2023. 11. 10. 23:14

    [ Redis 캐시 서버의 도입 ]

    Redis 캐시를 도입하면서 겪은 디버깅 과정을 글로 적어볼까 한다. Voca-World 프로젝트에 최근 사용자 정보를 캐싱하는 기능을 도입했다. 캐싱을 한 이유는 다음과 같다. 

    1. 토큰을 기반으로 사용자 정보를 찾는 일은 매 요청시마다 발생한다. 
    2. 사용자 정보는 자주 사용되지만 자주 바뀌지 않는다.

    사용자가 로그인할 때 Redis를 통해 캐싱하였다.

    유저 정보를 캐싱하는 코드

     

    [ 문제 발생 ]

    사용자 정보를 캐싱하고 나서 단어장이 보이지 않는 문제가 발생하였다. 

     

    [ 문제의 원인 ]

    Redis에서 사용자 정보를 가져올 때 Vocabularies와 관련된 정보가 없는 것을 발견하였다.

    레디스에서 유저 정보를 불러왔을 때의 로그

    [ 문제 해결 과정 ]

    코드를 확인해보니 vocabularies 필드에 @JsonIgnore 어노테이션이 붙어있었다.

    User.class의 vocabularies 필드

    Redis는 기본적으로 객체를 저장할 때 문자열로 직렬화하여 저장한다. 이때 주로 사용되는 것이 JSON 또는 MessagePack이다. @JsonIgnore는 JSON 직렬화 및 역직렬화 중 특정 필드를 무시하고 싶을 때 사용된다. 따라서 @JsonIgnore를 제거해주어야 한다.

     

    이후에 마주친 에러는 다음과 같다.

    1. 무한루프 

    다음 User코드와 UserVocabulary코드에서는 User -> UserVocabulary -> User -> UserVocabulary와 같이 같은 객체를 계속 반복함으로써 무한루프가 돈다. 

    @Entity
    public class User {
        private Long id;
        ...
        List<UserVocabulary> vocabularies = new ArrayList<>();
    }
    @Entity
    public class UserVocabulary {
        private Long id;
    
        @ManyToOne
        private User user;
    
        @ManyToOne
        private Vocabulary vocabulary;
    }

     

    이를 해결하기 위해 한 쪽 방향에 @JsonIgnore를 붙여주어야 한다.

    @Entity
    public class UserVocabulary {
        private Long id;
    
        @JsonIgnore          // 추가됨
        @ManyToOne
        private User user;
    }

     

    2. 데이터 타입 에러 

    만약 아래와 같은 에러가 뜬다면 Java8에서는 LocalDateTime형식을 지원하지 않는다는 의미임으로 다음과 같은 작업이 필요하다.

    Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"

     

    build.gradle에 다음 코드를 추가한다.

        implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
        implementation 'com.fasterxml.jackson.core:jackson-databind'

     

    LocalDateTimed에 직렬화/역직렬화에 사용할 클래스를 지정해준다.

    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    private LocalDateTime createdAt;

     

    3. 객체안에 boolean이 있는 경우 

    boolean을 직렬화할 때는 JSON은 'true' 또는 'false'를 문자열로 표현하지만, Java에서는 'boolean'으로 직렬화된다. 따라서 객체 안에 boolean이 있는 경우 다음과 같이 ObjectMapper를 활용하여 boolean 필드를 JSON 문자열로 직렬화해야 한다.

    public void setUser(User user) {
        String key = getKey(user.getUsername());
        log.info("Set User to Redis {}:{}", key, user);
        try {
            String userData = objectMapper.writeValueAsString(user);
            userRedisTemplate.opsForValue().set(key, userData, USER_CACHE_TTL);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
    
    public Optional<User> getUser(String username) {
        String key = getKey(username);
        String user = userRedisTemplate.opsForValue().get(key);
        log.info("Get data from Redis {}:{}", key, user);
        try {
            return Optional.ofNullable(objectMapper.readValue(user, User.class));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

     

    저작자표시 비영리 변경금지 (새창열림)
      '프로젝트 기록' 카테고리의 다른 글
      • [프로젝트 기록] fetch-join을 이용해 쿼리 최적화하기
      • [프로젝트 기록] Redis 메인/레프리카 서버 만들기
      • 인공지능 기반 웹 서비스 만들기
      • 로컬 PC 서버 만들기
      베어_
      베어_
      Today I learned | 문제를 해결하는 개발자

      티스토리툴바