다섯 주쯤 혼자 만든 업무 시스템 이야기입니다. 자랑할 결과물이 있어서가 아니라, 설계를 한 번 크게 뒤집으면서 배운 게 있어서 적어둡니다.
들어가며
장애인 자립생활센터 쪽 일을 합니다. 결재는 종이, 근태는 엑셀, 급여는 또 다른 엑셀, 이용자 관리는 담당자 머릿속에 있는 곳이 흔합니다. 시중 그룹웨어는 이 판의 서류 양식이나 보조금 정산 흐름을 모르고요. 그래서 직접 만들기 시작했습니다.
1. 처음엔 정석대로 짰습니다
Next.js + NestJS 모노레포, PostgreSQL 16, Row-Level Security로 기관별 데이터 격리, Azure 배포, 로그인은 Entra ID. 멀티테넌트 SaaS 교과서 그대로입니다.
RLS 격리 실증 테스트도 짰습니다. 세션 변수가 없으면 아무 행도 안 보인다는 걸 확인하고 커밋 찍었을 때는 꽤 뿌듯했습니다.
2. 한 달이 되기 전에 그걸 다 지웠습니다
이유는 한 줄입니다. 장애 유형과 등급은 개인정보보호법상 민감정보(건강에 관한 정보)에 해당할 가능성이 높습니다.
그러니까 제 서버에 남의 기관 이용자 수백 명의 장애 등급이 평문으로 쌓입니다. 기술적으로는 RLS로 막아뒀으니 괜찮다고 스스로 설득할 수 있었습니다. 다만 법적으로는 제가 수탁자가 되고, 사고가 나면 책임의 한 축이 될 가능성이 크다고 봤습니다.
보통은 일단 출시하고 나중에 고치라는 게 맞는 조언인데, 이건 나중에 못 고치는 종류입니다. 그때는 데이터가 이미 제 서버에 있으니까요.
그래서 정본 데이터를 각 기관 PC에 두는 로컬 설치형으로 갈아엎었습니다. Electron 셸 안에 기존 서버를 그대로 얹었습니다. PostgreSQL은 SQLite로 내렸고 RLS는 지웠습니다. 클라우드 백업은 종단간 암호화를 건 유료 옵션으로 빼서, 저는 암호문만 보관합니다. 기관 한 곳이 설치 하나니까 논리적 격리를 물리적 격리가 대신합니다.
정확히는 중간에 한 번 더 흔들렸습니다. 8월 중순에 그래도 서버 한 대를 우리가 운영하는 게 맞다고 결론 냈다가, 사흘 뒤에 그 결론을 다시 뒤집었습니다.
Next.js와 NestJS가 들어 있던 디렉토리는 통째로 지웠습니다. 두 벌을 유지하는 순간 둘 다 썩습니다.
3. 남은 것
데이터를 어디에 둘지는 기능이 아니라 구조입니다. 나중에 바꾸려면 이미 남의 개인정보를 손에 쥔 상태에서 바꿔야 하니까, 사실상 못 바꿉니다.
되돌아보면 제일 비쌌던 건 지운 코드가 아니라 이 결정을 한 달 가까이 미룬 시간이었습니다. 민감정보라는 건 첫날에도 알고 있었는데, 교과서적인 SaaS 구조가 너무 당연해 보여서 의심을 안 했습니다.
RLS를 걷어낸 대가는 그다음에 따로 치렀습니다. 그건 나중에 적겠습니다.