기본이라고 할 수 있죠. 카테캠에서는 프론트에서 쿠키로 구현할 시간이 부족하다고 하여 이전처럼 local storage를 이용하기로 했는데, 이번 see-realview
프로젝트는 시간이 널널하기 때문에 저장소 선택을 고민했습니다.
이건 다시 프론트와 조율해야 할 점이라서, 백엔드에서 토큰을 저장할 위치를 고민했습니다.
우선 Redis를 사용합니다! 이전에는 JpaRepository
로 구현하여 요청마다 유저 검증을 위한 DB 조회가 최소 2번 발생했습니다.
UserDetails
에서 user repository
조회 1회 token repository
조회 1회매 요청마다 유저 관련한 조회가 2번 발생하기 때문에, 이를 줄일 필요가 있었고 토큰 정보들은 Reids를 이용하여 보다 빠르게 접근하도록 변경하였습니다.
문제는 또 발생합니다. 이미지 OCR 과정이 아무리 성능을 높이긴 해도 1초라는 긴 시간이 필요하기 때문에, 이를 더 줄이고자 자주 OCR 요청이 될 것 같은 이미지들은 데이터베이스에 저장하여 빠르게 결과를 알 수 있도록 설계를 수정했습니다. 하지만 사진이 많아질수록, 데이터베이스에서 사진을 조회하는 시간이 오래 걸립니다.
이걸 방지하기 위해,
이렇게 설계했습니다. 따라서 Redis를 도입했습니다!
초간단! 도커로 redis 컨테이너 띄우고...!!
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
의존성 추가 후
@Configuration
public class RedisConfig {
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private int port;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.registerModule(new JavaTimeModule());
return objectMapper;
}
@Bean
public RedisTemplate<String, String> redisTemplate() {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
}
이렇게 템플릿 추가하면 완료!!! JPA를 이용해서 구현도 가능하지만, 사실 그런 기능 필요 없이 유저 id로 토큰 조회랑 이미지 주소로 검색 뿐이기도 하고.. 데이터베이스도 JDBC로 먼저 사용해본 뒤에 다른 기술들을 사용해서 해봤기 때문에 이것 또한 일단은! 기본 먼저 사용해보기로 .. 기타 등등의 이유도 있지만 뭐! 기본이 중요하자나?!
StringRedisSerializer
을 사용하기 때문에 ..! 그럼 왜 String을 ??
GenericJackson2Json, Jackson2Json은 역직렬화(?)랑 클래스 타입 문제가 발생해서 나중에 에러를 종잡을 수 없다고 하기 때문에 일단은 안전하게 String으로 진행.
@Override
public Optional<Token> findTokenById(Long id) {
String key = getKeyById(id);
String token = valueOperations.get(key);
if (token == null) {
return Optional.empty();
}
try {
return Optional.ofNullable(objectMapper.readValue(token, Token.class));
}
catch (JsonProcessingException e) {
throw new ServerException(ExceptionStatus.TOKEN_PARSING_ERROR);
}
}
@Override
public void save(Long id, Token token) {
try {
String key = getKeyById(id);
String value = objectMapper.writeValueAsString(token);
valueOperations.set(key, value);
}
catch (JsonProcessingException e) {
throw new ServerException(ExceptionStatus.TOKEN_PARSING_ERROR);
}
}
private static String getKeyById(Long id) {
return TOKEN_PREFIX + id;
}
이렇게 objectMapper
를 이용하면 끝!!
나중에 이미지 데이터도 들어가야 하기 때문에 prefix를 두어 구분하도록 설계했다.>!! 그래야 구분하기 쉽고 아마 redis도 인덱스를 설정할 수 있을 것 같은데... 그럴 때 편하려고 해두었다!
열흘만에 쓰는 글 .......
정리하고 싶은 내용들이 산더미지만
과제 .. 행사 .... 멈춰줘 ........