IRONSOFTWAREHOME
다른 구성 요소와 비교

ABBYY FineReader와 Tesseract의 OCR 비교

칸나오팟 우돈판트
Kannapat Udonpant
Updated: 2026년 6월 28일

OCR.space에는 NuGet 패키지가 없습니다. 그 단일 사실은 .NET 개발자가 그것을 선택할 때 내리는 모든 통합 결정을 정의합니다: 사용자 정의 HttpClient를 작성하고, 문서를 base64로 인코딩하여 https://api.ocr.space/parse/image에 POST하고, JSON 응답을 수동으로 구문 분석하고, HTTP 429를 잡고, 지수 백오프를 구현하고, 속도 제한 부기 관리를 구축하고, 예외 유형을 정의합니다 - 이 모든 것을 애플리케이션에 한 글자도 들어오기 전에 완료해야 합니다. 이 글에서는 DIY 방식의 오버헤드가 코드, 시간, 그리고 프로덕션 안정성 측면에서 실제로 얼마나 큰 비용을 초래하는지 살펴보고, 단일 NuGet 패키지로 제공되는 네이티브 .NET 라이브러리인 IronOCR 과 직접 비교합니다.

OCR.space 이해하기

OCR.space는 OCR.space에서 운영하는 원격 서버에서 이미지와 PDF를 처리하는 프리미엄 REST API입니다. 이 서비스는 무료 요금제를 중심으로 마케팅 전략을 펼치고 있습니다. 무료 요금제는 신용카드 정보 없이 월 25,000건의 요청을 처리할 수 있으며, OCR을 실험하거나 개인 프로젝트를 구축하는 개발자들에게 매력적입니다. NuGet 패키지도 없고, 공식 .NET SDK도 없으며, 어떤 언어로든 강력한 형식의 클라이언트 라이브러리도 없습니다. 모든 .NET 통합은 HttpClient를 기반으로 수작업으로 구축됩니다.

.NET 개발자를 위한 아키텍처 현실:

  • NuGet 패키지 없음: .NET 에 대한 완벽한 지원이 전혀 없습니다. IntelliSense 기능도 없고, 타입 모델도 없고, 통합 오류 처리 기능도 없습니다.
  • 클라우드 전용 처리: 모든 문서는 귀사 인프라를 벗어나 처리됩니다. 온프레미스 옵션이나 자체 호스팅 배포 경로는 제공되지 않습니다.
  • 수동 REST 통합: 개발자는 파일을 base64로 인코딩하여 FormUrlEncodedContent 또는 MultipartFormDataContent를 구성하고 원시 JSON 응답을 구문 분석합니다.
  • 자체 구현 속도 제한: 무료 티어에서는 분당 60건의 요청과 IP 주소당 하루 500건의 요청으로 제한됩니다. 운영 환경에서는 애플리케이션이 이러한 제한 로직을 직접 구현해야 합니다.
  • 파일 크기 제한: 무료 요금제에서는 5MB를 초과하는 파일을 허용하지 않습니다. 여러 페이지로 구성된 PDF 파일은 종종 이 제한을 초과합니다.
  • 워터마크가 포함된 PDF 출력: 무료 버전에서 검색 가능한 PDF를 생성할 때 OCR.space 워터마크가 삽입되어 고객에게 전달하거나 문서를 보관하는 데 사용할 수 없습니다.
  • SLA 없음: 무료 요금제는 가동 시간 보장이 없습니다. 유료 플랜은 더 높은 한도를 제공하지만, 아키텍처 제약 조건은 동일합니다.

REST 통합 개발자가 작성해야 하는 내용

OCR.space의 문서는 개발자에게 REST 엔드포인트를 안내하고 나머지는 개발자가 직접 구축하도록 맡겨둡니다. 실제 운영 환경에 맞게 보안을 강화하기 전의 최소한의 필수 기능을 갖춘 .NET 클라이언트는 다음과 같습니다.

// OCR.space: what you build before the first line of your actual application
public class OcrSpaceApiClient : IDisposable
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private readonly SemaphoreSlim _rateLimiter;
    private const string ApiEndpoint = "https://api.ocr.space/parse/image";
    private const int MaxFileSizeFree = 5 * 1024 * 1024; // 5MB — hard limit on free tier
    private const int RateLimitPerMinute = 60;

    public OcrSpaceApiClient(string apiKey)
    {
        _apiKey = apiKey ?? throw new ArgumentNullException(nameof(apiKey));
        _httpClient = new HttpClient();
        _httpClient.Timeout = TimeSpan.FromSeconds(120);
        _rateLimiter = new SemaphoreSlim(RateLimitPerMinute, RateLimitPerMinute);
    }

    public async Task<OcrResult> ExtractTextFromFileAsync(
        string filePath,
        string language = "eng",
        CancellationToken cancellationToken = default)
    {
        if (!File.Exists(filePath))
            throw new FileNotFoundException("Image file not found", filePath);

        var fileInfo = new FileInfo(filePath);
        if (fileInfo.Length > MaxFileSizeFree)
        {
            throw new InvalidOperationException(
                $"File size {fileInfo.Length / 1024 / 1024}MB exceeds free tier limit of 5MB.");
        }

        await _rateLimiter.WaitAsync(cancellationToken);

        try
        {
            // Document leaves your infrastructure here
            byte[] imageBytes = await File.ReadAllBytesAsync(filePath, cancellationToken);
            string base64Image = Convert.ToBase64String(imageBytes);
            string mimeType = GetMimeType(filePath);

            var formContent = new FormUrlEncodedContent(new[]
            {
                new KeyValuePair<string, string>("apikey", _apiKey),
                new KeyValuePair<string, string>("base64Image", $"data:{mimeType};base64,{base64Image}"),
                new KeyValuePair<string, string>("language", language),
                new KeyValuePair<string, string>("isOverlayRequired", "false"),
                new KeyValuePair<string, string>("filetype", Path.GetExtension(filePath).TrimStart('.')),
                new KeyValuePair<string, string>("detectOrientation", "true"),
                new KeyValuePair<string, string>("scale", "true"),
                new KeyValuePair<string, string>("OCREngine", "2")
            });

            var response = await _httpClient.PostAsync(ApiEndpoint, formContent, cancellationToken);

            if (!response.IsSuccessStatusCode)
            {
                string errorBody = await response.Content.ReadAsStringAsync(cancellationToken);
                throw new OcrSpaceException(
                    $"OCR.space API returned {response.StatusCode}: {errorBody}",
                    (int)response.StatusCode);
            }

            string jsonResponse = await response.Content.ReadAsStringAsync(cancellationToken);
            return ParseOcrResponse(jsonResponse);
        }
        finally
        {
            _ = Task.Run(async () =>
            {
                await Task.Delay(60000 / RateLimitPerMinute);
                _rateLimiter.Release();
            });
        }
    }

    private OcrResult ParseOcrResponse(string jsonResponse)
    {
        using var doc = JsonDocument.Parse(jsonResponse);
        var root = doc.RootElement;

        if (root.TryGetProperty("IsErroredOnProcessing", out var isErrored) && isErrored.GetBoolean())
        {
            string errorMessage = "OCR processing failed";
            if (root.TryGetProperty("ErrorMessage", out var errorMessages))
            {
                // Parse the array of error strings manually
                var messages = new List<string>();
                if (errorMessages.ValueKind == JsonValueKind.Array)
                {
                    foreach (var msg in errorMessages.EnumerateArray())
                        messages.Add(msg.GetString() ?? "Unknown error");
                }
                errorMessage = string.Join("; ", messages);
            }
            throw new OcrSpaceException(errorMessage, 500);
        }

        var result = new OcrResult();

        if (root.TryGetProperty("ParsedResults", out var parsedResults))
        {
            foreach (var parsedResult in parsedResults.EnumerateArray())
            {
                if (parsedResult.TryGetProperty("ParsedText", out var parsedText))
                    result.ParsedText += parsedText.GetString();

                if (parsedResult.TryGetProperty("FileParseExitCode", out var exitCode))
                    result.ExitCodes.Add(exitCode.GetInt32());
            }
        }

        return result;
    }

    private string GetMimeType(string filePath) =>
        Path.GetExtension(filePath).ToLowerInvariant() switch
        {
            ".png"          => "image/png",
            ".jpg" or ".jpeg" => "image/jpeg",
            ".bmp"          => "image/bmp",
            ".tiff" or ".tif" => "image/tiff",
            ".pdf"          => "application/pdf",
            _               => "application/octet-stream"
        };

    public void Dispose()
    {
        _httpClient?.Dispose();
        _rateLimiter?.Dispose();
    }
}

// Custom models — no SDK means you define these yourself
public class OcrResult
{
    public string ParsedText { get; set; } = string.Empty;
    public List<string> Warnings { get; } = new();
    public List<int> ExitCodes { get; } = new();
}

public class OcrSpaceException : Exception
{
    public int StatusCode { get; }
    public OcrSpaceException(string message, int statusCode) : base(message)
        => StatusCode = statusCode;
}

이는 첫 번째 비즈니스 로직 메서드 이전에 존재하는 90줄 이상의 인프라 코드입니다. 모든 OCR.space 통합에는 이러한 기본 템플릿(또는 그와 유사한 것)이 포함되어 있으며, 이를 작성하는 모든 팀은 OCR.space가 해결해주지 않은 동일한 문제를 해결하고 있습니다.

IronOCR 이해하기

IronOCR는 .NET용 상업용 OCR 라이브러리로, IronOcr NuGet 패키지를 통해 설치됩니다. 이 서비스는 최적화된 Tesseract 5 엔진에 자동 전처리, 네이티브 PDF 입력, 검색 가능한 PDF 출력, 125개 이상의 언어 팩, 구조화된 결과 추출 기능을 통합했으며, 클라우드 종속성, API 키 또는 요청당 요금 없이 모든 기능을 제공합니다. 문서는 .NET 애플리케이션이 실행되는 인프라에서 로컬로 처리됩니다.

주요 특징:

  • 단일 NuGet 패키지: dotnet add package IronOcr는 완전한 설치입니다. 관리해야 할 네이티브 바이너리도 없고, tessdata 디렉터리도 없고, 환경 변수도 없습니다.
  • 강력한 유형의 API: IronTesseract, OcrInput, OcrResult, CropRectangle는 전체 IntelliSense 지원을 제공하는 일류 .NET 유형입니다.
  • 자동 전처리: 기울기 보정, 노이즈 제거, 대비 조정, 이진화 및 해상도 향상 필터가 외부 라이브러리 없이 자동으로 또는 필요에 따라 적용됩니다.
  • PDF 기본 지원: 스캔한 PDF 파일을 읽는 기능과 검색 가능한 PDF 파일을 생성하는 기능이 모두 내장되어 있습니다. 사용 가능한 메모리 용량 외에는 크기 제한이 없습니다.
  • 로컬 실행: OCR 실행 중에는 네트워크 호출이 발생하지 않습니다. 이 라이브러리는 프로세스 내에서 완전히 실행됩니다. 에어갭 환경은 구성 변경 없이 작동합니다.
  • 스레드 안전: 여러 IronTesseract 인스턴스가 동기화 또는 경합 관리 없이 동시에 실행됩니다.
  • 영구 라이선스: $999 Lite / $1,499 Plus / $2,399 Professional. 요청량에 관계없이 건당 추가 요금이 없습니다.

기능 비교

기능OCR.spaceIronOCR
NuGet 패키지없음 — REST API만 사용IronOcr — 전체 .NET 지원
처리 위치OCR.space 클라우드 서버로컬 — 귀사의 인프라
무료 티어월 25,000개 요청 (한정 수량)체험 가능
유료 가격현재 가격 문의는 OCR.space에 연락하십시오$2,399 일회성 영구
속도 제한분당 60건, 하루 500건 (무료)None
오프라인 기능아니요네, 완전히 공기층이 분리되어 있습니다.
데이터 개인정보 보호제3자에게 발송된 문서문서는 서버에 보관됩니다.

상세 기능 비교

기능OCR.spaceIronOCR
통합
NuGet 패키지None네 (IronOcr)
강력한 타입 모델없음 — 수동 JSON 파싱네 (OcrResult, OcrInput)
IntelliSense 지원None전체
사용자 지정 예외 유형스스로를 정의해야 합니다내장형
재시도 로직직접 만들어야 합니다내장형
처리 중
처리 위치클라우드 서버현지 진행 중
인터넷 필요네, 모든 통화에 대해아니요
에어갭 서포트아니요
요금 제한네, 모든 플랜None
파일 크기 제한5MB (무료 요금제)메모리 전용
OCR 기능
이미지 OCR
PDF OCR네 (제한 사항이 있습니다)예 (네이티브 파일이며 크기 제한이 없습니다)
검색 가능한 PDF 출력무료 버전에 워터마크가 표시됩니다.깨끗한 출력, 워터마크 없음
자동 전처리없음 (서버 측에서만 사용)기울기 보정, 노이즈 제거, 대비 조정, 이진화, 해상도 향상
수동 전처리 제어None전체 필터 API
언어 지원약 25개 언어NuGet 언어 팩을 통해 125개 이상 추가 가능
문서별 다국어 지원제한적네 — 모국어와 제2언어 모두 해당됩니다.
구조화된 출력 (단어/줄/페이지)아니요, 일반 텍스트만 가능합니다.네, 페이지, 단락, 줄, 좌표가 있는 단어까지 모두 포함됩니다.
신뢰도 점수아니요네, 결과별로 그리고 단어별로 그렇습니다.
영역 기반 OCR아니요네 — CropRectangle
OCR 중 바코드 판독아니요네 — ReadBarCodes = true
배포
Docker외부 인터넷 연결이 필요합니다.별도의 설정 없이 바로 사용 가능합니다.
Linux외부 인터넷 연결이 필요합니다.완전히 지원
HIPAA 규정 준수위험 — 문서가 당신의 통제에서 벗어나게 됩니다예, 외부 전송 없음
GDPR 준수위험 요소 - EU 데이터 전송 불확실예, 제3자를 통한 데이터 처리는 없습니다.
가격
가격 모델월간 구독일회성 영구 라이선스
입장료현재 가격 문의는 OCR.space에 연락하십시오$999 일회성
대규모 문서당 비용None
SLA없음(무료), 유료는 종류에 따라 다름Enterprise SLA이용 가능

SDK 심층 활용 vs. 순수 HTTP: 통합 비용

OCR.space와 네이티브 SDK 간의 차이는 스타일 선호도의 문제가 아닙니다. 이는 개발 시간, 버그 발생 가능성, 그리고 모든 배포 과정에서의 유지 관리 부담을 기준으로 측정됩니다.

OCR.space 접근법

OCR.space를 사용하는 모든 .NET 개발자는 자체 HTTP 클라이언트를 작성합니다. 가장 간단한 구현 방식, 즉 재시도 로직, 일괄 처리 기능, 전처리 과정이 없는 기본적인 텍스트 추출 방식조차도 50줄이 넘습니다.

// OCR.space: minimum viable extraction — 50세 이상 lines before business logic
public class OcrSpaceBasicExtraction
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;

    public OcrSpaceBasicExtraction(string apiKey)
    {
        _apiKey = apiKey;
        _httpClient = new HttpClient();
    }

    public async Task<string> ExtractText(string imagePath)
    {
        // Read file and encode — document leaves your infrastructure
        byte[] imageBytes = File.ReadAllBytes(imagePath);
        string base64 = Convert.ToBase64String(imageBytes);

        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("apikey", _apiKey),
            new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}"),
            new KeyValuePair<string, string>("language", "eng")
        });

        // Send to cloud
        var response = await _httpClient.PostAsync(
            "https://api.ocr.space/parse/image", content);

        if (!response.IsSuccessStatusCode)
            throw new Exception($"API error: {response.StatusCode}");

        // Parse JSON manually — no typed models
        string json = await response.Content.ReadAsStringAsync();
        using var doc = JsonDocument.Parse(json);

        return doc.RootElement
            .GetProperty("ParsedResults")[0]
            .GetProperty("ParsedText")
            .GetString() ?? string.Empty;
    }
}
C#

일괄 처리를 추가하면 모든 일괄 요청에 대해 속도 제한 스로틀링, 지수 백오프를 사용하는 요청별 재시도 로직, 그리고 HTTP 429 응답에 대한 명시적 처리가 필요하기 때문에 코드 줄이 80줄 이상으로 늘어납니다. PDF 처리 기능을 추가하면 모든 호출 전에 5MB 파일 크기 검사가 추가됩니다. 무료 요금제는 이보다 큰 파일을 경고가 아닌 오류와 함께 거부하기 때문입니다.

IronOCR 접근법

NuGet 에서 IronOCR 설치하면 해당 인프라 코드가 모두 기존 메서드 호출로 대체됩니다.

// IronOCR: complete implementation
var result = new IronTesseract().Read("invoice.png");
Console.WriteLine(result.Text);

IronTesseract 클래스는 파일 읽기, 전처리, 엔진 실행 및 결과 생성 작업을 처리합니다. HTTP 클라이언트가 없습니다. base64 인코딩을 사용하지 않습니다. JSON 파싱은 지원하지 않습니다. API 키가 필요하지 않습니다. 속도 제한 없음. 이미지에서 텍스트를 읽는 방법에 대한 튜토리얼은 파일 경로, 바이트 배열, 스트림, 비트맵 등 다양한 입력 유형을 다루지만, 모두 동일한 단일 함수 호출 패턴을 따릅니다.

소스 파일의 코드 복잡도 수치는 가상적인 것이 아닙니다.

시나리오OCR.space 라인IronOCR라인
기본 추출50세 이상3
PDF 처리60세 이상12
일괄 처리80+15
오류 처리70+15
완벽한 클라이언트 인프라200+0 (NuGet 제공합니다)

일괄 처리 및 속도 제한

OCR.space의 무료 요금제가 실제 운영 환경에서 가장 눈에 띄게 무너지는 부분은 바로 사용량 제한 문제입니다.

OCR.space 접근법

OCR.space에서 배치 처리를 위해서는 60회/분 요청 한도를 준수할 수 있도록 SemaphoreSlim을 생성 및 관리해야 하며, 세마포어 논리가 부정확하거나 공유 인프라가 IP 주소를 공유할 때 발생하는 HTTP 429 응답에 대한 지수 백오프 재시도 논리가 필요합니다:

// OCR.space batch:80+lines of rate-limit plumbing before actual work
public class OcrSpaceBatchProcessing
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private readonly SemaphoreSlim _rateLimiter;

    public OcrSpaceBatchProcessing(string apiKey)
    {
        _apiKey = apiKey;
        _httpClient = new HttpClient();
        _rateLimiter = new SemaphoreSlim(60, 60); // Free tier: 60/minute
    }

    public async Task<Dictionary<string, string>> ProcessBatch(string[] imagePaths)
    {
        var results = new Dictionary<string, string>();

        foreach (var imagePath in imagePaths)
        {
            await _rateLimiter.WaitAsync();

            try
            {
                string text = await ProcessWithRetry(imagePath);
                results[imagePath] = text;
            }
            finally
            {
                _ = Task.Run(async () =>
                {
                    await Task.Delay(1000); // 1 second spacing
                    _rateLimiter.Release();
                });
            }
        }

        return results;
    }

    private async Task<string> ProcessWithRetry(string imagePath, int maxRetries = 3)
    {
        for (int attempt = 0; attempt < maxRetries; attempt++)
        {
            try
            {
                byte[] imageBytes = File.ReadAllBytes(imagePath);
                string base64 = Convert.ToBase64String(imageBytes);

                var content = new FormUrlEncodedContent(new[]
                {
                    new KeyValuePair<string, string>("apikey", _apiKey),
                    new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}"),
                    new KeyValuePair<string, string>("language", "eng")
                });

                var response = await _httpClient.PostAsync(
                    "https://api.ocr.space/parse/image", content);

                if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
                {
                    // Rate limited — exponential backoff
                    await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
                    continue;
                }

                response.EnsureSuccessStatusCode();

                string json = await response.Content.ReadAsStringAsync();
                using var doc = JsonDocument.Parse(json);

                return doc.RootElement
                    .GetProperty("ParsedResults")[0]
                    .GetProperty("ParsedText")
                    .GetString() ?? string.Empty;
            }
            catch (HttpRequestException) when (attempt < maxRetries - 1)
            {
                await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
            }
        }

        throw new Exception($"Failed to process {imagePath} after {maxRetries} attempts");
    }
}
C#

무료 요금제의 하루 500건 요청 제한은 이러한 제약을 더욱 강화합니다. 하루에 600건의 문서를 처리하는 애플리케이션은 처리 도중 실패하며, 다음 날까지 자동으로 이월되지 않습니다.

IronOCR 접근법

IronOCR 에는 사용량 제한이 없습니다. 일괄 처리는 반복문입니다.

//IronOCR batch: 8 lines, no rate limits, no retries needed
public Dictionary<string, string> ProcessBatch(string[] imagePaths)
{
    var results = new Dictionary<string, string>();
    var ocr = new IronTesseract();

    foreach (var imagePath in imagePaths)
    {
        // 아니요 rate limits, no retries, no cloud dependency
        var result = ocr.Read(imagePath);
        results[imagePath] = result.Text;
    }

    return results;
}
C#

배치 내에서 IronTesseract 인스턴스를 재사용하면 반복되는 엔진 초기화 오버헤드를 피할 수 있습니다. 처리 속도는 원격 API의 속도 제한 정책이 아니라 로컬 CPU 및 디스크 I/O에 의해 제한됩니다. 멀티스레딩 예제는 처리량이 중요할 때 코어 간 병렬화를 수행하는 방법을 보여줍니다.

PDF 처리

PDF 지원은 두 번째 주요 구조적 격차를 보여줍니다.

OCR.space 접근법

OCR.space는 유료 플랜에서 PDF 업로드를 지원하며, 무료 플랜에서도 일부 PDF 업로드를 지원합니다. 하지만 무료 계정의 파일 크기 제한(5MB)으로 인해 여러 페이지로 구성된 문서는 대부분 업로드할 수 없습니다. 무료 버전에서 생성되는 검색 가능한 PDF 출력물에는 워터마크가 포함됩니다. 이 구현 방식은 페이지별 결과 추출과 모든 요청 전에 명시적인 파일 크기 검사를 필요로 합니다.

// OCR.space PDF: size check required, watermarks on free tier
public async Task<List<string>> ExtractTextFromPdf(string pdfPath)
{
    var pageTexts = new List<string>();

    // Reject before wasting a request quota entry
    var fileInfo = new FileInfo(pdfPath);
    if (fileInfo.Length > 5 * 1024 * 1024)
        throw new InvalidOperationException("File exceeds 5MB free tier limit. Upgrade or split PDF.");

    byte[] pdfBytes = File.ReadAllBytes(pdfPath);
    string base64 = Convert.ToBase64String(pdfBytes);

    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("apikey", _apiKey),
        new KeyValuePair<string, string>("base64Image", $"data:application/pdf;base64,{base64}"),
        new KeyValuePair<string, string>("language", "eng"),
        new KeyValuePair<string, string>("filetype", "PDF"),
        new KeyValuePair<string, string>("isCreateSearchablePdf", "false") // Watermarked on free tier
    });

    var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);

    if (!response.IsSuccessStatusCode)
        throw new Exception($"API error: {response.StatusCode}");

    string json = await response.Content.ReadAsStringAsync();
    using var doc = JsonDocument.Parse(json);

    if (doc.RootElement.TryGetProperty("ParsedResults", out var results))
    {
        foreach (var result in results.EnumerateArray())
        {
            if (result.TryGetProperty("ParsedText", out var text))
                pageTexts.Add(text.GetString() ?? string.Empty);
        }
    }

    return pageTexts;
}

IronOCR 접근법

IronOCR PDF 파일을 기본적으로 읽습니다. 제한 요소는 임의의 파일 크기 임계값이 아니라 사용 가능한 메모리입니다. 검색 가능한 PDF 출력은 모든 라이선스 등급에서 워터마크가 없는 깔끔한 파일을 생성합니다.

//IronOCR PDF: native support, no size limit, no watermarks
public List<string> ExtractTextFromPdf(string pdfPath)
{
    var pageTexts = new List<string>();

    using var input = new OcrInput();
    input.LoadPdf(pdfPath); // 아니요 5MB ceiling

    var result = new IronTesseract().Read(input);

    foreach (var page in result.Pages)
        pageTexts.Add(page.Text);

    return pageTexts;
}

// Generate searchable PDF — no watermarks
public void CreateSearchablePdf(string inputPath, string outputPath)
{
    var result = new IronTesseract().Read(inputPath);
    result.SaveAsSearchablePdf(outputPath);
}
C#

암호로 보호된 PDF는 단일 매개변수입니다: input.LoadPdf("encrypted.pdf", Password: "secret"). PDF OCR 사용법 안내서 는 페이지 범위 선택 및 암호 처리 방법을 자세히 다룹니다. 검색 가능한 PDF 생성 패턴에 대해서는 검색 가능한 PDF 사용법을 참조하세요.

오류 처리

OCR.space는 HTTP 상태 코드와 JSON IsErroredOnProcessing 플래그라는 두 가지 채널을 통해 오류를 전달합니다. 생산 코드는 두 가지를 모두 확인하고, IsErroredOnProcessing가 true일 때 JSON 오류 배열을 구문 분석하며, 부분 오류에 대한 페이지별 FileParseExitCode 값을 검사해야 합니다. 이 중 아무 것도 형식화되지 않았으며 모든 것이 JsonDocument에서 문자열 분석입니다:

// OCR.space: two error layers, all string-based
public async Task<string> SafeExtract(HttpClient client, string apiKey, string imagePath)
{
    try
    {
        byte[] imageBytes = File.ReadAllBytes(imagePath);
        string base64 = Convert.ToBase64String(imageBytes);

        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("apikey", apiKey),
            new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}")
        });

        var response = await client.PostAsync("https://api.ocr.space/parse/image", content);

        if (!response.IsSuccessStatusCode)
        {
            switch (response.StatusCode)
            {
                case System.Net.HttpStatusCode.Unauthorized:
                    throw new Exception("Invalid API key");
                case System.Net.HttpStatusCode.TooManyRequests:
                    throw new Exception("Rate limit exceeded — wait and retry");
                case System.Net.HttpStatusCode.PaymentRequired:
                    throw new Exception("Quota exceeded — upgrade plan");
                default:
                    throw new Exception($"API error: {response.StatusCode}");
            }
        }

        // Second error layer: JSON-level failures
        string json = await response.Content.ReadAsStringAsync();
        using var doc = JsonDocument.Parse(json);

        if (doc.RootElement.TryGetProperty("IsErroredOnProcessing", out var isError)
            && isError.GetBoolean())
        {
            var messages = new List<string>();
            if (doc.RootElement.TryGetProperty("ErrorMessage", out var errors))
            {
                foreach (var e in errors.EnumerateArray())
                    messages.Add(e.GetString() ?? "Unknown error");
            }
            throw new Exception(string.Join("; ", messages));
        }

        // Third layer: per-page exit codes
        if (doc.RootElement.TryGetProperty("ParsedResults", out var results))
        {
            foreach (var result in results.EnumerateArray())
            {
                if (result.TryGetProperty("FileParseExitCode", out var exitCode)
                    && exitCode.GetInt32() != 1)
                    throw new Exception($"Page parse failed: exit code {exitCode.GetInt32()}");
            }
        }

        return doc.RootElement
            .GetProperty("ParsedResults")[0]
            .GetProperty("ParsedText")
            .GetString() ?? string.Empty;
    }
    catch (JsonException ex)
    {
        throw new Exception($"Invalid API response: {ex.Message}");
    }
    catch (HttpRequestException ex)
    {
        throw new Exception($"Network error: {ex.Message}");
    }
}

IronOCR 명확한 메시지와 함께 표준 .NET 예외를 발생시킵니다. JSON 파싱 없음, HTTP 상태 코드 전환 없음, 계층형 오류 추출 없음:

// IronOCR: standard .NET exceptions
public string SafeExtract(string imagePath)
{
    try
    {
        var result = new IronTesseract().Read(imagePath);
        return result.Text;
    }
    catch (FileNotFoundException)
    {
        throw; // File not found — straightforward
    }
    catch (Exception ex) when (ex.Message.Contains("corrupt"))
    {
        throw new Exception($"Image file is corrupt: {imagePath}");
    }
    // 아니요 JSON errors. 아니요 HTTP errors. 아니요 rate limit errors.
}
C#

IronTesseract API 참조OcrResult API 참조는 추출 품질 평가를 위한 Confidence 속성을 포함하여 형식화된 결과 구조를 문서화합니다.

API 매핑 참조

OCR.space 컨셉IronOCR에 상응하는
POST https://api.ocr.space/parse/imagenew IronTesseract().Read(path)
FormUrlEncodedContent / MultipartFormDataContentOcrInput
base64Image 매개변수input.LoadImage(path) 또는 input.LoadImage(bytes)
language 매개변수ocr.Language = OcrLanguage.English
OCREngine 매개변수엔진은 내부적으로 관리됩니다.
isOverlayRequired 매개변수result.Words / result.Lines (항상 사용 가능)
isCreateSearchablePdf 매개변수result.SaveAsSearchablePdf(outputPath)
filetype=PDF 매개변수input.LoadPdf(path)
ParsedResults[0].ParsedTextresult.Text
ParsedResults[n] (페이지별)result.Pages[n].Text
IsErroredOnProcessing JSON 플래그표준 .NET 예외
FileParseExitCode 페이지별 플래그표준 .NET 예외
ProcessingTimeInMilliseconds암시적 — 추가 구문 분석이 필요 없음
HTTP 429 / 속도 제한해당 사항 없음 - 요금 제한 없음
사용자 정의 OcrResult POCO (사용자 정의)IronOcr.OcrResult (NuGet에서 제공)
사용자 정의 OcrSpaceException (사용자 정의)표준 .NET 예외 유형
SemaphoreSlim 속도 제한기 (사용자가 제작)필요 없음
모든 요청에 ​​API 키가 포함됩니다.IronOcr.License.LicenseKey (시작 시 한 번만)

팀이 OCR.space에서IronOCR로 이전하는 것을 고려할 때

부하 테스트 중 무료 티어에 도달함

OCR.space의 IP 주소당 하루 500건 요청 제한은 권고 사항이 아니라 엄격한 제한입니다. 팀들은 여러 개발자가 사무실 IP 주소를 공유하는 부하 테스트 또는 스테이징 배포 중에 이러한 문제를 발견합니다. OCR.space에 대한 통합 테스트를 실행하는 공유 CI/CD 파이프라인은 업무일이 끝나기 전에 일일 할당량을 모두 소진할 것입니다. 그 시점에서 애플리케이션이 실패하는 이유는 코드가 잘못되었기 때문이 아니라, 제3자 카운터가 임의의 임계값에 도달했기 때문입니다. 로컬 처리로 전환하면 이러한 유형의 오류가 완전히 사라집니다. 처리가 프로세스 내에서 실행되므로 할당량 소진 문제가 발생하지 않습니다.

규정 준수 및 데이터 분류 검토

문서를 개인 식별 정보(PII), 개인 건강 정보(PHI) 또는 재정적으로 민감한 정보로 분류하는 보안 검토는 OCR.space에 이분법적인 문제를 야기합니다. 즉, 해당 서비스는 온프레미스 배포 경로가 없습니다. HIPAA 시나리오에 대한 비즈니스 제휴 계약(BAA)과 동등한 문서화된 계약은 없으며, GDPR에 따른 EU 데이터 전송을 명확하게 규정하는 데이터 처리 계약 구조도 없고, 계약상 통제가 있더라도 문서 전송을 방지할 수 있는 기술적 메커니즘도 없습니다. 클라우드 기반 문서 처리와 관련하여 규정 준수 지적을 받은 팀은 로컬 솔루션이 필요합니다.IronOCR설계상 문서가 애플리케이션 서버를 벗어나지 않도록 하여 이러한 요구 사항을 충족합니다. 라이선스 페이지에는 영구 라이선스 구조가 명시되어 있으며, 클라우드 공급업체 검토 범위를 제외함으로써 규정 준수 문서 작성을 간소화합니다.

통합 코드 유지 관리 부담

OCR.space 통합은 기능 요구 사항에 비례하여 기술적 부채를 축적합니다. 초기 HTTP 클라이언트는 관리가 용이합니다. 그런 다음 팀은 재시도 로직을 추가합니다. 그런 다음 여러 서비스가 IP 주소를 공유하기 때문에 IP 주소별 속도 제한 추적 기능을 추가합니다. 그런 다음 파일 크기 사전 유효성 검사 단계를 추가합니다. 그러다가 엔진 1과 엔진 2의 응답에서 JSON 오류 구조가 다르다는 것을 발견하고 분기를 추가합니다. 6개월 후, OCR.space 통합은 모든 신규 팀원이 OCR 관련 작업을 시작하기 전에 반드시 이해해야 하는 400줄의 인프라 코드로 발전했습니다. 그 팀이 유지 보수 비용과 $999 일회성IronOCR라이선스를 평가할 때, 계산은 간단합니다.

구조화된 출력 요구사항

OCR.space는 일반 텍스트만 반환합니다. 응답에는 단어 좌표, 줄 경계, 단락 구분, 단어별 신뢰도 점수가 없습니다. 송장에서 특정 필드(예: 알려진 지역의 공급업체 이름, 오른쪽 하단의 총액)를 추출해야 하는 애플리케이션은 OCR.space의 출력에서 ​​구조화된 기반을 활용할 수 없습니다.IronOCR페이지, 단락, 줄, 개별 단어 등 모든 세분화 수준에서 좌표와 신뢰도 값을 포함한 전체 문서 계층 구조를 보여줍니다. 영역 기반 처리를 통해 전체 페이지 출력을 후처리하지 않고도 문서의 특정 영역을 대상으로 처리할 수 있습니다. 판독 결과 활용 방법영역 기반 OCR 가이드에서는 이러한 패턴을 자세히 다룹니다.

무료 티어를 넘어선 볼륨 성장

월 25,000건의 요청까지 무료 요금제는 소량 처리 시나리오에 적합합니다. 그 외에도 유료 티어가 적용됩니다 — 현재 가격 문의는 OCR.space에 연락하십시오. 이러한 비용은 시간이 지남에 따라 증가하며, 한 번 구매한 $999 영구 라이선스와 비교할 때, 요청당 청구가 전혀 없기 때문에 그렇습니다. 월 문서 처리량 증가가 25,000건을 초과할 것으로 예상되는 팀은 현지 처리 비용에 대한 명확한 근거를 갖게 됩니다.

일반적인 마이그레이션 고려사항

HTTP 클라이언트를 메서드 호출로 대체

OCR.space에서IronOCR로의 마이그레이션은 두 프로그램 모두 문서에서 텍스트를 반환하기 때문에 아키텍처적으로 간단합니다. HTTP 클라이언트, JSON 파서, 속도 제한기 및 사용자 지정 예외 유형이 사라집니다. 그것들을 대체하는 것은 단일 NuGet 패키지와 메서드 호출입니다. 서비스 경계에서의 이전/이후:

// Before: OCR.space service (simplified — actual implementation is200+lines)
public class OcrSpaceService : IDisposable
{
    private readonly HttpClient _client = new();
    private readonly string _apiKey;

    public OcrSpaceService(string apiKey) => _apiKey = apiKey;

    public async Task<string> ProcessDocument(string path)
    {
        byte[] bytes = File.ReadAllBytes(path);
        string base64 = Convert.ToBase64String(bytes);

        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("apikey", _apiKey),
            new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}")
        });

        var response = await _client.PostAsync("https://api.ocr.space/parse/image", content);
        response.EnsureSuccessStatusCode();

        string json = await response.Content.ReadAsStringAsync();
        using var doc = JsonDocument.Parse(json);
        return doc.RootElement.GetProperty("ParsedResults")[0].GetProperty("ParsedText").GetString()!;
    }

    public void Dispose() => _client.Dispose();
}

// After:IronOCR service — no HttpClient, no JSON, no API key
public class IronOcrService
{
    private readonly IronTesseract _ocr = new();

    public string ProcessDocument(string path)
    {
        return _ocr.Read(path).Text;
    }
    // 아니요 Dispose — no external connections to close
}
C#

IDisposable 구현이 사라지는 이유는 관리해야 할 HttpClient가 없기 때문입니다.

API 키 삭제

OCR.space는 모든 HTTP 요청에 API 키를 요구합니다. 해당 키는 안전하게 보관해야 하며, 노출될 경우 주기적으로 변경하고, 소스 코드 관리 시스템에서 제외해야 합니다.IronOCR애플리케이션 시작 시 한 번 설정된 라이선스 키를 사용하며, 일반적으로 환경 변수 또는 구성 시스템에서 가져옵니다.

// At application startup — once, not per request
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");

마이그레이션 후에는 API 키 순환 프로세스, 비밀 관리 구성 및 요청별 키 주입 로직이 모두 불필요해집니다.

마이그레이션 후 전처리

OCR.space는 결과를 반환하기 전에 서버 측 처리를 수행하지만, 개발자는 해당 처리에 무엇이 포함되거나 포함되지 않는지에 대해 제어할 수 없습니다.IronOCR명시적인 전처리 방법을 제공합니다. 문서 품질이 문제가 되는 경우(예: 스캔 이미지 왜곡, 저대비 복사본, 노이즈가 심한 팩스 출력물) 전처리 파이프라인은 3~5개의 메서드 호출로 구성됩니다.

using var input = new OcrInput();
input.LoadImage("low-quality-scan.jpg");
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
input.EnhanceResolution(300);
var result = new IronTesseract().Read(input);

이미지 품질 보정 가이드이미지 색상 보정 가이드 에는 사용 가능한 모든 필터와 보정 전후의 정확도 차이를 보여주는 예시가 포함되어 있습니다.

비동기 vs. 동기

OCR.space 호출은 HTTP 왕복을 포함하므로 본질적으로 비동기적입니다.IronOCR호출은 기본적으로 동기식으로 이루어지므로 웹 환경이 아닌 곳에서는 코드 작성이 간소화됩니다. 논 블로킹 실행을 요구하는 ASP.NET 및 서버 시나리오에서는 IronOCR가 Task.Run 래핑 또는 직접 비동기 API를 통해 비동기 작업을 지원합니다. 비동기 OCR 가이드에서는 패턴을 다룹니다. 이전에는 클라우드 호출을 대기하던 서비스 메서드에서 await를 제거하면 OCR.space가 그것을 요구했던 환경에서 비동기화를 단독으로 채택한 경우 호출 스택이 간소화됩니다.

IronOCR의 추가 기능

위 섹션에서 다루지 않은 기능들은 각각 OCR.space에는 없는 고유한 기능들입니다.

  • hOCR 내보내기: result.SaveAsHocrFile("output.hocr")는 다운스트림 문서 처리 파이프라인을 위한 표준 준수 hOCR 출력을 생성합니다.
  • 진행 상황 추적 : 장시간 실행되는 다중 페이지 일괄 작업에 대한 진행 상황 이벤트를 구독하여 UI 애플리케이션에서 진행률 표시줄과 예상 완료 시간을 계산할 수 있습니다.
  • 테이블 추출 : 구조화된 테이블 데이터는 결과 모델을 통해 접근할 수 있으며, 열 정렬이 필요한 송장 품목 추출과 같은 사용 사례를 지원합니다.
  • 전문화된 문서 읽기: 여권 MRZ 구역, 차량 번호판, MICR 수표 줄, 필기 인식은 같은 IronTesseract API를 통해 접근할 수 있는 맞춤형 기능입니다.

.NET 호환성 및 미래 준비

IronOCR .NET 6, .NET 7, .NET 8 및 .NET 9를 대상으로 하며, 각 LTS 및 STS 릴리스를 적극적으로 지원합니다. 이 라이브러리는 Windows x64, Windows x86, Linuxx64, macOS, Docker, AWS Lambda 및 Azure App Service에서 실행되며, 플랫폼별 구성 없이 동일한 NuGet 패키지에서 모두 사용할 수 있습니다. OCR.space는 모든 배포 환경에서 외부 HTTPS 연결을 요구합니다. 따라서 에어갭 네트워크, 제한적인 기업 프록시, 그리고 정책에 의해 타사 API로의 외부 트래픽이 차단된 인프라는 제외됩니다.IronOCR런타임에 네트워크 요구 사항이 없습니다. 이 기능은 프로세스 내에서 완전히 실행됩니다. .NET 10이 2026년 말 출시를 향해 나아가고 있는 가운데, IronOCR은 .NET 주요 버전 전반에 걸쳐 호환성을 유지해 온 오랜 경험을 바탕으로 통합 변경 없이 연속성을 보장합니다.

결론

OCR.space는 실질적이고 유용한 틈새시장을 공략합니다. 주말 동안 OCR 기능을 프로토타입으로 제작해야 하는 개발자, 텍스트 추출 개념을 탐구하는 학생, 또는 월 25,000건 미만의 문서를 처리하고 규정 준수 요구 사항이 없는 개인용 도구를 사용하는 사용자에게 적합합니다. 해당 사용자층에게 무료 서비스는 진정한 가치를 제공합니다.

문제는 .NET 개발자들이 일반적으로 주말에 프로토타입을 만드는 것이 아니라는 점입니다. 그들은 청구 시스템, 의료 기록 처리 시스템, 문서 보관 파이프라인, 그리고 규정 준수에 민감한 비즈니스 애플리케이션을 개발합니다. 그러한 맥락에서 OCR.space의 NuGet 패키지가 없는 것은 단순한 불편함이 아니라 구조적인 비호환성입니다. HTTP 클라이언트, 속도 제한기, JSON 파서 및 재시도 인프라를 구축하는 데 소요되는 매 시간은 애플리케이션의 실제 요구 사항을 충족하는 데 사용할 수 없는 시간입니다. 그 비용은 초기 투자 비용이지만, 유지 보수 비용은 통합 시스템의 수명 기간 동안 계속 발생합니다.

IronOCR 모든 OCR.space 통합을 특징짓는 특정 실패 모드, 즉 실제 SDK의 부재 문제를 해결합니다. 하나의 NuGet 패키지, 하나의 메서드 호출, HTTP 연결 불필요, 속도 제한 없음, 외부 서버로 문서 전송 없음. $999 진입 가격은 일회성 비용이며, 반복적인 OCR.space 구독 비용은 시간이 지남에 따라 특히 볼륨이 증가함에 따라 이를 초과할 것입니다. 규정을 준수해야 하는 팀에게는, 비용에 상관없이 지역 처리만이 유일한 옵션입니다.

결국 비교는 단 하나의 질문으로 귀결됩니다. 애플리케이션이 OCR을 외부 REST 종속성으로 필요로 하는가, 아니면 라이브러리 함수로 필요로 하는가? 실제 운영 환경에서 사용되는 .NET 애플리케이션의 경우, 답은 거의 항상 후자입니다.

참고해 주세요: OCR.space 및 Tesseract는 각 소유자의 등록 상표입니다. 이 사이트는 Google 또는 OCR.space와 관련이 없으며, 승인되지 않았으며, 후원도 받지 않습니다. 모든 제품명, 로고 및 브랜드는 해당 소유자의 자산입니다. 비교는 정보 제공 목적으로만 사용되며, 작성 시점에 공개적으로 이용 가능한 정보를 반영합니다.

관련 기사

Key in blue circle

무료 30일 체험 키를 즉시 받으세요.

Your trial license will be sent to your email address

제한 없음. 100% 무제한 이용. 신용카드 불필요.

bullet_checked신용카드나 계정 생성은 필요하지 않습니다.제한 없음. 100% 무제한 이용. 신용카드 불필요.
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
부담 없는 무료 상담을 받아보세요
아래 양식을 작성하시거나 sales@ironsoftware.com으로 이메일을 보내주세요.
고객님의 정보는 항상 비밀로 유지됩니다.
전 세계 수백만 엔지니어들이 신뢰하는 제품입니다.
Iron Software의 고객 로고
지금 바로 30일 무료 체험판 키를 받으세요.
신용카드나 계정 생성은 필요하지 않습니다.