← 02 MAKE무엇을 만들었는가
브라우저 안에 또 하나의 탭 브라우저 만들기
상담원은 화면을 하나씩 쓰지 않습니다. 이 요구 하나가 페이지 62개짜리 관리자 화면의 구조를 전부 결정했습니다.
이 쪽은 원문을 쉬운 말로 다시 쓴 판입니다. 저자가 쓴 문장은 원문 쪽에 있습니다. 이 판의 문장은 사람이 쓰지 않았습니다.
회사 이름, 서버 주소, 회원 정보는 뺐습니다. 구조와 코드 방식은 실제 그대로입니다.
회사 안에서 직원들이 쓰는 관리자 화면입니다. 페이지 62개, 파일 721개, 코드 약 7만 5천 줄입니다.
상담원은 화면을 하나씩 쓰지 않는다
이 프로그램의 성격을 결정한 요구가 하나 있습니다.
상담원이 회원 목록을 띄워 둔 채로 계약을 확인하고, 그 사이에 매물을 검수합니다.
브라우저 탭을 여러 개 여는 게 아니라, 프로그램 안에 탭이 있어야 합니다. 그리고 탭마다 검색 조건과 스크롤 위치를 자기 것으로 따로 기억해야 합니다.
브라우저 안에 또 하나의 탭 브라우저를 만든다는 뜻이고, 이게 간단하지 않습니다.
탭을 바꿀 때 화면이 한 번 깜빡였다
증상은 "탭을 바꿀 때 빈 화면이 한 번 스친다"였습니다.
원인은 탭 이름표가 바뀌는 시점과 주소가 바뀌는 시점이 어긋나서였습니다.
탭을 누르면 이름표는 즉시 바뀌는데, 주소는 조금 뒤에 바뀝니다. 그 한 순간에 떠나는 화면이 도착할 탭의 이름표를 달고 다시 만들어졌다가 버려집니다.
다시 만들어지면 그 화면이 하던 일이 전부 처음부터 다시 실행됩니다. 서버에 자료를 다시 요청하고, 방금 받아 둔 것은 버려집니다.
해결은 주소가 따라잡을 때까지 이름표를 붙잡아 두는 것이었습니다. 코드 세 줄로 서버 재요청이 사라졌습니다.
이런 건 이유를 적어 두지 않으면 다음 사람이 "왜 이렇게 했지?" 하고 지웁니다.
탭마다 기억하는 것을 두 곳으로 나눴다
처음에는 탭별 상태를 한 곳에 모아 뒀습니다. 곧 두 종류가 섞이면 안 된다는 게 드러났습니다.
- 새로고침해도 남아야 하는 것 — 검색 조건 같은 것. 브라우저 저장소에 둡니다.
- 새로고침하면 사라져야 하는 것 — 서버에서 받아 온 결과 같은 것. 메모리에만 둡니다.
경계를 잘못 그으면 조용히 깨집니다.
서버 응답을 오래 남는 쪽에 저장하면, 응답이 도착하는 순간 이미 다른 탭으로 옮겨 갔을 때 남의 탭 자리에 결과가 기록됩니다. 화면에는 다른 탭의 데이터가 뜨고, 다시 재현하기도 어렵습니다.
그리고 메모리 쪽에서 두 번째 함정이 나왔습니다.
쓰던 도구가 한 번 만든 저장 칸을 계속 들고 있습니다. 탭을 닫아도 그 탭이 보던 자료가 메모리에 남습니다.
보통 프로그램이면 그냥 낭비지만, 여기 담기는 건 회원 개인정보입니다. 그래서 어떤 칸을 지워야 하는지 적어 두는 장부를 따로 만들고, 탭을 닫을 때 직접 지웁니다.
권한은 축이 세 개였다
관리자 화면이라 권한이 화면의 절반입니다. 그리고 축이 하나가 아니었습니다.
| 축 | 무엇을 판단하나 |
|---|---|
| 메뉴 권한 | 이 메뉴에서 읽기·만들기·고치기·지우기·내려받기가 되는가 |
| 일반 권한 | 여러 메뉴에 걸쳐 있는 기능이 되는가 |
| 최고 관리자 | 관리 화면에 들어갈 수 있는가 |
같은 기능이 여러 메뉴에 나오면 메뉴 권한만으로는 표현이 안 됩니다. 계약 승인은 이쪽 화면에도 있고 저쪽 화면에도 있는데, 메뉴별로 따로 주면 한쪽만 열려 있는 이상한 상태가 만들어집니다.
권한을 확인하는 자리는 네 군데로 나뉘어 있습니다 — 들어가는 문, 왼쪽 메뉴에 보이는지, 화면 안의 버튼들, 부서장인지 여부. 넷이 같은 규칙을 봐야 합니다. 메뉴에는 안 보이는데 주소로는 열리면 규칙을 읽을 수 없게 됩니다.
들어가도 되는지 판단하는 부분은 혼자 떼어내 시험할 수 있는 함수로 분리했습니다. 화면 코드 안에 흩어 두면 경우의 수를 표로 만들어 돌려 볼 수 없습니다.
반복해서 나온 결함은 한 가지 모양이었다
권한 관련 문제를 모아 보니 전부 "판단할 근거를 못 구하면 통과시킨다" 한 가지의 변형이었습니다.
- 값을 못 구하면 허용 — 기본값을 '허용'으로 두면 연결을 빠뜨려도 화면은 멀쩡해 보입니다. 그래서 권한 값은 반드시 넘기도록 강제합니다. 기본값 '거부'도 금지입니다 — 빠뜨린 것이 '정상 거부'처럼 보여서 더 늦게 발견됩니다.
- 들어가는 문만 잠그고 쓰는 곳은 안 잠금 — 버튼만 막으면 오늘은 맞습니다. 같은 일을 하는 다른 길이 하나 생기는 순간 열립니다. 실제로 한 화면은 등록은 권한을 보고 수정은 안 보고 있었습니다.
- 코드를 못 구하면 통과 — 메뉴 목록 조회가 실패하면 판단 자체를 건너뛰었습니다. 지금은 주소를 기준으로도 함께 판단합니다.
그리고 문서 맨 앞에 못박아 뒀습니다: 이 검사는 쓰기 편하라고 있는 것이지 보안이 아닙니다. 브라우저에서 전부 우회할 수 있고, 진짜 방어선은 서버입니다. 이 한 줄이 없으면 앞단 검사를 방어선으로 착각한 설계가 들어옵니다.
규약을 문서에 적어 둔다
프로그램마다 문서를 하나 두고 이 프로그램의 규약을 적습니다. 사람과 코딩을 돕는 AI가 같은 파일을 읽습니다.
적어 둔 것들 — 서버 주소는 한 곳에만 모아 두기, 목록 화면의 조회 방식 통일하기, 응답이 성공인지 항상 확인하기, 목록 화면의 뼈대 고정하기, 폼 작성 중에 실수로 창이 닫히지 않게 하기.
문서화 기준은 하나입니다. 한 번 물려 본 것만 적습니다. 일반적인 이야기를 적으면 분량만 늘고 읽히지 않습니다.
이 프로그램에서만 나온 함정들
- 알림 창이 서버 요청을 무한 반복시킨다. 알림이 뜨면 화면 전체가 다시 그려지고, 다시 그려지면 조회가 다시 나가고, 실패하면 또 알림이 뜹니다. 고리가 닫힙니다.
- 팝업 안에서 알림을 띄우면 부모 팝업까지 닫힌다. 저장 실패를 알리려다 사용자가 입력한 내용을 날립니다.
- 가로로 스크롤되는 상자 안에서는 머리글 고정이 조용히 안 먹는다. 에러도 경고도 없습니다.
- 화면 크기별로 정해진 기본값은 그냥 덮어써지지 않는다. 크기 조건까지 같이 적어야 합니다.
- 상자 안쪽이 "가로로 꽉 채우기"로 되어 있으면 바깥에서 준 너비가 밀린다.
공통점이 있습니다. 전부 에러 없이 조용히 틀립니다. 코드도 통과하고 콘솔도 깨끗합니다. 그래서 한 번 물려 본 사람의 기록이 유일한 방어입니다.
지금 시점의 솔직한 평가
잘 된 것 — 탭 시스템. 특히 메모리에 남는 자료를 개인정보 관점에서 처리한 것. 권한 판단을 따로 떼어내 시험할 수 있게 만든 것. 권한 값을 필수로 강제한 것. 함정을 재현해 본 뒤에만 기록한 것.
아직 안 된 것 — 조회가 실패했을 때 실패를 조용히 삼키고 "다 불러왔다"고 기록합니다. 그래서 장애와 권한 없음이 화면에서 구분되지 않고, 새로고침 전에는 복구되지 않습니다. 그리고 보기만 하는 화면 일부에 만들기·고치기·지우기 권한이 필요 이상으로 등록돼 있습니다.
다시 한다면 — 탭 시스템을 먼저 만들고 화면을 얹은 순서는 맞았습니다. 나중에 얹으려 했다면 화면 41개의 상태 관리를 전부 고쳐야 했을 겁니다.
대신 성공 / 실패 / 아직 안 함을 처음부터 세 갈래로 뒀어야 했습니다. 실패를 성공처럼 저장하면 그 뒤의 모든 판단이 잘못된 전제 위에서 돌아갑니다.
남겨 둘 규칙 셋
- 조용히 틀리는 것은 기록으로만 막습니다. 도구가 잡아 주는 건 이미 문제가 아닙니다.
- 권한 판단은 따로 떼어내 시험할 수 있게 만듭니다.
- 실패를 성공처럼 저장하지 않습니다. "다 불러왔다"고 적기 전에 그게 성공인지 확인합니다.