Arduino와 SpringBoot를 사용한 온습도 측정 사이트(6) - SpringBoot편

2024. 6. 17. 17:17Spring-Project

 

 

이전글 : https://codepracticeroom.tistory.com/202

 

 

이번에는 받은 데이터를 좀 더 가공하고 웹에 띄워볼 것이다.

 

 

 

먼저 domain에 WeatherData에 순서, 날짜, 시간 데이터를 추가해 준다.

순서는 PK로 사용한다.

 

이전글에서 온도와 시간을 String로 하였는데 여기서는 double 형태로 변환한다.

 

WeatherData

@Getter
@Setter
public class WeatherData {
    
    @Id
    private long sequence;      //Pk
    
    private LocalDate date;     //날짜
    private LocalTime time;     //시간 
    
    private double temperature; //온도
    private double humidity;    //시간
}

 

 

Service에 최근 DB데이터 중 하나를 가져오는 코드를 작성한다.

 

 

WeatherService

@Service
@RequiredArgsConstructor
public class WeatherService {

    private final WeatherRepository weatherRepository;
    private final MongoTemplate template;               //MongoDBTemplate 의존성 주입

    @Transactional
    public void save(WeatherData weatherData){
        weatherRepository.save(weatherData);
    }
    
     public long count(){                        // DB 데이터의 개수
        return weatherRepository.count();
    }
    
    public WeatherData findLatestData() {
        Query query = new Query();
        query.with(Sort.by(Sort.Direction.DESC, "date","time")).limit(1);       // 날짜, 시간을 내림차순으로 정렬, 하나 추출
        return template.findOne(query, WeatherData.class);
    }

}

 

 

 

이제 Controller의 GetMapping를 만들어 줘야 한다.

 

DataController

@Slf4j
@Controller
@RequiredArgsConstructor
public class DataController {

    private final WeatherService weatherService;
    private static long count;

    @GetMapping("/")
    public String getHome(Model model){
        model.addAttribute("weatherData", weatherService.findLatestData());
        return "home";
    }

    @ResponseBody
    @PostMapping("/home")
    public String postData(@RequestBody WeatherData weatherData){

        if(count != 0){
            weatherData.setSequence(++count);
        } else {
            count = weatherService.count()+1;
            weatherData.setSequence(weatherService.count()+1);
        }

        weatherData.setDate(LocalDate.now());           // 현재 날짜
        weatherData.setTime(LocalTime.now());           // 현재 시간
        weatherData.setHumidity(Math.round(weatherData.getHumidity()*100)/100.0);           //소수점 3번째에서 반올림
        weatherData.setTemperature(Math.round(weatherData.getTemperature()*100)/100.0);     //소수점 3번째에서 반올림

        weatherService.save(weatherData);
        log.info("temp={} hum={}", weatherData.getTemperature(), weatherData.getHumidity());
        return "";
    }
}

 

 

Service에서 만들었던 findLastestData를 가져와서 Model에 넣고 return을 templates의 "home"으로 한다.

 

 

 

html에서 CSS를 사용하기 때문에 먼저 CSS를 다운로드하여야 한다.

 

https://getbootstrap.kr/

 

Bootstrap

강력하고 확장 가능하며 기능이 풍부한 프론트엔드 툴킷. Sass로 빌드 및 커스터마이징하고, 사전 빌드된 그리드 시스템 및 구성 요소를 활용하고, 강력한 JavaScript 플러그인으로 프로젝트에 생기

getbootstrap.kr

 

 

다운로드 - 컴파일된 CSS와 JS를 다운로드한다.

다운로드한 파일을 압축 해제한다.

필요한 파일은 bootstrap.min.css이다.

 

스프링의 resources - static에 이름은 "css"로 디렉터리를 하나 만든다.

만든 곳에 bootstrap.min.css를 넣는다.

 

 

 

 

 

그다음 resources - templates에 home.html를 만들고 작성한다.

 

 

Home.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link href="../css/bootstrap.min.css"
          th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
    <style>
        body {
            padding-top: 20px;
        }

        .container {
            background-color: #f8f9fa;
            border-radius: 5px;
            padding: 20px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, .1);
        }

        #txt {
            margin-bottom: 20px;
            font-size: 20px;
            font-weight: bold;
        }

        .form-inline > div {
            margin-bottom: 10px;
            font-size: 16px;
        }

        .btn-primary {
            text-decoration: none;
            color: white;
            background-color: #007bff;
            padding: 10px 15px;
            border-radius: 5px;
            transition: background-color 0.2s;
        }

        .btn-primary:hover {
            background-color: #0056b3;
        }

        .form-container {
            background-color: #ffffff; 
            border: 1px solid #ddd; 
            border-radius: 8px; 
            padding: 20px;
            margin-top: 20px;
        }

        .form-container h3 {
            color: #007bff;
            margin-bottom: 20px; 
            text-align: center; 
        }

        .form-row {
            display: flex; 
            margin-bottom: 10px; 
        }

        .form-label {
            flex-basis: 20%; 
            font-weight: bold; 
        }

        .form-value {
            flex-grow: 1; 
        }

    </style>
    <script>
        function startTime() {
            var today = new Date();
            var h = today.getHours();
            var m = today.getMinutes();
            var s = today.getSeconds();
            var ampm = h >= 12 ? '오후' : '오전';
            h = h % 12;
            h = h ? h : 12;
            m = checkTime(m);
            s = checkTime(s);
            document.getElementById('txt').innerHTML =
                "현재시간 : " + ampm + " " + h + ":" + m + ":" + s + " ";
            var t = setTimeout(startTime, 500);
        }

        function checkTime(i) {
            if (i < 10) {
                i = "0" + i;
            }
            return i;
        }
    </script>
</head>
<body onload="startTime()">

<div id="txt"></div>
<div class="container">
    <div class="py-5 text-center">
        <h2>측정</h2>
    </div>
    <div class="row">
        <div class="col">
            <button class="btn btn-primary float-end" th:onclick="|location.href='@{/list}'|" type="button">모두보기</button>
        </div>
    </div>

    <div class="container">
        <div class="form-container">
            <form th:object="${weatherData}" method="get">
                <div class="form-row">
                    <span class="form-label">날짜 :</span>
                    <span class="form-value" th:text="${weatherData.date}"></span>
                </div>
                <div class="form-row">
                    <span class="form-label">시간 :</span>
                    <span class="form-value" th:text="${#temporals.format(weatherData.time, 'HH:mm:ss')}"></span>
                </div>
                <div class="form-row">
                    <span class="form-label">온도 :</span>

                    <span class="form-value" th:text="${weatherData.temperature} + ' ℃'"></span>
                </div>
                <div class="form-row">
                    <span class="form-label">습도 :</span>
                    <span class="form-value" th:text="${weatherData.humidity} + ' %'"></span>
                </div>
            </form>
        </div>
    </div>
</div>

</body>
</html>

 

 

 

 

스프링 실행 후 localhost에 접속하면 화이트라벨 에러가 뜰 수도 있다.

예전에 MongoDB에 저장하였던 데이터 타입이 맞지 않아서 뜨는 오류이다.

MongoDB의 데이터를 삭제한 후 다시 아두이노를 연결하여 데이터를 쌓고
localhost에 접속하며 정상적으로 웹 페이지가 뜬다.

 

 

 

localhost