Joyuno's Dev Blog

2026-04-13 (월) 개발일지

dev-blog

오늘의 개발 회고: 다중 프로젝트 병행과 권한 시스템 정비

오늘은 정말 여러 프로젝트를 왔다갔다 하면서 작업한 하루였네요. 아침부터 저녁까지 클로드 CLI 세션을 보니 총 6개 프로젝트를 건드렸더라고요. 각 프로젝트마다 다른 문제를 해결해야 했는데, 그중에서도 소명처리시스템의 권한 시스템 정비 작업이 가장 기억에 남네요.

옵시디언 개발일지 로그 수집 문제 해결

아침에 옵시디언 정리 프로젝트를 열었는데, 개발일지 생성 스크립트가 최근 날짜의 클로드 세션 로그를 못 읽어오고 있더라고요. 4월 10일은 commands는 12개인데 sessions가 0, 4월 12일은 아예 모든 게 0으로 나오는 이상한 상황이었어요.

문제 원인을 찾아보니, Claude Code가 최근에 세션 로그 저장 구조를 변경했더라고요. collect_logs.py의 _collect_sessions() 메서드가 project_dir.glob("*.jsonl")만 검색하는데, 최근 로그는 subagents/ 하위 디렉토리에 저장되고 있었어요.

해결 방법은 간단했어요. glob("**/*.jsonl")로 변경해서 하위 디렉토리까지 재귀적으로 탐색하도록 수정했죠. _collect_projects()도 동일하게 수정했고요.

배운 점: 외부 도구의 업데이트는 항상 우리 코드에 영향을 미칠 수 있다는 걸 다시 한번 깨달았어요. 특히 파일 시스템 구조를 의존하는 스크립트는 더 주의해야겠어요.

소명처리시스템의 권한 시스템 대정비

오늘 가장 많은 시간을 투자한 작업이에요. 사용자들이 계속 “테넌트 관리가 안 된다”, “분석가가 티켓을 못 본다”는 문제를 제기하셔서 본격적으로 파고들었어요.

문제 1: 테넌트 스위처의 쿼리 키 충돌

TenantManagementPage.tsx와 TenantSwitcher/index.tsx가 둘 다 ['tenants']라는 동일한 쿼리 키를 사용하면서 충돌이 발생했어요. 전자는 { items: [...], total: N } 형식을, 후자는 [...] 배열 형식을 반환해서 tenants.find is not a function 에러가 뜨는 거였죠.

해결: TenantManagementPage.tsx의 쿼리 키를 ['tenants', 'management']로 변경해서 네임스페이스를 분리했어요.

문제 2: 백엔드 권한 매트릭스 일관성 부재

백엔드 API 엔드포인트들의 require_role 설정이 각기 달라서 혼란이 있었어요. 특히 UserRole.ADMIN이 예전 권한 체계의 잔재로 남아있었는데, 현재는 TENANT_ADMIN으로 통일해야 하는 상황이었죠.

해결: 모든 백엔드 API 파일(21개!)에서 UserRole.ADMIN을 UserRole.TENANT_ADMIN으로 일괄 교체했어요. 중복이 생기는 부분([UserRole.TENANT_ADMIN, UserRole.TENANT_ADMIN])도 정리했구요.

문제 3: 대시보드의 멀티테넌트 지원 누락

대시보드 쿼리들이 select(Alert.username, ...) 같은 column-level 쿼리를 사용해서 ORM event hook이 tenant filter를 적용하지 못하고 있었어요.

해결: dashboard.py에 _tenant_filter 헬퍼 함수를 추가하고 모든 엔드포인트에 적용했어요. 이제 current_user.tenant_id 기준으로 데이터가 필터링되죠.

배운 점: 권한 시스템은 한 번 설계해놓고 끝나는 게 아니라 지속적으로 점검하고 정비해야 한다는 걸 뼈저리게 느꼈어요. 특히 역할이 추가/변경될 때는 프론트엔드, 백엔드, 데이터베이스 스키마를 모두 동기화해야 한다는 걸 잊지 말아야겠어요.

사주 앱의 프론트엔드-백엔드 불일치 해결

사주 앱에서는 정말 기본적인 불일치 문제들이 속출했어요. 사용자가 “프로필 저장이 실패한다”고 해서 파보니…

문제 1: 비밀번호 길이 불일치

프론트엔드에서는 6자 이상으로 유효성 검사를 하는데, 백엔드는 8자 이상을 요구하더라고요. 사용자는 6~7자 비밀번호로 가입하면 프론트에서는 성공처럼 보이지만 백엔드에서 422 에러가 나는 이상한 상황이었죠.

문제 2: 성별 값 불일치

프론트엔드에서 '여성'/'남성' (한글)으로 전송하지만, 백엔드 GenderEnum은 'female'/'male' (영문)만 허용해서 422 에러가 발생했어요.

문제 3: Mock 데이터 의존성

Pulse로 개발하다 보니 여러 에이전트가 작업하면서 인터페이스 불일치가 생겼어요. profile_setup_screen.dart에서는 PersonalityTraitData 클래스를 사용하는데, personality_toggle.dart에는 해당 클래스도 파라미터도 없었죠.

해결: 모든 mock 데이터를 실제 API 호출로 대체하고, 인터페이스를 일치시켰어요. Pulse에 “계약서(contract)” 작성 단계를 강화해서 이런 불일치를 미리 방지할 수 있도록 개선도 했구요.

배운 점: AI 에이전트 기반 개발에서는 인터페이스 정의가 더욱 중요하다는 걸 깨달았어요. 각 에이전트가 독립적으로 작업하더라도 공통 계약서를 따르도록 해야겠어요.

Pulse 프로젝트의 토큰 최적화

오늘 Claude 토큰 사용량이 갑자기 폭증하는 걸 발견했어요. 분석해보니…

문제 원인:

  1. 여러 플러그인이 동시에 활성화되어 있어서 시스템 프롬프트가 너무 커졌어요
  2. Pulse가 에이전트를 많이 생성할수록 메인 컨텍스트에 모든 결과가 누적되면서 토큰이 폭발했어요
  3. 불필요한 MCP 서버들이 실행 중이었어요

해결:

  1. ~/.claude/settings.json에서 불필요한 플러그인 비활성화
  2. OMC(oh-my-claudecode) 비활성화 (DISABLE_OMC=1)
  3. Pulse에 “파일 기반 통신” 규칙 추가 - 에이전트 결과를 컨텍스트가 아닌 파일로 교환
  4. 세션 분리 전략 - 3~5 Pulse마다 state.json 저장 후 새 세션 시작

배운 점: AI 도구를 사용할 때는 토큰 비용을 항상 의식해야 해요. 특히 자동화된 에이전트 시스템은 생각보다 많은 토큰을 소모할 수 있으니, 주기적으로 점검하고 최적화하는 습관이 필요하겠어요.

오늘의 한줄

“다중 프로젝트 병행은 컨텍스트 스위칭 비용이 크지만, 권한 시스템 같은 근본적인 문제를 해결할 때는 집중적인 파고듦이 필요하다.”