Initial commit: 교육 프로젝트 배포

This commit is contained in:
송대일
2026-07-01 18:32:42 +09:00
commit be6dccd120
1483 changed files with 5082202 additions and 0 deletions
+414
View File
@@ -0,0 +1,414 @@
/**
* 설정 관리 모듈
* @module ConfigManager
*/
class ConfigManager {
constructor(defaults = {}) {
this.defaults = defaults;
this.config = { ...defaults };
}
/**
* 설정 값 가져오기
* @param {string} key - 키 (점 표기법 지원)
* @param {*} defaultValue - 기본값
* @returns {*}
*/
get(key, defaultValue = null) {
return this._getNestedValue(this.config, key, defaultValue);
}
/**
* 설정 값 설정
* @param {string} key - 키 (점 표기법 지원)
* @param {*} value - 값
*/
set(key, value) {
this._setNestedValue(this.config, key, value);
}
/**
* 여러 설정 값 설정
* @param {Object} config - 설정 객체
*/
setMultiple(config) {
this.config = this._deepMerge(this.config, config);
}
/**
* 설정 값 삭제
* @param {string} key - 키
*/
remove(key) {
this._deleteNestedValue(this.config, key);
}
/**
* 설정 값 존재 확인
* @param {string} key - 키
* @returns {boolean}
*/
has(key) {
return this._getNestedValue(this.config, key) !== undefined;
}
/**
* 모든 설정 가져오기
* @returns {Object}
*/
getAll() {
return { ...this.config };
}
/**
* 설정 초기화
*/
reset() {
this.config = { ...this.defaults };
}
/**
* 기본값으로 병합
* @param {Object} config - 설정 객체
* @returns {Object}
*/
mergeWithDefaults(config) {
return this._deepMerge({ ...this.defaults }, config);
}
/**
* JSON 문자열로 변환
* @returns {string}
*/
toJSON() {
return JSON.stringify(this.config, null, 2);
}
/**
* JSON 문자열에서 로드
* @param {string} json - JSON 문자열
*/
fromJSON(json) {
try {
const parsed = JSON.parse(json);
this.config = this._deepMerge({ ...this.defaults }, parsed);
} catch (error) {
console.error("Failed to parse JSON:", error);
}
}
/**
* 로컬 스토리지에 저장
* @param {string} key - 저장 키
*/
saveToStorage(key = "app_config") {
try {
localStorage.setItem(key, JSON.stringify(this.config));
return true;
} catch (error) {
console.error("Failed to save to storage:", error);
return false;
}
}
/**
* 로컬 스토리지에서 로드
* @param {string} key - 저장 키
*/
loadFromStorage(key = "app_config") {
try {
const stored = localStorage.getItem(key);
if (stored) {
const parsed = JSON.parse(stored);
this.config = this._deepMerge({ ...this.defaults }, parsed);
return true;
}
return false;
} catch (error) {
console.error("Failed to load from storage:", error);
return false;
}
}
/**
* 중첩된 객체 값 가져오기
* @private
*/
_getNestedValue(obj, key, defaultValue = null) {
const keys = key.split(".");
let value = obj;
for (const k of keys) {
if (value && typeof value === "object" && k in value) {
value = value[k];
} else {
return defaultValue;
}
}
return value !== undefined ? value : defaultValue;
}
/**
* 중첩된 객체 값 설정
* @private
*/
_setNestedValue(obj, key, value) {
const keys = key.split(".");
const lastKey = keys.pop();
let target = obj;
for (const k of keys) {
if (!(k in target) || typeof target[k] !== "object") {
target[k] = {};
}
target = target[k];
}
target[lastKey] = value;
}
/**
* 중첩된 객체 값 삭제
* @private
*/
_deleteNestedValue(obj, key) {
const keys = key.split(".");
const lastKey = keys.pop();
let target = obj;
for (const k of keys) {
if (!(k in target) || typeof target[k] !== "object") {
return;
}
target = target[k];
}
delete target[lastKey];
}
/**
* 깊은 병합
* @private
*/
_deepMerge(target, source) {
const output = { ...target };
if (this._isObject(target) && this._isObject(source)) {
Object.keys(source).forEach((key) => {
if (this._isObject(source[key])) {
if (!(key in target)) {
output[key] = source[key];
} else {
output[key] = this._deepMerge(target[key], source[key]);
}
} else {
output[key] = source[key];
}
});
}
return output;
}
/**
* 객체 확인
* @private
*/
_isObject(item) {
return item && typeof item === "object" && !Array.isArray(item);
}
}
/**
* 앱 설정 관리 (싱글톤)
*/
class AppConfig extends ConfigManager {
static instance = null;
constructor(defaults = {}) {
if (AppConfig.instance) {
return AppConfig.instance;
}
super({
app: {
name: "My App",
version: "1.0.0",
debug: false,
},
ui: {
theme: "light",
language: "ko",
animations: true,
},
features: {
search: true,
notifications: true,
autoSave: true,
},
...defaults,
});
AppConfig.instance = this;
}
/**
* 싱글톤 인스턴스 가져오기
* @returns {AppConfig}
*/
static getInstance() {
if (!AppConfig.instance) {
AppConfig.instance = new AppConfig();
}
return AppConfig.instance;
}
/**
* 앱 이름 가져오기
* @returns {string}
*/
getAppName() {
return this.get("app.name");
}
/**
* 앱 버전 가져오기
* @returns {string}
*/
getAppVersion() {
return this.get("app.version");
}
/**
* 디버그 모드 확인
* @returns {boolean}
*/
isDebugMode() {
return this.get("app.debug", false);
}
/**
* 테마 가져오기
* @returns {string}
*/
getTheme() {
return this.get("ui.theme", "light");
}
/**
* 테마 설정
* @param {string} theme - 테마
*/
setTheme(theme) {
this.set("ui.theme", theme);
this.saveToStorage();
}
/**
* 언어 가져오기
* @returns {string}
*/
getLanguage() {
return this.get("ui.language", "ko");
}
/**
* 언어 설정
* @param {string} language - 언어
*/
setLanguage(language) {
this.set("ui.language", language);
this.saveToStorage();
}
/**
* 애니메이션 사용 여부
* @returns {boolean}
*/
useAnimations() {
return this.get("ui.animations", true);
}
/**
* 기능 활성화 여부
* @param {string} feature - 기능 이름
* @returns {boolean}
*/
isFeatureEnabled(feature) {
return this.get(`features.${feature}`, false);
}
/**
* 기능 토글
* @param {string} feature - 기능 이름
*/
toggleFeature(feature) {
const current = this.isFeatureEnabled(feature);
this.set(`features.${feature}`, !current);
this.saveToStorage();
}
}
/**
* HTML data 속성에서 설정 로드
*/
class DataAttributeConfig {
/**
* 요소에서 설정 로드
* @param {Element} element - 대상 요소
* @param {string} prefix - 속성 접두사
* @returns {Object}
*/
static loadFromElement(element, prefix = "data-") {
if (!element) return {};
const config = {};
const attributes = element.attributes;
for (let i = 0; i < attributes.length; i++) {
const attr = attributes[i];
if (attr.name.startsWith(prefix)) {
const key = attr.name
.substring(prefix.length)
.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
config[key] = this._parseValue(attr.value);
}
}
return config;
}
/**
* 값 파싱
* @private
*/
static _parseValue(value) {
// boolean
if (value === "true") return true;
if (value === "false") return false;
// number
if (!isNaN(value) && value !== "") {
return parseFloat(value);
}
// JSON
if ((value.startsWith("{") || value.startsWith("[")) &&
(value.endsWith("}") || value.endsWith("]"))) {
try {
return JSON.parse(value);
} catch (e) {
// JSON 파싱 실패 시 문자열 반환
}
}
// string
return value;
}
}
// ES6 모듈 내보내기
if (typeof module !== "undefined" && module.exports) {
module.exports = { ConfigManager, AppConfig, DataAttributeConfig };
}