SSmart Life US

← 목록 · 2026-08-08 · 엑셀·업무 자동화

구글시트 창고 랙 배치도 시각화 방법 | Apps Script 자동 색상 + 잠금 처리

구글시트 창고 랙 배치도 시각화 방법 | Apps Script 자동 색상 + 잠금 처리

도입: 숫자 재고표 대신 색으로 한눈에 보고 싶을 때

구글시트로 창고 재고를 관리하다 보면 로케이션별 수량은 확인할 수 있지만, 어느 랙이 꽉 찼는지, 어느 구역이 거의 비어 있는지는 직관적으로 보이지 않는 경우가 많습니다. 이 글에서는 구글시트 창고 랙 배치도 시각화를 목표로, FLOOR_MAP 시트에 실제 랙 모양을 그려 두고 재고량에 따라 셀 배경색이 자동으로 바뀌도록 만드는 과정을 정리합니다. 이 글 하나로 FLOOR_MAP 시트의 셀 색상이 자동으로 바뀌는 상태까지 완성할 수 있습니다.

FLOOR_MAP 색상 자동화 흐름

앞선 글인 구글시트로 창고 재고관리 시스템 만들기 | Apps Script 자동화구글시트 재고 자동 집계 방법 | Apps Script 3편에서 입고·출고 내역을 바탕으로 STOCK 시트에 현재 재고를 자동 집계하는 구조를 이미 만들어 두었다고 가정합니다. 이 글은 그 위에 FLOOR_MAP 랙 배치도 + 색상 시각화 레이어를 얹는 단계입니다. STOCK 시트에 로케이션별 현재 수량이 정리되어 있다면, 아래 코드를 그대로 적용해 FLOOR_MAP이라는 시트의 FLOOR_MAP 영역 색상이 자동 갱신되는 구조까지 완결할 수 있습니다.

운영 환경에서는 여러 사용자가 동시에 출고 처리나 집계를 돌릴 수 있기 때문에, refreshFloorMap() 코드가 동시 저장(동시 실행) 시 충돌하지 않도록 LockService로 잠금을 거는 것이 안전합니다. 아래 완성 코드에는 이 잠금 처리를 포함해 두었습니다.


FLOOR_MAP 설계: 랙 모양을 시트에 먼저 그립니다

구글시트에서 창고 랙 배치도를 만들 때 가장 먼저 해야 할 일은 FLOOR_MAP 시트 구조를 명확히 정하는 것입니다. 실제 랙처럼 열(column)과 단(row)을 시트 셀에 대응시키면 이후 구글시트 Apps Script 창고 관리 코드를 짜면서 색상을 칠하기가 수월해집니다.

실무에서는 보통 다음과 같이 설계합니다. 먼저 FLOOR_MAP이라는 이름의 시트를 하나 만들고, 상단 몇 줄은 제목·범례·필터 버튼 등을 위한 공간으로 비워 둡니다. 예를 들어 B열부터 G열까지, 18행부터 24행까지를 랙 배치용 범위로 잡고 B18:G24 범위를 실제 랙 위치에 대응시킵니다. 각 셀 하나가 창고의 한 로케이션(예: A01-01-01)을 의미하도록 하는 방식입니다. 이렇게 해 두면 이후 코드에서 FLOOR_RANGE_A1 = 'B18:G24' 한 줄로 전체 랙 블록을 지정할 수 있습니다.

이때 중요한 점은 로케이션 코드 규칙과 시트 좌표의 매핑을 미리 정해 두는 것입니다. 예를 들어 왼쪽에서 오른쪽으로 랙 열이 증가하고, 위에서 아래로 단 수가 증가하도록 통일해 두면 현장 인원이 FLOOR_MAP을 보며 실제 랙을 찾기 쉽습니다. 실제 창고에서는 처음 설계 때 방향이 바뀌면 이후 계속 헷갈리므로, 도면과 시트를 나란히 두고 한 번만 정확히 맞춰 놓는 것이 좋습니다.

또한 색상 범례를 FLOOR_MAP 왼쪽 상단이나 오른쪽에 작은 표로 만들어 두면 현장 교육에 도움이 됩니다. 예를 들어 “포화(빨강 = COLOR_FULL) / 여유(노랑 = COLOR_OK) / 비어 있음(회색 = COLOR_EMPTY)”처럼 적어 두고, 시트에 실제 색상 셀을 함께 보여 주면 구글시트 재고 현황 시각화의 의미가 누구에게나 명확해집니다. 이 범례는 코드와 직접 연결되지는 않지만, 색상 정책을 바꿀 때마다 현장과 이야기할 기준점이 되어 줍니다.


SETTINGS 설계: 기준값과 색상을 시트에서 관리합니다

색상을 Apps Script 코드 안에 숫자로 직접 넣을 수도 있지만, 실제 운영에서는 랙 용량이나 “포화” 기준을 바꾸고 싶은 일이 자주 생깁니다. 그래서 저는 항상 별도 시트에 설정값을 모아 두고 코드에서는 그 값을 읽어 쓰는 방식을 권장합니다. 이렇게 하면 운영 중에도 코드 수정 없이 기준을 조정할 수 있습니다.

SETTINGS라는 시트를 새로 만들고 다음과 같은 표를 준비합니다. 이 글에서는 임계값 단위를 0~100 퍼센트(%) 정수로 통일합니다.

  • A열: 키 이름
  • B열: 값

예시 구조는 다음과 같습니다.

  • MAX_PER_LOCATION / 100 → 한 로케이션 최대 수량(예시 수치)
  • WARN_THRESHOLD / 50 → 경고 단계 비율(%)
  • FULL_THRESHOLD / 90 → 포화 단계 비율(%)
  • COLOR_EMPTY / #eeeeee → 수량 0 또는 매우 여유일 때 회색
  • COLOR_OK / #fff2cc → 경고 이상이지만 포화 미만일 때 노란색
  • COLOR_FULL / #f4cccc → 포화 이상일 때 빨간색

코드에서는 qty / MAX_PER_LOCATION을 계산해 사용 비율을 구하고, 여기에 WARN_THRESHOLD / 100, FULL_THRESHOLD / 100을 곱해 나온 값과 비교해 색을 정합니다. 예를 들어 MAX_PER_LOCATION = 100, WARN_THRESHOLD = 50, FULL_THRESHOLD = 90이라면 수량이 0이면 회색, 1~49까지는 노랑 기준 미만으로 노랑 전 단계 처리, 50~89는 노랑, 90 이상은 빨간색으로 포화 표시가 됩니다.

이 구조를 사용하면 구글시트 setBackgrounds 색상 자동화 로직은 그대로 두고, 기준과 색상만 SETTINGS 시트에서 자유롭게 조정할 수 있습니다. 현장에서 “노랑 구간을 조금 더 일찍 보고 싶다”는 요청이 들어오면 WARN_THRESHOLD를 40으로 바꾸는 식으로 대응할 수 있습니다. 만약 MAX_PER_LOCATION을 0으로 두면 비율을 계산할 수 없으므로 코드에서 자동으로 0%로 간주해 모두 COLOR_EMPTY 기준에 가깝게 처리되며, 설정 누락 오류 메시지를 통해 다시 한 번 확인하도록 하는 편이 안전합니다.


FLOOR_MAP 새로고침 개요: STOCK을 읽어 색을 칠하는 흐름

이제 핵심인 refreshFloorMap() 함수를 만듭니다. 이 함수는 STOCK 시트에서 각 로케이션별 수량을 읽고, FLOOR_MAP 시트의 셀에 로케이션 코드와 수량을 표시하면서 구글시트 setBackgrounds 색상 자동화를 수행합니다. 전반적인 흐름은 다음과 같습니다.

  1. SETTINGS 시트에서 최대 수량, 경고 비율, 포화 비율, 색상값을 읽어 옵니다.
  2. STOCK 시트에서 로케이션별 재고 현황을 로케이션 코드 → 수량 형태의 객체로 만듭니다.
  3. FLOOR_MAP 시트의 랙 범위(B18:G24)에 적힌 로케이션 코드를 하나씩 읽어 해당 로케이션 수량을 STOCK에서 찾습니다.
  4. qty / MAX_PER_LOCATION으로 사용 비율을 계산한 뒤, 임계값과 비교하여 빈칸·여유·포화 중 하나의 색을 정합니다.
  5. 셀 안에는 로케이션 코드 + 줄바꿈 + 수량 형태의 텍스트를 넣고, setValues와 setBackgrounds로 한 번에 반영합니다.

앞에서 만든 구글시트 출고 자동화 방법 | Apps Script 2편과 연결하면 출고 처리 직후 refreshFloorMap()을 자동 호출해도 되지만, 처음에는 상단 메뉴 버튼으로 수동 새로고침을 운영하는 것이 안전합니다. 이 글에서는 전체 코드를 기능 단위로 나누어 설명하므로, 코드에 익숙하지 않은 분도 순서대로 따라가며 완성할 수 있습니다.

운영 환경에서 여러 사용자가 동시에 출고·입고를 처리하는 경우에는 refreshFloorMap()이 동시에 두 번 이상 실행될 수 있습니다. 이때 같은 범위를 덮어쓰는 작업이 겹치면 예기치 않은 색상 결과나 일시적인 오류가 발생할 수 있으므로, 아래 코드에서는 LockService를 이용해 동시 실행을 피하는 잠금 처리를 추가했습니다.


FLOOR_MAP / SETTINGS 코드 구성: 상수와 설정 읽기

1단계 — 시트 이름과 키를 상수로 모으기

이 코드는 FLOOR_MAP, STOCK, SETTINGS 시트 이름과 설정 키를 한 곳에 정의합니다.

붙여넣는 위치: Google Apps Script 편집기에서 Code.gs 파일 맨 위에 넣습니다.

붙여넣은 뒤 할 일: 저장(Ctrl+S 또는 ⌘S)만 하면 됩니다.

Apps Script (JavaScript)
// --- 시트 이름, 범위, SETTINGS 키 상수 정의 ---
// 여기만 본인 환경에 맞게 바꾸세요
const SHEET_FLOOR_MAP = 'FLOOR_MAP';       // → 랙 배치도 시트 이름
const SHEET_STOCK     = 'STOCK';           // → 재고 집계 시트 이름
const SHEET_SETTINGS  = 'SETTINGS';        // → 설정값 시트 이름

const FLOOR_RANGE_A1  = 'B18:G24';         // → 랙 범위(열=랙, 행=단)

const KEY_MAX_PER_LOC    = 'MAX_PER_LOCATION';  // → 최대 수량 키
const KEY_WARN_THRESHOLD = 'WARN_THRESHOLD';    // → 경고 비율 키(0~100)
const KEY_FULL_THRESHOLD = 'FULL_THRESHOLD';    // → 포화 비율 키(0~100)

const KEY_COLOR_EMPTY = 'COLOR_EMPTY';     // → 빈칸 색 키
const KEY_COLOR_OK    = 'COLOR_OK';        // → 여유 색 키
const KEY_COLOR_FULL  = 'COLOR_FULL';      // → 포화 색 키

제대로 됐는지 확인하는 법: 편집기에서 빨간 오류 표시가 없다면 정상입니다.


2단계 — SETTINGS에서 기준값과 색상을 읽는 함수

이 코드는 SETTINGS 시트의 “키–값” 표를 읽어 자바스크립트 객체로 바꿉니다.

붙여넣는 위치: 위 상수 정의 바로 아래에 추가합니다.

붙여넣은 뒤 할 일: 저장 후 getSettingsMap 함수 옆 실행 버튼을 눌러 한 번 실행하고, 권한 승인 요청을 허용합니다.

Apps Script (JavaScript)
/**
 * SETTINGS 시트(A열 키, B열 값)를 읽어 Map 형태(객체)로 반환
 */
function getSettingsMap() {                             // → SETTINGS를 객체로 읽기
  const ss = SpreadsheetApp.getActive();                // → 현재 파일 가져오기
  const sh = ss.getSheetByName(SHEET_SETTINGS);         // → SETTINGS 시트 가져오기
  if (!sh) {                                            // → 시트 없으면
    throw new Error('SETTINGS 시트를 찾을 수 없습니다'); // → 오류 발생
  }

  const lastRow = sh.getLastRow();                      // → 마지막 행 번호
  if (lastRow < 2) {                                    // → 데이터 없으면
    throw new Error('SETTINGS 시트에 값이 없습니다');     // → 오류 발생
  }

  const range = sh.getRange(2, 1, lastRow - 1, 2);      // → A2:B 마지막 행 범위
  const values = range.getValues();                     // → 키·값 전체 읽기

  const map = {};                                       // → 결과 객체
  values.forEach(function(row) {                        // → 각 행 처리
    const key = String(row[0]).trim();                  // → 키 문자열
    const value = row[1];                               // → 값 그대로
    if (key) {                                          // → 키가 비어 있지 않으면
      map[key] = value;                                 // → 객체에 저장
    }
  });

  return map;                                           // → 전체 설정 반환
}

제대로 됐는지 확인하는 법: getSettingsMap을 실행한 뒤, “실행 로그”에서 {MAX_PER_LOCATION=100.0, WARN_THRESHOLD=50.0…}처럼 키–값 구조가 보이면 성공입니다.


STOCK / FLOOR_MAP 연동: 재고 맵 생성과 색상 적용

3단계 — STOCK에서 로케이션별 수량을 가져오는 함수

이 코드는 STOCK 시트에서 로케이션 코드 → 수량 형태로 데이터를 가져옵니다. 예시는 A열에 로케이션 코드, B열에 현재 수량이 있다고 가정합니다. 다른 구조라면 열 인덱스를 조정하면 됩니다.

붙여넣는 위치: getSettingsMap 함수 아래에 추가합니다.

붙여넣은 뒤 할 일: 저장 후 buildStockMap을 실행해 권한과 데이터 구조를 확인합니다.

Apps Script (JavaScript)
/**
 * STOCK 시트에서 로케이션별 수량을 읽어 Map 형태(객체)로 반환
 * A열: 로케이션 코드, B열: 현재 수량 기준 예시
 */
function buildStockMap() {                              // → STOCK을 객체로 만들기
  const ss = SpreadsheetApp.getActive();                // → 현재 파일 가져오기
  const sh = ss.getSheetByName(SHEET_STOCK);            // → STOCK 시트 가져오기
  if (!sh) {                                            // → 시트 없으면
    throw new Error('STOCK 시트를 찾을 수 없습니다');    // → 오류 발생
  }

  const lastRow = sh.getLastRow();                      // → 마지막 행 번호
  if (lastRow < 2) {                                    // → 데이터 없으면
    return {};                                          // → 빈 객체 반환
  }

  const range = sh.getRange(2, 1, lastRow - 1, 2);      // → A2:B 범위(로케이션, 수량)
  const values = range.getValues();                     // → 값 읽기

  const stockMap = {};                                  // → 결과 객체
  values.forEach(function(row) {                        // → 각 행 처리
    const loc = String(row[0]).trim();                  // → 로케이션 코드
    const qty = Number(row[1]) || 0;                    // → 수량 숫자로
    if (loc) {                                          // → 로케이션 있으면
      stockMap[loc] = qty;                              // → 객체에 저장
    }
  });

  return stockMap;                                      // → 전체 재고 맵 반환
}

제대로 됐는지 확인하는 법: 실행 로그에 {A01-01-01=10.0, A01-01-02=0.0…}처럼 로케이션–수량 쌍이 보이면 정상입니다.


4단계 — FLOOR_MAP을 새로고침해 셀 값·색상 자동 적용 (LockService 포함)

이 코드는 핵심인 refreshFloorMap()입니다. FLOOR_RANGE_A1(B18:G24) 범위의 각 셀에서 로케이션 코드를 읽고, STOCK에서 수량을 찾아 텍스트와 배경색을 동시에 갱신합니다. WARN_THRESHOLDFULL_THRESHOLD는 0~100 사이 퍼센트값으로 가정하고, 코드에서 100으로 나누어 비율로 비교합니다.

여기에 더해, 동시에 두 사용자가 새로고침을 눌러도 한 번에 하나의 실행만 FLOOR_MAP을 수정하도록 LockService 잠금을 적용했습니다. 스크립트가 잠금을 얻지 못하면 3초 동안 재시도하고, 그래도 실패하면 예외를 던져 “잠시 후 다시 시도해 달라”는 에러를 띄우도록 구성했습니다.

붙여넣는 위치: buildStockMap 함수 아래에 추가합니다.

붙여넣은 뒤 할 일: 저장 후 refreshFloorMap을 실행하고 권한 요청을 승인합니다.

Apps Script (JavaScript)
/**
 * FLOOR_MAP 시트의 랙 배치도 영역(B18:G24)을 새로고침하여
 * 로케이션별 수량 텍스트와 배경색을 동시에 갱신.
 * LockService를 사용해 동시 실행(동시 저장) 충돌을 방지.
 */
function refreshFloorMap() {                                      // → FLOOR_MAP 시각화
  // --- LockService로 동시 실행 방지 ---
  const lock = LockService.getDocumentLock();                     // → 문서 단위 잠금
  let hasLock = false;
  try {
    // 최대 3초 동안 잠금 획득을 시도
    hasLock = lock.tryLock(3000);
    if (!hasLock) {
      throw new Error('다른 사용자가 랙 배치도를 업데이트 중입니다. 잠시 후 다시 시도하세요.');
    }

    const ss = SpreadsheetApp.getActive();                        // → 현재 파일 가져오기
    const shMap = ss.getSheetByName(SHEET_FLOOR_MAP);             // → FLOOR_MAP 시트
    if (!shMap) {                                                 // → 시트 없으면
      throw new Error('FLOOR_MAP 시트를 찾을 수 없습니다');       // → 오류 발생
    }

    const settings = getSettingsMap();                            // → 설정값 가져오기
    const maxPerLoc = Number(settings[KEY_MAX_PER_LOC]) || 0;     // → 최대 수량
    const warnRatio  = Number(settings[KEY_WARN_THRESHOLD]) || 0; // → 경고 비율(%)
    const fullRatio  = Number(settings[KEY_FULL_THRESHOLD]) || 0; // → 포화 비율(%)

    const colorEmpty = String(settings[KEY_COLOR_EMPTY] || '#eeeeee'); // → 빈칸 색
    const colorOk    = String(settings[KEY_COLOR_OK]    || '#fff2cc');  // → 여유 색
    const colorFull  = String(settings[KEY_COLOR_FULL]  || '#f4cccc');  // → 포화 색

    if (!maxPerLoc || !warnRatio || !fullRatio) {                 // → 기준값 누락 시
      throw new Error('SETTINGS 시트의 MAX_PER_LOCATION, WARN_THRESHOLD, FULL_THRESHOLD 값을 확인하세요'); // → 오류 발생
    }

    const stockMap = buildStockMap();                             // → 재고 맵 가져오기

    const range = shMap.getRange(FLOOR_RANGE_A1);                 // → 랙 범위 가져오기
    const values = range.getValues();                             // → 현재 셀 값
    const newValues = [];                                         // → 새 값 배열
    const bgColors  = [];                                         // → 배경색 배열

    for (let r = 0; r < values.length; r++) {                     // → 행 반복
      const rowValues = values[r];                                // → 한 행 값
      const newRowValues = [];                                    // → 새 행 값
      const rowColors = [];                                       // → 새 행 색

      for (let c = 0; c < rowValues.length; c++) {                // → 열 반복
        const cellVal = String(rowValues[c]).trim();              // → 셀 내용
        if (!cellVal) {                                           // → 로케이션 없으면
          newRowValues.push('');                                  // → 빈값 유지
          rowColors.push(colorEmpty);                             // → 빈칸 색
          continue;                                               // → 다음 셀
        }

        const loc = cellVal;                                      // → 로케이션 코드
        const qty = Number(stockMap[loc]) || 0;                   // → 재고 수량

        const text = loc + '\n' + qty;                            // → 코드+수량
        newRowValues.push(text);                                  // → 셀 값 넣기

        let ratio = 0;                                            // → 사용 비율
        if (maxPerLoc > 0) {                                      // → 기준 있으면
          ratio = qty / maxPerLoc;                                // → 비율 계산
        }

        let color = colorEmpty;                                   // → 기본 색
        if (qty === 0) {                                          // → 수량 0이면
          color = colorEmpty;                                     // → 빈칸 색
        } else if (ratio >= fullRatio / 100) {                    // → 포화 이상
          color = colorFull;                                      // → 포화 색
        } else if (ratio >= warnRatio / 100) {                    // → 경고 이상
          color = colorOk;                                        // → 여유 색
        } else {                                                  // → 경고 미만
          color = colorOk;                                        // → 여유 색(초기 여유 구간)
        }

        rowColors.push(color);                                    // → 색 추가
      }

      newValues.push(newRowValues);                               // → 새 값 행 추가
      bgColors.push(rowColors);                                   // → 새 색 행 추가
    }

    range.setValues(newValues);                                   // → 값 일괄 반영
    range.setBackgrounds(bgColors);                               // → 색 일괄 반영

  } finally {
    // 잠금을 획득했으면 반드시 해제
    if (hasLock) {
      lock.releaseLock();
    }
  }
}

제대로 됐는지 확인하는 법: FLOOR_MAP의 B18:G24 셀에 실제 로케이션 코드들을 입력해 두고 refreshFloorMap을 실행했을 때, 각 셀이 “코드(윗줄) + 수량(아랫줄)” 형태로 바뀌고, 수량에 따라 회색·노랑·빨강으로 나뉘면 정상입니다. STOCK에 데이터가 없는 로케이션은 0으로 표시되고 회색이 됩니다. 동시에 두 명이 메뉴를 눌렀을 때 한쪽에서 “다른 사용자가 랙 배치도를 업데이트 중입니다…” 메시지가 뜨면 LockService도 정상적으로 작동하는 것입니다.


메뉴 연동과 실무 운영 팁

5단계 — 메뉴 버튼으로 새로고침하기

현장 인원이 Apps Script 편집기까지 들어가지 않고도 FLOOR_MAP을 새로고침할 수 있도록, 구글시트 상단에 메뉴를 추가합니다.

붙여넣는 위치: 같은 파일(Code.gs) 맨 아래에 추가합니다.

붙여넣은 뒤 할 일: 저장 후 시트를 닫았다가 다시 열어 새 메뉴가 보이는지 확인합니다.

Apps Script (JavaScript)
/**
 * 시트가 열릴 때 상단 메뉴에 "창고 도구" 메뉴를 추가
 */
function onOpen() {                                              // → 시트 열 때 실행
  const ui = SpreadsheetApp.getUi();                             // → UI 객체
  ui.createMenu('창고 도구')                                      // → 새 메뉴 이름
    .addItem('랙 배치도 새로고침', 'refreshFloorMap')             // → 메뉴 항목
    .addToUi();                                                  // → 메뉴 추가
}

제대로 됐는지 확인하는 법: 시트를 다시 열었을 때 상단에 창고 도구 메뉴가 나타나고, 그 안의 랙 배치도 새로고침을 클릭하면 FLOOR_MAP 색상이 갱신되면 성공입니다.


실무 팁: FLOOR_MAP을 안정적으로 돌리는 방법

실제 운영에서 구글시트 창고 랙 배치도 시각화를 적용하면서 겪었던 부분 중, 바로 도움이 되는 것만 정리합니다.

  1. 로케이션 코드는 STOCK에서 복사해 사용하기

FLOOR_MAP 셀에 로케이션 코드를 입력할 때는 STOCK 시트의 로케이션 목록을 그대로 복사해 사용하는 것이 좋습니다. 사람 손으로 새로 입력하면 공백이나 하이픈 위치가 달라져 STOCK과 매칭되지 않아 수량이 0으로 보이는 문제가 자주 발생합니다. 초기에 한두 구역만 만들더라도, 항상 STOCK 기준으로 로케이션을 복사해 붙여 넣는 습관을 들이면 이후 확장 시에도 실수가 줄어듭니다.

  1. 임계값은 단순하게 시작하기

WARN_THRESHOLDFULL_THRESHOLD 값은 처음부터 세밀하게 조정하기보다 50%·90% 같은 단순 기준으로 시작하는 편이 이해하기 쉽습니다. 예를 들어 MAX_PER_LOCATION이 100개인 랙이라면, 49개까지는 상대적으로 여유, 50개 이상이면 노랑으로 표시해 “반 이상 찼다”는 경고 의미가 생기고, 90개 이상이면 빨강으로 거의 포화를 의미하게 됩니다. 현장에서 이 기준을 눈으로 보며 의견을 모은 뒤, 필요하면 60%·80%로 조정하는 식이 좋았습니다.

  1. 작은 범위로 먼저 검증하기

STOCK 구조와 FLOOR_MAP 범위는 최소 예시로 먼저 검증하는 것이 안정적입니다. 예를 들어 STOCK 시트에 로케이션 5개 정도만 입력해 두고, FLOOR_MAP에도 2×3 칸 정도만 채운 상태에서 refreshFloorMap()을 돌려 보는 것입니다. 이렇게 하면 MAX_PER_LOCATION이 0이거나, SETTINGS 키 이름을 잘못 쓴 경우처럼 자주 나는 오류를 작은 범위에서 먼저 발견할 수 있습니다.

  1. 동시 실행을 의식한 스케줄링

이 글의 코드에는 LockService가 들어가 있어 동시 실행 충돌을 어느 정도 막아 주지만, 출고·입고 자동화 트리거와 FLOOR_MAP 새로고침 트리거를 너무 촘촘히 겹치지 않는 것이 좋습니다. 예를 들어 refreshFloorMap()을 시간 기반 트리거로 쓸 경우, 1분 간격보다는 5분·10분 간격으로 두고, 현장에서는 필요할 때 메뉴 버튼을 눌러 수동 새로고침을 병행하는 식이 운영상 안정적이었습니다.

  1. 범위 확장은 단계적으로

구역이 늘어나면서 B18:G24 외에 다른 블록도 색칠하고 싶다면 코드를 복잡하게 바꾸기보다 FLOOR_RANGE_A1 값을 구역별로 늘리는 새 버전을 다음 단계에서 설계하는 것이 좋습니다. 한 파일에서 너무 많은 범위를 동시에 칠하면 실행 시간이 길어지고, 다른 Apps Script 작업과 충돌할 수 있습니다. 처음에는 핵심 통로나 주요 랙 일부만 시각화하고, 안정적으로 돌아가는 것이 확인되면 단계적으로 확장하는 방식을 추천합니다.


맺음말: 오늘 할 일은 FLOOR_MAP 시범 구역 만들기

이 글에서는 구글시트 창고 랙 배치도 시각화를 위해 FLOOR_MAP 시트에 랙 배치도를 그리고, SETTINGS 시트에서 기준값과 색상을 관리하며, STOCK 데이터를 읽어 setValuessetBackgrounds로 자동 색상을 입히는 과정을 정리했습니다. 임계값 단위를 0~100 퍼센트로 고정하고, 빈칸·여유·포화 색상 코드를 SETTINGS에서 통일함으로써, 이후 기준 변경이나 색상 변경도 시트에서 바로 처리할 수 있습니다.

또한 실제 운영 환경에서 자주 문제 되는 동시 저장(동시 실행) 충돌을 줄이기 위해 refreshFloorMap()에 LockService 잠금까지 포함해 두었으니, 여러 사용자가 같은 시트를 사용하는 상황에서도 보다 안정적으로 돌릴 수 있습니다.

지금 바로 할 수 있는 행동은 하나입니다.

STOCK 시트에 로케이션과 수량이 이미 있다면, FLOOR_MAP 시트에 2×3 정도의 작은 범위(B18:G20)를 만들고 이 글의 코드 전체를 붙여 넣은 뒤, SETTINGS 시트를 예시 값대로 채워 놓고 창고 도구 → 랙 배치도 새로고침 메뉴를 한 번만 눌러 보십시오. 작은 시범 구역에서 색이 의도대로 바뀌는 것이 확인되면, 같은 패턴으로 전체 창고 FLOOR_MAP을 확장해 나가는 일은 훨씬 수월해집니다.