본문 바로가기
Backend/Backend 관련 학습 내용

다채널 CCTV 사람 객체 이상행동 감지 및 추적

by pjhcsol 2024. 3. 23.

@PostConstruct 어노테이션을 사용하여 스프링 프레임워크에서 사용되는 초기화 메서드를 지정하였다. 해당 메서드는 빈(Bean)이 생성된 후에 자동으로 호출되어 초기화 작업을 수행하도록 구성했다.

3.4.1.JWT 인증인가 token을 통한 로그인/로그아웃 기능 & 사용자 접근 권한 부여 및 제어

JWT 기반 인증 방식을 사용하여 Client가 Server에게 요청을 보낼 때 HTTP의 Header에 이전에 Server로부터 발급받은 JWT를 담아 보내고, 이를 기반으로 Server가 Client를 식별하는 방식을 뜻한다.

클라이언트에서 패스워드를 통하지 않고 암호화된 token기반으로 로그인/로그아웃 성공 여부를 파악하기 때문에 보안성이 높다.

3.4.1.1.회원가입 및 로그인

회원가입 후 로그인시 DB에 정보가 저장되며 외부 침해에 대한 고려사항을 반영하여 안전한 시스템을 보장하기 위하여 Password 대신 JWT (JSON Web Token) 인증에 필요한 정보들을 암호화시킨 토큰을 발급한다.

3.4.1.1.1 Signature 구현

header의 인코딩 값 + payload의 인코딩 값 + 서버의 key값 을 header에서 정의한 알고리즘으로 암호화한 것. 이 때 서버의 key값이 유출되지 않는 이상 복호화할 수 없다.

3.4.1.1.2 Access token

Client는 JWT를 저장하고, 이후 인증이 필요한 요청마다 Authorization Header에 Access token을 담아 보냄

[' . '로 구분된 세 문자열인 Header, Payload, Signature로 이루어져있다.] Server는 발행했던 token과 Client로부터 받은 token의 일치여부를 확인한다 (인증).

JWT 인증/인가 token을 통한 로그인 기능 구현을 통하여 Signature 구현 및 Access token 발급으로 서버의 key값이 유출되지 않는 이상 복호화 할 수 없다.

3.4.1.2.로그아웃

로그아웃 시에 토큰 블랙리스트 추가를 하게끔 구현함에 따라 토큰 재사용을 방지하여 보안성을 향상시켰다.

3.4.1.3.token 검증

토큰에 대한 보안성을 향상 시키기위한 검증과정

3.4.1.4.재로그인

재 로그인 시에 새로운 토큰 발급과 토큰 유지 기간 초과(강제 로그아웃),사용자 로그아웃 시에 토큰 블랙리스트에 추가되어 토큰 탈취 위험성을 제거하였다.

3.4.1.5.특정 User에게 접근 권한 부여

스프링 시큐리티에서 특정 유저에게 권한을 부여하고 인증 정보를 설정하는 부분이다. 서비스는 사업자나 정부단체에서만 사용가능하며 민간이 사용 불가능하게끔 구현하기 위해 로그인이나 인증이 완료된 후에 Cctv 및 메타데이터 서비스를 사용될 수 있게 구현하였다.

  1. UsernamePasswordAuthenticationToken 생성:
    • **UsernamePasswordAuthenticationToken**은 스프링 시큐리티에서 사용자의 인증 정보를 나타내는 토큰이다.
    • **userName**은 사용자 이름(또는 아이디)을 나타내며, **List.of(new SimpleGrantedAuthority("ROLE_HOST_USER"))**는 해당 사용자에게 부여할 권한을 나타낸다. 여기서는 "ROLE_HOST_USER"라는 권한을 가진다고 설정하였다.
  2. AuthenticationToken 설정:
    • **WebAuthenticationDetails**는 인증 요청에 대한 세부 정보를 나타내는 클래스로, 주로 IP 주소나 세션 ID 등의 정보를 제공한다.
  3. SecurityContextHolder에 인증 정보 설정:
    • **SecurityContextHolder.getContext().setAuthentication(authenticationToken)**을 사용하여 현재 스레드의 **SecurityContext**에 인증 정보를 설정한다. 이를 통해 해당 사용자가 권한을 가진 상태로 애플리케이션 내에서 권한 검사를 수행할 수 있다.
  4. 다음 필터 체인으로 이동:
    • **filterChain.doFilter(request, response)**를 호출하여 다음 필터 체인으로 제어를 넘긴다.

3.4.2. 테스트 환경

데이터베이스 초기화**@PostConstruct**와 @Transactional을 사용하여 테스트 케이스 외에도 실제 DB 접근 테스트를 시행하였다.

INSERT 시 매번 commit, rollback을 명시적으로 관리해 주어야 하기 때문에 일부 로직에서 중복 코드가 발생한다. 이를 JAVA 계열의 서버 개발에 주로 사용되는 Spring 프레임 워크의 @Transactional으로 커밋과 롤백을 하며 데이터 무결성을 유지하며 @PostConstruct 초기화를 사용하여 정의하고, 이를 사용한 클로저 형태로 구현 하여 보다 간편한 개발과 유지보수가 가능하도록 하였다.

3.4.3.서버 주요 서비스 기능

AI에서 받은 이상 행동 MetaData를 통하여 Video영상 정보와 Cctv 정보와 매핑 작업을 통한 key 관리 등을 구현한 메인 기능이다.

Video와 Cctv Entitiy의 Primary Key값인 videoId,cctvId를 사용하여 Foreign Key로 MetaData로 매핑하여 데이터 무결성을 유지하게끔 구현하였다.

3.4.3.1. MetaData

영상/이미지 데이터를 decoding한 문자열 데이터 처리 과정:

Video와 Photo 등의 데이터를 문자열로 변환하여 받기 때문에, 데이터베이스에서 이를 수용하기 위해 @Column 어노테이션을 사용하여 문자열의 길이를 제한하였다. 특히, base64와 같은 긴 문자열을 처리하기 위한 조치로 이를 상한으로 설정하고 있다.

3.4.3.1.1 searchLegendByOptions

searchLegendByOptions 메서드는 MetaData에서 제공되는 기능으로, 사용자에게 입력받은 날짜 기간, CCTV ID, 그리고 이상행동 유형에 따라서 **foundTime**을 기준으로 메타데이터를 검색하여 리스트로 반환하는 기능을 제공한다. 이 메서드는 사용자 편의성을 고려하여 날짜 기간을 입력받지 않거나 CCTV ID, 이상행동 유형을 선택하지 않은 경우에는 각각 null 값으로 처리하여 전체 메타데이터 리스트를 반환한다.

즉, 이 메서드를 사용하면 사용자가 지정한 조건에 따라 메타데이터를 검색하여 결과를 리스트로 반환할 수 있으며, 필요에 따라 다양한 옵션을 선택하여 메타데이터를 조회할 수 있다.

클라이언트로부터 리스트 형태의 요청을 받아 각 요청에 대해 데이터베이스 엔터티의 무결성 조건을 고려하여 파싱을 수행하고, 그 결과를 반환하는 컨트롤러 메서드이다. 클라이언트는 여러 개의 검색 요청을 리스트로 전송하며, 각 요청에는 검색 기간, CCTV ID, 이상 행동 유형 등의 파라미터가 포함된다.

컨트롤러는 리스트로 전송된 각 검색 요청을 순회하면서 요청에 포함된 파라미터를 추출하고, 이를 데이터베이스 엔터티의 무결성을 유지하며 적절히 파싱한다. 이후, 파싱된 조건에 맞게 데이터베이스에서 검색을 수행하고, 검색된 결과를 리스트에 추가한다. 최종적으로 모든 요청에 대한 검색 결과가 담긴 리스트를 클라이언트에 반환한다.

데이터를 파싱하는 부분과 Type을 변환하는 과정은 별도로 구현하여 각 코드의 기능에 맞게 최소한으로 구현함으로써 코드를 간결하고 가독성 있게 유지하는 것을 목표하였다.

3.4.3.2. Cctv 및 나머지 Entitiy

Entitiy의 Id를 데이터베이스 Primary Key로 지정하여 자동 증가 기능을 활용하여 생성되는 값으로 Entitiy가 생성될때 증가해서 입력되도록 설정하였다.

searchCctvsByOptions(CCTV 옵션 검색 로직) searchCctvsByOptions 메서드는 동적으로 생성된 SQL 쿼리와 검색 옵션을 사용하여 CCTV 엔티티를 데이터베이스에서 검색하고 결과를 리스트로 반환한다. 입력된 검색 옵션에 따라 필드가 매치되는 CCTV 엔티티들을 반환하며, 어떤 옵션도 주어지지 않을 경우 모든 CCTV를 반환하게끔 구현하였다.

모든 Test case는 일반적으로 "Given-When-Then" 구조를 따르게 구성하여 어떤 로직에서 문제가 발생하였는지 더욱 명시적이게 작성하였다.

3.4.4.비동기 및 동시성 문제 처리

3.4.4.1. 비동기 (Asynchronous) 처리 과정

REST (Representational State Transfer)API를 사용한 플랫폼 초기의 구현에서는 데이터베이스에 동기 방식으로 접근하여 잠재적으로 성능 저하 문제가 존재했다. 즉, 다수의 유저가 MetaData(비디오와 이미지로 매핑이 되었는 상태)에 데이터베이스 쿼리, 외부 API 호출 등과 같이 I/O 작업을 요청하는 경우를 대비하여 @Async 처리로 비동기 세션과 동기 세션을 각각 할당받아 필요에 따라 선택적으로 사용하도록 구현하였다.

서비스 기능 중 하나의 예시로 readOnly로 변경되는 값이 없지만, 다양한 옵션에 따른 필터링된 값을 처리하는 쿼리 로직에서는 대량의 데이터를 받아와 리스트로 반환해야 한다 이러한 작업에서 비동기 프로세스를 통한 성능 향상을 이끌어냈다.

3.4.4.2. 동시성 (Concurrency) 문제 해결 과정

애플리케이션 레벨의 낙관적 락을 Version을 사용하여 데이터베이스 관리 시스템(DBMS)이 아닌 애플리케이션 코드에서 동시성 제어를 수행하게끔 구현하였다.

**@OptimisticLocking**은 낙관적 락(Optimistic Locking)을 지원하기 위한 JPA(Java Persistence API)에서 제공하는 어노테이션이다. 낙관적 락은 동시에 여러 트랜잭션이 데이터를 읽고 수정하는 경우 충돌을 최소화하기 위해 사용하였다. 이러한 충돌은 트랜잭션이 일어나기 전에 미리 확인되는 방식으로 해결된다.

@Version과 @OptimisticLocking을 사용하여 테이블 동시 참조 시에 발생하는 과정에서 동시성 문제를 해결하기 위해 어트리뷰트 version을 추가 후 @Version 어노테이션을 사용하여 엔터티 버전을 관리하는 방식으로 낙관적 락을 구현하였다. 버전 정보는 데이터가 변경될 때마다 자동으로 증가하며, 엔터티를 업데이트할 때 버전이 일치하는지 확인한다.(애플리케이션 레벨의 낙관적 락 사용)

Java에서는 JPA(Java Persistence API)에서 위 방법을 사용하면 데이터베이스 자체에서는 락을 걸지 않고, 애플리케이션 코드에서 충돌을 검출하고 처리할 수 있다. 이는 동시성을 향상시키면서도 데드락과 같은 문제를 피할 수 있게 구현하였다.

CCTV를 통한 실시간 영상

스트리밍/저장 서버 및 동영상 전송 서버

CCTV를 통한 실시간 영상 스트리밍 서버는 NVR(Network Video Recorder) 장치를 사용하여 구현되었다. NVR은 CCTV와 연결되어 있으며, TCP 기반의 RTSP(Real-Time Streaming Protocol) 프로토콜을 통해 실시간 영상을 수신할 수 있다. 사용자는 "rtsp://userId:password@ip:port/Streaming/channels/stream+channel"와 같은 형식의 주소를 통해 소켓 통신을 설정하여 영상에 접근할 수 있다. 이를 위해 NVR 장치는 외부에서 접근이 가능하도록 포트 포워딩을 설정해야 한다. 포트 포워딩은 라우터나 스위치 등에서 설정할 수 있다. 실시간 스트리밍 서버는 파이썬으로 구현되었으며, OpenCV(Open Source Computer Vision)를 사용하여 초당 30프레임의 영상을 가져온다. 이후 가져온 영상은 mp4 의 h.264 코덱을 사용하여 1분 단위로 저장된다. 저장된 영상은 YYYY/MM/DD/HH/MM의 디렉토리 구조를 따라 관리되어 영상을 쉽게 찾을 수 있다.

동영상 전송 서버는 Spring 프레임워크를 기반으로 구현되었다. 이 서버는 HTTP 통신을 사용하여 영상을 전송한다. 클라이언트는 POST 요청을 통해 JSON 형식의 요청 본문(request body)을 전송하게 된다. 요청 본문에는 다음과 같은 정보가 포함된다: NVR 의 IP 주소, CCTV의 채널, 시작 시간(년/월/일/시간/분), 종료 시간(년/월/일/시간/분).

서버는 받은 요청에 해당하는 시간대의 영상들을 ffmpeg를 사용하여 연결(concat)한다.

아래는 해당 concatenation 관련 코드이다.

Java의 ProcessBuilder을 사용하여 command 라는 명령어를 실행 시킨다. 그리고 해당 프로세스가 끝날때 까지 기다리고, 해당 영상을 반환 하는 형태이다.

만약 CCTV 의 mp4 sub codec 이 h.264가 아닌 경우를 위해

아래 코드처럼 ffmpeg를 사용하여 sub-codec 변환을 h.264로 강제할 수 있다.

연결된 영상은 사용자에게 확인 응답(acknowledgment)을 보내고, 사용자는 다시 GET 요청을 통해 영상을 전달 받을 수 있다. 전송되는 영상은 byte 형식으로 인코딩되어 클라이언트에게 전송된다.

위와 같이 구현된 스트리밍 서버 및 동영상 전송 서버를 통해 CCTV의 실시간 영상을 스트리밍하고, 필요한 시간대의 영상을 전송할 수 있다.@PostConstruct 어노테이션을 사용하여 스프링 프레임워크에서 사용되는 초기화 메서드를 지정하였다. 해당 메서드는 빈(Bean)이 생성된 후에 자동으로 호출되어 초기화 작업을 수행하도록 구성했다.