설명

설정

Title Method Name Request Param Response Type
관심사 전체 리스트 조회 getInterest String path : 조회할 옵션의 도메인 Path InteresteResponse : 옵션 값의 정보를 담은 Response 객체
제품 리스트 조회 getProduct - ProductResponse
인기 게시글 조회 getPopular - GatheringContentsResponse
표현 조회 하기 getExpression - ExpressionResponse
근처 모임 모임 리스트 조회 getGathering - GatheringResponse
노출 위치별 배너 조회 getBanner - BannerResponse
유저 리프레쉬 토큰 업데이트 getAuthRefresh - RefreshTokenResponse

Service.java

package com.sinor.cache.main.service;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.client.WebClient;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sinor.cache.admin.metadata.model.MetadataGetResponse;
import com.sinor.cache.admin.metadata.service.MetadataService;
import com.sinor.cache.common.CustomException;
import com.sinor.cache.common.ResponseStatus;
import com.sinor.cache.main.model.MainCacheResponse;

import lombok.AllArgsConstructor;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.Map;

@AllArgsConstructor
@Service
@Transactional
public class MainCacheService implements IMainCacheServiceV1 {
	private WebClient webClient;
	private final RedisTemplate<String, String> redisTemplate;
	private final ObjectMapper objectMapper;
	private final MetadataService metadataService;

	/**
	 * Main 서버에 요청을 보내는 메서드
	 * @param path 요청 path
	 * @param queryString 요청 queryString
	 */
	public String getMainPathData(String path, MultiValueMap<String, String> queryString) {

		//테스트 Main uri
		UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("<http://mainHost:8080/{path}>");
		builder.path(path);

		if(queryString != null) builder.queryParams(queryString);
		// uri 확인
		System.out.println(builder.toUriString());

		String mainResponse = webClient.get()
				.uri(builder.build().toUri())
				.retrieve()
				.bodyToMono(String.class) // 메인 서버에서 오는 요청을 String 으로 받는다.
				// main 서버는 모든 데이터에 대해 ok, data 형태로 넘어온다. 이를 받을 Response 객체를 활용할 수 없을까?
				.log()
				.block();
		System.out.println(mainResponse);
		// 여기서 그냥 반환을 MainServerResponse 로 하는 것
		// 캐시 서버에서 Main 의 데이터를 활용할 것도 아닌데 String 에서 꼭 변환을 해야할까?
		return mainResponse;
	}

	/**
	 * Main 서버에 요청을 보내는 메서드
	 * @param path 요청 path
	 * @param queryString 요청 queryString
	 * @param body Requestbody
	 */
	public String postMainPathData(String path, MultiValueMap<String, String> queryString, Map<String, String> body) {

		UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("<http://mainHost:8080/{path}>");
		builder.path(path);

		if(queryString != null) builder.queryParams(queryString);
		// uri 확인
		System.out.println(builder.toUriString());

		return webClient.post()
				.uri(builder.build().toUri())
				.bodyValue(body)
				.retrieve()
				.bodyToMono(String.class)
				.log()
				.block();
	}

	/**
	 * Main 서버에 요청을 보내는 메서드
	 * @param path 요청 path
	 * @param queryString 요청 queryString
	 */
	public String deleteMainPathData(String path, MultiValueMap<String, String> queryString) {

		UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("<http://mainHost:8080/{path}>");
		builder.path(path);

		if(queryString != null) builder.queryParams(queryString);
		// uri 확인
		System.out.println(builder.toUriString());

		return webClient.delete()
				.uri(builder.build().toUri())
				.retrieve()
				.bodyToMono(String.class)
				.log()
				.block();
	}

	/**
	 * Main 서버에 요청을 보내는 메서드
	 * @param path 요청 path
	 * @param queryString 요청 queryString
	 * @param body Requestbody
	 */
	public String updateMainPathData(String path, MultiValueMap<String, String> queryString, Map<String, String> body) {

		UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("<http://mainHost:8080/{path}>");
		builder.path(path);

		if(queryString != null) builder.queryParams(queryString);
		// uri 확인
		System.out.println(builder.toUriString());

		return webClient.put()
				.uri(builder.build().toUri())
				.bodyValue(body)
				.retrieve()
				.bodyToMono(String.class)
				.log()
				.block();
	}

	/**
	 * 캐시에 데이터가 있는지 확인하고 없으면 데이터를 조회해서 있으면 데이터를 조회해서 반환해주는 메소드
	 * opsForValue() - Strings 를 쉽게 Serialize / Deserialize 해주는 Interface
	 * @param path 특정 path에 캐시가 있나 확인하기 위한 파라미터
	 * @return 값이 있다면 value, 없다면 null
	 */
	public String getDataInCache(String path) {
		String cachedData = redisTemplate.opsForValue().get(path);
		System.out.println("cachedData: " + cachedData + "값입니다.");
		return cachedData;
	}

	/**
	 * 캐시에 데이터가 없으면 메인 데이터를 조회해서 캐시에 저장하고 반환해주는 메소드
	 * @param path 검색할 캐시의 Path
	 * @param queryString 각 캐시의 구별을 위한 QueryString
	 */
	public String postInCache(String path, MultiValueMap<String, String> queryString) {
		try {
			String response = getMainPathData(path, queryString);

			MainCacheResponse userCacheResponse = MainCacheResponse.builder()
					.response(response)
					.build();

			MetadataGetResponse metadata = metadataService.findOrCreateMetadataById(path);
			redisTemplate.opsForValue().set(path, objectMapper.writeValueAsString(userCacheResponse), metadata.getMetadataTtlSecond());

			return userCacheResponse.getResponse();

		} catch (JsonProcessingException e) {
			throw new CustomException(ResponseStatus.INTERNAL_SERVER_ERROR);
		}
	}

	// 1. RequestBody에 다음과 같은 형식으로 전달하면 캐시에 저장해주는 API
	// application/json
	// {
	//  "key": /main/expression,
	//  "value": { "url": "<http://localhost:8080/main/expression>", "createAt": "2021-08-31T15:00:00", "ttl": 600 }
	// }
	// public UserCacheResponse postCache(String key, Object value) {
	// 	try {
	// 		String jsonValue = objectMapper.writeValueAsString(value);
	// 		redisTemplate.opsForValue().set(key, jsonValue);
	// 		System.out.println("redisTemplate.opsForValue().get(key) = " + redisTemplate.opsForValue().get(key));
	// 		return objectMapper.readValue(jsonValue, UserCacheResponse.class);
	// 	} catch (Exception e) {
	// 		e.fillInStackTrace();
	// 		return null;
	// 	}
	// }
}

Controller.java

package com.sinor.cache.main.controller;

import com.sinor.cache.main.model.MainCacheRequest;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RestController;

import com.sinor.cache.main.service.MainCacheService;

@RestController
public class MainCacheController implements IMainCacheControllerV1 {

	private final MainCacheService mainCacheService;

	public MainCacheController(MainCacheService mainCacheService) {
		this.mainCacheService = mainCacheService;
	}

	/**
	 * 데이터 조회 및 캐시 조회
	 *
	 * @param path        요청에 전달된 path
	 * @param queryParams 요청에 전달된 queryString
	 * @apiNote <a href="<https://www.baeldung.com/spring-request-response-body#@requestbody>">reference</a>
	 */
	@Override
	public String getDataReadCache(String path, MultiValueMap<String, String> queryParams) {
		String pathCache = mainCacheService.getDataInCache(path);
		if (pathCache == null) {
			return mainCacheService.postInCache(path, queryParams);
		}
		return pathCache;
	}

	/**
	 * 데이터 조회 또는 생성 및 캐시 조회
	 *
	 * @apiNote <a href="<https://www.baeldung.com/spring-request-response-body#@requestbody>">reference</a>
	 * @param path 요청에 전달된 path
	 * @param queryParams 요청에 전달된 queryString
	 * @param body 요청에 전달된 RequestBody 내용에 매핑된 RequestBodyDto 객체
	 */
	@Override
	public String postDataReadCache(String path, MultiValueMap<String, String> queryParams, MainCacheRequest body) {

		return mainCacheService.postMainPathData(path, queryParams, body.getRequestBody());
	}

	/**
	 * 데이터 삭제 및 캐시 갱신
	 *
	 * @apiNote <a href="<https://www.baeldung.com/spring-request-response-body#@requestbody>">reference</a>
	 * @param path 요청에 전달된 path
	 * @param queryParams 요청에 전달된 queryString
	 */
	@Override
	public String deleteDataRefreshCache(String path, MultiValueMap<String, String> queryParams) {
		return mainCacheService.deleteMainPathData(path, queryParams);
	}

	/**
	 * 데이터 수정 및 캐시 갱신
	 * @apiNote <a href="<https://www.baeldung.com/spring-request-response-body#@requestbody>">reference</a>
	 * @param path 요청에 전달된 path
	 * @param queryParams 요청에 전달된 queryString
	 * @param body 요청에 전달된 RequestBody 내용에 매핑된 RequestBodyDto 객체
	 */
	@Override
	public String updateDataRefreshCache(String path, MultiValueMap<String, String> queryParams, MainCacheRequest body) {
		return mainCacheService.updateMainPathData(path, queryParams, body.getRequestBody());
	}
}