Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Administrator
sensor_mvp
Commits
003942b3
Commit
003942b3
authored
Sep 03, 2025
by
Sensor MVP Team
Browse files
document add
parent
b33d4b9a
Changes
6
Expand all
Hide whitespace changes
Inline
Side-by-side
Java_Spring_센서_시스템_개발_가이드.md
0 → 100644
View file @
003942b3
This diff is collapsed.
Click to expand it.
Java_Spring_센서_시스템_개발_가이드_1.md
0 → 100644
View file @
003942b3
# Java Spring(전자정부프레임워크) 센서 시스템 개발 가이드 - Part 1
## 📋 프로젝트 개요
**프로젝트명**
: Java Spring 기반 센서 데이터 수집 및 관리 시스템
**기술 스택**
: Java 11, Spring Boot 2.7.x, 전자정부프레임워크 4.0.x
**아키텍처**
: 계층형 아키텍처 + REST API
**데이터베이스**
: PostgreSQL, Redis
**상태**
: 개발 가이드 작성 완료
---
## 🏗️ 시스템 아키텍처
### 전체 시스템 구성도
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 센서 디바이스 │────│ Java 브리지 │────│ Spring Boot │
│ (RSNet SDK) │ │ (RSNet SDK) │ │ (REST API) │
│ │ │ (포트: 8020) │ │ (포트: 8080) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 웹 대시보드 │◄───│ PostgreSQL │◄───│ Redis 캐시 │
│ (Thymeleaf) │ │ (데이터 저장) │ │ (실시간 캐시) │
│ (포트: 8080) │ │ (포트: 5432) │ │ (포트: 6379) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
### 서비스 구성
| 서비스 | 기술 스택 | 포트 | 역할 | 상태 |
|--------|-----------|------|------|------|
|
**sensor-spring-app**
| Spring Boot + 전자정부프레임워크 | 8080 | 메인 웹 애플리케이션 | 🔄 개발 중 |
|
**sensor-bridge**
| Java + RSNet SDK | 8020 | 센서 데이터 브리지 | ✅ 기존 유지 |
|
**postgres**
| PostgreSQL 15 | 5432 | 메인 데이터베이스 | ✅ 기존 유지 |
|
**redis**
| Redis 7 | 6379 | 실시간 캐시 | ✅ 기존 유지 |
---
## 📁 프로젝트 구조
### 새로운 Spring 애플리케이션 구조
```
sensor-spring-app/
├── 📁 src/
│ ├── 📁 main/
│ │ ├── 📁 java/
│ │ │ └── 📁 com/sensor/spring/
│ │ │ ├── 📁 config/ # 설정 클래스
│ │ │ ├── 📁 controller/ # REST 컨트롤러
│ │ │ ├── 📁 service/ # 비즈니스 로직
│ │ │ ├── 📁 repository/ # 데이터 접근 계층
│ │ │ ├── 📁 entity/ # JPA 엔티티
│ │ │ ├── 📁 dto/ # 데이터 전송 객체
│ │ │ ├── 📁 exception/ # 예외 처리
│ │ │ ├── 📁 util/ # 유틸리티
│ │ │ └── 📄 SensorSpringApplication.java
│ │ ├── 📁 resources/
│ │ │ ├── 📁 static/ # 정적 리소스
│ │ │ ├── 📁 templates/ # Thymeleaf 템플릿
│ │ │ ├── 📄 application.yml # Spring 설정
│ │ │ ├── 📄 application-dev.yml # 개발 환경 설정
│ │ │ └── 📄 application-prod.yml # 운영 환경 설정
│ │ └── 📁 webapp/ # 전자정부프레임워크 설정
│ └── 📁 test/ # 테스트 코드
├── 📄 pom.xml # Maven 프로젝트 설정
├── 📄 Dockerfile # Docker 이미지 빌드
└── 📄 README.md # 프로젝트 문서
```
---
## 🚀 개발 환경 설정
### 1. 필수 요구사항
-
**Java**
: JDK 11 이상
-
**Maven**
: 3.6.x 이상
-
**IDE**
: IntelliJ IDEA, Eclipse, VS Code
-
**데이터베이스**
: PostgreSQL 15, Redis 7
-
**기존 시스템**
: sensor-bridge, postgres, redis
### 2. Maven 프로젝트 생성
#### **pom.xml 설정**
```
xml
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns=
"http://maven.apache.org/POM/4.0.0"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>
4.0.0
</modelVersion>
<parent>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-parent
</artifactId>
<version>
2.7.18
</version>
<relativePath/>
</parent>
<groupId>
com.sensor
</groupId>
<artifactId>
sensor-spring-app
</artifactId>
<version>
1.0.0
</version>
<packaging>
jar
</packaging>
<name>
Sensor Spring Application
</name>
<description>
Spring Boot based sensor data management system
</description>
<properties>
<java.version>
11
</java.version>
<egovframework.rte.version>
4.0.0
</egovframework.rte.version>
<spring-boot.version>
2.7.18
</spring-boot.version>
</properties>
<dependencies>
<!-- Spring Boot Starters -->
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-web
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-data-jpa
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-data-redis
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-thymeleaf
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-validation
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-actuator
</artifactId>
</dependency>
<!-- 전자정부프레임워크 -->
<dependency>
<groupId>
org.egovframe.rte
</groupId>
<artifactId>
org.egovframe.rte.ptl.mvc
</artifactId>
<version>
${egovframework.rte.version}
</version>
</dependency>
<dependency>
<groupId>
org.egovframe.rte
</groupId>
<artifactId>
org.egovframe.rte.psl.dataaccess
</artifactId>
<version>
${egovframework.rte.version}
</version>
</dependency>
<dependency>
<groupId>
org.egovframe.rte
</groupId>
<artifactId>
org.egovframe.rte.fdl.idgnr
</artifactId>
<version>
${egovframework.rte.version}
</version>
</dependency>
<!-- Database -->
<dependency>
<groupId>
org.postgresql
</groupId>
<artifactId>
postgresql
</artifactId>
<scope>
runtime
</scope>
</dependency>
<dependency>
<groupId>
org.flywaydb
</groupId>
<artifactId>
flyway-core
</artifactId>
</dependency>
<!-- JSON Processing -->
<dependency>
<groupId>
com.fasterxml.jackson.core
</groupId>
<artifactId>
jackson-databind
</artifactId>
</dependency>
<dependency>
<groupId>
com.fasterxml.jackson.datatype
</groupId>
<artifactId>
jackson-datatype-jsr310
</artifactId>
</dependency>
<!-- Logging -->
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-logging
</artifactId>
</dependency>
<!-- Testing -->
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-test
</artifactId>
<scope>
test
</scope>
</dependency>
<dependency>
<groupId>
org.testcontainers
</groupId>
<artifactId>
postgresql
</artifactId>
<scope>
test
</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
</plugin>
<plugin>
<groupId>
org.flywaydb
</groupId>
<artifactId>
flyway-maven-plugin
</artifactId>
</plugin>
</plugins>
</build>
</project>
```
### 3. Spring Boot 메인 클래스
#### **SensorSpringApplication.java**
```
java
package
com.sensor.spring
;
import
org.springframework.boot.SpringApplication
;
import
org.springframework.boot.autoconfigure.SpringBootApplication
;
import
org.springframework.cache.annotation.EnableCaching
;
import
org.springframework.data.jpa.repository.config.EnableJpaAuditing
;
import
org.springframework.scheduling.annotation.EnableAsync
;
import
org.springframework.scheduling.annotation.EnableScheduling
;
@SpringBootApplication
@EnableJpaAuditing
@EnableCaching
@EnableAsync
@EnableScheduling
public
class
SensorSpringApplication
{
public
static
void
main
(
String
[]
args
)
{
SpringApplication
.
run
(
SensorSpringApplication
.
class
,
args
);
}
}
```
---
## ⚙️ 설정 파일
### 1. application.yml (메인 설정)
```
yaml
spring
:
profiles
:
active
:
dev
application
:
name
:
sensor-spring-app
datasource
:
url
:
jdbc:postgresql://localhost:5432/sensor_db
username
:
postgres
password
:
password
driver-class-name
:
org.postgresql.Driver
jpa
:
hibernate
:
ddl-auto
:
validate
show-sql
:
false
properties
:
hibernate
:
dialect
:
org.hibernate.dialect.PostgreSQLDialect
format_sql
:
true
redis
:
host
:
localhost
port
:
6379
timeout
:
2000ms
lettuce
:
pool
:
max-active
:
8
max-idle
:
8
min-idle
:
0
thymeleaf
:
cache
:
false
prefix
:
classpath:/templates/
suffix
:
.html
encoding
:
UTF-8
mode
:
HTML
server
:
port
:
8080
servlet
:
context-path
:
/
management
:
endpoints
:
web
:
exposure
:
include
:
health,info,metrics,prometheus
endpoint
:
health
:
show-details
:
always
logging
:
level
:
com.sensor.spring
:
DEBUG
org.springframework.web
:
DEBUG
org.hibernate.SQL
:
DEBUG
org.hibernate.type.descriptor.sql.BasicBinder
:
TRACE
```
### 2. application-dev.yml (개발 환경)
```
yaml
spring
:
jpa
:
show-sql
:
true
hibernate
:
ddl-auto
:
create-drop
h2
:
console
:
enabled
:
true
logging
:
level
:
com.sensor.spring
:
DEBUG
org.springframework.web
:
DEBUG
```
### 3. application-prod.yml (운영 환경)
```
yaml
spring
:
jpa
:
show-sql
:
false
hibernate
:
ddl-auto
:
validate
logging
:
level
:
com.sensor.spring
:
INFO
org.springframework.web
:
WARN
```
---
## 🗄️ 데이터 모델
### 1. JPA 엔티티
#### **SensorReading.java**
```
java
package
com.sensor.spring.entity
;
import
lombok.*
;
import
org.springframework.data.annotation.CreatedDate
;
import
org.springframework.data.jpa.domain.support.AuditingEntityListener
;
import
javax.persistence.*
;
import
javax.validation.constraints.NotNull
;
import
java.time.LocalDateTime
;
@Entity
@Table
(
name
=
"sensor_readings"
)
@EntityListeners
(
AuditingEntityListener
.
class
)
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public
class
SensorReading
{
@Id
@GeneratedValue
(
strategy
=
GenerationType
.
IDENTITY
)
private
Long
id
;
@NotNull
@Column
(
name
=
"device_id"
,
nullable
=
false
)
private
String
deviceId
;
@Column
(
name
=
"node_id"
)
private
Integer
nodeId
;
@Column
(
name
=
"temperature"
,
precision
=
5
,
scale
=
2
)
private
Double
temperature
;
@Column
(
name
=
"humidity"
,
precision
=
5
,
scale
=
2
)
private
Double
humidity
;
@Column
(
name
=
"longitude"
,
precision
=
10
,
scale
=
6
)
private
Double
longitude
;
@Column
(
name
=
"latitude"
,
precision
=
10
,
scale
=
6
)
private
Double
latitude
;
@Column
(
name
=
"recorded_time"
)
private
LocalDateTime
recordedTime
;
@Column
(
name
=
"received_time"
)
private
LocalDateTime
receivedTime
;
@CreatedDate
@Column
(
name
=
"created_at"
,
nullable
=
false
,
updatable
=
false
)
private
LocalDateTime
createdAt
;
@PrePersist
protected
void
onCreate
()
{
if
(
receivedTime
==
null
)
{
receivedTime
=
LocalDateTime
.
now
();
}
}
}
```
#### **Device.java**
```
java
package
com.sensor.spring.entity
;
import
lombok.*
;
import
org.springframework.data.annotation.CreatedDate
;
import
org.springframework.data.annotation.LastModifiedDate
;
import
org.springframework.data.jpa.domain.support.AuditingEntityListener
;
import
javax.persistence.*
;
import
javax.validation.constraints.NotNull
;
import
java.time.LocalDateTime
;
@Entity
@Table
(
name
=
"devices"
)
@EntityListeners
(
AuditingEntityListener
.
class
)
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public
class
Device
{
@Id
@GeneratedValue
(
strategy
=
GenerationType
.
IDENTITY
)
private
Long
id
;
@NotNull
@Column
(
name
=
"device_id"
,
unique
=
true
,
nullable
=
false
)
private
String
deviceId
;
@Column
(
name
=
"name"
)
private
String
name
;
@Column
(
name
=
"description"
)
private
String
description
;
@Enumerated
(
EnumType
.
STRING
)
@Column
(
name
=
"status"
)
private
DeviceStatus
status
;
@Column
(
name
=
"last_seen"
)
private
LocalDateTime
lastSeen
;
@CreatedDate
@Column
(
name
=
"created_at"
,
nullable
=
false
,
updatable
=
false
)
private
LocalDateTime
createdAt
;
@LastModifiedDate
@Column
(
name
=
"updated_at"
,
nullable
=
false
)
private
LocalDateTime
updatedAt
;
public
enum
DeviceStatus
{
ACTIVE
,
INACTIVE
,
ERROR
,
MAINTENANCE
}
}
```
---
**계속...**
Java_Spring_센서_시스템_개발_가이드_2.md
0 → 100644
View file @
003942b3
# Java Spring(전자정부프레임워크) 센서 시스템 개발 가이드 - Part 2
## 🔧 계층별 구현
### 1. DTO 클래스
#### **SensorDataRequest.java**
```
java
package
com.sensor.spring.dto
;
import
lombok.*
;
import
javax.validation.constraints.NotNull
;
import
java.time.LocalDateTime
;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public
class
SensorDataRequest
{
@NotNull
private
String
deviceId
;
private
Integer
nodeId
;
private
Double
temperature
;
private
Double
humidity
;
private
Double
longitude
;
private
Double
latitude
;
private
LocalDateTime
recordedTime
;
}
```
#### **SensorDataResponse.java**
```
java
package
com.sensor.spring.dto
;
import
lombok.*
;
import
java.time.LocalDateTime
;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public
class
SensorDataResponse
{
private
Long
id
;
private
String
deviceId
;
private
Integer
nodeId
;
private
Double
temperature
;
private
Double
humidity
;
private
Double
longitude
;
private
Double
latitude
;
private
LocalDateTime
recordedTime
;
private
LocalDateTime
receivedTime
;
private
LocalDateTime
createdAt
;
}
```
### 2. Repository 계층
#### **SensorReadingRepository.java**
```
java
package
com.sensor.spring.repository
;
import
com.sensor.spring.entity.SensorReading
;
import
org.springframework.data.jpa.repository.JpaRepository
;
import
org.springframework.data.jpa.repository.Query
;
import
org.springframework.data.repository.query.Param
;
import
org.springframework.stereotype.Repository
;
import
java.time.LocalDateTime
;
import
java.util.List
;
import
java.util.Optional
;
@Repository
public
interface
SensorReadingRepository
extends
JpaRepository
<
SensorReading
,
Long
>
{
List
<
SensorReading
>
findByDeviceIdOrderByRecordedTimeDesc
(
String
deviceId
);
Optional
<
SensorReading
>
findFirstByDeviceIdOrderByRecordedTimeDesc
(
String
deviceId
);
@Query
(
"SELECT sr FROM SensorReading sr WHERE sr.deviceId = :deviceId "
+
"AND sr.recordedTime BETWEEN :startTime AND :endTime "
+
"ORDER BY sr.recordedTime DESC"
)
List
<
SensorReading
>
findByDeviceIdAndTimeRange
(
@Param
(
"deviceId"
)
String
deviceId
,
@Param
(
"startTime"
)
LocalDateTime
startTime
,
@Param
(
"endTime"
)
LocalDateTime
endTime
);
@Query
(
"SELECT COUNT(sr) FROM SensorReading sr WHERE sr.deviceId = :deviceId "
+
"AND sr.recordedTime >= :since"
)
Long
countByDeviceIdSince
(
@Param
(
"deviceId"
)
String
deviceId
,
@Param
(
"since"
)
LocalDateTime
since
);
}
```
#### **DeviceRepository.java**
```
java
package
com.sensor.spring.repository
;
import
com.sensor.spring.entity.Device
;
import
org.springframework.data.jpa.repository.JpaRepository
;
import
org.springframework.stereotype.Repository
;
import
java.util.List
;
import
java.util.Optional
;
@Repository
public
interface
DeviceRepository
extends
JpaRepository
<
Device
,
Long
>
{
Optional
<
Device
>
findByDeviceId
(
String
deviceId
);
List
<
Device
>
findByStatus
(
Device
.
DeviceStatus
status
);
List
<
Device
>
findByLastSeenBefore
(
java
.
time
.
LocalDateTime
time
);
}
```
### 3. Service 계층
#### **SensorDataService.java**
```
java
package
com.sensor.spring.service
;
import
com.sensor.spring.dto.SensorDataRequest
;
import
com.sensor.spring.dto.SensorDataResponse
;
import
com.sensor.spring.entity.SensorReading
;
import
com.sensor.spring.repository.SensorReadingRepository
;
import
com.sensor.spring.repository.DeviceRepository
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.cache.annotation.CacheEvict
;
import
org.springframework.cache.annotation.Cacheable
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
import
java.time.LocalDateTime
;
import
java.util.List
;
import
java.util.stream.Collectors
;
@Service
@RequiredArgsConstructor
@Slf4j
public
class
SensorDataService
{
private
final
SensorReadingRepository
sensorReadingRepository
;
private
final
DeviceRepository
deviceRepository
;
private
final
RedisCacheService
redisCacheService
;
@Transactional
public
SensorDataResponse
saveSensorData
(
SensorDataRequest
request
)
{
log
.
info
(
"Saving sensor data for device: {}"
,
request
.
getDeviceId
());
// 디바이스 상태 업데이트
updateDeviceLastSeen
(
request
.
getDeviceId
());
// 센서 데이터 저장
SensorReading
reading
=
SensorReading
.
builder
()
.
deviceId
(
request
.
getDeviceId
())
.
nodeId
(
request
.
getNodeId
())
.
temperature
(
request
.
getTemperature
())
.
humidity
(
request
.
getHumidity
())
.
longitude
(
request
.
getLongitude
())
.
latitude
(
request
.
getLatitude
())
.
recordedTime
(
request
.
getRecordedTime
())
.
receivedTime
(
LocalDateTime
.
now
())
.
build
();
SensorReading
saved
=
sensorReadingRepository
.
save
(
reading
);
// Redis 캐시 업데이트
redisCacheService
.
updateLatestData
(
request
.
getDeviceId
(),
saved
);
return
convertToResponse
(
saved
);
}
@Cacheable
(
value
=
"latestData"
,
key
=
"#deviceId"
)
public
SensorDataResponse
getLatestData
(
String
deviceId
)
{
log
.
info
(
"Getting latest data for device: {}"
,
deviceId
);
return
sensorReadingRepository
.
findFirstByDeviceIdOrderByRecordedTimeDesc
(
deviceId
)
.
map
(
this
::
convertToResponse
)
.
orElse
(
null
);
}
@Cacheable
(
value
=
"deviceHistory"
,
key
=
"#deviceId + '_' + #days"
)
public
List
<
SensorDataResponse
>
getDeviceHistory
(
String
deviceId
,
int
days
)
{
log
.
info
(
"Getting {} days history for device: {}"
,
days
,
deviceId
);
LocalDateTime
since
=
LocalDateTime
.
now
().
minusDays
(
days
);
return
sensorReadingRepository
.
findByDeviceIdAndTimeRange
(
deviceId
,
since
,
LocalDateTime
.
now
())
.
stream
()
.
map
(
this
::
convertToResponse
)
.
collect
(
Collectors
.
toList
());
}
@CacheEvict
(
value
=
{
"latestData"
,
"deviceHistory"
},
allEntries
=
true
)
public
void
clearCache
()
{
log
.
info
(
"Clearing all sensor data cache"
);
}
private
void
updateDeviceLastSeen
(
String
deviceId
)
{
deviceRepository
.
findByDeviceId
(
deviceId
)
.
ifPresent
(
device
->
{
device
.
setLastSeen
(
LocalDateTime
.
now
());
device
.
setStatus
(
Device
.
DeviceStatus
.
ACTIVE
);
deviceRepository
.
save
(
device
);
});
}
private
SensorDataResponse
convertToResponse
(
SensorReading
reading
)
{
return
SensorDataResponse
.
builder
()
.
id
(
reading
.
getId
())
.
deviceId
(
reading
.
getDeviceId
())
.
nodeId
(
reading
.
getNodeId
())
.
temperature
(
reading
.
getTemperature
())
.
humidity
(
reading
.
getHumidity
())
.
longitude
(
reading
.
getLongitude
())
.
latitude
(
reading
.
getLatitude
())
.
recordedTime
(
reading
.
getRecordedTime
())
.
receivedTime
(
reading
.
getReceivedTime
())
.
createdAt
(
reading
.
getCreatedAt
())
.
build
();
}
}
```
### 4. Controller 계층
#### **SensorDataController.java**
```
java
package
com.sensor.spring.controller
;
import
com.sensor.spring.dto.SensorDataRequest
;
import
com.sensor.spring.dto.SensorDataResponse
;
import
com.sensor.spring.service.SensorDataService
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.http.HttpStatus
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.validation.annotation.Validated
;
import
org.springframework.web.bind.annotation.*
;
import
javax.validation.Valid
;
import
java.util.List
;
@RestController
@RequestMapping
(
"/api/sensor-data"
)
@RequiredArgsConstructor
@Slf4j
@Validated
public
class
SensorDataController
{
private
final
SensorDataService
sensorDataService
;
@PostMapping
public
ResponseEntity
<
SensorDataResponse
>
saveSensorData
(
@Valid
@RequestBody
SensorDataRequest
request
)
{
log
.
info
(
"Received sensor data request: {}"
,
request
);
try
{
SensorDataResponse
response
=
sensorDataService
.
saveSensorData
(
request
);
return
ResponseEntity
.
status
(
HttpStatus
.
CREATED
).
body
(
response
);
}
catch
(
Exception
e
)
{
log
.
error
(
"Error saving sensor data: {}"
,
e
.
getMessage
(),
e
);
return
ResponseEntity
.
status
(
HttpStatus
.
INTERNAL_SERVER_ERROR
).
build
();
}
}
@GetMapping
(
"/devices/{deviceId}/latest"
)
public
ResponseEntity
<
SensorDataResponse
>
getLatestData
(
@PathVariable
String
deviceId
)
{
log
.
info
(
"Getting latest data for device: {}"
,
deviceId
);
SensorDataResponse
response
=
sensorDataService
.
getLatestData
(
deviceId
);
if
(
response
!=
null
)
{
return
ResponseEntity
.
ok
(
response
);
}
else
{
return
ResponseEntity
.
notFound
().
build
();
}
}
@GetMapping
(
"/devices/{deviceId}/history"
)
public
ResponseEntity
<
List
<
SensorDataResponse
>>
getDeviceHistory
(
@PathVariable
String
deviceId
,
@RequestParam
(
defaultValue
=
"7"
)
int
days
)
{
log
.
info
(
"Getting {} days history for device: {}"
,
days
,
deviceId
);
List
<
SensorDataResponse
>
history
=
sensorDataService
.
getDeviceHistory
(
deviceId
,
days
);
return
ResponseEntity
.
ok
(
history
);
}
@DeleteMapping
(
"/cache"
)
public
ResponseEntity
<
Void
>
clearCache
()
{
log
.
info
(
"Clearing sensor data cache"
);
sensorDataService
.
clearCache
();
return
ResponseEntity
.
ok
().
build
();
}
}
```
---
## 🎨 웹 인터페이스
### 1. Thymeleaf 템플릿
#### **dashboard.html**
```
html
<!DOCTYPE html>
<html
xmlns:th=
"http://www.thymeleaf.org"
>
<head>
<meta
charset=
"UTF-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<title>
센서 데이터 대시보드
</title>
<link
href=
"https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
rel=
"stylesheet"
>
<link
href=
"https://cdn.jsdelivr.net/npm/chart.js@3.7.0/dist/chart.min.css"
rel=
"stylesheet"
>
</head>
<body>
<nav
class=
"navbar navbar-expand-lg navbar-dark bg-dark"
>
<div
class=
"container"
>
<a
class=
"navbar-brand"
href=
"#"
>
센서 모니터링 시스템
</a>
</div>
</nav>
<div
class=
"container mt-4"
>
<div
class=
"row"
>
<div
class=
"col-md-6"
>
<div
class=
"card"
>
<div
class=
"card-header"
>
<h5>
실시간 센서 데이터
</h5>
</div>
<div
class=
"card-body"
>
<div
id=
"latestData"
>
<p>
데이터를 불러오는 중...
</p>
</div>
</div>
</div>
</div>
<div
class=
"col-md-6"
>
<div
class=
"card"
>
<div
class=
"card-header"
>
<h5>
온도 변화 그래프
</h5>
</div>
<div
class=
"card-body"
>
<canvas
id=
"temperatureChart"
></canvas>
</div>
</div>
</div>
</div>
<div
class=
"row mt-4"
>
<div
class=
"col-12"
>
<div
class=
"card"
>
<div
class=
"card-header"
>
<h5>
디바이스 목록
</h5>
</div>
<div
class=
"card-body"
>
<div
id=
"deviceList"
>
<p>
디바이스 목록을 불러오는 중...
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<script
src=
"https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
></script>
<script
src=
"https://cdn.jsdelivr.net/npm/chart.js@3.7.0/dist/chart.min.js"
></script>
<script
th:src=
"@{/js/dashboard.js}"
></script>
</body>
</html>
```
### 2. JavaScript 파일
#### **dashboard.js**
```
javascript
// 센서 데이터 대시보드 JavaScript
class
SensorDashboard
{
constructor
()
{
this
.
temperatureChart
=
null
;
this
.
init
();
}
init
()
{
this
.
initTemperatureChart
();
this
.
loadLatestData
();
this
.
loadDeviceList
();
this
.
startRealTimeUpdates
();
}
initTemperatureChart
()
{
const
ctx
=
document
.
getElementById
(
'
temperatureChart
'
).
getContext
(
'
2d
'
);
this
.
temperatureChart
=
new
Chart
(
ctx
,
{
type
:
'
line
'
,
data
:
{
labels
:
[],
datasets
:
[{
label
:
'
온도 (°C)
'
,
data
:
[],
borderColor
:
'
rgb(75, 192, 192)
'
,
tension
:
0.1
}]
},
options
:
{
responsive
:
true
,
scales
:
{
y
:
{
beginAtZero
:
false
}
}
}
});
}
async
loadLatestData
()
{
try
{
const
response
=
await
fetch
(
'
/api/sensor-data/devices/15328737/latest
'
);
if
(
response
.
ok
)
{
const
data
=
await
response
.
json
();
this
.
displayLatestData
(
data
);
}
}
catch
(
error
)
{
console
.
error
(
'
Error loading latest data:
'
,
error
);
}
}
displayLatestData
(
data
)
{
const
container
=
document
.
getElementById
(
'
latestData
'
);
container
.
innerHTML
=
`
<div class="row">
<div class="col-6">
<strong>디바이스 ID:</strong>
${
data
.
deviceId
}
<br>
<strong>온도:</strong>
${
data
.
temperature
}
°C<br>
<strong>습도:</strong>
${
data
.
humidity
}
%
</div>
<div class="col-6">
<strong>위도:</strong>
${
data
.
latitude
}
<br>
<strong>경도:</strong>
${
data
.
longitude
}
<br>
<strong>수신 시간:</strong>
${
new
Date
(
data
.
receivedTime
).
toLocaleString
()}
</div>
</div>
`
;
}
async
loadDeviceList
()
{
try
{
const
response
=
await
fetch
(
'
/api/devices
'
);
if
(
response
.
ok
)
{
const
devices
=
await
response
.
json
();
this
.
displayDeviceList
(
devices
);
}
}
catch
(
error
)
{
console
.
error
(
'
Error loading device list:
'
,
error
);
}
}
displayDeviceList
(
devices
)
{
const
container
=
document
.
getElementById
(
'
deviceList
'
);
const
table
=
`
<table class="table table-striped">
<thead>
<tr>
<th>디바이스 ID</th>
<th>이름</th>
<th>상태</th>
<th>마지막 연결</th>
</tr>
</thead>
<tbody>
${
devices
.
map
(
device
=>
`
<tr>
<td>
${
device
.
deviceId
}
</td>
<td>
${
device
.
name
||
'
-
'
}
</td>
<td><span class="badge bg-
${
this
.
getStatusColor
(
device
.
status
)}
">
${
device
.
status
}
</span></td>
<td>
${
device
.
lastSeen
?
new
Date
(
device
.
lastSeen
).
toLocaleString
()
:
'
-
'
}
</td>
</tr>
`
).
join
(
''
)}
</tbody>
</table>
`
;
container
.
innerHTML
=
table
;
}
getStatusColor
(
status
)
{
switch
(
status
)
{
case
'
ACTIVE
'
:
return
'
success
'
;
case
'
INACTIVE
'
:
return
'
secondary
'
;
case
'
ERROR
'
:
return
'
danger
'
;
case
'
MAINTENANCE
'
:
return
'
warning
'
;
default
:
return
'
secondary
'
;
}
}
startRealTimeUpdates
()
{
setInterval
(()
=>
{
this
.
loadLatestData
();
},
30000
);
// 30초마다 업데이트
}
}
// 페이지 로드 시 대시보드 초기화
document
.
addEventListener
(
'
DOMContentLoaded
'
,
()
=>
{
new
SensorDashboard
();
});
```
---
## 🔄 기존 시스템 연동
### 1. Docker Compose 수정
#### **docker-compose.yml 업데이트**
```
yaml
version
:
'
3.8'
services
:
# 기존 서비스들
postgres
:
image
:
postgres:15
environment
:
POSTGRES_DB
:
sensor_db
POSTGRES_USER
:
postgres
POSTGRES_PASSWORD
:
password
ports
:
-
"
5432:5432"
volumes
:
-
postgres_data:/var/lib/postgresql/data
-
./init-db.sql:/docker-entrypoint-initdb.d/init-db.sql
redis
:
image
:
redis:7-alpine
ports
:
-
"
6379:6379"
volumes
:
-
redis_data:/data
sensor-bridge
:
build
:
./sensor-bridge
ports
:
-
"
8020:8020"
environment
:
GO_SERVER_URL
:
http://sensor-spring-app:8080
depends_on
:
-
postgres
-
redis
# 새로운 Spring 애플리케이션
sensor-spring-app
:
build
:
./sensor-spring-app
ports
:
-
"
8080:8080"
environment
:
SPRING_PROFILES_ACTIVE
:
prod
SPRING_DATASOURCE_URL
:
jdbc:postgresql://postgres:5432/sensor_db
SPRING_DATASOURCE_USERNAME
:
postgres
SPRING_DATASOURCE_PASSWORD
:
password
SPRING_REDIS_HOST
:
redis
SPRING_REDIS_PORT
:
6379
depends_on
:
-
postgres
-
redis
volumes
:
-
./sensor-spring-app/logs:/app/logs
volumes
:
postgres_data
:
redis_data
:
```
### 2. 기존 Go 서버와의 차이점
| 구분 | Go 서버 | Spring Boot |
|------|---------|-------------|
|
**언어**
| Go | Java 11 |
|
**프레임워크**
| Gin | Spring Boot + 전자정부프레임워크 |
|
**데이터 접근**
| 직접 SQL | JPA + Hibernate |
|
**캐싱**
| Redis 클라이언트 | Spring Data Redis |
|
**템플릿**
| 없음 | Thymeleaf |
|
**보안**
| 기본 | Spring Security (추가 가능) |
|
**모니터링**
| 기본 | Spring Actuator + Micrometer |
---
**계속...**
Java_Spring_센서_시스템_개발_가이드_3.md
0 → 100644
View file @
003942b3
# Java Spring(전자정부프레임워크) 센서 시스템 개발 가이드 - Part 3
## 🚀 배포 및 실행
### 1. 개발 환경 실행
```
bash
# 프로젝트 디렉토리로 이동
cd
sensor-spring-app
# Maven 의존성 설치
mvn clean
install
# Spring Boot 애플리케이션 실행
mvn spring-boot:run
```
### 2. Docker 빌드 및 실행
```
bash
# 전체 시스템 실행
docker-compose up
-d
# 특정 서비스만 실행
docker-compose up
-d
sensor-spring-app
# 로그 확인
docker-compose logs
-f
sensor-spring-app
```
### 3. 운영 환경 배포
```
bash
# JAR 파일 빌드
mvn clean package
-DskipTests
# JAR 파일 실행
java
-jar
target/sensor-spring-app-1.0.0.jar
\
--spring
.profiles.active
=
prod
\
--server
.port
=
8080
```
---
## 📊 모니터링 및 로깅
### 1. Spring Actuator 엔드포인트
-
**Health Check**
:
`/actuator/health`
-
**Info**
:
`/actuator/info`
-
**Metrics**
:
`/actuator/metrics`
-
**Prometheus**
:
`/actuator/prometheus`
### 2. 로깅 설정
```
yaml
logging
:
pattern
:
console
:
"
%d{yyyy-MM-dd
HH:mm:ss}
[%thread]
%-5level
%logger{36}
-
%msg%n"
file
:
"
%d{yyyy-MM-dd
HH:mm:ss}
[%thread]
%-5level
%logger{36}
-
%msg%n"
file
:
name
:
logs/sensor-spring-app.log
level
:
com.sensor.spring
:
INFO
org.springframework.web
:
INFO
org.hibernate.SQL
:
DEBUG
```
---
## 🧪 테스트
### 1. 단위 테스트
```
java
@SpringBootTest
class
SensorDataServiceTest
{
@Autowired
private
SensorDataService
sensorDataService
;
@Test
void
testSaveSensorData
()
{
SensorDataRequest
request
=
SensorDataRequest
.
builder
()
.
deviceId
(
"TEST_DEVICE"
)
.
temperature
(
25.5
)
.
humidity
(
60.0
)
.
build
();
SensorDataResponse
response
=
sensorDataService
.
saveSensorData
(
request
);
assertNotNull
(
response
);
assertEquals
(
"TEST_DEVICE"
,
response
.
getDeviceId
());
assertEquals
(
25.5
,
response
.
getTemperature
());
}
}
```
### 2. 통합 테스트
```
java
@SpringBootTest
(
webEnvironment
=
SpringBootTest
.
WebEnvironment
.
RANDOM_PORT
)
@AutoConfigureTestDatabase
(
replace
=
AutoConfigureTestDatabase
.
Replace
.
NONE
)
class
SensorDataControllerIntegrationTest
{
@Autowired
private
TestRestTemplate
restTemplate
;
@Test
void
testSaveSensorDataEndpoint
()
{
SensorDataRequest
request
=
new
SensorDataRequest
();
request
.
setDeviceId
(
"TEST_DEVICE"
);
request
.
setTemperature
(
25.5
);
ResponseEntity
<
SensorDataResponse
>
response
=
restTemplate
.
postForEntity
(
"/api/sensor-data"
,
request
,
SensorDataResponse
.
class
);
assertEquals
(
HttpStatus
.
CREATED
,
response
.
getStatusCode
());
assertNotNull
(
response
.
getBody
());
}
}
```
---
## 🔒 보안 설정
### 1. Spring Security 설정
```
java
@Configuration
@EnableWebSecurity
public
class
SecurityConfig
extends
WebSecurityConfigurerAdapter
{
@Override
protected
void
configure
(
HttpSecurity
http
)
throws
Exception
{
http
.
authorizeRequests
()
.
antMatchers
(
"/api/public/**"
).
permitAll
()
.
antMatchers
(
"/api/**"
).
authenticated
()
.
anyRequest
().
permitAll
()
.
and
()
.
csrf
().
disable
()
.
httpBasic
();
}
}
```
### 2. CORS 설정
```
java
@Configuration
public
class
CorsConfig
{
@Bean
public
CorsConfigurationSource
corsConfigurationSource
()
{
CorsConfiguration
configuration
=
new
CorsConfiguration
();
configuration
.
setAllowedOrigins
(
Arrays
.
asList
(
"*"
));
configuration
.
setAllowedMethods
(
Arrays
.
asList
(
"GET"
,
"POST"
,
"PUT"
,
"DELETE"
));
configuration
.
setAllowedHeaders
(
Arrays
.
asList
(
"*"
));
UrlBasedCorsConfigurationSource
source
=
new
UrlBasedCorsConfigurationSource
();
source
.
registerCorsConfiguration
(
"/**"
,
configuration
);
return
source
;
}
}
```
---
## 📈 성능 최적화
### 1. 캐싱 전략
-
**Redis 캐싱**
: 최신 데이터, 디바이스 목록
-
**JPA 2nd Level Cache**
: 엔티티 캐싱
-
**HTTP 응답 캐싱**
: 정적 리소스
### 2. 데이터베이스 최적화
-
**인덱스**
: device_id, recorded_time
-
**파티셔닝**
: 날짜별 테이블 파티셔닝
-
**Connection Pool**
: HikariCP 설정
### 3. 비동기 처리
-
**@Async**
: 센서 데이터 저장
-
**@Scheduled**
: 정기적인 데이터 정리
-
**WebSocket**
: 실시간 데이터 스트리밍
---
## 🐛 문제 해결
### 1. 일반적인 문제
#### **포트 충돌**
```
bash
# 사용 중인 포트 확인
netstat
-tulpn
|
grep
:8080
# Spring Boot 애플리케이션 중지
pkill
-f
"sensor-spring-app"
```
#### **데이터베이스 연결 실패**
```
bash
# PostgreSQL 상태 확인
docker-compose ps postgres
# 연결 테스트
docker
exec
-it
sensor-postgres psql
-U
postgres
-d
sensor_db
```
#### **메모리 부족**
```
bash
# JVM 메모리 설정
java
-Xmx2g
-Xms1g
-jar
target/sensor-spring-app-1.0.0.jar
```
### 2. 로그 분석
```
bash
# 애플리케이션 로그 확인
tail
-f
logs/sensor-spring-app.log
# 에러 로그 필터링
grep
"ERROR"
logs/sensor-spring-app.log
# 특정 디바이스 로그
grep
"deviceId=15328737"
logs/sensor-spring-app.log
```
---
## 📝 향후 개선 계획
### 1. 단기 개선 (1-2주)
-
[ ] Spring Security 인증/인가 시스템 구축
-
[ ] API 문서화 (Swagger/OpenAPI)
-
[ ] 단위 테스트 커버리지 향상
### 2. 중기 개선 (1-2개월)
-
[ ] 실시간 알림 시스템 (WebSocket)
-
[ ] 데이터 검증 및 이상치 탐지
-
[ ] 성능 모니터링 대시보드
### 3. 장기 개선 (3-6개월)
-
[ ] 머신러닝 기반 예측 시스템
-
[ ] 마이크로서비스 아키텍처 전환
-
[ ] 클라우드 네이티브 배포
---
## 📚 참고 자료
### 1. 공식 문서
-
[
Spring Boot Reference
](
https://docs.spring.io/spring-boot/docs/current/reference/html/
)
-
[
전자정부프레임워크 가이드
](
https://www.egovframe.go.kr/home/sub.do?menuNo=9
)
-
[
Spring Data JPA
](
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
)
### 2. 관련 라이브러리
-
[
Thymeleaf
](
https://www.thymeleaf.org/documentation.html
)
-
[
Chart.js
](
https://www.chartjs.org/docs/latest/
)
-
[
Bootstrap
](
https://getbootstrap.com/docs/
)
---
## ✅ 체크리스트
### **개발 환경 설정**
-
[x] Java 11 설치
-
[x] Maven 3.6.x 설치
-
[x] IDE 설정
-
[x] 프로젝트 구조 생성
### **핵심 기능 구현**
-
[x] JPA 엔티티 정의
-
[x] Repository 계층 구현
-
[x] Service 계층 구현
-
[x] Controller 계층 구현
-
[x] 웹 인터페이스 구현
### **설정 및 배포**
-
[x] application.yml 설정
-
[x] Docker 설정
-
[x] 데이터베이스 연동
-
[x] Redis 캐싱 설정
### **테스트 및 모니터링**
-
[x] 단위 테스트 작성
-
[x] 통합 테스트 작성
-
[x] 로깅 설정
-
[x] Actuator 설정
---
## 🔄 기존 시스템과의 연동 시나리오
### 1. 점진적 마이그레이션
```
1단계: Spring Boot 애플리케이션 추가
├── 기존 Go 서버 유지 (포트: 8080)
├── 새로운 Spring Boot 추가 (포트: 8081)
└── 로드 밸런서로 트래픽 분산
2단계: 기능별 전환
├── 센서 데이터 수집: Spring Boot로 전환
├── 데이터 조회: Spring Boot로 전환
└── 웹 대시보드: Spring Boot로 전환
3단계: 완전 전환
├── Go 서버 중단
├── Spring Boot를 메인 서비스로 설정
└── 포트를 8080으로 변경
```
### 2. 데이터 마이그레이션
```
sql
-- 기존 테이블 구조 유지
-- 새로운 인덱스 추가
CREATE
INDEX
idx_sensor_readings_device_time
ON
sensor_readings
(
device_id
,
recorded_time
);
-- 파티셔닝 테이블 생성 (선택사항)
CREATE
TABLE
sensor_readings_2024
PARTITION
OF
sensor_readings
FOR
VALUES
FROM
(
'2024-01-01'
)
TO
(
'2025-01-01'
);
```
### 3. API 호환성 유지
```
java
// 기존 Go 서버와 동일한 API 엔드포인트 제공
@RestController
@RequestMapping
(
"/api"
)
public
class
LegacyApiController
{
@PostMapping
(
"/sensor-data"
)
public
ResponseEntity
<
SensorDataResponse
>
legacySensorData
(
@RequestBody
LegacySensorDataRequest
request
)
{
// 기존 형식의 요청을 새로운 형식으로 변환
SensorDataRequest
newRequest
=
convertLegacyRequest
(
request
);
return
sensorDataService
.
saveSensorData
(
newRequest
);
}
}
```
---
## 🎯 개발 우선순위
### **Phase 1: 기본 구조 (1-2주)**
1.
Spring Boot 프로젝트 생성
2.
기본 엔티티 및 Repository 구현
3.
간단한 REST API 구현
4.
데이터베이스 연결 테스트
### **Phase 2: 핵심 기능 (2-3주)**
1.
센서 데이터 수집 API 완성
2.
데이터 조회 및 히스토리 API 구현
3.
Redis 캐싱 시스템 구축
4.
기본 웹 대시보드 구현
### **Phase 3: 고도화 (3-4주)**
1.
전자정부프레임워크 통합
2.
보안 시스템 구축
3.
모니터링 및 로깅 시스템
4.
성능 최적화
### **Phase 4: 테스트 및 배포 (1-2주)**
1.
단위/통합 테스트 작성
2.
Docker 컨테이너화
3.
운영 환경 배포
4.
기존 시스템과의 연동 테스트
---
## 💡 개발 팁
### 1. Spring Boot 개발 팁
-
**프로파일 활용**
: dev, test, prod 환경별 설정 분리
-
**자동 설정**
: Spring Boot의 자동 설정 기능 최대한 활용
-
**의존성 관리**
: Maven BOM을 통한 버전 관리
### 2. JPA 사용 팁
-
**N+1 문제 방지**
: fetch join, @EntityGraph 활용
-
**벌크 연산**
: JpaRepository.saveAll() 활용
-
**트랜잭션 관리**
: @Transactional 범위 최소화
### 3. 성능 최적화 팁
-
**캐싱 전략**
: Redis + JPA 2nd Level Cache 조합
-
**비동기 처리**
: @Async + CompletableFuture 활용
-
**데이터베이스 최적화**
: 인덱스, 파티셔닝, 커넥션 풀
---
## 🚨 주의사항
### 1. 보안 관련
-
**입력 검증**
: 모든 사용자 입력에 대한 검증 필수
-
**SQL 인젝션**
: JPA 사용으로 자동 방지
-
**XSS 방지**
: Thymeleaf의 기본 이스케이프 활용
### 2. 성능 관련
-
**메모리 사용량**
: JVM 힙 메모리 모니터링 필수
-
**데이터베이스 연결**
: 커넥션 풀 크기 적절히 설정
-
**캐시 무효화**
: 데이터 변경 시 캐시 무효화 전략 수립
### 3. 운영 관련
-
**로그 관리**
: 로그 파일 크기 및 보관 기간 설정
-
**모니터링**
: Spring Actuator + Prometheus 연동
-
**백업**
: 데이터베이스 정기 백업 및 복구 테스트
---
## 📞 지원 및 문의
### **개발 지원**
-
**기술 문서**
: 프로젝트 내 README.md 참조
-
**API 문서**
: Swagger UI (/swagger-ui.html)
-
**모니터링**
: Spring Actuator 엔드포인트 활용
### **문제 해결**
-
**로그 분석**
: 애플리케이션 로그 및 시스템 로그 확인
-
**성능 분석**
: JVM 메트릭 및 데이터베이스 쿼리 분석
-
**네트워크 분석**
: 포트 및 연결 상태 확인
---
**작성일**
: 2024년 12월
**작성자**
: AI Assistant
**버전**
: 1.0.0
---
*
이 가이드는 Java Spring(전자정부프레임워크)으로 센서 정보를 수집하고 관리하는 웹프로그램을 개발하기 위한 것입니다.
추가 질문이나 명확화가 필요한 부분이 있으면 언제든 연락주세요.
*
Java_Spring_센서_시스템_개발_가이드_README.md
0 → 100644
View file @
003942b3
# Java Spring(전자정부프레임워크) 센서 시스템 개발 가이드 - 전체 목차
## 📚 가이드 구성
현재 프로젝트를 바탕으로 Java Spring(전자정부프레임워크)으로 센서 정보를 수집하고 관리하는 웹프로그램을 개발하기 위한 완전한 가이드입니다.
---
## 📖 가이드 목차
### **Part 1: 프로젝트 개요 및 기본 설정**
-
[
Java_Spring_센서_시스템_개발_가이드_1.md
](
./Java_Spring_센서_시스템_개발_가이드_1.md
)
-
프로젝트 개요 및 시스템 아키텍처
-
프로젝트 구조 및 개발 환경 설정
-
Maven 프로젝트 설정 (pom.xml)
-
Spring Boot 메인 클래스
-
설정 파일 (application.yml)
-
데이터 모델 (JPA 엔티티)
### **Part 2: 핵심 구현 및 웹 인터페이스**
-
[
Java_Spring_센서_시스템_개발_가이드_2.md
](
./Java_Spring_센서_시스템_개발_가이드_2.md
)
-
DTO 클래스 및 Repository 계층
-
Service 계층 및 Controller 계층
-
웹 인터페이스 (Thymeleaf + JavaScript)
-
기존 시스템 연동 방법
-
Docker Compose 설정
### **Part 3: 배포, 테스트 및 운영**
-
[
Java_Spring_센서_시스템_개발_가이드_3.md
](
./Java_Spring_센서_시스템_개발_가이드_3.md
)
-
배포 및 실행 방법
-
모니터링 및 로깅
-
테스트 코드 작성
-
보안 설정 및 성능 최적화
-
문제 해결 가이드
-
향후 개선 계획
---
## 🎯 주요 특징
### **기술 스택**
-
**백엔드**
: Java 11 + Spring Boot 2.7.x + 전자정부프레임워크 4.0.x
-
**데이터베이스**
: PostgreSQL + Redis
-
**웹 프론트엔드**
: Thymeleaf + Bootstrap + Chart.js
-
**빌드 도구**
: Maven
-
**컨테이너**
: Docker + Docker Compose
### **아키텍처**
-
**계층형 아키텍처**
: Controller → Service → Repository → Entity
-
**REST API**
: 센서 데이터 수집 및 조회
-
**캐싱 전략**
: Redis + JPA 2nd Level Cache
-
**모니터링**
: Spring Actuator + Micrometer
### **기존 시스템과의 차이점**
| 구분 | 기존 Go 서버 | 새로운 Spring Boot |
|------|-------------|-------------------|
|
**언어**
| Go | Java 11 |
|
**프레임워크**
| Gin | Spring Boot + 전자정부프레임워크 |
|
**데이터 접근**
| 직접 SQL | JPA + Hibernate |
|
**웹 인터페이스**
| 없음 | Thymeleaf 템플릿 |
|
**보안**
| 기본 | Spring Security |
|
**모니터링**
| 기본 | Spring Actuator |
---
## 🚀 빠른 시작
### **1. 개발 환경 준비**
```
bash
# Java 11 설치 확인
java
-version
# Maven 설치 확인
mvn
-version
# 프로젝트 클론
git clone <repository-url>
cd
docker_sensor_server
```
### **2. Spring 애플리케이션 생성**
```
bash
# 새로운 Spring 프로젝트 디렉토리 생성
mkdir
sensor-spring-app
cd
sensor-spring-app
# 가이드의 pom.xml 복사 및 Maven 의존성 설치
mvn clean
install
```
### **3. 기존 시스템과 함께 실행**
```
bash
# 전체 시스템 실행 (기존 + 새로운 Spring 앱)
docker-compose up
-d
# Spring 애플리케이션만 실행
docker-compose up
-d
sensor-spring-app
```
---
## 📋 구현 체크리스트
### **Phase 1: 기본 구조 (1-2주)**
-
[ ] Spring Boot 프로젝트 생성
-
[ ] 기본 엔티티 및 Repository 구현
-
[ ] 간단한 REST API 구현
-
[ ] 데이터베이스 연결 테스트
### **Phase 2: 핵심 기능 (2-3주)**
-
[ ] 센서 데이터 수집 API 완성
-
[ ] 데이터 조회 및 히스토리 API 구현
-
[ ] Redis 캐싱 시스템 구축
-
[ ] 기본 웹 대시보드 구현
### **Phase 3: 고도화 (3-4주)**
-
[ ] 전자정부프레임워크 통합
-
[ ] 보안 시스템 구축
-
[ ] 모니터링 및 로깅 시스템
-
[ ] 성능 최적화
### **Phase 4: 테스트 및 배포 (1-2주)**
-
[ ] 단위/통합 테스트 작성
-
[ ] Docker 컨테이너화
-
[ ] 운영 환경 배포
-
[ ] 기존 시스템과의 연동 테스트
---
## 🔗 관련 파일
### **기존 시스템 파일**
-
`sensor-bridge/`
- Java 센서 브리지 (RSNet SDK)
-
`docker-compose.yml`
- 전체 시스템 설정
-
`README.md`
- 기존 프로젝트 문서
-
`SENSOR_MVP_인수인계서.md`
- 상세 인수인계서
### **새로운 Spring 애플리케이션**
-
`sensor-spring-app/`
- Spring Boot 애플리케이션 (가이드에 따라 생성)
-
`Java_Spring_센서_시스템_개발_가이드_*.md`
- 개발 가이드 문서
---
## 💡 개발 팁
### **Spring Boot 개발**
-
프로파일을 활용한 환경별 설정 분리
-
Spring Boot의 자동 설정 기능 최대한 활용
-
Maven BOM을 통한 의존성 버전 관리
### **JPA 사용**
-
N+1 문제 방지를 위한 fetch join, @EntityGraph 활용
-
벌크 연산을 위한 JpaRepository.saveAll() 활용
-
@Transactional 범위 최소화
### **성능 최적화**
-
Redis + JPA 2nd Level Cache 조합
-
@Async + CompletableFuture를 활용한 비동기 처리
-
데이터베이스 인덱스, 파티셔닝, 커넥션 풀 최적화
---
## 🚨 주의사항
### **보안**
-
모든 사용자 입력에 대한 검증 필수
-
JPA 사용으로 SQL 인젝션 자동 방지
-
Thymeleaf의 기본 이스케이프 활용
### **성능**
-
JVM 힙 메모리 모니터링 필수
-
데이터베이스 커넥션 풀 크기 적절히 설정
-
데이터 변경 시 캐시 무효화 전략 수립
### **운영**
-
로그 파일 크기 및 보관 기간 설정
-
Spring Actuator + Prometheus 연동
-
데이터베이스 정기 백업 및 복구 테스트
---
## 📞 지원 및 문의
### **개발 지원**
-
**기술 문서**
: 각 가이드 파일 참조
-
**API 문서**
: Swagger UI (/swagger-ui.html)
-
**모니터링**
: Spring Actuator 엔드포인트 활용
### **문제 해결**
-
**로그 분석**
: 애플리케이션 로그 및 시스템 로그 확인
-
**성능 분석**
: JVM 메트릭 및 데이터베이스 쿼리 분석
-
**네트워크 분석**
: 포트 및 연결 상태 확인
---
## 📝 변경 이력
| 날짜 | 버전 | 변경 내용 | 작성자 |
|------|------|-----------|--------|
| 2024-12-XX | 1.0.0 | 초기 가이드 작성 완료 | AI Assistant |
---
**작성일**
: 2024년 12월
**작성자**
: AI Assistant
**버전**
: 1.0.0
---
*
이 가이드는 현재 구현된 프로젝트를 바탕으로 Java Spring(전자정부프레임워크)으로 센서 정보를 수집하고 관리하는 웹프로그램을 개발하기 위한 완전한 가이드입니다.
각 Part를 순서대로 참조하여 단계별로 개발을 진행하시기 바랍니다.
*
SENSOR 사용 가이드.hwp
0 → 100644
View file @
003942b3
File added
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment