mLog 명판이 있는 공방에서 일곱 색 유리 조각을 골라 패널에 맞추는 흰색 3D 유령 스테인드글라스 공예가

IBSheet 8 요일 다중 선택: Enum·EnumKeys와 저장 값 구분하기

개발 IBSheet 2026년 10월 6일

요일 선택 칸에서 월·수·금을 골랐는데 저장 후에는 코드가 그대로 보이거나 선택이 풀리는 경우, 화면에 표시할 문자와 서버에 보낼 값을 먼저 나눠 확인해야 한다. 월, 수, 금, 1|3|5, 1;3;5는 같은 선택을 표현할 수 있지만 서로 교환 가능한 문자열은 아니다.

이 글은 IBSheet 8 공식 예제를 바탕으로 컬럼 설정을 정리하고, 애플리케이션 경계에서 요일 값을 변환하는 작은 JavaScript 모듈을 만든다. 2026-10-06 Node.js v24.19.0에서 변환 모듈의 31개 검사와 그중 128개 요일 조합의 왕복 검사를 실행했다. IBSheet 라이브러리 자체나 실제 브라우저·서버·DB 연동을 실행 검증한 결과는 아니다. 예제 데이터는 설명용이며 운영 장애를 경험했다는 기록이 아니다.

먼저 요일 코드 계약을 정한다

이 예제는 월요일부터 일요일까지 문자열 코드 "1"부터 "7"을 사용한다. 이것은 이 글에서 정한 애플리케이션 규약이다. IBSheet가 요일 코드를 이렇게 강제한다는 뜻이 아니다. 기존 시스템이 일요일을 "0"으로 쓰거나 MON 같은 코드를 쓴다면 매핑부터 맞춰야 한다.

계층 월·수·금의 예시 역할
서버의 기존 문자열 1|3|5 이 글에서 가정한 기존 API 형식
애플리케이션 내부 배열 ["1", "3", "5"] 검증과 변환의 기준
시트 셀에 전달할 문자열 1;3;5 세미콜론을 사용하는 구성의 예시
사용자에게 보여 줄 문자 월, 수, 금 표시용 결과, 저장 키가 아님

선택 여부와 선택 순서도 구분한다. 이 글의 요일은 집합으로 취급한다. 금요일을 먼저 눌러도 저장할 때는 월→일 순으로 정렬하고 중복을 제거한다. 사용자가 누른 순서 자체가 업무 데이터라면 이 정규화 정책을 그대로 적용하면 안 된다.

Enum 정의의 구분자와 선택 값의 구분자를 혼동하지 않는다

IBSheet 공식 FAQ는 EnumKeys를 값의 기준, Enum을 표시 문자의 기준으로 설명한다. 공식 릴리스 예제에는 Type: "Enum", Range: 1과 함께 복수 선택을 세미콜론으로 나누는 코드가 나온다. 이 글의 기본 컬럼은 그 형태를 요일로 바꾼 구성 예제다.

const weekdayColumn = {
  Header: '운영 요일',
  Name: 'weekdays',
  Type: 'Enum',
  Range: 1,
  Enum: '|월|화|수|목|금|토|일',
  EnumKeys: '|1|2|3|4|5|6|7',
  Width: 220
};

두 목록의 같은 위치가 같은 요일을 가리키도록 관리한다. 앞의 |가 포함된 목록 정의와 선택된 셀 값은 별개의 표현이다. 목록 정의를 보고 서버 값 1|3|5를 그대로 넣거나, 선택 값에 앞 구분자를 덧붙이는 방식은 피한다.

여기서는 공식 예제처럼 선택 값에 ;를 쓰는 구성을 전제로 한다. 프로젝트가 사용 중인 빌드·언어 파일·공통 설정에 따라 실제 입출력을 먼저 확인한다. 확인하지 않은 구분자 옵션 이름을 임의로 추가해 맞추려 하지 않는다.

출처: IBSheet 값과 표시 문자 FAQ, IBSheet 8.2.0.9 릴리스 예제.

저장할 값과 진단할 표시 문자를 따로 읽는다

공식 FAQ는 현재 키를 읽는 getValue와 표시 문자를 읽는 getString을 구분한다. 연결 코드는 다음처럼 두 결과를 함께 확인하는 것부터 시작할 수 있다.

// IBSheet가 제공하는 실제 행 객체 row를 사용한다.
const raw = sheet.getValue(row, 'weekdays');
const display = sheet.getString(row, 'weekdays');
console.log({ raw, display });

이 코드는 대상 IBSheet 환경에 넣어 확인할 진단 예시다. 이번 Node 검사에서 시트 객체를 만들어 실행한 코드는 아니다. 다중 선택 결과의 순서와 구분자, 빈 선택을 해제했을 때 반환값을 실제 빌드에서 기록한다. 화면에서 편집 중이라면 편집을 확정한 시점의 값인지도 확인해야 한다.

getString 결과를 서버의 요일 코드로 저장하지 않는다. 라벨은 표시 언어나 문구 변경에 따라 달라질 수 있다. 저장 계약은 코드 배열 또는 명확히 정의한 코드 문자열로 유지한다.

경계 변환을 한곳에 모은다

변환 코드는 조회할 때 한 번, 저장할 때 한 번 실행하도록 둔다. 이벤트마다 문자열을 바꿔 쓰면 이미 변환된 값이 다시 변환되거나 화면 값과 원본 값이 어긋나기 쉽다.

아래는 첨부한 weekdays.mjs의 핵심 코드다. 알 수 없는 코드, 앞뒤 구분자, 잘못된 자료형은 오류로 처리한다. 잘못된 항목을 조용히 버리고 저장하지 않기 위한 예제 정책이다.

export const DAYS = Object.freeze([
  ['1', '월'], ['2', '화'], ['3', '수'], ['4', '목'],
  ['5', '금'], ['6', '토'], ['7', '일']
].map(pair => Object.freeze(pair)));
const allowed = new Set(DAYS.map(([key]) => key));

export function normalizeKeys(keys) {
  if (!Array.isArray(keys)) throw new TypeError('요일 코드는 배열이어야 합니다.');
  for (const key of keys) {
    if (typeof key !== 'string' || !allowed.has(key)) {
      throw new TypeError(`허용되지 않은 요일 코드: ${String(key)}`);
    }
  }
  const selected = new Set(keys);
  return DAYS.map(([key]) => key).filter(key => selected.has(key));
}

function parseDelimited(value, separator) {
  if (value === null || value === '') return [];
  if (typeof value !== 'string') throw new TypeError('요일 값은 문자열이어야 합니다.');
  return normalizeKeys(value.split(separator));
}

export const legacyToKeys = value => parseDelimited(value, '|');
export const sheetToKeys = value => parseDelimited(value, ';');
export const keysToLegacy = keys => normalizeKeys(keys).join('|');
export const keysToSheet = keys => normalizeKeys(keys).join(';');

여기서 null과 빈 문자열은 모두 선택 없음으로 정했다. 반면 필드가 누락된 undefined는 오류다. 부분 수정 API에서 null이 삭제이고 빈 문자열이 다른 의미라면 이 계약을 바꿔야 한다. 기존 데이터를 읽을 때 값이 비었다는 이유로 자동으로 주말이나 전체 요일을 선택하지 않는다.

trim()이나 filter(Boolean)을 넣지 않은 것도 의도적이다. 1||3을 1|3으로 몰래 바꾸면 잘못된 입력이 어디서 생겼는지 놓칠 수 있다. 공백 허용이나 레거시 정리 정책이 필요하면 별도 마이그레이션 단계로 구분한다.

조회와 저장에서 같은 규칙을 사용한다

다음 예제의 apiRow는 인공 데이터이며, 실제 HTTP 요청을 보내지 않는다.

import {
  legacyToKeys, keysToSheet, sheetToKeys, keysToLegacy
} from './weekdays.mjs';

const apiRow = { id: 42, weekdays: '1|3|5' };
const gridRow = {
  ...apiRow,
  weekdays: keysToSheet(legacyToKeys(apiRow.weekdays))
};
// gridRow.weekdays === '1;3;5'

// 아래 변수에는 대상 환경에서 getValue로 읽은 문자열을 넣는다.
const confirmedCellValue = '1;3;5';
const payload = {
  id: apiRow.id,
  weekdays: keysToLegacy(sheetToKeys(confirmedCellValue))
};
// payload.weekdays === '1|3|5'

그리드가 사용하는 행 객체를 직접 덮어써도 변경 상태가 갱신된다고 가정하지 않는다. 기존 로드·편집·저장 API 흐름에 경계 변환을 연결해야 한다. getSaveJson처럼 여러 행을 저장하는 API를 사용한다면 수정·삭제 상태와 다른 필드를 보존하면서 해당 필드만 변환할 위치를 정한다. 이 글은 IBSheet의 전체 저장 파이프라인을 대체하는 구현을 제공하지 않는다.

새 API를 설계할 수 있다면 코드 배열을 전송하는 계약도 고려할 수 있다. 문자열 구분자는 기존 서버 계약과 호환해야 할 때만 경계에 남긴다. 배열을 쓰더라도 서버에서는 허용 코드, 자료형, 수정 대상 권한을 다시 확인해야 한다.

화면에 선택하지 못하게 만든 설정만으로 서버 검증을 대신할 수는 없다. 관련 원칙은 Vue disabled와 서버 검증에서 이어 볼 수 있다.

표시를 줄이는 작업은 저장이 확인된 다음에 한다

긴 표시를 월 외 2개처럼 줄이고 싶다면 표시 기능을 별도로 검토한다. EnumFormat은 공식 8.2.0.9-20241121-14 릴리스에 추가 기록이 있다. 이 번호는 기능 도입을 확인한 버전이며 최신 버전이라는 의미는 아니다.

현재 빌드에 기능이 있는지, 콜백에 들어오는 값이 어떤 형태인지부터 확인한다. 이 글은 공식 예제의 콜백을 직접 실행하지 않았기 때문에, 키 배열을 받는 함수처럼 가정한 EnumFormat 구현은 제시하지 않는다. 특히 표시 결과를 줄이는 함수가 서버 저장 값을 만드는 함수가 되어서는 안 된다.

요일 이름이 모두 필요한 화면이라면 기본 표시를 유지하는 편이 더 읽기 쉬울 수 있다. 내보내기 파일에서 코드와 표시 문자 중 무엇을 요구하는지도 별도로 정한다. 브라우저 표시가 맞는다고 엑셀 내보내기까지 맞는 것은 아니다. 내보내기 요청 자체가 실패한다면 Vue·IBSheet 엑셀 CORS 점검을 참고한다.

로컬 검사에서 확인한 범위

첨부 압축 파일에는 외부 의존성 없이 실행하는 변환 모듈과 검사 코드, 결과 JSON이 들어 있다.

node verify.mjs

검사는 Node.js v24.19.0에서 실행했다. 31개 검사 중 하나가 가능한 요일 부분집합 128개를 순회하면서 배열→문자열→배열 왕복을 확인한다. 이것을 128개 실제 브라우저 테스트라고 해석하면 안 된다.

입력 또는 검사 확인 결과
기존 문자열 1|3|5 시트용 문자열 1;3;5로 변환
시트용 문자열 1;3;5 기존 문자열 1|3|5로 복원
코드 1, 3, 5의 라벨 변환 월, 수, 금
역순·중복 배열 ["7", "1", "3", "1"] ["1", "3", "7"]로 정규화
빈 문자열·null 선택 없음 배열
0, 8, 01, 월, 잘못된 구분자 오류로 거부
숫자 코드 배열·누락 필드 오류로 거부
원본 배열 변환 과정에서 변경하지 않음
전체 128개 요일 조합 두 문자열 형식의 왕복 결과 일치

results.json은 위 순수 함수 검사 기록이다. 시트 드롭다운의 체크 상태, 키보드 조작, 편집 종료, 변경 표시, 서버 검증, DB 저장, 엑셀 결과는 대상 시스템에서 별도 확인할 항목으로 남아 있다.

저장 후 다시 불러와야 끝난다

적용할 때는 한 행에서 월·수·금을 선택한 뒤 다음 값이 이어지는지 확인한다.

  1. 편집을 확정하고 getValue의 원시 값과 getString의 표시를 확인한다.
  2. 저장 직전 payload의 코드와 구분자가 서버 계약과 일치하는지 확인한다.
  3. 서버가 반환한 값과 실제 재조회 응답을 비교한다.
  4. 재조회 응답을 같은 조회용 변환에 통과시킨다.
  5. 다시 열린 셀에서 같은 세 요일이 선택되는지 확인한다.

빈 선택, 전체 선택, 일요일만 선택도 같은 경로로 확인한다. 오류가 났을 때는 잘못된 코드가 있는 행의 저장을 중단하고 사용자에게 수정할 항목을 알려 준다. 일부 값만 버리고 저장 성공으로 처리하지 않는다.

행 삭제나 순서 변경 후 다른 행에 값이 붙는다면 요일 구분자와는 다른 문제일 수 있다. Vue 동적 폼의 안정적인 key에서 다루는 행 정체성과 함께 확인한다. 일반 Vue의 key 설정이 IBSheet 내부 행 ID를 자동으로 설정해 주는 것은 아니다.

요일 선택 문제는 컬럼 표시, 셀의 키 값, 서버 직렬화, 재조회 결과를 나눠 보면 원인을 좁히기 쉽다. 표시 문자는 사람이 읽는 값으로, 코드와 변환 규약은 저장 계약으로 유지하고 실제 저장 후 재조회까지 확인한다.

참고 문서

공식 문서 확인일: 2026-10-06.

태그

mLog

웹 개발과 서버 운영 과정에서 마주친 문제와 해결 과정을 기록합니다. 직접 확인한 설정과 실행 결과를 함께 정리해, 비슷한 문제를 겪는 분들이 참고할 수 있도록 합니다.