Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b542b963d2 |
@@ -1,5 +1,8 @@
|
||||
# 의사결정 이력
|
||||
|
||||
## 2026-04-02 v1.3.63
|
||||
- 이미지 최적화 기록은 내부 라우트 카테고리를 그대로 보여주면 운영자가 실제 의미를 해석해야 하므로, 관리자 화면에는 기능 기준의 한국어 라벨과 재사용 여부를 함께 보여주는 편이 맞다고 정리했다.
|
||||
|
||||
## 2026-04-02 v1.3.62
|
||||
- 커스텀 이미지가 많은 상태에서 저장할 때 사용자 체감 순서가 흔들리는 것은 업로드 성공보다 더 직접적인 UX 문제이므로, 내부 객체 키 순서가 아니라 현재 화면 배치 순서를 저장 기준으로 삼는 편이 맞다고 정리했다.
|
||||
- 템플릿 요청이 저장본에서만 가능하다면 삭제도 같은 기준을 따르는 편이 흐름상 자연스러우므로, 저장되지 않은 초안에는 삭제 액션을 노출하지 않는 쪽으로 판단했다.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
- 티어표 만들기 화면의 보드 드롭존은 점선/높이/중앙 정렬로 존재감이 커졌으므로, 데스크톱과 모바일에서 파일 선택 버튼과 안내 문구 밀도를 한 번 더 QA한다.
|
||||
- 관리자 썸네일 드롭존과 기본 아이템 추가 드롭존은 에디터 드롭존과 시각 문법을 맞췄으므로, 라이트모드와 좁은 화면에서 아이콘 대비와 배경 밝기가 과하지 않은지 한 번 더 QA한다.
|
||||
- 커스텀 이미지가 많은 티어표를 저장할 때 커스텀 이름 정리 목록과 미배치 아이템 영역 순서가 실제로 안정적으로 유지되는지, 10장 이상 업로드한 상태로 한 번 더 QA한다.
|
||||
- 관리자 이미지 최적화 현황은 최근 작업 라벨을 읽기 쉬운 이름으로 바꿨으므로, 실제 운영 데이터에서 `커스텀 아이템 / 티어표 썸네일 / 게임·템플릿 이미지 / 프로필 아바타` 흐름이 기대와 맞게 찍히는지 한 번 더 QA한다.
|
||||
|
||||
## 중기 개선
|
||||
- 신규 템플릿 요청은 현재 `첫 생성 1회 후 요청과 게임을 연결해 재사용` 단계까지 정리했으므로, 다음 단계에서는 정말로 게임을 DB에 만들기 전까지 임시 작업 상태로 유지할지 여부를 운영 흐름 기준으로 결정한다.
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# 업데이트 로그
|
||||
|
||||
## 2026-04-02 v1.3.63
|
||||
- 관리자 이미지 최적화 최근 작업 목록은 더 이상 내부 카테고리 문자열 `custom / tierlists / games / avatars`를 그대로 노출하지 않고, 각각 `커스텀 아이템 / 티어표 썸네일 / 게임·템플릿 이미지 / 프로필 아바타`처럼 사람이 이해할 수 있는 이름으로 표시함.
|
||||
- 같은 이미지 해시를 다시 업로드해 기존 최적화 파일을 재사용한 경우에는 최근 작업 목록에 `기존 최적화 파일 재사용` 문구를 함께 보여, 새로 압축된 건지 중복 자산이 재사용된 건지 운영자가 바로 구분할 수 있게 함.
|
||||
|
||||
## 2026-04-02 v1.3.62
|
||||
- 티어표 저장과 템플릿 요청 전 커스텀 이미지 업로드에서는 더 이상 `itemsById` 객체 키 순서에 기대지 않고, 실제 화면에 보이는 `아이템 영역 + 보드 배치 순서` 기준으로 아이템 배열을 만들도록 바꿔 저장 중 이미지 목록이 흔들리던 현상을 줄임.
|
||||
- 따라서 커스텀 아이템 이름 정리 목록, 저장 payload, 템플릿 요청 payload 모두 같은 순서 기준을 공유하게 되어, 이미지를 여러 장 올린 뒤 저장해도 사용자가 보고 있던 흐름이 덜 흔들리도록 정리함.
|
||||
|
||||
@@ -523,6 +523,36 @@ function formatBytes(value) {
|
||||
return `${current >= 10 || unitIndex === 0 ? current.toFixed(0) : current.toFixed(1)} ${units[unitIndex]}`
|
||||
}
|
||||
|
||||
function formatImageJobSourceCategory(category) {
|
||||
switch (String(category || '').trim()) {
|
||||
case 'custom':
|
||||
return '커스텀 아이템'
|
||||
case 'tierlists':
|
||||
return '티어표 썸네일'
|
||||
case 'games':
|
||||
return '게임/템플릿 이미지'
|
||||
case 'avatars':
|
||||
return '프로필 아바타'
|
||||
default:
|
||||
return '기타 이미지'
|
||||
}
|
||||
}
|
||||
|
||||
function formatImageJobStatus(status) {
|
||||
switch (String(status || '').trim()) {
|
||||
case 'queued':
|
||||
return '대기'
|
||||
case 'processing':
|
||||
return '처리중'
|
||||
case 'completed':
|
||||
return '완료'
|
||||
case 'failed':
|
||||
return '실패'
|
||||
default:
|
||||
return status || '알 수 없음'
|
||||
}
|
||||
}
|
||||
|
||||
const imageDiagnosticsCards = computed(() => {
|
||||
const stats = imageStats.value
|
||||
if (!stats) return []
|
||||
@@ -2079,10 +2109,11 @@ function userAvatarFallback(user) {
|
||||
<div v-else class="imageJobList">
|
||||
<article v-for="job in imageRecentJobs" :key="job.id" class="imageJobRow">
|
||||
<div class="imageJobRow__head">
|
||||
<strong>{{ job.sourceCategory || 'asset' }}</strong>
|
||||
<span class="imageJobRow__status">{{ job.status }}</span>
|
||||
<strong>{{ formatImageJobSourceCategory(job.sourceCategory) }}</strong>
|
||||
<span class="imageJobRow__status">{{ formatImageJobStatus(job.status) }}</span>
|
||||
</div>
|
||||
<div class="hint hint--tight">{{ formatBytes(job.originalByteSize) }} → {{ formatBytes(job.optimizedByteSize) }}</div>
|
||||
<div v-if="job.reusedAsset" class="hint hint--tight">기존 최적화 파일 재사용</div>
|
||||
<div class="hint hint--tight">{{ fmt(job.queuedAt) }}</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user