
대학 역량관리 플랫폼을 개발하면서 가장 먼저 해결해야 했던 문제는 화면이 아니라 데이터 조회 구조였다.
학사 데이터는 학교의 IBM DB2에 저장되어 있었다. 운영 DB를 직접 조회하면 화면 하나를 여는 데 5분 이상이 걸렸다. 수업 등록과 성적 처리를 담당하는 운영 DB에 서비스 조회를 직접 연결하는 구조는 적절하지 않았다.
그래서 학교 데이터를 읽기 전용 PostgreSQL 미러 DB로 동기화하고, 서비스의 모든 조회는 미러에서 수행하도록 설계했다.
왜 스냅샷을 선택했나
변경된 데이터만 반영하는 증분 동기화를 먼저 검토했다. 하지만 학교에서 제공한 데이터는 변경 이력이 없는 View 형태였다. View에는 변경 시점을 알 수 있는 타임스탬프나 변경 로그가 없어 증분 동기화를 적용할 수 없었다.
대신 하루 한 번 전체 데이터를 다시 적재하는 스냅샷 방식을 선택했다. 필요한 학년도만 다시 적재한 뒤 기존 데이터를 교체했다.
구현은 단순했지만 운영은 더 안정적이었다. 같은 데이터를 여러 번 적재해도 항상 동일한 결과를 만들 수 있고(멱등성), 이전 데이터와 새 데이터가 섞이지 않아 정합성을 유지하기도 쉬웠다.
하루 한 번 동기화
동기화는 하루 한 번 배치 작업으로 수행했다. 사용량이 가장 적은 새벽 시간(KST 03시)에 IBM DB2 데이터를 PostgreSQL 미러에 다시 적재해 최신 상태를 유지했다.
실시간 반영은 포기해야 했다. 최신 데이터는 최대 하루 정도 늦게 반영된다. 하지만 학사·역량 데이터는 초 단위 최신성이 필요하지 않아 하루 한 번의 동기화로도 충분했다.
반복되는 집계는 미리 계산한다
미러를 구축했다고 해서 모든 조회가 빨라지는 것은 아니었다. 화면마다 학과 평균이나 전체 분포 같은 집계를 매번 계산하면 여전히 응답 시간이 길어진다.
자주 사용하는 집계는 미리 계산해 캐시에 저장하고, 나머지는 지연 로딩으로 분리했다.
이 구조를 적용한 결과 조회 시간은 5분 이상에서 1초 이내로 줄었고, 같은 화면과 같은 조건에서 체감 성능은 300배 이상 개선됐다.
미러 계층이 만든 변화
읽기 전용 미러는 조회 속도를 높이기 위한 계층이 아니라 운영계와 서비스를 분리하는 계층이었다.
서비스는 항상 미러만 조회하고, 반복되는 집계는 캐시에서 처리했다. 서비스 트래픽이 늘어나도 운영 DB에는 영향이 없었고, 원천 데이터 구조가 바뀌더라도 동기화 계층에서 대부분의 변경을 흡수할 수 있었다.
조회 성능을 바꾼 것은 쿼리가 아니라 읽기 구조였다.
다음에 읽을 글

긴 영상을 한 번에 렌더하지 않은 이유
서버리스 렌더에는 약 10분의 실행 시간 제한이 있다. 긴 영상은 프레임을 청크로 나눠 병렬로 렌더하고 하나로 결합했다. 총 시간은 가장 느린 청크 수준으로 줄었고, 이 병렬성은 렌더를 프레임 번호의 결정적 함수로 설계해 둔 데서 공짜로 얻었다.

예외가 나지 않는 버그가 더 무섭다
마이그레이션에서 가장 기억에 남는 버그는 화면을 깨뜨린 버그가 아니다. 에러 레벨로 쏟아지는 무해한 로그가 진짜 오류를 가렸고, 복제 로직이 새 필드를 몰라 보정값이 조용히 사라졌다. 시끄러운 버그는 스스로를 고발하지만, 조용한 버그는 발견 자체를 늦춘다.

357개 정점을 매 프레임 CPU로 움직이기
본편에서 걷어낸 수치와 공식의 기록. 합성 관절은 랜드마크 평균으로, 가중치는 역거리제곱으로, 회전은 atan2 참조 벡터로 구했다. 자료구조 다섯 개에 지오메트리 전체가 18KB. 규모가 작을 때 단순함은 성능보다 값지다.