gofillus 기술·운영 저널

화면을 새로 만들다 잃어버린 것들

React Native 화면을 디자인 정본에 맞춰 다시 만들면서, UI보다 먼저 사라질 수 있는 것은 상태 처리와 업무 규칙이라는 걸 알게 됐다. 화면을 바꾸기 전에 무엇을 적어 두고, 어떤 테스트를 남겨야 하는지 실제 재생성 과정으로 정리했다.

배경 — 화면은 새로 만들 수 있지만, 동작은 쉽게 잃는다

앞 글에서는 시간 값을 화면에 그대로 표시하면서 같은 문제가 세 번 반복된 일을 썼다. 이번에는 조금 다른 종류의 문제를 만났다.

앱의 화면을 새 디자인에 맞춰 전면으로 다시 만드는 작업이었다. 화면의 색, 간격, 버튼 모양만 바꾸는 일이 아니었다. 출퇴근, 휴가, 직원 관리처럼 이미 서비스 중인 흐름을 새 화면에 다시 담아야 했다.

처음에는 화면별로 구현을 끝내고 실기기로 보면 충분할 것 같았다. 그런데 화면이 보기 좋게 바뀌어도, 사용자가 실제로 만나는 상태는 훨씬 많았다.

  • 인터넷이 끊겼을 때는 무엇을 보여 줄까.
  • 위치 권한을 거부했지만 WIFI 확인은 가능한 경우는 어떻게 될까.
  • 관리자가 다른 지점 직원의 상세 화면을 열면 어떤 안내가 나와야 할까.
  • 휴가 기간에 주말과 공휴일이 섞이면 며칠을 빼야 할까.

화면 하나를 다시 만드는 일은 결국 이 질문들을 다시 연결하는 일이었다. 화면을 잘 옮겼는지 보려면, 평소에는 잘 드러나지 않는 상태까지 함께 확인해야 했다.

선택 — 화면 단위가 아니라 상태 단위로 다시 만들기

선택지는 두 가지였다.

방법좋은 점부족한 점
기존 컴포넌트의 모양만 조금씩 고치기빨리 끝난다화면마다 예외 처리가 남아 새 디자인과 동작이 섞인다
정본 화면을 기준으로 다시 만들되 기존 로직을 따로 목록화하기화면과 동작의 관계를 다시 확인할 수 있다처음에 적고 대조하는 시간이 든다

두 번째 방법을 골랐다. 대신 화면을 비슷하게 그리는 데서 끝내지 않기로 했다.

먼저 디자인 정본의 모든 화면을 상태별 프레임으로 나눴다. 출퇴근 화면은 정상 상태만 있는 것이 아니라 위치 권한 거부, GPS 약함, 오프라인, 이미 출근함, 서버 시각 확인 실패 같은 11개 상태가 있었다. 그다음 기존 코드가 하던 일을 보존, 재연결, 추가, 제거로 나눠 적었다.

여기서 가장 도움이 된 기준은 간단했다. 화면에서 보이는 결과와, 그 결과를 만드는 판단을 분리한다.

예를 들어 위치 상태를 받아 어떤 문구와 버튼을 보여 줄지는 작은 순수 함수로 뺐다. 입력이 같으면 언제나 같은 화면 상태가 나오는 함수다. 그러면 실제 GPS를 일부러 약하게 만들 수 없는 상황에서도, GPS_WEAK일 때 어떤 화면이 나오는지는 테스트로 고정할 수 있다.

적용 — 새 화면을 만들기 전에 남긴 다섯 가지

1. 화면 목록보다 상태 목록을 먼저 만들었다

“출퇴근 화면 완료”라고 적으면 완료의 뜻이 너무 넓다. 그래서 정본의 각 프레임을 하나씩 적고, 구현 결과를 세 가지로 표시했다.

  • 정본과 같은 상태
  • 일부만 표현한 상태
  • 아직 없는 상태

이 목록은 구현자에게는 빠뜨린 화면을 알려 주고, 검토자에게는 무엇을 실제로 확인해야 하는지 알려 줬다. 출퇴근 파일럿에서는 11개 상태를 모두 구현했다. 이후 8개 화면 그룹을 세 번에 나눠 확인할 때도 같은 기준을 사용했다.

2. 화면에 숨어 있던 판단을 함수로 꺼냈다

화면 컴포넌트 안에는 생각보다 많은 판단이 섞여 있다. 위치 판정 결과에 따라 색을 고르고, 버튼을 막고, 안내 문구를 바꾸는 일들이다. 화면을 다시 쓰는 동안 이 판단이 빠지면, 정상 상태에서는 티가 나지 않는다.

그래서 위치 표시처럼 상태가 많은 부분은 deriveLocView처럼 입력과 출력이 분명한 함수로 분리했다. 출퇴근 파일럿에서는 이 함수에 11개 테스트를 붙였다. 어떤 상태에서 어떤 표시가 나와야 하는지를 코드가 기억하게 한 셈이다.

이 방식의 장점은 테스트가 화면의 모양을 검사하는 데 그치지 않는다는 데 있다. 위치 권한, 네트워크, 서버 응답처럼 실기기에서 매번 만들기 어려운 조건도 같은 기준으로 확인할 수 있다.

3. 실기기에서 가능한 것과 불가능한 것을 나눴다

실기기는 꼭 필요하다. 버튼의 크기, 안전 영역, 실제 권한 창, 화면 전환은 코드만 보고 알 수 없다.

하지만 실기기만으로는 모든 상태를 만들 수 없다. 테스트 기기의 GPS가 잘 잡히면 GPS 약함을 재현하기 어렵다. 서버가 정상이라면 서버 시각 오류도 만들기 어렵다. 권한을 거부한 상태와 WIFI 식별값을 읽는 상태가 운영체제 제약 때문에 동시에 성립하지 않는 경우도 있었다.

그래서 검증 방법을 나눴다.

상태확인 방법
정상 출퇴근, 반경 밖, 권한 안내, 이미 출근함실기기에서 실제 흐름 확인
GPS 약함, 미등록 WIFI, 서버 시각 실패상태 파생 함수 테스트와 API 응답 확인
오프라인기기 네트워크 차단과 오류 처리 순서 확인

모든 것을 실기기로 확인했다고 말하는 것보다, 무엇을 어디까지 확인했는지를 나누는 편이 다음 작업에도 더 쓸모가 있었다.

4. 서버와 화면이 같은 말을 하는지 확인했다

재생성 중에는 화면 코드가 틀리지 않았는데도 예상과 다른 화면이 나온 적이 있었다.

관리자가 다른 지점 직원의 상세를 열었을 때, 화면 설계는 서버가 403을 주면 전용 잠금 안내를 보여 주도록 되어 있었다. 실제 서버는 같은 요청에 404를 보냈다. 접근은 막혔지만 앱은 “다시 시도”라는 일반 오류를 보여 줬다.

이런 문제는 프론트엔드 화면만 보고는 찾기 어렵다. 화면이 기대하는 상태 코드와 실제 응답을 함께 적어 두어야 한다. 그래야 서버를 바꿀지, 앱이 두 응답을 같은 접근 제한으로 다룰지, 설계를 바꿀지를 정할 수 있다.

5. 새 빌드가 정말 기기에 들어갔는지 확인했다

한 번은 수정한 화면을 확인하려고 앱을 설치했는데, 기기에는 이전 번들이 남아 있었다. 화면이 바뀌지 않은 원인이 코드인지 빌드인지 한동안 구분되지 않았다.

그 뒤부터는 빌드가 끝났다는 메시지만 보지 않았다. 빌드 시각이 소스 수정 뒤인지, 번들 식별자가 새 코드에 있는지, 설치 직전 번들과 값이 다른지를 같이 확인했다.

작은 확인이지만, 재생성처럼 변경 범위가 큰 작업에서는 시간을 가장 많이 아껴 줬다. 화면이 잘못된 것이 아니라 오래된 앱을 보고 있다는 사실을 빨리 알 수 있기 때문이다.

실수한 것들

오프라인 화면이 있는데도 사용자는 보지 못했다

오프라인일 때 보여 줄 안내 화면을 만들고 테스트도 붙였다. 그런데 앱을 인터넷 없는 상태로 처음 열면, 데이터를 가져오는 요청이 먼저 실패했다. 공통 오류 화면이 먼저 나타나면서 우리가 만든 오프라인 안내는 뒤에 가려졌다.

화면의 상태는 존재했지만, 실제 실행 순서에서는 도달하기 어려웠다. 이후에는 화면에 어떤 상태가 있는지뿐 아니라, 데이터 요청 실패와 화면 상태 중 무엇이 먼저 보일지도 함께 확인했다.

테스트가 통과해도 기본 명령은 실패했다

상태를 나눈 테스트는 모두 통과했다. 다만 저장소의 Jest 설정은 설치되지 않은 React Native preset을 가리키고 있었다. 그래서 평소의 npm test는 시작부터 실패했다.

테스트 결과만 적고 실행 환경을 확인하지 않은 실수였다. 당장은 필요한 테스트를 별도 설정으로 실행해 확인했지만, 다음 화면 작업 전에 기본 명령도 같은 방식으로 동작하도록 정리해야 한다는 숙제가 남았다.

설계에 있는 상태를 곧바로 재현할 수 있다고 생각했다

처음에는 정본에 그려진 상태라면 기기에서 모두 확인할 수 있다고 생각했다. 위치, 네트워크, 서버 상태는 기기와 서버 환경이 함께 맞아야 한다. 어떤 상태는 테스트 데이터나 API 호출 없이는 만들 수 없었다.

이후에는 화면을 만들기 전에 각 상태의 확인 방법도 같이 적었다. 실기기, API, 단위 테스트 중 어느 쪽이 근거가 되는지 먼저 정해 두면 검증 막바지에 억지로 상태를 만들지 않아도 된다.

결과

항목화면을 다시 만들기 전재생성 뒤
출퇴근 파일럿의 상태별 확인정상 흐름 중심11개 상태를 목록화하고 테스트
전면 재생성 범위화면별 완료 여부만 확인8개 그룹을 프레임·기능·실기기로 대조
화면 판단을 고정한 테스트화면마다 흩어진 확인Wave 1~3에서 순수 상태 함수 테스트 556개
발견한 계약 불일치화면 오류로 보일 수 있음서버 404와 설계 403의 차이로 분리

마지막 Wave까지 확인한 결과, 8개 그룹의 화면 재생성은 기능 회귀 없이 끝냈다. 다만 모든 화면이 완전히 같은 방식으로 검증된 것은 아니다. 실기기로 만들 수 없는 상태는 코드와 API 확인으로 근거를 남겼고, 서버 응답이 설계와 다른 한 곳은 후속 정렬 항목으로 남겼다.

남는 것

화면을 새로 만드는 일을 앞두고 있다면, 디자인 파일을 열기 전에 먼저 묻고 싶다. 이 화면이 사용자에게 보여 주는 상태는 몇 개인가. 그리고 각 상태는 어디에서 확인할 수 있는가.

정상 화면을 만드는 일은 대개 어렵지 않다. 어려운 쪽은 사용자가 권한을 거부했을 때, 네트워크가 끊겼을 때, 서버가 예상과 다르게 답했을 때도 화면이 자기 역할을 하게 만드는 일이다.

이번에 남은 미해결도 있다. 공통 테스트 설정은 기본 명령으로 바로 실행되도록 정리해야 한다. 다른 지점의 직원 상세를 요청할 때 404403 중 무엇을 계약으로 삼을지도 서버와 앱이 같은 결론을 내야 한다.

그래도 다음 화면을 다시 만들 때는 출발점이 생겼다. 화면을 그리기 전에 상태를 적고, 판단을 테스트로 남기고, 실제 기기에서 새 빌드를 보고 있는지 확인한다. 이 세 가지만 해도 새 화면 때문에 오래된 기능을 조용히 잃을 가능성은 크게 줄어든다.


← 목록으로