Java Base64 인코딩 완전 가이드
무료 Base64 인코더을 브라우저에서 직접 사용하세요 — 설치 불필요.
Base64 인코더 온라인으로 사용하기 →HTTP Basic Auth 헤더를 설정하거나, Kubernetes 시크릿에 인증서를 임베드하거나, JSON API를 통해 이진 데이터를 전송할 때마다 첫 번째 단계는 항상 같습니다: base64 인코딩으로 원시 바이트를 ASCII 안전 문자열로 변환하는 것입니다. Java는 java.util.Base64를 통해 이를 간단하게 처리할 수 있습니다. 이 표준 API는 deprecated된 sun.misc.BASE64Encoder를 대체하여 Java 8부터 사용할 수 있습니다. 코드 작성 없이 바로 인코딩하려면 ToolDeck의 Base64 인코더 를 이용해 브라우저에서 즉시 처리할 수 있습니다. 이 가이드에서는 Base64.getEncoder(), getUrlEncoder(), getMimeEncoder(), 파일 인코딩, wrap(OutputStream)을 이용한 스트리밍, 그리고 숙련된 Java 개발자도 자주 겪는 실수들을 다룹니다. 모든 예제는 Java 8부터 Java 21+에서 컴파일됩니다.
- ✓Base64.getEncoder().encodeToString(bytes)는 표준 한 줄 코드입니다 — Java 8부터 JDK에 내장되어 있으며 Java 17, 21에서도 동일합니다.
- ✓인코딩 전에 String.getBytes()에 항상 StandardCharsets.UTF_8을 전달하세요 — 생략하면 JVM마다 다른 플랫폼 기본값이 사용됩니다.
- ✓getUrlEncoder()는 URL 안전 출력(+ 대신 -, / 대신 _)을 생성하며, withoutPadding()은 후행 = 문자를 제거합니다.
- ✓getMimeEncoder()는 76자마다 줄 바꿈을 삽입합니다 — 이메일(MIME) 및 PEM 인증서 형식에서 필요합니다.
- ✓대용량 파일의 경우 Base64.getEncoder().wrap(OutputStream)을 사용하여 전체 파일을 메모리에 로드하지 않고 스트리밍합니다.
Base64 인코딩이란?
Base64는 임의의 이진 데이터를 64개의 출력 가능한 ASCII 문자(A-Z, a-z, 0-9, +, 및 /)로 구성된 문자열로 변환합니다. 입력 3바이트는 정확히 4개의 Base64 문자를 생성합니다. 입력 길이가 3의 배수가 아닌 경우 하나 또는 두 개의 = 패딩 문자가 추가됩니다. 인코딩된 출력은 원본 데이터보다 약 33% 더 큽니다.
Base64는 암호화가 아닙니다. 인코딩된 문자열을 가진 사람은 누구나 디코딩할 수 있습니다. 그 목적은 전송 안전성에 있습니다. HTTP 헤더, JSON 페이로드, XML 문서, 이메일 본문은 텍스트 기반 프로토콜이므로 손상 없이 원시 이진 바이트를 전달할 수 없습니다. Java에서 흔히 사용되는 사례로는 HTTP Basic 인증, PEM 인증서 임베딩, 데이터베이스 텍스트 컬럼에 이진 데이터 저장, JWT 토큰 세그먼트 구성 등이 있습니다.
deploy-svc:sk_live_4eC39HqLyjWDarjtT1zdp7dc
ZGVwbG95LXN2Yzpza19saXZlXzRlQzM5SHFMeWpXRGFyanRUMXpkcDdkYw==
Base64.getEncoder().encodeToString() — 표준 API
java.util.Base64 는 Java 8에서 sun.misc.BASE64Encoder의 공식 대체재로 도입되었습니다. 이 클래스는 세 가지 정적 팩토리 메서드를 제공하며, 각각 RFC 4648에 정의된 특정 Base64 변형에 맞게 구성된 Base64.Encoder 중첩 클래스 인스턴스를 반환합니다. 서드파티 라이브러리가 필요하지 않습니다. Maven 의존성도 없습니다. 임포트하고 호출하기만 하면 됩니다.
최소 예제 — 문자열 인코딩
import java.util.Base64;
import java.nio.charset.StandardCharsets;
public class EncodeDemo {
public static void main(String[] args) {
String credentials = "monitoring-svc:9f2a7c4e-b1d8-4a3f";
byte[] credentialBytes = credentials.getBytes(StandardCharsets.UTF_8);
String encoded = Base64.getEncoder().encodeToString(credentialBytes);
System.out.println(encoded);
// bW9uaXRvcmluZy1zdmM6OWYyYTdjNGUtYjFkOC00YTNm
}
}대부분의 Java 개발자가 처음에 놓치는 핵심 단계가 있습니다. String 은 인코딩 전에 byte[] 로 변환해야 합니다. Base64는 문자가 아닌 바이트를 처리합니다. encodeToString() 은 byte[] 를 받아 Base64 String 을 직접 반환합니다. 인코딩 결과를 바이트로 얻으려면 encode(byte[]) 를 사용하세요 — 이는 ASCII 인코딩된 Base64 문자들의 byte[] 를 반환하며, OutputStream에 직접 쓰거나 이진 프로토콜 프레임을 구성할 때 유용합니다.
HTTP Basic 인증 — 가장 일반적인 사용 사례
HTTP Basic 인증은 Java 개발자가 Base64 인코딩을 사용하는 가장 흔한 이유입니다. 스펙(RFC 7617)에 따라 자격 증명 문자열 username:password 를 Base64로 인코딩하여 Authorization 헤더에 넣어야 합니다. 콜론 구분자를 빠뜨리거나 각 구성요소를 따로 인코딩하는 등의 실수를 수없이 목격해 왔습니다.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Base64;
import java.nio.charset.StandardCharsets;
public class BasicAuthExample {
public static void main(String[] args) throws Exception {
String username = "metrics-exporter";
String apiKey = "sk_live_4eC39HqLyjWDarjtT1zdp7dc";
// username:password → Base64
String credentials = username + ":" + apiKey;
String authHeader = "Basic " + Base64.getEncoder()
.encodeToString(credentials.getBytes(StandardCharsets.UTF_8));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/v2/metrics"))
.header("Authorization", authHeader)
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode()); // 200
}
}왕복 처리 — 인코딩 및 디코딩
import java.util.Base64;
import java.nio.charset.StandardCharsets;
public class RoundTrip {
public static void main(String[] args) {
String original = "X-Correlation-ID: req_8a4f2c91-e7b3-4d56-9012-3f7a8b9c0d1e";
// 인코딩
String encoded = Base64.getEncoder()
.encodeToString(original.getBytes(StandardCharsets.UTF_8));
System.out.println(encoded);
// WC1Db3JyZWxhdGlvbi1JRDogcmVxXzhhNGYyYzkxLWU3YjMtNGQ1Ni05MDEyLTNmN2E4YjljMGQxZQ==
// 디코딩
byte[] decodedBytes = Base64.getDecoder().decode(encoded);
String decoded = new String(decodedBytes, StandardCharsets.UTF_8);
System.out.println(original.equals(decoded)); // true
}
}java.util.Base64 API는 Java 8부터 Java 17, Java 21까지 동일합니다. JDK를 업그레이드할 때 마이그레이션이 필요하지 않습니다. Java 8 이후 모든 버전에서 동일한 코드가 컴파일되고 실행됩니다.문자열이 아닌 데이터 인코딩 — byte[], UUID, 타임스탬프
Java에서 Base64 인코딩은 항상 byte[]에서 시작합니다. 문자열은 getBytes(StandardCharsets.UTF_8)로 변환하지만, 다른 타입은 먼저 변환 단계가 필요합니다. UUID, 타임스탬프, 숫자 식별자는 Base64로 인코딩하기 전에 문자열 또는 바이트 표현으로 직렬화해야 합니다.
UUID — 문자열 표현으로 인코딩
import java.util.Base64;
import java.util.UUID;
import java.nio.charset.StandardCharsets;
UUID sessionId = UUID.fromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8");
String encoded = Base64.getEncoder()
.encodeToString(sessionId.toString().getBytes(StandardCharsets.UTF_8));
System.out.println(encoded);
// NmJhN2I4MTAtOWRhZC0xMWQxLTgwYjQtMDBjMDRmZDQzMGM4컴팩트 UUID — 원시 16바이트로 인코딩
더 짧은 인코딩 결과를 원한다면 UUID의 128비트를 36자 문자열 형식으로 변환하는 대신 16개의 원시 바이트로 추출합니다. Base64 출력이 48자에서 24자로 줄어듭니다.
import java.nio.ByteBuffer;
import java.util.Base64;
import java.util.UUID;
UUID eventId = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
ByteBuffer buffer = ByteBuffer.wrap(new byte[16]);
buffer.putLong(eventId.getMostSignificantBits());
buffer.putLong(eventId.getLeastSignificantBits());
String compact = Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(buffer.array());
System.out.println(compact);
// VQ6EAOKbQdSnFkRmVUQAAA
// 문자열 기반 방식의 48자 대비 22자타임스탬프 및 혼합 페이로드
import java.time.Instant;
import java.util.Base64;
import java.nio.charset.StandardCharsets;
// JWT 스타일 페이로드 시뮬레이션
String payload = String.format(
"{"sub":"usr_7b3c","iss":"auth.internal","iat":%d,"exp":%d}",
Instant.now().getEpochSecond(),
Instant.now().plusSeconds(3600).getEpochSecond()
);
String encoded = Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(payload.getBytes(StandardCharsets.UTF_8));
System.out.println(encoded);
// eyJzdWIiOiJ1c3JfN2IzYyIsImlzcyI6ImF1dGguaW50ZXJuYWwiLCJpYXQiOj... (URL 안전, 패딩 없음)byte[]에서 toString()을 호출하면 내용이 아닌 [B@6d06d69c와 같은 배열의 identity hash가 반환됩니다.new String(bytes, StandardCharsets.UTF_8)를 사용하거나 바이트 배열을 직접 encodeToString()에 전달하세요.Base64.Encoder 메서드 참조
java.util.Base64 클래스는 세 가지 팩토리 메서드를 제공하며, 각각 특정 변형에 맞게 구성된 Base64.Encoder 를 반환합니다. 인코더 인스턴스는 스레드 안전하고 상태가 없습니다 — 한 번 생성하여 재사용하세요.
Base64.getUrlEncoder() — URL 안전 인코딩
URL 안전 인코더는 RFC 4648 섹션 5에 정의된 대체 알파벳을 사용합니다. + 는 - 로, / 는 _로 변환됩니다. Base64 문자열이 URL 쿼리 파라미터, 파일명, 쿠키 값에 나타날 때 이것이 중요합니다 — 표준 Base64 문자는 URL 구분자 및 파일 시스템 예약 문자와 충돌합니다.
import java.util.Base64; import java.nio.charset.StandardCharsets; String redirectUri = "https://app.internal/callback?state=auth_pending&nonce=9f2a7c"; byte[] data = redirectUri.getBytes(StandardCharsets.UTF_8); // 표준 인코더 — URL을 깨뜨리는 + 와 / 포함 String standard = Base64.getEncoder().encodeToString(data); System.out.println(standard); // aHR0cHM6Ly9hcHAuaW50ZXJuYWwvY2FsbGJhY2s/c3RhdGU9YXV0aF9wZW5kaW5nJm5vbmNlPTlmMmE3Yw== // URL 안전 인코더 — 쿼리 파라미터와 파일명에 안전 String urlSafe = Base64.getUrlEncoder().encodeToString(data); System.out.println(urlSafe); // aHR0cHM6Ly9hcHAuaW50ZXJuYWwvY2FsbGJhY2s_c3RhdGU9YXV0aF9wZW5kaW5nJm5vbmNlPTlmMmE3Yw== // 패딩 없는 URL 안전 — JWT 및 컴팩트 토큰용 String noPadding = Base64.getUrlEncoder().withoutPadding().encodeToString(data); System.out.println(noPadding); // aHR0cHM6Ly9hcHAuaW50ZXJuYWwvY2FsbGJhY2s_c3RhdGU9YXV0aF9wZW5kaW5nJm5vbmNlPTlmMmE3Yw
withoutPadding() 변형은 후행 = 문자를 제거합니다. JWT 스펙은 헤더와 페이로드 세그먼트에 패딩 없는 URL 안전 Base64를 요구하므로, JWT 토큰을 수동으로 구성하거나 조작할 때 getUrlEncoder().withoutPadding() 이 정확히 필요한 호출입니다.
withoutPadding() 메서드는 새로운 인코더 인스턴스를 반환합니다 — 원본을 수정하지 않습니다. 두 인스턴스 모두 static final 필드에 할당하여 스레드 간에 안전하게 재사용할 수 있습니다.파일 및 API 응답 인코딩
Java에서 Base64 인코딩의 실제 사용 사례 두 가지: 디스크에서 이진 파일 읽기 (인증서, 이미지, 설정 번들)와 HTTP 응답으로 받은 데이터 인코딩입니다.
파일을 Base64로 인코딩
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
public class FileEncoder {
public static void main(String[] args) {
try {
byte[] fileBytes = Files.readAllBytes(Path.of("certs/server.pem"));
String encoded = Base64.getEncoder().encodeToString(fileBytes);
System.out.printf("원본: %d 바이트%n", fileBytes.length);
System.out.printf("인코딩: %d 문자%n", encoded.length());
// 인코딩된 내용을 텍스트 파일에 저장
Files.writeString(
Path.of("certs/server.pem.b64"),
encoded
);
} catch (java.io.IOException e) {
System.err.println("파일 읽기 실패: " + e.getMessage());
}
}
}API 응답 본문 인코딩
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Base64;
public class ApiEncoder {
public static void main(String[] args) {
try {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/v2/reports/weekly.pdf"))
.header("Authorization", "Bearer tok_8f2a9c3d")
.build();
HttpResponse<byte[]> response = client.send(
request, HttpResponse.BodyHandlers.ofByteArray()
);
if (response.statusCode() == 200) {
String encoded = Base64.getEncoder()
.encodeToString(response.body());
System.out.printf("%d 바이트 → %d 문자로 인코딩%n",
response.body().length, encoded.length());
} else {
System.err.printf("HTTP %d: %s%n",
response.statusCode(),
new String(response.body()));
}
} catch (Exception e) {
System.err.println("요청 실패: " + e.getMessage());
}
}
}CLI 섹션 전 간략한 참고 사항: 파일이나 API 응답을 붙여넣어 코드 작성 없이 Base64 출력을 얻으려면 온라인 Base64 인코더 에서 텍스트와 이진 입력 모두 처리할 수 있습니다.
커맨드 라인 Base64 인코딩
Java 프로젝트, IDE, 빌드 단계 없이 터미널에서 문자열이나 파일을 인코딩해야 할 때가 있습니다. 대부분의 Unix 시스템에는 base64 명령이 있으며, JDK가 설치되어 있다면 jshell 을 사용하여 Java 네이티브 방식으로 처리할 수 있습니다.
# macOS / Linux — 문자열 인코딩
echo -n "deploy-bot:sk_prod_9f2a7c4e" | base64
# ZGVwbG95LWJvdDpza19wcm9kXzlmMmE3YzRl
# 파일 인코딩
base64 < certs/server.pem > certs/server.pem.b64
# jshell 사용 (JDK 9+)
echo 'System.out.println(java.util.Base64.getEncoder().encodeToString("deploy-bot:sk_prod_9f2a7c4e".getBytes()))' | jshell -
# java로 직접 한 줄 실행
java -e 'System.out.println(java.util.Base64.getEncoder().encodeToString(args[0].getBytes()))' "my-secret"
# 참고: java -e는 JDK 23+가 필요합니다 (JEP 477)jshell 방식은 Java 코드가 Unix 도구와 동일한 출력을 생성하는지 확인하거나, 서비스가 보내는 내용과 수신자가 기대하는 내용 사이의 불일치를 디버깅할 때 특히 유용합니다. 저는 이를 위한 셸 별칭을 만들어 두었습니다.
base64 명령은 디코딩에 -D를 사용합니다. Linux(GNU coreutils)에서는 -d를 사용합니다. 인코딩 동작은 양쪽 모두 동일합니다. Linux의 -w 0 플래그는 출력에서 줄 바꿈을 비활성화합니다. 다른 명령으로 파이프할 때는 대부분 이것이 필요합니다.Apache Commons Codec — 고성능 대안
대부분의 애플리케이션에서 java.util.Base64 는 충분히 빠릅니다. 하지만 로그 수집 파이프라인이나 고처리량 메시지 브로커처럼 타이트한 루프에서 수백만 번의 인코딩을 처리해야 한다면 Apache Commons Codec을 벤치마킹해 볼 가치가 있습니다. Java 8 이전부터 존재했으며 약간 다른 API 표면을 가진 검증된 대안을 제공합니다.
// Maven: org.apache.commons:commons-codec:1.17.0
import org.apache.commons.codec.binary.Base64;
import java.nio.charset.StandardCharsets;
byte[] telemetryPayload = ("{"service":"metrics-collector","
+ ""host":"prod-east-07","
+ ""cpu_pct":72.4,"
+ ""mem_mb":3891,"
+ ""timestamp":1710523200}")
.getBytes(StandardCharsets.UTF_8);
// 표준 인코딩
String encoded = Base64.encodeBase64String(telemetryPayload);
// URL 안전 인코딩
String urlSafe = Base64.encodeBase64URLSafeString(telemetryPayload);
// 문자열이 유효한 Base64인지 확인
boolean valid = Base64.isBase64(encoded);
System.out.println(valid); // trueApache Commons Codec은 스트리밍 시나리오를 위한 Base64OutputStream 및 Base64InputStream 도 제공하며, JDK 인코더에 없는 유효성 검사 메서드도 포함합니다. Commons Codec이 이미 의존성 트리에 있다면(많은 Apache 프로젝트에 포함됨) 사용하지 않을 이유가 없습니다.
Guava BaseEncoding
Google의 Guava 라이브러리에는 BaseEncoding 이 포함되어 있으며, 설정 가능한 줄 구분자, 패딩 제어, 표준 및 URL 안전 알파벳 지원을 갖춘 유창한 API를 제공합니다. API가 읽기 쉽지만, Base64 인코딩만을 위해 Guava(약 3 MB)를 추가하는 것은 과도합니다. Guava가 이미 컬렉션이나 캐싱 유틸리티로 프로젝트에 있다면 인코딩 API를 유용하게 활용할 수 있습니다.
// Maven: com.google.guava:guava:33.1.0-jre
import com.google.common.io.BaseEncoding;
import java.nio.charset.StandardCharsets;
byte[] webhookPayload = ("{"event":"deployment.completed","
+ ""repo":"payments-api","
+ ""sha":"a7f2c91e4b3d","
+ ""environment":"production"}")
.getBytes(StandardCharsets.UTF_8);
// 표준 Base64
String standard = BaseEncoding.base64().encode(webhookPayload);
// URL 안전
String urlSafe = BaseEncoding.base64Url().encode(webhookPayload);
// 패딩 없음
String noPad = BaseEncoding.base64Url().omitPadding().encode(webhookPayload);
// 줄 구분자 포함 (PEM 스타일)
String wrapped = BaseEncoding.base64()
.withSeparator("\n", 64)
.encode(webhookPayload);Base64.getMimeEncoder() — MIME 및 PEM 줄 바꿈 출력
MIME 인코더는 MIME 스펙(RFC 2045)에 맞게 76자마다 \r\n 줄 바꿈을 삽입합니다. PEM 인증서, S/MIME 이메일 첨부 파일, 일부 레거시 API는 이 형식을 기대합니다. 표준 및 URL 안전 인코더는 단일한 연속된 줄을 생성하므로, 줄 바꿈된 Base64를 기대하는 시스템에 그 출력을 전달하면 자동으로 실패하거나 데이터를 거부할 수 있습니다.
import java.util.Base64;
import java.nio.charset.StandardCharsets;
// PEM 인증서 본문 시뮬레이션
byte[] certData = new byte[256]; // 실제로는 .der 파일에서 읽습니다
new java.security.SecureRandom().nextBytes(certData);
// 기본 MIME 인코더 — 줄당 76자, \r\n 구분자
String mimeEncoded = Base64.getMimeEncoder().encodeToString(certData);
System.out.println(mimeEncoded);
// QYx2K3p8Xg7JmN1R+wFkLd... (76자)
// Ht5Bv9CzAq0PnSjYl8WxUe... (76자)
// ...
// 사용자 정의 MIME 인코더 — 줄당 64자 (PEM 표준), \n 구분자
Base64.Encoder pemEncoder = Base64.getMimeEncoder(64, new byte[]{'\n'});
String pemBody = pemEncoder.encodeToString(certData);
System.out.println("-----BEGIN CERTIFICATE-----");
System.out.println(pemBody);
System.out.println("-----END CERTIFICATE-----");getMimeEncoder()를 사용하지 마세요. 줄 바꿈이 해당 컨텍스트에서 데이터를 손상시킵니다. 대신 getEncoder() 또는 getUrlEncoder()를 사용하세요.Base64.getEncoder().wrap()로 대용량 파일 스트리밍
Files.readAllBytes() 로 전체 파일을 byte[] 에 로드하는 방식은 소형 파일에는 작동하지만, 50~100 MB 이상의 파일에서는 OutOfMemoryError위험이 있습니다. JDK는 Base64.getEncoder().wrap(OutputStream)을 제공합니다. 이는 쓸 때 즉석에서 데이터를 인코딩하는 OutputStream 을 반환합니다. 전체 입력을 버퍼링하지 않고 인코딩된 바이트가 기반 스트림으로 흘러갑니다.
import java.io.*;
import java.nio.file.*;
import java.util.Base64;
public class StreamingEncoder {
public static void main(String[] args) throws IOException {
Path inputPath = Path.of("backups/database-export.sql.gz");
Path outputPath = Path.of("backups/database-export.sql.gz.b64");
try (
InputStream in = Files.newInputStream(inputPath);
OutputStream fileOut = Files.newOutputStream(outputPath);
OutputStream base64Out = Base64.getEncoder().wrap(fileOut)
) {
byte[] buffer = new byte[8192];
int bytesRead;
long totalBytes = 0;
while ((bytesRead = in.read(buffer)) != -1) {
base64Out.write(buffer, 0, bytesRead);
totalBytes += bytesRead;
}
System.out.printf("Base64 인코더를 통해 %d 바이트 스트리밍 완료%n", totalBytes);
}
// base64Out을 닫으면 최종 패딩 바이트가 자동으로 플러시됩니다
}
}try-with-resources 블록이 플러시와 닫기를 처리합니다. 주의해야 할 세부 사항이 있습니다. 최종 Base64 패딩은 래핑된 OutputStream 이 닫힐 때만 기록됩니다. 닫기를 잊거나 외부 스트림만 닫으면 인코딩된 출력의 마지막 몇 문자가 누락될 수 있습니다.
네트워크 소켓으로 스트리밍
wrap() 메서드는 파일 출력, 소켓 출력, HTTP 응답 본문, 심지어 ByteArrayOutputStream등 모든 OutputStream 에서 작동합니다. 다음은 Base64로 인코딩된 데이터를 인메모리 버퍼에 직접 쓰는 예제로, 단위 테스트나 HTTP로 전송할 페이로드 빌드에 유용합니다.
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.Base64;
import java.nio.charset.StandardCharsets;
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try (OutputStream encoder = Base64.getEncoder().wrap(buffer)) {
// 청크 단위로 데이터 쓰기 — 스트림 읽기 시뮬레이션
encoder.write("chunk-1:telemetry-data-".getBytes(StandardCharsets.UTF_8));
encoder.write("chunk-2:more-payload-".getBytes(StandardCharsets.UTF_8));
encoder.write("chunk-3:final-segment".getBytes(StandardCharsets.UTF_8));
}
String encoded = buffer.toString(StandardCharsets.UTF_8);
System.out.println(encoded);
// Y2h1bmstMTp0ZWxlbWV0cnktZGF0YS1jaHVuay0yOm1vcmUtcGF5bG9hZC1jaHVuay0zOmZpbmFsLXNlZ21lbnQ=
// 왕복 검증
byte[] decoded = Base64.getDecoder().decode(encoded);
System.out.println(new String(decoded, StandardCharsets.UTF_8));
// chunk-1:telemetry-data-chunk-2:more-payload-chunk-3:final-segmentBufferedInputStream이 사용하는 기본 버퍼 크기와 일치하며 메모리 사용량과 시스템 호출 오버헤드 사이의 적절한 균형점입니다. 작은 버퍼는 읽기/쓰기 호출 횟수를 늘리고, 큰 버퍼는 의미 있는 처리량 향상 없이 메모리를 낭비합니다.스레드 안전 인코더 인스턴스 — 저장하여 재사용
팩토리 메서드가 반환하는 Base64.Encoder 는 불변이며 스레드 안전합니다. 모든 인코딩 작업마다 Base64.getEncoder() 를 호출하면 매번 새 객체가 생성됩니다. JVM이 이를 최적화할 가능성이 높지만, 인코더를 static final 필드에 저장하면 의도가 명확해지고 핫 경로에서 불필요한 할당을 피할 수 있습니다.
import java.util.Base64;
import java.nio.charset.StandardCharsets;
public class TokenService {
// 한 번 생성, 어디서나 재사용 — 스레드 안전
private static final Base64.Encoder STANDARD = Base64.getEncoder();
private static final Base64.Encoder URL_SAFE = Base64.getUrlEncoder().withoutPadding();
private static final Base64.Encoder MIME = Base64.getMimeEncoder();
public static String encodeForHeader(String value) {
return STANDARD.encodeToString(value.getBytes(StandardCharsets.UTF_8));
}
public static String encodeForUrl(byte[] data) {
return URL_SAFE.encodeToString(data);
}
public static String encodeForEmail(byte[] attachment) {
return MIME.encodeToString(attachment);
}
}이 패턴은 여러 컨트롤러나 서비스 메서드에서 인코딩을 처리하는 유틸리티 클래스가 있는 Spring Boot 서비스에서 특히 유용합니다. withoutPadding() 호출은 새로운 인코더 인스턴스를 반환하므로 패딩 있는 버전과 없는 버전을 각각 필드에 저장할 수 있습니다.encodeToString() 또는 encode() 호출은 상태가 없습니다 — 동기화가 필요 없고, 공유되는 가변 상태도 없습니다.
흔한 실수
문제: charset 인자 없이 String.getBytes()를 사용하면 플랫폼의 기본 인코딩이 사용됩니다. Windows에서는 windows-1252, 대부분의 Linux 시스템에서는 UTF-8, macOS에서는 다를 수 있습니다. 동일한 코드가 다른 머신에서 서로 다른 Base64 출력을 생성합니다.
해결책: 항상 StandardCharsets.UTF_8을 명시적으로 전달하세요.
String text = "Ключ доступа: prod-east"; byte[] bytes = text.getBytes(); // 플랫폼 기본값 — 예측 불가 String encoded = Base64.getEncoder().encodeToString(bytes);
String text = "Ключ доступа: prod-east"; byte[] bytes = text.getBytes(StandardCharsets.UTF_8); String encoded = Base64.getEncoder().encodeToString(bytes);
문제: Base64.getEncoder()는 + 와 / 문자를 출력합니다. URL 쿼리 문자열에 포함되면 +는 공백으로, /는 경로 구분자로 해석되어 수신 측에서 값이 자동으로 손상됩니다.
해결책: URL에 나타나는 값에는 Base64.getUrlEncoder()를 사용하세요.
// URL 쿼리 파라미터의 토큰 — 작동하지 않습니다
String token = Base64.getEncoder()
.encodeToString(sessionData);
String url = "https://auth.internal/verify?token=" + token;// URL 안전 인코딩 — + 또는 / 문자 없음
String token = Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(sessionData);
String url = "https://auth.internal/verify?token=" + token;문제: getUrlEncoder()로 인코딩하고 getDecoder()로 디코딩(또는 그 반대)하면 IllegalArgumentException이 발생합니다. - 와 _ 는 표준 Base64 알파벳에서 유효하지 않고, + 와 / 는 URL 안전 알파벳에서 유효하지 않기 때문입니다.
해결책: 항상 일치하는 디코더를 사용하세요: URL 안전은 getUrlDecoder(), 표준은 getDecoder().
String encoded = Base64.getUrlEncoder()
.encodeToString(data);
// 나중에...
byte[] decoded = Base64.getDecoder() // 잘못된 디코더
.decode(encoded);
// - 또는 _ 가 포함된 경우 IllegalArgumentException 발생String encoded = Base64.getUrlEncoder()
.encodeToString(data);
// 나중에...
byte[] decoded = Base64.getUrlDecoder() // 일치하는 디코더
.decode(encoded);문제: 스트리밍 인코더는 완전한 3바이트 그룹을 기다리며 최대 2바이트를 버퍼링합니다. 래핑된 OutputStream을 닫지 않으면 최종 1~4개의 Base64 문자(패딩 포함)가 기록되지 않습니다.
해결책: try-with-resources를 사용하거나, 출력을 읽기 전에 래핑된 스트림에서 close()를 명시적으로 호출하세요.
ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputStream b64os = Base64.getEncoder().wrap(baos); b64os.write(data); // baos.toString()은 불완전합니다 — 마지막 바이트가 누락됨
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (OutputStream b64os = Base64.getEncoder().wrap(baos)) {
b64os.write(data);
} // close()가 최종 패딩을 플러시합니다
String encoded = baos.toString(); // 완전한 결과Base64 인코딩 방법 비교
대부분의 프로젝트에서 java.util.Base64 가 올바른 선택입니다. 의존성이 없고, JDK에 내장되어 있으며, 스레드 안전하고, RFC 4648의 세 가지 변형을 모두 지원합니다. Apache Commons Codec은 이미 클래스패스에 있고 isBase64() 유효성 검사 메서드나 스트리밍 Base64OutputStream이 필요한 경우에만 선택하세요. Guava의 BaseEncoding 은 이미 Guava를 의존하는 프로젝트라면 합리적인 선택이지만, Base64만을 위해 3 MB 의존성을 추가하는 것은 정당화하기 어렵습니다.
세 가지 시나리오, 세 가지 선택: Basic Auth나 JWT 인코딩이 필요한 표준 웹 서비스? JDK를 사용하세요. Spring이나 Apache HTTP Client를 통해 이미 Commons Codec을 가져오는 레거시 프로젝트? 그것을 사용하세요 — 클래스패스에 두 개의 Base64 라이브러리를 둘 이유가 없습니다. 캐싱과 컬렉션을 위해 Guava를 사용하는 프로젝트? 깔끔한 유창한 API를 위해 BaseEncoding 을 사용하세요. Base64 인코딩만을 위해 라이브러리를 추가하지 마세요 — JDK 버전은 2014년부터 충분히 좋습니다.
인코딩 결과를 Java 코드를 실행하지 않고 빠르게 확인하려면 Base64 인코더 에 붙여넣어 코드가 생성하는 출력과 일치하는지 확인하세요.
자주 묻는 질문
Java에서 String을 Base64로 인코딩하려면 어떻게 하나요?
먼저 getBytes(StandardCharsets.UTF_8)로 문자열을 바이트로 변환한 다음, 바이트 배열을 Base64.getEncoder().encodeToString()에 전달합니다. UTF-8을 명시적으로 지정하는 것이 중요합니다 — charset 인자 없이 getBytes()를 호출하면 플랫폼 기본값이 사용되며, 이는 운영 체제와 JVM 설정에 따라 달라집니다.
import java.util.Base64;
import java.nio.charset.StandardCharsets;
String payload = "grant_type=client_credentials&scope=read:metrics";
String encoded = Base64.getEncoder()
.encodeToString(payload.getBytes(StandardCharsets.UTF_8));
// Z3JhbnRfdHlwZT1jbGllbnRfY3JlZGVudGlhbHMmc2NvcGU9cmVhZDptZXRyaWNzBase64.getEncoder()와 Base64.getUrlEncoder()의 차이점은 무엇인가요?
두 메서드 모두 Base64로 인코딩하지만, getUrlEncoder()는 RFC 4648 섹션 5에 정의된 URL 안전 알파벳을 사용합니다. +를 -로, /를 _로 대체하므로 출력 결과를 percent-encoding 없이 URL과 파일명에 사용할 수 있습니다. 표준 인코더는 URL 쿼리 파라미터 및 경로 세그먼트와 충돌하는 +와 /를 사용합니다.
byte[] data = "subject=usr_7b3c&role=admin".getBytes(StandardCharsets.UTF_8); String standard = Base64.getEncoder().encodeToString(data); // c3ViamVjdD11c3JfN2IzYyZyb2xlPWFkbWlu String urlSafe = Base64.getUrlEncoder().encodeToString(data); // c3ViamVjdD11c3JfN2IzYyZyb2xlPWFkbWlu // (여기서는 같지만, + → - 와 / → _ 로 치환됩니다)
java.util.Base64는 Java 8과 Java 17에서 동일한가요?
네. java.util.Base64 API는 Java 8에서 도입된 이후 변경된 적이 없습니다. 클래스, 중첩된 Encoder 및 Decoder 클래스, 그리고 모든 팩토리 메서드(getEncoder, getUrlEncoder, getMimeEncoder)는 Java 8, 11, 17, 21에서 동일합니다. JDK 버전을 업그레이드할 때 마이그레이션이나 코드 변경이 필요하지 않습니다.
// 이 코드는 Java 8부터 Java 21+까지 동일하게 컴파일되고 실행됩니다
import java.util.Base64;
import java.nio.charset.StandardCharsets;
String encoded = Base64.getEncoder()
.encodeToString("stable-api".getBytes(StandardCharsets.UTF_8));
System.out.println(encoded); // c3RhYmxlLWFwaQ==Java에서 파일을 Base64로 인코딩하려면 어떻게 하나요?
Files.readAllBytes(Path)로 파일을 바이트 배열로 읽어 Base64.getEncoder().encodeToString()에 전달합니다. 전체 파일을 메모리에 로드하지 않아야 하는 대용량 파일의 경우, Base64.getEncoder().wrap(OutputStream)을 사용하여 인코딩된 출력을 스트리밍합니다.
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
byte[] fileBytes = Files.readAllBytes(Path.of("config/tls-cert.pem"));
String encoded = Base64.getEncoder().encodeToString(fileBytes);sun.misc.BASE64Encoder가 deprecated된 이유는 무엇인가요?
sun.misc.BASE64Encoder는 공개 API의 일부가 아닌 내부 JDK 클래스였습니다. Oracle이 명시적으로 사용을 경고한 sun.misc 패키지에 속해 있었습니다. Java 8은 공식적이고 공개적으로 지원되는 대체재로 java.util.Base64를 도입했습니다. Java 9와 모듈 시스템부터는 sun.misc 클래스에 접근하면 JDK 구성에 따라 경고 또는 오류가 발생합니다.
// 구버전 방식 — 사용하지 마세요, 최신 JDK에서 제거되었습니다 // import sun.misc.BASE64Encoder; // String encoded = new BASE64Encoder().encode(data); // Java 8 이후 올바른 방식 import java.util.Base64; String encoded = Base64.getEncoder().encodeToString(data);
Java에서 Base64 인코딩과 디코딩을 왕복 처리하려면 어떻게 하나요?
Base64.getEncoder().encodeToString(bytes)로 인코딩하고, Base64.getDecoder().decode(encodedString)로 디코딩합니다. 디코딩된 바이트 배열을 new String(bytes, StandardCharsets.UTF_8)로 다시 String으로 변환합니다. getBytes()와 new String() 모두에서 동일한 charset을 사용하는 한 원본 데이터가 정확히 보존됩니다.
import java.util.Base64; import java.nio.charset.StandardCharsets; // 인코딩 String original = "session_token=eyJhbGciOiJSUzI1NiJ9"; byte[] originalBytes = original.getBytes(StandardCharsets.UTF_8); String encoded = Base64.getEncoder().encodeToString(originalBytes); // 디코딩 byte[] decodedBytes = Base64.getDecoder().decode(encoded); String decoded = new String(decodedBytes, StandardCharsets.UTF_8); System.out.println(original.equals(decoded)); // true
관련 도구
- Base64 Decoder — Base64 문자열을 원본 텍스트 또는 이진 형식으로 디코딩합니다 — 인코딩의 역방향 연산입니다.
- URL Encoder — URL에서 안전하게 사용할 수 있도록 문자열을 percent-encoding합니다 — Base64 URL 안전 인코딩과는 다르지만 함께 사용되는 경우가 많습니다.
- JWT Decoder — 헤더와 페이로드 세그먼트가 Base64url로 인코딩된 JSON인 JWT 토큰을 검사합니다 — 라이브러리 없이 디코딩할 수 있습니다.
- JSON Formatter — Base64 인코딩 전후에 JSON 페이로드를 보기 좋게 출력합니다 — API 통합 디버깅 시 유용합니다.
Aisha is a Java engineer specialising in application security, Spring Security, and API design. She has worked on identity and access management systems, OAuth 2.0 integrations, and microservice security at scale. She writes about secure Java coding practices, token validation, cryptographic utilities, and the Spring ecosystem from a security-first perspective.
Pavel is a backend engineer with deep roots in the JVM ecosystem, working primarily with Java and Kotlin. He has extensive experience building data-intensive services and integrating third-party APIs at scale. He writes about modern Java features, the Jackson ecosystem, serialisation patterns, and practical approaches to keeping large codebases maintainable.