멀티 에이전트 모니터 대시보드 첫 출시와 코드 품질 개선
목차
멀티 에이전트 환경을 운영하다 보면 어느 순간 "지금 뭐가 돌고 있는지" 한눈에 파악이 안 되는 상황이 온다. 에이전트 하나씩 로그 찾아가며 상태 확인하는 건 초반엔 감당이 되는데, 수가 늘어날수록 대응 속도가 떨어진다. Claude Multi-Agent Monitor Dashboard 첫 버전을 내보낸 이유도 거기서 시작됐음.
첫 출시니까 기능을 쌓는 것보다 기존 코드의 안정화와 일관성 확보에 더 집중했다. 신기능은 나중에도 붙일 수 있지만, 기반이 흔들리면 나중에 고치는 게 훨씬 비싸다. 그리고 모니터 대시보드 특성상 이 도구 자체가 신뢰를 잃으면 안 된다. "모니터가 이상한 건지, 실제 에이전트가 이상한 건지"를 구분 못 하는 상황은 없애고 싶었음.
변경 범위가 레이어를 넘어갈 때
이번 작업은 백엔드 로직, 화면(JSP), 쿼리(XML), 스타일 레이어 전반에 걸쳐있었다. 한 레이어만 건드려도 다른 레이어에서 사이드 이펙트가 생기는 경우가 있어서 변경 후마다 전체 플로우를 한 번씩 돌려봤음.
| 레이어 | 주요 작업 |
|---|---|
| 백엔드 로직 | 핵심 처리 로직 정리, 중복 제거 |
| 화면 (JSP) | UI/UX 일관성 확보 |
| 쿼리 (XML) | SQL 가독성 정리 |
| 스타일 | CSS 중복 제거, 네이밍 통일 |
작업 포인트는 크게 네 가지였다. 중복 코드 제거 및 공통화, 엣지 케이스 처리 보강, 로그와 에러 메시지 개선, 불필요한 주석과 죽은 코드 정리. 기능 변경이 없으니 PR만 보면 "뭘 한 거야?" 싶은 커밋인데, 이런 게 쌓여야 시스템이 단단해진다.
로그/에러 메시지 정비가 실제로 효과가 크다. "NullPointerException at line 42"보다 "AgentTask [taskId=abc] 초기화 실패: 의존 서비스 응답 없음"이 새벽 대응 시간을 몇 십 분씩 줄여준다. 이번에 적용한 패턴:
// 전: 컨텍스트 없음
throw new RuntimeException("Failed to initialize");
// 후: 컨텍스트 명시
throw new AgentInitException(
String.format("[AgentTask:%s] 초기화 실패 - 원인: %s", taskId, cause.getMessage()),
cause
);
주석도 정리했다. 코드와 중복되는 주석은 오히려 읽는 흐름을 끊는다. 코드 자체가 의도를 설명하면 주석은 최소화해도 된다. 정말 설명이 필요한 경우, 즉 "왜 이렇게 했는지"가 코드에서 안 보일 때만 남겼음.
// 나쁜 예: 코드와 중복
// i를 1 증가시킨다
i++;
// 나쁜 예: 코드와 불일치 (더 위험함)
// 에러가 발생하면 null 반환
if (error) throw new RuntimeException(error); // 실제론 throw
// 좋은 예: 의도가 코드만으로 불분명한 경우
// 3초 대기: 에이전트 초기화 완료 신호 대기 (race condition 방지)
Thread.sleep(3000);
운영 코드 변경할 때 지키는 것
운영 중인 서비스를 건드릴 때 가장 무서운 건 "멀쩡한 걸 내가 망가뜨리는" 상황이다. 변경 전에 현재 동작을 반드시 파악하고 들어가야 한다. 그래서 테스트를 먼저 가져간다. 변경 후 동일하게 동작하는지 확인하는 것, 특히 빈 값이 들어올 때, 연결이 끊겼을 때, 응답이 지연될 때 같은 엣지 케이스들.
작은 커밋 단위도 강박처럼 지키고 있다. 커밋 하나에 여러 변경을 묶으면 리뷰도 어렵고 문제 추적도 힘들다. 귀찮더라도 쪼개두면, 롤백이 필요할 때 특정 변경만 되돌릴 수 있다. 코드가 바뀌면 관련 주석과 문서도 같이 업데이트하는 것도 여기 포함된다. 나중에 문서와 코드가 따로 노는 상황이 생각보다 자주 벌어진다.
코드를 짤 때 머릿속에 두는 세 가지 기준이 있다.
- 6개월 후의 나: 지금은 다 알고 있으니 읽히는 코드가, 컨텍스트 날아간 상태로 다시 열었을 때 10분 안에 파악이 되는가
- 다음 개발자: 내 의도를 추측하지 않아도 되도록 명시적으로 짰는가
- 새벽 3시 장애 상황: 반수면 상태로 접속했을 때 어디서 왜 터졌는지 빠르게 파악할 수 있는가
마지막 기준이 제일 냉정한 테스트다. 그 상황에서 "영리한 코드"는 최악이다. 읽기 쉬운 코드가 옳다.
| 좋은 코드의 신호 | 나쁜 코드의 신호 |
|---|---|
| 읽으면 의도가 바로 보임 | 주석 없으면 해석 불가 |
| 변경이 한 곳에만 영향 | 한 곳 바꾸면 여러 곳 수정 필요 |
| 테스트 작성이 자연스러움 | 테스트하려면 구조를 먼저 바꿔야 함 |
| 에러 메시지가 문제를 설명함 | 로그 보고도 코드 다시 열어야 함 |
이런 작업들은 당장 티가 안 난다. PR 올려도 "기능 추가 없음"이라 리뷰도 대충 넘어가기 쉽다. 그런데 6개월, 1년 쌓이면 차이가 생긴다. 새 기능 붙이는 속도, 버그 수정 시간, 온보딩 비용 전부 여기에 영향받는다. 디버깅 시간이 줄어드는 건 당장 숫자로 안 잡히지만, 결국 팀 전체의 생산성이 거기서 나온다.
댓글 0
첫 댓글 달아줘.