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

나선 모션을 손보고 파티클을 상자에 가두는 옵션을 만들며 나온 것들.

기능을 합치고 나누기

달라 보이는 둘이 같은 것의 설정 차이일 수 있다

모션 목록에 “나선형”과 “나선 기둥”이 따로 있었다. 하나는 위로 갈수록 넓어지는 원뿔이고 하나는 폭이 일정한 원통이다.

앞선 TIL 6에서 나선 기둥을 만들 때 봤듯이 계산은 같고 값 하나만 다르다. 넓어지는 정도가 0이면 기둥, 0보다 크면 원뿔이다. 그래서 그 값을 슬라이더로 꺼내고 둘을 하나로 합쳤다. 목록이 짧아졌을 뿐 아니라, 원래 없던 그 사이의 모든 모양이 열렸다.

기능 목록이 길어질 때 한 번쯤 볼 만하다. 나란히 놓인 항목들이 사실 한 축의 양 끝인 경우가 있다. 그러면 항목을 줄이면서 표현은 오히려 늘어난다.

하나의 슬라이더가 두 가지를 바꾸면 조합을 못 만든다

“광택”이라는 슬라이더 하나가 사실 두 가지를 동시에 조절하고 있었다. 건반 표면의 유리 같은 반짝임(sheen)과, 건반 위로 뻗어 나가는 발광(글로우)이다. 코드에서는 이미 다른 곳에서 그려지고 있었는데 슬라이더만 공유했다.

이러면 “표면은 반짝이되 위로 뻗는 빛은 없음” 같은 조합을 만들 수가 없다. 사용자가 원하는 그림이 그 조합이면 방법이 없다.

둘로 나누고 나서 하나 더 정리했다. 빛의 세기를 0부터 1까지 하나로 주던 것을, 켜고 끄는 것과 크기와 밝기 셋으로 나눴다. “아주 약한 빛”과 “빛 없음”의 경계가 모호했는데, 켜고 끄는 스위치가 따로 있으니 의도가 분명해졌다.

슬라이더 하나가 두 가지를 움직이고 있으면 대개 나누는 게 맞다. 개수가 느는 게 부담스러우면, 자주 안 바꾸는 쪽을 접어 두면 된다.


얽힘을 푸는 법

불가능한 조합을 요구하고 있지는 않은지 먼저 본다

나선의 코일 간격을 넓히려고 슬라이더를 만졌는데, 간격을 넓히면 파티클이 날아가듯 빨라지고 속도를 늦추면 회전이 느려졌다. 원하는 그림을 못 만들고 슬라이더를 서로 반대로 올렸다 내렸다 하게 됐다.

이유는 셋이 한 식으로 묶여 있어서였다. 한 바퀴 도는 동안 얼마나 올라가느냐가 곧 코일 간격이니, 간격과 회전 빠르기와 상승 빠르기 중 둘을 정하면 나머지는 자동으로 정해진다. “넓은 간격에 빠른 회전에 느린 상승”은 애초에 불가능한 요구였다.

이걸 모르면 조절이 안 되는 게 구현 탓이라고 생각하게 된다. 원하는 그림이 안 나올 때 먼저 물어야 할 건 “왜 안 되지”가 아니라 “이 조합이 성립하기는 하나”다.

기준을 바꾸면 얽힘이 풀린다

그렇다고 포기할 필요는 없었다. 회전을 “시간이 지날수록”이 아니라 “높이 올라갈수록”으로 바꾸니 코일 간격이 상승 속도와 무관해졌다. 빨리 오르든 천천히 오르든 같은 높이마다 한 바퀴를 돈다.

같은 세 값이 여전히 묶여 있는데, 무엇을 기준으로 재느냐를 바꿨더니 사용자가 만지고 싶어 하던 값이 독립이 됐다.

얽혀 있는 값을 억지로 떼려 하기 전에, 기준을 다른 것으로 바꿔 볼 수 있다. 시간 기준을 공간 기준으로, 절대값을 비율로, 개수를 용량으로 바꾸는 식이다. 기준이 바뀌면 무엇이 독립인지도 바뀐다.

슬라이더를 늘리기 전에 중복부터 덜어낸다

기준을 바꾸고 나니 나선의 “회전 속도” 슬라이더가 결국 공용 속도 슬라이더와 같은 것을 조절하고 있었다. 하나를 지웠다.

컨트롤이 부족해 보일 때 반사적으로 추가하게 되는데, 이미 있는 것 중에 같은 일을 하는 게 있는지 먼저 보는 게 낫다. 중복된 슬라이더는 사용자에게 “둘 다 만져야 하나”라는 부담만 준다.


여럿을 다룰 때

같은 시점에 벽에 닿으면 다 같이 멈춘다

파티클을 정해진 폭 안에 가두려고 여러 방법을 거쳤다. 넘으면 그 자리에 고정하는 하드 클램프를 걸었더니 벽에 붙어 멈칫거렸고, 폭에 맞춰 좌우로 흔들게(사인 진동) 했더니 사인은 양 끝에서 속도가 0이라 다 같이 느려져 여전히 어색했고, S자 곡선으로 부드럽게 눌러 담았더니 그 곡선이 양 끝에서 포화돼 벽 근처에 정체가 생겼다.

답은 개체마다 흔들리는 폭을 다르게 준 것이었다. 벽에 닿는 시점이 흩어지니 어떤 것은 한가운데를 빠르게 지나는 중이라, 전체적으로는 매끈하게 도는 기둥으로 보인다.

여럿을 같은 규칙으로 다루면 그 규칙이 만드는 순간이 한꺼번에 온다. 경계를 다루는 방식이 문제가 아니라 다 같이 경계에 닿는 것이 문제였다.


사용자 파일

저장 형식을 바꾸면 옛 파일이 깨진다

빛 설정을 숫자 하나에서 스위치와 크기와 밝기로 바꿨고, 모션 두 개를 하나로 합쳤다. 그러면 예전에 저장해 둔 작업 파일에는 없는 항목이 생기고, 없어진 모션 이름이 들어 있게 된다.

그래서 파일을 읽을 때 옛 형태를 알아보고 지금 형태로 옮기게 했다. 없는 항목은 기본값으로 채우고, 옛 빛 설정 숫자가 0보다 크면 스위치를 켜진 것으로 보고, 없어진 모션 이름은 합쳐진 쪽으로 바꾼다.

사용자가 저장한 파일은 우리 것이 아니다. 형식을 고칠 자유는 있지만, 고친 만큼 옛 파일을 읽어 주는 코드를 같이 써야 한다. 이 코드는 한 번 쓰면 계속 남아서 늘어나기만 하니, 형식을 바꿀 때마다 “이 변경이 그 부담을 질 만한가”를 같이 따지게 된다.


남 탓과 내 탓

우리 코드부터 의심하기 전에 남의 것도 같은지 본다

웹에 붙인 카카오 애드핏 광고가 자동으로 안 바뀌길래 우리 쪽 코드를 뒤졌다. 되돌려도 보고 다른 페이지에서도 해 봤는데 그대로였다.

다른 사람의 블로그에서도 같은 광고가 안 바뀌는 걸 보고 나서야 알았다. 그 광고 서비스가 원래 페이지를 열 때 한 번만 그리고 끝내는 것이었다. 우리 코드는 처음부터 정상이었다.

바깥 서비스를 쓰는 부분이 이상할 때, 우리 코드를 파기 전에 “다른 데서도 그런가”를 먼저 보면 훨씬 빠르다. 우리 코드가 원인이면 우리만 그렇고, 서비스가 원인이면 모두가 그렇다. 이 한 번의 확인이 방향을 정한다.


요약

  • 나란히 놓인 두 기능이 한 축의 양 끝인지 본다 — 합치면 항목은 줄고 표현은 는다
  • 슬라이더 하나가 두 가지를 움직이면 원하는 조합을 못 만든다
  • 조절이 안 될 때 “왜 안 되지”보다 “이 조합이 성립하기는 하나”를 먼저 묻는다
  • 얽힌 값은 기준을 바꾸면 풀린다 — 시간을 공간으로, 개수를 용량으로
  • 컨트롤이 부족해 보이면 추가하기 전에 중복부터 덜어낸다
  • 여럿을 같은 규칙으로 다루면 그 규칙이 만드는 순간이 한꺼번에 온다
  • 사용자가 저장한 파일은 우리 것이 아니다. 형식을 바꾼 만큼 옛 파일을 읽는 부담이 늘어난다
  • 바깥 서비스가 이상할 땐 “다른 데서도 그런가”를 먼저 본다

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