최종 갱신 (KST)

← 일감 목록
T-046

클라 — 배치 목록의 산업 필터 · 줄 프리팹화 · 창고 '배' 표시

태웅 Closed 우선순위 높음 목표 미정 · 보관됨

배경

작업슬롯 배치 목록(2단계)이 "지금 고를 수 있는 것"을 보여 주지 못한다. 셋이 한 화면에서 만난다.

  1. 산업을 눌러도 캐릭터가 걸러지지 않는다. 지금 산업 버튼은 적성 0인 줄을 잠그기만 하고 (CharacterStateRowView.SetAssignable) 목록에는 그대로 남긴다. 16마리를 기르면 낚시를 눌러도 16줄이 그대로 있고, 그중 누를 수 있는 것만 골라 봐야 한다.

  2. 배치된 캐릭터가 목록에 그대로 뜬다. 눌러서 배치하면 그 캐릭터가 어느 슬롯에서 빠져나오는지 화면에 없다.

  3. 🔴 줄이 1개뿐이라 나머지가 아예 안 보인다. 지금은 경고로 막고 있다.

    [UI] 보유 캐릭터가 16인데 깔아 둔 줄은 1개뿐이다. 줄을 늘리거나 프리팹으로 빼서 만들 것.
    WorkStationSelectPresenter:RefreshRows () (…:423)
    

    ⚠️ UI 배치 현황.md는 "씬에 21줄 깔아 두고 풀처럼 쓴다"고 적고 있는데 실제 씬은 1줄이다. 문서가 낡았다 — 프리팹화하면 이 불일치 자체가 사라진다.

⚠️ 이 일감은 기존 원칙을 뒤집는다

지금까지 이 화면의 규칙은 "숨기지 않고 잠근다" 였다. 근거는 WorkStationSelectPresenter 클래스 주석에 있다 — "목록에서 빼면 '이 캐릭터가 왜 안 보이지'가 되고, 적성이 오르는 수단이 붙었을 때 화면이 조용히 틀린다." T-035의 (a)도 같은 축이었다.

이 일감은 "건다"로 바꾼다. 사용자 결정이며, 성립하는 근거는 창고 캐릭터 탭이 답을 대신 주기 때문이다 — 보유 목록의 진실은 창고에 있고, 배치 화면은 "지금 이 슬롯에 넣을 수 있는 것" 만 보이면 된다. 그래서 아래 D(창고 '배' 표시)가 이 일감에서 빠질 수 없다.

🔴 남는 위험 하나 — 적성으로 걸러진 캐릭터는 창고도 답해 주지 못한다. 창고 캐릭터 칸에는 등급·이름·배치 상태만 있고 적성이 없다. "낚시를 눌렀더니 내 캐릭터가 없다"의 이유를 어디서도 볼 수 없게 된다. 창고 칸에 적성을 함께 보일지, 배치 목록에 "적성 없는 N마리는 숨겼다" 한 줄을 남길지를 B를 만들 때 정한다.


할 일

A가 먼저다. B·C는 목록의 내용을 바꾸는 일이라 줄이 1개인 채로는 확인할 수 없다.

A. Character State Row를 프리팹으로 (선행)

  • [x] 씬에 깔아 둔 줄을 프리팹 + 풀로 바꾼다 ⚠️ 창고 격자가 아니라 판매 줄(StorageInformationPresenter)을 베꼈다 — 아래 "진행" 참조 (GetOrCreateRow · HideRowsFrom · LayoutRebuilder.ForceRebuildLayoutImmediate)
  • [x] 파괴하지 않고 풀로 되돌린다 — 상주 앱이라 목록을 오갈 때마다 만들고 부수면 GC가 쌓인다
  • [x] ⚠️ 창고와 다른 점을 먼저 정한다. 창고는 프레임 200개가 씬에 고정으로 깔려 있고 코드는 그 안에 칸만 만든다. 캐릭터 목록은 보유 수가 곧 줄 수라 프레임을 씬에 깔면 그 개수가 그대로 상한이 된다 — 지금 겪는 문제가 형태만 바꿔 돌아온다. 프레임까지 코드가 만들지, 창고처럼 프레임을 깔고 상한을 둘지 정한다
  • [x] RefreshRows의 "줄이 모자라다" 경고를 걷어낸다 (그 상태가 없어진다)
  • [x] UI 배치 현황.md의 "21줄" 서술 정정

B. 산업 필터 — 고른 산업을 다루는 캐릭터만 보인다

  • [x] 산업 버튼을 누르면 그 산업 적성이 있는 캐릭터만 목록에 남긴다 (_data.GetAptitude > 0)
  • [x] ⚠️ RefreshRows가 _data.Characters를 인덱스로 그대로 줄에 태우고 있다. 걸러 내면 인덱스가 어긋나므로 걸러 낸 목록을 먼저 만들고 그것을 태운다
  • [x] CharacterStateRowView의 적성 0 잠금(SetAssignable의 _aptitude > 0)이 죽은 코드가 되는지 확인한다 — 걸러 냈으면 잠글 대상이 없다. 지우지 말고 판단해서 정한다
  • [x] 위 "남는 위험"의 안내를 어떤 형태로 둘지 정해 넣는다
  • [x] 📌 T-034·T-006이 남긴 "적성 0 사전 차단"이 여기서 닫힌다. 단 3단계 세팅의 산업 버튼은 여전히 적성을 보지 않는다(눌리고 서버가 NoAptitude로 거절). 거기도 막을지 함께 정한다

C. 배치된 캐릭터는 목록에서 뺀다

  • [x] 다른 슬롯에 배치 중인 캐릭터를 목록에서 제외한다
  • [x] 판정은 PlayerDataModel.FindSlotIndexOf 를 쓴다 — 창고 캐릭터 탭이 같은 것을 보므로 각자 WorkStationSlots를 훑으면 두 화면이 다른 말을 한다
  • [x] ⚠️ T-035의 (a)를 대체한다. 그쪽은 "숨기지 않고 표시한다" 였으므로 이 일감과 공존할 수 없다 — T-035에서 (a)를 걷어낸다

D. 창고 캐릭터 탭에 '배' 마크

  • [x] 배치 중인 캐릭터 칸 오른쪽 위에 마크를 띄운다 — 판매 담기의 '판'과 같은 자리·같은 방식
  • [x] 구조는 sellMark를 그대로 베낀다 — 프리팹에 마크 오브젝트 + SetXxxMark(bool) + Clear()에서 되돌리기 (InventorySlotView), 켜는 판단은 격자가 매번 그릴 때 (StorageGridPresenter.Redraw의 view.SetSellMark(…) 한 줄 옆)
  • [x] ⚠️ 자원 탭에서는 꺼져야 한다. 판매 표시가 IsSellableTab으로 탭을 가르듯, 배치 표시도 캐릭터 탭에서만 켜지는 축이 필요하다
  • [x] ⚠️ 기존 표시와 겹친다 — 어느 쪽을 남길지 정한다. CharacterSlotSource가 이미 보조 문구로 배치 중 / 배치 가능 을 내고 있다. 마크만 남길지, 문구를 다른 것으로 바꿀지(예: 적성), 둘 다 둘지 — 셋 다 두면 한 칸이 같은 말을 두 번 한다

진행 — 코드·에디터(2026-09-11) · 플레이 확인(2026-09-12) 전부 끝났다

결정한 것

갈림길 결정 근거
줄을 만드는 방식 프레임을 씬에 깔지 않는다. 코드가 프리팹을 찍어 풀로 재사용 창고는 프레임 200개가 상한을 정하는 구조다. 캐릭터는 보유 수가 곧 줄 수라 프레임을 깔면 지금 문제가 형태만 바꿔 돌아온다 → 판매 줄(StorageInformationPresenter) 쪽을 베꼈다
숨김 안내 빈 목록일 때만 Empty Text (TMP) 하나 — "해당 적성을 가진 캐릭터가 없습니다." 처음엔 "숨긴 N마리"를 늘 띄웠는데 줄 끝에 겹쳐 지저분했다(2026-09-11 실물 확인). 판매 목록의 빈 안내와 같은 자리·같은 방식으로 바꿨다 — 숨긴 캐릭터의 사정은 창고가 답한다
산업 버튼 색 셋 — 눌림 노랑 (0.839, 0.682, 0.067) · 안 눌림 하양 · 잠김 회색 상태가 셋인데 색이 둘이면 "고를 수 있는 것"과 "못 고르는 것"이 같아 보인다. ⚠️ 잠김은 disabledColor로 칠해야 한다 — normalColor에 넣으면 무시된다
적성 0 잠금(CharacterStateRowView) 걷어냈다 목록이 걸러 내면 적성 0인 줄은 생기지 않는다. 관문이 둘이면 다음에 보는 사람이 어느 쪽이 진짜인지 알 수 없다
3단계 산업 버튼 함께 잠갔다 (CanSelectIndustry) 2단계가 걸러 내는데 3단계만 서버 거절로 남으면 같은 화면이 두 말을 한다
창고 칸 보조 문구 배치 중/배치 가능 → 적성 요약(농사7·낚시2) 배치 여부는 '배' 마크가 말한다. 걸러 낸 캐릭터의 적성을 볼 곳이 창고뿐이다

바뀐 파일 — WorkStationSelectPresenter · CharacterStateRowView · InventorySlotView · StorageGridPresenter · CharacterSlotSource · WorkStationSlotView(산업 이름) · 새 파일 UI/System/IndustryLabel.cs(산업 → 한글 이름). ⚠️ 이건 사본이지 출처가 아니다 — 엑셀의 이름이 Enum.cs 주석으로만 나와 런타임에서 못 읽는다. 출처를 엑셀로 되돌리는 것은 T-047이고, 끝나면 이 파일을 지운다. 문서는 UI 배치 현황.md · System 규칙.md · Storage 규칙.md를 함께 고쳤다.

에디터 작업 — 끝났다 (Unity MCP로 처리 · 씬 저장 완료)

  • [x] Character State Row → Assets/Prefabs/CharacterStateRowView.prefab으로 뽑고 씬 인스턴스 삭제 (프리팹 루트 이름도 CharacterStateRowView로 맞췄다 — 다른 프리팹 관례와 같게)
  • [x] Row Prefab 배선. Row Parent는 손대지 않았다 — 타입만 Transform → RectTransform으로 바뀌어 저장된 참조가 그대로 살아 있다
  • [x] Empty Text (TMP) — 판매 목록의 빈 안내를 복제해 스크롤 패널 위에 겹쳐 뒀다 → 배선 (처음엔 Content 안 마지막 줄로 뒀다가 실물에서 줄과 겹쳐 보여 자리를 옮겼다)
  • [x] 산업 버튼 3색 — 인스펙터 값 교체(노랑/하양/회색) + disabledColor 적용
  • [x] InventorySlotView.prefab에 Sell Mark를 복제한 Assign Mark("배" · 파랑) 추가 → 배선

⚠️ 씬 저장으로 RectTransform 값이 대량으로 다시 쓰였다 — anchoredPosition·sizeDelta 약 270곳. LayoutGroup이 계산해 둔 값이 파일에 내려앉은 것이라 동작은 같다. 구조 변화는 줄 4개(삭제)와 안내 문구 1개(추가)뿐이다. 커밋할 때 diff 크기에 놀라지 않도록 적어 둔다.

플레이 확인 — 통과 (2026-09-12)

  • [x] 산업별 목록 전환 · 빈 목록 안내 · 창고 '배' 마크 · 3단계 잠금 · 버튼 색이 마우스·클릭에 흔들리지 않는지(1차 확인에서 걸렸던 자리) — 전부 확인했다

📌 여기서 잘려 나간 조각 하나 — 창고 칸의 적성 요약이 읽히지 않는다. 보조 문구를 적성으로 바꾼 결정(위 표 마지막 줄)은 섰지만, 농사1·낚시1·채굴1·벌목1·사냥1처럼 5종을 다 가진 캐릭터는 19자가 100px 칸의 20px 줄에 들어가 글자가 뭉개진다 (전 산업 적성 1인 기본 지급 캐릭터가 바로 그 경우다). 표시 자체는 동작해 이 일감의 완료 조건은 채웠고, 읽히게 만드는 것은 T-048로 올렸다 — 하단 밴드를 5칸 고정 스트립(위치 = 산업 · 숫자만)으로 바꿔 아이콘이 들어올 자리를 먼저 잡는다.

1차 플레이(2026-09-11)에서 UI 두 가지가 걸려 고쳤다 — 안내 문구 자리와 버튼 색이다. 버튼이 이상했던 진짜 원인은 전이색이었다: highlighted·selected가 기본값(0.961 흰색)이라 마우스를 올리거나 마지막으로 누른 버튼이라는 이유로 색이 덮였다. 네 상태를 같은 색으로 칠해 막았다.

완료 조건

배치 목록에서 산업을 누르면 그 산업을 다루는, 아직 배치되지 않은 캐릭터만 보이고, 보유 수가 몇이든 줄이 모자라지 않는다(경고가 뜨지 않는다). 창고 캐릭터 탭에서는 배치 중인 캐릭터가 마크로 한눈에 구분된다.

막고 있는 것 / 선행 일감

  • 없음. 서버 요청도 패킷 추가도 없다 — 적성(CharacterInfo.Aptitudes)과 배치 상태 (WorkStationSlots)가 이미 클라에 다 있다.
  • 일감 안에서는 A → B·C → D 순서다.

관련 커밋

  • b024f68 feat: 배치 목록을 고를 수 있는 캐릭터만 보이게 — 코드·프리팹·씬·문서 일체

참고

  • Assets/Scripts_Client/UI/Main/WorkStationSelectPresenter/WorkStationSelectPresenter.cs — RefreshRows · CollectRows · SelectIndustry
  • Assets/Scripts_Client/UI/Main/WorkStationSelectPresenter/CharacterStateRowView.cs — 프리팹으로 뺄 대상
  • Assets/Scripts_Client/UI/Storage/StorageGridPresenter/ — 베낄 원본 (StorageGridPresenter 풀 · InventorySlotView.SetSellMark · CharacterSlotSource)
  • Assets/Scripts_Client/UI/Storage/Storage 규칙.md — "탭이 달라도 격자는 하나다"
  • Assets/Scripts_Client/UI/UI 배치 현황.md 3장 — 작업슬롯 화면 흐름