이 글은 Today I Learn 시리즈의 92번째 기록입니다. (총 125개)

근무 트래커를 빈 폴더에서 시작해 하루 만에 쓸 수 있는 상태까지 만들었다. 코드보다 오래 남을 것들만 추렸다.


서버도 없는데 저장 자리를 약속 뒤에 숨긴 이유

이 앱은 지금 브라우저 안에만 저장한다. 계정도 서버도 없다. 그런데도 화면 코드가 「브라우저 저장소」를 직접 부르지 않게 하고, 사이에 「무엇을 할 수 있는지」만 적은 약속을 하나 뒀다.

당장은 순전히 낭비다. 파일이 하나 늘고 거치는 단계가 생긴다.

그럼에도 둔 이유는 값을 재 봤기 때문이다.

  • 지금 두는 값 — 약속 하나. 화면 코드가 부르는 이름은 어차피 필요하다
  • 나중에 안 뒀을 때의 값 — 서버가 붙는 날 화면을 전부 뒤져 고친다. 그 시점엔 화면이 지금보다 훨씬 많다

「나중에 필요하면 그때 하자」가 대개 맞는 말인데, 안 맞는 경우가 이런 자리다. 나중에 드는 값이 시간이 갈수록 커지고, 지금 드는 값은 거의 0일 때. 그럴 땐 미룬 만큼 값이 붙는다.

  • 판별하는 질문 — 나중에 하면 값이 더 비싸지나, 그대로인가
  • 그대로면 미룬다. 비싸지면 지금 싼 만큼만 해 둔다

업무 정의와 하루 기록을 따로 둔다

「매주 금요일에 뜨는 업무」는 정의고 「8월 20일에 그걸 끝냈다」는 기록이다. 둘을 한 덩어리로 두면, 업무 이름을 고치는 순간 지난 기록의 이름까지 같이 바뀐다.

그러면 8월 20일에 무엇을 했는지가 오늘 바뀌는 셈이다. 기록이 기록이 아니게 된다.

  • 기록은 이름을 베끼지 않고 정의를 가리키기만 한다
  • 대신 정의를 지우면 지난 기록이 빈칸이 된다. 그래서 도장을 찍은 적 있는 것은 목록에서 감추기만 하고, 한 번도 안 쓴 것만 진짜로 지운다

여기서 나온 규칙이 하나 더 있다. 무언가를 지우기 전에 그걸 가리키는 것이 있는지 먼저 본다. 가리키는 것이 있으면 지우기와 감추기는 다른 일이다.


저장 구조를 바꿀 때 옛 데이터를 안 지운다

저장 구조는 반드시 바뀐다. 바뀔 때 옛 데이터를 못 읽으면 그동안 쌓인 게 날아간다. 이 앱에서 그건 실사용자가 몇 달간 찍은 도장이다.

두 가지를 정했다.

  • 구조 버전을 데이터 안에 넣는다. 저장소 이름표에 넣으면 안 된다 — 버전을 올리는 순간 옛 데이터가 다른 이름표를 달고 통째로 안 보이게 된다
  • 옛 데이터를 새 모양으로 바꿔 저장하되, 옛 것을 지우지 않는다

둘째가 핵심이다. 변환 코드는 틀릴 수 있고, 틀린 걸 아는 시점은 대개 지운 다음이다. 되돌아갈 자리를 남겨 두는 값은 저장 공간 몇 줄이고, 안 남겼을 때의 값은 복구 불가다. 값이 이렇게 기울면 고민할 일이 아니다.

같은 이유로 개발 중에 쓰는 저장 자리와 실제로 쓰는 자리를 이름부터 갈랐다. 주소가 같으면 브라우저는 같은 곳을 본다. 「조심하면 된다」로 두면 언젠가 한 번은 밟는다. 조심이 아니라 구조로 막는 쪽이 싸다.


CSS에서 막힌 셋의 원인이 전부 「안 적은 것」이었다

이날 CSS에서 막힌 게 셋이었는데 원인이 전부 같았다. 안 적어서 아무 일도 안 일어난 게 아니라, 안 적은 자리를 기본값이 채우고 있었다.

  • 세로 스크롤만 켰는데 가로 스크롤바가 생겼다 — 한 축을 정하면 나머지 축의 기본값이 따라 바뀐다
  • 카드 하나를 펼쳤더니 다른 카드가 눌려 글자가 잘렸다 — 「필요하면 줄어들어도 좋다」가 기본값이다
  • 「다음 줄을 통째로 차지하라」가 무시됐다 — 줄바꿈이 꺼져 있으면 그 지정이 의미를 잃는다

셋 다 「내가 그렇게 하라고 한 적 없는데」로 시작했다. 그런데 안 한 지시가 곧 지시였다.

  • 원인을 찾을 때 「내가 뭘 잘못 적었나」와 「내가 뭘 안 적었나」는 다른 질문이다. 둘째를 안 물으면 오래 헤맨다
  • 이건 CSS만의 얘기가 아니다. 기본값이 있는 모든 것에서 같은 함정이 난다

선택자가 부딪치면 세게 만들지 말고 범위를 좁힌다

켜진 탭에 마우스를 올리면 글자가 사라졌다. 두 규칙이 부딪쳤고, 구체적인 쪽이 이기는 규칙 때문에 의도하지 않은 쪽이 이겼다.

이길 규칙을 더 세게 만드는 길과, 애초에 겹치지 않게 범위를 좁히는 길이 있었다. 뒤쪽으로 갔다.

  • 세게 만들어 이기면 다음에 또 겹칠 때 더 세게 만들어야 한다. 끝이 없고, 나중에 읽는 사람이 왜 그렇게 센지 모른다
  • 범위를 좁히면 규칙이 하나씩만 닿는다. 읽으면 그대로 뜻이 된다

누르는 기능을 만들 때 되돌리는 방법을 같이 정한다

도장을 실수로 찍으면 지울 방법이 없었다. 만들 때는 「찍는다」만 생각했지 「잘못 찍었다」를 생각하지 않았다.

  • 되돌리기 없는 조작은 사용자가 실수를 고칠 수 없게 만든다. 그러면 조심해서 쓰게 되고, 조심해서 쓰는 도구는 잘 안 쓴다
  • 되돌리는 길은 만드는 순간에 같이 정하는 게 싸다. 나중에 붙이면 그때까지 쌓인 잘못된 기록도 같이 처리해야 한다

이미 완료인 도장을 눌러도 애니메이션이 돌았다

완료를 되돌리려고 눌렀든, 이미 찍힌 걸 또 눌렀든 연출은 똑같이 돌았다. 바뀐 건 아무것도 없는데 화면은 무언가 일어난 것처럼 굴었다.

  • 화면의 반응은 사용자에게 하는 말이다. 아무 일도 안 일어나야 하는 자리에서는 아무 일도 안 일어나야 한다
  • 반대도 마찬가지다. 일어났는데 표시가 없으면 안 된 줄 안다. 반응은 실제로 바뀐 것과 정확히 붙어 있어야 한다

같은 집계를 두 화면에서 다르게 접어 뒀다

하위 집계를 두 화면에 넣었는데 접힘 기본값을 다르게 뒀다.

  • 오늘 보드 — 바로 아래에 하위 목록이 이미 펼쳐져 있다. 집계까지 펼치면 같은 내용이 두 번이라 접어 둔다
  • 기록실 — 그날 무엇을 했는지 알 길이 그 집계뿐이다. 접어 두면 눌러야 아는 화면이 된다

같은 부품을 두 곳에 넣으면 설정도 같이 맞추고 싶어지는데, 그게 틀릴 때가 있다. 기본값은 그 부품이 아니라 그 부품이 놓인 자리가 정한다.


Series: Today I Learn

1 C++ 자료형(Data Type) 2 MD5 vs pHash 3 C++에서 함수의 선언과 정의 4 Tkinter padx, pady 5 메모리와 포인터 변수 6 Call by Value, Call by Reference, Call by Pointer 비교 7 const 8 Gemfile — Jekyll 프로젝트의 의존성 파일 9 kramdown-parser-gfm — Jekyll의 GFM 파서 10 파서(Parser) 11 AHU vs OHU 12 I might try it vs I'll try it 뉘앙스 차이 13 SESSION_EXPIRE_AT_BROWSER_CLOSE=True 14 configuration key 15 Git stash vs discard 16 subprocess.Popen으로 Windows 탐색기에 명령어를 전달 17 Post 잔디 분석하기 18 Google Sheets Sync 최적화 19 DSL (Domain Specific Language)과 GPL (General Purpose Language) 20 마크다운 표 그리는 방법 21 쿼리 파라미터(Query Parameter). 기존 QR코드 재활용 22 Django 보안 취약점 점검 및 수정 23 OOP Object-Oriented Programming 객체 지향 프로그래밍 24 Fernet 대칭 암호화 25 Jekyll 코드블록 안의 Liquid 태그 26 insertOnConflictUpdate vs DoUpdate(target) 27 세션 필터 28 아코디언(Accordiaon) UI를 펼친상태로 만들기 29 input의 step 30 Word Cloud 31 Google Sheets를 데이터 버스로(with AppSheet) 32 Django 모델 텍스트 필드 자동 수집 패턴 33 localStorage로 섹션 토글 상태 유지 34 순차 ID 생성(`select_for_update()` + `max()` 조합) 35 역참조 검색과 distinct() 36 xlsx 다운로드와 로딩 오버레이 충돌 37 Android 파일 공유 MIME 타입 38 AssetManifest — Flutter 빌드 타임 asset 목록 런타임 조회 39 UTF-8 BOM과 PowerShell 파일 쓰기 40 소리꽃 KeyBloom TIL 1 41 소리꽃 KeyBloom TIL 2 42 소리꽃 KeyBloom TIL 3 43 메트로놈 Simple Metronome TIL 1 44 소리꽃 KeyBloom TIL 4 45 메트로놈 Simple Metronome TIL 2 46 소리꽃 KeyBloom TIL 5 47 메트로놈 Simple Metronome TIL 3 48 정적 블로그 SEO 정비와 Pagefind 검색 도입 49 메트로놈 Simple Metronome TIL 4 50 안드로이드 AudioTrack 연속 재생, 실측 피커 정렬, 카메라 토치 플래시 51 메트로놈 Simple Metronome TIL 5 52 모래게임 Sandrop TIL 1 53 메트로놈 Simple Metronome TIL 6 54 모래게임 Sandrop TIL 2 55 모래게임 Sandrop TIL 3 56 메트로놈 Simple Metronome TIL 7 57 모래게임 Sandrop TIL 4 58 모래게임 Sandrop TIL 5 59 모래게임 Sandrop TIL 6 60 소리꽃 KeyBloom TIL 6 61 모래게임 Sandrop TIL 7 62 모래게임 Sandrop TIL 8 63 소리꽃 KeyBloom TIL 7 64 모래게임 Sandrop TIL 9 65 소리꽃 KeyBloom TIL 8 66 모래게임 Sandrop TIL 10 67 소리꽃 KeyBloom TIL 9 68 소리꽃 KeyBloom TIL 10 69 소리꽃 KeyBloom TIL 11 70 소리꽃 KeyBloom TIL 12 71 소리꽃 KeyBloom TIL 13 72 온실 GreenHouse TIL 1 73 소리꽃 KeyBloom TIL 14 74 온실 GreenHouse TIL 2 75 소리꽃 KeyBloom TIL 15 76 소리꽃 KeyBloom TIL 16 77 소리꽃 KeyBloom TIL 17 78 소리꽃 KeyBloom TIL 18 79 소리꽃 KeyBloom TIL 19 80 소리꽃 KeyBloom TIL 20 81 로그스톤 상표 셀프 출원 TIL 1 82 소리꽃 KeyBloom TIL 21 83 소리꽃 KeyBloom TIL 22 84 소리꽃 KeyBloom TIL 23 85 소리꽃 KeyBloom TIL 24 86 MiniMacro TIL 1 87 Astro가 무엇인지, 왜 옮기는지 88 MiniMacro TIL 2 89 메트로놈 Simple Metronome TIL 8 90 메트로놈 Simple Metronome TIL 9 91 블로그 Astro 이관 TIL 92 오픈데이 Openday TIL 1 읽는 중 93 오픈데이 Openday TIL 2 94 Unreal Engine MCP TIL 1 95 콘티온 Conti On TIL 11 96 오픈데이 Openday TIL 3 97 오픈데이 Openday TIL 4 98 오픈데이 Openday TIL 5 99 ScorePlayer TIL 1 100 BuildingHub TIL 1 101 ScorePlayer TIL 2 102 BuildingHub TIL 2 103 ScorePlayer TIL 3 104 Unreal Engine MCP TIL 2 105 궁 미로 PalaceMaze TIL 1 106 메트로놈 Simple Metronome TIL 10 107 메트로놈 Simple Metronome TIL 11 108 오픈데이 Openday TIL 6 109 BuildingHub TIL 3 110 Obsidian vault에서 코드만 빼기 — directory junction 111 ScorePlayer TIL 4 112 오픈데이 Openday TIL 7 113 URL은 바뀔 수 있는 것에 묶지 않는다 114 도구는 기능이 아니라 내 일에 맞는지로 고른다 115 돌이키기 힘든 변경은 작게 먼저 확인한다 116 궁 미로 PalaceMaze TIL 3 117 궁 미로 PalaceMaze TIL 2 118 소리꽃 KeyBloom TIL 29 119 오픈데이 Openday TIL 8 120 BuildingHub TIL 4 121 ScorePlayer TIL 5 122 BuildingHub TIL 5 123 MiniMacro TIL 3 124 소리꽃 KeyBloom TIL 30 125 궁 미로 PalaceMaze TIL 4