1. 소개

이 기사에서는 Fauna 및 Spring 에서 제공하는 IoT 애플리케이션을 빌드합니다 .

2. IoT 애플리케이션 – 고속 에지 및 분산 데이터베이스

IoT 애플리케이션은 사용자 가까이에서 작동합니다. 대기 시간이 짧은 대량의 실시간 데이터를 소비하고 처리하는 일을 담당합니다. 짧은 대기 시간과 최대 성능을 달성하려면 빠른 Edge Computing 서버와 분산 데이터베이스가 필요합니다.

또한 IoT 애플리케이션은 구조화되지 않은 데이터를 처리하는데, 주로 데이터를 소비하는 소스가 다양하기 때문입니다. IoT 애플리케이션에는 이러한 비정형 데이터를 효율적으로 처리할 수 있는 데이터베이스가 필요합니다.

이 기사에서는 온도, 심박수, 혈중 산소 수준 등과 같은 개인의 건강 바이탈을 처리하고 저장하는 IoT 애플리케이션의 백엔드를 구축합니다. 이 IoT 애플리케이션은 카메라, 인프라에서 건강 바이탈을 소비할 수 있습니다. 빨간색 스캐너 또는 스마트워치와 같은 웨어러블을 통한 센서.

3. IoT 애플리케이션을 위한 동물군 사용

이전 섹션에서 우리는 일반적인 IoT 응용 프로그램의 기능을 배웠고 IoT 공간에 사용되는 데이터베이스에 대한 기대도 이해했습니다.

아래 기능으로 인해 Fauna는 IoT 애플리케이션에서 데이터베이스로 사용하기에 가장 적합할 수 있습니다.

분산: Fauna에서 애플리케이션을 생성하면 여러 클라우드 지역에 자동으로 분산됩니다. Cloudflare Workers 또는 Fastly Compute @ Edge 와 같은 기술을 사용하는 Edge Computing 애플리케이션에 매우 유용합니다 . 우리의 사용 사례에서 이 기능은 최소한의 대기 시간으로 전 세계에 분산된 개인의 건강 정보에 빠르게 액세스, 처리 및 저장하는 데 도움이 될 수 있습니다.

문서 관계형: Fauna는 JSON 문서의 유연성과 친숙성을 기존 관계형 데이터베이스의 관계 및 쿼리 기능과 결합합니다 . 이 기능은 구조화되지 않은 IoT 데이터를 대규모로 처리하는 데 도움이 됩니다.

서버리스: Fauna를 사용하면 데이터베이스와 관련된 관리 및 인프라 오버헤드가 아니라 애플리케이션 구축에만 집중할 수 있습니다 .

4. 애플리케이션의 상위 수준 요청 흐름

높은 수준에서 애플리케이션의 요청 흐름은 다음과 같습니다.

요청 흐름

여기서 Aggregator는 다양한 센서에서 받은 데이터를 집계하는 간단한 애플리케이션입니다. 이 기사에서는 Aggregator 구축에 중점을 두지 않지만 클라우드에 배포된 간단한 Lambda 함수로 목적을 해결할 수 있습니다.

다음으로 Spring Boot를 사용하여 Edge Service를 빌드하고 다른 지역의 요청을 처리하기 위해 다른 지역 그룹으로 Fauna 데이터베이스를 설정합니다.

5. Edge Service 생성 – Spring Boot 사용

건강 센서의 데이터를 소비하고 적절한 Fauna 지역으로 푸시할 Spring Boot를 사용하여 Edge Service를 생성해 보겠습니다.

Fauna에 대한 이전 사용방법(예제), Spring을 사용하여 FaunaDB 소개첫 번째 웹 에이전시 클라이언트에 대해 Fauna 및 Spring을 사용하여 웹 앱 빌드에서 Fauna와 연결되는 Spring Boot 애플리케이션을 만드는 방법을 살펴보았습니다. 자세한 내용은 이 문서를 참조하십시오.

5.1. 도메인

먼저 Edge 서비스의 도메인을 이해해 보겠습니다.

앞서 언급한 바와 같이 Edge Service는 건강 바이탈을 소비하고 처리하므로 개인의 기본 건강 바이탈이 포함된 레코드를 생성해 보겠습니다.

public record HealthData(
  
    String userId,
  
    float temperature, 
    float pulseRate,
    int bpSystolic,
    int bpDiastolic,
  
    double latitude, 
    double longitude, 
    ZonedDateTime timestamp) {
}

5.2. 건강 서비스

Out health service는 건강 데이터 처리, 요청에서 지역 식별 및 적절한 동물군 지역으로 라우팅을 담당합니다.

public interface HealthService {
    void process(HealthData healthData);
}

구현 빌드를 시작하겠습니다.

public class DefaultHealthService implements HealthService {

    @Override
    public void process(HealthData healthData) {
        // ...
    }
}

다음으로 요청 영역, 즉 요청이 트리거되는 위치를 식별하기 위한 코드를 추가해 보겠습니다.

Java에는 지리적 위치를 식별하기 위한 여러 라이브러리가 있습니다. 그러나 이 문서에서는 모든 요청에 ​​대해 "US" 지역을 반환하는 간단한 구현을 추가할 것입니다.

인터페이스를 추가해 봅시다:

public interface GeoLocationService {
    String getRegion(double latitude, double longitude);
}

그리고 모든 요청에 ​​대해 "미국" 지역을 반환하는 구현:

public class DefaultGeoLocationService implements GeoLocationService {

    @Override
    public String getRegion(double latitude, double longitude) {
        return "US";
    }
}

다음으로 HealthService 에서 이 GeoLocationService를 사용하겠습니다 . 주입하자:

@Autowired
private GeoLocationService geoLocationService;

그리고 이를 process 메서드에서 사용하여 영역을 추출합니다.

public void process(HealthData healthData) {

    String region = geoLocationService.getRegion(
        healthData.latitude(), 
        healthData.longitude());
    
    // ...
}

지역이 있으면 데이터를 저장하기 위해 적절한 동물군 지역 그룹을 쿼리해야 하지만, 지역 그룹을 사용하여 동물군 데이터베이스를 설정해야 합니다. 그 후에 통합을 재개할 것입니다.

6. 지역 그룹이 있는 동물군 - 설정

Fauna에서 새 데이터베이스를 만드는 것부터 시작하겠습니다. 아직 계정이 없다면 계정을 만들어야 합니다 .

6.1. 데이터베이스 생성

로그인했으면 새 데이터베이스를 생성해 보겠습니다.

동물군 생성 DB

여기에서는 유럽(EU) 리전에서 이 데이터베이스를 생성하고 있습니다. 미국의 요청을 처리하기 위해 미국 지역에 동일한 데이터베이스를 생성해 보겠습니다.

동물 군 db us

다음으로 외부(이 경우에는 생성한 에지 서비스)에서 데이터베이스에 액세스하려면 Security 키가 필요합니다. 두 데이터베이스에 대해 개별적으로 키를 생성합니다.

동물군 열쇠

마찬가지로 healthapp-us 데이터베이스 에 액세스하기 위한 키를 생성합니다 . Fauna에서 데이터베이스 및 Security 키 생성에 대한 자세한 지침은 Spring을 사용한 FaunaDB 소개 기사로 이동하십시오.

키가 생성되면 지역별 Fauna 연결 URL과 Security 키를 Spring Boot 서비스의 application.properties 에 저장합니다.

fauna-connections.EU=https://db.eu.fauna.com/
fauna-secrets.EU=eu-secret
fauna-connections.US=https://db.us.fauna.com/
fauna-secrets.US=us-secret

Fauna 데이터베이스와 연결하도록 Fauna 클라이언트를 구성하는 데 사용할 때 이러한 속성이 필요합니다.

6.2. HealthData 컬렉션 만들기

다음으로 Fauna에서 HealthData 컬렉션을 만들어 개인의 건강 상태를 저장해 보겠습니다.

데이터베이스 대시보드의 컬렉션 탭으로 이동하고 "새 컬렉션" 버튼을 클릭하여 컬렉션을 추가해 보겠습니다.

동물 컬렉션

다음으로 다음 화면에서 “New Document” 버튼을 클릭하여 하나의 샘플 문서를 삽입하고 JavaScript 콘솔에 아래 JSON을 추가해 보겠습니다.

{
  "userId": "baeldung-user",
  "temperature": "37.2",
  "pulseRate": "90",
  "bpSystolic": "120",
  "bpDiastolic": "80",
  "latitude": "40.758896",
  "longitude": "-73.985130",
  "timestamp": Now()
}

Now ()  함수는 타임스탬프 필드 에 현재 타임스탬프를 삽입합니다 .

저장을 누르면 위의 데이터가 삽입되고 healthdata 컬렉션 내의 컬렉션 탭에서 삽입된 모든 문서를 볼 수 있습니다.

동물군 수집 보기

이제 데이터베이스, 비밀 키 및 컬렉션이 생성되었으므로 Edge Service를 Fauna 데이터베이스와 통합하고 작업을 수행해 보겠습니다.

7. Fauna와 엣지 서비스 통합

Spring Boot 애플리케이션을 Fauna와 통합하려면 Java용 Fauna 드라이버를 프로젝트에 추가해야 합니다. 의존성을 추가해 보겠습니다.

<dependency>
    <groupId>com.faunadb</groupId>
    <artifactId>faunadb-java</artifactId>
    <version>4.2.0</version>
    <scope>compile</scope>
</dependency>

우리는 여기에서 항상 zoodb-java  의 최신 버전을 찾을 수 있습니다 .

7.1. 지역별 동물 클라이언트 만들기

이 드라이버는 주어진 연결 Endpoints과 암호로 쉽게 구성할 수 있는 FaunaClient를 제공합니다 .

FaunaClient client = FaunaClient.builder()
    .withEndpoint("connection-url")
    .withSecret("secret")
    .build();

애플리케이션에서 요청이 어디에서 오는지에 따라 Fauna의 EU 및 미국 지역과 연결해야 합니다. 두 지역에 대해 서로 다른 FaunaClient  인스턴스를 개별적으로 미리 구성하거나 런타임에 클라이언트를 동적으로 구성하여 이 문제를 해결할 수 있습니다. 두 번째 접근 방식을 살펴보겠습니다.

영역을 허용하고 올바르게 구성된 FaunaClient를 반환하는 새 클래스 인 FaunaClients를 만들어 보겠습니다 .

public class FaunaClients {

    public FaunaClient getFaunaClient(String region) {
        // ...
    }
}

우리는 이미 application.properties에 Fauna의 지역별 Endpoints과 비밀을 저장했습니다 .  Endpoints과 비밀을 맵으로 주입할 수 있습니다 .

@ConfigurationProperties
public class FaunaClients {

    private final Map<String, String> faunaConnections = new HashMap<>();
    private final Map<String, String> faunaSecrets = new HashMap<>();

    public Map<String, String> getFaunaConnections() {
        return faunaConnections;
    }

    public Map<String, String> getFaunaSecrets() {
        return faunaSecrets;
    }
}

여기서는 클래스에 구성 속성을 주입하는 @ConfigurationProperties를 사용했습니다. 이 어노테이션을 활성화하려면 다음도 추가해야 합니다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

마지막으로 각 맵에서 올바른 연결 Endpoints과 암호를 가져와 그에 따라 FaunaClient를 생성하는 데 사용해야 합니다 .

public FaunaClient getFaunaClient(String region) {

    String faunaUrl = faunaConnections.get(region);
    String faunaSecret = faunaSecrets.get(region);

    log.info("Creating Fauna Client for Region: {} with URL: {}", region, faunaUrl);
    
    return FaunaClient.builder()
        .withEndpoint(faunaUrl)
        .withSecret(faunaSecret)
        .build();
}

또한 클라이언트를 생성하는 동안 올바른 Fauna URL이 선택되었는지 확인하기 위해 로그를 추가했습니다.

7.2. Health Service에서 지역별 Fauna 클라이언트 사용

클라이언트가 준비되면 Health Service에서 클라이언트를 사용하여 Fauna에 건강 데이터를 전송합니다.

FaunaClients를 주입해봅시다 :

public class DefaultHealthService implements HealthService {
    
    @Autowired
    private FaunaClients faunaClients;
    
    // ...
}

다음으로 GeoLocationService 에서 이전에 추출한 지역을 전달하여 지역별 FaunaClient를 가져옵니다 .

public void process(HealthData healthData) {

    String region = geoLocationService.getRegion(
        healthData.latitude(), 
        healthData.longitude());
    
    FaunaClient faunaClient = faunaClients.getFaunaClient(region);
}

지역별 FaunaClient 가 있으면 이를 사용하여 건강 데이터를 특정 데이터베이스에 삽입해 보겠습니다.

Fauna와 통합하기 위해 FQL(Fauna Query Language)로 여러 CRUD 쿼리를 작성한 기존 Faunadb Spring Web App 기사 를 기반으로 합니다 .

Fauna에서 건강 데이터를 생성하는 쿼리를 추가해 보겠습니다. Create 키워드로 시작 하고 데이터를 삽입할 컬렉션 이름을 언급합니다.

Value queryResponse = faunaClient.query(
    Create(Collection("healthdata"), //)
    ).get();

다음으로 삽입할 실제 데이터 개체를 만듭니다. 개체의 특성과 해당 값을 Map 의 항목으로 정의하고 FQL의 Value 키워드를 사용하여 래핑합니다.

Create(Collection("healthdata"), 
    Obj("data", 
        Obj(Map.of(
            "userId", Value(healthData.userId())))))
)

여기에서는 건강 데이터 레코드에서 userId를 읽고 삽입하는 문서의 userId 필드에 매핑합니다.

마찬가지로 나머지 모든 속성에 대해 다음을 수행할 수 있습니다.

Create(Collection("healthdata"), 
    Obj("data", 
        Obj(Map.of(
            "userId", Value(healthData.userId()), 
            "temperature", Value(healthData.temperature()),
            "pulseRate", Value(healthData.pulseRate()),
            "bpSystolic", Value(healthData.bpSystolic()),
            "bpDiastolic", Value(healthData.bpDiastolic()),
            "latitude", Value(healthData.latitude()),
            "longitude", Value(healthData.longitude()),
            "timestamp", Now()))))

마지막으로 쿼리 실행 중 문제를 인식할 수 있도록 쿼리 응답을 기록해 보겠습니다.

log.info("Query response received from Fauna: {}", queryResponse);

참고 : 이 기사의 목적을 위해 Edge 서비스를 Spring Boot 애플리케이션으로 구축했습니다. 프로덕션 환경에서 이러한 서비스는 모든 언어로 구축할 수 있으며 Fastly , Cloudflare Workers , Lambda@Edge 등과 같은 에지 제공업체를 통해 글로벌 네트워크에 배포할 수 있습니다.

통합이 완료되었습니다. 통합 테스트로 전체 흐름을 테스트해 보겠습니다.

8. 종단 간 통합 테스트

통합이 종단 간 작동하고 요청이 올바른 Fauna 지역으로 이동하는지 확인하는 테스트를 추가해 보겠습니다. 우리는 테스트를 위해 지역을 토글하기 위해 GeoLocationService 를 조롱할 것입니다.

@SpringBootTest
class DefaultHealthServiceTest {

    @Autowired
    private DefaultHealthService defaultHealthService;

    @MockBean
    private GeoLocationService geoLocationService;
    
    // ...
} 

EU 지역에 대한 테스트를 추가해 보겠습니다.

@Test
void givenEURegion_whenProcess_thenRequestSentToEURegion() {

    HealthData healthData = new HealthData("user-1-eu",
        37.5f,
        99f,
        120, 80,
        51.50, -0.07,
        ZonedDateTime.now());

    // ...
}

다음으로 영역을 모의 처리하고 프로세스 메서드를 호출해 보겠습니다.

when(geoLocationService.getRegion(51.50, -0.07)).thenReturn("EU");

defaultHealthService.process(healthData);

테스트를 실행할 때 로그에서 FaunaClient를 생성하기 위해 올바른 URL을 가져왔는지 확인할 수 있습니다 .

Creating Fauna Client for Region:EU with URL:https://db.eu.fauna.com/

또한 레코드가 올바르게 생성되었음을 확인하는 Fauna 서버에서 반환된 응답을 확인할 수도 있습니다.

Query response received from Fauna: 
{
ref: ref(id = "338686945465991375", 
collection = ref(id = "healthdata", collection = ref(id = "collections"))), 
ts: 1659255891215000, 
data: {bpDiastolic: 80, 
userId: "user-1-eu", 
temperature: 37.5, 
longitude: -0.07, latitude: 51.5, 
bpSystolic: 120, 
pulseRate: 99.0, 
timestamp: 2022-07-31T08:24:51.164033Z}}

또한 EU 데이터베이스에 대한 HealthData 컬렉션 아래의 Fauna 대시보드에서 동일한 레코드를 확인할 수 있습니다 .

동물군 대시보드 컬렉션

마찬가지로 미국 지역에 대한 테스트를 추가할 수 있습니다.

@Test
void givenUSRegion_whenProcess_thenRequestSentToUSRegion() {

    HealthData healthData = new HealthData("user-1-us", //
        38.0f, //
        100f, //
        115, 85, //
        40.75, -74.30, //
        ZonedDateTime.now());

    when(geoLocationService.getRegion(40.75, -74.30)).thenReturn("US");

    defaultHealthService.process(healthData);
}

9. 결론

이 기사에서는 Fauna의 분산 , 문서 관계형 및 서버리스 기능을 활용하여 IoT 애플리케이션용 데이터베이스로 사용할 수 있는 방법을 살펴보았습니다. Fauna의 지역 그룹 인프라는 지역 문제를 해결하고 에지 서버의 대기 시간 문제를 완화합니다.

Fauna가 에지 앱에서 대기 시간을 줄이는 방법에 대한 Fauna의 주문형 웨비나를 확인할 수도 있습니다 .

여기에 표시된 모든 코드는 Github에서 사용할 수 있습니다 .