Option Explicit

Sub 모든시트_개별파일저장()

    ' ==========================================================
    ' [목적]
    ' 현재 파일의 모든 시트를 각각 별도의 Excel 파일로 저장합니다.
    '
    ' [저장 위치]
    ' 현재 파일이 저장된 폴더 하위에 "거래처별자료" 폴더를 만들고 저장합니다.
    '
    ' [파일명 형식]
    ' {시트명}_{yyyymmdd}.xlsx
    '
    ' [실행 로그]
    ' 직접 실행 창에 Debug.Print로 출력합니다.
    ' VBA 편집기에서 Ctrl + G를 누르면 직접 실행 창을 볼 수 있습니다.
    ' ==========================================================

    Dim wbSource As Workbook          ' 원본 통합문서
    Dim wbNew As Workbook             ' 새로 생성되는 개별 저장용 통합문서
    Dim ws As Worksheet               ' 반복 대상 시트
    Dim sourcePath As String          ' 현재 파일이 저장된 폴더 경로
    Dim outputFolder As String        ' 결과 파일 저장 폴더 경로
    Dim saveFileName As String        ' 저장할 파일명
    Dim saveFullPath As String        ' 저장할 전체 경로
    Dim todayText As String           ' yyyymmdd 형식의 날짜 문자열
    Dim safeSheetName As String       ' 파일명으로 사용 가능하도록 정리한 시트명
    Dim successCount As Long          ' 저장 성공 개수
    Dim failCount As Long             ' 저장 실패 개수

    On Error GoTo ErrHandler

    ' ----------------------------------------------------------
    ' (1) 작업 시작 로그 출력
    ' ----------------------------------------------------------
    Debug.Print String(70, "=")
    Debug.Print "[시작] 모든 시트 개별 파일 저장 작업"
    Debug.Print "[시간] " & Format(Now, "yyyy-mm-dd hh:nn:ss")
    Debug.Print String(70, "=")

    ' ----------------------------------------------------------
    ' (2) 원본 통합문서 지정
    '     ThisWorkbook은 현재 매크로 코드가 들어 있는 파일을 의미합니다.
    '     ActiveWorkbook을 사용할 경우 사용자가 다른 파일을 클릭한 상태에서
    '     실행하면 엉뚱한 파일을 기준으로 작업할 수 있으므로 ThisWorkbook을 사용합니다.
    ' ----------------------------------------------------------
    Set wbSource = ThisWorkbook

    Debug.Print "[1단계] 원본 파일명: " & wbSource.Name

    ' ----------------------------------------------------------
    ' (3) 현재 파일이 저장되어 있는지 확인
    '     파일이 아직 한 번도 저장되지 않았다면 ThisWorkbook.Path가 빈 값입니다.
    '     이 경우 결과 파일을 저장할 기준 폴더를 알 수 없으므로 작업을 중단합니다.
    ' ----------------------------------------------------------
    sourcePath = wbSource.Path

    If Len(sourcePath) = 0 Then
        Debug.Print "[오류] 현재 파일이 아직 저장되지 않았습니다."
        MsgBox "현재 파일이 아직 저장되지 않았습니다." & vbCrLf & _
               "먼저 이 파일을 저장한 뒤 다시 실행해주세요.", vbExclamation
        Exit Sub
    End If

    Debug.Print "[2단계] 원본 파일 위치: " & sourcePath

    ' ----------------------------------------------------------
    ' (4) 결과 저장 폴더 경로 만들기
    '     현재 파일이 있는 폴더 아래에 "거래처별자료" 폴더를 만듭니다.
    ' ----------------------------------------------------------
    outputFolder = sourcePath & Application.PathSeparator & "거래처별자료"

    Debug.Print "[3단계] 결과 저장 폴더 경로: " & outputFolder

    ' ----------------------------------------------------------
    ' (5) 결과 저장 폴더가 없으면 생성
    '     Dir 함수로 폴더 존재 여부를 확인합니다.
    '     폴더가 이미 있으면 그대로 사용합니다.
    ' ----------------------------------------------------------
    If Dir(outputFolder, vbDirectory) = "" Then
        MkDir outputFolder
        Debug.Print "[4단계] 결과 저장 폴더 생성 완료"
    Else
        Debug.Print "[4단계] 결과 저장 폴더가 이미 존재함"
    End If

    ' ----------------------------------------------------------
    ' (6) 오늘 날짜를 yyyymmdd 형식으로 준비
    '     파일명 뒤에 붙일 날짜입니다.
    ' ----------------------------------------------------------
    todayText = Format(Date, "yyyymmdd")

    Debug.Print "[5단계] 파일명 날짜 문자열: " & todayText

    ' ----------------------------------------------------------
    ' (7) 원본 파일의 모든 시트를 하나씩 반복
    '     각 시트를 새 통합문서로 복사한 뒤 개별 파일로 저장합니다.
    ' ----------------------------------------------------------
    Debug.Print "[6단계] 전체 시트 수: " & wbSource.Worksheets.Count

    For Each ws In wbSource.Worksheets

        On Error GoTo SheetErrHandler

        Debug.Print String(70, "-")
        Debug.Print "[시트 처리 시작] " & ws.Name

        ' ------------------------------------------------------
        ' (7-1) 시트명을 파일명으로 사용할 수 있도록 정리
        '       Windows 파일명에는 사용할 수 없는 문자가 있습니다.
        '       예: \ / : * ? " < > |
        '       이러한 문자가 포함되어 있으면 저장 오류가 발생하므로
        '       안전한 문자 "_"로 바꿉니다.
        ' ------------------------------------------------------
        safeSheetName = GetSafeFileName(ws.Name)

        Debug.Print "[7-1단계] 파일명용 시트명: " & safeSheetName

        ' ------------------------------------------------------
        ' (7-2) 저장할 파일명과 전체 경로 생성
        '       파일명 형식: {시트명}_{yyyymmdd}.xlsx
        ' ------------------------------------------------------
        saveFileName = safeSheetName & "_" & todayText & ".xlsx"
        saveFullPath = outputFolder & Application.PathSeparator & saveFileName

        Debug.Print "[7-2단계] 저장 파일명: " & saveFileName
        Debug.Print "[7-2단계] 저장 전체 경로: " & saveFullPath

        ' ------------------------------------------------------
        ' (7-3) 기존에 같은 이름의 파일이 있으면 삭제
        '       SaveAs 실행 시 같은 파일이 있으면 경고창이 뜰 수 있으므로
        '       DisplayAlerts를 끄고 기존 파일을 삭제합니다.
        '
        '       기존 파일을 덮어쓰지 않고 보존하고 싶다면
        '       아래 Kill saveFullPath 부분을 주석 처리해야 합니다.
        ' ------------------------------------------------------
        If Dir(saveFullPath) <> "" Then
            Debug.Print "[7-3단계] 같은 이름의 파일이 이미 존재하여 삭제 후 다시 저장합니다."
            Kill saveFullPath
        Else
            Debug.Print "[7-3단계] 같은 이름의 기존 파일 없음"
        End If

        ' ------------------------------------------------------
        ' (7-4) 현재 시트를 새 통합문서로 복사
        '       ws.Copy를 사용하면 현재 시트 하나만 들어 있는 새 통합문서가 생성됩니다.
        '       복사 직후 ActiveWorkbook이 새 통합문서가 됩니다.
        ' ------------------------------------------------------
        ws.Copy

        Set wbNew = ActiveWorkbook

        Debug.Print "[7-4단계] 새 통합문서 생성 완료"

        ' ------------------------------------------------------
        ' (7-5) 새 통합문서를 xlsx 형식으로 저장
        '       FileFormat:=xlOpenXMLWorkbook은 .xlsx 형식입니다.
        '       매크로가 없는 일반 Excel 통합문서로 저장됩니다.
        ' ------------------------------------------------------
        Application.DisplayAlerts = False

        wbNew.SaveAs Filename:=saveFullPath, _
                     FileFormat:=xlOpenXMLWorkbook

        Application.DisplayAlerts = True

        Debug.Print "[7-5단계] 파일 저장 완료"

        ' ------------------------------------------------------
        ' (7-6) 새 통합문서 닫기
        '       이미 저장했으므로 SaveChanges:=False로 닫습니다.
        ' ------------------------------------------------------
        wbNew.Close SaveChanges:=False
        Set wbNew = Nothing

        successCount = successCount + 1

        Debug.Print "[시트 처리 완료] " & ws.Name

        ' ------------------------------------------------------
        ' (7-7) 다음 시트 처리를 위해 일반 오류 처리 위치 복원
        ' ------------------------------------------------------
        On Error GoTo ErrHandler

    Next ws

    ' ----------------------------------------------------------
    ' (8) 모든 작업 완료 로그 출력
    ' ----------------------------------------------------------
    Debug.Print String(70, "=")
    Debug.Print "[완료] 모든 시트 개별 파일 저장 작업 종료"
    Debug.Print "[성공 개수] " & successCount
    Debug.Print "[실패 개수] " & failCount
    Debug.Print "[저장 폴더] " & outputFolder
    Debug.Print "[종료 시간] " & Format(Now, "yyyy-mm-dd hh:nn:ss")
    Debug.Print String(70, "=")

    MsgBox "모든 시트 저장 작업이 완료되었습니다." & vbCrLf & vbCrLf & _
           "성공: " & successCount & "개" & vbCrLf & _
           "실패: " & failCount & "개" & vbCrLf & vbCrLf & _
           "저장 위치:" & vbCrLf & outputFolder, vbInformation

    Exit Sub

' --------------------------------------------------------------
' (9) 특정 시트 처리 중 오류가 발생했을 때 실행되는 오류 처리 구간
'     한 시트에서 오류가 발생해도 전체 작업이 중단되지 않고
'     다음 시트로 넘어가도록 구성했습니다.
' --------------------------------------------------------------
SheetErrHandler:

    failCount = failCount + 1

    Debug.Print "[오류] 시트 처리 중 오류 발생"
    Debug.Print "[오류 시트] " & ws.Name
    Debug.Print "[오류 번호] " & Err.Number
    Debug.Print "[오류 내용] " & Err.Description

    ' 오류가 발생한 새 통합문서가 열려 있을 수 있으므로 닫기 시도
    On Error Resume Next

    If Not wbNew Is Nothing Then
        Application.DisplayAlerts = False
        wbNew.Close SaveChanges:=False
        Application.DisplayAlerts = True
        Set wbNew = Nothing
    End If

    On Error GoTo ErrHandler

    Debug.Print "[처리 계속] 다음 시트로 이동합니다."

    Resume Next

' --------------------------------------------------------------
' (10) 전체 작업 수준에서 예상하지 못한 오류가 발생했을 때 실행되는 구간
' --------------------------------------------------------------
ErrHandler:

    Application.DisplayAlerts = True

    Debug.Print String(70, "!")
    Debug.Print "[치명적 오류] 작업이 중단되었습니다."
    Debug.Print "[오류 번호] " & Err.Number
    Debug.Print "[오류 내용] " & Err.Description
    Debug.Print "[시간] " & Format(Now, "yyyy-mm-dd hh:nn:ss")
    Debug.Print String(70, "!")

    MsgBox "작업 중 오류가 발생했습니다." & vbCrLf & vbCrLf & _
           "오류 번호: " & Err.Number & vbCrLf & _
           "오류 내용: " & Err.Description & vbCrLf & vbCrLf & _
           "VBA 직접 실행 창(Ctrl + G)의 실행 로그를 확인해주세요.", vbCritical

End Sub


Function GetSafeFileName(ByVal originalName As String) As String

    ' ==========================================================
    ' [목적]
    ' 시트명을 Windows 파일명으로 사용할 수 있도록 안전하게 변환합니다.
    '
    ' [처리 내용]
    ' 파일명에 사용할 수 없는 문자 \ / : * ? " < > | 를 "_"로 변경합니다.
    ' ==========================================================

    Dim result As String

    ' ----------------------------------------------------------
    ' (1) 원본 문자열을 결과 변수에 저장
    ' ----------------------------------------------------------
    result = originalName

    ' ----------------------------------------------------------
    ' (2) Windows 파일명에 사용할 수 없는 문자를 "_"로 변경
    ' ----------------------------------------------------------
    result = Replace(result, "\", "_")
    result = Replace(result, "/", "_")
    result = Replace(result, ":", "_")
    result = Replace(result, "*", "_")
    result = Replace(result, "?", "_")
    result = Replace(result, """", "_")
    result = Replace(result, "<", "_")
    result = Replace(result, ">", "_")
    result = Replace(result, "|", "_")

    ' ----------------------------------------------------------
    ' (3) 앞뒤 공백 제거
    ' ----------------------------------------------------------
    result = Trim(result)

    ' ----------------------------------------------------------
    ' (4) 파일명이 비어 있는 경우 기본 이름 지정
    '     일반적으로 시트명이 비어 있을 수는 없지만,
    '     안전성을 위해 예외 처리를 넣습니다.
    ' ----------------------------------------------------------
    If Len(result) = 0 Then
        result = "Sheet"
    End If

    ' ----------------------------------------------------------
    ' (5) 결과 반환
    ' ----------------------------------------------------------
    GetSafeFileName = result

End Function