
🔧 연구용 기술 스택
- GUI: PyQt5 (크로스 플랫폼, 연구용 위젯 풍부)
- 하드웨어 통신: pySerial (Arduino/Teensy와 시리얼 통신)
- AI: PyTorch + CNN (MNIST 분류, 확장 가능)
- 시각화: PyQtGraph (실시간 플롯, 빠른 렌더링)
- 빌드: PyInstaller + .spec 파일
- 데이터: NumPy + 자체 CSV # 연구용 16x16 어레이 제어 시스템
최근에 신소재 연구실에서 잠깐 작업할 일이 생겼다. 16x16 광학 센서 어레이를 이용한 실험 시스템을 만드는 건데, 연구진이 매번 Python 코드를 직접 실행하면서 실험하기엔 너무 번거로워 보였다.
평소에 연구실용 도구를 만드는 걸 좋아해서 제대로 된 GUI 프로그램을 만들어보자고 마음먹었다. 연구자들이 복잡한 코드 신경 안 쓰고 그냥 버튼 클릭으로 실험할 수 있게 하는 게 목표였다.
근데 문제가 있더라. 연구실 컴퓨터마다 Python 환경이 다르고, 매번 패키지 설치하고 경로 설정하고... 다른 연구실에서 협업할 때도 설명서를 따로 써줘야 하는 것도 많고, 새로운 학생이 들어와서 실험하려면 또 환경 세팅을 해야 했다.

그냥 연구진들이 쉽게 쓸 수 있는 실행 파일을 만들어보자 싶어서 시작했다.
요구사항은 연구실 상황에 맞춰 정했다.
Python 설치 없이도 쓸 수 있고, 복잡한 환경 설정 안 해도 되고, 16x16 어레이에서 실시간으로 데이터를 수집하면서 AI 분류 결과를 볼 수 있으면 됐다. 주피터 노트북처럼 매번 코드를 수정하고 실행하고 싶지는 않았다.
연구실 특성상 필요한 기능들
- 16x16 광학 센서 어레이와 시리얼 통신
- 실시간 데이터 수집 및 시각화
- AI 기반 MNIST 숫자 분류
- 실험 파라미터 실시간 조정
- 데이터 저장 및 성능 분석
- 하드웨어 없이도 테스트 가능한 시뮬레이션 모드
아이디어 자체는 복잡하지 않았지만 실제 연구에 쓸 수 있을 만큼 안정적으로 만드는 게 중요했다. 실험 중에 프로그램이 죽으면 데이터를 다 잃어버릴 수도 있거든요...
(유진님 이거 죽었서요..🤔)
PyQt5로 슥슥 만들었다.
문제는 구체적인 기술 요소들을 정하는 거였다. GUI 프레임워크 뭐 쓸지, 모델 저장 형식은 어떻게 할지, 실행 파일 빌드는 어떻게 할지... 이런 것들 고민하는데 시간이 오래 걸렸다.
특히 PyInstaller vs cx_Freeze vs Nuitka 선택에서 고민이 많았다. 각각 장단점이 다른데
결국 PyInstaller로 결정했다. 문서도 많고 PyTorch 같은 라이브러리와 호환성도 검증되어 있어서.
특히 하드웨어 통신 부분이 까다로웠다. Arduino/Teensy와 시리얼 통신으로 16x16 어레이를 제어해야 하는데, 연결이 끊어지거나 데이터 전송 오류가 생기면 실험 전체가 망가질 수 있다.
# 시리얼 통신 안정성을 위한 처리
class SerialManager:
def __init__(self):
self.connection = None
self.reconnect_attempts = 0
def safe_write(self, data):
try:
if self.connection and self.connection.is_open:
self.connection.write(data)
return True
except serial.SerialException:
self.attempt_reconnect()
return False
모델 파일 경로 처리도 연구실 환경에 맞춰 신경써야 했다. 개발할 때는 상대 경로로 models/mnist_cnn_model.pth 이렇게 썼는데, 실행 파일로 만들면 경로가 달라진다. 특히 연구실 컴퓨터들이 OS가 다 달라서...
import sys
import os
def get_resource_path(relative_path):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.dirname(__file__), relative_path)
이런 식으로 경로 처리 함수를 만들어서 해결했다.
완성하고 나서 연구실에서 실제로 테스트해보니까 생각보다 문제가 많았다.
개발 환경에서는 잘 돌아가던 게 실제 실험 장비와 연결하면 여러 오류가 터져나왔다. 특히 PyTorch의 CUDA 설정이 꼬이면서 GPU 가속이 안되고, 시리얼 포트 권한 문제로 하드웨어 통신이 안 되는 경우가 발생했다.
연구실 특성상 생기는 문제들도 있었다.
급하게 .spec 파일을 수정해가면서 연구실 환경에 맞는 설정을 추가했다.
# ArrayControlSystem.spec - 연구실 환경 최적화
a = Analysis(['array_control_system.py'],
pathex=['.'],
binaries=[],
datas=[('models', 'models'),
('mnist_cnn_model.py', '.'),
('configs', 'configs')], # 실험 설정 파일들 포함
hiddenimports=['torch', 'torchvision', 'PyQt5',
'serial', 'pyqtgraph', 'numpy'],
hookspath=[],
runtime_hooks=[],
excludes=['tkinter'], # 불필요한 GUI 라이브러리 제외
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
연구실에서 중요한 건 재현 가능성이었다. 같은 실험을 다른 날, 다른 컴퓨터에서 해도 동일한 결과가 나와야 한다. 그래서 실험 파라미터들을 모두 설정 파일로 저장하고, 실행 파일에 포함시켰다.
사용자 인터페이스는... 솔직히 말하면 처음에는 기능만 돌아가게 만드는 데 집중했다. 연구자들이 쓸 거니까 예쁠 필요는 없다고 생각했는데, 막상 써보니까 가독성이 떨어져서 실험하기 불편할 것 같았다. 그래서 뭐 사용성 높은 걸 가능한 위로 아닌 것을 아래로 내렸다.
연구실 환경에 맞는 UI 요구사항들:
/* 연구용 대시보드 스타일 */
QMainWindow {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #1e3c72, stop:1 #2a5298);
}
QGroupBox {
font-weight: bold;
border: 2px solid #3498db;
border-radius: 8px;
margin: 5px;
padding-top: 10px;
background-color: rgba(255, 255, 255, 0.1);
}
/* 실험 상태에 따른 색상 변화 */
QPushButton[status="connected"] {
background-color: #27ae60;
}
QPushButton[status="disconnected"] {
background-color: #e74c3c;
}
연구실에서 중요한 건 정보의 밀도였다. 한 화면에 최대한 많은 정보를 담으면서도 가독성을 유지해야 했다. 16x16 히트맵, 성능 지표 테이블, 로그 창을 모두 실시간으로 업데이트하면서도 UI가 느려지면 안디는데...
PyInstaller로 실행 파일 만들고, 내 드라이브에 dmg와 exe를 배포했다.
# 연구실별 빌드 스크립트
# macOS (주로 분석용)
pyinstaller --onefile --windowed ArrayControlSystem.spec
# Windows (실험 장비 제어용)
pyinstaller --onefile --windowed ArrayControlSystem.spec
# Linux (서버 환경)
docker run --rm -v $(pwd):/app python:3.9 bash -c "cd /app && pip install -r requirements.txt && pyinstaller ArrayControlSystem.spec"
연구실 특성상 여러 OS가 섞여 있다. 실험 장비는 Windows, 분석은 macOS, 서버는 Linux... 각각 빌드해서 배포해야 했다.
버전 관리는 파일명에 날짜를 넣어ArrayControlSystem_v1.2_20241215.exe 이런 식으로 했다.

파일 크기가 생각보다 컸다. macOS 기준으로 1.9GB나 나왔다.
연구실에서는 파일 크기보다는 안정성이 더 중요했다. 실험 중에 프로그램이 죽으면 몇 시간 분량의 데이터를 잃을 수 있거든. 그래서 파일 크기는 좀 크더라도 모든 의존성을 포함시키기로 했다.
주원인은 PyTorch였다. 연구용이라 GPU 가속이 필요할 수도 있어서 CUDA 버전을 포함시켰는데, 이게 용량을 많이 차지했다.
연구실 환경에 맞춘 최적화:
# 연구용 최적화 설정
# GPU 가속 필요한 경우
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118
# CPU만 쓰는 경우
pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
# 연구용 필수 모듈만 포함
pyinstaller --exclude-module=matplotlib --exclude-module=scipy ArrayControlSystem.spec
실행 속도는 연구 환경에서 치명적이다.
실시간 데이터 수집하면서 AI 분류까지 해야 하는데, 처리가 느리면 데이터 손실이 생길 수 있다.
멀티스레딩으로 해결했다:
프론트엔드는 PyQt5로 구성했다. tkinter도 고려했는데 UI가 너무 단조롭고, Kivy는 모바일 중심이라 데스크톱용으로는 PyQt5가 최적이었다.
AI 모델은 간단한 CNN으로 만들었다. 차후 연구에 맞춰서 바꿔나갈 예정이다ㅏ.
AI를 개발에 활용하는 방법을 좀 더 구체적으로 알게 됐다. 코드 전체를 짜달라고 하는 것보다는 내가 고민하고 있는 기술적 선택지들에 대해 조언을 구하거나, UI 디자인 같은 내가 약한 부분을 보완하는 용도로 쓰는 게 효과적이었다.
그리고 요즘 나온 빌드 도구들이 정
말 좋아졌다는 걸 실감했다. 예전에는 실행 파일 하나 만들려면 복잡한 설정 파일 작성하고 이것저것 해야 했는데, 지금은 몇 줄 명령어면 끝이다.
개발 환경과 실제 사용 환경의 차이도 다시 한번 느꼈다. 혼자 테스트할 때는 문제없던 게 다른 컴퓨터에서는 여러 문제가 생기더라. 특히 라이브러리 의존성이 복잡한 경우 패키징을 더 꼼꼼하게 해야겠다는 생각이 들었다.
처음에 기술 스택 고민하는데 시간을 너무 많이 썼다. tkinter가 익숙해서 만들었는데 너무 단조로웠다. 그냥 빨리 만들어보고 바꾸길 잘했다. 완벽한 설계보다는 일단 돌아가는 걸 만드는 게 중요하다는 걸 또 한 번 느꼈다.
결과적으로는 만족스럽다. 내가 원하던 기능은 다 구현했고, 배포도 잘 됐고, 실제로 다른 사람들도 쓰고 있다. AI 모델을 실제 프로그램으로 만들어서 배포까지 해볼 수 있는 좋은 경험이었다.
array_control_system.py - 메인 GUI 애플리케이션 mnist_cnn_model.py - CNN 모델 정의models/mnist_cnn_model.pth - 훈련된 모델 가중치ArrayControlSystem.spec - PyInstaller 빌드 설정build_all_platforms.yml - GitHub Actions CI/CDrequirements.txt - Python 패키지 의존성README.md - 사용법 및 설치 가이드앞으로는 더 복잡한 AI 모델도 실행 파일로 만들어보고 싶다. 그리고 웹 버전도 만들어서 브라우저에서 바로 쓸 수 있게 해보는 것도 재밌을 것 같다! 🎯