import React, { useState, useEffect, useRef } from 'react';
import {
BookOpen,
Code,
Play,
Save,
FileText,
HelpCircle,
PlusCircle
} from 'lucide-react';
import Editor from '@monaco-editor/react';
const VBAWorkbench = () => {
// 상태 관리
const [activeTab, setActiveTab] = useState('learn');
const [codeSnippets, setCodeSnippets] = useState([]);
const [selectedSnippet, setSelectedSnippet] = useState(null);
const [editorContent, setEditorContent] = useState('');
const [output, setOutput] = useState('');
const [searchTerm, setSearchTerm] = useState('');
const [activeGuide, setActiveGuide] = useState('intro');
const editorRef = useRef(null);
// 샘플 코드 스니펫
const initialSnippets = [
{
id: 1,
title: "Hello World 매크로",
description: "기본적인 메시지 박스 표시하기",
code: `Sub HelloWorld()
MsgBox "Hello, World!", vbInformation, "VBA 예제"
End Sub`,
category: "기초",
output: "간단한 메시지 박스가 표시됩니다."
},
{
id: 2,
title: "셀 범위 선택하기",
description: "특정 범위의 셀을 선택하는 코드",
code: `Sub SelectRange()
' A1부터 D10까지 범위를 선택
Range("A1:D10").Select
' 선택된 범위에 색상 적용
Selection.Interior.Color = RGB(255, 255, 0)
End Sub`,
category: "셀 조작",
output: "A1부터 D10까지의 셀 범위가 선택되고 노란색으로 채워집니다."
},
{
id: 3,
title: "데이터 루프 처리",
description: "For 루프를 사용해 데이터 처리하기",
code: `Sub ProcessData()
Dim i As Integer
' A1부터 A10까지 순차적으로 데이터 입력
For i = 1 To 10
Cells(i, 1).Value = "데이터 " & i
Next i
' 메시지로 완료 알림
MsgBox "데이터 처리 완료!", vbInformation
End Sub`,
category: "루프",
output: "A1부터 A10까지 순차적으로 '데이터 1', '데이터 2'... 형태로 입력되고 완료 메시지가 표시됩니다."
},
{
id: 4,
title: "조건문 사용하기",
description: "If-Then-Else 문을 사용한 조건 처리",
code: `Sub CheckValues()
Dim cell As Range
Dim count As Integer
count = 0
' A1:A10 범위의 셀 확인
For Each cell In Range("A1:A10")
' 셀 값이 5보다 크면 배경색 변경
If cell.Value > 5 Then
cell.Interior.Color = RGB(255, 0, 0)
count = count + 1
End If
Next cell
MsgBox "5보다 큰 값의 개수: " & count, vbInformation
End Sub`,
category: "조건문",
output: "A1:A10 범위에서 값이 5보다 큰 셀의 배경색이 빨간색으로 변경되고, 해당 셀의 개수가 메시지로 표시됩니다."
},
{
id: 5,
title: "사용자 정의 함수",
description: "재사용 가능한 사용자 정의 함수 만들기",
code: `Function CalculateDiscount(price As Double, discountRate As Double) As Double
' 할인된 가격 계산
CalculateDiscount = price * (1 - discountRate)
End Function
Sub UseFunction()
Dim originalPrice As Double
Dim discount As Double
Dim finalPrice As Double
originalPrice = 100
discount = 0.2 ' 20% 할인
' 사용자 정의 함수 호출
finalPrice = CalculateDiscount(originalPrice, discount)
MsgBox "원가: " & originalPrice & vbCrLf & _
"할인율: " & discount * 100 & "%" & vbCrLf & _
"최종 가격: " & finalPrice, vbInformation, "할인 계산"
End Sub`,
category: "함수",
output: "사용자 정의 함수를 사용하여 할인 가격을 계산하고 결과를 메시지 박스로 표시합니다."
}
];
// 가이드 컨텐츠
const guideContent = {
intro: {
title: "VBA 소개",
content: "Visual Basic for Applications (VBA)는 Microsoft Office 애플리케이션에 내장된 프로그래밍 언어로, Excel, Word, PowerPoint 등에서 작업을 자동화하고 사용자 정의 기능을 개발할 수 있게 해줍니다."
},
basics: {
title: "VBA 기초",
content: "VBA에서 변수를 선언하고 사용하는 방법, 기본 구문, 주요 데이터 타입 등 VBA의 기초 지식을 설명합니다."
},
macro: {
title: "매크로 작업",
content: "매크로는 일련의 명령과 작업을 기록하여 반복 작업을 자동화하는 도구입니다. Excel에서 매크로를 기록하고 실행하는 방법을 설명합니다."
},
controls: {
title: "폼 컨트롤",
content: "UserForm은 사용자 인터페이스를 만들기 위한 VBA의 도구입니다. 다양한 컨트롤을 배치하여 사용자와 상호작용하는 창을 만들 수 있습니다."
},
advanced: {
title: "고급 기법",
content: "VBA에서 오류를 처리하는 방법, 클래스 모듈 사용, API 함수 활용 등 고급 기법에 대해 설명합니다."
},
bestpractices: {
title: "모범 사례",
content: "명확한 변수 이름 사용, 코드 모듈화, 변수 선언, 성능 최적화, 에러 처리, 코드 문서화 등 VBA 코드 작성 모범 사례를 소개합니다."
}
};
// 컴포넌트 초기화
useEffect(() => {
setCodeSnippets(initialSnippets);
if (initialSnippets.length > 0) {
setSelectedSnippet(initialSnippets[0]);
setEditorContent(initialSnippets[0].code);
}
}, []);
// 스니펫 선택 처리
const handleSelectSnippet = (snippet) => {
setSelectedSnippet(snippet);
setEditorContent(snippet.code);
setOutput(snippet.output);
};
// 코드 실행 시뮬레이션
const executeCode = () => {
if (!editorContent.trim()) {
setOutput("실행할 코드가 없습니다.");
return;
}
// 실제로는 VBA를 실행할 수 없으므로 실행 결과를 시뮬레이션합니다
if (selectedSnippet) {
setOutput(`[시뮬레이션된 실행 결과]\n${selectedSnippet.output}`);
} else {
// 기본적인 코드 분석을 통한 가상 실행 결과
let result = "코드 실행이 시뮬레이션되었습니다.\n";
if (editorContent.includes("MsgBox")) {
const msgMatch = editorContent.match(/MsgBox\s+["'](.+?)["']/);
if (msgMatch && msgMatch[1]) {
result += `메시지 박스: "${msgMatch[1]}"\n`;
}
}
setOutput(result);
}
};
// 새로운 스니펫 추가 (예시 기능)
const addNewSnippet = () => {
const newId = codeSnippets.length > 0 ? Math.max(...codeSnippets.map(s => s.id)) + 1 : 1;
const newSnippet = {
id: newId,
title: "새 스니펫",
description: "설명을 입력하세요",
code: "Sub NewMacro()\n ' 코드를 입력하세요\nEnd Sub",
category: "사용자 정의",
output: "실행 결과가 여기에 표시됩니다."
};
setCodeSnippets([...codeSnippets, newSnippet]);
setSelectedSnippet(newSnippet);
setEditorContent(newSnippet.code);
};
// 스니펫 저장 (예시 기능)
const saveSnippet = () => {
if (!selectedSnippet) return;
const updatedSnippets = codeSnippets.map(snippet =>
snippet.id === selectedSnippet.id
? { ...snippet, code: editorContent }
: snippet
);
setCodeSnippets(updatedSnippets);
setOutput("코드가 저장되었습니다.");
};
// 필터링된 스니펫 목록
const filteredSnippets = codeSnippets.filter(snippet =>
snippet.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
snippet.description.toLowerCase().includes(searchTerm.toLowerCase()) ||
snippet.category.toLowerCase().includes(searchTerm.toLowerCase())
);
const handleEditorChange = (value) => {
setEditorContent(value);
};
const handleEditorDidMount = (editor, monaco) => {
editorRef.current = editor;
};
return (
<div className="min-h-screen bg-gray-50 py-6 flex flex-col justify-center sm:py-12">
<div className="relative py-3 sm:max-w-3xl sm:mx-auto">
<div className="absolute inset-0 bg-gradient-to-r from-blue-300 to-blue-600 shadow-lg transform -skew-y-6 sm:skew-y-0 sm:-rotate-6 rounded-3xl"></div>
<div className="relative bg-white shadow-lg rounded-3xl p-4 sm:p-8">
<h1 className="text-2xl font-bold mb-4 text-center text-gray-800">Excel VBA 학습 워크벤치</h1>
<div className="flex-grow flex flex-col">
{/* 탭 헤더 */}
<div className="flex bg-gray-100 p-1 rounded-t-lg">
<button
className={`flex items-center px-4 py-2 rounded-t-lg ${activeTab === 'learn' ? 'bg-white shadow-md border-b-2 border-blue-500' : 'hover:bg-gray-200'}`}
onClick={() => setActiveTab('learn')}
>
<BookOpen className="mr-2 h-4 w-4" />
학습
</button>
<button
className={`flex items-center px-4 py-2 rounded-t-lg ${activeTab === 'code' ? 'bg-white shadow-md border-b-2 border-blue-500' : 'hover:bg-gray-200'}`}
onClick={() => setActiveTab('code')}
>
<Code className="mr-2 h-4 w-4" />
코드 작성
</button>
<button
className={`flex items-center px-4 py-2 rounded-t-lg ${activeTab === 'snippets' ? 'bg-white shadow-md border-b-2 border-blue-500' : 'hover:bg-gray-200'}`}
onClick={() => setActiveTab('snippets')}
>
<FileText className="mr-2 h-4 w-4" />
스니펫
</button>
<button
className={`flex items-center px-4 py-2 rounded-t-lg ${activeTab === 'help' ? 'bg-white shadow-md border-b-2 border-blue-500' : 'hover:bg-gray-200'}`}
onClick={() => setActiveTab('help')}
>
<HelpCircle className="mr-2 h-4 w-4" />
도움말
</button>
</div>
{/* 탭 콘텐츠 */}
<div className="flex-grow p-4 overflow-auto">
{/* 학습 탭 */}
{activeTab === 'learn' && (
<div className="flex flex-col md:flex-row h-full">
<div className="w-full md:w-1/4 pr-4 border-r">
<h3 className="font-semibold mb-2 text-gray-700">학습 가이드</h3>
<div className="space-y-2">
{Object.entries(guideContent).map(([key, guide]) => (
<button
key={key}
className={`w-full text-left px-4 py-2 rounded hover:bg-gray-100 ${activeGuide === key ? 'bg-blue-50 text-blue-800 font-medium' : 'text-gray-600'}`}
onClick={() => setActiveGuide(key)}
>
{guide.title}
</button>
))}
</div>
</div>
<div className="w-full md:w-3/4 pl-4">
<h2 className="text-xl font-bold mb-3 text-blue-700">{guideContent[activeGuide].title}</h2>
<p className="text-gray-700">{guideContent[activeGuide].content}</p>
{activeGuide === 'intro' && (
<div className="mt-4">
<h3 className="font-semibold mb-2 text-gray-700">VBA의 주요 특징</h3>
<ul className="list-disc pl-5 space-y-2 text-gray-600">
<li>자동화: 반복적인 작업을 자동화하여 시간을 절약합니다.</li>
<li>사용자 정의: Excel의 기능을 확장하고 맞춤형 솔루션을 개발할 수 있습니다.</li>
<li>통합성: 다른 Office 애플리케이션 및 외부 데이터 소스와 통합이 가능합니다.</li>
<li>접근성: 프로그래밍 초보자도 비교적 쉽게 배울 수 있습니다.</li>
</ul>
<h3 className="font-semibold mt-4 mb-2 text-gray-700">VBA 시작하기</h3>
<p className="text-gray-700">Excel에서 VBA를 시작하려면:</p>
<ol className="list-decimal pl-5 space-y-2 text-gray-600">
<li>Alt + F11 키를 눌러 VBA 편집기를 엽니다.</li>
<li>개발자 탭이 필요한 경우, Excel 옵션 > 리본 사용자 지정에서 활성화할 수 있습니다.</li>
</ol>
</div>
)}
{activeGuide === 'basics' && (
<div className="mt-4">
<h3 className="font-semibold mb-2 text-gray-700">변수와 데이터 타입</h3>
<pre className="bg-gray-50 p-3 rounded mt-2 mb-4 font-mono text-sm text-gray-800">
{`' 변수 선언
Dim myNumber As Integer
Dim myText As String
Dim myDecimal As Double
Dim myBoolean As Boolean
' 값 할당
myNumber = 10
myText = "Hello VBA"
myDecimal = 3.14
myBoolean = True`}
</pre>
<h3 className="font-semibold mt-4 mb-2 text-gray-700">주요 데이터 타입</h3>
<ul className="list-disc pl-5 space-y-2 text-gray-600">
<li><strong>Integer</strong>: 정수 (-32,768 ~ 32,767)</li>
<li><strong>Long</strong>: 긴 정수 (-2,147,483,648 ~ 2,147,483,647)</li>
<li><strong>Double</strong>: 실수 (소수점)</li>
<li><strong>String</strong>: 텍스트</li>
<li><strong>Boolean</strong>: True/False 값</li>
<li><strong>Date</strong>: 날짜 및 시간</li>
<li><strong>Object</strong>: 객체 참조</li>
</ul>
</div>
)}
</div>
</div>
)}
{/* 코드 작성 탭 */}
{activeTab === 'code' && (
<div className="flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-4">
<div className="w-full md:w-2/3 flex flex-col">
<div className="flex justify-between items-center mb-2">
<h3 className="font-semibold text-gray-700">VBA 코드 에디터</h3>
<div className="space-x-2">
<button
className="px-4 py-2 text-sm bg-blue-500 text-white rounded hover:bg-blue-600 flex items-center"
onClick={executeCode}
>
<Play className="mr-1 h-4 w-4" />
실행
</button>
<button
className="px-4 py-2 text-sm bg-gray-200 rounded hover:bg-gray-300 flex items-center"
onClick={saveSnippet}
>
<Save className="mr-1 h-4 w-4" />
저장
</button>
</div>
</div>
<Editor
height="400px"
language="vbscript"
theme="vs-dark"
value={editorContent}
onChange={handleEditorChange}
onMount={handleEditorDidMount}
/>
</div>
<div className="w-full md:w-1/3 flex flex-col">
<h3 className="font-semibold mb-2 text-gray-700">실행 결과</h3>
<div className="flex-grow p-3 bg-gray-50 rounded border border-gray-300 font-mono text-sm overflow-auto min-h-64">
{output || "코드를 실행하면 결과가 여기에 표시됩니다."}
</div>
</div>
</div>
)}
{/* 스니펫 탭 */}
{activeTab === 'snippets' && (
<div className="flex flex-col md:flex-row space-x-4">
<div className="w-full md:w-1/3 flex flex-col">
<div className="flex justify-between items-center mb-2">
<h3 className="font-semibold text-gray-700">코드 스니펫</h3>
<button
className="px-4 py-2 text-sm bg-green-500 text-white rounded hover:bg-green-600 flex items-center"
onClick={addNewSnippet}
>
<PlusCircle className="mr-1 h-4 w-4" />
새 스니펫
</button>
</div>
<div className="mb-2">
<input
type="text"
placeholder="스니펫 검색..."
className="w-full p-3 rounded border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<div className="overflow-auto border rounded h-64">
{filteredSnippets.length > 0 ? (
filteredSnippets.map(snippet => (
<div
key={snippet.id}
className={`p-3 border-b cursor-pointer hover:bg-gray-100 ${
selectedSnippet && selectedSnippet.id === snippet.id ? 'bg-blue-50' : ''
}`}
onClick={() => handleSelectSnippet(snippet)}
>
<div className="font-medium text-gray-800">{snippet.title}</div>
<div className="text-sm text-gray-600">{snippet.description}</div>
<div className="text-xs text-gray-500 mt-1">카테고리: {snippet.category}</div>
</div>
))
) : (
<div className="p-4 text-center text-gray-500">
검색 결과가 없습니다.
</div>
)}
</div>
</div>
<div className="w-full md:w-2/3 flex flex-col">
{selectedSnippet ? (
<>
<div className="flex justify-between items-center mb-2">
<h3 className="font-semibold text-gray-700">{selectedSnippet.title}</h3>
</div>
<Editor
height="300px"
language="vbscript"
theme="vs-dark"
value={editorContent}
onChange={handleEditorChange}
onMount={handleEditorDidMount}
/>
<div className="mt-2">
<h4 className="font-medium text-sm text-gray-700">설명:</h4>
<p className="text-sm text-gray-600">{selectedSnippet.description}</p>
<h4 className="font-medium text-sm mt-2 text-gray-700">실행 결과:</h4>
<p className="text-sm text-gray-600">{selectedSnippet.output}</p>
</div>
</>
) : (
<div className="h-64 flex items-center justify-center text-gray-400">
왼쪽에서 스니펫을 선택하세요.
</div>
)}
</div>
</div>
)}
{/* 도움말 탭 */}
{activeTab === 'help' && (
<div>
<h2 className="text-xl font-bold mb-4 text-blue-700">VBA 워크벤치 도움말</h2>
<div className="space-y-4">
<div>
<h3 className="font-semibold text-lg text-gray-700">워크벤치 사용법</h3>
<p className="mb-2 text-gray-600">이 워크벤치는 Excel VBA 코드를 학습하고 실험할 수 있는 환경을 제공합니다.</p>
<ul className="list-disc pl-5 space-y-2 text-gray-600">
<li><strong>학습 탭</strong>: VBA에 대한 기본 지식과 사용법을 배울 수 있습니다.</li>
<li><strong>코드 작성 탭</strong>: 직접 VBA 코드를 작성하고 실행 결과를 시뮬레이션할 수 있습니다.</li>
<li><strong>스니펫 탭</strong>: 자주 사용하는 코드 조각을 저장하고 관리할 수 있습니다.</li>
<li><strong>도움말 탭</strong>: 워크벤치 사용법과 VBA 관련 리소스를 볼 수 있습니다.</li>
</ul>
</div>
<div>
<h3 className="font-semibold text-lg text-gray-700">주요 기능</h3>
<ul className="list-disc pl-5 space-y-2 text-gray-600">
<li><strong>코드 실행</strong>: 작성한 VBA 코드의 실행 결과를 시뮬레이션합니다. (실제 Excel과 연동되지는 않습니다)</li>
<li><strong>스니펫 저장</strong>: 유용한 코드 조각을 저장하고 나중에 다시 사용할 수 있습니다.</li>
<li><strong>가이드 보기</strong>: VBA 학습에 필요한 다양한 가이드와 참조 자료를 볼 수 있습니다.</li>
</ul>
</div>
<div>
<h3 className="font-semibold text-lg text-gray-700">추가 리소스</h3>
<ul className="list-disc pl-5 space-y-2 text-gray-600">
<li>Microsoft 공식 VBA 문서</li>
<li>Excel VBA 프로그래밍 튜토리얼</li>
<li>VBA 코드 예제 및 샘플 프로젝트</li>
<li>온라인 VBA 커뮤니티 및 포럼</li>
</ul>
</div>
</div>
</div>
)}
</div>
</div>
</div>
</div>
</div>
);
};
export default VBAWorkbench;