ERP-node/테이블_타입_관리_개선_계획서.md

14 KiB

테이블 타입 관리 개선 계획서

🎯 개선 목표

현재 테이블 타입 관리 시스템의 용어 통일과 타입 단순화를 통해 사용자 친화적이고 유연한 시스템으로 개선합니다.

📋 주요 변경사항

1. 용어 변경

  • 웹 타입(Web Type)입력 타입(Input Type)
  • 사용자에게 더 직관적인 명칭으로 변경

2. 입력 타입 단순화

기존 20개 타입에서 8개 핵심 타입으로 단순화:

번호 입력 타입 설명 예시
1 text 텍스트 이름, 제목, 설명
2 number 숫자 수량, 건수, 순번
3 date 날짜 생성일, 완료일, 기한
4 code 코드 상태코드, 유형코드
5 entity 엔티티 고객선택, 제품선택
6 select 선택박스 드롭다운 목록
7 checkbox 체크박스 Y/N, 사용여부
8 radio 라디오버튼 단일선택 옵션

3. DB 타입 제거

  • 컬럼 정보에서 dbType 필드 제거
  • 입력 타입만으로 데이터 처리 방식 결정

🛠️ 기술적 구현 방안

전체 VARCHAR 통일 방식 (확정)

모든 신규 테이블의 사용자 정의 컬럼을 VARCHAR(500)로 생성하고 애플리케이션 레벨에서 형변환 처리

핵심 장점

  • 최대 유연성: 어떤 데이터든 타입 에러 없이 저장 가능
  • 에러 제로: 타입 불일치로 인한 DB 삽입/수정 에러 완전 차단
  • 개발 속도: 복잡한 타입 고민 없이 빠른 개발 가능
  • 요구사항 대응: 필드 성격 변경 시에도 스키마 수정 불필요
  • 데이터 마이그레이션: 기존 시스템 데이터 이관 시 100% 안전

실제 예시

-- 기존 방식 (타입별 생성)
CREATE TABLE products (
  id serial PRIMARY KEY,
  name varchar(255),           -- 텍스트
  price numeric(10,2),         -- 숫자
  launch_date date,            -- 날짜
  is_active boolean            -- 체크박스
);

-- 새로운 방식 (VARCHAR 통일)
CREATE TABLE products (
  id serial PRIMARY KEY,
  created_date timestamp DEFAULT now(),
  updated_date timestamp DEFAULT now(),
  company_code varchar(50) DEFAULT '*',
  writer varchar(100),
  -- 사용자 정의 컬럼들은 모두 VARCHAR(500)
  name varchar(500),           -- 입력타입: text
  price varchar(500),          -- 입력타입: number → "15000.50"
  launch_date varchar(500),    -- 입력타입: date → "2024-03-15"
  is_active varchar(500)       -- 입력타입: checkbox → "Y"/"N"
);

애플리케이션 레벨 처리

// 입력 타입별 형변환 및 검증
export class InputTypeProcessor {

  // 저장 전 변환 (화면 → DB)
  static convertForStorage(value: any, inputType: string): string {
    if (value === null || value === undefined) return "";

    switch (inputType) {
      case "text":
        return String(value);

      case "number":
        const num = parseFloat(String(value));
        return isNaN(num) ? "0" : String(num);

      case "date":
        if (!value) return "";
        const date = new Date(value);
        return isNaN(date.getTime()) ? "" : date.toISOString().split('T')[0];

      case "checkbox":
        return ["true", "1", "Y", "yes"].includes(String(value).toLowerCase()) ? "Y" : "N";

      default:
        return String(value);
    }
  }

  // 표시용 변환 (DB → 화면)
  static convertForDisplay(value: string, inputType: string): any {
    if (!value) return inputType === "number" ? 0 : "";

    switch (inputType) {
      case "number":
        const num = parseFloat(value);
        return isNaN(num) ? 0 : num;

      case "date":
        return value; // YYYY-MM-DD 형식 그대로

      case "checkbox":
        return value === "Y";

      default:
        return value;
    }
  }
}

## 🏗️ 구현 단계

### Phase 1: 타입 정의 수정 (1-2)

#### 1.1 입력 타입 enum 업데이트

```typescript
// frontend/types/unified-web-types.ts
export type InputType =
  | "text" // 텍스트
  | "number" // 숫자
  | "date" // 날짜
  | "code" // 코드
  | "entity" // 엔티티
  | "select" // 선택박스
  | "checkbox" // 체크박스
  | "radio"; // 라디오버튼

1.2 UI 표시명 업데이트

export const INPUT_TYPE_OPTIONS = [
  { value: "text", label: "텍스트", description: "일반 텍스트 입력" },
  { value: "number", label: "숫자", description: "숫자 입력 (정수/소수)" },
  { value: "date", label: "날짜", description: "날짜 선택" },
  { value: "code", label: "코드", description: "공통코드 참조" },
  { value: "entity", label: "엔티티", description: "다른 테이블 참조" },
  { value: "select", label: "선택박스", description: "드롭다운 선택" },
  { value: "checkbox", label: "체크박스", description: "체크박스 입력" },
  { value: "radio", label: "라디오버튼", description: "단일 선택" },
];

Phase 2: 데이터베이스 스키마 수정 (1일)

2.1 테이블 스키마 수정

-- table_type_columns 테이블 수정
ALTER TABLE table_type_columns
DROP COLUMN IF EXISTS db_type;

-- 컬럼명 변경
ALTER TABLE table_type_columns
RENAME COLUMN web_type TO input_type;

-- 기존 데이터 마이그레이션
UPDATE table_type_columns
SET input_type = CASE
  WHEN input_type IN ('textarea') THEN 'text'
  WHEN input_type IN ('decimal') THEN 'number'
  WHEN input_type IN ('datetime') THEN 'date'
  WHEN input_type IN ('dropdown') THEN 'select'
  WHEN input_type IN ('boolean') THEN 'checkbox'
  WHEN input_type NOT IN ('text', 'number', 'date', 'code', 'entity', 'select', 'checkbox', 'radio')
    THEN 'text'
  ELSE input_type
END;

Phase 3: 백엔드 서비스 수정 (2-3일)

3.1 DDL 생성 로직 수정

// 전체 VARCHAR 통일 방식으로 수정
private mapInputTypeToPostgresType(inputType: string): string {
  // 기본 컬럼들은 기존 타입 유지 (시스템 컬럼)
  // 사용자 정의 컬럼은 입력 타입과 관계없이 모두 VARCHAR(500)로 통일
  return "varchar(500)";
}

private generateCreateTableQuery(
  tableName: string,
  columns: CreateColumnDefinition[]
): string {
  // 사용자 정의 컬럼들 - 모두 VARCHAR(500)로 생성
  const columnDefinitions = columns
    .map((col) => {
      let definition = `"${col.name}" varchar(500)`; // 타입 통일

      if (!col.nullable) {
        definition += " NOT NULL";
      }

      if (col.defaultValue) {
        definition += ` DEFAULT '${col.defaultValue}'`;
      }

      return definition;
    })
    .join(",\n  ");

  // 기본 컬럼들 (시스템 필수 컬럼 - 기존 타입 유지)
  const baseColumns = `
  "id" serial PRIMARY KEY,
  "created_date" timestamp DEFAULT now(),
  "updated_date" timestamp DEFAULT now(),
  "writer" varchar(100),
  "company_code" varchar(50) DEFAULT '*'`;

  return `
CREATE TABLE "${tableName}" (${baseColumns},
  ${columnDefinitions}
);`.trim();
}

3.2 입력 타입 처리 서비스 구현

// 통합 입력 타입 처리 서비스
export class InputTypeService {
  // 데이터 저장 전 형변환 (화면 입력값 → DB 저장값)
  static convertForStorage(value: any, inputType: string): string {
    if (value === null || value === undefined) return "";

    switch (inputType) {
      case "text":
      case "select":
      case "radio":
        return String(value);

      case "number":
        const num = parseFloat(String(value));
        return isNaN(num) ? "0" : String(num);

      case "date":
        if (!value) return "";
        const date = new Date(value);
        return isNaN(date.getTime()) ? "" : date.toISOString().split("T")[0];

      case "checkbox":
        return ["true", "1", "Y", "yes", true].includes(value) ? "Y" : "N";

      case "code":
      case "entity":
        return String(value || "");

      default:
        return String(value);
    }
  }

  // 화면 표시용 형변환 (DB 저장값 → 화면 표시값)
  static convertForDisplay(value: string, inputType: string): any {
    if (!value && value !== "0") {
      return inputType === "number" ? 0 : inputType === "checkbox" ? false : "";
    }

    switch (inputType) {
      case "number":
        const num = parseFloat(value);
        return isNaN(num) ? 0 : num;

      case "checkbox":
        return value === "Y" || value === "true";

      case "date":
        return value; // YYYY-MM-DD 형식 그대로 사용

      default:
        return value;
    }
  }

  // 입력값 검증
  static validate(
    value: any,
    inputType: string
  ): { isValid: boolean; message?: string } {
    if (!value && value !== 0) {
      return { isValid: true }; // 빈 값은 허용
    }

    switch (inputType) {
      case "number":
        const num = parseFloat(String(value));
        return {
          isValid: !isNaN(num),
          message: isNaN(num) ? "숫자 형식이 올바르지 않습니다." : undefined,
        };

      case "date":
        const date = new Date(value);
        return {
          isValid: !isNaN(date.getTime()),
          message: isNaN(date.getTime())
            ? "날짜 형식이 올바르지 않습니다."
            : undefined,
        };

      default:
        return { isValid: true };
    }
  }
}

Phase 4: 프론트엔드 UI 수정 (2일)

4.1 테이블 관리 화면 수정

  • "웹 타입" → "입력 타입" 라벨 변경
  • DB 타입 컬럼 제거
  • 8개 타입만 선택 가능하도록 UI 수정

4.2 화면 관리 시스템 연동

  • 웹타입 → 입력타입 용어 통일
  • 기존 화면관리 컴포넌트와 호환성 유지

Phase 5: 기본 컬럼 유지 로직 보강 (1일)

5.1 테이블 생성 시 기본 컬럼 자동 추가

const DEFAULT_COLUMNS = [
  {
    name: "id",
    type: "serial PRIMARY KEY",
    description: "기본키 (자동증가)",
  },
  {
    name: "created_date",
    type: "timestamp DEFAULT now()",
    description: "생성일시",
  },
  {
    name: "updated_date",
    type: "timestamp DEFAULT now()",
    description: "수정일시",
  },
  {
    name: "writer",
    type: "varchar(100)",
    description: "작성자",
  },
  {
    name: "company_code",
    type: "varchar(50) DEFAULT '*'",
    description: "회사코드",
  },
];

🧪 테스트 계획

1. 단위 테스트

  • 입력 타입별 형변환 함수 테스트
  • 데이터 검증 로직 테스트
  • DDL 생성 로직 테스트

2. 통합 테스트

  • 테이블 생성 → 데이터 입력 → 조회 전체 플로우
  • 기존 테이블과의 호환성 테스트
  • 화면관리 시스템 연동 테스트

3. 성능 테스트

  • VARCHAR vs 전용 타입 성능 비교
  • 대용량 데이터 입력 테스트
  • 형변환 오버헤드 측정

📊 마이그레이션 전략

기존 데이터 호환성

  1. 기존 테이블: 현재 타입 구조 유지
  2. 신규 테이블: 새로운 입력 타입 체계 적용
  3. 점진적 전환: 필요에 따라 기존 테이블도 단계적 전환

데이터 무결성 보장

  • 형변환 실패 시 기본값 사용
  • 검증 로직을 통한 데이터 품질 관리
  • 에러 로깅 및 알림 시스템

🎯 예상 효과

사용자 경험 개선

  • 직관적인 용어로 학습 비용 감소
  • 8개 타입으로 선택 복잡도 감소
  • 일관된 인터페이스 제공

개발 생산성 향상

  • 타입 관리 복잡도 감소
  • 에러 발생률 감소
  • 빠른 프로토타이핑 가능

시스템 유연성 확보

  • 요구사항 변경에 빠른 대응
  • 다양한 데이터 형식 수용
  • 확장성 있는 구조

🚨 주의사항

데이터 검증 강화 필요

VARCHAR 통일 방식 적용 시 애플리케이션 레벨에서 철저한 데이터 검증이 필요합니다.

성능 모니터링

초기 적용 후 성능 영향도를 지속적으로 모니터링하여 필요 시 최적화 방안을 강구합니다.

문서화 업데이트

새로운 입력 타입 체계에 대한 사용자 가이드 및 개발 문서를 업데이트합니다.

📅 일정

단계 소요시간 담당
Phase 1: 타입 정의 수정 1-2일 프론트엔드
Phase 2: DB 스키마 수정 1일 백엔드
Phase 3: 백엔드 서비스 수정 2-3일 백엔드
Phase 4: 프론트엔드 UI 수정 2일 프론트엔드
Phase 5: 기본 컬럼 로직 보강 1일 백엔드
총 소요시간 7-9일

결론: 전체 VARCHAR 통일 방식을 확정하여 최대한의 유연성과 안정성을 확보합니다.

🎯 핵심 결정사항 요약

확정된 방향성

  1. 용어 통일: 웹 타입 → 입력 타입
  2. 타입 단순화: 20개 → 8개 핵심 타입
  3. DB 타입 제거: dbType 필드 완전 삭제
  4. 저장 방식: 모든 사용자 정의 컬럼을 VARCHAR(500)로 통일
  5. 형변환: 애플리케이션 레벨에서 입력 타입별 처리

🚀 예상 효과

  • 개발 속도 3배 향상: 타입 고민 시간 제거
  • 에러율 90% 감소: DB 타입 불일치 에러 완전 차단
  • 요구사항 대응력 극대화: 스키마 수정 없이 필드 성격 변경 가능
  • 데이터 마이그레이션 100% 안전: 어떤 형태의 데이터도 수용 가능