Capstone Project

Project Description

Description.pdf

Project Setting

  • Gradle Project, Java(ver 11), Spring Boot(ver 2.7.0)
  • Spring MVC - Spring Web
  • Annotation library - Lombok
  • View Template - Thymeleaf
  • DataBase - H2 Database (SQL), Spring Data JPA, AWS RDS (SQL)

  • 스프링 부트 라이브러리
    • spring-boot-starter-web
      • spring-boot-starter-tomcat: 톰캣 (웹서버)
      • spring-webmvc: 스프링 웹 MVC
    • spring-boot-starter-thymeleaf: 타임리프 템플릿 엔진(View)
    • spring-boot-starter(공통): 스프링 부트 + 스프링 코어 + 로깅
    • spring-boot
      • spring-core
    • spring-boot-starter-logging logback, slf4j
  • 테스트 라이브러리
    • spring-boot-starter-test
      • junit : 테스트 프레임워크
      • mockito : 목 라이브러리
      • aseertj : : 테스트 코드를 좀 더 편하게 작성하게 도와주는 라이브러리
      • spring-test : 스프링 통합 테스트 지원
  • Gradle (build.gradle)

      plugins {
      	id 'org.springframework.boot' version '2.6.4'
      	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
      	id 'java'
      }
        
      group = 'Talk_with'
      version = '0.0.1-SNAPSHOT'
      sourceCompatibility = '11'
        
      configurations {
      	compileOnly {
      		extendsFrom annotationProcessor
      	}
      }
        
      repositories {
      	mavenCentral()
      }
        
      dependencies {
      	implementation 'org.springframework.boot:spring-boot-starter-data-jpa' //spring-data-jpa
      	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' //veiw template
      	implementation 'org.springframework.boot:spring-boot-starter-web' // spring-web
      	implementation 'org.springframework.boot:spring-boot-devtools' // for easy rebuilding
      	implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.8.0' //for tracking sql query
      	runtimeOnly 'mysql:mysql-connector-java' // for AWS RDS
      	// implementation 'com.h2database:h2' // for Test _ in Local DB
      	annotationProcessor 'org.projectlombok:lombok' // for annotation
      	testImplementation 'org.springframework.boot:spring-boot-starter-test' // spring-test
        
      	// AWS SDK (Using S3)
      	implementation platform('com.amazonaws:aws-java-sdk-bom:1.11.1000')
      	implementation 'com.amazonaws:aws-java-sdk-s3'
      }
        
      tasks.named('test') {
      	useJUnitPlatform()
      }
    
  • application.yml

      spring:
        datasource:
          url:  jdbc:mysql://${DB_URL}:3306/capstone # jdbc:h2:tcp://localhost/~/capstone
          username: ${DB_NAME} # park
          password: ${DB_PASSWORD} # 
          driver-class-name: com.mysql.cj.jdbc.Driver # org.h2.Driver
        
        jpa:
          hibernate:
            ddl-auto : update
          properties:
            hibernate:
              format_sql : true
        
      logging:
        level:
          org.hibernate.SQL: debug
      #    org.hibernate.type: trace # p6spy로 대체
    

Domain Analysis

요구사항 분석

  • 기능 목록
    1. 라이브 지도 기능
      • 실시간 인원 측정 및 혼잡도
      • 실시간 드론 측정 위치 알림
      • 좌표에 따른 장소 정보 제공
    2. 라이브 통계 기능
      • 실시간 인원 변화량 제공
      • 누적 데이터 통계량 제공

도메인 모델과 테이블 설계 및 분석

  • 도메인 모델 설계 및 분석

    “장소”와 “검출 객체”의 관계

    • 하나의 장소에 여러 검출 객체가 Mapping 됨
    • 검출 객체의 좌표(위도, 경도)에 따라 장소에 Mapping
  • 엔티티 설계 및 분석

    • 장소(Point)
      • 장소 정보 : id(식별자), place(장소 명), percentage(혼잡도), count(실시간 인원), avgCount(평균 인원), imgSrc(장소 사진), latitude(위도), longitude(경도)
      • 위치 정보 [사각형 범위] : latTopLeft(좌상단 위도), lonTopLeft(좌상단 경도), latBottRight(우하단 위도), lonBottRight(우하단 경도)
      • 최근 관련 데이터(검출 객체) 변경 시점 : lastCommittedTime(새로 들어온 데이터를 인지하기 위함)
      • 연관 관계 : infos(검출 객체 정보, @OneToMany)
    • 검출 객체(Info)
      • 검출 객체 정보 : date(검출 시각), leftValue(객체 박스 위치), topValue(객체 박스 위치), widthValue(객체 박스 너비), heightValue(객체 박스 높이)
      • 드론 정보: lat(위도), lon(경도), droneHeight(고도), azimuth(방위각)
      • 연관 관계 : point(장소, @ManyToOne)
    • 검출 데이터(Temp)
      • Info와 동일한 field를 가지고 있는 Entity지만, 연관관계가 설정되어 있지 않은 Entity
      • 드론에서 실시간으로 저장된 데이터
      • 해당 데이터를 Spring에서 분석 후 JPA를 통해 장소(Point)와 매핑시켜 새로운 Info를 만드는 것
      • 즉, Info를 만들기 위한 일시적인 데이터

      최대한 단순한 구조로 Entity 생성

  • 테이블 설계 및 분석

    • INFO: point의 point_id를 forigen key로 설정하여 연결
    • TEMP에 POINT와의 연관관계 생성하여 만든 것이 INFO
  • 엔티티 클래스 속 주요 메서드

    Entity 생성 및 수정은 되도록이면 Entity안에서 이루어지도록 설정 Setter는 되도록이면 열어두지 않도록 설정 (변경이나 저장 자체를 추후에 추적하기 편하게 만들기 위함. [유지 보수])

    1. Point(장소)
      • 생성 메서드

        생성할 때 필요한 값들만을 가지고 생성 (Point는 미리 생성해두는 값이므로 initDB를 통해 DB에 저장 후 새로 생성할때만 사용. 즉, Client에 의해서 생성되지 않음)

          public static Point createPoint(String place, int percentage, int count, int avgCount, double latitude, double longitude, String imgSrc,
                                          double lonTopLeft, double latTopLeft, double lonBottRight, double latBottRight, LocalDateTime lastCommittedTime) {
              Point point = new Point();
              point.place = place;
              point.percentage = percentage;
              point.count = count;
              point.avgCount = avgCount;
              point.latitude = latitude;
              point.longitude = longitude;
              point.imgSrc = imgSrc;
              point.lonTopLeft = lonTopLeft;
              point.latTopLeft = latTopLeft;
              point.lonBottRight = lonBottRight;
              point.latBottRight = latBottRight;
              point.lastCommittedTime = lastCommittedTime;
              return point;
          }
        
      • 수정 메서드

        수정에 사용되는 상황에 따른 값들만을 가지고 생성

          public void changeCount(int cnt) {
              this.count = cnt;
          }
                    
          public void changePercentage(int percentage) {
              this.percentage = percentage;
          }
                    
          public void changeLastCommittedTime(LocalDateTime lastCommittedTime) {
              this.lastCommittedTime = lastCommittedTime;
          }
        
        • changeCount : Map에서의 각 Point의 실시간 측정 인원 수의 변화를 위한 수정 메서드
        • changePercentage : Map에서의 각 Point의 실시간 혼잡도 변화를 위한 수정 메서드
        • changeLastCommittedTime : 가장 최근 접근 시간 → 해당 시간 이후의 데이터를 인지하기 위함
    2. Info(검출 객체)
      • 연관관계 메서드

        Point와 양방향 연관 관계 : Info가 생성되면 Point의 post list에 더해줄 필요가 있음(→ 해당 검출된 객체(Info)가 어느 장소(Point)의 검출 결과인지 매핑하기 위함). 연관관계 메서드는 생성메서드 안에서만 사용되도록 private으로 설정

          private void setPoint(Point point) {
              this.point = point;
              point.getInfos().add(this);
          }
        
      • 생성 메서드

        연관관계를 이용하며 생성할 필요가 있음 (Point와의 연관관계)

          public static Info createInfo(Point point, Temp temp) {
              Info info = new Info();
              info.date = temp.getDate();
              info.leftValue = temp.getLeftValue();
              info.topValue = temp.getTopValue();
              info.widthValue = temp.getWidthValue();
              info.heightValue = temp.getHeightValue();
              info.lon = temp.getLon();
              info.lat = temp.getLat();
              info.droneHeight = temp.getDroneHeight();
              info.azimuth = temp.getAzimuth();
              info.setPoint(point);
              return info;
          }
        
        • 연관관계를 위해 point를 인자로 받아, 해당 point의 Info List에 현재 생성되는 info 추가. point는 해당 temp의 위치정보를 통해 정해진 장소(위치)
        • temp는 info의 모든 정보를 가지고 있는 객체. 해당 객체의 정보를 받아와 위치정보를 따라 point와의 연관관계를 설정.

Application Implementation

구현 요구사항

  • 기능 목록
    1. 공통 관심사 처리 (Interceptor)
      • 검출 객체와 장소 매핑
    2. Map 기능 (지도)
      • 모든 장소 Floating
      • 장소 정보 제공 ( 실시간 변경 → 측정 인원, 혼잡도)
      • 장소 검색
    3. Statics 기능 (분석)
      • 실시간 측정 인원 및 변경 인원 제공
      • 누적 데이터 분석 제공
      • 장소 검색

아키텍쳐 구성

  • 계층형 구조 사용
    • controller, web: 웹 계층
    • service: 비즈니스 로직, 트랜잭션 처리
    • repository: JPA를 직접 사용하는 계층, 엔티티 매니저 사용
    • domain: 엔티티가 모여 있는 계층, 모든 계층에서 사용
  • 패키지 구조

    Group6.capstone

    • config : Interceptor관련 설정
    • Interceptor : custom Interceptor 구현
    • controller : web 계층 관련 구현
    • service : Business Logic, Transaction 정의 및 구현
    • repository : EM(JPA), DB connect 정의 및 구현
    • domain : Entity 정의 및 구현

장소 로직 개발 (Point)

구현 순서

  • 장소 레포지토리 개발
  • 장소 서비스 개발

Point (Repository, Service)

  • 장소 레포지토리 (PointRepository)
    • JPA를 직접 사용하여 Point Entity와 관련된 Table에 접근하는 계층
    • DI(Dependency Injection) : EntityManager(JPA)
    • public List<Point> findAll() : 모든 장소 조회. jpql(em.createQuery) 사용
    • public Point findOne(Long id) : id를 통해 하나의 장소 조회. em.find 사용
    • public Point findByName(String name) : 이름을 통해 하나의 회원 조회. jpql(em.createQuery), where문 사용
  • 장소서비스 (PointService)
    • Business Logic, Transaction 정의 및 구현
    • DI(Dependency Injection) : 장소 레포지토리(PointRepository)
    • @Transactional(readOnly = true)
      • public List<Point> findAll() : 리포지토리 단순 위임
      • public Point findOne(Long id) : 리포지토리 단순 위임
      • public Point findByName(String name) : 리포지토리 단순 위임
      • public List<Integer> getStaticsData(String name)
        • 분석 화면에 제공될 누적 데이터 생성 및 반환
        • findByName() 을 통해 해당 name을 가지는 point에 대해서 ‘5일 간의 데이터’, ‘해당 날짜의 3시간간의 데이터’ 를 가져와 제공
        • 5일 간의 데이터 : 5일간의 측정 인원 수를 List에 추가(+5)
        • 3시간의 데이터 : 3시간동안(1,2,3)의 측정 인원수를 List에 추가(+3)
        • 반환 : List statics → size : 8, `[1일전, 2일전, ... , 5일전, 1시간전, 2시간전, 3시간전]`
    • @Transactional
      • public void updatePoint(Long id)
        • Point, Info 연관관계 매핑이 이루어진 마지막 시각 → 추후 해당 시각 이후의 데이터만을 불러와 매핑 해주기 위함

검출 데이터 로직 개발 (Temp)

구현 순서

  • 검출데이터 레포지토리 개발
  • 검출데이터 서비스 개발

Temp (Repository, Service)

  • 라이브로 받아올 때 쓰는 데이터, Info로 변환하기 위한 임시 데이터
  • 검출데이터 레포지토리 (TempRepository)
    • 해당 데이터는 드론에 의해 이미 DB에 있는 Entity
    • navtive query를 사용해야 하므로 Spring Data JPA를 사용하여 Temp Entity와 관련된 Table에 접근하는 계층→ public interface TempRepository extends JpaRepository<Temp, Long>
    • List<Temp> getLiveData() : 요청된 시점에서의 10초전부터 현재까지의 검출 객체 조회. @Query(value= *query*, nativeQuery = true) 사용
    • List<Temp> getLastData(@Param(value = "last_commit") LocalDateTime lastCommittedTime); : lastCommitedTime 이후의 검출 객체 조회.
      • @Param : nativeQuery에서 변수로 사용하기 위한 파라미터 설정
      • @Query(value= *query*, nativeQuery = true) 사용
  • 검출데이터 서비스 (TempService)
    • Business Logic, Transaction 정의 및 구현
    • DI(Dependency Injection) : 검출데이터 레포지토리(TempRepository)
    • @Transactional(readOnly = true)
      • public List<Temp> getLiveData() : 리포지토리 단순 위임
    • @Transactional
      • public int getLiveCount(Long id)
        • 실시간 데이터를 가져와 Count하는 service
        • id에 해당 하는 point의 영역에 맞는 실시간 데이터 temp의 개수를 count

검출 객체 로직 개발 (Info)

구현 순서

  • 검출객체 레포지토리 개발
  • 검출객체 서비스 개발

Info (Repository, Service)

  • 라이브로 적재된 데이터(Temp)를 Point와 연관관계를 생성하여 저장하는 데이터
  • 검출객체 레포지토리 (InfoRepository)
    • public void save(Info info) : 연관관계를 생성한 info 저장. em.persist() 사용
    • 해당 info 데이터들을 불러오는건 point의 info List통해 불러옴
  • 검출객체 서비스 (InfoService)
    • Business Logic, Transaction 정의 및 구현
    • DI(Dependency Injection) : 검출객체 레포지토리(InfoRepository)
    • @Transactional
      • public void createInfo(Long id)
        • 연관관계를 생성하여 Info를 저장하는 service
        • id에 해당 하는 point의 영역에 맞는 실시간 데이터 temp와 연관관계를 맺은 Info를 생성하여 저장
        • 추가로 point의 실시간 반영 데이터(count, percentage)도 변경 (변경감지를 통해 저장)

공통 관심사 처리

구현 기능

  • 검출 객체와 장소 매핑

구현 순서

  • Interceptor 개발
  • WebConfig 설정 및 개발
  1. 기능 설명
    • 검출 객체와 장소 매핑
      • Interceptor 사용 (공통 관심사 처리)
      • Client의 서비스 도입 시점(Map, Static 서비스 요청 시)에 새로 누적된 데이터(검출 객체)들에 대해서 장소(Place)에 Mapping 하기 위함 → 연관관계 생성
      • 드론을 통해 축적된 DB의 값을 받아와 연관관계를 만들기 위함
      • 해당 "/", "/statics", "/statics/**" URI들에 대해 적용
  2. 기능 개발
    • custome Interceptor
      • UpdateInterceptor implements HandlerInterceptor
        • 컨트롤러를 실행하기 전에 해당 로직 실행 (preHandle)
        • DI(Dependency Injection) : infoService, pointService
        • preHandle
          • 모든 point들에 대해 최근 업데이트된 시점부터 현재까지 누적된 모든 Temp(검출 데이터)와 연관관계를 생성한 Info 생성 → (누적 분석데이터에 활용) → infoService.createInfo
          • 추가로 point의 최근 업데이트 시간 변경 → pointService.updatePoint
    • Web Configurer
      • 생성한 Interceptor를 Web에 적용
      • DI(Dependency Injection) : updateInterceptor
      • WebConfig
        • public void addInterceptors(InterceptorRegistry registry) : 인터셉터 등록 : updateInterceptor 를 첫번째 순서로, "/", "/statics", "/statics/**" 의 URL들에만 적용되도록 registry에 등록

웹 서비스 개발 (controller)

Map(지도) Service

  • 모든 장소 Floating
  • 장소 정보 제공 ( 실시간 변경 → 측정 인원, 혼잡도)
  • 장소 검색

Statics(분석) Service

  • 실시간 측정 인원 및 변경 인원 제공
  • 누적 데이터 분석 제공
  • HomeController → @Controller
    • 클라이언트와 서버의 동기식 통신을 위한 controller
    • DI : PointService, TempService
    • @ModelAttribute("pointList") : 해당 컨트롤러의 모든 method에 point List가 필요하므로 model에 기본으로 추가
    • @RequestMapping("/")
      • home(Map) 화면 렌더링
      • Map 화면 및 모든 point를 Map화면 에 Floating
      • 각 point의 상세 정보 (장소 명, 혼잡도, 실시간 측정 인원)
    • @GetMapping(path = {"/statics/{id}", "/static"})
      • Statics 화면 렌더링
      • 주어진 path variable에 따른 장소의 분석화면 렌더링 (기본값 포함) → pointService.findByName("세종대 정문") , pointService.findOne(id) 사용
      • 누적 데이터 분석 결과 렌더링 → pointService.getStaticsData(point.getPlace()) 사용
      • 실시간 데이터 분석 결과 렌더링 → tempService.getLiveCount(id) 사용
  • RestHomeController→ @RestController
    • 클라이언트와 서버의 비동기식 통신을 위한 controller
    • 실시간 데이터 변화량을 비동기식으로 제공
    • DI : TempService
    • @GetMapping("/info-live/{id}")
      • 해당 point의 실시간 인원 측정량을 반환
      • tempService.getLiveCount(id) 사용
    • @GetMapping("/infos")
      • 현재 측정된 모든 검출 객체 반환
      • tempService.getLiveData() 사용
      • Json으로 반환 ({’count’ : int , ‘data’ : list})

Project Results (Service)

  • Map Service
  • Statics Service