“자습실 라즈베리파이 CCTV 제작 및 운영”이라는 거창한 제목을 설정했다. CCTV를 사는 게 아니라 제작한다고? 라즈베리파이를 잘 활용하면 가능하다.
일단 내가 CCTV를 만드려고 하는 목적부터 정리해보자.
자습실 CCTV 제작 목적
예로부터 학교는 도둑들 천지였다. 아니, 정확히 말하자면, 도둑은 아니다. 조금 심한 말 같다. 하지만 도둑질을 시작하는 학생들이 생긴다. 그것도 많이.
서로에 대한 감시 느낌이라 교사도, 학생도 CCTV에 대한 거부감은 있다. 하지만 작년부터 였나? 하도 분실물이 생기니 그냥 CCTV를 설치해 달라는 의견이 나왔다.
그냥 막 설치한 것은 아니다. 괜히 기성품 사서 설치했다가 개인정보니 뭐니 걸리면 피곤해진다. 동의서야 당연히 받겠지만, 강압적으로 동의를 했다는 소리가 나올 수도 있고.
아무튼, 그래서 직접 만들어보기로 했다. 딱 특별반 한정 사용하는 CCTV를 말이다.
사실 진짜 목적은 따로 있었다. 특별반 학생들의 자습실 자습 태도 관리. 그리고 힘든 고3 생활의 일종의 이벤트 같은 목적도 있었다.

CCTV 작동 방식 정리
라즈베리파이는 작은 컴퓨터와 같다. 여기에 코드를 짜 넣고 실행하면 그게 돌아간다. 일반 컴퓨터에서 할 수 있는 것도 가능하고, 심지어 카메라나 마이크, 기타 외부 기기의 제어가 용이하다.
물론 말이 용이하다는 거지, 쉽다는 뜻은 아니다. 나도 내가 이것저것 만들고는 있지만, 사실 이게 어떻게 돌아가는지는 모른다. 내가 할 일은 코드를 짜는 것이 아니다. 어떻게 작동하는지 잘 구상하고 잘 설명하는 일 뿐이다.
코드는 그럼 어떻게 되는 거냐고? 설명만 잘 하면, 그리고 대략적으로 코드가 어떻게 실행되는지만 알면 된다. 나머지는 ChatGPT가 다 해준다.
나는 자습실 CCTV 작동 방식을 이렇게 정했다.
- 라즈베리파이와 파이카메라를 사용하여 영상을 녹화하고 저장한다.
- 저장되는 위치는 구글 드라이브로 연동시킨 폴더로, 파일이 저장되면 구글 드라이브에서 확인할 수 있도록 한다.
- 하나의 영상은 20분 단위로 끊어 저장한다.
- 각 영상의 해상도는 카메라 기능상 최대치로 설정하되, framerate은 5로 하여 용량을 조절한다.
일단 추후에 학생이 직접 만들어볼 수 있도록 해야 하기에, 이런저런 기기 없이 라즈베리파이 1대와 파이카메라 만을 사용하기로 했다. 라즈베리파이 가격이 10만원 정도이며, 파이카메라는 3~4만원 정도.
하나의 CCTV를 구성하는데 들어가는 비용은 15만원 정도이다. 케이스나 전원 아답터도 필요하니깐.
사실 CCTV를 하나 만들어둔 것이 있긴 했다. 유튜브 스트리밍을 통해 송출하는 방식인데, 학교 와이파이 속도가 느려서 자꾸 에러가 나고 끊겼다.
그래서 이번에는 아예 영상 파일을 저장하고 구글 드라이브에 올려버리기로 했다. 이렇게 하면 실시간 확인은 안 되겠지만, 30분 이전 상황을 끊김 없이 볼 수 있다.
하나의 영상은 20분 단위로 끊기로 했고, framerate은 5로 조정했다. 가뜩이나 느리고 불안정한 무선망이니 업로드 용량을 너무 크게 두면 문제가 생길 듯 했다. (실제로 문제가 있었다)
그리고 이걸 어떻게 했는냐…
ChatGPT에다가 잘 설명했다. 마지막으로 마법의 요청문. “라즈베리파이로 이렇게 작동하는 CCTV의 코드를 짜줘.”

약간의 어려움
이러고 그냥 딱 만들어지면, 정말 이제 프로그래머는 필요가 없어진 시대가 된 것이리라. 하지만 세상 일은 그렇게 돌아가지 않지.
당연히 수많은 오류가 뜬다. 그리고 그냥 프로그램 구동 코드만 받아서 실행하면 되는 것이 아니다. 그걸 돌리기 위해 필요한 라이브러리나 프로그램을 함께 설치해야 한다.
이걸 다 제대로 했음에도 뜨는 오류들. 카메라가 작동이 되질 않거나, 실행 상황 코드가 무한 반복된다거나, 아무튼 다양한 문제가 생겼다.
어떡하지?
뭘 어떡하는가. 원래 이런 것인데. 되지 않으면 될 때까지 상황을 구체적으로 설명하며 ChatGPT에게 묻는다. 또는 그냥 에러 문구를 복붙하여 물어본다.
사람이라면 짜증을 낼 법도 한데, 이 녀석은 그렇지 않다. 어떻게든 문제를 해결하기 위해 자신의 머리를 굴리고 답을 내놓는 모습이 기특하다.
그리고 이번 작업을 진행하면서 구글 api에 대해서도 조금 알게 되었다. (말이 조금이지 아예 모름) 구글 드라이브에 파일을 자동으로 넣으려면 필요한 절차가 조금 있었다. 무슨 json 파일을 받는다던지 하는.
용량 제한도 있다고 하는데, 이것도 그냥 ChatGPT에게 물어봐서 일정 기간이 지난 것들은 드라이브에서 지우는 방향으로 해결했다.
다만, 잘 돌아간 이야기만 하는데, 이거 하나 만들고 고치고 다시 만드는 일에 한 달 정도 걸린 듯 하다. 정확히 말하면 한 달은 아니고, 그냥 이 주 넘게… 아무튼 오류가 생각보다 많다는 의미이다.
CCTV 구동 모습
일단 잘 돌아가는 걸 확인했다. 인터넷 환경 때문인지 멀쩡히 돌아가다가 멈추기도 하고, 라즈베리파이가 꺼지기도 했다. 하지만 그때마다 물어보면서 코드를 수정했고, 이제는 좀 제대로 돌아가는 듯 싶다.
그리고 아래 화면이 바로 그 화면이다.


잘 보면 알겠지만, 영상에는 CPU의 온도와 램 사용량, 그리고 날짜와 시간이 나오도록 했다. 촬영하면서 인코딩 작업을 한 번 거쳐야 하는 일이다. 이것도 물어봐서 넣었다.
여기까지 쓰고 보니 조금 기억해야 할 사안이 있었는데, 이건 지난 번 포스팅의 주제였던 출석관리 시스템을 구성할 때도 적용했던 사안이었다. 구글 드라이브의 특정 폴더에 뭔가를 업로딩을 하려면, 해당 폴더의 주소를 당연히 알고 프로그램에다 넣어야 한다. 거기에 추가적으로, 해당 폴더를 공유해야 한다. 누구한테? 바로 그 API 사용자에게.
여기가 조금 특이하긴 했다. 내 계정으로 드라이브를 사용하는 것인데 별도의 권한 부여가 필요하단 말. API를 활용할 때는 그냥 그런가보다 했다. 일반적인 계정 사용과는 다른 점이 뭔가가 있겠지.
아무튼, 폴더 공유를 하고 이메일 주소를 넣어 추가한 뒤, 등록 권한을 줘야 하니깐 “편집자” 권한을 부여했다.
업로드 잘~ 된다, 이제는.
사용한 파이썬 코드 살짝 공개
이건 뭐, 내가 짠 코드가 아니기에 굳이 공개를 해야 하나 싶다. 그래도 내가 ai에게 지시해서 만들어진 것이니 정보 부분을 제외하고 한 번 공개하려 한다.
이걸 그대로 쓸 분은 없을 것이고, 그냥 이런 식으로 코드가 짜인다는 것만 알아주시면 좋겠다.
import time
import psutil
from picamera2 import Picamera2, MappedArray, libcamera
from libcamera import Transform
import cv2
import os
from datetime import datetime, timedelta
def add_overlay(request):
with MappedArray(request, "main") as m:
overlay = m.array.copy()
current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
cpu_temp = psutil.sensors_temperatures().get('cpu_thermal', [])[0].current
ram_usage = psutil.virtual_memory().percent
text = f"Time: {current_time} | CPU Temp: {cpu_temp:.1f}C | RAM Usage: {ram_usage}%"
cv2.putText(overlay, text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
alpha = 0.6
cv2.addWeighted(overlay, alpha, m.array, 1 - alpha, 0, m.array)
def record_video_with_overlay(output_file, duration, framerate=5):
picam2 = Picamera2()
try:
video_config = picam2.create_video_configuration(
main={"size": (1920, 1080), "format": "RGB888"},
controls={"FrameRate": framerate},
transform=Transform(rotation=0)
)
picam2.configure(video_config)
picam2.post_callback = add_overlay
picam2.start_and_record_video(output_file)
time.sleep(duration)
picam2.stop_recording()
except Exception as e:
print(f"Recording failed: {e}")
finally:
picam2.close()
def ensure_directories():
os.makedirs('/home/pi/gdrive/recordings', exist_ok=True)
if __name__ == "__main__":
ensure_directories()
while True:
current_time = datetime.now()
if 7 <= current_time.hour < 24:
timestamp = current_time.strftime('%Y_%m_%d_%H%M')
output_file = f'/home/pi/gdrive/recordings/recording_{timestamp}.h264'
# 20분(1200초) 동안 녹화
duration = 1200
try:
record_video_with_overlay(output_file, duration, framerate=5)
except Exception as e:
print(f"Recording failed: {e}")
else:
print("녹화 시간이 아닙니다. 대기 중...")
time.sleep(600)
근데 하도 만들었다가 수정과 지우기, 재작성을 반복하다 보니, 이 메인 파일에는 가려야 할 정보는 딱히 없다.
구글 드라이브를 마운트하기 전에, 그냥 업로드할 때는 무슨 중요 키 같은 것을 막 넣고 했었다. 이 코드에는 그런 건 없다. 그냥 마운트해서 구글 드라이브를 하나의 폴더처럼 만들어 뒀기 때문이다.
코드는 이것만 있는 것은 아니다. 참고로 이건 영상 촬영하고 특정 폴더에 저장하는 것 까지고, 저장한 내용을 어떻게 하고 뭘 하고 하는 건 별도의 파일이 있다.
정확하지는 않지만 서너 개의 파일이 필요했던 것 같다. 라즈베리파이에 전원이 들어오면 바로 구글 드라이브를 rclone으로 마운트한다. 이 촬영 코드를 실행하는 것도 별도의 service로 만들었다.
아무튼, 이게 뭔지는 모른다. 대략적으로 영어 단어와 구조를 보고 파악할 뿐.
위쪽에서 함수를 만든다. 그걸 아래쪽에서 프로그램을 구동할 때 활용한다. 적절히.
가끔 값을 바꿔보면, 다른 화면이 나타나거나 한다. 필요한 만큼 찾아서 바꾼다. 모르겠으면 또 물어본다.
마무리하기
이렇게 오늘은 지난 몇 주간 끙끙대며 만든 “자습실 라즈베리파이 CCTV 제작 및 운영”에 관한 글을 올려 보았다. 구체적인 제작기는 없다. 문제가 생기면 바로 해결해야지, 그걸 또 기록하고 있을 여유나 이유가 없기 때문이다.
그래도 일단 몇 시간 제대로 돌아간다.
음, 사실 오래 작동한 건 아니다. 몇 시간 작동하다가 멈춘 적도 있다. 그러면 또 뜯어 고치고 코드 수정하고 하는데, 이번에는 잘 될까.
잘 될 것 같다. 다른 때보다 확실히 안정적으로 파일이 올라온다.