요약
망남마을협동조합 홈페이지에 운영진용 백엔드(월별 회계, 회의록 기록·보관, 주요 문서 보관)와 주민용 공개 페이지(경영공시, 사업 실적)를 하루 만에 붙였다. 새 서버를 사지도, 새 비밀번호를 만들지도 않았다. 이미 돌고 있는 사교원 자체 서버의 API 프로그램(app.py)에 확장 모듈 한 파일을 끼워 넣는 방식을 택했고, 서버 반영은 관리자가 SSH에서 한 줄만 붙여 넣으면 끝나게 스크립트로 만들었다.
핵심은 “무엇을 공개하고 무엇을 감출지”를 코드로 못 박은 것이다. 전표 한 장 한 장은 절대 밖으로 나가지 않고, 운영진이 “공개”로 표시한 달의 수입·지출 합계만 실적 페이지에 나간다. 회의록도 결정 사항만 공시되고 논의 경과와 참석자는 남지 않는다.
왜 이 기술인가
협동조합기본법은 정관·규약, 총회·이사회 활동, 사업결산과 사업결과를 경영공시하도록 정한다. 작은 마을조합이 이걸 따로 게시판 사이트를 빌려 하면 비용과 관리 부담이 늘고, 엑셀·카톡에 흩어진 회계와 회의록은 결산 때마다 다시 모아야 한다. 홈페이지에 운영 기록을 두고 그중 공개할 것만 버튼 하나로 내보내면, 기록과 공시가 한 흐름이 된다.
기술적으로는 “서버를 늘리지 않는다”가 원칙이었다. 사교원의 관계 사이트 열 곳이 서버 하나(가비아, FastAPI + SQLite)를 함께 쓰고 있고, 그 서버의 원본 코드는 두 저장소에 미러돼 있다. 원본을 고치면 동기화 부담이 생기므로, 원본은 그대로 두고 옆에 모듈을 붙이는 구조가 유지비가 가장 낮다.
단계별 따라하기
1. 기존 서버가 무엇을 빌려줄 수 있는지 먼저 읽는다
서버 코드를 gh api repos/<owner>/<repo>/contents/app.py로 내려받아 세 가지를 찾는다. DB 연결 함수(db()), 관리자 판별 함수(admin_ok(request, key) — 관리자 키 또는 통합계정 세션), 유틸(now_iso, new_id, s). 이 셋을 빌리면 새 DB·새 로그인이 필요 없다.
2. 모듈은 install(app, …) 하나만 내보낸다
def install(app, *, db, admin_ok, current_user, now_iso, new_id, s, db_path=""):
init_tables(db) # CREATE TABLE IF NOT EXISTS mn_ledger, mn_meetings, …
r = APIRouter(prefix="/api/mangnam")
... # 관리자 라우트 + public/ 라우트
app.include_router(r)서버 쪽 app.py 끝에는 세 줄만 들어간다(from mangnam_api import install; install(app, db=db, …)). 모듈 파일이 없으면 ImportError를 잡아 기존 기능만 그대로 돌게 한다.
3. 데이터 모델은 “운영에서 실제 쓰는 단어”로
- 회계 전표: 일자·수입/지출·항목(계정과목)·사업(마을식당/잠자리/마을학교…)·적요·금액·거래처·결제방법·증빙·메모
- 월 상태: 마감 여부(마감하면 전표 수정이 막힘, 해제 가능), 공개 여부, 마감 소견
- 회의록: 종류(총회/이사회/운영회의)·안건·논의 내용·결정 사항·후속 조치·첨부·공개 여부
- 문서: 분류(정관·규약/등기·인허가/계약서/보고서…)·문서 일자·파일 또는 외부 링크·태그
- 게시물: 경영공시/실적 구분·분류·연도·기간·제목·요약·본문·지표(숫자 카드)·첨부·초안/게시
4. 공개 범위는 엔드포인트를 따로 판다
공개용은 public/ 아래에 모으고, DB 행을 통째로 내보내지 않고 필드를 골라 내보낸다. 첨부 파일은 “게시된 글이 참조하는 동안만” 공개로 바꾸고, 게시를 내리면 자동으로 운영진 전용으로 돌아온다.
5. 로컬에서 서버와 화면을 함께 돌려 본다
모듈에 단독 실행 모드를 넣어 python server/mangnam_api.py로 띄우고, 관리자 키 하나로 인증한다. 전 엔드포인트를 훑는 스모크 테스트(마감 후 수정 409, 비공개 파일 401, 게시 해제 후 파일 회수 등)를 통과시킨 뒤, 프론트 개발 서버에서 로그인→입력→게시→공개 페이지 노출까지 브라우저로 눌러 본다.
6. 서버 반영은 한 줄 스크립트로 넘긴다
curl -fsSL https://raw.githubusercontent.com/deka2026/mangnam-coop/main/server/install-on-server.sh | sudo bash모듈을 내려받고, app.py에 include 블록이 없으면 넣고, 서비스를 재시작해 /api/mangnam/health를 확인한다. 다시 실행하면 모듈만 갱신된다. 정적 화면은 기존 절차(deploy-www.sh)로 재배치한다.
교훈
- 원본을 고치지 말고 옆에 붙여라. 여러 사이트가 한 서버를 나눠 쓰면, 사이트별 기능은 사이트 저장소에 두고 서버는 “빌려주는 쪽”으로만 남긴다. 반영 스크립트가 재실행 가능(idempotent)해야 관리자가 겁내지 않는다.
- 공개 범위를 코드로 정하라. “합계만 공개”, “결정 사항만 공시” 같은 규칙은 화면이 아니라 서버의 공개 엔드포인트가 지켜야 한다. 화면은 언제든 바뀐다.
- 서버가 아직 없을 때의 화면을 먼저 생각하라. API가 404를 주면 오류 대신 “아직 게시된 자료가 없습니다”로 보이게 해 두면, 정적 화면과 서버 반영의 순서가 어긋나도 사이트가 깨지지 않는다.
- 개발 도구의 함정 두 가지. 개발 서버가 켜진 채 프로덕션 빌드를 돌리면 캐시가 덮여 개발 서버가 죽는다. 윈도 임시 폴더는 경로가 240자를 넘어 SQLite가 열리지 않는다 — 짧은 경로를 쓴다.
참고
- 코드:
deka2026/mangnam-coop—server/mangnam_api.py(모듈),server/install-on-server.sh(반영),server/README.md(설계),app/admin/*(관리 화면),app/disclosure·app/performance(공개 페이지) - 서버 구조: 핸드오버-2026-08-19-가비아-자체서버-구축 (Caddy →
/api/*→ FastAPI → SQLite) - 앞선 사례: 서버-없는-마을-홈페이지에-신청접수-기능-만들기 — 신청 접수를 같은 서버로 옮긴 경위
- 협동조합기본법 제49조의2(경영공시) — 공시 항목의 근거
- 문서 정리 = 데카(deka2026). 공은 사람에게, 노동은 정직하게.