@@ -19,8 +19,6 @@ if (!document.querySelector('script[src*="feather"]')) {
1919 document . head . appendChild ( script ) ;
2020}
2121
22- // ⛔ Script error 방지를 위해 applyResizeObserverFix 함수 정의 전체를 제거했습니다.
23-
2422const IDE = ( ) => {
2523 // 🆕 더미 파일 데이터 (mockData 기반)
2624 const [ dummyFiles ] = useState ( ( ) => [ ...codeExampleMocks , ...jsonExampleMocks ] ) ;
@@ -210,7 +208,7 @@ const IDE = () => {
210208 }
211209 } , [ toast ] ) ;
212210
213- // 🔑 개선된 파일 선택 핸들러
211+ // 🔑 개선된 파일 선택 핸들러 (파일 상세 보기 및 조회)
214212 const handleFileSelect = async ( identifier , isServerFile = false ) => {
215213 if ( ! isSaved ) {
216214 const shouldSave = window . confirm ( '변경 사항을 저장하시겠습니까?' ) ;
@@ -270,6 +268,71 @@ const IDE = () => {
270268 }
271269 } ;
272270
271+ // 🔑 파일 삭제 핸들러 (새로 추가된 기능)
272+ const handleDeleteFile = async ( fileUUID , fileName ) => {
273+ if ( ! isLoggedIn ) {
274+ toast ( "로그인 후 이용 가능한 기능입니다." , 'toast-error' ) ;
275+ return ;
276+ }
277+
278+ if ( ! window . confirm ( `정말로 서버 파일 "${ fileName } "을 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.` ) ) {
279+ return ;
280+ }
281+
282+ const token = localStorage . getItem ( 'token' ) ;
283+ if ( ! token ) {
284+ toast ( "인증 토큰이 없습니다. 다시 로그인해 주세요." , 'toast-error' ) ;
285+ return ;
286+ }
287+
288+ try {
289+ // API 호출: 파일 삭제
290+ const response = await fetch ( `${ config . API_BASE_URL } /api/file/${ fileUUID } ` , {
291+ method : 'DELETE' ,
292+ headers : {
293+ 'Authorization' : `Bearer ${ token } ` ,
294+ } ,
295+ } ) ;
296+
297+ if ( ! response . ok ) {
298+ let errorMsg = `파일 삭제 실패: ${ response . statusText } ` ;
299+ if ( response . status === 404 ) errorMsg = "삭제할 파일을 서버에서 찾을 수 없습니다." ;
300+ throw new Error ( errorMsg ) ;
301+ }
302+
303+ // 1. 로컬 상태 업데이트: savedFiles에서 제거
304+ setSavedFiles ( prev => prev . filter ( f => f . fileUUID !== fileUUID ) ) ;
305+
306+ // 2. 현재 활성화 파일 상태 확인 및 리셋
307+ if ( activeFileUUID === fileUUID ) {
308+ const defaultLang = supportedLanguages . find ( lang => lang . id === selectedLanguage ) || supportedLanguages [ 0 ] ;
309+ const newDefaultFile = {
310+ name : "untitled.py" ,
311+ code : defaultLang . template ,
312+ type : 'code' ,
313+ fileUUID : null ,
314+ isServerFile : false
315+ } ;
316+
317+ // 삭제된 파일이 현재 편집 중이었습니다. 새 기본 파일로 상태 리셋.
318+ setCode ( newDefaultFile . code ) ;
319+ setFileName ( newDefaultFile . name ) ;
320+ setActiveFileUUID ( null ) ;
321+ setIsSaved ( true ) ;
322+ setCurrentFileType ( newDefaultFile . type ) ;
323+ setSelectedLanguage ( getLanguageFromFileName ( newDefaultFile . name ) ) ;
324+ toast ( `삭제된 파일이 현재 편집 중이었습니다. 기본 파일로 돌아갑니다.` , 'toast-warning' ) ;
325+ }
326+
327+ toast ( `파일 "${ fileName } "이(가) 성공적으로 삭제되었습니다.` ) ;
328+
329+ } catch ( error ) {
330+ console . error ( '❌ 파일 삭제 중 오류:' , error ) ;
331+ toast ( `파일 삭제 실패: ${ error . message } ` , 'toast-error' ) ;
332+ }
333+ } ;
334+
335+
273336 // 🆕 더미 파일 선택 핸들러 (원본 유지)
274337 const handleDummyFileSelect = ( file ) => {
275338 if ( ! isSaved ) {
@@ -770,7 +833,6 @@ const IDE = () => {
770833
771834 // 3. 🐛 에디터 레이아웃 관련 useEffect (최종 안정화)
772835 useEffect ( ( ) => {
773- // applyResizeObserverFix() 제거했으므로, 브라우저 resize 이벤트에만 의존합니다.
774836 const updateAllEditorLayouts = ( ) => {
775837 if ( editorRef . current ) {
776838 window . requestAnimationFrame ( ( ) => {
@@ -788,7 +850,6 @@ const IDE = () => {
788850
789851 // 4. 🐛 사이드바 접힘/펼침 상태 변경 시 에디터 레이아웃 업데이트 (간소화 유지)
790852 useEffect ( ( ) => {
791- // 애니메이션 완료 후 한 번만 레이아웃을 최종 업데이트
792853 const timeoutId = setTimeout ( ( ) => {
793854 if ( editorRef . current ) {
794855 try {
@@ -803,6 +864,13 @@ const IDE = () => {
803864 return ( ) => clearTimeout ( timeoutId ) ;
804865 } , [ isLeftPanelCollapsed ] ) ;
805866
867+ // 5. 🎨 사이드바 파일 목록 업데이트 시 Feather Icons 새로고침
868+ useEffect ( ( ) => {
869+ if ( window . feather ) {
870+ window . feather . replace ( ) ;
871+ }
872+ } , [ savedFiles , sidebarSections ] ) ; // 파일 목록이나 섹션 토글 시 업데이트
873+
806874 // 🎨 다크 모드 토글 시 에디터 테마 변경 (원본 유지)
807875 useEffect ( ( ) => {
808876 const observer = new MutationObserver ( ( mutations ) => {
@@ -827,7 +895,7 @@ const IDE = () => {
827895 return ( ) => { observer . disconnect ( ) ; } ;
828896 } , [ isDarkMode ] ) ;
829897
830- // 🆕 ModernSidebar 렌더링 함수 (원본 유지 )
898+ // 🆕 ModernSidebar 렌더링 함수 (삭제 버튼 추가 )
831899 const renderModernSidebar = ( ) => {
832900 const myServerFiles = savedFiles . filter ( f => f . isServerFile && f . fileUUID ) ;
833901 const myLocalFiles = savedFiles . filter ( f => ! f . isServerFile && ! f . fileUUID ) ;
@@ -861,7 +929,8 @@ const IDE = () => {
861929 { sidebarSections . myFiles ? '▼' : '▶' }
862930 </ span >
863931 < i data-feather = "folder" className = "section-icon" > </ i >
864- < span className = "section-title" > 내 파일 ({ myServerFiles . length + myLocalFiles . length } 개)</ span >
932+ { /* 💡 파일 개수 표시 제거 */ }
933+ < span className = "section-title" > 내 파일</ span >
865934 </ button >
866935
867936 { sidebarSections . myFiles && (
@@ -875,7 +944,19 @@ const IDE = () => {
875944 >
876945 { getFileIcon ( file . name ) }
877946 < span className = "file-name" > { file . name } </ span >
878- < span className = "file-badge server-badge" title = "서버 저장 파일" > S</ span >
947+ { /* ⛔ 뱃지 제거됨 */ }
948+
949+ { /* 🔑 파일 삭제 버튼 추가 */ }
950+ < button
951+ className = "delete-file-button"
952+ onClick = { ( e ) => {
953+ e . stopPropagation ( ) ; // 파일 선택 이벤트 방지
954+ handleDeleteFile ( file . fileUUID , file . name ) ;
955+ } }
956+ title = "파일 삭제"
957+ >
958+ < i data-feather = "trash-2" > </ i >
959+ </ button >
879960 </ div >
880961 ) ) }
881962
@@ -888,7 +969,7 @@ const IDE = () => {
888969 >
889970 { getFileIcon ( file . name ) }
890971 < span className = "file-name" > { file . name } </ span >
891- < span className = "file-badge local-badge" title = "로컬 임시 파일" > L </ span >
972+ { /* ⛔ 뱃지 제거됨 */ }
892973 </ div >
893974 ) ) }
894975 </ div >
0 commit comments