본문 바로가기
Projects/센서 모니터링 시스템

[센서 모니터링 시스템] 8. Service, REST Controller 개발

by DevJaewoo 2022. 8. 18.
반응형

Intro

이전에 Repository를 만들었으니, 이제 Service와 Controller를 만들면 된다.


DTO 생성

Service에선 RequestDTO를 받아 Repository를 제어하고, Entity를 다시 DTO로 변환해서 반환해주는 역할을 한다.

때문에 Service 및 Controller에서 반환해줄 DTO 클래스를 새로 만들어야 한다.

 

SensorDto.java

Sensor 엔티티에서 클라이언트에게 넘겨주지 않아도 되는 ID와 Client를 제외하였다.

package com.example.sensormonitoringserver.dto;

import com.example.sensormonitoringserver.entity.Sensor;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.time.LocalDateTime;
import java.util.List;

@Data
@AllArgsConstructor
public class SensorDto {
    private int eco2;
    private int tvoc;
    private double temp;
    private CoordDto accel;
    private LocalDateTime createdDate;

    public SensorDto(Sensor sensor) {
        this.eco2 = sensor.getEco2();
        this.tvoc = sensor.getTvoc();
        this.temp = sensor.getTemp();
        this.accel = new CoordDto(sensor.getAccel());
        this.createdDate = sensor.getCreatedDate();
    }

    @Data
    @AllArgsConstructor
    public static class ResponseList {
        private List<SensorDto> sensorData;
    }
}

 

CoordDTO

Coord 클래스와 차이는 없지만, Entity를 직접 반환하면 수정 시 사이드 이펙트가 생길 확률이 높아 따로 DTO를 만들어줬다.

package com.example.sensormonitoringserver.dto;

import com.example.sensormonitoringserver.entity.Coord;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CoordDto {
    private double x;
    private double y;
    private double z;

    public CoordDto(Coord coord) {
        this.x = coord.getX();
        this.y = coord.getY();
        this.z = coord.getZ();
    }
}

 

SensorCreateRequestDto.java

Device에서 서버로 센서 데이터를 넘길 때 사용할 DTO다.

package com.example.sensormonitoringserver.dto;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class SensorCreateRequestDto {

    private double temp;
    private int eco2;
    private int tvoc;
    private CoordDto accel;
}

Service 개발

작성한 DTO를 통해 비즈니스 로직을 수행하는 Service를 만들었다.

센서 데이터를 읽어와 저장하는 uploadSensorData와 SensorSearch 조건에 따라 SensorDto를 반환해주는 searchSensorData 함수가 있다.

 

SensorService.java

package com.example.sensormonitoringserver.service;

import com.example.sensormonitoringserver.dto.SensorCreateRequestDto;
import com.example.sensormonitoringserver.dto.SensorDto;
import com.example.sensormonitoringserver.dto.SensorSearch;
import com.example.sensormonitoringserver.entity.Client;
import com.example.sensormonitoringserver.entity.Coord;
import com.example.sensormonitoringserver.entity.Sensor;
import com.example.sensormonitoringserver.repository.ClientRepository;
import com.example.sensormonitoringserver.repository.SensorRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class SensorService {

    private final ClientRepository clientRepository;
    private final SensorRepository sensorRepository;

    @Transactional
    public void uploadSensorData(Long clientId, SensorCreateRequestDto createRequestDto) {

        Client client = clientRepository.findById(clientId).orElseThrow(() -> new NoSuchElementException("ID가 " + clientId + "인 사용자가 존재하지 않습니다."));

        Sensor sensor = new Sensor(
                client,
                createRequestDto.getTemp(),
                createRequestDto.getEco2(),
                createRequestDto.getTvoc(),
                new Coord(
                        createRequestDto.getAccel().getX(),
                        createRequestDto.getAccel().getY(),
                        createRequestDto.getAccel().getZ()
                )
        );

        sensorRepository.save(sensor);
    }

    public List<SensorDto> searchSensorData(Long clientId, SensorSearch sensorSearchDto) {
        return sensorRepository.search(clientId, sensorSearchDto).stream()
                .map(SensorDto::new)
                .collect(Collectors.toList());
    }
}

Controller 개발

Request를 받아 Service에 넘기고 결과만 반환하면 되기 때문에 특별할건 없다.

package com.example.sensormonitoringserver.controller;

import com.example.sensormonitoringserver.dto.SensorCreateRequestDto;
import com.example.sensormonitoringserver.dto.SensorDto;
import com.example.sensormonitoringserver.dto.SensorSearch;
import com.example.sensormonitoringserver.service.SensorService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;

@RequestMapping("/api")
@RestController
@RequiredArgsConstructor
public class SensorController {

    private final SensorService sensorService;

    @PostMapping("/{id}/sensor")
    public ResponseEntity<?> createSensorData(@PathVariable Long id, @Valid @RequestBody SensorCreateRequestDto sensorCreateRequestDTO) {
        sensorService.uploadSensorData(id, sensorCreateRequestDTO);
        return ResponseEntity.ok("");
    }

    @GetMapping("/{id}/sensor")
    public ResponseEntity<?> readSensorData(@PathVariable Long id, SensorSearch sensorSearch) {
        List<SensorDto> result = sensorService.searchSensorData(id, sensorSearch);
        return ResponseEntity.ok(new SensorDto.ResponseList(result));
    }
}

동작 테스트

Postman으로 /api/1/sensor URL로 POST, GET 요청을 보내 서버가 잘 돌아가는지 테스트해보자.

Postman은 아래의 링크에서 다운받을 수 있다.

 

https://www.postman.com/downloads/

 

Download Postman | Get Started for Free

Try Postman for free! Join 20 million developers who rely on Postman, the collaboration platform for API development. Create better APIs—faster.

www.postman.com

 

센서 데이터 업로드 동작 확인

/api/1/sensor로 센서 데이터와 함께 POST 요청 시 아래와 같이 빈 response가 온다.

아무런 응답도 하지 않도록 했기 때문에 200 OK가 뜨면 잘 동작하는 것이다.

 

센서 데이터 조회 동작 확인

센서 데이터를 조금 더 업로드하고 Param에 DateTime 정보를 넣어 조회해봤다.

아래와 같이 잘 조회되는 것을 볼 수 있다.

사용자에게 보여주지 않으려 했던 ID, Client 정보가 설정한대로 보여지지 않는것을 확인할 수 있다.

 

사진에는 화면 한계상 데이터가 잘렸는데, 전문은 아래와 같다.

{
    "sensorData": [
        {
            "eco2": 1234,
            "tvoc": 1234,
            "temp": 12.34,
            "accel": {
                "x": 1.0,
                "y": 2.0,
                "z": 3.0
            },
            "createdDate": "2022-08-18T00:11:08.761628"
        },
        {
            "eco2": 12345,
            "tvoc": 12345,
            "temp": 12.345,
            "accel": {
                "x": 12.0,
                "y": 23.0,
                "z": 34.0
            },
            "createdDate": "2022-08-18T00:11:22.09544"
        },
        {
            "eco2": 12,
            "tvoc": 12345,
            "temp": 12.345,
            "accel": {
                "x": 12.0,
                "y": 23.0,
                "z": 345.0
            },
            "createdDate": "2022-08-18T00:11:30.426674"
        },
        {
            "eco2": 12,
            "tvoc": 15,
            "temp": 12.345,
            "accel": {
                "x": 12.0,
                "y": 23.0,
                "z": 345.0
            },
            "createdDate": "2022-08-18T00:11:33.25846"
        },
        {
            "eco2": 12,
            "tvoc": 15,
            "temp": 1.5,
            "accel": {
                "x": 12.0,
                "y": 23.0,
                "z": 35.0
            },
            "createdDate": "2022-08-18T00:11:38.819844"
        }
    ]
}

 

서버도 기본 기능은 됐으니 다음엔 라즈베리파이에서 측정한 실제 센서 값을 서버에 보내보도록 하겠다.

반응형