개요
에러 처리는 에디터 안정성을 유지하는 데 중요합니다. 모델 작업, DOM 동기화, 사용자 입력 처리 또는 외부 통합 중에 에러가 발생할 수 있습니다. 이 가이드는 에러 타입, 처리 전략 및 복구 메커니즘을 다룹니다.
핵심 원칙:
- 우아하게 실패 - 에디터가 크래시되지 않도록
- 모델 일관성 유지 - 에러 시 롤백
- 사용자 피드백 제공 - 문제를 사용자에게 알림
- 디버깅을 위한 에러 로깅 - 에러 컨텍스트 캡처
- 가능한 경우 자동 복구
에러 타입
에디터에서 일반적인 에러 타입:
Model Errors
// 잘못된 operation
class ModelError extends Error {
constructor(
message: string,
public operation: Operation,
public model: DocumentModel
) {
super(message);
this.name = 'ModelError';
}
}
// 스키마 검증 에러
class SchemaError extends Error {
constructor(
message: string,
public node: Node,
public schema: Schema
) {
super(message);
this.name = 'SchemaError';
}
}DOM Errors
// DOM 노드를 찾을 수 없음
class DOMNodeError extends Error {
constructor(
message: string,
public expectedNode: Node | null,
public actualDOM: HTMLElement
) {
super(message);
this.name = 'DOMNodeError';
}
}에러 처리 전략
다른 에러 타입은 다른 처리 전략이 필요합니다:
Try-Catch 블록
class Editor {
applyOperation(operation: Operation) {
try {
// Operation 검증
this.validateOperation(operation);
// 모델에 적용
const newModel = this.model.applyOperation(operation);
// DOM 업데이트
this.renderer.update(newModel);
} catch (error) {
// 에러 처리
this.handleError(error, operation);
// 중요 에러면 재발생
if (error instanceof CriticalError) {
throw error;
}
}
}
private handleError(error: Error, operation: Operation) {
// 에러 로깅
this.logger.error('Operation 실패', { error, operation });
// 필요 시 롤백
if (this.model.isDirty) {
this.model.rollback();
}
// 사용자에게 알림
this.notifyUser('Operation을 완료할 수 없습니다', 'error');
}
}복구 메커니즘
복구 메커니즘은 에러 후 에디터 상태를 복원합니다:
Rollback
class Model {
private history: ModelSnapshot[] = [];
private currentSnapshot: ModelSnapshot;
beginTransaction() {
// 현재 상태 저장
this.history.push(this.currentSnapshot.clone());
}
rollback() {
if (this.history.length > 0) {
this.currentSnapshot = this.history.pop()!;
return true;
}
return false;
}
}
// 에러 처리에서 사용
try {
editor.model.beginTransaction();
editor.applyOperation(operation);
editor.model.commit();
} catch (error) {
// 에러 시 롤백
editor.model.rollback();
editor.renderer.update(editor.model);
throw error;
}재동기화
class Editor {
reSync() {
try {
// DOM에서 모델 재구축
const newModel = this.parser.parse(this.element);
// 모델 검증
const validation = this.schema.validate(newModel);
if (!validation.valid) {
// 모델 수정 시도
const fixedModel = this.schema.fix(newModel);
this.model = fixedModel;
} else {
this.model = newModel;
}
// 일관성 보장을 위해 재렌더링
this.renderer.fullRender(this.model);
} catch (error) {
// 최후의 수단: 빈 상태로 리셋
this.reset();
this.notifyUser('에러로 인해 에디터가 리셋되었습니다', 'warning');
}
}
}에러 로깅
포괄적인 에러 로깅은 디버깅 및 모니터링에 도움이 됩니다:
interface ErrorLog {
timestamp: number;
error: Error;
context: {
operation?: Operation;
model?: DocumentModel;
selection?: Selection;
userAction?: string;
};
recovered: boolean;
}
class ErrorLogger {
private logs: ErrorLog[] = [];
log(error: Error, context: ErrorContext, recovered: boolean = false) {
const log: ErrorLog = {
timestamp: Date.now(),
error,
context: {
operation: context.operation,
selection: context.selection,
userAction: context.userAction,
},
recovered,
};
this.logs.push(log);
// 프로덕션에서 모니터링 서비스로 전송
if (process.env.NODE_ENV === 'production') {
this.sendToMonitoring(log);
}
}
}사용자 피드백
사용자는 방해가 되지 않는 방식으로 에러에 대해 알림을 받아야 합니다:
class ErrorNotifier {
notify(message: string, type: 'error' | 'warning' | 'info' = 'error') {
// 알림 생성 또는 업데이트
const element = document.createElement('div');
element.textContent = message;
element.className = 'error-notification error-notification-' + type;
element.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
padding: 12px 16px;
background: #f44336;
color: white;
border-radius: 4px;
z-index: 10000;
`;
document.body.appendChild(element);
// 5초 후 자동 숨김
setTimeout(() => {
element.remove();
}, 5000);
}
}
// 사용
try {
editor.applyOperation(operation);
} catch (error) {
notifier.notify('Operation을 완료할 수 없습니다. 변경 사항이 되돌려졌습니다.', 'error');
}