MySQL과 MariaDB: TO_BASE64와 FROM_BASE64
SELECT TO_BASE64('héllo wörld');
-- aMOpbGxvIHfDtnJsZA== (connection charset utf8mb4)
SELECT CONVERT(FROM_BASE64('aMOpbGxvIHfDtnJsZA==') USING utf8mb4);
-- héllo wörld
TO_BASE64는 문자열의 바이트를 그 문자 집합 그대로 인코딩합니다. utf8mb4라면 UTF-8이 나오고, latin1 열이라면 Latin-1 바이트가 나옵니다(euckr 열이라면 EUC-KR 바이트). 그래서 TO_BASE64(CONVERT('héllo' USING latin1))은 aMOpbGxv가 아니라 aOlsbG8=를 반환합니다. FROM_BASE64는 바이너리 문자열을 반환하며, CONVERT(... USING utf8mb4)로 다시 텍스트로 바꿀 수 있습니다.
TO_BASE64는 MIME처럼 76자에서 줄을 바꿉니다.
SELECT LENGTH(TO_BASE64(REPEAT('x', 60))); -- 81 (80 chars + 1 newline)
SELECT REPLACE(TO_BASE64(REPEAT('x', 60)), '\n', ''); -- single line, 80 chars
잘못된 입력은 오류를 일으키지 않습니다. FROM_BASE64는 NULL을 반환합니다.
| 입력 | 결과 |
|---|---|
aGk= | hi |
aGk | NULL (패딩 없음) |
aGk=!! | NULL |
PDw_Pz8-Pg== | NULL (URL-safe) |
a | NULL |
aGl= | hi (0이 아닌 패딩 비트 허용) |
'aGVs\nbG8K', 'aGVs bG8K' | hello\n (공백 무시) |
문자열 함수로 URL-safe 변환하기:
SELECT TRIM(TRAILING '=' FROM REPLACE(REPLACE(TO_BASE64('<<???>>'), '+', '-'), '/', '_'));
-- PDw_Pz8-Pg
SELECT FROM_BASE64(CONCAT(
REPLACE(REPLACE('PDw_Pz8-Pg', '-', '+'), '_', '/'),
REPEAT('=', (4 - LENGTH('PDw_Pz8-Pg') % 4) % 4)));
-- <<???>>
값이 더 길다면 인코더도 REPLACE(..., '\n', '')로 감싸세요. 두 함수 모두 MySQL 5.6부터 있으며 MariaDB에도 있습니다.
PostgreSQL: encode와 decode
PostgreSQL은 bytea를 다룹니다. 텍스트는 convert_to로 바꾸고 convert_from으로 되돌리세요.
SELECT encode(convert_to('héllo wörld', 'UTF8'), 'base64');
-- aMOpbGxvIHfDtnJsZA==
SELECT convert_from(decode('aMOpbGxvIHfDtnJsZA==', 'base64'), 'UTF8');
-- héllo wörld
MySQL처럼 encode(..., 'base64')도 76자에서 줄을 바꾸며, psql은 줄 끝의 줄 바꿈을 +로 표시합니다. replace(encode(x, 'base64'), E'\n', '')로 제거하세요.
MySQL과 달리 잘못된 입력에는 오류가 납니다.
decode('aGk', 'base64')
ERROR: invalid base64 end sequence
HINT: Input data is missing padding, is truncated, or is otherwise corrupted.
decode('PDw_Pz8-Pg==', 'base64')
ERROR: invalid symbol "_" found while decoding base64 sequence
aGk=!!는 invalid symbol "!"로, a는 invalid base64 end sequence로 실패합니다. 줄 바꿈과 공백을 포함한 공백 문자는 무시되며, aGl=는 허용됩니다.
PostgreSQL 17과 18에는 base64url 형식이 없으므로(unrecognized encoding: "base64url") translate로 변환하세요.
SELECT translate(rtrim(encode('<<???>>'::bytea, 'base64'), '='), '+/', '-_');
-- PDw_Pz8-Pg
SELECT convert_from(decode(
rpad(translate('PDw_Pz8-Pg', '-_', '+/'), (length('PDw_Pz8-Pg') + 3) / 4 * 4, '='),
'base64'), 'UTF8');
-- <<???>>
트랜잭션을 중단시키지 않고 검증하려면 NULL을 반환하는 EXCEPTION WHEN others 블록이 있는 PL/pgSQL 함수로 decode를 감싸세요.
SQL Server: XML base64Binary 기법
SQL Server 2025 이전에는 Base64 함수가 없습니다. XML 타입은 varbinary와 Base64인 xs:base64Binary 사이를 변환합니다.
-- encode
DECLARE @bin varbinary(max) = CAST('hi' AS varbinary(max));
SELECT CAST('' AS xml).value('xs:base64Binary(sql:variable("@bin"))', 'varchar(max)');
-- aGk=
-- decode
DECLARE @b64 varchar(max) = 'aGk=';
SELECT CAST('' AS xml).value('xs:base64Binary(sql:variable("@b64"))', 'varbinary(max)');
-- 0x6869
FOR XML ... BINARY BASE64와 FOR JSON도 varbinary 열을 Base64로 내보냅니다. SELECT CAST('hi' AS varbinary(max)) AS bin FOR JSON PATH는 [{"bin":"aGk="}]를 반환합니다. XML 변환은 줄을 바꾸지 않습니다. 60바이트가 80자짜리 문자열 하나로 나왔습니다.
XML 메서드에는 QUOTED_IDENTIFIER ON이 필요합니다. sqlcmd는 기본값이 off라서 Msg 1934: SELECT failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'가 나옵니다. -I를 넘기거나 SET QUOTED_IDENTIFIER ON을 실행하세요.
SQL Server의 텍스트 인코딩
nvarchar는 UTF-16LE이므로 CAST(N'héllo wörld' AS varbinary(max))는 aADpAGwAbABvACAAdwD2AHIAbABkAA==로 인코딩되며, 다른 시스템에서 디코딩하면 글자 사이에 빈칸이 생깁니다. UTF-8이 필요하면(SQL Server 2019 이상) 먼저 UTF-8 데이터 정렬을 거치세요.
DECLARE @bin varbinary(max) = CAST(CAST(N'héllo wörld'
COLLATE Latin1_General_100_CI_AS_SC_UTF8 AS varchar(max)) AS varbinary(max));
-- encodes to aMOpbGxvIHfDtnJsZA==
UTF-8을 다시 디코딩하려면 UTF-8 대상이 필요합니다. 기본 SQL_Latin1_General_CP1_CI_AS 데이터 정렬을 쓰는 데이터베이스에서 그냥 CAST(@raw AS varchar(max))를 하면 héllo wörld처럼 깨져 보입니다. varchar(max) COLLATE Latin1_General_100_CI_AS_SC_UTF8로 선언한 열에 바이트를 넣으면 héllo wörld가 나옵니다. UTF-16 데이터라면 CAST(@raw AS nvarchar(max))로 충분합니다.
오류
문자열 리터럴이라면 잘못된 Base64는 컴파일 시점에 실패합니다. Msg 9319: XQuery [value()]: Static simple type validation: Invalid simple type value 'aGk'. 같은 입력을 sql:variable로 넘기면 대신 NULL이 반환됩니다. 패딩 누락, URL-safe 문자(PDw_Pz8-Pg==), 0이 아닌 패딩 비트(aGl=)는 모두 거부되었고, 문자열 안의 줄 바꿈은 허용되었습니다.
SQL Server 2025: BASE64_ENCODE와 BASE64_DECODE
SQL Server 2025(17.x)와 Azure SQL에는 URL-safe 플래그까지 갖춘 진짜 함수가 추가되었습니다.
SELECT BASE64_ENCODE(0x3C3C3F3F3F3E3E); -- PDw/Pz8+Pg==
SELECT BASE64_ENCODE(0x3C3C3F3F3F3E3E, 1); -- PDw_Pz8-Pg (url_safe, no padding)
SELECT BASE64_DECODE('PDw_Pz8-Pg'); -- 0x3C3C3F3F3F3E3E
BASE64_ENCODE는 varbinary만 받습니다. varchar를 넘기면 Msg 8116: Argument data type varchar(max) is invalid for argument 1 of base64_encode function으로 실패합니다. BASE64_DECODE는 두 알파벳을 패딩 유무와 관계없이 받으며 aGl=도 받습니다. aGk=!!나 a 같은 잘못된 입력은 Msg 9803: Invalid data for type "Base64Decode"를 일으킵니다.
데이터베이스 공통 함정
- 입력의 문자 집합. 같은 텍스트라도
latin1열,utf8mb4열,nvarchar열에서 서로 다른 Base64가 나옵니다. UTF-8 바이트로 정하고 명시적으로 변환하세요. - 숨은 줄 바꿈. MySQL과 PostgreSQL은 76자에서 줄을 바꿉니다. JSON 소비자와 HTTP 헤더는 그 안에 든
\n때문에 깨집니다. - 조용한
NULL. MySQL과 SQL Server XML 기법은 잘못된 입력에NULL을 반환하며,JOIN이나WHERE가 이를 조용히 걸러 버립니다. - 큰 값. 쿼리에서 BLOB을 인코딩하면 네트워크로 33% 더 많은 데이터가 오갑니다. 저장된 값을 직접 확인하려면 Base64로 조회한 뒤 Base64 파일 변환이나 Base64 hex 변환으로 여세요.
자주 묻는 질문
MySQL FROM_BASE64가 NULL을 반환하는 이유는 무엇인가요?
MySQL은 잘못된 입력에 오류 대신 NULL을 반환합니다. 패딩 누락, URL-safe 문자 -나 _, 쓰레기 문자가 원인입니다. Base64 검사기로 입력을 확인하고, URL-safe 문자열은 디코딩 전에 REPLACE로 고치세요.
MySQL TO_BASE64 출력에 줄 바꿈이 들어가는 이유는 무엇인가요?
TO_BASE64는 MIME 관례를 따라 76자마다 줄 바꿈을 넣습니다. 한 줄 출력이 필요하면 REPLACE(TO_BASE64(x), '\n', '')로 감싸세요.
SQL Server에 Base64 함수가 있나요?
SQL Server 2025와 Azure SQL에는 BASE64_ENCODE와 BASE64_DECODE가 있습니다. 이전 버전에는 함수가 없지만 XML xs:base64Binary 변환으로 같은 일을 할 수 있습니다.
파일을 Base64로 데이터베이스에 저장해야 하나요?
대개는 아닙니다. 바이너리 열(BLOB, bytea, varbinary)은 같은 데이터를 약 25% 작게 저장하고 변환도 필요 없습니다. 텍스트가 필요한 시스템으로 보낼 때만 인코딩하세요. Base64 크기 증가를 참고하세요.