<현상>

- 우연히 내부 개발서버 테스트중 발견함

- "ParkUser1" 이라는 사용자가 DB에 있는데 로그인API를 "parkuser1"로 해도 로그인이 됨!

 

<이유>

- 기존에 많은 블로그에서 포스팅된 것처럼 MySQL이 대소문자가 다음과 같이 구분한다.

 VARCHAR : 대소문자 구분 안함

 VARBINARY : 대소문자 구분 함

 BINARY() 함수 : 대소문자를 구분하여 WHERE 이하 절 연산

parkuser1로 검색했는데 ParkUser1 조회완료

 

<해결방법>

1. WHERE 절에 BINARY() 함수를 사용하여 조건을 검색하면 대소문자 구분이 됩니다.

정상적으로 대소문자 구분

2. VARBINARY타입으로 테이블 생성

 

<아키텍처 보완을 위한 추가사항>

- 이번 개발계는 Spring JPA로 진행하면서 테이블이 자동생성 되었고 이때 타입이 VARCHAR로 기본생성되어 발생된 문제였다.

- 클라우드를  사용하면서 대부분의 환경을 자동화하려고 한다.

- 또한 JPA가 지향하는 바 Database 디펜던시를 낮추고 대부분의 로직은 어플리케이션으로 처리한다.

- 확장에 열려있고 변경에는 닫혀있는 디자인 원칙을 적용하는 것은, 설계레벨 뿐만 아니라 아키텍처 레벨에서도 적용되어야 한다. (OCP)

 

UserDetailsService에 다음과 같은 로직을 추가하였다.

대소문자 구분을 하지 않아서 로그인에 성공하더라도 Real값과 일치하는지 한번더 비교한다.

 

테스트 결과 MySQL을 사용할 때 Column Type을 VARCHAR로 사용해도 문제가 없었다.

<개요>

- Azure Cosmos 의 경우 RU 단위로 과금을 한다.

<내용>

- 400RU 가 최소단위이다.

- Partition Key를 잘 설정하지 않으면 HotSpot이 발생하여 RU만큼의 성능을 못 느낄 수도 있다.

- RU는 결국 초당 Read/Write를 위해서 접근하는 데이터의 양이라고 볼 수 있는데, 최적화를 잘못할 경우 기대한 한큼은 Throughput이 나오지 않을 수 있다.

- RU는 database 레벨에서 설정할 수도 있고 개별 Collection 단위로도 설정이 가능하다.

- 물리 1파티션당 10기가 용량제한이 있어서 이를 염두해야 한다.

 

<Scale설정화면>

Collection 내의 RU 설정화면

- 개별 Collection 의 사용빈도나 데이터가 다르다면 별도로 세팅하는 것이 좋다.

 

Database 내의 RU설정화면

 

database 레벨에서 RU를 설정할 경우 각 컨테이너의 RU는 별도설정이 불가능하고 database RU를 공유하는 방식이 된다.

컨테이너 RU 설정 공유화면

전체 사용량이 많지 않다면 공유하는 방법을 추천한다. (과금은 RU단위로 되기 때문)

<결론>

-400RU 로만 사용하면 월 5만원 안쪽으로 부담이 크지 않다.

-그러나 지역중복을 체크하면 x로 비용이 추가된다.

-RU를 초과하는 요청에 대해서는 즉시 응답이 오지 않으며 대기하다가 처리하는 방식으로 이루어진다.

-timeout이 발생하는 경우는 명시적으로 exception 이 발생한다.

-전체 data가 많을 경우 스캔해야하는 범위가 늘어나므로 RU최적화를 위해서는 data tts를 조정하는 것도 하나의 방법이 될 수 있다.

-파티션키 조정을 통해서 HotSpot발생을 줄이고 퍼포먼스를 증가시키는 것은 일반적인 NoSQL 과 유사하다.

기존에 Spring Boot 를 잘 사용중이었는데 어느날 신규API를 추가한 뒤로 기존 API에서 404 Not Found가 발생하기 시작했다.

{

"timestamp": "2019-09-02T02:09:12.322+0000",

"status": 404,

"error": "Not Found",

"message": "No message available",

"path": "/api/devicetelemetry/1/TestDevice1"

}

maven 빌드를 다시 해보기도 하고 API depth를 바꿔보기도 했다. 대소문자도 다시 확인하고.. 

대략 30분정도 삽질을 하다가 예전에 프레임워크 작업할 때 생각났던 component scan 옵션을 찾아봤다!

 

아무 패키지도 설정하지 않으면 기본적으로 최상위 패키지부터 순차적으로 찾아가는데 신규 API를 추가하면서 패키지구조가 약간 틀어졌다.

     <기존> org.apache.telemetry

-> <변경> org.apache.service.telemetry

<신규> org.apache.status

 

이렇게 수정이 되니 신규로 추가된 최상위 패키지인 org.apache.status 만 읽게 되고 org.apache.service 하위는 읽지 않는 문제가 발생한 것이다.

org.apache.service.telemetry

org.apache.service.status 

와 같은 형태로 맞추어주니 다시 동작하지만 동일한 상황이 재발하는 것을 방지하기 위해서 명시적으로 다음 선언을 추가하였다.

@ComponentScan(basePackages="org.apache.service")

'Rest API' 카테고리의 다른 글

Spring HttpPutFormContentFilter  (0) 2017.03.16
@Test
    public void updateDeviceTelemetryInterval() throws Exception {

        int deviceId = 1;
        String serviceName = "AAAService";

        //given service info
        ServiceDto serviceDto = new ServiceDto();
        serviceDto.setServiceId(1);
        AzureIotHubEntity azureIotHubEntity = new AzureIotHubEntity();
        azureIotHubEntity.setAzureIotHubConnectionString("");
        serviceDto.setAzureIotHub(azureIotHubEntity);
        //given device info
        DeviceDto deviceDto = new DeviceDto();
        deviceDto.setDeviceId(1);
        //given interval info
        Gson gson = new Gson();
        DeviceDto device = new DeviceDto();
        device.setTelemetryInterval(1);
        String str = gson.toJson(device);

        given(serviceService.findServiceByName(serviceName)).willReturn(serviceDto);
        given(deviceService.findDeviceById(deviceId)).willReturn(deviceDto);

        //when then
        MockHttpServletRequestBuilder createUserRequest = post("/ServiceName/setTelemetryInterval/1")
                .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
                .content(str);

        this.mvc.perform(createUserRequest)
                .andExpect(status().is2xxSuccessful());
    }

이와 같은 테스트코드는 정상적 상황을 테스트하는 상황입니다.

 

만약 해당하는 서비스가 없는 상황에서는 다음과 같은 Exception이 발생하도록 내부적으로 정의했습니다.

@ResponseStatus(value= HttpStatus.NOT_FOUND, reason="No such Service")
public class ServiceNotFoundException extends Exception {

    public ServiceNotFoundException(String message){
        super(message);
    }

    public ServiceNotFoundException(String message, Throwable cause) {
        super(message,cause);
    }
}

이와 같은 Exception이 의도한 것처럼 발생하는지 우리는 일반적으로 TestCode에서 아래와 같은 annotation을 통해서 처리합니다.

@Test(expected = ServiceNotFoundException.class)

그런데 여기서 주의해야 할점이 있습니다!!! 

 

우리가 일반적으로 Spring mvc를 사용할때 위와 같은 형태로 Exception을 정의할 경우, 내부적으로 Exception을 catch하여 밖으로 나가지 않도록 하고 HTTP STATUS코드를 변경하게 됩니다.

따라서 위와 같이 @Test(expected) 를 추가하더라도 Exception은 잡히지 않습니다.

어떻게 하면 내가 의도한 Exception을 잡아낼 수 있을지 Spring 내부소스를 살펴보았습니다.

final class TestDispatcherServlet extends DispatcherServlet {

@Override
	protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
			@Nullable Object handler, Exception ex) throws Exception {

		ModelAndView mav = super.processHandlerException(request, response, handler, ex);

		// We got this far, exception was processed..
		DefaultMvcResult mvcResult = getMvcResult(request);
		mvcResult.setResolvedException(ex);
		mvcResult.setModelAndView(mav);

		return mav;
	}
 

위와 같이 result에 resolvedException에 발생한 Exception정보를 저장하고 있습니다.

그렇다면 아래와 같이 테스트 코드를 작성하면 될것이라고 추측됩니다.

@Test
    public void updateDeviceTelemetryIntervalNonExistService() throws Exception {

        //given
        ServiceDto serviceDto = new ServiceDto();

        Gson gson = new Gson();
        DeviceDto device = new DeviceDto();
        device.setTelemetryInterval(1);
        String str = gson.toJson(device);

        given(serviceService.findServiceByName("TestService")).willReturn(serviceDto);

        //when then
        MockHttpServletRequestBuilder createUserRequest = post("/AAAService/setTelemetryInterval/1")
                .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
                .content(str);

        this.mvc.perform(createUserRequest)
                .andExpect((result)->assertTrue(result.getResolvedException().getClass().isAssignableFrom(ServiceNotFoundException.class)))
                .andExpect(status().is4xxClientError());
    }

테스트 결과 404 NotFound  와 exception의 유형을 모두 확인할 수  있습니다.

 수년간 오픈소스 기반으로 시스템을 개발하다가 이번에 Public Cloud를 사용하면서 경험했던 부분을 간단히 공유하겠습니다.

기존에 사용하던 OpenSource

기존에 처리하던 방식입니다. 대부분의 Client로부터의 요청은 Kafka로 전달됩니다. 전달받는 데이터 소스위치에 따라서 Kafka 앞에 하나의 Layer를 더 두는 것이 일반적입니다.

이렇게 개발하다보면 로직자체의 개발보다 때로는 오픈소스의 사용법, 버그패치, 운영시 발생하는 설정상 문제에 더 많은 시간을 사용하고는 합니다. 지금 개발하는 곳에서는 예전보다 개발 리소스가 더 부족한 상황이어서 과감하게 Azure PaaS 를 사용하도록 결정했습니다.

지금 개발중인 시스템은 데이터 입력채널이 대부분 IoT Device, Sensor의 영역인 것을 제외하고는 기존에 개발하던 시스템들과 구조가 90%이상 동일합니다. 

각 Layer에 해당하는 Public Cloud 및 오픈소스 현황

기존에는 Nifi -> SparkStreaming -> Druid, NoSQL, HDFS -> Grafana 를 사용하던 구조였으며 이와 매칭되는 구성요소들을 Azure 에서 찾기 시작했습니다.

 

MS Azure에 있는 다양한 PaaS 요소를 사용하여 IoT Device Data를 수집하는 간단한 예제를 만들어 보았습니다.

(소요시간은 과거 오픈소스를 사용할때와는 비교할 수 없을만큼 단축되었습니다!)

https://docs.microsoft.com/ko-kr/azure/architecture/reference-architectures/iot/

 

Azure IoT 참조 아키텍처 - Azure Reference Architectures

PaaS(platform-as-a-service) 구성 요소를 사용하는 Azure에서 IoT 애플리케이션에 대한 권장 아키텍처

docs.microsoft.com

 여기에 나오는 아키텍처와 크게 다르지 않으나 실시간 분석처리등은 간소화를 위해서 일단 제외하고 진행했습니다.

아키텍처를 그대로 진행하더라도 각 구성요소의 설정 및 구성에 따라서 동작방식이 달라지기 때문에 살펴봐야 하는 부분에 대해서 간단히 정리해봤습니다.

1. Azure IoT Hub

- 각 계정별로 무료 Hub는 1개 생성가능하고 그 외 유료 Hub의 경우 가격정책에 따라 메시지량이 정해진다. (S1 월 28000원)

- 각 Tier별로 Scale Out이 가능하지만 수작업이 필요하며, 높은 Tier를 한 개 쓸것인지 낮은 Tier에서 x times로 늘릴 것인지는 선택필요

- 자체적으로 event hub가 내장되어 있어서 별도 event hub 생성 없이 간단히 사용가능하다.

  그러나 바로 사용하는 것은 권장하지 않고 event hub로 endpoint를 연결하는 것을 권장한다.

  IoT Hub는 디바이스와의 연결을 담당하며 장애가 발생할 경우 변경할 수 도 있다. 그런데 기타 로직을 구성하는 Application이 IoTHub에 바로 연결되어 있다면 변경사항에 영향을 받기 쉽다(설계5대원칙의 OCP를 생각해보세요) 

 또한 실환경에서는 하나의 메시지큐에 여러 개의 Application 으로 확장되는 것이 일반적이며, 이 때 Consumer Group을 여러 개 두고 병렬처리를 각각 하고 파티션을 조정하는 등의 행위가 필요한데, IoT Hub는 이러한 부분에 제약사항이 있다.

- 메시지의 속성값, 내용에 따라서 Routing기능을 활용할 수 있으나 UTF-8 인코딩에 JSON String 을 사용해야만 한다.

- 개별 Device를 등록하는 방법에는 대칭키, 인증서 등의 방식이 있다.

- D2C, C2D 메시지 전송이 가능하며, 디바이스 제어를 위해서는 Device Twin 이나 Method Invoke를 사용하는 것으로 가이드한다.

 

2. Azure Event Hub

 - Apache Kafka와 유사한 구조로 만들어진 Message Queue이다.

 - IoT Hub의 경우 위치가 변경될 수 있으며 Fail over가 필요한 경우도 있다. 또한 직접적으로 Device와 연결되는 부분이기 때문에 Layer Architecture구성을 위해서 실제 메시지 소비는 Event Hub에서 이루어 지는 것을 권장한다.

 - Namespace를 생성 후 각 필요에 따라서 실제 Event Hub 를 생성하는 방식이다.

 - 보관주기를 정할 수 있으며 옵션으로 스냅샷을 Storage Account에 저장할 수 있다. (Cold Storage)

 - 필요에 따라서 파티션 수와 보관주기를 설정할 수 있다.

 - Kafka Enabled를 활성하면 기존 Kafka Consumer Application을 그대로 사용할 수 있다!

 

3. Azure App Services - Functions

 - AWS의 Lambda와 같은 Serverless이다. 지원하는 방식이나 언어가 Lambda보다는 제한적이지만 크게 사용에 어려움은 없다.

 - Azure의 PaaS 구성요소에 대해서 Trigger, Input, Output 연결이 가능해서 보다 빠르게 Application을 구성할 수 있다.

 - Azure DevOps나 Github등의 소스를 연결하여 자동배포가 가능하도록 구성할 수 있으나 현재 Trigger, Input, Output등을 사용할 경우 Extension Plugin설치가 필요한데 이러한 부분의 자동화는 아직 지원하고 있지 않다.

 - 실행 횟수에 따라 요금이 부과되지만 크게 부담되는 수준은 아니며 주요 과금은 묶여있는 App Service Plan의 사양에 따라서 결정된다.

 

4. Azure App Services - Web App

 - Web을 위한 Tomcat등의 WAS를 Managed 형태로 제공한다.

 - WAR, JAR배포를 지원하며 Spring Boot를 올리는 것도 가능하다.

 - 외부로 나타나는 EndPoint로만 접근하면 되고, 내부적으로 구성한 App Service Plan에 따라서 서버자원의 자유로운 확장이 가능하다.

 

5. Azure App Service Plan

 - Azure App Service를 이용하기 위해서 반드시 생성해야 한다.

 - Azure App Service를 위한 서버자원을 묶음형태로 정의한다고 보면 이해가 쉽다.

 - OS, CPU, Storage등의 옵션과 Tier를 설정하고 사용할 실제 App Service (Functions, Web app)를 연결하면 해당 어플리케이션들이 수행될때 지정된 만큼의 자원을 자유롭게 사용한다.

 - Linux/Windows선택에 따라서 사용하는 App Service형태에 일부 제약이 있을수도 있으니 확인이 필요하다.

 

6. Azure Database for MySQL

 - 일반적으로 사용하는 MySQL과 크게 다르지 않다.

 - AWS RDS와 유사하다.

 

7. Azure Cosmos DB

 - Azure에서 제공하는 NoSQL 이다. (AWS의 DynamoDB유사)

 - 사용할 수 있는 SQL API를 4가지 중에 선택가능하며 이론상 기존 API와 버전만 맞다면 호환이 된다.

 - 생각보다 비용이 비싸서 몇가지 고려해야 하는점이 있다.

 a. RU 및 Storage사용량에 따라서 과금이 늘어난다. https://docs.microsoft.com/en-us/azure/cosmos-db/understand-your-bill

 

Understanding your Azure Cosmos DB bill

This article explains how to understand your Azure Cosmos DB bill with some examples.

docs.microsoft.com

 b. Partition관련 Hotspot이 생기지 않도록 Key를 잡는 것이 중요하다. 선택을 잘못한다면 RU를 설정한 것보다 성능이 나오지 않는다.

    일반적인 NoSQL의 특성과 크게 다르지 않은 부분이다. 인덱스는 Collection별로 세팅이 가능하다.

 c. Multi Region 선택시 비용도 Multi로... 들어갑니다..;

 

추가로 현재 Cosmos에 Spring JPA를 사용하여 개발시 고려해야하는 부분이 몇 가지 있는데

 ㄱ. Top keyword, Custom Query

https://github.com/Microsoft/spring-data-cosmosdb/issues/144

 

Query method creation with keyword · Issue #144 · microsoft/spring-data-cosmosdb

New feature on Query Method Creation with Keyword. List the keyword and example in below. AND List findByFirstNameAndLastName(String firstName, String lastName) OR List ...

github.com

 ㄴ. page처리시 next page에 관련된 이슈 -> 이부분은 제가 지금 테스트했을때는 발생하지 않고 있습니다.(2019.08.05)

https://github.com/Microsoft/spring-data-cosmosdb/issues/225

 

Issue Spring Data Rest · Issue #225 · microsoft/spring-data-cosmosdb

Hi, Using Spring Boot 2.0.5, Spring Data Rest 2.0.5 and Spring Data CosmosDB 2.0.5 When exposing paged resources and accessing them via RestAPI, paging does not work. The first page is returned cor...

github.com

https://github.com/Microsoft/spring-data-cosmosdb/issues/363

 

Pagination and Sorting · Issue #363 · microsoft/spring-data-cosmosdb

When using Pagination with Sorting, only the first page is sorted. The rest of the pages are unsorted. For example, a list with 5 elements, starting from page1 Page page = rep...

github.com

오픈소스에 비해서 커스터마이징할 수 있는 영역이 부족하고 몇몇 기능의 부재가 있지만 개발시간은 체감상 20%도 되지 않는듯 합니다.

특히 사용량에 따른 자세한 설정이나 모니터링을 위한 다양한 도구들이 더욱 사용을 편리하게 해주고 있습니다.

 

물론 전체적인 아키텍처와 원리를 잘 이해하고 있어야 최적의 설계를 통해서 성능을 최대화하고 비용을 최소화하는 것이 가능합니다.

<참고사이트>

https://intellipaat.com/blog/tutorial/hadoop-tutorial/introduction-hadoop/

https://www.xenonstack.com/blog/iot-analytics-platform/

 가장 많이 사용하고 있는 Spring Boot + JPA 로  Azure Cosmos DB를 연결하는 작업을 진행해보았다.

https://docs.microsoft.com/ko-kr/java/azure/spring-framework/configure-spring-boot-starter-java-app-with-cosmos-db?view=azure-java-stable


1. maven pom.xml에 아래와 같이 추가하고.

<dependency>

   <groupId>com.microsoft.azure</groupId>

   <artifactId>azure-documentdb-spring-boot-starter</artifactId>

   <version>2.0.4</version>

</dependency>


2. JPA에서 제공하는 CRUD Repository를 이용해서 다음과 같이 작업했다.

public interface DeviceTelemetryRepository extends DocumentDbRepository<DeviceTelemetry, String>{

    List<DeviceTelemetry> findAll();

    @Query(value="SELECT * FROM DeviceTelemetry where deviceId= ?1 and date >= ?2 and date <= ?3", nativeQuery = true)
    List<DeviceTelemetry> findDeviceTelemetryByDeviceIdAndDateGreaterThanAndDateLessThan(@Param("deviceId") int deviceId,
                                                               @Param("from") long from,
                                                               @Param("to") long to    );

}


3. Entity클래스의 경우 테이블에 따라서 잦은 변경이 일어나기 때문에 Controller, Service Layer에서는 DTO클래스를 별도 로 사용하는 것을 권장한다. (디자인 패턴 참고)

@Service
public class DeviceTelemetryService {
@Autowired
private DeviceTelemetryRepository deviceTelemetryRepository;

@Autowired
private ModelMapper modelMapper;

public List<DeviceTelemetryDto> getDeviceTelemetry(int deviceId, long from, long to){
return deviceTelemetryRepository.findDeviceTelemetryByDeviceIdAndDateBetween(deviceId, from, to).stream()
.map(deviceTelemetry -> modelMapper.map(deviceTelemetry, DeviceTelemetryDto.class))
.collect(Collectors.toList());
}

}

4. Controller 까지 완성하여. 테스트하였는데 아래와 같은 오류가 발생한다.

@RequestMapping(path="/api/")
public class DeviceTelemetryController {

@Autowired
private DeviceTelemetryService deviceTelemetryService;


@RequestMapping(value="/{serviceId}/{deviceId}/telemetry", method= RequestMethod.GET)
public @ResponseBody
List<DeviceTelemetryDto> getAllDeviceTelemetry(@PathVariable("deviceId") int deviceId,
@RequestParam("from") long from,
@RequestParam("to") long to) {

return deviceTelemetryService.getDeviceTelemetry(deviceId,from,to);
}
}

java.lang.IllegalArgumentException: unsupported keyword: GREATER_THAN (1): [IsGreaterThan, GreaterThan]
	at com.microsoft.azure.spring.data.documentdb.repository.query.DocumentDbQueryCreator.from(DocumentDbQueryCreator.java:82) ~[spring-data-cosmosdb-2.0.3.jar:na]
	at com.microsoft.azure.spring.data.documentdb.repository.query.DocumentDbQueryCreator.and(DocumentDbQueryCreator.java:56) ~[spring-data-cosmosdb-2.0.3.jar:na]
	at com.microsoft.azure.spring.data.documentdb.repository.query.DocumentDbQueryCreator.and(DocumentDbQueryCreator.java:25) ~[spring-data-cosmosdb-2.0.3.jar:na]
	at org.springframework.data.repository.query.parser.AbstractQueryCreator.createCriteria(AbstractQueryCreator.java:122) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
	at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:95) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
	at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:81) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
	at com.microsoft.azure.spring.data.documentdb.repository.query.PartTreeDocumentDbQuery.createQuery(PartTreeDocumentDbQuery.java:38) ~[spring-data-cosmosdb-2.0.3.jar:na]
	at com.microsoft.azure.spring.data.documentdb.repository.query.AbstractDocumentDbQuery.execute(AbstractDocumentDbQuery.java:25) ~[spring-data-cosmosdb-2.0.3.jar:na]


5. Azure Cosmos DB에 문의하니 다음과 같은 답변을 받았다.

spring-data-cosmosdb 2.0.6을 사용해야 하는데 아무리 찾아도 azure-documentdb-spring-boot-starter에 포함된 최신버전은 2.0.4가 최신버전이다!

다시 문의를 했다. 수동으로 업데이트해야하는가?

결론은 "azure-documentdb는 구 네이빙 버전입니다. 신버전을 사용하세요."


Thanks for filing issue, according to your stack trace, seems you are using version 2.0.3, if you are using Spring Boot 2.0.x, try upgrading to 2.0.6.


You can use the azure-cosmosdb-spring-boot-starter(not azure-documentdb-spring-boot-starter) version 2.0.13, which uses spring-data-cosmosdb 2.0.6. Between should have been supported in 2.0.6, check this issue. Also you can reference the integration tests in this repo for the usage.


6. pom.xml에서 azure-cosmosdb-spring-boot-starter 최신버전으로 변경하였다.

<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-cosmosdb-spring-boot-starter</artifactId>
<version>2.0.13</version>
</dependency>


7. 하지만 Spring JPA의 naming rule에 같은컬럼을 중복해서 사용하면 에러가 발생한다.

BETWEEN 쿼리로 변경!

(GREATER_THAN, BETWEEN등은 위에서 언급한 2.0.6 버전에서 정상지원한다.)



8. Repository 소스 변경(@Query는 없어도 쿼리를 자동으로 생성하여 동작한다)

public interface DeviceTelemetryRepository extends DocumentDbRepository<DeviceTelemetry, String> {

List<DeviceTelemetry> findAll();

// @Query(value="SELECT * FROM DeviceTelemetry where deviceId= ?1 and date >= ?2 and date <= ?3", nativeQuery = true)
List<DeviceTelemetry> findDeviceTelemetryByDeviceIdAndDateBetween(@Param("deviceId") int deviceId,
@Param("from") long from,
@Param("to") long to );
}

9. Azure Cosmos DB에서 정상적으로 데이터를 조회하는 것을 확인할 수 있다.

[

{ "id": "3ebd07c0-0740-466f-acb4-1e04a58cdf1a", "serviceId": 1, "deviceId": 1, "contents": "{\"temperature\":34.797642257199705,\"humidity\":79.18982439419167,\"illuminance\":100}", "date": 1552376519931 }, { "id": "9424f15a-e452-4dcc-8ff6-bc3707b7ec3a", "serviceId": 1, "deviceId": 1, "contents": "{\"temperature\":25.964463142640522,\"humidity\":73.64654000868197,\"illuminance\":100}", "date": 1552376579937 }, { "id": "c72aed1b-b4d0-4338-a21f-ae8b7e5b4eba", "serviceId": 1, "deviceId": 1, "contents": "{\"temperature\":28.32913263660401,\"humidity\":73.40374660917695,\"illuminance\":100}", "date": 1552376639938 }, { "id": "c1571a80-7eb6-49dc-be9f-32457e41f69a", "serviceId": 1, "deviceId": 1, "contents": "{\"temperature\":30.035071643495087,\"humidity\":70.52682127516005,\"illuminance\":100}", "date": 1552376699940 } ]

참고사이트)

1. 위 내용 관련하여 MS Github에서 주고 받은 내용들

https://github.com/Microsoft/spring-data-cosmosdb/issues/347


2. 현재 google 에서 조회하면 가장 상단에 Azure SDK for Java Stable 나오는 페이지

https://docs.microsoft.com/ko-kr/java/azure/spring-framework/configure-spring-boot-starter-java-app-with-cosmos-db?view=azure-java-stable




+ Recent posts