Visual Basic에서의 Base64 디코딩: 완전한 가이드
장면을 상상해 보세요. Visual Basic 프로젝트에 어떤 문자열이 떨어집니다. 섞어 둔 글자와 숫자 묶음처럼 보이고, 가끔 더하기 기호, 슬래시, 등호 기호가 섞여 있으며, 보낸 사람은 그것이 한때 지극히 평범한 문장, JPEG, 설정 덩어리였다고 맹세합니다. 그 문자열이 Base64이며, 이 페이지는 그것을 원래 모습으로 되돌리는 현장 가이드입니다. 미리 좋은 소식부터: Visual Basic은 아주 초기 .NET Framework 시대부터 일급 Base64 디코더를 갖고 있고, 그것은 런타임 안에 함께 들어 있으며, 쓰려면 패키지 하나 설치할 필요도 없습니다.
이 사이트의 홈 페이지가 포맷을 깊이 있게 전부 설명해 두므로, 간단히 복기만 하고 갑니다. Base64는 3바이트를 64기호 알파벳에서 딴 4문자로 쓰고, 꼬리에 매달린 = 한두 개가 진짜 데이터가 끝난 곳을 표시합니다. 그래서 인코딩된 텍스트는 원본보다 살짝 부푼 편입니다: 입력 바이트 3개마다 문자 4개, 대략 3분의 1 더 많죠. 디코딩은 그 거래를 그저 거꾸로 실행할 뿐입니다. 문제의 모양을 머리에 두고, 자, 몇 가지 봉투를 열어 봅시다.
디코더 패밀리: 하나의 런타임, 네 개의 시대
디코딩에 필요한 모든 것은 .NET 런타임 안에 있습니다. 지난 20년 동안 그것은 네 물결에 걸쳐 자라 왔고, 오래된 물결도 예전과 똑같이 정확히 작동하므로, 현역에서는 그 전부를 만나게 됩니다:
| API | 사용 가능 | 용도 |
|---|---|---|
System.Convert.FromBase64String |
.NET Framework 1.1 (2003) | 클래식 그 자체. 문자열 하나 들어가고, 새로 만든 Byte() 배열 하나 나옵니다. 잘못된 입력에는 예외를 던집니다. |
System.Convert.FromBase64CharArray |
.NET Framework 1.1 (2003) | 동일한 디코딩인데, 이미 당신 것이 된 문자 배열의 한 조각에서 읽습니다. |
System.Convert.TryFromBase64String, TryFromBase64Chars |
.NET Core 2.1 (2018) | 예외 대신 부울 값, 당신이 제공한 버퍼에 씁니다. 신뢰할 수 없는 입력을 위한 다정한 경호원입니다. |
System.Buffers.Text.Base64 |
.NET Core 2.1 (2018) | 로우레벨 스판 기반 디코딩: 예외 대신 상태 코드, 인플레이스 압축 해제, 그리고 IsValid 사전 검사. |
System.Buffers.Text.Base64Url |
.NET 9 (2024) | URL 안전 알파벳(같은 64문자 체계에 -와 _가 +와 /의 자리를 차지함), 패딩은 선택 사항. 오래된 런타임에서는 Microsoft.Bcl.Memory NuGet 패키지로 들어옵니다. |
FromBase64Transform + CryptoStream |
.NET Framework 1.1 (2003) | 스트리밍 디코딩: 파일에서 파일로, 네트워크에서 디스크로, 조각 조각, 페이로드 전체를 메모리에 안고 있지 않습니다. |
더 앞서가기 전에 Visual Basic 정리 하나. VB 프로젝트에서 접두어 없는 이름 Convert는 System.Convert로 해석됩니다. 표준 프로젝트 템플릿이 System 네임스페이스를 대신 가져다 놓기 때문이죠, Visual Basic 런타임 안에서 그 이름을 가리는 것도 없습니다. 그래도 이 글은 대부분 풀 이름인 System.Convert 표기로 씁니다: 아무 비용도 들지 않고, 코드를 읽는 누구에게도 의도를 분명하게 보여주니까요.
버전 이야기는 이렇습니다: .NET 10이 지금의 장기 지원 릴리스(2025년 11월, 2028년 11월까지 지원)이며, .NET 8과 .NET 9도 2026년 11월까지 지원을 유지하고, .NET 11은 프리뷰 중으로 새 Base64 편의 메서드 한 무더기를 더합니다. 디코딩 API는 그 모든 버전에서 안정적입니다. 유일한 버전 문턱은 Base64Url입니다: .NET 9부터 내장되어 있고, .NET Framework 4.6.2 이상에서는 Microsoft.Bcl.Memory 패키지로 끌어올 수 있습니다. 이 글의 다른 곳에서는 어떤 패키지도 필요하지 않습니다.
아무것도 없는 상태에서 시작한다면, .NET SDK는 Visual Basic을 박스 안에 함께 담고 있으니, 전체 의식은 이 정도입니다:
dotnet new console -lang VB -o EnvelopeOpener
cd EnvelopeOpener
dotnet run
그러면 Program.vb라는 작은 파일이 하나 생기고, 맨 위에는 Imports System이 있으니, 당신은 디코딩할 준비가 됩니다.
원라인러: FromBase64String
Visual Basic에서의 디코딩 생활 90퍼센트는 호출 하나입니다. 문자열을 주면, 안에 담아져 있던 바이트를 정확히 그대로 돌려 줍니다:
Imports System
Imports System.Text
Module EnvelopeOpener
Sub Main()
Dim packed As String = "TWFu"
Dim bytes() As Byte = System.Convert.FromBase64String(packed)
Dim text As String = Encoding.UTF8.GetString(bytes)
Console.WriteLine(text)
' Man
End Sub
End Module
기억에 새길 가치가 있는 세 가지가 있습니다. 첫째, 결과는 텍스트가 아니라 바이트입니다: 디코더는 처음부터 끝까지 바이트를 향해 일하는데, 이것이 정확히 당신이 원하는 모습입니다. 페이로드가 문장일 수도, JPEG일 수도, 인증서일 수도, 해시일 수도 있고, 그 어느 것도 특별 대우받아서는 안 되니까요. 그 바이트를 읽을 수 있는 문자열로 올리는 것은 Encoding 객체를 거치는 또 다른, 의도된 단계이고, 문자 집합 결정은 바로 그 단계에서 이루어집니다(아래에서 더 다룹니다). 둘째, FromBase64String은 디코딩된 길이에 딱 맞는 크기의 새 배열을 할당하므로, 당신은 절대 여유 용량을 들고 다닐 일이 없습니다. 셋째, "TWFu"는 세 바이트의 단어 "Man"으로 디코딩되며, 전혀 예상 밖의 것이 없으므로, 당신이 쓰는 어떤 디코딩 코든 위한 완벽한 스모크 테스트가 됩니다.
용서하는 것과 거절하는 것
여기가 바로 .NET 디코더의 개성이 드러나는 곳이며, 꽤 뚜렷한 개성입니다: 한 가지에는 관대하고, 그 외 모든 것에는 무자비하죠. 관대한 것은 공백입니다. 디코더는 어디에 나타나든 정확히 네 문자를 건너뜁니다: 스페이스(U+0020), 탭(U+0009), 라인 피드(U+000A), 캐리지 리턴(U+000D). 이 정책은 Base64 페이로드가 짧은 줄로 감겨 도착하는 이메일을 의도적으로 배려한 것인데, 덕분에 MIME 포장된 첨부 파일은 전처리 없이 디코딩됩니다. 재미있는 사실 하나: 이 관대함은 스판 기반 System.Buffers.Text.Base64와 URL 안전 Base64Url 클래스를 포함한 내장 패밀리 전체가 공유하므로, 어느 API를 손에 들어도 똑같이 관대한 행동을 얻습니다. 64기호 알파벳 밖의 것, 길이 규칙 위반, 패딩 자리 잘못, 어느 하나라도 있으면 예외를 받습니다. 같은 디코더가 몇 가지 다른 입력을 만나는 모습을 보여 드릴게요:
| 입력 | 결과 |
|---|---|
"TWFu" |
디코딩 결과 Man (3바이트). |
"TWF" + CRLF + "u" |
디코딩 결과 Man. 중간 줄바꿈은 디코더에게 보이지 않습니다. |
"TWFu" + 붙이지 않는 공백 |
FormatException. 건너뛰는 공백은 위에서 나열한 네 문자뿐이며, 붙이지 않는 공백은 그중 하나도 아닙니다. |
"TWE" |
FormatException. 공백을 무시하면, 길이는 4의 배수여야 합니다. |
"TWFu=" |
FormatException. 데이터가 끝난 뒤의 패딩은 허용되지 않습니다. |
"====" |
FormatException. 패딩 문자가 두 개를 넘으면 유효하지 않습니다. |
"" 또는 공백만 있는 것 |
빈 바이트 배열. 조용하고 유효한 성공입니다. |
"TW=u" |
FormatException. 중간에 있는 패딩은 유효하지 않습니다. |
그러니 솔직한 계약은 작고 기억하기 쉽습니다: Nothing 참조는 ArgumentNullException을 던지고, 비어 있거나 공백만 있는 입력은 빈 배열로 디코딩되며, 유효한 입력은 바이트로 디코딩되고, 그 외 모든 유효하지 않은 것은 아주 특정한 예외 하나, FormatException을 던집니다.
바이트에서 텍스트로: 문자 집합 고르기
디코딩된 바이트가 실제로 텍스트라고 결정한 순간부터, 당신은 문자 집합을 이름 붙여야 합니다. 어떻게 읽을지 말할 때까지 바이트는 텍스트가 아니기 때문이죠. Visual Basic 문자열은 내부적으로 UTF-16이지만, 디코더에서 나오는 바이트는 누군가가 만든 것이며, 아마도 다른 방식 아래에서 만들어졌을 테니, 그쪽의 선택에 맞춰야 합니다. 실무 메뉴는 이렇습니다:
Encoding.UTF8: 웹을 건너오거나 API를 통과한 것에 대한 안전한 기본값. 망설여진다면 여기서 시작하세요.Encoding.Unicode: UTF-16 리틀 엔디안, .NET의 네이티브 맛. 교환의 양쪽이 UTF-16을 명시적으로 고른 .NET 프로그램이라면 합리적인 선택입니다.Encoding.ASCII: 7비트만. ASCII가 아닌 바이트는 물음표로 대체되므로, 이것은 조용히 발음 기호를 죽이는 손실 있는 선택입니다.Encoding.Default: .NET Framework에서는 머신의 ANSI 코드 페이지였지만, .NET (Core)에서는 로케일에 관계없이 항상 UTF-8입니다. 주고받는 데이터에는 여전히 피하세요 - 인코딩을 명시적으로 적고, 보통Encoding.UTF8입니다.
Imports System
Imports System.Text
Module CharsetDemo
Sub Main()
' UTF-8 바이트로 저장된 단어 "Café"
Dim bytes() As Byte = System.Convert.FromBase64String("Q2Fmw6k=")
Dim correct As String = Encoding.UTF8.GetString(bytes)
Console.WriteLine(correct)
' Café
End Sub
End Module
그 같은 5바이트를 Encoding.Unicode로 읽으면, 엉터리 짧은 문자열을 얻습니다 - 기이한 CJK 범위 문자 몇 개와 대체 문자 하나가 - 디코더가 바이트를 두 개씩 짝짓기 때문이죠. "Café"의 UTF-8 바이트를 Encoding.ASCII로 읽으면 발음 기호가 ??, 물음표 두 개가 됩니다. UTF-8에서 그 발음 기호는 두 바이트라서요. 이 선택들 중 어느 것도 예외를 던지지 않습니다. 그저 조용히 잘못된 텍스트를 만들어 낼 뿐이죠. 그래서 문자 집합은 물려받는 기본값이 아니라, 의도적으로 내리는 결정입니다.
URL 안전 알파벳: Base64Url
표준 Base64는 +와 /를 쓰고, 이 두 문자는 모두 URL 안에서 각자의 의미를 갖고 있으므로, 표준 알파벳은 쿼리 문자열에 닿는 순간 링크를 부술 수 있습니다. RFC 4648 5절에서 표준화된 해결책은 URL과 파일명 안전 변형입니다: 같은 64문자 체계인데, -가 +의 자리에, _가 /의 자리에 있고, 길이가 암시하므로 꼬리 패딩은 보통 생략됩니다. JWT, API 토큰, 그리고 Base64가 URL 안에 올라가는 모든 곳에서 이 알파벳을 만나게 될 겁니다. .NET 9는 이를 위한 전용 클래스 System.Buffers.Text.Base64Url을 추가했고, 쓰기가 즐거워요:
Imports System.Buffers.Text
Imports System.Text
Module UrlSafeOpener
Sub Main()
' URL 안전 입력, 끝에 패딩 없음
Dim packed As String = "SGVsbG8gd29ybGQ"
Dim bytes() As Byte = Base64Url.DecodeFromChars(packed)
Dim text As String = Encoding.UTF8.GetString(bytes)
Console.WriteLine(text)
' Hello world
End Sub
End Module
알아 둘 가치가 있는 행동 두 가지. Base64Url 인코더는 의도적으로 패딩 없이 출력을 만들지만, 디코더는 패딩이 있는 입력이든 없는 입력이든 모두 받아들입니다. 다른 생태계에서 온 데이터에도 다정하죠. 그리고 Base64Url.IsValid는 디코딩 전에 후보 문자열을 미리 검사하게 해 주는데, 데이터가 바깥 세계에서 왔을 때 유용합니다. 그 클래스가 없는 오래된 런타임에 머물러 있다면, 변환은 문자 두 개 교환과 패딩 보충일 뿐이며, 인코더가 한 일의 정확히 반대입니다:
Imports System
Module CompatOpener
Function FromUrlSafe(ByVal packed As String) As Byte()
Dim standard As String = packed.Replace("-"c, "+"c).Replace("_"c, "/"c)
Select Case standard.Length Mod 4
Case 2
standard &= "=="
Case 3
standard &= "="
End Select
Return System.Convert.FromBase64String(standard)
End Function
End Module
.NET Framework 4.6.2 이상에서는 대신 Microsoft.Bcl.Memory NuGet 패키지를 설치하고 진짜 Base64Url 클래스를 쓸 수 있습니다. 어느 쪽이든 규칙은 간단합니다: 컨텍스트(URL, JWT, API 토큰)에서 알파벳을 알아채고, 그에 맞는 디코더를 고르세요.
파일 열기
Base64의 가장 오래된 용도 중 하나는 텍스트 파일로 바이너리를 밀반입하는 것입니다: 인코딩된 바이트를 담은 .b64나 .txt 파일이죠. Visual Basic에서는 왕복이 파일 호출 두 개와 디코딩 한 번입니다. 텍스트를 읽고, 디코딩하고, 바이트를 씁니다:
Imports System.IO
Module FileOpener
Sub Main()
Dim packed As String = File.ReadAllText("payload.b64")
Dim bytes() As Byte = System.Convert.FromBase64String(packed)
File.WriteAllBytes("payload.bin", bytes)
End Sub
End Module
공백에 대한 관대함이 이 방식을 만족스럽게 견고하게 만들어 줍니다: 페이로드가 한 줄로 길게 쓰였든, 76자마다 줄바꿈되었든, 64자마다 줄바꿈되었든 파일은 신경 쓰지 않습니다. 디코더가 어느 쪽이든 줄바꿈을 건너뛰기 때문이죠. 주머니에 넣어 둘 크기 사실 하나: 텍스트 파일은 숨기고 있는 바이너리보다 약 3분의 1 더 큽니다. 그래서 10메가바이트 파일은 약 13.3메가바이트의 문자로 도착하죠. 대단한 것은 아니지만, "작은" 텍스트 파일이 커 보일 때 기억할 숫자입니다.
이미지와 데이터 URI
데이터 URI 스킴(RFC 2397)은 URL이 자기 콘텐츠를 싣게 합니다: data: 뒤에 미디어 타입, 리터럴 마커 ;base64, 쉼표, 그리고 인코딩된 바이트가 이어집니다. 웹 어디에서든, HTML과 CSS 안에서 본 적 있을 겁니다. 별도 파일을 가리키지 않고 작은 이미지와 폰트를 마크업에 바로 임베드하죠. Visual Basic에서는 하나를 여는 것이 문자열 분할과 디코딩일 뿐입니다. 아래 예제는 데이터 URI에서 PNG를 빼내어 그것을 WPF 이미지로 만듭니다:
Imports System.IO
Imports System.Windows.Media.Imaging
Module DataUriOpener
Function ImageFromDataUri(ByVal dataUri As String) As BitmapImage
Dim comma As Integer = dataUri.IndexOf(","c)
Dim header As String = dataUri.Substring(0, comma)
If Not header.EndsWith(";base64") Then
Throw New FormatException("Not a base64 data URI")
End If
Dim packed As String = dataUri.Substring(comma + 1)
Dim bytes() As Byte = System.Convert.FromBase64String(packed)
Dim image As New BitmapImage()
image.BeginInit()
image.CacheOption = BitmapCacheOption.OnLoad
image.StreamSource = New MemoryStream(bytes)
image.EndInit()
Return image
End Function
End Module
헤더에 대한 방어적 검사에 주목하세요: ;base64 마커가 없는 데이터 URI는 대신 URL 이스케이프된 데이터를 담고 있으며, 그것을 Base64로 디코딩하면 실패하거나 쓰레기가 됩니다. RFC 자체가 데이터 URI는 짧은 값에만 유용하다고 경고하고, HTML에는 자기만의 속성 길이 한계가 있으므로, 이것은 사진 도서관 전체를 한 속성에 싣는 도구가 아니라, 아이콘, 아바타, 섬네일에 맞는 도구로 여길 것입니다.
HTTP, API, Basic 인증
Base64는 웹 API 어디에나 있으며, 가장 흔한 모습 둘은 HTTP Basic 인증 헤더와 바이너리나 미리 인코딩된 데이터를 싣는 JSON 필드입니다. Basic 인증이 가장 단순한 경우입니다: 클라이언트가 Authorization: Basic 뒤에 username:password의 Base64를 보내죠. Visual Basic에서 디코딩하는 것은 접두어 검사와 호출 한 번입니다:
Imports System
Imports System.Text
Module BasicAuthOpener
Function ReadCredentials(ByVal header As String) As String
If Not header.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase) Then
Throw New FormatException("Not a Basic auth header")
End If
Dim packed As String = header.Substring(6)
Dim bytes() As Byte = System.Convert.FromBase64String(packed)
Return Encoding.UTF8.GetString(bytes)
End Function
End Module
이 함수는 username:password를 하나의 문자열로 돌려 주며, 당신은 그것을 콜론에서 나눕니다. 또 다른 일상적인 경우는, 어떤 필드가 이미지나 인증서 같은 미리 인코딩된 덩어리인 JSON 응답입니다. HttpClient와 System.Text.Json를 쓰면(System.Text.Json은 .NET Core 3.0부터 박스에 들어 있었고, HttpClient는 훨씬 그 전부터) 패턴은 곧바로 따라갈 수 있습니다:
Imports System.Net.Http
Imports System.Text.Json
Module ApiOpener
Async Function ReadImageAsync() As Task(Of Byte())
Using client As New HttpClient()
Dim json As String = Await client.GetStringAsync("https://httpbin.org/get?attachment=TWFuIGlzIGhlcmU%3D&name=man.txt")
Dim doc As JsonDocument = JsonDocument.Parse(json)
Dim packed As String = doc.RootElement.GetProperty("args").GetProperty("attachment").GetString()
Return System.Convert.FromBase64String(packed)
End Using
End Function
End Module
집안 정리 노트 두 가지. 그럴 수 있다는 이유만으로 디코딩한 자격 증명을 로그나 UI에 출력하지 마세요. 그리고 평문 HTTP 위 Basic 인증에는 절대 의존하지 마세요. 그러면 당신은 그저 비밀번호를 더 재미있는 알파벳으로 써 놓는 것뿐이니까요.
JWT: 세 개의 조각 읽기
컴팩트 형태의 JSON Web Token은 점으로 나뉜 Base64Url 조각 세 개입니다: 헤더, 페이로드, 서명. 첫 둘은 눈으로(혹은 디코딩 호출 한 번으로) 읽을 수 있는 평문 JSON이고, 세 번째는 디코딩이 아니라 정확한 키로 검사되어야 할 암호학적 서명입니다. 흔한 데모의 샘플 토큰은 이렇습니다: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.W-5gI_FdnMBdpgG4twX96rptvh13gmXQ75kKLRXVaGs. Visual Basic에서 그 페이로드를 읽는 것은 점을 기준으로 나누고, URL 안전 알파벳을 표준으로 되돌리고, 디코딩하는 것입니다:
Imports System
Imports System.Text
Module JwtOpener
Function ReadPayload(ByVal token As String) As String
Dim parts() As String = token.Split("."c)
If parts.Length <> 3 Then
Throw New FormatException("Not a compact JWT")
End If
' URL 안전 알파벳을 표준으로 되돌린다
Dim packed As String = parts(1).Replace("-"c, "+"c).Replace("_"c, "/"c)
Select Case packed.Length Mod 4
Case 2
packed &= "=="
Case 3
packed &= "="
End Select
Dim bytes() As Byte = System.Convert.FromBase64String(packed)
Return Encoding.UTF8.GetString(bytes)
End Function
End Module
그걸 샘플 토큰에 적용하면 JSON {"sub":"1234567890","name":"John Doe"}를 얻습니다. 헤더를 같은 방식으로 읽으면 {"alg":"HS256","typ":"JWT"}를 얻습니다. 여기서 중요한 경고: JWT를 읽는 것은 JWT를 검증하는 것이 아닙니다. 누구나 토큰을 만들 수 있으므로, 안의 어떤 클레임을 신뢰하기 전에 발급자의 키로 서명을 검증하세요. 그 일에는 System.IdentityModel.Tokens.Jwt NuGet 패키지(Microsoft Entra 팀의 IdentityModel 스위트)가 Base64Url 세부 사항, 서명 검사, 클레임 파싱을 대신해 줍니다. 바로 당신이 직접 굴리고 싶지 않은 층이죠.
76자마다 줄바꿈된 이메일 첨부 파일
이메일은 Base64가 명성을 쌓은 곳입니다. SMTP는 7비트 ASCII로 설계되어 있으므로, 바이너리 첨부 파일은 날아가기 전에 텍스트가 되어야 하고, MIME 표준(RFC 2045)은 76자 줄 한계로 Base64를 골랐습니다. 더 오래된 PEM의 64자 줄의 가까운 친척이죠. 원본 이메일을 받아 본 적 있다면 그 결과를 본 적 있을 겁니다: 빽빽한 Base64 블록이 가지런한 짧은 줄로 나뉘어, Content-Transfer-Encoding: base64 헤더 아래 놓인 모습이죠. 디코더에게 아름다운 부분은, 무엇을 벗길 필요가 없다는 것입니다. .NET 디코더는 줄바꿈과 공백이 어디에 있든 건너뛰므로, 줄바꿈된 블록이 그대로 디코딩됩니다:
Imports System
Imports System.Text
Module MimeOpener
Sub Main()
' 59바이트 문장, CRLF로 76자마다 MIME 줄바꿈됨
Dim wrapped As String = "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZywgYW5kIHRoZW4gc29t" & vbCr & vbLf & "ZS4="
Dim bytes() As Byte = System.Convert.FromBase64String(wrapped)
Console.WriteLine(Encoding.UTF8.GetString(bytes))
' The quick brown fox jumps over the lazy dog, and then some.
End Sub
End Module
당신이 System.Net.Mail 클래스로 일한다면, 디코딩은 더 보이지 않게 됩니다: Attachment를 MailMessage에 추가하면, ContentEncoding은 TransferEncoding.Base64가 되고, 메일 라이브러리가 포장하고, 보내고, 다시 여는 전 과정을 당신 대신 처리해 줍니다. 스트림, 테스트 픽스처, 레거시 메일박스 파일에서 원본 MIME을 읽을 때만 수동 디코딩이 필요합니다.
데이터베이스, 설정, 환경 변수
텍스트 전용 저장은 계속 Base64를 요구합니다: 텍스트 타입의 데이터베이스 컬럼, XML 설정 값, 환경 변수. 전부 평문 문자만 원하므로, 바이너리는 저장 전에 인코딩되고 돌아올 때 디코딩됩니다. 디코딩 쪽은 언제나 같은 원라인러이며, 재미있는 부분은 크기 산수입니다. SQL Server의 일반 NVARCHAR 컬럼은 8,000자로 끝나므로, 33퍼센트 세금이 한계를 넘게 하기 전까지 바이너리 약 6,000바이트를 담을 수 있습니다. 그 이상에서는 MAX 변형을 찾거나, 더 솔직하게 말하면 진짜 바이너리 컬럼을 찾게 되죠. Windows에서는 사용자 정의 환경 변수 하나당 32,767자 한계가 있고(XP 시대 시스템에서는 환경 블록 전체도 그 크기로 제한되어 있었으며), "라이선스 덩어리 전체를 환경 변수에 저장하자"는 계획에는 단단한 상한선이 있습니다. 머릿속 구석에 넣어 둘 가치가 있는 디코딩 패턴이 하나 있습니다: 저장된 지문을 파일과 대조하는 것인데, 일치하지 않아도 타이밍 정보가 새지 않도록 고정 시간 비교를 쓰는 것입니다:
Imports System.Security.Cryptography
Module FingerprintCheck
Function FingerprintsMatch(ByVal expectedPacked As String, ByVal fileBytes() As Byte) As Boolean
Dim expected() As Byte = System.Convert.FromBase64String(expectedPacked)
Dim actual() As Byte = SHA256.HashData(fileBytes)
Return CryptographicOperations.FixedTimeEquals(expected, actual)
End Function
End Module
같은 모양이 어떤 저장된 해시든 통합니다: 저장된 값을 디코딩하고, 새로운 해시를 계산하고, 고정 시간으로 비교하는 것. 값이 XML app.config 항목, JSON 설정 파일, 레지스트리 문자열 어디에서 왔든, 설정 파일도 동일한 패턴을 따릅니다.
빅데이터: 로딩하지 않고 스트림 디코딩하기
지금까지 모든 예제가 페이로드 전체를 메모리에 읽었습니다. 첨부 파일이나 설정 값에는 괜찮지만, 누군가 텍스트 파일로 인코딩한 2기가바이트 파일에는 잘못된 접근이죠. 그 규모를 위해, .NET에는 .NET Framework 1.1(2003)년부터 존재하는 스트리밍 쌍이 있습니다: FromBase64Transform 암호 변환을 CryptoStream에 넣은 것이죠. 인코딩된 텍스트 청크를 읽고, 변환기가 그 자리에서 디코딩하며, 바이트를 써 내면, 파일이 아무리 커도 메모리는 평평하게 유지됩니다:
Imports System.IO
Imports System.Security.Cryptography
Module StreamOpener
Sub DecodeFile(ByVal packedPath As String, ByVal outputPath As String)
Using packedStream As New FileStream(packedPath, FileMode.Open, FileAccess.Read)
Using decodedStream As New CryptoStream(packedStream, New FromBase64Transform(), CryptoStreamMode.Read)
Using outputStream As New FileStream(outputPath, FileMode.Create)
Dim buffer(65535) As Byte
While True
Dim read As Integer = decodedStream.Read(buffer, 0, buffer.Length)
If read = 0 Then Exit While
outputStream.Write(buffer, 0, read)
End While
End Using
End Using
End Using
End Sub
End Module
인코딩된 파일은 평문 ASCII 텍스트이므로, 바이트 스트림으로 읽는 것은 완전히 안전하며, 당신은 아무것도 하지 않아도 변환기가 줄바꿈 포장을 처리해 줍니다. 출력 파일은 입력의 약 4분의 3 크기로 나오는데, 들어올 때 낸 33퍼센트 세금과 같은 것을 나올 때 징수하는 것입니다.
Visual Basic을 특히 물리는 함정들
이 섹션 함정의 대부분은 다른 .NET 언어와 공유하지만, 몇 개는 뚜렷한 VB 모자를 쓰고 있습니다. 그래서 함께 모았습니다:
- Byte와 Byte(). Visual Basic에서 바이트 하나는
Byte이고, 바이트 배열은Byte()입니다. 모든 일은 빈 괄호가 하죠.Dim b As Byte를 쓰면서도 뜻한 것은Byte()인 경우, 바로 첫날의 클래식 실수이며,Option Strict On가 컴파일 시 잡는 바로 그런 종류입니다. 프로젝트에 이미 켜져 있지 않다면 켜세요:dotnet new console -lang VB템플릿은 그 결정을 당신에게 맡기는 반면, Visual Studio 프로젝트 템플릿은 켜 놓고 있습니다. - 스판 벽. 최신 스판 기반 API는 VB에서 호출할 수 있지만, 호출 위치에서만입니다:
Byte()나Char()배열을 스판을 받는 메서드에 그대로 넘길 수 있고, 컴파일러가 대신 변환해 줍니다. 하지만 할 수 없는 것도 있습니다. 자기 코드에서 스판을 이름 붙이는 것.Span또는ReadOnlySpan타입의 변수, 필드, 파라미터를 선언하면 컴파일러가 "Types with embedded references are not supported in this version of your compiler"로 답합니다. 그래서 VB 관용 표현은 이것입니다: 스판 API는 평범한 배열로 호출하고, 스판을 변수에 저장하려 하지 마세요. - BitConverter는 Base64가 아닙니다.
BitConverter.ToString(bytes)는 바이트를 하이픈으로 구분한 16진수로 렌더링하므로, "바이트를 문자열로 변환"을 들어 본 누구나 손이 가는 유혹하는 잘못된 답이 됩니다. 기꺼이4D-61-6E를 돌려 주는데, API가 기대하는 것은TWFu입니다. 망설여진다면System.Convert에 손을 뻗으세요. - MidB는 유령입니다. 클래식 Visual Basic에는 바이트 단위 문자열 함수
MidB,LeftB,RightB가 있었는데, 이중 바이트 문자 집합을 겨냥한 것이었습니다. 이제 모든 .NET 문자열은 유니코드이고, 런타임 문서는 직설적입니다: 더 이상 지원되지 않습니다. 레거시 스니펫이 그것들을 쓴다면, 바이트 배열과 이 글의 API로 다시 쓰세요. - Encoding.Default는 머신을 따라갑니다. 로케일 분기 이야기는 .NET Framework의 것입니다: 최신 .NET에서는
Default가 항상 UTF-8이라, 같은 바이트가 어디서든 같은 방식으로 디코딩됩니다. 공유하는 데이터에는 인코딩을 명시적으로 적고, 보통Encoding.UTF8- 어떤 경우든 그 조언은 유효합니다. - 왕복은 동일성이 아닙니다. 문자열을 디코딩한 뒤 결과를 인코딩하면, 새 문자열이 원래 것과 일치한다는 보장은 없습니다: 공백은 사라지고, 패딩은 정규화됩니다. 줄바꿈이 있는 페이로드는 깨끗한 한 줄로 돌아오죠. 데이터에는 괜찮지만, 디코딩된 바이트 대신 인코딩된 텍스트를 비교하는 로직이라면 위험합니다.
- 공백만 있는 입력은 조용한 성공입니다. 공백과 줄바꿈만으로 이루어진 문자열은 어떤 에러 없이 빈 바이트 배열로 디코딩됩니다. 즉, "사용자가 줄바꿈만 붙여 넣었다"가 "사용자가 빈 페이로드를 붙여 넣었다"와 똑같이 보이는 것이죠. 그 구분이 중요하다면, 디코딩 전에 입력 길이를 확인하세요.
디코딩을 위한 최고의 관행
위 모든 것에서 증류한, 디코딩 코드를 지루하게(최고의 의미에서) 유지하는 습관들입니다:
- Base64를 보호가 아니라 운송으로 취급하세요. 이것은 암호화가 아니라 인코딩입니다: 누구나 함수 호출 한 번으로 원본을 읽을 수 있고, RFC 4648은 심지어 방치된 알파벳 처리가 은밀한 채널을 열 수 있다고도 언급합니다. 콘텐츠를 숨겨야 한다면 먼저 암호화하고, 그 다음에 인코딩하세요.
- 신뢰할 수 없는 입력에는
Try메서드나IsValid사전 검사를 선호하세요 -FormatException을 날아다니게 하는 대신에. 예외를 삼키는 것보다 부울 값을 다정한 에러 메시지로 바꾸기 쉬우니까요. - 문자 집합을 의도적으로 고르고, 다른 방식에 문서화된 이유가 없으면 UTF-8을 기본으로 하세요.
- 알파벳을 출처에 맞추세요: MIME, 이메일, 설정에는 표준 Base64, JWT와 URL 안의 모든 것에는 Base64Url.
- 크게는 문자열로 읽기보다
FromBase64Transform와CryptoStream로 스트리밍하세요. - 지문과 해시는
CryptographicOperations.FixedTimeEquals로 비교하고,=로는 비교하지 마세요. 그러면 타이밍이 값이 어느 정도까지 일치했는지를 새지 않으니까요. Option Strict On을 유지하세요. 그러면Byte/Byte()계열의 실수가 프로덕션의 미스터리가 아니라 컴파일 오류가 됩니다.
Visual Basic이 디코더를 갖게 된 이야기
이야기는 Visual Basic에 Base64가 아예 없던 시대로 거슬러 올라갑니다. Visual Basic 6와 VBA 세계에서(오늘날에도 여전히 Excel과 Office 안에서 돌아가는 매크로 언어), Base64가 필요한 개발자들은 이미 머신에 있는 COM 구성 요소에서 빌었습니다. 유명한 트릭은 XML DOM 요소를 썼습니다: MSXML 파서는 노드가 DataType을 bin.base64로 선언하게 해 주므로, 노드의 text 프로퍼티에 Base64 문자열을 대입하고 nodeTypedValue를 다시 읽으면 원본 바이트가 손에 오고, 실제 Base64 수학은 DOM이 해 줍니다(ADO Stream 객체 자신의 Charset 프로퍼티는 "utf-8"이나 "iso-8859-1" 같은 진짜 문자 집합 이름만 이해하고 "base64"는 이해하지 못하므로, 트릭은 대신 MSXML에 손을 뻗죠). 인코딩은 같은 아이디어를 거꾸로 실행하며, 바이트를 nodeTypedValue에 쓰고 인코딩된 문자열을 text에서 다시 읽어 냅니다:
' 컨텍스트를 위한 클래식 VB6 / VBA 디코딩 트릭
Dim node As Object
Set node = CreateObject("MSXML2.DOMDocument").createElement("b64")
node.DataType = "bin.base64"
node.text = packed ' Base64 문자열
Dim bytes() As Byte
bytes = node.nodeTypedValue
작동했고, 영리했으며, 30년이 지났는데도 "base64 VBA"가 여전히 검색 엔진을 밝히는 이유입니다. 그리고 2002년이 모든 것을 바꿨습니다: Visual Basic 7.0(이 언어의 첫 .NET 버전, 당시 이름은 Visual Basic .NET)이 새로운 Common Language Runtime에 합류했고, .NET Framework가 System.Convert와 FromBase64String 및 친구들을 박스에 함께 들여 놓았죠. 2003년 .NET Framework 1.1부터, 모든 VB 프로그램은 등록할 구성 요소 없이 일급 디코더를 갖게 되었습니다. 현대 물결은 2018년 .NET Core 2.1과 함께 도착하여, 예외 없는 Try 메서드와 빠른 스판 기반 System.Buffers.Text.Base64 클래스를 추가했고, 2024년 .NET 9에서는 마침내 URL 안전 알파벳을 Base64Url로 표준화했습니다. 2026년 현재, 2025년 11월에 릴리스된 .NET 10이 장기 지원 릴리스이며, 프리뷰 .NET 11 라이브러리는 새로운 세대의 Base64 편의 메서드를 추가하고 있습니다. 2003년 원래 원라인러가 변함없이 이어지는 동안에도, 디코더는 계속 좋아지고 있는 것이죠.
VB 세계의 재미있는 사실들
- 공식 Visual Basic 컴파일러 자체가 Visual Basic로 작성되어 있습니다. 오픈소스 Roslyn 프로젝트의 일부로서, 이 언어는 자기 자신을 컴파일합니다.
- 아주 첫 Visual Basic은 웹 시대가 본격화되기 전인 1991년에 출시되었습니다. Base64의 큰 순간은 1993년 MIME과 함께 도착했고, VB 자체도 1995년 Visual Basic 4에서 32비트 프로그램을 처음 가능하게 만들며 32비트 능력을 기릅니다. 1997년 VB 5는 끝까지 갔죠: 32비트만, 박스에 16비트는 하나도 남아 있지 않았습니다.
- 클래식 VB의 바이트 단위 함수
MidB,LeftB,RightB는 .NET에서 공식적으로 "더 이상 지원되지 않습니다". 모든 VB 문자열이 프레임워크 첫날부터 유니코드였기 때문이죠. 인코딩 선택 하나에 은퇴한 API 패밀리 전체입니다. - 런타임의 Base64 기계는 정겨운 테이블 룩업이 아닙니다: 최신 구현은 머신이 지원할 때 하드웨어 벡터화 코드 경로(AVX-512, AVX2, SSE 변형)를 실행하므로, 엔진 하에서 일어나는 일은 "느린 텍스트 코덱"이 아닙니다.
- VB6 시대의 MSXML
bin.base64트릭은 오늘날에도 프로덕션 Excel 매크로에서 돌아가고 있습니다. 1990년대 말의 우회책과 2003년의Convert호출이 같은 조직의 코드베이스에서 행복하게 공존한다는 뜻이죠.
떠나가기 전에
이 글은 Visual Basic에서 Base64의 디코딩 쪽을 다뤘습니다: 원라인러부터 스트리밍까지, JWT부터 VB 모자를 쓴 함정까지. 동전의 다른 면, 즉 바이트와 텍스트를 애초에 Base64로 만드는 쪽은 패딩, 줄바꿈, 크기 세금에 대한 자기만의 결정 집합을 갖고 있으며, 자매 사이트의 동반 인코딩 글에서 자세히 다룹니다. 그 링크는 바로 아래 줄에 있고, 홈 페이지의 도구는 여전히 작은 페이로드를 손으로 확인하는 가장 빠른 방법입니다.
마지막 업데이트: 2026-09-08