Java Base64 디코딩 — getDecoder()

·Backend Engineer·검토자Aisha Osei·게시일

무료 Base64 디코더을 브라우저에서 직접 사용하세요 — 설치 불필요.

Base64 디코더 온라인으로 사용하기 →

Java에서 Base64 디코딩은 며칠에 한 번씩 손이 가는 작업입니다 — Kubernetes 환경 변수에서 시크릿을 추출하거나, REST API의 바이너리 페이로드를 읽거나, 디버깅 중에 JWT 토큰을 확인할 때 필요합니다. Java의 내장 java.util.Base64 클래스(JDK 8부터 제공)는 세 가지 디코더를 제공합니다: 표준 Base64용 getDecoder(), URL 안전 입력용 getUrlDecoder(), 이메일 첨부파일 같은 줄 바꿈 데이터용 getMimeDecoder(). 코드를 작성하지 않고 빠르게 확인하고 싶다면, ToolDeck의 Base64 디코더 를 사용하면 브라우저에서 즉시 처리할 수 있습니다. 이 가이드는 Java 8 이상을 대상으로 하며, 세 가지 디코더 전체, wrap(InputStream)을 이용한 스트리밍, JWT 페이로드 추출, 파일 및 API 응답 디코딩, Apache Commons Codec 대안, 그리고 프로덕션에서 잘못된 출력을 유발하는 네 가지 실수를 다룹니다.

  • Base64.getDecoder().decode(s)가 표준 방식입니다 — JDK 8부터 java.util.Base64에 내장되어 있으며 별도 의존성이 필요 없습니다.
  • JWT 토큰과 OAuth 페이로드에는 getUrlDecoder()를 사용하세요 — + 및 / 대신 - 및 _ 알파벳을 사용합니다.
  • getMimeDecoder()는 줄바꿈과 공백을 무시하므로 이메일 첨부파일과 PEM 인증서에 적합합니다.
  • decoder.wrap(InputStream)은 대용량 파일을 전부 메모리에 로드하지 않고 실시간으로 디코딩합니다.
  • 기본 디코더는 엄격합니다 — 뒤따르는 개행 문자, 공백, 또는 잘못된 알파벳 문자가 있으면 즉시 IllegalArgumentException을 던집니다.

Base64 디코딩이란?

Base64 인코딩은 바이너리 데이터를 64개의 ASCII 문자 표현으로 변환해 텍스트 전용 채널 — JSON 필드, HTTP 헤더, XML 문서, 이메일 본문 — 을 통해 안전하게 전송할 수 있도록 합니다. 디코딩은 이를 역으로 수행합니다: Base64 문자 4개가 원본 바이트 3개로 복원됩니다. 끝의 = 패딩은 마지막 그룹을 채우기 위해 추가된 바이트 수를 나타냅니다. Base64는 암호화가 아닙니다 — 누구나 역변환할 수 있습니다. 목적은 전송 안전성이지 기밀성이 아닙니다.

Java에서 일반적인 디코딩 시나리오: Base64 환경 변수로 주입된 설정 값 추출, 클라우드 API 응답에서 바이너리 파일 내용 언패킹, PEM 인코딩 인증서 읽기, 디버깅 중 JWT 토큰 페이로드 확인.

Before · text
After · text
ZGItcHJvZC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbTo1NDMy
db-prod.us-east-1.amazonaws.com:5432

Base64.getDecoder().decode() — 표준 디코딩 방법

java.util.Base64 클래스는 JDK 8에 추가되어 이전에 많이 사용하던 sun.misc.BASE64Decoder를 대체했습니다. 외부 의존성이 필요 없습니다 —import java.util.Base64만 하고 Base64.getDecoder().decode()를 호출하면 됩니다. 이 메서드는 String 또는 byte[]를 받아 디코딩된 데이터의 byte[]를 반환합니다.

최소 동작 예제

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class DecodeCredential {
    public static void main(String[] args) {
        // Kubernetes secret value, Base64-encoded
        String encoded = "ZGItcHJvZC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbTo1NDMy";

        byte[] decodedBytes = Base64.getDecoder().decode(encoded);
        String connectionString = new String(decodedBytes, StandardCharsets.UTF_8);

        System.out.println(connectionString);
        // db-prod.us-east-1.amazonaws.com:5432
    }
}

String을 생성할 때는 항상 StandardCharsets.UTF_8을 지정하세요. 인자 없는 new String(bytes) 생성자는 플랫폼 기본 인코딩을 사용하는데, 시스템마다 다를 수 있습니다. 기본 인코딩이 Cp1252인 Windows 서버에서는 멀티바이트 UTF-8 문자가 자동으로 손상됩니다.

왕복 검증

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class RoundTrip {
    public static void main(String[] args) {
        String original = "redis://cache-prod.internal:6379/session-store";

        String encoded = Base64.getEncoder().encodeToString(
            original.getBytes(StandardCharsets.UTF_8)
        );
        System.out.println(encoded);
        // cmVkaXM6Ly9jYWNoZS1wcm9kLmludGVybmFsOjYzNzkvc2Vzc2lvbi1zdG9yZQ==

        byte[] decoded = Base64.getDecoder().decode(encoded);
        String recovered = new String(decoded, StandardCharsets.UTF_8);

        System.out.println(recovered.equals(original)); // true
    }
}

사전 할당 버퍼에 디코딩

세 인자를 받는 decode(byte[] src, byte[] dst) 오버로드는 목적 버퍼에 직접 쓰고 쓰여진 바이트 수를 반환합니다. 핫 경로에서 불필요한 할당을 피할 수 있습니다:

Java 8+
import java.util.Base64;

public class DecodeToBuffer {
    public static void main(String[] args) {
        byte[] src = "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=".getBytes();
        byte[] dst = new byte[1024]; // pre-allocated

        int len = Base64.getDecoder().decode(src, dst);
        String result = new String(dst, 0, len);

        System.out.println(result);
        // {"host":"10.0.1.50","port":8443}
    }
}
참고:decode()는 입력에 Base64 알파벳 외의 문자(줄바꿈, 공백 포함)가 있으면 IllegalArgumentException을 던집니다. 입력에 공백이 포함될 수 있다면 getMimeDecoder()로 전환하거나 디코딩 전에 encoded.strip()으로 제거하세요.

비표준 타입 및 커스텀 객체의 Base64 디코딩

decode()에서 반환된 원시 byte[]는 종종 더 구체적인 타입으로 변환해야 합니다: UUID, 직렬화된 Java 객체, protobuf 메시지, 또는 타임스탬프. 디코더 자체는 항상 바이트를 반환하며, 도메인 타입으로의 변환은 개발자의 책임입니다.

Base64에서 UUID로

Java 8+
import java.util.Base64;
import java.nio.ByteBuffer;
import java.util.UUID;

public class DecodeUUID {
    public static UUID fromBase64(String encoded) {
        byte[] bytes = Base64.getUrlDecoder().decode(encoded);
        ByteBuffer bb = ByteBuffer.wrap(bytes);
        return new UUID(bb.getLong(), bb.getLong());
    }

    public static void main(String[] args) {
        // Compact Base64-encoded UUID from an API response
        String encoded = "f47ac10b-58cc-4372-a567-0e02b2c3d479";
        UUID original = UUID.fromString(encoded);

        // Encode to Base64 (compact form — 22 chars vs 36)
        ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
        bb.putLong(original.getMostSignificantBits());
        bb.putLong(original.getLeastSignificantBits());
        String compact = Base64.getUrlEncoder().withoutPadding()
            .encodeToString(bb.array());
        System.out.println(compact); // 9HrBC1jMQ3KlZw4CssPUeQ

        // Decode back
        UUID recovered = fromBase64(compact);
        System.out.println(recovered); // f47ac10b-58cc-4372-a567-0e02b2c3d479
    }
}

Jackson으로 Base64를 역직렬화된 JSON 객체로

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.Instant;

public class DecodeJsonPayload {
    record DeployEvent(String service, String region, Instant deployedAt, int replicas) {}

    public static void main(String[] args) throws Exception {
        // Base64-encoded JSON payload from a message queue
        String encoded = "eyJzZXJ2aWNlIjoicGF5bWVudC1nYXRld2F5Iiwi"
            + "cmVnaW9uIjoiZXUtd2VzdC0xIiwiZGVwbG95ZWRBdCI6"
            + "IjIwMjYtMDMtMTVUMTQ6MzA6MDBaIiwicmVwbGljYXMiOjR9";

        byte[] jsonBytes = Base64.getDecoder().decode(encoded);
        String json = new String(jsonBytes, StandardCharsets.UTF_8);
        System.out.println(json);
        // {"service":"payment-gateway","region":"eu-west-1",
        //  "deployedAt":"2026-03-15T14:30:00Z","replicas":4}

        ObjectMapper mapper = new ObjectMapper();
        mapper.findAndRegisterModules(); // picks up JavaTimeModule
        DeployEvent event = mapper.readValue(jsonBytes, DeployEvent.class);

        System.out.println(event.service());   // payment-gateway
        System.out.println(event.deployedAt()); // 2026-03-15T14:30:00Z
    }
}
경고:신뢰할 수 없는 Base64 데이터를 역직렬화하는 데 ObjectInputStream을 사용하지 마세요. Java 역직렬화 공격은 잘 알려져 있습니다 — 인코딩된 내용이 외부 소스에서 온 경우, 네이티브 Java 직렬화 대신 JSON 또는 protobuf로 파싱하세요.

Base64.Decoder 메서드 레퍼런스

모든 메서드는 java.util.Base64와 내부 클래스 Base64.Decoder에 속합니다. Base64의 세 가지 팩토리 메서드는 각기 다른 디코더 인스턴스를 반환하며, decode()wrap() 메서드는 Decoder 인스턴스에 있습니다.

메서드
반환 타입
입력 타입
설명
getDecoder()
Base64.Decoder
표준 디코더 (RFC 4648 §4, + 및 / 알파벳, = 패딩)
getUrlDecoder()
Base64.Decoder
URL 안전 디코더 (RFC 4648 §5, - 및 _ 알파벳, = 패딩)
getMimeDecoder()
Base64.Decoder
MIME 디코더 — 줄 구분자와 Base64 외 문자를 무시함
decode(String src)
byte[]
String
입력 문자열을 새 바이트 배열로 디코딩
decode(byte[] src)
byte[]
byte[]
입력 바이트 배열을 새 바이트 배열로 디코딩
decode(byte[] src, byte[] dst)
int
byte[] + byte[]
사전 할당된 dst 버퍼에 디코딩하고 쓴 바이트 수를 반환
wrap(InputStream is)
InputStream
InputStream
Base64 데이터를 실시간으로 디코딩하는 스트림을 반환

getMimeDecoder() — 줄 바꿈 및 MIME Base64 디코딩

기본 디코더는 Base64 알파벳 외의 모든 것을 거부합니다 — MIME 인코딩 내용에 항상 포함된 \r\n 줄바꿈도 포함됩니다. 이메일 첨부파일, PEM 인증서, 그리고 일부 구형 API 응답은 Base64 출력을 76자 단위로 줄 바꿈합니다. getMimeDecoder()는 줄 구분자와 Base64 알파벳 외의 문자를 조용히 무시하므로, 이러한 경우를 즉시 처리할 수 있습니다.

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class MimeDecode {
    public static void main(String[] args) {
        // PEM certificate body — line-wrapped at 76 characters
        String pemBody = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t\r\n"
            + "TUlJQm96Q0NBVWlnQXdJQkFnSUpBSXBhVDJU\r\n"
            + "aVFvZU1BMEdDU3FHU0liM0RRRU==";

        // getDecoder() would throw IllegalArgumentException here
        byte[] decoded = Base64.getMimeDecoder().decode(pemBody);
        System.out.println(new String(decoded, StandardCharsets.UTF_8));
        // -----BEGIN CERTIFICATE-----
        // MIIBozCCAUigAwIBAgIJAIpaT2T...
    }
}
참고:getMimeDecoder()는 관대합니다: 예외를 던지는 대신 유효하지 않은 문자를 건너뜁니다. 알려진 MIME 데이터에는 괜찮지만, 임의의 입력에서 데이터 손상을 조용히 삼킬 수 있습니다. 엄격한 유효성 검사가 필요하면 getDecoder()를 사용하세요.

파일 및 API 응답에서 Base64 디코딩

디스크에서 Base64 인코딩 파일 읽기

이미지, 인증서, 암호화된 블롭 같은 바이너리 파일은 때때로 Base64 텍스트로 디스크에 저장됩니다. 파일을 읽고, 디코딩하고, 바이너리 출력을 씁니다:

Java 8+
import java.util.Base64;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class DecodeFile {
    public static void main(String[] args) {
        Path inputPath = Path.of("tls-cert.pem.b64");
        Path outputPath = Path.of("tls-cert.pem");

        try {
            String encoded = Files.readString(inputPath).strip();
            byte[] decoded = Base64.getMimeDecoder().decode(encoded);
            Files.write(outputPath, decoded);

            System.out.printf("Decoded %d bytes → %s%n", decoded.length, outputPath);
        } catch (IOException e) {
            System.err.println("File error: " + e.getMessage());
        } catch (IllegalArgumentException e) {
            System.err.println("Invalid Base64: " + e.getMessage());
        }
    }
}

HTTP API 응답에서 Base64 필드 디코딩

클라우드 API(AWS KMS, GitHub Contents, Vault)는 JSON 내부의 Base64 문자열로 바이너리 데이터를 자주 반환합니다. JSON을 먼저 파싱한 다음 대상 필드를 디코딩합니다:

Java 11+
import java.util.Base64;
import java.nio.charset.StandardCharsets;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class DecodeApiResponse {
    public static void main(String[] args) {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.example.com/secrets/db-password"))
            .header("Authorization", "Bearer sk-prod-9f8e7d6c")
            .build();

        try {
            HttpResponse<String> response = client.send(
                request, HttpResponse.BodyHandlers.ofString());

            if (response.statusCode() != 200) {
                System.err.printf("Unexpected status: %d%n", response.statusCode());
                return;
            }

            ObjectMapper mapper = new ObjectMapper();
            JsonNode root = mapper.readTree(response.body());

            // API returns: {"name":"db-password","value":"cG9zdGdyZXM6eGs5...","version":3}
            String encodedValue = root.get("value").asText();
            byte[] decoded = Base64.getDecoder().decode(encodedValue);
            String secret = new String(decoded, StandardCharsets.UTF_8);

            System.out.println("Secret: " + secret);
            // Secret: postgres:xk9mP2qR@db-prod:5432/orders
        } catch (Exception e) {
            System.err.println("Failed to fetch secret: " + e.getMessage());
        }
    }
}
참고:디코딩 호출을 네트워크 오류와 별도로 IllegalArgumentException try-catch로 감싸세요. I/O 예외와 디코딩 실패를 혼용하면 디버깅이 어려워집니다 — API가 잘못된 데이터를 반환했는지, 아니면 네트워크가 실패했는지 즉시 파악할 수 있어야 합니다.

커맨드라인에서 Base64 디코딩

Java 프로그램이 항상 필요한 것은 아닙니다. Linux와 macOS 시스템에는 base64 커맨드가 있으며, JDK 9+에는 인터랙티브 Java 한 줄 실행을 위한 jshell이 포함되어 있습니다. 디버깅 중 빠른 확인에는 클래스를 컴파일하는 것보다 이 방법이 훨씬 빠릅니다.

bash
# Decode a Base64 string (Linux / macOS)
echo "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=" | base64 --decode
# {"host":"10.0.1.50","port":8443}

# Decode and pretty-print with jq
echo "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=" | base64 --decode | jq .
# {
#   "host": "10.0.1.50",
#   "port": 8443
# }

# Quick decode with jshell (JDK 9+)
echo 'System.out.println(new String(java.util.Base64.getDecoder().decode("c2VydmVyLWNvbmZpZw==")))' | jshell -
# server-config

# macOS uses -D instead of --decode
echo "c2VydmVyLWNvbmZpZw==" | base64 -D

인코딩된 문자열을 브라우저에 직접 붙여넣으려면, ToolDeck의 Base64 디코더 가 별도 설정 없이 표준 및 URL 안전 방식을 모두 처리합니다.

고성능 대안: Apache Commons Codec

Java 내장 java.util.Base64는 이미 잘 최적화되어 있습니다 — JDK 11+는 인코딩 및 디코딩에 x86 인트린식을 사용합니다. 대부분의 애플리케이션에서는 서드파티 라이브러리가 필요하지 않습니다. 다만, Apache Commons Codec은 레거시 코드베이스에서 여전히 널리 사용되며, 자동 공백 처리를 지원하는 스트리밍 디코딩용 Base64InputStream을 제공합니다.

XML (Maven)
<!-- pom.xml -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.17.0</version>
</dependency>
Java 8+
import org.apache.commons.codec.binary.Base64;

public class CommonsCodecDecode {
    public static void main(String[] args) {
        // Commons Codec is more lenient — handles whitespace and line breaks
        String encoded = "eyJob3N0IjoiMTAuMC4xLjUw\nIiwicG9ydCI6ODQ0M30=";
        byte[] decoded = Base64.decodeBase64(encoded);

        System.out.println(new String(decoded));
        // {"host":"10.0.1.50","port":8443}
    }
}

Commons Codec이 내장 API보다 유리한 점은 기본적으로 공백에 관대하다는 것과, Java의 decoder.wrap()보다 먼저 등장한 Base64InputStream 클래스를 제공한다는 것입니다. Java 8 이상이라면 내장 API로 Commons Codec이 하는 모든 것을 처리할 수 있습니다. 프로젝트가 이미 Commons Codec에 의존하는 경우에만 사용합니다.

decoder.wrap()으로 대용량 Base64 파일 스트리밍

200MB Base64 파일을 Files.readString()으로 불러온 뒤 decode()를 호출하면 힙에 약 350MB가 할당됩니다: 인코딩된 문자열과 디코딩된 바이트 배열이 합산됩니다. decoder.wrap(InputStream)은 실시간으로 디코딩하여 메모리 사용량을 일정하게 유지합니다.

Java 8+
import java.util.Base64;
import java.io.*;
import java.nio.file.*;

public class StreamDecode {
    public static void main(String[] args) throws IOException {
        Path src = Path.of("database-dump.sql.b64");
        Path dst = Path.of("database-dump.sql");

        try (InputStream in = Base64.getMimeDecoder().wrap(
                 new BufferedInputStream(Files.newInputStream(src)));
             OutputStream out = new BufferedOutputStream(Files.newOutputStream(dst))) {

            byte[] buffer = new byte[8192];
            int bytesRead;
            long total = 0;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
                total += bytesRead;
            }
            System.out.printf("Decoded %d bytes → %s%n", total, dst);
        }
    }
}

Java 9 이상에서는 읽기 루프를 in.transferTo(out)으로 대체할 수 있습니다 — 코드가 더 간결해집니다. 파일에 줄바꿈이 포함될 수 있다면(PEM 파일, 이메일 내보내기) getDecoder().wrap() 대신 getMimeDecoder().wrap()을 사용하세요.

Java 9+
import java.util.Base64;
import java.io.*;
import java.nio.file.*;

public class StreamDecodeSimple {
    public static void main(String[] args) throws IOException {
        try (InputStream in = Base64.getMimeDecoder().wrap(
                 new BufferedInputStream(Files.newInputStream(Path.of("backup.tar.b64"))));
             OutputStream out = Files.newOutputStream(Path.of("backup.tar"))) {
            in.transferTo(out); // Java 9+
        }
    }
}
경고:getDecoder().wrap()은 스트림의 줄바꿈을 허용하지 않습니다. Base64 데이터에 개행 문자가 있다면(76자 단위로 줄 바꿈), 대신 getMimeDecoder().wrap()을 사용하세요 — 그렇지 않으면 스트림이 손상된 출력을 조용히 생성하거나 예측 불가능한 읽기 위치에서 예외를 던집니다.

JWT 라이브러리 없이 Java에서 Base64 JWT 페이로드 디코딩

JWT는 점으로 연결된 세 개의 Base64url 인코딩 세그먼트로 구성됩니다. 가운데 세그먼트가 페이로드 — 디버깅 시 필요한 부분입니다. jjwt나 Nimbus를 끌어오지 않고도 디코딩할 수 있습니다. .으로 분할하고, 두 번째 부분을 getUrlDecoder()로 디코딩하고, 결과 JSON을 파싱합니다:

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class JWTInspect {
    public static String decodeJwtPayload(String token) {
        String[] parts = token.split("\\.");
        if (parts.length != 3) {
            throw new IllegalArgumentException(
                "Invalid JWT: expected 3 segments, got " + parts.length);
        }
        // JWT uses URL-safe Base64 without padding
        byte[] payload = Base64.getUrlDecoder().decode(parts[1]);
        return new String(payload, StandardCharsets.UTF_8);
    }

    public static void main(String[] args) {
        String token = "eyJhbGciOiJSUzI1NiJ9"
            + ".eyJzdWIiOiJ1c3ItNjcyIiwiaXNzIjoiYXV0aC5leGFtcGxlLmNvbSIs"
            + "ImV4cCI6MTc0MTk1NjgwMCwicm9sZXMiOlsiYWRtaW4iLCJiaWxsaW5nIl19"
            + ".SIGNATURE_PLACEHOLDER";

        String payload = decodeJwtPayload(token);
        System.out.println(payload);
        // {"sub":"usr-672","iss":"auth.example.com",
        //  "exp":1741956800,"roles":["admin","billing"]}
    }
}

흔한 실수

코드 리뷰에서 이 모든 실수를 직접 목격했으며, 첫 번째와 두 번째가 Java 서비스에서 발생하는 Base64 관련 프로덕션 버그의 대다수를 차지합니다.

URL 안전 입력에 getDecoder() 사용

문제: JWT 토큰과 OAuth 액세스 토큰은 URL 안전 알파벳(- 및 _)을 사용합니다. getDecoder()에 전달하면 -가 표준 Base64 알파벳에 없기 때문에 IllegalArgumentException이 발생합니다.

해결책: 데이터 소스를 확인하세요: 인증 시스템의 토큰은 getUrlDecoder()가 필요하고, MIME 첨부파일은 getMimeDecoder()가 필요합니다.

Before · Java
After · Java
// JWT header — URL-safe, no padding
String header = "eyJhbGciOiJSUzI1NiJ9";
byte[] decoded = Base64.getDecoder().decode(header);
// IllegalArgumentException: Illegal base64 character 2d
String header = "eyJhbGciOiJSUzI1NiJ9";
byte[] decoded = Base64.getUrlDecoder().decode(header);
System.out.println(new String(decoded));
// {"alg":"RS256"}
String 생성 시 문자셋 미지정

문제: new String(bytes)는 JVM 기본 문자셋을 사용하는데, 환경마다 다릅니다. Linux CI 서버(UTF-8)와 Windows 프로덕션 호스트(Cp1252)는 동일한 바이트에 대해 다른 결과를 생성합니다.

해결책: 항상 두 번째 인자로 StandardCharsets.UTF_8을 전달하세요.

Before · Java
After · Java
byte[] decoded = Base64.getDecoder().decode(encoded);
String result = new String(decoded);
// platform-dependent — may corrupt multi-byte characters
byte[] decoded = Base64.getDecoder().decode(encoded);
String result = new String(decoded, StandardCharsets.UTF_8);
// consistent across all platforms
뒤따르는 공백이 있는 문자열 디코딩

문제: 터미널에서 붙여넣거나 설정 파일에서 읽은 Base64 문자열에는 종종 뒤따르는 개행 문자가 있습니다. 기본 디코더는 Base64 알파벳 외의 모든 문자를 거부합니다.

해결책: 디코딩 전에 입력에 .strip()을 호출하거나, 공백을 무시하는 getMimeDecoder()로 전환하세요.

Before · Java
After · Java
// Read from environment variable — has a trailing newline
String encoded = System.getenv("DB_PASSWORD_B64"); // "cG9zdGdyZXM=
"
byte[] decoded = Base64.getDecoder().decode(encoded);
// IllegalArgumentException: Illegal base64 character a
String encoded = System.getenv("DB_PASSWORD_B64");
byte[] decoded = Base64.getDecoder().decode(encoded.strip());
System.out.println(new String(decoded, StandardCharsets.UTF_8));
// postgres
바이너리 데이터를 String으로 변환

문제: 바이너리 내용(이미지, protobuf, 암호화된 블롭)에 new String(decoded)을 호출하면 유효하지 않은 String이 생성됩니다. String 생성자가 유효하지 않은 UTF-8 시퀀스를 대체하기 때문에 나중에 바이트로 다시 변환하면 데이터가 조용히 손상됩니다.

해결책: 바이너리 데이터는 전체 파이프라인에서 byte[]로 유지하세요. 내용이 텍스트임을 알 때만 String으로 변환하세요.

Before · Java
After · Java
byte[] decoded = Base64.getDecoder().decode(pngBase64);
String imageStr = new String(decoded); // corrupts binary
Files.writeString(Path.of("image.png"), imageStr); // broken file
byte[] decoded = Base64.getDecoder().decode(pngBase64);
// Write bytes directly — no String conversion
Files.write(Path.of("image.png"), decoded);

메서드 비교

내장 디코더가 대부분의 사용 사례를 커버합니다. Apache Commons Codec과 Guava는 오래된 코드베이스에서 만날 수 있는 대안입니다.

메서드
인코딩 방식
공백 무시
스트리밍
커스텀 타입
별도 설치
Base64.getDecoder()
표준 (+, /)
불필요 (JDK 8+)
Base64.getUrlDecoder()
URL 안전 (-, _)
불필요 (JDK 8+)
Base64.getMimeDecoder()
MIME (줄바꿈 허용)
불필요 (JDK 8+)
decoder.wrap(InputStream)
모든 방식
디코더에 따라 다름
불필요 (JDK 8+)
Apache Commons Base64InputStream
표준 / URL 안전
필요 (commons-codec)
Apache Commons Base64.decodeBase64()
표준
필요 (commons-codec)
Guava BaseEncoding.base64().decode()
표준
필요 (guava)

JWT 토큰과 최신 API 페이로드에는: getUrlDecoder(). 이메일 첨부파일과 PEM 인증서에는: getMimeDecoder(). 메모리가 중요한 대용량 파일에는: decoder.wrap(InputStream). 그 외 모든 경우에는: getDecoder(). Apache Commons Codec은 이미 의존성 트리에 포함된 경우에만 사용하는 것이 적합합니다.

개발 중 빠른 검증에는, 온라인 Base64 디코더 가 일회용 클래스를 작성하는 것보다 빠릅니다.

자주 묻는 질문

Java에서 Base64 문자열을 디코딩하는 방법은?

java.util.Base64를 임포트하고 Base64.getDecoder().decode(encodedString)을 호출합니다. byte[]가 반환되며, 텍스트로 읽으려면 new String(bytes, StandardCharsets.UTF_8)로 감싸면 됩니다. JWT 등에서 사용되는 URL 안전 Base64의 경우 getDecoder() 대신 getUrlDecoder()를 사용하세요.

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

byte[] decoded = Base64.getDecoder().decode("c2VydmVyLWNvbmZpZw==");
String result = new String(decoded, StandardCharsets.UTF_8);
System.out.println(result); // server-config

Java에서 getDecoder()와 getMimeDecoder()의 차이는 무엇인가요?

getDecoder()는 엄격한 방식으로, 줄바꿈을 포함한 Base64 알파벳 외의 문자를 모두 거부합니다. getMimeDecoder()는 줄 구분자(\r\n)를 허용하고 Base64 외 문자를 무시하므로, 76자 단위로 줄 바꿈된 이메일 첨부파일이나 PEM 인증서 디코딩에 적합합니다.

Java 8+
String wrapped = "c2VydmVyLWNv\r\nbmZpZw==";

// getDecoder() throws IllegalArgumentException
// Base64.getDecoder().decode(wrapped); // FAILS

// getMimeDecoder() handles it
byte[] decoded = Base64.getMimeDecoder().decode(wrapped);
System.out.println(new String(decoded)); // server-config

Java에서 URL 안전 Base64 문자열을 디코딩하는 방법은?

Base64.getUrlDecoder().decode(encoded)를 사용합니다. URL 디코더는 + 및 / 대신 RFC 4648 §5에 정의된 - 및 _ 알파벳을 사용합니다. JWT 토큰은 항상 이 알파벳을 사용합니다. 패딩 문자(=)가 제거된 경우(JWT에서 흔함)에도 Java의 URL 디코더는 패딩이 있는 경우와 없는 경우를 모두 처리합니다.

Java 8+
import java.util.Base64;

// JWT header — URL-safe, no padding
String jwtHeader = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
byte[] decoded = Base64.getUrlDecoder().decode(jwtHeader);
System.out.println(new String(decoded));
// {"alg":"HS256","typ":"JWT"}

Java에서 대용량 Base64 파일을 스트리밍으로 디코딩하는 방법은?

decoder.wrap(inputStream)을 사용해 FileInputStream을 감싸면 됩니다. 반환된 InputStream은 바이트를 읽는 동안 실시간으로 Base64를 디코딩하므로, 파일 크기에 관계없이 메모리 사용량이 일정하게 유지됩니다. BufferedInputStream을 통해 파이프하거나 Files.copy()로 직접 연결하면 처리량을 극대화할 수 있습니다.

Java 8+
import java.util.Base64;
import java.io.*;
import java.nio.file.*;

try (InputStream in = Base64.getDecoder().wrap(
        new BufferedInputStream(new FileInputStream("payload.b64")));
     OutputStream out = new FileOutputStream("payload.bin")) {
    in.transferTo(out);
}

Base64.getDecoder().decode()가 IllegalArgumentException을 던지는 이유는?

기본 디코더는 엄격합니다. A-Za-z0-9+/= 외의 문자, 줄바꿈, 공백을 모두 거부합니다. 흔한 원인 세 가지: 입력 끝에 개행 문자가 있는 경우(strip() 사용), - 및 _ 같은 URL 안전 문자가 포함된 경우(getUrlDecoder()로 전환), 76자 단위로 줄 바꿈된 경우(getMimeDecoder()로 전환). 오류 메시지가 불분명하면 원시 바이트를 직접 확인하세요.

Java 8+
String raw = "c2VydmVyLWNvbmZpZw==\n"; // trailing newline

// Option 1: trim whitespace
byte[] decoded = Base64.getDecoder().decode(raw.strip());

// Option 2: use MIME decoder which ignores whitespace
byte[] decoded2 = Base64.getMimeDecoder().decode(raw);

Java에서 java.util.Base64 없이 Base64를 디코딩할 수 있나요?

가능하지만, Java 8 이상에서는 그렇게 할 이유가 없습니다. Java 8 이전에는 sun.misc.BASE64Decoder(내부 API, Java 9+에서 제거), javax.xml.bind.DatatypeConverter.parseBase64Binary()(Java 11에서 제거), 또는 Apache Commons Codec을 사용했습니다. 세 가지 모두 deprecated되었거나 추가 의존성이 필요합니다. java.util.Base64를 사용하세요 — 더 빠르고, JDK에 포함되어 있으며, 기본·URL 안전·MIME 세 가지 방식을 모두 지원합니다.

관련 도구

  • Base64 인코더 — 브라우저에서 텍스트나 바이너리 데이터를 Base64로 인코딩합니다. Java 단위 테스트에 붙여넣을 테스트 픽스처를 생성할 때 편리합니다.
  • JWT 디코더 — JWT의 세 세그먼트를 한 번에 분할하고 디코딩하며, 필드별 페이로드 검사를 제공합니다 — 토큰을 읽기만 할 때 Java 클래스를 작성하는 것보다 빠릅니다.
  • URL 디코더 — 퍼센트 인코딩된 URL 문자열을 디코딩합니다. API 응답이 Base64url 데이터와 퍼센트 인코딩된 쿼리 파라미터를 함께 사용할 때 유용합니다.
  • JSON 포매터 — Base64 JWT 페이로드나 API 설정을 디코딩한 후, JSON을 여기에 붙여넣어 구조를 예쁘게 출력하고 검증하세요.
다른 언어로도 제공됩니다:JavaScriptPythonGoC#
PN
Pavel NovakBackend Engineer

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.

AO
Aisha Osei기술 검토자

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.