로펌 그룹웨어법무법인 종합 법률 ERP POC · 개발 진행중
Project Guide & Developer Handoff

변호사 업무의 처음부터 끝까지 —
사건 · 기일 · 정산 · 협업을 하나로

법무법인 전용 종합 법률 ERP의 개념검증(POC)입니다. Claude Design 프로토타입에서 출발해 Next.js · Azure SQL · Microsoft 365(Entra ID · Teams · Outlook · SharePoint · Graph API)로 실제 통합했습니다. 이 문서는 무엇을 만들었고, 어떻게 동작하며, 어디까지 왔는지를 정리한 기능 소개 겸 개발자 인수인계(핸드오프) 자료입니다.

24화면 라우트
40+DB 테이블(Prisma)
5M365 서비스 연동
2Azure 테넌트 구성

01프로젝트 개요#

로펌 그룹웨어는 법무법인의 핵심 업무 — 사건 관리, 기일·불변기한, 의뢰인·당사자, 시간과금(TC/TM), 수납·정산, 전자결재, 사내 협업 — 을 하나의 시스템으로 묶는 종합 법률 ERP입니다. 시중 경쟁 제품을 교차분석해 국내 로펌 실무에 필요한 기능을 설계 근거로 삼았고, Microsoft 365를 이미 쓰는 조직이 "만들 것은 만들고, 빌릴 것은 빌린다"는 원칙으로 붙였습니다.

무엇을 직접 만드나

사건 원장, 담당자·권한, 기일/불변기한, TC/TM 정산 엔진, 수익 분배, 전자결재 원장 — M365로 대체 불가능한 법률 도메인 로직이 제품의 해자입니다.

무엇을 빌리나

문서(SharePoint), 메일·캘린더(Outlook), 화상회의(Teams), 설문(Forms), SSO(Entra ID). 이미 잘 하는 것은 M365를 원장으로 두고 링크·포인터만 소유합니다.

현재 단계

POC(개념검증)입니다. 화면·아키텍처·핵심 읽기 흐름이 실제 클라우드에서 동작하며, 쓰기(CRUD)·SSO·정산 엔진을 단계적으로 채우는 중입니다.

이 문서는 공개용 소개·핸드오프입니다. 자격증명·실서버 주소·개인정보는 담기지 않으며, 운영에 필요한 민감 정보는 비공개 저장소 문서와 Azure Key Vault / Vercel 환경변수에만 존재합니다.

02기술 스택#

레이어채택비고
프레임워크Next.js (App Router) + TypeScript서버 컴포넌트 · 서버 액션 기반. 코드가 유일한 원본
UITailwind CSS + 디자인 토큰Pretendard 폰트, 사건유형·D-day 배지 등 도메인 토큰화
데이터베이스Azure SQL DatabasePrisma ORM(provider = "sqlserver"), 서버리스 티어
ORMPrisma 7 (Query Compiler)드라이버 어댑터 @prisma/adapter-mssql 사용
인증Microsoft Entra IDAuth.js + MSAL, 위임 권한(Authorization Code) 기반
M365 연동Microsoft Graph API@microsoft/microsoft-graph-client, 사용자 위임 토큰
배포Vercelmain push 시 자동 배포 · 함수 리전 서울(icn1) 고정
DNSCloudflare커스텀 도메인 · CNAME(DNS only)로 Vercel 연결

03시스템 아키텍처#

브라우저 → Vercel(Next.js, 서울 리전) → Azure SQL(서울 리전) 이 핵심 경로이고, 여기에 사용자의 위임 토큰으로 Microsoft Graph를 호출해 M365 데이터를 실시간으로 끌어옵니다.

          사용자  ─ Entra ID SSO(위임 권한)
            │ HTTPS
            ▼
      Vercel (Next.js) · icn1(Seoul)   # 함수와 DB를 같은 서울 리전에
       · 서버 컴포넌트 / 서버 액션
       · Prisma 싱글턴
        │  mssql                    │  Graph API(사용자 위임 토큰)
        ▼                           ▼
  Azure SQL (Korea Central)    Microsoft 365
   사건 원장 = 우리가 소유       Outlook · Teams · SharePoint · Forms · Entra ID
                                문서·일정·회의 = M365가 원장, 우리는 링크만 소유
설계 원칙 — "우리가 소유하는 것은 파일이나 메시지가 아니라 링크와 타임라인이다." 문서 실체는 SharePoint, 회의는 Teams, 메일은 Outlook이 원장입니다. 우리 DB는 사건(Matter)을 중심으로 그 링크와 사건 결부 메타데이터를 소유합니다. 이게 없으면 M365를 아무리 잘 붙여도 "파일서버 + 메일함"에 불과해집니다.

04기능 모듈 소개#

상태 범례: 동작 실제 동작 · DB 연동 Azure SQL 읽기 · 개발중 CRUD 진행 · M365 외부 연동 · 화면 화면 우선

사건 관리

사건 홈 DB 연동

수임·수행·보조 담당자, 사건유형 4축(대분류/소분류/심급/자문), 계속기관 3단계(경찰→검찰→법원), 다음 기일 D-day를 한 화면에. 권한에 따라 보이는 사건이 쿼리 레벨에서 걸립니다.

신건 등록 · 사건메모 · 종결사건 화면

사건메모는 단순 로그가 아니라 파일노트(file note) — 징계·손배 방어 증거가 되므로 출처·최종수정자를 원장에 기록합니다.

기일 / 일정

기일관리 DB 연동

기일은 삭제하지 않습니다(소거·중간줄 2종 숨김). 출석인은 담당자와 별개 축이라 외부 변호사·복대리를 담습니다. 결과란은 수기/자동 2칸으로 분리해 파싱봇이 사람 입력을 덮지 않습니다.

불변기한 계산기 화면

민법 §155~161(초일불산입·공휴일 순연)을 적용한 기간 계산. 항소·상고 같은 불변기간은 놓치면 곧 사고이므로 알림에 mandatory 플래그가 처음부터 들어갑니다.

고객 · TC/TM · 수납/정산

고객/당사자 화면

Contact(마스터)/Party(사건 스냅샷) 2계층. 사건 시점의 당사자 정보가 보존되고, 상대방도 레코드라 이해충돌 검사가 성립합니다.

TC/TM 정산 화면

타임시트→정산서 라인은 값 복사 스냅샷. "표시 단가 × 시간 ≠ 청구액" 불일치가 구조적으로 불가능합니다.

수납/정산 · 수익분배 화면

분배 트리거는 청구가 아니라 입금. 실비는 매출이 아닌 통과자금이라 분배 모수에서 제외됩니다.

전자결재 · 그룹웨어

전자결재(결재함) 화면

지출품의·지급·수임리포트·휴가·세금계산서 결재선. 승인 흐름은 Power Automate 연계를 검토합니다.

구성원 디렉토리 DB 연동 CRUD 개발중

법인 구성원·연락처·담당 사건 수. 실제 사용자는 Entra ID와 연동해 채웁니다. 퇴사자는 hard delete가 아니라 soft delete.

업무공유 · 근태/부재 CRUD 개발중

사건 중심 업무 배분, 휴가·출장·재택 신청/승인. 스키마(Task · Absence)가 준비되어 쓰기 기능을 붙이는 중입니다.

게시판 · 차량운행일지 CRUD 개발중

사내 공지, 법인차량 운행 기록(사건 출장·실비 정산 근거). 전용 테이블을 추가해 실제 CRUD로 전환하는 중입니다.

문서함 · 설문/투표 · 화상원격 M365

SharePoint 문서, Microsoft Forms 설문, Teams 회의 — M365가 원장. 앱은 실시간 조회 런처 + 사건 결부 포인터만 소유합니다.

05Microsoft 365 연동#

이미 M365를 쓰는 로펌을 전제로, 잘 하는 것은 빌려 씁니다. 인증은 위임 권한(사용자 대신)이 기본이라 각 사용자는 본인이 볼 수 있는 것에만 접근합니다(앱 전체 권한 아님).

서비스역할연동 상태
Entra IDSSO 로그인 · 사용자 디렉토리앱 등록 완료, 위임 권한 로그인 배선 중
Outlook기일 → 캘린더 단방향 push, 메일 사건 결부Graph 캘린더 연동 구현
Teams화상/원격 회의(온라인 미팅)실 로그인 시 /me/events로 실제 Teams 회의 표시
SharePoint / OneDrive문서 원장실 로그인 시 /me/drive 실시간 조회, 미로그인은 딥링크 런처
Forms설문/투표Forms 바로가기 런처(자체 구현 안 함)
테넌트가 2개인 구조입니다. 구독·Azure SQL은 개인 Azure 테넌트에, M365(Business Basic)와 Entra 앱 등록은 조직 테넌트에 있습니다. 포털 작업 시 어느 테넌트에 로그인했는지를 항상 먼저 확인해야 합니다 — 실제로 한 번 헤맨 지점입니다.

06Graph API 활용#

Graph 호출은 사용자 위임 토큰으로만 합니다. 앱 권한(application permission)을 쓰지 않으므로, 화면에 보이는 M365 데이터는 언제나 "그 사용자가 원래 볼 수 있는 것"으로 제한됩니다.

화면Graph 엔드포인트동작
문서함GET /me/drive/root/childrenOneDrive/SharePoint 파일 실시간 목록, 실패 시 런처 폴백
화상/원격GET /me/events온라인 미팅(Teams) 이벤트만 필터, 참여 링크 제공
기일 → 캘린더POST/PATCH /me/events사건 기일을 Outlook 캘린더로 단방향 push
승인 스코프Calendars.ReadWrite, Files.Read 등 사용자 위임 범위 내에서만 사용합니다. 데모 계정에는 Graph 토큰이 없으므로 M365 화면은 목업/런처로 폴백합니다 — 위임 권한은 진짜 로그인 사용자만 가집니다.

07데이터베이스 — Azure SQL + Prisma#

Matter-centric 설계: 모든 테이블이 사건(Matter)을 중심으로 연결됩니다. 스키마는 40여 개 모델로, 되돌릴 수 없는 설계 결정(스냅샷·값 복사·이력)을 v1에 미리 넣었습니다.

핵심 설계 결정

  • 담당자 4축 — 수임/수행/보조 + 기일 출석인(별개 축)
  • 사건유형 4축 — 대분류·소분류·심급·자문 플래그
  • 스냅샷/값 복사 — 정산 라인·업무보고서는 조인이 아니라 발행 시점 값 복사
  • hard delete 금지 — 사건·기일·구성원은 소프트 삭제/숨김
  • 이벤트 스트림 — MatterActivity로 사건 타임라인을 소유

서버리스 대응

  • Prisma 싱글턴 + 커넥션 풀 상한으로 서버리스 커넥션 고갈 방지
  • 지연 생성 — 빌드가 DB 자격증명을 요구하지 않도록 런타임에만 클라이언트 생성
  • auto-pause 대응 — 무료 티어 DB가 절전에서 깨어나는 30~60초 동안 "깨우는 중" 화면 후 자동 복구
  • 장애 격리 — DB 실패 시 통짜 500 대신 안내 화면으로 대체(사이드바 유지)

SQL Server 제약(Prisma enum 미지원 → String+상수, 인덱스 키 900바이트 상한, 다중 캐스케이드 경로 금지)을 스키마 전체의 작성 규칙으로 삼았습니다.

08권한 모델#

RBAC도 ACL도 아닙니다. 권한의 1차 축은 직급이 아니라 "이 사건의 담당자인가"입니다. 담당(수임·수행·보조 중 하나) 여부가 약 20개 화면의 행 단위 필터를 좌우합니다.

계정 예시(데모)사건 범위금액결재
대표변호사전 사건보임있음
파트너담당건만본인 사건 보임있음
어소시에이트담당건만가려짐없음
회계담당전 사건보임없음
필터를 화면마다 걸면 반드시 구멍이 뚫립니다. 그래서 Prisma 쿼리 레벨의 공통 스코프 헬퍼로 강제하고, 금액은 화면에서 가리는 게 아니라 서버에서 값 자체를 넘기지 않습니다.

09배포 · 인프라#

배포 파이프라인

호스팅
Vercel (main push → 자동 배포)
함수 리전
서울(icn1) — DB와 같은 리전으로 왕복 지연 제거
DNS
Cloudflare (CNAME → Vercel, DNS only)
DB
Azure SQL (Korea Central, 서버리스)

겪고 해결한 함정

  • 리전 불일치 — 함수(미국)↔DB(서울) 왕복 지연 → 함수 리전을 서울로 고정
  • DB 방화벽 — "Azure 서비스 허용" 토글로는 Vercel(외부 클라우드)이 못 붙음 → 별도 규칙 필요
  • 절전 타임아웃 — 무료 티어 auto-pause 첫 접속 실패 → "깨우는 중" 폴링 화면으로 흡수
  • 테넌트 혼동 — SQL은 개인 테넌트, M365는 조직 테넌트

10작업 히스토리#

POC가 만들어진 순서. 각 단계는 배포까지 확인한 뒤 다음으로 넘어갔습니다.

STEP 1 · 완료

프로토타입 → Next.js 전환

Claude Design 산출 HTML을 App Router + TypeScript로 이관. 좌측 사이드바 + 상단 멀티탭 구조 유지. 코드가 유일한 원본.

STEP 2 · 완료

Prisma 스키마 v1 + Azure SQL

Matter-centric 40여 개 모델. 담당자 4축·사건유형 4축·스냅샷·이벤트 스트림 등 되돌릴 수 없는 결정을 v1에 확정.

STEP 3 · 완료

사건·기일·구성원 화면을 DB로 전환

목업을 Azure SQL 읽기로 교체. 권한 스코프를 쿼리 레벨에서 적용. 시드 데이터로 권한별 화면 차이 검증.

STEP 4 · 완료

그룹웨어 3종 + Teams/SharePoint 연동

전자결재·게시판·차량운행일지 화면 추가. 문서함·화상원격을 Graph 실연동으로 업그레이드.

STEP 5 · 완료

운영 안정화 — 리전·방화벽·장애격리

함수 리전 서울 고정, DB 방화벽·절전 대응, DB 장애 시 안내 화면 대체. 커스텀 도메인 연결.

STEP 6 · 진행중

쓰기(CRUD) · SSO · 정산 엔진

그룹웨어 모듈에 생성/수정/삭제를 붙이고, Entra ID 위임 로그인과 TC/TM 정산 엔진을 채우는 단계.

11개발자 핸드오프#

다음 개발자가 맥락을 잃지 않고 이어받도록 하는 인수인계입니다. 이 프로젝트는 공용 PC를 포함해 여러 대를 오가므로 — 맥락은 로컬이 아니라 GitHub(문서 · Issues · git history)에만 남깁니다.

세션 시작 루틴

  1. git pull — 다른 PC 작업 받기
  2. git log --oneline -10 — 마지막 작업 확인
  3. 열린 Issue 확인 = 할 일 목록
  4. 해당 Issue 본문에서 "왜 이렇게 했는지" 복원

환경 세팅 핵심

  • 비밀값은 저장소에 넣지 않음 — Vercel 환경변수 / Key Vault
  • 새 PC는 git 커밋 계정 설정 먼저(안 하면 배포 차단)
  • 스키마 변경은 DATABASE_URLprisma db push
  • 공용 PC였다면 종료 시 로그아웃 + 폴더 삭제

지금 이어서 할 일 (재개 지점)

우선작업메모
P0그룹웨어 CRUD 완성업무공유·근태·게시판·차량·구성원. 게시판·차량은 신규 테이블 필요
P0Entra ID 위임 로그인 배선실 로그인 시 User 매칭 → PermissionSet 적용
P1TC/TM 정산 엔진M365로 대체 불가 — 제품의 해자
P1전자결재 원장스키마에 결재 원장 보강
보안실데이터 전 개발용 임시설정 정리데모 로그인 차단, DB 접근 범위 축소 등 — 상세는 비공개 문서
실서비스 전 반드시 정리해야 하는 개발용 임시 설정이 있습니다. 데모 로그인, 개발 편의를 위한 접근 범위 등 — 구체 항목과 조치 방법은 비공개 저장소의 핸드오프 문서 docs/09-개발자-핸드오프.md 및 GitHub Issues에만 기록되어 있습니다(공개 페이지엔 싣지 않습니다).
핸드오프 파일의 원본은 저장소에 있습니다. 이 페이지는 요약이고, 다음 개발자는 비공개 저장소의 docs/00-개발자-온보딩.md · docs/09-개발자-핸드오프.md와 GitHub Issues에서 전체 맥락(설계 근거·함정 로그·미해결 목록)을 복원합니다.