개발 slecs

다중 시스템 UI와 SQL 분석에 에러 처리까지 구현한 과정

목차

기능 명세 나오고 나서 바로 착수했던 작업. 다중 시스템 UI에 SQL 분석 뷰, 거기다 에러 처리까지 한 사이클 안에 묶어서 완성해야 했다.

명세가 나왔다고 바로 코딩으로 뛰어드는 건 아닌데, 이번엔 scope가 비교적 뚜렷했고 기존 코드 흐름이 이미 파악돼 있어서 큰 고민 없이 착수했다.

설계 결정 - 기존 패턴 위에 얹기

구현 시작 전에 고민한 건 하나였음. 기존 코드에 덧대느냐, 새로 짜느냐.

"새로 짠다"는 선택지가 항상 매력적으로 느껴지는 이유가 있다. 기존 코드는 맥락이 쌓인 흔적들이 많고, 처음부터 설계하면 더 깔끔할 것 같다는 착각이 든다. 근데 실제로 해보면 절반쯤 짰을 때 "이거 왜 기존 방식이랑 다르게 했지?"라는 질문이 생기고, 팀 컨벤션이 두 갈래로 갈린다. 유지보수 부담이 올라가는 건 결국 다음 사람, 아니면 몇 달 후의 나.

그래서 결정은 기존 구조를 최대한 활용하되 새 기능 부분만 분리해서 작성하는 방향. 변경 영향 범위가 명확해지고, 테스트도 기존 틀 안에서 할 수 있다. 이번에 건드린 파일은 6개. 범위가 크지 않아서 리뷰하기도 편했다.

구현 세부 - 스타일시트에서 컨트롤러까지

스타일시트

일단 동작하는 최소한의 UI를 만든 다음 예외 상태를 하나씩 채워가는 방식으로 했음. 에러 상태, 로딩 상태, 데이터 없음 상태 같은 건 처음부터 완벽하게 그려놓고 시작하기 어렵다. 실제 데이터를 넣어봐야 "아, 이 케이스가 있었구나"가 나온다.

다중 시스템이 붙으면 각 시스템마다 상태가 달라지는 케이스가 생긴다. A 시스템은 정상인데 B가 로딩 중이거나, B만 에러가 있거나. 이 조합들을 UI 레이어에서 명확하게 처리해줘야 사용자가 상황 파악이 된다. "뭔가 이상한 것 같은" 상태로 화면이 멈춰 있는 건 제일 나쁜 UX다. 스타일시트 작업이 단순해 보여도 이 상태 케이스 목록을 얼마나 촘촘하게 잡느냐가 실제 완성도를 가른다.

컨트롤러

입출력 스펙을 먼저 정의하고 구현으로 들어갔다. 스펙을 미리 써두면 중간에 요구사항이 바뀌어도 인터페이스 레벨에서 변경 포인트가 명확해진다. 안쪽 구현이 바뀌어도 외부 계약은 고정된 채로.

처리 흐름은 단순하게 유지했다.

입력 수신 -> 변환/정규화 -> 핵심 처리 -> 출력 변환 -> 반환

각 단계를 함수 단위로 쪼개두면 나중에 특정 단계만 테스트하거나 교체하기가 편하다. "정규화 로직이 이상한 것 같은데?"라는 의심이 생겼을 때 전체 플로우를 디버깅하는 게 아니라 그 단계만 격리해서 볼 수 있다. 이 단순한 분리가 디버깅 시간을 꽤 줄여준다.

SQL 분석 처리

SQL 분석이 끼어드는 지점이 좀 까다로웠음. 쿼리 파싱 과정에서 발생하는 예외 종류가 다양하고, 어느 시점에 실패해도 전체 흐름이 죽지 않아야 했다. 부분 실패를 어떻게 핸들링하느냐가 이번 작업의 핵심 중 하나였다.

에러를 그냥 던지는 게 아니라 어디서 뭐가 실패했는지 context를 담아서 올려줘야, 위에서 받아서 처리할 때도 선택지가 생긴다. 조용히 fallback 할지, 사용자에게 알릴지, 아니면 전파할지. context 없이 올라온 에러는 위에서 받아도 뭘 해야 할지 모른다. 그 판단을 아래 레이어에서 다 해버리면 에러 처리 전략이 여기저기 흩어진다.

구분 상태
기본 기능 완료
예외 처리 완료
로깅 추가됨
변경 파일 6개

로깅은 초반에 스킵하려다가 추가했다. 에러 처리 로직을 검증하는 게 로컬에서는 한계가 있고, 실제 환경에서 어떤 경로로 에러가 발생하는지 나중에 확인하려면 결국 로그가 있어야 한다. 나중에 "왜 이 케이스에서 실패했지?"를 추적할 때 로그가 없으면 재현하는 데 시간을 다 쓰게 된다.

트레이드오프와 설계 판단

단순함 vs 확장성

지금 당장 필요한 기능만 만드는 게 맞는지, 나중 확장을 미리 고려해야 하는지. 결국 YAGNI 원칙대로 지금 필요한 것에 집중했음. 과도한 추상화는 오히려 코드를 이해하기 어렵게 만든다는 경험이 있어서. 추상화 레이어가 늘어날수록 "어디서 실제로 처리되는 거지?"를 쫓아가야 하는 거리가 늘어난다.

물론 YAGNI가 절대 원칙은 아니다. 변경 비용이 명확하게 높은 지점, 예를 들어 외부 시스템 인터페이스 같은 부분은 미리 추상화를 두는 게 맞다. 교체할 때 파급이 크기 때문. 이번 컨트롤러 입출력 스펙을 인터페이스 레벨에서 정의한 것도 그 이유다.

명시적 vs 묵시적

코드에 의도를 명시적으로 드러낼수록 후임자가 이해하기 쉽다. 변수명, 함수명에 공을 들인 이유다. data보다 parsedQueryResult, process()보다 normalizeAndValidateInput() 같은 방향. 짧은 이름이 나쁜 건 아닌데, 이름이 모호하면 함수 안을 반복해서 열어봐야 한다. 시간이 쌓이면 꽤 큰 비용이다.

결정 항목 선택 이유
구조 기존 패턴 재사용 일관성, 팀 컨벤션 유지
네이밍 명시적 코드 자체가 문서 역할
예외 처리 방어적 부분 실패 허용, 전파 제어

테스트는 TDD보다 실제 데이터 투입 방식으로 했다. 미리 케이스를 다 생각해서 테스트를 작성하는 게 어떤 도메인에서는 맞고, 어떤 도메인에서는 돌려봐야 케이스가 나온다. SQL 분석 쪽은 후자였음. 실제 쿼리를 넣어보면서 엣지 케이스가 드러났고, 그걸 그때그때 처리했다. 어떤 방식이든 경계 조건을 빠트리지 않는 게 핵심이지, 방법론 자체가 목표는 아니다.

댓글 0

첫 댓글 달아줘.