SSmart Life USA

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

구글시트 Apps Script로 창고 관리 UI 만들기

구글시트 Apps Script로 창고 관리 UI 만들기

구글시트 창고 관리 UI 만들기를 찾고 있다면, 핵심은 단순합니다. 시트 4개(INBOUND·LOCATIONS·FLOOR_MAP·SETTINGS)와 Apps Script 사이드바 하나로 입고 확인, putaway 위치 배정, floor map 시각화를 한 번에 처리하는 구조를 만드는 겁니다. 별도 WMS 없이, 구글시트와 Apps Script만으로 무료에 가깝게 구성할 수 있습니다.

창고 관리 UI 처리 흐름

현장에서는 아직도 “컨테이너별 입고 확인 → 어디에 둘지 결정 → 나중에 어디에 있는지 찾기”를 엑셀과 구두로 해결하는 경우가 많습니다. 이 글은 제가 실제로 써본 방식 기준으로, 재현 가능한 최소 구현 흐름과 코드 뼈대를 정리한 것입니다.


1. 시트 4개로 기본 뼈대 잡기

먼저 구글시트에 아래 4개 시트를 만듭니다.

1) INBOUND – 입고 예정·검수·putaway 결과

  • A: ETA_DATE (YYYY-MM-DD)
  • B: CONTAINER (예: CTR-00001)
  • C: MODEL (예: MODEL-A)
  • D: QTY
  • E: PUTAWAY (예: B22-4)
  • F: STATUS (PLANNED / RECEIVED / PUTAWAY_DONE 등)

→ 매일 입고 예정 컨테이너를 여기 올려두고, “오늘 ETA” 기준으로만 사이드바에 노출합니다.

2) LOCATIONS – 실제 재고 테이블

  • A: LOCATION (예: B22-4)
  • B: CONTAINER
  • C: MODEL
  • D: QTY
  • E: TIMESTAMP

→ 입고 확정 시 이 시트에 한 줄 추가하며, 이게 로케이션별 재고의 기준이 됩니다.

3) FLOOR_MAP – 랙·슬롯 시각화

  • 예: B18~G24 범위를 창고 랙으로 사용
  • 열(B~G): 랙 열
  • 행(18~24): 랙 단
  • 한 셀 안에서 슬롯 1~6을 줄바꿈으로 표시
  • Apps Script가 색과 텍스트를 이 영역에 그려 넣습니다.

4) SETTINGS – 파라미터 모음

  • FLOOR_MAP_RANGE, MAX_SLOTS, 색 단계 등 상수를 나중에 여기로 빼서 관리합니다.

2. Apps Script: 메뉴·사이드바 뼈대 코드

스프레드시트에서 확장 프로그램 → Apps Script를 열고, Code.gs에 기본 골격을 넣습니다.

```javascript

function onOpen() {

const ui = SpreadsheetApp.getUi();

ui.createMenu('창고')

.addItem('입고 UI 열기', 'showInboundSidebar')

.addToUi();

}

function showInboundSidebar() {

const html = HtmlService.createHtmlOutputFromFile('Sidebar')

.setTitle('입고 / Putaway');

SpreadsheetApp.getUi().showSidebar(html);

}

function getTodayInbound() {

const ss = SpreadsheetApp.getActive();

const sh = ss.getSheetByName('INBOUND');

const values = sh.getDataRange().getValues();

const today = Utilities.formatDate(new Date(), ss.getSpreadsheetTimeZone(), 'yyyy-MM-dd');

const header = values[0];

const etaIdx = header.indexOf('ETA_DATE');

const result = values.filter((row, i) =>

i > 0 && row[etaIdx] && row[etaIdx].toString().slice(0,10) === today

);

return result; // Sidebar에서 드롭다운 데이터로 사용

}

function savePutaway(data) {

// data: {container, model, qty, location}

const ss = SpreadsheetApp.getActive();

const inbound = ss.getSheetByName('INBOUND');

const locations = ss.getSheetByName('LOCATIONS');

// 1) LOCATIONS에 기록

locations.appendRow([

data.location,

data.container,

data.model,

data.qty,

new Date()

]);

// 2) INBOUND 업데이트 (간단히 컨테이너 첫 행만 찾는 예시)

const range = inbound.getDataRange();

const values = range.getValues();

const header = values[0];

const contIdx = header.indexOf('CONTAINER');

const putIdx = header.indexOf('PUTAWAY');

const statIdx = header.indexOf('STATUS');

for (let i = 1; i < values.length; i++) {

if (values[i][contIdx] === data.container) {

values[i][putIdx] = data.location;

values[i][statIdx] = 'PUTAWAY_DONE';

break;

}

}

range.setValues(values);

refreshFloorMap();

}

function refreshFloorMap() {

const ss = SpreadsheetApp.getActive();

const locSh = ss.getSheetByName('LOCATIONS');

const mapSh = ss.getSheetByName('FLOOR_MAP');

const setSh = ss.getSheetByName('SETTINGS');

// 예시: SETTINGS!A1에 FLOOR_MAP_RANGE, A2에 MAX_SLOTS

const floorRangeA1 = setSh.getRange('A1').getValue() || 'B18:G24';

const maxSlots = setSh.getRange('A2').getValue() || 6;

const mapRange = mapSh.getRange(floorRangeA1);

const colors = [];

const texts = [];

// 기본값 초기화

for (let r = 0; r < mapRange.getNumRows(); r++) {

colors[r] = [];

texts[r] = [];

for (let c = 0; c < mapRange.getNumColumns(); c++) {

colors[r][c] = '#ffffff';

texts[r][c] = '';

}

}

// LOCATIONS를 읽어 cell별 슬롯 수/컨테이너 텍스트 구성

// (실무에 맞게 LOCATION → 셀 좌표 매핑 로직을 추가)

// ...

mapRange.setBackgrounds(colors);

mapRange.setValues(texts);

mapRange.setNumberFormat('@'); // 텍스트로 고정

}

```

위 함수들은 “메뉴 → 사이드바 열기 → 오늘 입고 조회 → putaway 저장 → floor map 갱신”까지의 기본 흐름을 담당합니다.


3. Sidebar.html: google.script.run으로 입고 UI 만들기

Sidebar.html 파일을 추가해 사이드바 UI를 구성합니다. 핵심은 버튼 클릭 시 google.script.run.함수명()으로 Apps Script 함수를 부르는 구조입니다.

```html

<!DOCTYPE html>

<html>

<body>

<h3>입고 / Putaway</h3>

<label>컨테이너 선택</label>

<select id="containerSelect" onchange="onContainerChange()"></select>

<div id="info"></div>

<h4>로케이션 선택</h4>

<div id="loc-ui">

<!-- B~G / 18~24 / 1~6 선택용 버튼을 JS로 생성 -->

</div>

<button onclick="save()">저장</button>

<script>

let inboundRows = [];

let selected = { container: '', model: '', qty: 0, location: '' };

function loadInbound() {

google.script.run

.withSuccessHandler(function(rows){

inboundRows = rows;

const sel = document.getElementById('containerSelect');

rows.forEach(function(r){

const opt = document.createElement('option');

opt.value = r[1]; // CONTAINER

opt.text = r[1];

sel.appendChild(opt);

});

})

.getTodayInbound();

}

function onContainerChange() {

const cont = document.getElementById('containerSelect').value;

const row = inboundRows.find(r => r[1] === cont);

if (!row)