kmem: 가족을 위한 사진 관리 시스템
부모님의 폰 용량 문제를 해결하기 위해 만든 개인 사진/동영상 관리 시스템
문제 상황
부모님께서 폰에 사진과 동영상을 많이 저장하시다가 용량이 꽉 차서 정리를 시도하셨는데, 그 과정에서 소중한 사진들을 실수로 삭제하셨다. 상심을 많이 하시는 모습을 보고 구글 클라우드 같은 서비스를 권해드렸지만, 새로운 서비스 사용법을 배우는 것을 부담스러워하셨다.j
"아들이 조악하게나마 만들어두면 자식이 만든 건데 쓰는 시늉이라도 하시지 않을까?" 하는 마음으로 시작한 프로젝트다.
기술 스택
Backend
- Go + Gin Framework
- PostgreSQL
- FFmpeg: 비디오 썸네일 생성
- JWT: 토큰 기반 인증
Frontend
- React + TypeScript
- TailwindCSS
Infrastructure
- Docker + Docker Compose
- Ubuntu ZFS: 데이터 백업과 성능
핵심 기능
중복 파일 감지
- SHA256 해싱을 통한 중복 파일 자동 감지
- 파일 업로드 시 스트리밍 방식으로 해시 계산 (메모리 효율성)
- 중복 파일 업로드 시 기존 파일 참조로 저장 공간 절약
썸네일 자동 생성
- 이미지: Go imaging 라이브러리로 3가지 크기 생성 (150x150, 300x300, 800x600)
- 비디오: FFmpeg로 영상 길이의 30% 지점에서 썸네일 추출
- 백그라운드 워커풀(16개 고루틴)로 처리하여 업로드 응답 속도 보장
파일 관리
- 소프트 삭제: 실제 파일은 30일 후 자동 정리
- 파일명 변경, 검색, 필터링 (이미지/비디오 구분)
- 무한 스크롤 방식의 갤러리 인터페이스
캐싱 시스템
- TTL + LRU 조합의 인메모리 캐시 직접 구현
- 사용자별 갤러리 캐시 무효화
- sync.Map으로 동시성 처리
기술적 선택 이유
Go 선택
솔직히 내가 편해서 선택했다. Go를 좋아하는 이유는 동시성 모델이 잘 설계되어 있어서 쓰기 편하기 때문이다. 특히 고루틴을 활용한 백그라운드 작업 처리가 간편했다.
PostgreSQL vs SQLite
이전에 SQLite 사용 시 파일 락 문제를 겪었고, 혼자 쓰는 게 아니라 부모님, 나, 동생까지 4인 가족이 동시에 사용할 가능성을 고려해서 동시 접근에 더 안정적인 PostgreSQL을 선택했다.
SHA256 해싱
처음에는 속도 때문에 MD5를 시도했었는데 혹시나 충돌날까 봐 SHA256으로 바꿨다. 파일 첫 몇 바이트만 해싱하는 것도 고려했지만 결국 정확성을 위해 전체 파일을 해싱했다.
워커 풀 크기
4인 가족 기준으로 인당 4개 스레드면 충분할 것이라고 임의로 16개로 설정했다. 사용하면서 부족하면 늘리고 과하면 줄일 계획이었다.
Ubuntu ZFS
속도와 백업 기능을 위해 선택했다. btrfs의 스냅샷 기능도 고려했지만 ZFS가 더 빠르다는 정보를 바탕으로 결정했다. 원래 BSD 계열 OS 사용을 고려했으나 운영 체제 자체가 익숙하지 않아 Ubuntu ZFS로 타협했다. (당시 Ubuntu ZFS는 완벽 지원이 아닌 실험 단계였음)
FFmpeg
비디오 썸네일 생성에 다른 방법을 몰라서 별 생각 없이 사용했다.
아키텍처 설계
워커 풀 패턴
type Queue struct {
ctx context.Context
list chan item
workers int // 16개 고루틴
}
파일 업로드 플로우
- 파일명 인코딩/검증 (Base64 + URL encoding)
- 스트리밍으로 파일 저장하면서 동시에 SHA256 해시 계산
- 트랜잭션으로 중복 체크 및 DB 저장
- 백그라운드에서 썸네일 생성 대기열에 추가