불필요한 Asset file 제거 Script (모듈 분리)

check_unused_assets:
    @zsh script/check_unused_assets.sh
#!/bin/zsh

# ===============================
# 1. 유틸리티 함수 선언
# ===============================
to_camel_case() {
  echo "$1" | awk -F'[-_ ]' '{
    for(i=1;i<=NF;i++){
      if(i==1) printf "%s", tolower($i);
      else printf "%s%s", toupper(substr($i,1,1)), tolower(substr($i,2));
    }
  }'
}

print_table_header() {
  echo "+--------------------------+-------+---------------------+"
  echo "| VARIABLE                 | TYPE  | ASSET FILE PATH     |"
  echo "+--------------------------+-------+---------------------+"
}

# ===============================
# 2. 설정값
# ===============================
search_dirs=("features" "lib")
image_extensions=("png" "jpg" "jpeg" "gif" "bmp" "svg" "webp")
RED=$'\033[31m'
BLUE=$'\033[34m'
RESET=$'\033[0m'
base_dir="assets/images"
image_dirs=()

# ===============================
# 3. 이미지 폴더 목록 동적 설정
# ===============================
if [[ ! -d "$base_dir" ]]; then
  echo "기본 이미지 폴더 ($base_dir) 가 존재하지 않습니다."
  exit 1
fi

# 안전하게 배열에 할당 (공백, 한글, 특수문자 폴더명도 OK)
all_dirs=()
while IFS= read -r dir; do
  all_dirs+=("$dir")
done < <(find "$base_dir" -mindepth 1 -maxdepth 1 -type d | sort)

echo
echo "=== 이미지 폴더 검사 선택 ==="

echo
echo "a) 전체 폴더 검사 (all)"
echo "s) 특정 폴더 선택 검사(selective)"
echo "[폴더 검사할 범위를 정하기 위해 a 또는 s 중에서 선택]: "
read choice

if [[ "$choice" == "a" ]]; then
  image_dirs=("${all_dirs[@]}")
elif [[ "$choice" == "s" ]]; then
  # 폴더 목록 다시 출력
  echo
  echo "선택 가능한 폴더 목록:"
  for ((i=1; i<=${#all_dirs[@]}; i++)); do
    folder_name=$(basename "${all_dirs[$((i))]}")
    echo "  $i) $folder_name"
  done
  echo
  echo "검사할 폴더 번호를 쉼표(,)로 구분하여 입력하세요 (예: 1,3):"
  echo -n "번호 입력: "
  read input_nums
  # shellcheck disable=SC2296
  nums=("${(@s/,/)input_nums}")
  for num in "${nums[@]}"; do
    if [[ "$num" =~ '^[0-9]+$' ]] && (( num >= 0 )) && (( num <= ${#all_dirs[@]} )); then
      image_dirs+=("${all_dirs[$((num))]}")
    else
      echo "경고: 유효하지 않은 번호 무시됨: $num"
    fi
  done
  if (( ${#image_dirs[@]} == 0 )); then
    echo "선택된 폴더가 없습니다. 종료합니다."
    exit 1
  fi
else
  echo "잘못된 선택입니다. 종료합니다."
  exit 1
fi

# ===============================
# 4. 참조되지 않은 asset 찾기 (hashmap 방식)
# ===============================
any_asset_found=0
tmpfile=$(mktemp)

echo
echo "=== 스크립트 시작 ==="
echo "대상 이미지 폴더 목록: ${image_dirs[*]}"
echo "확장자 목록: ${image_extensions[*]}"
echo "검색 대상 폴더: ${search_dirs[*]}"
echo

print_table_header

# 1. 전체 소스에서 asset 변수명 패턴을 모두 grep해서 해시맵에 저장 (소문자 변환)
all_refs=()
for dir in "${search_dirs[@]}"; do
  if [[ -d "$dir" ]]; then
    all_refs+=($(grep -rhoE 'Assets\.images\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+' "$dir" --include="*.dart"))
  fi
done

typeset -A ref_map
for ref in "${all_refs[@]}"; do
  ref_map["${ref:l}"]=1  # 소문자 변환
done

for dir in "${image_dirs[@]}"; do
  echo "${BLUE}>> 폴더 확인: $dir${RESET}"
  if [[ ! -d "$dir" ]]; then
    echo "   폴더가 존재하지 않아 건너뜁니다."
    continue
  fi

  find "$dir" -type f | while read -r filepath; do
    filename=$(basename "$filepath")
    ext="${filename##*.}"
    lower_ext=$(echo "$ext" | tr '[:upper:]' '[:lower:]')

    match=0
    for valid_ext in "${image_extensions[@]}"; do
      if [[ "$lower_ext" == "$valid_ext" ]]; then
        match=1
        break
      fi
    done
    (( match == 0 )) && continue

    any_asset_found=1

    name_wo_ext="${filename%.*}"
    parent_folder=$(basename "$(dirname "$filepath")")
    camel_case_name=$(to_camel_case "$name_wo_ext")
    var_name="Assets.images.${parent_folder}.${camel_case_name}"
    var_name_lc="${var_name:l}"

    # 해시맵에서 소문자 비교로 빠르게 체크
    if [[ -z ${ref_map["$var_name_lc"]} ]]; then
      echo "$filepath" >> "$tmpfile"
      printf "| ${RED}%-24s${RESET} | %-5s | %-19s |\n" "$var_name" "$ext" "$filepath"
    fi
  done
done

echo "+--------------------------+-------+---------------------+"

unref_count=$(grep -cve '^\s*$' "$tmpfile")

if (( any_asset_found == 0 )); then
  echo
  echo "※ 검색된 asset 파일이 없습니다."
elif (( unref_count > 0 )); then
  echo
  echo "⚠️  참조되지 않은 파일이 총 ${unref_count}개 있습니다."
  echo -n "이 파일들을 모두 삭제하시겠습니까? [y/N]: "
  read confirm
  if [[ "$confirm" == [yY] ]]; then
    echo "🗑️  삭제를 시작합니다..."
    deleted_count=0
    while IFS= read -r file; do
      if [[ -f "$file" ]]; then
        rm -v -- "$file" && ((deleted_count++))
      fi
    done < "$tmpfile"
    echo "✅ ${deleted_count}/${unref_count} 개 파일이 삭제되었습니다."
  else
    echo "❌ 삭제하지 않고 종료합니다."
  fi
else
  echo
  echo "모든 asset이 참조되고 있습니다."
fi

rm -f "$tmpfile"
echo "=== 스크립트 종료 ==="

Last updated