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

값이 하나뿐인 칸을 없앴다가 되살렸다

어제 계절 태그를 크리스마스 하나로 줄였다. 값이 하나면 고르는 칸을 둘 이유도 없다고 보고 화면에서 그 칸을 뺐다.

다시 넣었다. 12월이 되면 매장이 캐럴만 골라 듣고 싶을 텐데, 칸이 없으면 내가 크리스마스 재생목록을 만들어 줄 때까지 기다려야 한다. 1년에 한 달만 쓰는 칸이라도 그 한 달에는 그게 전부다.

값이 하나인 칸을 둘지 정할 때 물을 것은 「지금 값이 몇 개인가」가 아니다. 「그 칸이 없으면 쓰는 사람이 못 하는 일이 생기나」다. 화면이 하나 늘어나는 비용은 매일 조금이고, 못 하는 일이 생기는 비용은 그 시기에 한꺼번에 온다.


「그 곡을 듣고 그 때를 맞힐 수 있나」

그러면서 이 칸에 앞으로 무엇을 넣을지도 정해야 했다. 봄·여름·겨울을 걷어낸 게 어제인데, 오늘은 명절이 떠올랐고 이어서 할로윈도 떠올랐다. 그때그때 판단하면 또 늘었다 줄었다 할 게 뻔했다.

그래서 기준을 문장 하나로 못박았다. 그 곡을 듣고 그 때를 맞힐 수 있나.

  • 크리스마스는 통과한다. 벨 소리와 캐럴 진행이 음악 안에 있다.
  • 명절도 통과한다. 국악 편성의 밝은 축하 결이 뚜렷하다.
  • 새해는 떨어진다. 카운트다운을 빼면 「활기찬 축하」인데 그건 이미 분위기에 있다.
  • 봄·여름은 떨어진다. 사람이 들어도 애매하다.

기준을 문장으로 적어 두면 다음에 후보가 올 때 다시 토론하지 않는다. 앞선 TIL 19에서 「칸을 늘릴 때 다른 축과 만나 몇 개의 조합이 되는지 센다」를 배웠는데, 그건 늘려도 되는지를 세는 방법이고 이건 애초에 자격이 있는지를 묻는 문장이다. 둘이 같이 있어야 칸이 함부로 늘지 않는다.


구분되지 않는 것은 나누지 않는다

명절을 통과시키면서 바로 걸린 게 있다. 설날과 추석을 따로 둘 것인가.

나누지 않기로 했다. 둘 다 국악 편성의 축하 음악이고, 곡만 듣고는 어느 명절인지 가를 수 없다. 나누면 칸은 둘이 되는데 그 안의 곡은 구분되지 않아서, 고른 사람이 두 칸에서 같은 곡을 보게 된다.

여기서 소재와 성격과 기분이 서로 다른 축이라는 것도 정리됐다. 크리스마스는 소재고, 국악 편성은 소리의 성격이며, 활기찬 축하는 기분이다. 「명절 음악」을 분위기에 넣으면 「명절스러운 기분」이라는 말이 되는데 그런 기분은 없다.

분류 칸을 만들 때 그 이름이 무엇을 가리키는지 한 번 더 본다. 소재인지, 소리의 성격인지, 기분인지, 아니면 쓰임인지. 축이 이미 넷 있으니 대개는 들어갈 자리가 정해져 있다.


한 달 몫이 정해져 있어 하나를 고르면 나머지가 밀린다

할로윈은 자격이 있을 수도 있었다. 으스스한 오르간이나 단조 왈츠 같은 관용구가 잡히면 통과다. 그런데 이번 달 몫이 반쯤 남았고 다음 달 몫도 정해져 있다. 할로윈을 채우자면 그 몫을 거기에 다 써야 한다.

미뤘다. 이유는 관용구가 약할까 봐가 아니라, 할로윈이 오기 전에 이 앱이 팔릴지가 불확실해서다. 아직 손님이 없는데 시즌 한 칸을 채우는 데 두 달 몫을 쓰면, 정작 얇아서 결과가 한두 곡뿐인 조합은 그대로 남는다.

미루는 조건도 날짜가 아니라 상태로 걸었다. 앞선 TIL 2에서 「미루는 일에는 날짜가 아니라 조건을 붙인다」고 적었는데, 그때는 「두 번째 고객이 오면」처럼 그 일이 필요해지는 순간이었다. 이번 조건은 「카탈로그가 두꺼워지고 팔린 뒤」라 성격이 다르다. 필요해지는 때가 아니라 감당할 수 있게 되는 때다.

예산이 정해진 자원에서는 무엇을 할지 고르는 일이 곧 무엇을 밀어낼지 고르는 일이다. 정해진 한 달 몫은 그걸 눈에 보이게 만든다.


비어 보이는 것이 없는 것은 아니었다

내부 테스트를 돌리다 무료 체험 카드의 종료 날짜가 「-」로 떴다. 구독 화면의 다음 결제 날짜도 같았다. 상태는 「체험 중」으로 맞고 재생도 열렸는데 날짜만 비어 있었다.

서버에는 날짜가 있었다. 앱이 못 읽은 것이다. 서버가 주는 시각이 두 형식인데, 대부분은 시간대 표시가 없고 구독 만료일만 표시가 이미 붙어 있다. 앱이 모든 값에 표시를 덧붙이다가 뒤엣것이 깨졌다.

읽는 쪽을 한 함수로 모으고, 표시가 이미 있으면 그대로 읽게 했다. 화면마다 각자 붙이다 난 문제였다.

  • 한 값에 형식이 둘 섞이면 읽는 쪽을 한 곳으로 모은다. 쓰는 쪽을 통일하는 게 정석이지만 남이 만든 형식이 섞여 있으면 그럴 수 없다.
  • 「비어 보인다」를 데이터가 없다고 읽지 않는다. 없는 것과 못 읽는 것은 고치는 자리가 다르다.

안 보이는 오류는 없는 오류와 같다

같은 테스트에서 로그인에 틀린 비밀번호를 넣었다. 아무 일도 안 일어나는 것처럼 보였다. 오류 문구는 폼 아래에 떠 있었는데 키보드가 그 자리를 덮고 있었다.

버튼을 누르면 키보드를 내리게 고쳤다. 오류를 만든 것과 오류를 보여 주는 것은 다른 일이고, 보여 주지 못하면 만들지 않은 것과 같다.

같은 날 더 큰 예가 나왔다. 서버가 실패하자 앱이 「예기치 않은 문자」라고 띄웠다. 개발자에게는 단서지만 매장에게는 아무 뜻이 없다. 무엇을 해야 할지도 알 수 없다. 서버가 사람이 읽을 수 없는 응답을 줄 때 앱이 「지금 서비스에 문제가 있어요」로 바꿔 말해 주는 일이 남았다.

기능을 다 만들고 나서 마지막으로 물을 것은 「잘못됐을 때 쓰는 사람이 무엇을 보게 되나」다. 거기까지가 그 기능이다.


원인인 줄 알았던 작업이 13%였다

그 「예기치 않은 문자」의 원인은 우리 코드가 아니었다. 데이터베이스 무료 요금제의 하루 읽기 한도가 소진돼 있었다. 로그인은 실패 횟수를 기록하려고 데이터베이스를 건드리는데 그게 막혀 오류가 됐고, 앱이 그 오류 본문을 읽다 터진 것이다.

짐작은 바로 나왔다. 낮에 전수 작업을 돌렸기 때문이다 — 곡을 분류하고 조합을 세느라 곡 목록을 통째로 열 번 넘게 다시 받는 일이다. 저게 원인이겠거니 하고 요금제를 올려 한도를 풀었다.

재 봤다. 어떤 요청이 몇 행을 읽었는지 쿼리별로 남는 집계가 있었다. 이 집계는 상위 쿼리만 표본으로 보여 주고 지금부터 24시간을 뒤로 세는 창이라 총합이 청구 기준과 다르다 — 표본 총합은 454만이었다. 몫을 보는 데는 쓸 수 있다.

그 안에서 전수 작업은 57만 행, 13%였다. 짐작이 가리킨 대상 자체는 맞았는데 몫이 작았다. 가장 많이 먹은 것은 태그 목록을 내려 주는 요청으로 180만 행, 40%였다. 이 요청이 돌려주는 값은 태그 37개인데 그걸 고르려고 매번 1만 행을 읽는다. 곡과 태그를 잇는 표가 4,894줄인데 「지금 쓰이고 있는 태그만」을 알아내려고 그 표를 전수로 훑기 때문이다. 그다음은 재생목록 미리보기로 216만 행, 48%였다 — 홈 화면을 한 번 열면 재생목록 일곱 개의 곡 수와 길이를 그때그때 세운다.

합치면 진단이 뒤집힌다. 한도를 먹은 건 내가 돌린 무거운 작업이 아니라 평소 트래픽이었다. 앱을 열고 닫는 것만으로 나가는 요청이 88%를 차지했으니, 그날 전수 작업을 한 번도 안 돌렸어도 한도 언저리였다. 여유가 있는데 내가 다 써 버린 게 아니라, 처음부터 여유가 없었다.

앞선 TIL 17에서 「가끔 실패하는 한도는 양이 늘면 자주가 된다」를 적었다. 그때는 곡을 다시 굽는 작업이 잘리는 이야기였고 실패해도 다시 돌리면 되는 일이었다. 이번에는 같은 한도가 서비스를 멈췄다. 개발하며 쓰는 몫과 손님이 쓰는 몫이 같은 지갑에서 나오기 때문이다.

  • 실측 전에 원인을 확정하지 않는다. 눈에 띄는 작업이 범인처럼 보여도 재 보면 몫이 작을 수 있고, 확정해 버리면 엉뚱한 것을 고치게 된다.
  • 한도를 재는 단위는 「몇 번 불렀나」가 아니라 「한 번에 몇 행을 읽나 × 몇 번」이다. 37개를 받으려고 1만 행을 읽는 요청은 하루 500번이 상한이다.
  • 요금제를 올린 것은 시간을 산 것이지 원인을 고친 게 아니다. 한도가 넓어지면 고칠 동기가 사라지므로 무엇을 고칠지를 같이 정해 둔다.
  • 무료 한도를 쓰는 서비스는 「언제 돈을 낼지」를 손님 수로만 재면 늦는다. 손님이 없는데 개발자가 먼저 한도를 치는 날이 온다.

어디서 확인할지가 무엇을 확인할지를 정한다

심사에 낼 시연 영상을 에뮬레이터로 찍기로 했다. 폰 화면에 개인 정보가 비치는 걸 피하려는 이유였다. 그런데 에뮬레이터에서 재생 소리가 버벅였다. 룸톤은 곡을 앱 안에서 풀어 재생하는데 그 처리가 에뮬레이터에 무겁다. 실기기에서는 화면을 끈 채 열세 곡이 끊기지 않았다.

그래서 확인할 것을 갈랐다. 소리가 실제로 매끄러운지는 실기기에서, 화면 흐름은 에뮬레이터에서 본다.

영상도 소리 없이 찍기로 했다. 심사자가 확인하는 건 「앱을 나간 뒤에도 재생이 계속되고 알림에서 조작할 수 있나」다. 음질은 보는 항목이 아니다.

제출 자료를 만들 때 완벽하게 만들려 들기 전에 「이 자료가 무엇을 증명해야 하나」를 먼저 본다. 증명할 것이 정해지면 대개 그보다 적게 만들어도 된다.


코드가 바뀌면 스토어 답안을 먼저 고친다

어제 재생 기록에 계정을 붙였다. 오늘 스토어 데이터 보안 답안을 보니 「곡 재생 기록은 계정과 연결하지 않는다」가 그대로 있었다. 심사는 이 답안과 앱이 실제로 모으는 정보를 대조한다. 그대로 냈으면 서로 다른 말이 된다.

답안을 고치면서 하나 더 걸렸다. 우리 문서에 「계정과 연결」이라는 칸을 두고 있었는데, 그건 애플 앱스토어의 말이고 구글 양식에는 없는 질문이다. 구글은 일시 처리인지, 필수인지, 무슨 목적인지를 묻는다. 내가 그 칸을 콘솔 입력값처럼 안내해서 한동안 엉뚱한 자리를 찾게 만들었다.

목적도 바꿨다. 처음엔 「앱 기능」이라고 했는데, 플랫폼 설명을 열어 보니 맞춤설정의 예시가 「청취 습관에 따른 재생목록 추천」으로 우리 경우와 똑같았다. 앱 기능은 로그인이나 권한 확인처럼 그게 없으면 앱이 안 도는 것에 쓴다.

  • 모으는 정보를 바꾸면 그날 스토어 답안을 같이 고친다. 제출 직전에 몰아서 보면 무엇이 달라졌는지 기억나지 않는다.
  • 플랫폼 용어를 다른 플랫폼 것과 섞어 쓰지 않는다. 같은 뜻처럼 보이는 말이 서로 다른 질문일 수 있다.
  • 분류 항목을 고를 때는 짐작하지 말고 그 플랫폼이 붙여 둔 예시를 읽는다. 예시가 우리 경우와 같은 문장으로 적혀 있을 때가 많다.

아이디가 뭐냐는 질문에 답이 없었다

심사용 계정 비밀번호를 새로 정해야 했다. 그건 운영자만 발급할 수 있어서 관리자로 들어가야 하는데, 관리자 아이디가 뭔지 기억이 안 났다.

찾아보니 아이디가 없었다. 관리자는 계정이 아니라 서버가 들고 있는 비밀값 하나였다.

그래서 질문 자체가 틀렸다. 답이 안 나온 게 아니라 답이 없는 질문이었다.

  • 권한이 늘 계정에 붙어 있다고 가정하지 않는다. 사람이 하나뿐인 자리는 계정 없이 비밀값 하나로 두는 게 더 간단할 때가 있다.
  • 대신 그런 자리는 잃어버려도 되찾을 데가 없다. 다행히 덮어쓰면 되는 구조였다 — 지금 값을 몰라도 새 값을 넣으면 그만이다. 되찾는 길이 없는 대신 다시 정하는 길이 열려 있어야 한다.

링크를 손으로 옮기다 끝이 잘렸다

관리자로 들어가 재설정 링크를 뽑았다. 열자마자 「링크가 만료되었거나 이미 사용되었습니다」가 떴다. 방금 만든 링크였다.

원인은 링크가 아니라 복사였다. 화면의 긴 주소를 손으로 긁다가 끝이 잘렸다. 복사 버튼을 눌러 다시 하니 됐다.

여기서 두 가지가 걸렸다.

첫째, 긴 비밀을 사람이 옮기게 만들면 잘린다. 화면에 보이니까 옮길 수 있다고 본 건데, 옮기는 사람은 끝까지 긁었는지 확인할 방법이 없다.

둘째, 문구가 원인을 가렸다. 이 링크에는 서명(누가 만든 링크인지 확인하려고 서버가 붙여 둔 확인용 값)이 들어 있는데, 주소가 잘리면서 그게 안 맞게 된 것이다. 그런데 화면은 「만료되었거나 이미 사용되었습니다」라고 했다. 잘린 걸 의심할 단서가 없어서 시간만 썼다. 서로 다른 실패를 한 문구로 묶으면 쓰는 사람이 무엇을 고쳐야 할지 모른다.

  • 사람이 옮길 것은 옮길 수 있는 길이로 만든다. 못 줄이면 옮기지 않아도 되게 한다.
  • 실패 문구는 원인별로 가른다. 뭉뚱그린 문구는 친절한 게 아니라 정보를 지운 것이다.

고치는 사람이 한 명이면 그 사람이 병목이다

그 링크는 원래 매장을 위한 것이다. 매장이 비밀번호를 잊으면 지원 메일로 알려 오고, 그러면 우리가 링크를 만들어 보낸다. 자동 발송이 붙어 있지 않다.

매장이 하나면 되는데 열 곳이면 안 된다. 새벽에 음악이 끊긴 매장이 우리를 못 찾으면 그 시간 내내 안 되는 거다.

셀프로 가입하고 셀프로 결제하게 만들어 놓고 복구만 사람 손에 걸어 뒀다는 게 앞뒤가 안 맞는다. 손님이 알아서 들어오게 만들었으면 알아서 나가고 알아서 돌아올 수도 있어야 한다.

  • 자동화한 곳과 사람이 남은 곳이 짝이 맞는지 본다. 어긋난 자리는 손님이 늘 때 제일 먼저 터진다.
  • 손이 가는 일은 지금 감당되는지가 아니라 열 배가 됐을 때 감당되는지로 본다.

메일을 받는 사람과 기기를 쥔 사람이 다르다

그래서 자동 발송을 붙이기로 하고, 링크를 보낼지 인증번호를 보낼지를 정해야 했다.

처음엔 앱을 나가지 않아도 된다는 이유로 번호가 낫겠다고 봤다. 그런데 그건 부차적인 이유였다.

진짜 이유는 이거다. 법인 고객은 가입 메일이 대표 주소일 수 있다. 계산대에 선 직원은 그 메일함을 못 연다. 링크는 메일함을 여는 사람만 쓸 수 있어서 그 자리에서 못 푼다. 번호는 사장이 보고 전화로 불러 주면 직원이 앱에 친다.

같은 결론이어도 근거가 달라지면 규격이 달라진다. 전화로 불러 줄 것을 전제하니 이런 것들이 정해졌다. 아직 만들지는 않았고, 만들 때 지킬 것들이다.

  • 숫자만 쓴다. 영문을 섞으면 전화로 부를 때 O와 0, I와 1에서 틀린다
  • 세 자리씩 끊어 보여 준다
  • 유효시간을 길게 잡는다. 사장이 자리에 없을 수 있어서 불러 주기까지 시간이 걸린다
  • 메일에 어느 매장 계정인지 적는다. 대표 메일함에는 여러 건이 들어온다

그리고 짧게 만든 대가를 다른 데서 치러야 한다. 여섯 자리는 백만 가지뿐이라 자동으로 계속 넣어 보면 뚫린다. 틀린 횟수를 조이고, 한 번 쓰면 버리고, 발급 자체도 제한한다. 이건 선택이 아니라 번호 방식을 고른 순간 따라오는 값이다.

  • 결론이 같아도 근거를 확인한다. 근거가 바뀌면 만들 물건이 바뀐다.
  • 「누가 그걸 받나」와 「누가 그걸 쓰나」를 따로 묻는다. 개인 고객만 생각하면 둘이 같은 사람이라 질문이 떠오르지 않는다.
  • 편하게 만든 만큼 어딘가 약해진다. 약해진 자리를 같이 막지 않으면 편해진 게 아니라 구멍이 난 것이다.

새 기능의 값을 묻기 전에 이미 낸 것을 본다

붙이기로 했으니 얼마나 드는지가 다음 질문이었다.

추가 비용이 없었다. 메일 발송이 오늘 올린 요금제에 월 삼천 통까지 포함돼 있었다. 그것도 모르고 외부 메일 업체를 알아볼 뻔했다.

거꾸로 보면 더 재미있다. 무료 요금제에서는 임의 주소로 메일을 보내는 것 자체가 안 된다. 오늘 데이터베이스 한도 때문에 낸 돈이 몇 시간 뒤에 메일 기능을 열어 준 셈이다.

  • 새 기능을 붙이기 전에 지금 쓰는 플랫폼에 이미 있는지 본다. 업체를 하나 더 들이면 계정·요금·장애 지점이 하나씩 는다.
  • 요금제를 올릴 때는 그게 무엇을 같이 열어 주는지도 본다. 한 가지 문제 때문에 올렸는데 다른 문제의 답이 딸려 오기도 한다.

지운 번호도 플랫폼은 기억한다

번들을 올리려니 버전 번호 7이 이미 쓰인 것이라며 거절당했다. 그 번호로 올렸다가 지운 적이 있어서 비었다고 봤는데 아니었다.

스토어는 한 번 받은 번호를 영구히 기억한다. 지우는 건 내 화면에서 치우는 것이지 그쪽 기록에서 지우는 게 아니다. 8로 올려 다시 빌드했다. 코드는 한 글자도 안 바뀌었다.

  • 번호를 매기는 건 우리인데 그 번호를 기억하는 건 상대다. 회수되는 식별자와 안 되는 식별자를 구별해 둔다.
  • 되돌릴 수 있어 보이는 조작이 어디까지 되돌리는지 확인한다. 「지웠다」가 「없던 일이 됐다」는 아니다.

메일 주소가 하나인 회사는 어떻게 하나

제출을 마치고 문득 걸려서 물어봤다. 우리는 계정을 이메일로 가른다. 그런데 매장을 여럿 가진 회사가 메일 주소는 하나만 쓰면 어떻게 되나.

계정이 안 갈린다. 메일이 곧 아이디라서, 주소가 하나면 계정도 하나다. 매장이 다섯이어도 하나만 만들 수 있다.

식별자를 무엇에 묶느냐가 그대로 제약이 된다는 걸 여기서 봤다. 이메일은 우리가 발급하는 게 아니라 손님이 다른 데서 받아 오는 것이다. 편해 보여서 골랐는데, 손님 쪽 사정이 우리 제품의 한계가 됐다.

  • 아이디를 남이 발급한 것에 묶으면 그쪽 사정이 그대로 우리 제약이 된다.
  • 「한 사람이 하나만 쓴다」는 가정이 깔려 있지 않은지 본다. 개인 손님만 생각하면 그 가정이 안 보인다.

막힌 곳은 메일이 아니었다

메일을 여러 개 만들게 하면 풀리는 줄 알았다. 실제로 방법도 있었다.

그런데 그다음이 막혔다. 구글 결제는 같은 구독 상품을 한 계정이 두 번 살 수 없다. 다시 사면 새 구독이 기존 것을 밀어낸다. 그리고 우리는 계정당 동시 재생을 1대로 묶어 놨다. 매장 다섯을 한 계정으로 돌리는 건 애초에 안 되는 방향이었다.

눈에 보이는 막힘은 이메일이었는데 실제로 못 지나가는 문은 두 개 더 뒤에 있었다. 이메일만 고쳤으면 고쳐 놓고도 안 됐을 것이다.

  • 막힌 곳을 하나 찾으면 거기서 멈추지 말고 그 뒤를 마저 본다. 앞의 것을 고쳐도 뒤가 남아 있으면 헛일이다.
  • 우리가 건 제한도 같이 센다. 동시 재생 1대는 도난을 막으려고 우리가 건 것인데, 그게 체인 고객을 막는 벽이 되기도 한다.

「한국은 되지 않나」 — 규칙에 날짜가 붙어 있었다

그래서 체인은 앱이 아니라 웹에서 결제해야 한다는 결론이 나왔다. 그런데 앱에서 판매 페이지로 링크를 걸 수 없다고 했다. 구글 플레이 정책상 앱 밖 결제 유도가 막혀 있다는 것이었다.

한국은 개발자가 자기 결제를 붙일 수 있게 법이 바뀌지 않았나 싶어 되물었다. 찾아보니 그보다 더 나간 게 있었다. 구글 플레이가 수수료를 낮추고 외부 결제와 바깥 링크를 허용하기로 했는데, 적용 시기가 지역마다 달라서 한국은 2026년 12월 적용 예정이다(2026-09-28 확인).

그러면 두 달 뒤엔 앱에서 판매 페이지로 정식으로 보낼 수 있다. 그러면 지금 급하게 만들 게 없어진다. 매장 여럿을 묶는 구조를 새로 짜려던 걸 접었다.

  • 「안 된다」는 답에는 대개 날짜가 숨어 있다. 지금 안 되는 것인지, 앞으로도 안 되는 것인지 갈라 묻는다.
  • 규칙이 바뀔 날짜가 잡혀 있으면 그때까지 안 만드는 것도 설계다. 두 달 버티는 방법만 있으면 된다.
  • 플랫폼 정책은 알고 있는 것을 다시 확인하고 말한다. 자주 바뀌고, 바뀐 걸 모르면 있는 길을 없다고 말하게 된다. 적어 둘 때도 언제 확인한 건지 같이 적는다 — 이 글의 12월도 나중에 읽으면 어느 해인지 모른다.

요약

  • 값이 하나인 칸을 둘지는 값의 수가 아니라 「없으면 못 하는 일이 생기나」로 정한다.
  • 분류 칸에 값을 더할 자격을 문장 하나로 못박아 두면 후보가 올 때 다시 토론하지 않는다.
  • 구분되지 않는 것은 나누지 않는다. 칸만 늘고 같은 것이 두 번 보인다.
  • 이름이 소재인지 소리의 성격인지 기분인지 쓰임인지 가리면 들어갈 축이 정해져 있다.
  • 예산이 정해진 자원에서 무엇을 할지 고르는 일은 무엇을 밀어낼지 고르는 일이다.
  • 미루는 조건에는 「필요해지는 때」와 「감당할 수 있게 되는 때」가 따로 있다.
  • 한 값에 형식이 둘이면 읽는 쪽을 한 곳으로 모은다. 비어 보이는 것과 없는 것은 다르다.
  • 오류를 만든 것과 보여 주는 것은 다른 일이다. 기능은 「잘못됐을 때 무엇을 보게 되나」까지다.
  • 실측 전에 원인을 확정하지 않는다. 한도를 먹는 건 눈에 띄는 무거운 작업이 아니라 값싸 보이는데 자주 부르는 화면일 수 있다.
  • 제출 자료는 「무엇을 증명해야 하나」를 먼저 정하면 그보다 적게 만들어도 된다.
  • 모으는 정보를 바꾸면 그날 스토어 답안을 같이 고치고, 플랫폼 용어를 섞어 쓰지 않는다.
  • 권한이 늘 계정에 붙어 있지는 않다. 되찾을 길이 없는 자리는 다시 정할 길이 있어야 한다.
  • 사람이 옮길 것은 옮길 수 있는 길이로 만들고, 실패 문구는 원인별로 가른다.
  • 자동화한 곳과 사람이 남은 곳의 짝을 본다. 어긋난 자리가 손님이 늘 때 먼저 터진다.
  • 결론이 같아도 근거를 확인한다 — 「누가 받나」와 「누가 쓰나」가 다르면 만들 물건이 달라진다.
  • 편해진 만큼 약해진 자리를 같이 막는다. 안 막으면 편해진 게 아니라 구멍이 난 것이다.
  • 새 기능을 붙이기 전에 지금 요금제에 이미 들어 있는지 본다.
  • 「지웠다」가 「없던 일이 됐다」는 아니다. 회수되는 식별자와 안 되는 식별자를 구별한다.
  • 아이디를 남이 발급한 것에 묶으면 그쪽 사정이 우리 제약이 된다.
  • 막힌 곳을 하나 찾으면 그 뒤를 마저 본다. 우리가 건 제한도 벽이 될 수 있다.
  • 「안 된다」에는 날짜가 숨어 있다. 바뀔 날이 잡혀 있으면 그때까지 안 만드는 것도 설계다.

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 | 습관만들기 Pawbit TIL 1 27 | insertOnConflictUpdate vs DoUpdate(target) 28 | 세션 필터 29 | 아코디언(Accordiaon) UI를 펼친상태로 만들기 30 | input의 step 31 | 습관만들기 Pawbit TIL 2 32 | 습관만들기 Pawbit TIL 3 33 | Word Cloud 34 | Google Sheets를 데이터 버스로(with AppSheet) 35 | Django 모델 텍스트 필드 자동 수집 패턴 36 | localStorage로 섹션 토글 상태 유지 37 | 순차 ID 생성(`select_for_update()` + `max()` 조합) 38 | 역참조 검색과 distinct() 39 | xlsx 다운로드와 로딩 오버레이 충돌 40 | Android 파일 공유 MIME 타입 41 | AssetManifest — Flutter 빌드 타임 asset 목록 런타임 조회 42 | 크레센도 Phaser + TypeScript TIL 1 43 | 크레센도 Phaser + TypeScript TIL 2 44 | 크레센도 Phaser + TypeScript TIL 3 45 | 크레센도 Phaser + TypeScript TIL 4 46 | 크레센도 Phaser + TypeScript TIL 5 47 | 여둘까 Office Layout TIL 1 48 | 습관만들기 Pawbit TIL 4 49 | 여둘까 Office Layout TIL 2 50 | 여둘까 Office Layout TIL 3 51 | 크레센도 Phaser + TypeScript TIL 6 52 | UTF-8 BOM과 PowerShell 파일 쓰기 53 | 소리꽃 KeyBloom TIL 1 54 | 습관만들기 Pawbit TIL 5 55 | 여둘까 Office Layout TIL 4 56 | 소리꽃 KeyBloom TIL 2 57 | 소리꽃 KeyBloom TIL 3 58 | 메트로놈 Simple Metronome TIL 1 59 | 소리꽃 KeyBloom TIL 4 60 | 메트로놈 Simple Metronome TIL 2 61 | 소리꽃 KeyBloom TIL 5 62 | 습관만들기 Pawbit TIL 6 63 | 크레센도 Phaser + TypeScript TIL 7 64 | 메트로놈 Simple Metronome TIL 3 65 | 소설 타임라인 관리 도구 Lumior TIL 1 66 | 습관만들기 Pawbit TIL 7 67 | 정적 블로그 SEO 정비와 Pagefind 검색 도입 68 | 크레센도 Phaser + TypeScript TIL 8 69 | 메트로놈 Simple Metronome TIL 4 70 | 습관만들기 Pawbit TIL 8 71 | 안드로이드 AudioTrack 연속 재생, 실측 피커 정렬, 카메라 토치 플래시 72 | 메트로놈 Simple Metronome TIL 5 73 | 모래게임 Sandrop TIL 1 74 | 습관만들기 Pawbit TIL 9 75 | 메트로놈 Simple Metronome TIL 6 76 | 모래게임 Sandrop TIL 2 77 | 모래게임 Sandrop TIL 3 78 | 습관만들기 Pawbit TIL 10 79 | 습관만들기 Pawbit TIL 11 80 | 메트로놈 Simple Metronome TIL 7 81 | 모래게임 Sandrop TIL 4 82 | 모래게임 Sandrop TIL 5 83 | 습관만들기 Pawbit TIL 12 84 | 모래게임 Sandrop TIL 6 85 | 소리꽃 KeyBloom TIL 6 86 | 모래게임 Sandrop TIL 7 87 | 모래게임 Sandrop TIL 8 88 | 소리꽃 KeyBloom TIL 7 89 | 모래게임 Sandrop TIL 9 90 | 소리꽃 KeyBloom TIL 8 91 | 모래게임 Sandrop TIL 10 92 | 소리꽃 KeyBloom TIL 9 93 | 콘티온 ContiOn TIL 1 94 | 소리꽃 KeyBloom TIL 10 95 | 콘티온 ContiOn TIL 2 96 | 습관만들기 Pawbit TIL 13 97 | 콘티온 ContiOn TIL 3 98 | 룸톤 Roomtone TIL 1 99 | 모래게임 Sandrop TIL 11 100 | 소리꽃 KeyBloom TIL 11 101 | 여둘까 Office Layout TIL 5 102 | 모래게임 Sandrop TIL 12 103 | 모래게임 Sandrop TIL 13 104 | 콘티온 ContiOn TIL 4 105 | 모래게임 Sandrop TIL 14 106 | 소리꽃 KeyBloom TIL 12 107 | 습관만들기 Pawbit TIL 14 108 | 모래게임 Sandrop TIL 15 109 | 모래게임 Sandrop TIL 16 110 | 소리꽃 KeyBloom TIL 13 111 | 온실 GreenHouse TIL 1 112 | 콘티온 ContiOn TIL 5 113 | 모래게임 Sandrop TIL 17 114 | 소리꽃 KeyBloom TIL 14 115 | 온실 GreenHouse TIL 2 116 | 콘티온 ContiOn TIL 6 117 | 모래게임 Sandrop TIL 18 118 | 콘티온 ContiOn TIL 7 119 | 모래게임 Sandrop TIL 19 120 | 소리꽃 KeyBloom TIL 15 121 | 룸톤 Roomtone TIL 2 122 | 모래게임 Sandrop TIL 20 123 | 모래게임 Sandrop TIL 21 124 | 소리꽃 KeyBloom TIL 16 125 | 소리꽃 KeyBloom TIL 17 126 | 소리꽃 KeyBloom TIL 18 127 | 여둘까 Office Layout TIL 6 128 | 소리꽃 KeyBloom TIL 19 129 | 소리꽃 KeyBloom TIL 20 130 | 콘티온 ContiOn TIL 8 131 | 콘티온 ContiOn TIL 9 132 | 로그스톤 상표 셀프 출원 TIL 1 133 | 소리꽃 KeyBloom TIL 21 134 | 로그스톤 샵 LogStone Shop TIL 1 135 | 소리꽃 KeyBloom TIL 22 136 | 로그스톤 샵 LogStone Shop TIL 2 137 | 룸톤 Roomtone TIL 3 138 | 소리꽃 KeyBloom TIL 23 139 | 모래게임 Sandrop TIL 22 140 | 모래게임 Sandrop TIL 23 141 | 소리꽃 KeyBloom TIL 24 142 | 로그스톤 샵 LogStone Shop TIL 3 143 | 모래게임 Sandrop TIL 24 144 | 소리꽃 KeyBloom TIL 25 145 | 모래게임 Sandrop TIL 25 146 | MiniMacro TIL 1 147 | 룸톤 Roomtone TIL 4 148 | 모래게임 Sandrop TIL 26 149 | 콘티온 ContiOn TIL 10 150 | Astro가 무엇인지, 왜 옮기는지 151 | 룸톤 Roomtone TIL 5 152 | 룸톤 Roomtone TIL 6 153 | 모래게임 Sandrop TIL 27 154 | 소리꽃 KeyBloom TIL 26 155 | MiniMacro TIL 2 156 | 로그스톤 샵 LogStone Shop TIL 4 157 | 메트로놈 Simple Metronome TIL 8 158 | 모래게임 Sandrop TIL 28 159 | 여둘까 Office Layout TIL 7 160 | 룸톤 Roomtone TIL 7 161 | 메트로놈 Simple Metronome TIL 9 162 | 블로그 Astro 이관 TIL 163 | 여둘까 Office Layout TIL 8 164 | 로그스톤 샵 LogStone Shop TIL 5 165 | 모래게임 Sandrop TIL 29 166 | 오픈데이 Openday TIL 1 167 | 로그스톤 샵 LogStone Shop TIL 6 168 | 모래게임 Sandrop TIL 30 169 | 오픈데이 Openday TIL 2 170 | 모래게임 Sandrop TIL 31 171 | Unreal Engine MCP TIL 1 172 | 모래게임 Sandrop TIL 32 173 | 소리꽃 KeyBloom TIL 27 174 | 콘티온 ContiOn TIL 11 175 | 오픈데이 Openday TIL 3 176 | 로그스톤 샵 LogStone Shop TIL 7 177 | 모래게임 Sandrop TIL 33 178 | 소리꽃 KeyBloom TIL 28 179 | 오픈데이 Openday TIL 4 180 | 여둘까 Office Layout TIL 9 181 | 오픈데이 Openday TIL 5 182 | ScorePlayer TIL 1 183 | 모래게임 Sandrop TIL 34 184 | BuildingHub TIL 1 185 | ScorePlayer TIL 2 186 | 여둘까 Office Layout TIL 10 187 | BuildingHub TIL 2 188 | ScorePlayer TIL 3 189 | Unreal Engine MCP TIL 2 190 | 궁 미로 PalaceMaze TIL 1 191 | 메트로놈 Simple Metronome TIL 11 192 | 메트로놈 Simple Metronome TIL 10 193 | 오픈데이 Openday TIL 6 194 | BuildingHub TIL 3 195 | Obsidian vault에서 코드만 빼기 — directory junction 196 | ScorePlayer TIL 4 197 | 여둘까 Office Layout TIL 11 198 | 오픈데이 Openday TIL 7 199 | URL은 바뀔 수 있는 것에 묶지 않는다 200 | 도구는 기능이 아니라 내 일에 맞는지로 고른다 201 | 돌이키기 힘든 변경은 작게 먼저 확인한다 202 | 궁 미로 PalaceMaze TIL 3 203 | 궁 미로 PalaceMaze TIL 2 204 | 소리꽃 KeyBloom TIL 29 205 | 오픈데이 Openday TIL 8 206 | 룸톤 Roomtone TIL 8 207 | BuildingHub TIL 4 208 | ScorePlayer TIL 5 209 | 로그스톤 샵 LogStone Shop TIL 8 210 | 여둘까 Office Layout TIL 12 211 | 오픈데이 Openday TIL 9 212 | BuildingHub TIL 5 213 | MiniMacro TIL 3 214 | 룸톤 Roomtone TIL 10 215 | 룸톤 Roomtone TIL 9 216 | 소리꽃 KeyBloom TIL 30 217 | 오픈데이 Openday TIL 10 218 | 여둘까 Office Layout TIL 13 219 | 룸톤 Roomtone TIL 11 220 | 소리꽃 KeyBloom TIL 31 221 | 궁 미로 PalaceMaze TIL 4 222 | 룸톤 Roomtone TIL 12 223 | 로그스톤 샵 LogStone Shop TIL 9 224 | 룸톤 Roomtone TIL 13 225 | 로그스톤 샵 LogStone Shop TIL 10 226 | 룸톤 Roomtone TIL 14 227 | 소리꽃 KeyBloom TIL 32 228 | 룸톤 Roomtone TIL 15 229 | 룸톤 Roomtone TIL 16 230 | 로그스톤 샵 LogStone Shop TIL 11 231 | 룸톤 Roomtone TIL 17 232 | 룸톤 Roomtone TIL 18 233 | 메트로놈 Simple Metronome TIL 12 234 | 콘티온 ContiOn TIL 12 235 | 소리꽃 KeyBloom TIL 33 236 | 온실 GreenHouse TIL 3 237 | 로그스톤 샵 LogStone Shop TIL 12 238 | 룸톤 Roomtone TIL 19 239 | 모래게임 Sandrop TIL 35 240 | 여러 세션을 INBOX 하나로 굴리기 241 | 궁 미로 PalaceMaze TIL 5 242 | CS2 Practice Host TIL 1 243 | 룸톤 Roomtone TIL 20 읽는 중 244 | 여둘까 Office Layout TIL 14