<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ko">
	<id>https://devcafe.co.kr/w/index.php?action=history&amp;feed=atom&amp;title=Autoit_web_%ED%95%98%EC%9D%B4%EB%B8%8C%EB%A6%AC%EB%93%9C_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</id>
	<title>Autoit web 하이브리드 프로그래밍 - 편집 역사</title>
	<link rel="self" type="application/atom+xml" href="https://devcafe.co.kr/w/index.php?action=history&amp;feed=atom&amp;title=Autoit_web_%ED%95%98%EC%9D%B4%EB%B8%8C%EB%A6%AC%EB%93%9C_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D"/>
	<link rel="alternate" type="text/html" href="https://devcafe.co.kr/w/index.php?title=Autoit_web_%ED%95%98%EC%9D%B4%EB%B8%8C%EB%A6%AC%EB%93%9C_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D&amp;action=history"/>
	<updated>2026-05-19T07:31:23Z</updated>
	<subtitle>이 문서의 편집 역사</subtitle>
	<generator>MediaWiki 1.42.1</generator>
	<entry>
		<id>https://devcafe.co.kr/w/index.php?title=Autoit_web_%ED%95%98%EC%9D%B4%EB%B8%8C%EB%A6%AC%EB%93%9C_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D&amp;diff=2411&amp;oldid=prev</id>
		<title>2026년 5월 18일 (월) 10:14에 Devcafe님의 편집</title>
		<link rel="alternate" type="text/html" href="https://devcafe.co.kr/w/index.php?title=Autoit_web_%ED%95%98%EC%9D%B4%EB%B8%8C%EB%A6%AC%EB%93%9C_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D&amp;diff=2411&amp;oldid=prev"/>
		<updated>2026-05-18T10:14:36Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;ko&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← 이전 판&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;2026년 5월 18일 (월) 19:14 판&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l302&quot;&gt;302번째 줄:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;302번째 줄:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	•	셀 서식: value 외에 style_json 컬럼 추가, jspreadsheet getStyle()/setStyle() 활용&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	•	셀 서식: value 외에 style_json 컬럼 추가, jspreadsheet getStyle()/setStyle() 활용&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	•	수식: jspreadsheet-ce가 기본 SUM/AVG 등 지원 — 자동 동작함&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	•	수식: jspreadsheet-ce가 기본 SUM/AVG 등 지원 — 자동 동작함&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	•	오라클 연동: /api/oracle/query 엔드포인트 추가해서 AutoIt에서 sqlplus.exe 호출 결과를 &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;그리드에 &lt;/del&gt;표시&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	•	오라클 연동: /api/oracle/query 엔드포인트 추가해서 &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;[[&lt;/ins&gt;AutoIt에서 sqlplus.exe 호출 결과를 &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;그리드]]에 &lt;/ins&gt;표시&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;CSV import나 다중 시트, 수식 자동계산 결과 저장 등 원하시는 방향 있으면 이어서 만들어 드릴게요.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;CSV import나 다중 시트, 수식 자동계산 결과 저장 등 원하시는 방향 있으면 이어서 만들어 드릴게요.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Devcafe</name></author>
	</entry>
	<entry>
		<id>https://devcafe.co.kr/w/index.php?title=Autoit_web_%ED%95%98%EC%9D%B4%EB%B8%8C%EB%A6%AC%EB%93%9C_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D&amp;diff=2410&amp;oldid=prev</id>
		<title>Devcafe: 새 문서: 좋습니다. AutoIt이 로컬 HTTP 서버 + SQLite 백엔드 역할을 하고, 브라우저에서 jspreadsheet-ce(엑셀형 그리드)로 화면을 띄우는 구조로 만들어 드릴게요.  구조 &lt;source lang=bash&gt; project/ ├── main.au3          ← HTTP 서버 + SQLite ├── sqlite3.dll       ← SQLite UDF용 (AutoIt SQLite UDF에 포함) └── web/     └── index.html    ← 엑셀형 UI &lt;/source&gt;   동작: AutoIt이 127.0.0.1:8765 에서 listen →...</title>
		<link rel="alternate" type="text/html" href="https://devcafe.co.kr/w/index.php?title=Autoit_web_%ED%95%98%EC%9D%B4%EB%B8%8C%EB%A6%AC%EB%93%9C_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D&amp;diff=2410&amp;oldid=prev"/>
		<updated>2026-05-18T10:03:55Z</updated>

		<summary type="html">&lt;p&gt;새 문서: 좋습니다. AutoIt이 로컬 HTTP 서버 + SQLite 백엔드 역할을 하고, 브라우저에서 jspreadsheet-ce(엑셀형 그리드)로 화면을 띄우는 구조로 만들어 드릴게요.  구조 &amp;lt;source lang=bash&amp;gt; project/ ├── main.au3          ← HTTP 서버 + SQLite ├── sqlite3.dll       ← SQLite UDF용 (AutoIt SQLite UDF에 포함) └── web/     └── index.html    ← 엑셀형 UI &amp;lt;/source&amp;gt;   동작: AutoIt이 127.0.0.1:8765 에서 listen →...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;새 문서&lt;/b&gt;&lt;/p&gt;&lt;div&gt;좋습니다. AutoIt이 로컬 HTTP 서버 + SQLite 백엔드 역할을 하고, 브라우저에서 jspreadsheet-ce(엑셀형 그리드)로 화면을 띄우는 구조로 만들어 드릴게요.&lt;br /&gt;
&lt;br /&gt;
구조&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
project/&lt;br /&gt;
├── main.au3          ← HTTP 서버 + SQLite&lt;br /&gt;
├── sqlite3.dll       ← SQLite UDF용 (AutoIt SQLite UDF에 포함)&lt;br /&gt;
└── web/&lt;br /&gt;
    └── index.html    ← 엑셀형 UI&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
동작: AutoIt이 127.0.0.1:8765 에서 listen → 자동으로 기본 브라우저 오픈 → 그리드에서 입력/수정 → /api/save, /api/load 로 SQLite와 통신.&lt;br /&gt;
&lt;br /&gt;
main.au3&lt;br /&gt;
&amp;lt;source lang=autoit&amp;gt;&lt;br /&gt;
#include &amp;lt;SQLite.au3&amp;gt;&lt;br /&gt;
#include &amp;lt;SQLite.dll.au3&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Opt(&amp;quot;TCPTimeout&amp;quot;, 100)&lt;br /&gt;
OnAutoItExitRegister(&amp;quot;OnExit&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Global $g_sDBPath  = @ScriptDir &amp;amp; &amp;quot;\sheet.db&amp;quot;&lt;br /&gt;
Global $g_sWebRoot = @ScriptDir &amp;amp; &amp;quot;\web&amp;quot;&lt;br /&gt;
Global $g_iPort    = 8765&lt;br /&gt;
Global $g_hDB&lt;br /&gt;
&lt;br /&gt;
_SQLite_Startup()&lt;br /&gt;
If @error Then Exit MsgBox(16, &amp;quot;Err&amp;quot;, &amp;quot;SQLite 시작 실패&amp;quot;)&lt;br /&gt;
$g_hDB = _SQLite_Open($g_sDBPath)&lt;br /&gt;
_SQLite_Exec($g_hDB, _&lt;br /&gt;
    &amp;quot;CREATE TABLE IF NOT EXISTS sheet_data(&amp;quot; &amp;amp; _&lt;br /&gt;
    &amp;quot;  row_idx INTEGER, col_idx INTEGER, value TEXT,&amp;quot; &amp;amp; _&lt;br /&gt;
    &amp;quot;  PRIMARY KEY(row_idx, col_idx));&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
TCPStartup()&lt;br /&gt;
Local $iListen = TCPListen(&amp;quot;127.0.0.1&amp;quot;, $g_iPort)&lt;br /&gt;
If $iListen = -1 Then Exit MsgBox(16, &amp;quot;Err&amp;quot;, &amp;quot;포트 사용중: &amp;quot; &amp;amp; $g_iPort)&lt;br /&gt;
&lt;br /&gt;
ShellExecute(&amp;quot;http://127.0.0.1:&amp;quot; &amp;amp; $g_iPort &amp;amp; &amp;quot;/&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
While 1&lt;br /&gt;
    Local $iSock = TCPAccept($iListen)&lt;br /&gt;
    If $iSock &amp;gt;= 0 Then _Handle($iSock)&lt;br /&gt;
    Sleep(10)&lt;br /&gt;
WEnd&lt;br /&gt;
&lt;br /&gt;
Func _Handle($iSock)&lt;br /&gt;
    Local $sReq = &amp;quot;&amp;quot;, $iWait = 0&lt;br /&gt;
    While $iWait &amp;lt; 300&lt;br /&gt;
        Local $c = TCPRecv($iSock, 8192)&lt;br /&gt;
        If $c &amp;lt;&amp;gt; &amp;quot;&amp;quot; Then&lt;br /&gt;
            $sReq &amp;amp;= $c&lt;br /&gt;
            If StringInStr($sReq, @CRLF &amp;amp; @CRLF) Then ExitLoop&lt;br /&gt;
        EndIf&lt;br /&gt;
        Sleep(5)&lt;br /&gt;
        $iWait += 1&lt;br /&gt;
    WEnd&lt;br /&gt;
&lt;br /&gt;
    Local $iSplit = StringInStr($sReq, @CRLF &amp;amp; @CRLF)&lt;br /&gt;
    Local $sHead  = StringLeft($sReq, $iSplit - 1)&lt;br /&gt;
    Local $sBody  = StringMid($sReq, $iSplit + 3)&lt;br /&gt;
&lt;br /&gt;
    Local $aCL = StringRegExp($sHead, &amp;quot;(?i)Content-Length:\s*(\d+)&amp;quot;, 1)&lt;br /&gt;
    Local $iCL = (IsArray($aCL)) ? Int($aCL[0]) : 0&lt;br /&gt;
    While StringLen(BinaryToString(StringToBinary($sBody, 4), 4)) &amp;lt; $iCL&lt;br /&gt;
        Local $c = TCPRecv($iSock, 8192)&lt;br /&gt;
        If $c &amp;lt;&amp;gt; &amp;quot;&amp;quot; Then $sBody &amp;amp;= $c&lt;br /&gt;
        Sleep(5)&lt;br /&gt;
    WEnd&lt;br /&gt;
&lt;br /&gt;
    Local $aFirst = StringRegExp($sHead, &amp;quot;^(\S+)\s+(\S+)&amp;quot;, 1)&lt;br /&gt;
    If Not IsArray($aFirst) Then&lt;br /&gt;
        TCPCloseSocket($iSock)&lt;br /&gt;
        Return&lt;br /&gt;
    EndIf&lt;br /&gt;
    Local $sMethod = $aFirst[0], $sPath = $aFirst[1]&lt;br /&gt;
&lt;br /&gt;
    Switch $sPath&lt;br /&gt;
        Case &amp;quot;/&amp;quot;, &amp;quot;/index.html&amp;quot;&lt;br /&gt;
            _SendFile($iSock, $g_sWebRoot &amp;amp; &amp;quot;\index.html&amp;quot;, &amp;quot;text/html; charset=utf-8&amp;quot;)&lt;br /&gt;
        Case &amp;quot;/api/load&amp;quot;&lt;br /&gt;
            _ApiLoad($iSock)&lt;br /&gt;
        Case &amp;quot;/api/save&amp;quot;&lt;br /&gt;
            _ApiSave($iSock, $sBody)&lt;br /&gt;
        Case &amp;quot;/api/clear&amp;quot;&lt;br /&gt;
            _SQLite_Exec($g_hDB, &amp;quot;DELETE FROM sheet_data;&amp;quot;)&lt;br /&gt;
            _SendJson($iSock, &amp;#039;{&amp;quot;ok&amp;quot;:true}&amp;#039;)&lt;br /&gt;
        Case Else&lt;br /&gt;
            _Send404($iSock)&lt;br /&gt;
    EndSwitch&lt;br /&gt;
&lt;br /&gt;
    TCPCloseSocket($iSock)&lt;br /&gt;
EndFunc&lt;br /&gt;
&lt;br /&gt;
Func _ApiLoad($iSock)&lt;br /&gt;
    Local $aRes, $iRows, $iCols&lt;br /&gt;
    _SQLite_GetTable2d($g_hDB, _&lt;br /&gt;
        &amp;quot;SELECT row_idx,col_idx,value FROM sheet_data ORDER BY row_idx,col_idx;&amp;quot;, _&lt;br /&gt;
        $aRes, $iRows, $iCols)&lt;br /&gt;
    Local $s = &amp;#039;{&amp;quot;cells&amp;quot;:[&amp;#039;&lt;br /&gt;
    For $i = 1 To $iRows&lt;br /&gt;
        If $i &amp;gt; 1 Then $s &amp;amp;= &amp;quot;,&amp;quot;&lt;br /&gt;
        $s &amp;amp;= &amp;#039;{&amp;quot;r&amp;quot;:&amp;#039; &amp;amp; $aRes[$i][0] &amp;amp; &amp;#039;,&amp;quot;c&amp;quot;:&amp;#039; &amp;amp; $aRes[$i][1] &amp;amp; _&lt;br /&gt;
              &amp;#039;,&amp;quot;v&amp;quot;:&amp;#039; &amp;amp; _JsonStr($aRes[$i][2]) &amp;amp; &amp;#039;}&amp;#039;&lt;br /&gt;
    Next&lt;br /&gt;
    $s &amp;amp;= &amp;#039;]}&amp;#039;&lt;br /&gt;
    _SendJson($iSock, $s)&lt;br /&gt;
EndFunc&lt;br /&gt;
&lt;br /&gt;
Func _ApiSave($iSock, $sBody)&lt;br /&gt;
    _SQLite_Exec($g_hDB, &amp;quot;BEGIN;&amp;quot;)&lt;br /&gt;
    _SQLite_Exec($g_hDB, &amp;quot;DELETE FROM sheet_data;&amp;quot;)&lt;br /&gt;
    Local $aM = StringRegExp($sBody, _&lt;br /&gt;
        &amp;#039;\{&amp;quot;r&amp;quot;:(\d+),&amp;quot;c&amp;quot;:(\d+),&amp;quot;v&amp;quot;:&amp;quot;((?:\\.|[^&amp;quot;\\])*)&amp;quot;\}&amp;#039;, 3)&lt;br /&gt;
    If Not @error Then&lt;br /&gt;
        Local $sStmt&lt;br /&gt;
        For $i = 0 To UBound($aM) - 1 Step 3&lt;br /&gt;
            Local $v = _JsonUnesc($aM[$i + 2])&lt;br /&gt;
            $v = StringReplace($v, &amp;quot;&amp;#039;&amp;quot;, &amp;quot;&amp;#039;&amp;#039;&amp;quot;)&lt;br /&gt;
            _SQLite_Exec($g_hDB, _&lt;br /&gt;
                &amp;quot;INSERT INTO sheet_data VALUES(&amp;quot; &amp;amp; _&lt;br /&gt;
                $aM[$i] &amp;amp; &amp;quot;,&amp;quot; &amp;amp; $aM[$i+1] &amp;amp; &amp;quot;,&amp;#039;&amp;quot; &amp;amp; $v &amp;amp; &amp;quot;&amp;#039;);&amp;quot;)&lt;br /&gt;
        Next&lt;br /&gt;
    EndIf&lt;br /&gt;
    _SQLite_Exec($g_hDB, &amp;quot;COMMIT;&amp;quot;)&lt;br /&gt;
    _SendJson($iSock, &amp;#039;{&amp;quot;ok&amp;quot;:true}&amp;#039;)&lt;br /&gt;
EndFunc&lt;br /&gt;
&lt;br /&gt;
Func _SendFile($iSock, $sFile, $sCT)&lt;br /&gt;
    If Not FileExists($sFile) Then Return _Send404($iSock)&lt;br /&gt;
    Local $h = FileOpen($sFile, 16)&lt;br /&gt;
    Local $b = FileRead($h)&lt;br /&gt;
    FileClose($h)&lt;br /&gt;
    Local $sH = &amp;quot;HTTP/1.1 200 OK&amp;quot; &amp;amp; @CRLF &amp;amp; _&lt;br /&gt;
        &amp;quot;Content-Type: &amp;quot; &amp;amp; $sCT &amp;amp; @CRLF &amp;amp; _&lt;br /&gt;
        &amp;quot;Content-Length: &amp;quot; &amp;amp; BinaryLen($b) &amp;amp; @CRLF &amp;amp; _&lt;br /&gt;
        &amp;quot;Connection: close&amp;quot; &amp;amp; @CRLF &amp;amp; @CRLF&lt;br /&gt;
    TCPSend($iSock, $sH)&lt;br /&gt;
    TCPSend($iSock, $b)&lt;br /&gt;
EndFunc&lt;br /&gt;
&lt;br /&gt;
Func _SendJson($iSock, $sJson)&lt;br /&gt;
    Local $b = StringToBinary($sJson, 4)&lt;br /&gt;
    Local $sH = &amp;quot;HTTP/1.1 200 OK&amp;quot; &amp;amp; @CRLF &amp;amp; _&lt;br /&gt;
        &amp;quot;Content-Type: application/json; charset=utf-8&amp;quot; &amp;amp; @CRLF &amp;amp; _&lt;br /&gt;
        &amp;quot;Content-Length: &amp;quot; &amp;amp; BinaryLen($b) &amp;amp; @CRLF &amp;amp; _&lt;br /&gt;
        &amp;quot;Connection: close&amp;quot; &amp;amp; @CRLF &amp;amp; @CRLF&lt;br /&gt;
    TCPSend($iSock, $sH)&lt;br /&gt;
    TCPSend($iSock, $b)&lt;br /&gt;
EndFunc&lt;br /&gt;
&lt;br /&gt;
Func _Send404($iSock)&lt;br /&gt;
    TCPSend($iSock, &amp;quot;HTTP/1.1 404 Not Found&amp;quot; &amp;amp; @CRLF &amp;amp; &amp;quot;Content-Length: 0&amp;quot; &amp;amp; @CRLF &amp;amp; @CRLF)&lt;br /&gt;
EndFunc&lt;br /&gt;
&lt;br /&gt;
Func _JsonStr($s)&lt;br /&gt;
    $s = StringReplace($s, &amp;quot;\&amp;quot;, &amp;quot;\\&amp;quot;)&lt;br /&gt;
    $s = StringReplace($s, &amp;#039;&amp;quot;&amp;#039;, &amp;#039;\&amp;quot;&amp;#039;)&lt;br /&gt;
    $s = StringReplace($s, @CR, &amp;quot;\r&amp;quot;)&lt;br /&gt;
    $s = StringReplace($s, @LF, &amp;quot;\n&amp;quot;)&lt;br /&gt;
    $s = StringReplace($s, @TAB, &amp;quot;\t&amp;quot;)&lt;br /&gt;
    Return &amp;#039;&amp;quot;&amp;#039; &amp;amp; $s &amp;amp; &amp;#039;&amp;quot;&amp;#039;&lt;br /&gt;
EndFunc&lt;br /&gt;
&lt;br /&gt;
Func _JsonUnesc($s)&lt;br /&gt;
    $s = StringReplace($s, &amp;quot;\n&amp;quot;, @LF)&lt;br /&gt;
    $s = StringReplace($s, &amp;quot;\r&amp;quot;, @CR)&lt;br /&gt;
    $s = StringReplace($s, &amp;quot;\t&amp;quot;, @TAB)&lt;br /&gt;
    $s = StringReplace($s, &amp;#039;\&amp;quot;&amp;#039;, &amp;#039;&amp;quot;&amp;#039;)&lt;br /&gt;
    $s = StringReplace($s, &amp;quot;\\&amp;quot;, &amp;quot;\&amp;quot;)&lt;br /&gt;
    Return $s&lt;br /&gt;
EndFunc&lt;br /&gt;
&lt;br /&gt;
Func OnExit()&lt;br /&gt;
    _SQLite_Close($g_hDB)&lt;br /&gt;
    _SQLite_Shutdown()&lt;br /&gt;
    TCPShutdown()&lt;br /&gt;
EndFunc&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
web/index.html&lt;br /&gt;
&amp;lt;source lang=html&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;ko&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
&amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;title&amp;gt;웹 시트 (AutoIt + SQLite)&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;https://cdn.jsdelivr.net/npm/jspreadsheet-ce@4/dist/jspreadsheet.css&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;https://cdn.jsdelivr.net/npm/jsuites@4/dist/jsuites.css&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
  body { font-family:&amp;#039;맑은 고딕&amp;#039;,sans-serif; padding:12px; background:#fafafa; }&lt;br /&gt;
  .bar { margin-bottom:10px; display:flex; align-items:center; gap:8px; }&lt;br /&gt;
  button { padding:7px 14px; border:1px solid #ccc; background:#fff;&lt;br /&gt;
           border-radius:4px; cursor:pointer; }&lt;br /&gt;
  button:hover { background:#f0f0f0; }&lt;br /&gt;
  #status { margin-left:10px; color:#555; font-size:13px; }&lt;br /&gt;
  h2 { margin:0 0 12px; font-size:18px; }&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&amp;lt;h2&amp;gt;📊 웹 스프레드시트&amp;lt;/h2&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;bar&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;button onclick=&amp;quot;saveAll()&amp;quot;&amp;gt;💾 저장&amp;lt;/button&amp;gt;&lt;br /&gt;
  &amp;lt;button onclick=&amp;quot;loadAll()&amp;quot;&amp;gt;🔄 불러오기&amp;lt;/button&amp;gt;&lt;br /&gt;
  &amp;lt;button onclick=&amp;quot;clearAll()&amp;quot;&amp;gt;🗑 전체삭제&amp;lt;/button&amp;gt;&lt;br /&gt;
  &amp;lt;button onclick=&amp;quot;exportCsv()&amp;quot;&amp;gt;📤 CSV&amp;lt;/button&amp;gt;&lt;br /&gt;
  &amp;lt;span id=&amp;quot;status&amp;quot;&amp;gt;준비&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;sheet&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script src=&amp;quot;https://cdn.jsdelivr.net/npm/jsuites@4/dist/jsuites.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;https://cdn.jsdelivr.net/npm/jspreadsheet-ce@4/dist/jspreadsheet.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
const COLS = 10, ROWS = 50;&lt;br /&gt;
let sheet;&lt;br /&gt;
const setStatus = s =&amp;gt; document.getElementById(&amp;#039;status&amp;#039;).textContent = s;&lt;br /&gt;
const emptyData = () =&amp;gt; Array.from({length:ROWS}, () =&amp;gt; Array(COLS).fill(&amp;#039;&amp;#039;));&lt;br /&gt;
&lt;br /&gt;
function initSheet(data){&lt;br /&gt;
  const el = document.getElementById(&amp;#039;sheet&amp;#039;);&lt;br /&gt;
  el.innerHTML = &amp;#039;&amp;#039;;&lt;br /&gt;
  sheet = jspreadsheet(el, {&lt;br /&gt;
    data,&lt;br /&gt;
    columns: Array.from({length:COLS}, (_,i) =&amp;gt; ({&lt;br /&gt;
      title: String.fromCharCode(65+i), width: 110&lt;br /&gt;
    })),&lt;br /&gt;
    allowInsertRow: true,&lt;br /&gt;
    minDimensions: [COLS, ROWS],&lt;br /&gt;
    onchange: () =&amp;gt; setStatus(&amp;#039;수정됨 (저장 필요)&amp;#039;)&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
async function loadAll(){&lt;br /&gt;
  setStatus(&amp;#039;불러오는 중...&amp;#039;);&lt;br /&gt;
  const data = emptyData();&lt;br /&gt;
  const r = await fetch(&amp;#039;/api/load&amp;#039;);&lt;br /&gt;
  const j = await r.json();&lt;br /&gt;
  for (const c of j.cells){&lt;br /&gt;
    while (data.length &amp;lt;= c.r) data.push(Array(COLS).fill(&amp;#039;&amp;#039;));&lt;br /&gt;
    while (data[c.r].length &amp;lt;= c.c) data[c.r].push(&amp;#039;&amp;#039;);&lt;br /&gt;
    data[c.r][c.c] = c.v;&lt;br /&gt;
  }&lt;br /&gt;
  initSheet(data);&lt;br /&gt;
  setStatus(`불러옴 (${j.cells.length}셀)`);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
async function saveAll(){&lt;br /&gt;
  setStatus(&amp;#039;저장 중...&amp;#039;);&lt;br /&gt;
  const data = sheet.getData();&lt;br /&gt;
  const cells = [];&lt;br /&gt;
  for (let r=0; r&amp;lt;data.length; r++)&lt;br /&gt;
    for (let c=0; c&amp;lt;data[r].length; c++){&lt;br /&gt;
      const v = data[r][c];&lt;br /&gt;
      if (v !== &amp;#039;&amp;#039; &amp;amp;&amp;amp; v != null) cells.push({r, c, v: String(v)});&lt;br /&gt;
    }&lt;br /&gt;
  const res = await fetch(&amp;#039;/api/save&amp;#039;, {&lt;br /&gt;
    method:&amp;#039;POST&amp;#039;,&lt;br /&gt;
    headers:{&amp;#039;Content-Type&amp;#039;:&amp;#039;application/json&amp;#039;},&lt;br /&gt;
    body: JSON.stringify({cells})&lt;br /&gt;
  });&lt;br /&gt;
  const j = await res.json();&lt;br /&gt;
  setStatus(j.ok ? `저장 완료 (${cells.length}셀)` : &amp;#039;저장 실패&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
async function clearAll(){&lt;br /&gt;
  if (!confirm(&amp;#039;전체 데이터를 삭제할까요?&amp;#039;)) return;&lt;br /&gt;
  await fetch(&amp;#039;/api/clear&amp;#039;, {method:&amp;#039;POST&amp;#039;});&lt;br /&gt;
  initSheet(emptyData());&lt;br /&gt;
  setStatus(&amp;#039;삭제됨&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function exportCsv(){&lt;br /&gt;
  const data = sheet.getData();&lt;br /&gt;
  const csv = data.map(r =&amp;gt; r.map(v =&amp;gt;&lt;br /&gt;
    `&amp;quot;${String(v ?? &amp;#039;&amp;#039;).replace(/&amp;quot;/g,&amp;#039;&amp;quot;&amp;quot;&amp;#039;)}&amp;quot;`).join(&amp;#039;,&amp;#039;)).join(&amp;#039;\n&amp;#039;);&lt;br /&gt;
  const blob = new Blob([&amp;#039;\uFEFF&amp;#039;+csv], {type:&amp;#039;text/csv;charset=utf-8&amp;#039;});&lt;br /&gt;
  const a = document.createElement(&amp;#039;a&amp;#039;);&lt;br /&gt;
  a.href = URL.createObjectURL(blob);&lt;br /&gt;
  a.download = &amp;#039;sheet.csv&amp;#039;;&lt;br /&gt;
  a.click();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
loadAll();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
실행 방법&lt;br /&gt;
&lt;br /&gt;
	1.	위 구조대로 폴더 생성, 두 파일 저장&lt;br /&gt;
	2.	sqlite3.dll을 main.au3 옆에 두기 (AutoIt SQLite UDF 폴더에 포함)&lt;br /&gt;
	3.	main.au3 더블클릭 → 브라우저 자동 오픈&lt;br /&gt;
	4.	셀에 입력 후 💾 저장 → SQLite에 즉시 저장됨&lt;br /&gt;
&lt;br /&gt;
확장 포인트&lt;br /&gt;
&lt;br /&gt;
	•	시트 여러 개: sheet_data에 sheet_name 컬럼 추가 + 상단 탭 UI&lt;br /&gt;
	•	셀 서식: value 외에 style_json 컬럼 추가, jspreadsheet getStyle()/setStyle() 활용&lt;br /&gt;
	•	수식: jspreadsheet-ce가 기본 SUM/AVG 등 지원 — 자동 동작함&lt;br /&gt;
	•	오라클 연동: /api/oracle/query 엔드포인트 추가해서 AutoIt에서 sqlplus.exe 호출 결과를 그리드에 표시&lt;br /&gt;
&lt;br /&gt;
CSV import나 다중 시트, 수식 자동계산 결과 저장 등 원하시는 방향 있으면 이어서 만들어 드릴게요.&lt;/div&gt;</summary>
		<author><name>Devcafe</name></author>
	</entry>
</feed>