C#을 사용하여 로그인 및 인증을 관리하는 방법

C#에서 로그인 인증 후 HTML을 PDF로 변환 — PDF 변환 완벽 가이드

This article was translated from English: Does it need improvement?
Translated
View the article in English

C#에서 로그인 인증 뒤에 HTML을 PDF로 변환하려면 IronPDF의 ChromeHttpLoginCredentials을(를) 네트워크 인증에 사용하거나 변환 전에 HttpClient으로 HTML을 다운로드하십시오. 이 접근 방식은 네트워크 인증과 HTML 폼 로그인 모두를 효과적으로 처리합니다.

빠른 시작: IronPDF 사용하여 로그인하지 않은 HTML을 PDF로 변환하기

IronPDF API를 사용하여 로그인 양식 뒤에 있는 HTML 페이지를 PDF로 변환하세요. 이 가이드는 인증 및 보호된 콘텐츠 검색을 위한 ChromeHttpLoginCredentials을(를) 설명합니다. 간단한 코드 예제를 통해 네트워크 인증과 HTML 폼 로그인 모두를 처리하세요.

  1. NuGet 패키지 관리자를 사용하여 https://www.nuget.org/packages/IronPdf 설치하기

    PM > Install-Package IronPdf
  2. 다음 코드 조각을 복사하여 실행하세요.

    new ChromePdfRenderer { LoginCredentials = new ChromeHttpLoginCredentials("username","password") }
        .RenderUrlAsPdf("https://example.com/protected")
        .SaveAs("secure.pdf");
  3. 실제 운영 환경에서 테스트할 수 있도록 배포하세요.

    무료 체험판으로 오늘 프로젝트에서 IronPDF 사용 시작하기

    arrow pointer


로그인 인증 처리를 위한 최적의 방법은 무엇일까요?

IronPDF ChromeHttpLoginCredentials API를 통해 TLS 네트워크 인증(사용자 이름 및 암호)을 지원합니다. 다양한 로그인 시나리오에 대한 자세한 안내는 TLS 웹사이트 및 시스템 로그인 튜토리얼을 참조하세요.

권장 방법은 System.Net.WebClient 또는 HttpClient을(를) 사용하여 HTML과 자산을 다운로드하는 것입니다. 이 메서드는 헤더, 로그인 및 기타 요구 사항을 지원합니다. IronPDF 메모리 또는 디스크에 다운로드한 후 HTML을 PDF로 변환합니다. 스타일시트 및 이미지를 HtmlAgilityPack을(를) 사용해 추출한 후 System.Net.WebClient으로 다운로드합니다.

// Download HTML content from a URL with authentication
string html;
using (WebClient client = new WebClient()) 
{
    // Add authentication headers if needed
    client.Headers.Add("Authorization", "Bearer " + accessToken);

    // Download the HTML string
    html = client.DownloadString("http://www.example.com/protected-content");
}

// Load the HTML into an HtmlDocument for parsing
HtmlDocument doc = new HtmlDocument();        
doc.LoadHtml(html);

// Extract all image sources for downloading
foreach(HtmlNode img in doc.DocumentNode.SelectNodes("//img")) 
{
    string imgSrc = img.GetAttributeValue("src", null);
    Console.WriteLine($"Found image: {imgSrc}");

    // Download each image asset
    if (!string.IsNullOrEmpty(imgSrc))
    {
        string fileName = Path.GetFileName(imgSrc);
        client.DownloadFile(imgSrc, fileName);
    }
}

// Convert the downloaded HTML to PDF
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("authenticated-content.pdf");
// Download HTML content from a URL with authentication
string html;
using (WebClient client = new WebClient()) 
{
    // Add authentication headers if needed
    client.Headers.Add("Authorization", "Bearer " + accessToken);

    // Download the HTML string
    html = client.DownloadString("http://www.example.com/protected-content");
}

// Load the HTML into an HtmlDocument for parsing
HtmlDocument doc = new HtmlDocument();        
doc.LoadHtml(html);

// Extract all image sources for downloading
foreach(HtmlNode img in doc.DocumentNode.SelectNodes("//img")) 
{
    string imgSrc = img.GetAttributeValue("src", null);
    Console.WriteLine($"Found image: {imgSrc}");

    // Download each image asset
    if (!string.IsNullOrEmpty(imgSrc))
    {
        string fileName = Path.GetFileName(imgSrc);
        client.DownloadFile(imgSrc, fileName);
    }
}

// Convert the downloaded HTML to PDF
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("authenticated-content.pdf");
Imports System.Net
Imports HtmlAgilityPack
Imports IronPdf

' Download HTML content from a URL with authentication
Dim html As String
Using client As New WebClient()
    ' Add authentication headers if needed
    client.Headers.Add("Authorization", "Bearer " & accessToken)

    ' Download the HTML string
    html = client.DownloadString("http://www.example.com/protected-content")
End Using

' Load the HTML into an HtmlDocument for parsing
Dim doc As New HtmlDocument()
doc.LoadHtml(html)

' Extract all image sources for downloading
For Each img As HtmlNode In doc.DocumentNode.SelectNodes("//img")
    Dim imgSrc As String = img.GetAttributeValue("src", Nothing)
    Console.WriteLine($"Found image: {imgSrc}")

    ' Download each image asset
    If Not String.IsNullOrEmpty(imgSrc) Then
        Dim fileName As String = Path.GetFileName(imgSrc)
        Using client As New WebClient()
            client.DownloadFile(imgSrc, fileName)
        End Using
    End If
Next

' Convert the downloaded HTML to PDF
Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("authenticated-content.pdf")
$vbLabelText   $csharpLabel

참고해 주세요상대 URL을 절대 URL로 재기반할 때 오버로드된 System.Uri 생성자를 사용하십시오. HTML 문서에서 모든 상대 경로를 재기반하기 위해 HtmlAgilityPack을 사용해 헤더에 <base> 태그를 추가하십시오. . URL 및 자산 처리 방법에 대한 자세한 내용은 기본 URL 및 자산 인코딩 가이드를 참조하세요.

HTML 콘텐츠를 먼저 다운로드해야 하는 이유는 무엇인가요?

변환 전에 HTML 콘텐츠를 다운로드하면 다음과 같은 몇 가지 이점이 있습니다.

  1. 완벽한 제어 : 변환 전에 HTML을 수정하고, 깨진 링크를 고치거나, 인증 토큰을 삽입할 수 있습니다.
  2. 자산 관리 : 이미지, CSS, JavaScript 파일과 같은 외부 리소스를 다운로드하고 캐시합니다.
  3. 인증 유연성 : OAuth, JWT 토큰 또는 사용자 지정 헤더를 포함한 모든 .NET 인증 메커니즘을 사용할 수 있습니다.
  4. 성능 : 자주 액세스하는 콘텐츠를 캐시하여 서버 부하를 줄입니다.
  5. 디버깅 : 변환되는 HTML을 정확히 검사하여 문제를 해결합니다.

쿠키 및 세션을 사용하는 복잡한 인증 시나리오의 경우, PDF 변환 중 인증 상태 관리에 대해 설명하는 쿠키 가이드를 참조하십시오.

이미지나 스타일시트 같은 에셋은 어떻게 관리해야 하나요?

인증된 페이지를 변환할 때 외부 자산에도 동일한 인증이 필요한 경우가 많습니다. 여기서는 HttpClient을(를) 사용한 종합적 접근법을 제공합니다:

public async Task<string> DownloadAuthenticatedHtmlWithAssets(string url, string authToken)
{
    using (var client = new HttpClient())
    {
        // Set authentication header
        client.DefaultRequestHeaders.Authorization = 
            new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authToken);

        // Download the main HTML
        string html = await client.GetStringAsync(url);

        // Parse HTML to find assets
        var doc = new HtmlDocument();
        doc.LoadHtml(html);

        // Create a base URI for resolving relative paths
        var baseUri = new Uri(url);

        // Download CSS files
        var cssLinks = doc.DocumentNode.SelectNodes("//link[@rel='stylesheet']");
        if (cssLinks != null)
        {
            foreach (var link in cssLinks)
            {
                string href = link.GetAttributeValue("href", "");
                if (!string.IsNullOrEmpty(href))
                {
                    var cssUri = new Uri(baseUri, href);
                    string cssContent = await client.GetStringAsync(cssUri);

                    // Embed CSS directly in the HTML
                    var styleNode = doc.CreateElement("style");
                    styleNode.InnerHtml = cssContent;
                    doc.DocumentNode.SelectSingleNode("//head").AppendChild(styleNode);

                    // Remove the external link
                    link.Remove();
                }
            }
        }

        // Return the modified HTML with embedded assets
        return doc.DocumentNode.OuterHtml;
    }
}
public async Task<string> DownloadAuthenticatedHtmlWithAssets(string url, string authToken)
{
    using (var client = new HttpClient())
    {
        // Set authentication header
        client.DefaultRequestHeaders.Authorization = 
            new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authToken);

        // Download the main HTML
        string html = await client.GetStringAsync(url);

        // Parse HTML to find assets
        var doc = new HtmlDocument();
        doc.LoadHtml(html);

        // Create a base URI for resolving relative paths
        var baseUri = new Uri(url);

        // Download CSS files
        var cssLinks = doc.DocumentNode.SelectNodes("//link[@rel='stylesheet']");
        if (cssLinks != null)
        {
            foreach (var link in cssLinks)
            {
                string href = link.GetAttributeValue("href", "");
                if (!string.IsNullOrEmpty(href))
                {
                    var cssUri = new Uri(baseUri, href);
                    string cssContent = await client.GetStringAsync(cssUri);

                    // Embed CSS directly in the HTML
                    var styleNode = doc.CreateElement("style");
                    styleNode.InnerHtml = cssContent;
                    doc.DocumentNode.SelectSingleNode("//head").AppendChild(styleNode);

                    // Remove the external link
                    link.Remove();
                }
            }
        }

        // Return the modified HTML with embedded assets
        return doc.DocumentNode.OuterHtml;
    }
}
Imports System
Imports System.Net.Http
Imports System.Threading.Tasks
Imports HtmlAgilityPack

Public Class HtmlDownloader
    Public Async Function DownloadAuthenticatedHtmlWithAssets(url As String, authToken As String) As Task(Of String)
        Using client As New HttpClient()
            ' Set authentication header
            client.DefaultRequestHeaders.Authorization = New System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authToken)

            ' Download the main HTML
            Dim html As String = Await client.GetStringAsync(url)

            ' Parse HTML to find assets
            Dim doc As New HtmlDocument()
            doc.LoadHtml(html)

            ' Create a base URI for resolving relative paths
            Dim baseUri As New Uri(url)

            ' Download CSS files
            Dim cssLinks = doc.DocumentNode.SelectNodes("//link[@rel='stylesheet']")
            If cssLinks IsNot Nothing Then
                For Each link In cssLinks
                    Dim href As String = link.GetAttributeValue("href", "")
                    If Not String.IsNullOrEmpty(href) Then
                        Dim cssUri As New Uri(baseUri, href)
                        Dim cssContent As String = Await client.GetStringAsync(cssUri)

                        ' Embed CSS directly in the HTML
                        Dim styleNode = doc.CreateElement("style")
                        styleNode.InnerHtml = cssContent
                        doc.DocumentNode.SelectSingleNode("//head").AppendChild(styleNode)

                        ' Remove the external link
                        link.Remove()
                    End If
                Next
            End If

            ' Return the modified HTML with embedded assets
            Return doc.DocumentNode.OuterHtml
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

HTML 파싱에 도움이 되는 도구는 무엇인가요?

HtmlAgilityPack은(는) .NET에서 가장 인기 있는 HTML 파싱 라이브러리이지만, 다른 대안도 존재합니다:

  1. HtmlAgilityPack : 일반적인 HTML 구문 분석 및 조작에 가장 적합합니다.
  2. AngleSharp : CSS 선택자 지원 기능을 갖춘 최신 표준 준수 HTML 파서
  3. CsQuery : jQuery에 익숙한 C# 개발자를 위한 jQuery와 유사한 구문
  4. 정규 표현식 : 간단한 추출 작업에 적합합니다(복잡한 HTML에는 권장하지 않습니다).

네트워크 인증을 사용하여 로그인하는 방법은 무엇인가요?

대부분의 ASP.NET 애플리케이션은 네트워크 인증을 지원하며, 이는 HTML 폼 제출 방식보다 더 안정적입니다. IronPDF는 ChromeHttpLoginCredentials 클래스를 통해 기본, 다이제스트, NTLM 인증에 대한 내장 지원을 제공합니다. 헤더를 추가로 사용자 지정하려면 HTTP 요청 헤더 가이드를 참조하세요.

:path=/static-assets/pdf/content-code-examples/how-to/logins-username-password.cs
using IronPdf;
using System;

ChromePdfRenderer renderer = new ChromePdfRenderer
{
    // setting login credentials to bypass basic authentication
    LoginCredentials = new ChromeHttpLoginCredentials()
    {
        NetworkUsername = "testUser",
        NetworkPassword = "testPassword"
    }
};

var uri = new Uri("http://localhost:51169/Invoice");

// Render web URL to PDF
PdfDocument pdf = renderer.RenderUrlAsPdf(uri);

// Export PDF
pdf.SaveAs("UrlToPdfExample.Pdf");
Imports IronPdf
Imports System

Private renderer As New ChromePdfRenderer With {
	.LoginCredentials = New ChromeHttpLoginCredentials() With {
		.NetworkUsername = "testUser",
		.NetworkPassword = "testPassword"
	}
}

Private uri = New Uri("http://localhost:51169/Invoice")

' Render web URL to PDF
Private pdf As PdfDocument = renderer.RenderUrlAsPdf(uri)

' Export PDF
pdf.SaveAs("UrlToPdfExample.Pdf")
$vbLabelText   $csharpLabel

네트워크 인증이 폼 제출 방식보다 더 신뢰할 수 있는 이유는 무엇일까요?

네트워크 인증은 HTML 폼 제출에 비해 여러 가지 장점을 제공합니다.

  1. 표준화된 프로토콜 : RFC 표준을 준수하는 HTTP 인증 헤더를 사용합니다.
  2. 브라우저 통합 : Chrome 렌더링 엔진이 인증을 원활하게 처리합니다.
  3. 세션 관리 : 인증 문제 자동 처리 및 세션 지속성 유지
  4. 보안 : 자격 증명은 폼 데이터가 아닌 헤더를 통해 안전하게 전송됩니다.
  5. 호환성: 대부분의 Enterprise 인증 시스템과 작동(Active Directory, LDAP)

네트워크 인증에 필요한 자격 증명은 무엇입니까?

인증 유형에 따라 필요한 자격 증명이 다릅니다.

// Basic Authentication (most common)
var basicAuth = new ChromeHttpLoginCredentials
{
    NetworkUsername = "user@domain.com",
    NetworkPassword = "password123",
    AuthenticationType = ChromeHttpLoginCredentials.AuthType.Basic
};

// NTLM/Windows Authentication
var ntlmAuth = new ChromeHttpLoginCredentials
{
    NetworkUsername = "DOMAIN\\username", // Include domain
    NetworkPassword = "password123",
    AuthenticationType = ChromeHttpLoginCredentials.AuthType.Ntlm
};

// Custom authentication headers
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.CustomHttpHeaders.Add("X-API-Key", "your-api-key");
renderer.RenderingOptions.CustomHttpHeaders.Add("Authorization", "Bearer " + jwtToken);
// Basic Authentication (most common)
var basicAuth = new ChromeHttpLoginCredentials
{
    NetworkUsername = "user@domain.com",
    NetworkPassword = "password123",
    AuthenticationType = ChromeHttpLoginCredentials.AuthType.Basic
};

// NTLM/Windows Authentication
var ntlmAuth = new ChromeHttpLoginCredentials
{
    NetworkUsername = "DOMAIN\\username", // Include domain
    NetworkPassword = "password123",
    AuthenticationType = ChromeHttpLoginCredentials.AuthType.Ntlm
};

// Custom authentication headers
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.CustomHttpHeaders.Add("X-API-Key", "your-api-key");
renderer.RenderingOptions.CustomHttpHeaders.Add("Authorization", "Bearer " + jwtToken);
Imports System.Collections.Generic

' Basic Authentication (most common)
Dim basicAuth As New ChromeHttpLoginCredentials With {
    .NetworkUsername = "user@domain.com",
    .NetworkPassword = "password123",
    .AuthenticationType = ChromeHttpLoginCredentials.AuthType.Basic
}

' NTLM/Windows Authentication
Dim ntlmAuth As New ChromeHttpLoginCredentials With {
    .NetworkUsername = "DOMAIN\username", ' Include domain
    .NetworkPassword = "password123",
    .AuthenticationType = ChromeHttpLoginCredentials.AuthType.Ntlm
}

' Custom authentication headers
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.CustomHttpHeaders.Add("X-API-Key", "your-api-key")
renderer.RenderingOptions.CustomHttpHeaders.Add("Authorization", "Bearer " & jwtToken)
$vbLabelText   $csharpLabel

인증 실패 문제를 어떻게 해결하나요?

일반적인 인증 문제 및 해결 방법:

  1. 401 권한 없음 : 자격 증명 및 인증 유형을 확인하십시오.
  2. 403 Forbidden : 사용자는 인증되었지만 권한이 부족합니다.
  3. 타임아웃 오류: 느린 인증 시스템을 위해 RenderDelay을(를) 증가시키십시오.
  4. 인증서 오류 : TLS/SSL 설정을 적절하게 구성하십시오.

문제 진단을 위해 디버깅을 활성화하세요.

// Enable detailed logging
IronPdf.Logging.Logger.EnableDebugging = true;
IronPdf.Logging.Logger.LogFilePath = "IronPdf.log";
IronPdf.Logging.Logger.LoggingMode = IronPdf.Logging.Logger.LoggingModes.All;

// Test authentication
try 
{
    var pdf = renderer.RenderUrlAsPdf("https://secure.example.com");
    pdf.SaveAs("authenticated.pdf");
}
catch (Exception ex)
{
    Console.WriteLine($"Authentication failed: {ex.Message}");
    // Check IronPdf.log for detailed error information
}
// Enable detailed logging
IronPdf.Logging.Logger.EnableDebugging = true;
IronPdf.Logging.Logger.LogFilePath = "IronPdf.log";
IronPdf.Logging.Logger.LoggingMode = IronPdf.Logging.Logger.LoggingModes.All;

// Test authentication
try 
{
    var pdf = renderer.RenderUrlAsPdf("https://secure.example.com");
    pdf.SaveAs("authenticated.pdf");
}
catch (Exception ex)
{
    Console.WriteLine($"Authentication failed: {ex.Message}");
    // Check IronPdf.log for detailed error information
}
Imports IronPdf
Imports System

' Enable detailed logging
Logging.Logger.EnableDebugging = True
Logging.Logger.LogFilePath = "IronPdf.log"
Logging.Logger.LoggingMode = Logging.Logger.LoggingModes.All

' Test authentication
Try
    Dim pdf = renderer.RenderUrlAsPdf("https://secure.example.com")
    pdf.SaveAs("authenticated.pdf")
Catch ex As Exception
    Console.WriteLine($"Authentication failed: {ex.Message}")
    ' Check IronPdf.log for detailed error information
End Try
$vbLabelText   $csharpLabel

HTML 폼을 사용하여 로그인하는 방법은 무엇인가요?

HTML 폼에 데이터를 전송하여 로그인하려면 ChromeHttpLoginCredentials 클래스를 사용하십시오. IronPDF의 ChromeHttpLoginCredentials API를 참조하세요.

다음 사항들을 고려해 보세요:

  • HTML 폼의 ACTION 속성에 지정된 URL로 로그인 데이터를 전송합니다. 이를 HttpLoginCredentials의 LoginFormUrl 속성으로 설정하십시오. 이는 PDF로 렌더링하려는 URL과 다를 수 있습니다.
  • HTML 폼의 모든 입력란과 텍스트 영역을 나타내는 데이터를 전송합니다. name 속성은 각 변수의 이름을 정의합니다(id가 아님).
  • 일부 웹사이트는 기기 로그인을 적극적으로 차단합니다.

다음은 폼 기반 인증의 전체 예시입니다.

// Configure form-based login
var formLogin = new ChromeHttpLoginCredentials
{
    LoginFormUrl = "https://example.com/login",
    LoginFormData = new Dictionary<string, string>
    {
        {"username", "user@example.com"},
        {"password", "securePassword123"},
        {"rememberMe", "true"},
        {"csrf_token", "abc123"} // Include any hidden fields
    }
};

var renderer = new ChromePdfRenderer
{
    LoginCredentials = formLogin,
    RenderingOptions = new ChromePdfRenderOptions
    {
        RenderDelay = 3000, // Allow time for login redirect
        EnableJavaScript = true
    }
};

// The actual page you want to convert (after login)
var pdf = renderer.RenderUrlAsPdf("https://example.com/dashboard");
pdf.SaveAs("dashboard.pdf");
// Configure form-based login
var formLogin = new ChromeHttpLoginCredentials
{
    LoginFormUrl = "https://example.com/login",
    LoginFormData = new Dictionary<string, string>
    {
        {"username", "user@example.com"},
        {"password", "securePassword123"},
        {"rememberMe", "true"},
        {"csrf_token", "abc123"} // Include any hidden fields
    }
};

var renderer = new ChromePdfRenderer
{
    LoginCredentials = formLogin,
    RenderingOptions = new ChromePdfRenderOptions
    {
        RenderDelay = 3000, // Allow time for login redirect
        EnableJavaScript = true
    }
};

// The actual page you want to convert (after login)
var pdf = renderer.RenderUrlAsPdf("https://example.com/dashboard");
pdf.SaveAs("dashboard.pdf");
Imports System.Collections.Generic

' Configure form-based login
Dim formLogin As New ChromeHttpLoginCredentials With {
    .LoginFormUrl = "https://example.com/login",
    .LoginFormData = New Dictionary(Of String, String) From {
        {"username", "user@example.com"},
        {"password", "securePassword123"},
        {"rememberMe", "true"},
        {"csrf_token", "abc123"} ' Include any hidden fields
    }
}

Dim renderer As New ChromePdfRenderer With {
    .LoginCredentials = formLogin,
    .RenderingOptions = New ChromePdfRenderOptions With {
        .RenderDelay = 3000, ' Allow time for login redirect
        .EnableJavaScript = True
    }
}

' The actual page you want to convert (after login)
Dim pdf = renderer.RenderUrlAsPdf("https://example.com/dashboard")
pdf.SaveAs("dashboard.pdf")
$vbLabelText   $csharpLabel

어떤 양식 데이터를 수집해야 하나요?

HTML 폼을 통해 성공적으로 인증하려면 모든 폼 입력값을 캡처해야 합니다.

// Use this helper method to extract form fields
public Dictionary<string, string> ExtractFormFields(string loginPageHtml)
{
    var formData = new Dictionary<string, string>();
    var doc = new HtmlDocument();
    doc.LoadHtml(loginPageHtml);

    // Find all input fields
    var inputs = doc.DocumentNode.SelectNodes("//input");
    if (inputs != null)
    {
        foreach (var input in inputs)
        {
            string name = input.GetAttributeValue("name", "");
            string value = input.GetAttributeValue("value", "");
            string type = input.GetAttributeValue("type", "text");

            if (!string.IsNullOrEmpty(name))
            {
                // Handle different input types
                switch (type.ToLower())
                {
                    case "checkbox":
                        if (input.Attributes["checked"] != null)
                            formData[name] = "on";
                        break;
                    case "radio":
                        if (input.Attributes["checked"] != null)
                            formData[name] = value;
                        break;
                    default:
                        formData[name] = value;
                        break;
                }
            }
        }
    }

    // Don't forget select elements
    var selects = doc.DocumentNode.SelectNodes("//select");
    if (selects != null)
    {
        foreach (var select in selects)
        {
            string name = select.GetAttributeValue("name", "");
            var selected = select.SelectSingleNode(".//option[@selected]");
            if (selected != null && !string.IsNullOrEmpty(name))
            {
                formData[name] = selected.GetAttributeValue("value", "");
            }
        }
    }

    return formData;
}
// Use this helper method to extract form fields
public Dictionary<string, string> ExtractFormFields(string loginPageHtml)
{
    var formData = new Dictionary<string, string>();
    var doc = new HtmlDocument();
    doc.LoadHtml(loginPageHtml);

    // Find all input fields
    var inputs = doc.DocumentNode.SelectNodes("//input");
    if (inputs != null)
    {
        foreach (var input in inputs)
        {
            string name = input.GetAttributeValue("name", "");
            string value = input.GetAttributeValue("value", "");
            string type = input.GetAttributeValue("type", "text");

            if (!string.IsNullOrEmpty(name))
            {
                // Handle different input types
                switch (type.ToLower())
                {
                    case "checkbox":
                        if (input.Attributes["checked"] != null)
                            formData[name] = "on";
                        break;
                    case "radio":
                        if (input.Attributes["checked"] != null)
                            formData[name] = value;
                        break;
                    default:
                        formData[name] = value;
                        break;
                }
            }
        }
    }

    // Don't forget select elements
    var selects = doc.DocumentNode.SelectNodes("//select");
    if (selects != null)
    {
        foreach (var select in selects)
        {
            string name = select.GetAttributeValue("name", "");
            var selected = select.SelectSingleNode(".//option[@selected]");
            if (selected != null && !string.IsNullOrEmpty(name))
            {
                formData[name] = selected.GetAttributeValue("value", "");
            }
        }
    }

    return formData;
}
Imports HtmlAgilityPack

Public Function ExtractFormFields(loginPageHtml As String) As Dictionary(Of String, String)
    Dim formData As New Dictionary(Of String, String)()
    Dim doc As New HtmlDocument()
    doc.LoadHtml(loginPageHtml)

    ' Find all input fields
    Dim inputs = doc.DocumentNode.SelectNodes("//input")
    If inputs IsNot Nothing Then
        For Each input In inputs
            Dim name As String = input.GetAttributeValue("name", "")
            Dim value As String = input.GetAttributeValue("value", "")
            Dim type As String = input.GetAttributeValue("type", "text")

            If Not String.IsNullOrEmpty(name) Then
                ' Handle different input types
                Select Case type.ToLower()
                    Case "checkbox"
                        If input.Attributes("checked") IsNot Nothing Then
                            formData(name) = "on"
                        End If
                    Case "radio"
                        If input.Attributes("checked") IsNot Nothing Then
                            formData(name) = value
                        End If
                    Case Else
                        formData(name) = value
                End Select
            End If
        Next
    End If

    ' Don't forget select elements
    Dim selects = doc.DocumentNode.SelectNodes("//select")
    If selects IsNot Nothing Then
        For Each selectNode In selects
            Dim name As String = selectNode.GetAttributeValue("name", "")
            Dim selected = selectNode.SelectSingleNode(".//option[@selected]")
            If selected IsNot Nothing AndAlso Not String.IsNullOrEmpty(name) Then
                formData(name) = selected.GetAttributeValue("value", "")
            End If
        Next
    End If

    Return formData
End Function
$vbLabelText   $csharpLabel

올바른 양식 액션 URL을 어떻게 찾나요?

폼 액션 URL은 인증 성공에 매우 중요합니다.

public string ExtractFormAction(string loginPageUrl, string loginPageHtml)
{
    var doc = new HtmlDocument();
    doc.LoadHtml(loginPageHtml);

    // Find the login form
    var form = doc.DocumentNode.SelectSingleNode("//form[contains(@action, 'login') or contains(@id, 'login') or contains(@class, 'login')]");

    if (form == null)
    {
        // Try finding any form with password field
        form = doc.DocumentNode.SelectSingleNode("//form[.//input[@type='password']]");
    }

    if (form != null)
    {
        string action = form.GetAttributeValue("action", "");

        // Resolve relative URLs
        if (!string.IsNullOrEmpty(action))
        {
            var baseUri = new Uri(loginPageUrl);
            var actionUri = new Uri(baseUri, action);
            return actionUri.ToString();
        }
    }

    // Default to the login page URL if no action found
    return loginPageUrl;
}
public string ExtractFormAction(string loginPageUrl, string loginPageHtml)
{
    var doc = new HtmlDocument();
    doc.LoadHtml(loginPageHtml);

    // Find the login form
    var form = doc.DocumentNode.SelectSingleNode("//form[contains(@action, 'login') or contains(@id, 'login') or contains(@class, 'login')]");

    if (form == null)
    {
        // Try finding any form with password field
        form = doc.DocumentNode.SelectSingleNode("//form[.//input[@type='password']]");
    }

    if (form != null)
    {
        string action = form.GetAttributeValue("action", "");

        // Resolve relative URLs
        if (!string.IsNullOrEmpty(action))
        {
            var baseUri = new Uri(loginPageUrl);
            var actionUri = new Uri(baseUri, action);
            return actionUri.ToString();
        }
    }

    // Default to the login page URL if no action found
    return loginPageUrl;
}
Imports System

Public Function ExtractFormAction(loginPageUrl As String, loginPageHtml As String) As String
    Dim doc As New HtmlDocument()
    doc.LoadHtml(loginPageHtml)

    ' Find the login form
    Dim form = doc.DocumentNode.SelectSingleNode("//form[contains(@action, 'login') or contains(@id, 'login') or contains(@class, 'login')]")

    If form Is Nothing Then
        ' Try finding any form with password field
        form = doc.DocumentNode.SelectSingleNode("//form[.//input[@type='password']]")
    End If

    If form IsNot Nothing Then
        Dim action As String = form.GetAttributeValue("action", "")

        ' Resolve relative URLs
        If Not String.IsNullOrEmpty(action) Then
            Dim baseUri As New Uri(loginPageUrl)
            Dim actionUri As New Uri(baseUri, action)
            Return actionUri.ToString()
        End If
    End If

    ' Default to the login page URL if no action found
    Return loginPageUrl
End Function
$vbLabelText   $csharpLabel

폼 기반 인증에서 흔히 발생하는 문제점은 무엇인가요?

  1. CSRF 토큰 : 많은 양식에는 만료되는 위조 방지 토큰이 포함되어 있습니다.
  2. JavaScript 유효성 검사 : 일부 양식은 JavaScript 실행을 필요로 합니다.
  3. 다단계 인증 : 여러 페이지를 필요로 하는 양식
  4. CAPTCHA 보호 : 사람 인증 문제
  5. 세션 시간 초과 : 로그인 세션이 빠르게 만료되는 경우

봇 방지 보호는 어떻게 설정해야 하나요?

최신 웹사이트는 자동 로그인 방지 기능을 구현하고 있습니다.

// Strategies for handling anti-bot measures
var renderer = new ChromePdfRenderer
{
    RenderingOptions = new ChromePdfRenderOptions
    {
        // Mimic real browser behavior
        ViewPortWidth = 1920,
        ViewPortHeight = 1080,
        EnableJavaScript = true,
        RenderDelay = 5000, // Wait for JavaScript

        // Set a real user agent
        CustomHttpHeaders = new Dictionary<string, string>
        {
            {"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}
        }
    }
};

 // For sites with rate limiting, add delays between requests
 System.Threading.Thread.Sleep(2000);
// Strategies for handling anti-bot measures
var renderer = new ChromePdfRenderer
{
    RenderingOptions = new ChromePdfRenderOptions
    {
        // Mimic real browser behavior
        ViewPortWidth = 1920,
        ViewPortHeight = 1080,
        EnableJavaScript = true,
        RenderDelay = 5000, // Wait for JavaScript

        // Set a real user agent
        CustomHttpHeaders = new Dictionary<string, string>
        {
            {"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}
        }
    }
};

 // For sites with rate limiting, add delays between requests
 System.Threading.Thread.Sleep(2000);
' Strategies for handling anti-bot measures
Dim renderer = New ChromePdfRenderer With {
    .RenderingOptions = New ChromePdfRenderOptions With {
        ' Mimic real browser behavior
        .ViewPortWidth = 1920,
        .ViewPortHeight = 1080,
        .EnableJavaScript = True,
        .RenderDelay = 5000, ' Wait for JavaScript

        ' Set a real user agent
        .CustomHttpHeaders = New Dictionary(Of String, String) From {
            {"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}
        }
    }
}

' For sites with rate limiting, add delays between requests
System.Threading.Thread.Sleep(2000)
$vbLabelText   $csharpLabel

복잡한 인증 시나리오를 포함한 HTML을 PDF로 변환하는 방법에 대한 자세한 안내는 HTML을 PDF로 변환하는 튜토리얼을 참조하세요.

MVC 인증은 어떻게 처리하나요?

다음 해결 방법은 .NET MVC 뷰를 프로그래밍 방식으로 문자열로 렌더링하여 MVC 로그인을 사용하지 않고도 뷰를 정확하게 렌더링하는 방법입니다. 이 접근 방식은 MVC Core 또는 MVC Framework 에서 CSHTML을 PDF로 변환할 때 효과적입니다.

// Converts an MVC partial view to a string
public static string RenderPartialViewToString(this Controller controller, string viewPath, object model = null)
{
    try
    {
        // Set the model
        var context = controller.ControllerContext;
        controller.ViewData.Model = model;

        using (var sw = new StringWriter())
        {
            // Find the partial view
            var viewResult = ViewEngines.Engines.FindPartialView(context, viewPath);

            if (viewResult.View == null)
            {
                throw new Exception($"Partial view {viewPath} could not be found.");
            }

            // Create a view context
            var viewContext = new ViewContext(context, viewResult.View, context.Controller.ViewData, context.Controller.TempData, sw);

            // Render the view
            viewResult.View.Render(viewContext, sw);
            viewResult.ViewEngine.ReleaseView(context, viewResult.View);

            return sw.GetStringBuilder().ToString();
        }
    }
    catch (Exception ex)
    {
        // Return error message if there is an exception
        return ex.Message;
    }
}

// Usage in an MVC Controller
public ActionResult GeneratePdf()
{
    // Render authenticated view to string
    var model = new InvoiceViewModel { /* populate model */ };
    string html = this.RenderPartialViewToString("~/Views/Invoice/Details.cshtml", model);

    // Convert to PDF
    var renderer = new ChromePdfRenderer();
    var pdf = renderer.RenderHtmlAsPdf(html);

    // Return PDF file
    return File(pdf.BinaryData, "application/pdf", "invoice.pdf");
}
// Converts an MVC partial view to a string
public static string RenderPartialViewToString(this Controller controller, string viewPath, object model = null)
{
    try
    {
        // Set the model
        var context = controller.ControllerContext;
        controller.ViewData.Model = model;

        using (var sw = new StringWriter())
        {
            // Find the partial view
            var viewResult = ViewEngines.Engines.FindPartialView(context, viewPath);

            if (viewResult.View == null)
            {
                throw new Exception($"Partial view {viewPath} could not be found.");
            }

            // Create a view context
            var viewContext = new ViewContext(context, viewResult.View, context.Controller.ViewData, context.Controller.TempData, sw);

            // Render the view
            viewResult.View.Render(viewContext, sw);
            viewResult.ViewEngine.ReleaseView(context, viewResult.View);

            return sw.GetStringBuilder().ToString();
        }
    }
    catch (Exception ex)
    {
        // Return error message if there is an exception
        return ex.Message;
    }
}

// Usage in an MVC Controller
public ActionResult GeneratePdf()
{
    // Render authenticated view to string
    var model = new InvoiceViewModel { /* populate model */ };
    string html = this.RenderPartialViewToString("~/Views/Invoice/Details.cshtml", model);

    // Convert to PDF
    var renderer = new ChromePdfRenderer();
    var pdf = renderer.RenderHtmlAsPdf(html);

    // Return PDF file
    return File(pdf.BinaryData, "application/pdf", "invoice.pdf");
}
Imports System
Imports System.IO
Imports System.Web.Mvc
Imports IronPdf

' Converts an MVC partial view to a string
Public Module ControllerExtensions
    <System.Runtime.CompilerServices.Extension>
    Public Function RenderPartialViewToString(controller As Controller, viewPath As String, Optional model As Object = Nothing) As String
        Try
            ' Set the model
            Dim context = controller.ControllerContext
            controller.ViewData.Model = model

            Using sw As New StringWriter()
                ' Find the partial view
                Dim viewResult = ViewEngines.Engines.FindPartialView(context, viewPath)

                If viewResult.View Is Nothing Then
                    Throw New Exception($"Partial view {viewPath} could not be found.")
                End If

                ' Create a view context
                Dim viewContext As New ViewContext(context, viewResult.View, context.Controller.ViewData, context.Controller.TempData, sw)

                ' Render the view
                viewResult.View.Render(viewContext, sw)
                viewResult.ViewEngine.ReleaseView(context, viewResult.View)

                Return sw.GetStringBuilder().ToString()
            End Using
        Catch ex As Exception
            ' Return error message if there is an exception
            Return ex.Message
        End Try
    End Function
End Module

' Usage in an MVC Controller
Public Class InvoiceController
    Inherits Controller

    Public Function GeneratePdf() As ActionResult
        ' Render authenticated view to string
        Dim model As New InvoiceViewModel() ' populate model
        Dim html As String = Me.RenderPartialViewToString("~/Views/Invoice/Details.cshtml", model)

        ' Convert to PDF
        Dim renderer As New ChromePdfRenderer()
        Dim pdf = renderer.RenderHtmlAsPdf(html)

        ' Return PDF file
        Return File(pdf.BinaryData, "application/pdf", "invoice.pdf")
    End Function
End Class
$vbLabelText   $csharpLabel

뷰를 직접 변환하는 대신 문자열로 렌더링하는 이유는 무엇일까요?

MVC 뷰를 문자열로 렌더링하면 다음과 같은 몇 가지 주요 이점이 있습니다.

  1. 인증 컨텍스트 : 뷰는 인증된 사용자의 컨텍스트 내에서 렌더링됩니다.
  2. 전체 MVC 파이프라인: 모든 MVC 기능이 작동하며 ViewBag, TempDataHtml 도우미를 포함합니다.
  3. 레이아웃 지원 : 마스터 페이지 및 레이아웃이 올바르게 표시됩니다.
  4. 모델 바인딩 : 복잡한 뷰 모델도 원활하게 작동합니다.
  5. 액션 필터 : 보안 및 로깅 필터가 정상적으로 실행됩니다.

이 MVC 해결 방법의 장점은 무엇인가요?

MVC 문자열 렌더링 방식은 다음과 같은 이점을 제공합니다.

  • 보안 : 내부 URL을 노출하거나 인증을 우회할 필요가 없습니다.
  • 성능 : 추가 HTTP 요청 방지
  • 일관성 : 사용자가 브라우저에서 보는 것과 동일한 출력
  • 유연성 : PDF 변환 전에 HTML을 수정할 수 있습니다.
  • 테스트 : 단위 테스트 및 HTML 생성 기능 제공

렌더링된 뷰에 모델을 전달하는 방법은 무엇인가요?

다음은 복잡한 모델을 사용한 포괄적인 예시입니다.

public class InvoiceController : Controller
{
    private readonly IInvoiceService _invoiceService;

    public async Task<ActionResult> DownloadInvoicePdf(int invoiceId)
    {
        // Load data within authenticated context
        var invoice = await _invoiceService.GetInvoiceAsync(invoiceId);

        if (invoice == null || invoice.UserId != User.Identity.GetUserId())
        {
            return HttpNotFound();
        }

        // Create view model
        var viewModel = new InvoiceDetailsViewModel
        {
            Invoice = invoice,
            Company = await _invoiceService.GetCompanyDetailsAsync(),
            LineItems = await _invoiceService.GetLineItemsAsync(invoiceId),
            TaxDetails = await _invoiceService.GetTaxDetailsAsync(invoiceId)
        };

        // Render to HTML string
        string html = this.RenderPartialViewToString("~/Views/Invoice/DetailsPdf.cshtml", viewModel);

        // Add custom styling for PDF
        html = $@"
            <html>
            <head>
                <style>
                    body {{ font-family: Arial, sans-serif; }}
                    .invoice-header {{ background-color: #f0f0f0; padding: 20px; }}
                    .line-items {{ width: 100%; border-collapse: collapse; }}
                    .line-items th, .line-items td {{ border: 1px solid #ddd; padding: 8px; }}
                </style>
            </head>
            <body>
                {html}
            </body>
            </html>";

        // Convert to PDF with options
        var renderer = new ChromePdfRenderer
        {
            RenderingOptions = new ChromePdfRenderOptions
            {
                MarginTop = 20,
                MarginBottom = 20,
                MarginLeft = 10,
                MarginRight = 10,
                PrintHtmlBackgrounds = true
            }
        };

        var pdf = renderer.RenderHtmlAsPdf(html);

        // Add metadata
        pdf.MetaData.Author = "Invoice System";
        pdf.MetaData.Title = $"Invoice #{invoice.Number}";
        pdf.MetaData.CreationDate = DateTime.Now;

        return File(pdf.BinaryData, "application/pdf", $"Invoice-{invoice.Number}.pdf");
    }
}
public class InvoiceController : Controller
{
    private readonly IInvoiceService _invoiceService;

    public async Task<ActionResult> DownloadInvoicePdf(int invoiceId)
    {
        // Load data within authenticated context
        var invoice = await _invoiceService.GetInvoiceAsync(invoiceId);

        if (invoice == null || invoice.UserId != User.Identity.GetUserId())
        {
            return HttpNotFound();
        }

        // Create view model
        var viewModel = new InvoiceDetailsViewModel
        {
            Invoice = invoice,
            Company = await _invoiceService.GetCompanyDetailsAsync(),
            LineItems = await _invoiceService.GetLineItemsAsync(invoiceId),
            TaxDetails = await _invoiceService.GetTaxDetailsAsync(invoiceId)
        };

        // Render to HTML string
        string html = this.RenderPartialViewToString("~/Views/Invoice/DetailsPdf.cshtml", viewModel);

        // Add custom styling for PDF
        html = $@"
            <html>
            <head>
                <style>
                    body {{ font-family: Arial, sans-serif; }}
                    .invoice-header {{ background-color: #f0f0f0; padding: 20px; }}
                    .line-items {{ width: 100%; border-collapse: collapse; }}
                    .line-items th, .line-items td {{ border: 1px solid #ddd; padding: 8px; }}
                </style>
            </head>
            <body>
                {html}
            </body>
            </html>";

        // Convert to PDF with options
        var renderer = new ChromePdfRenderer
        {
            RenderingOptions = new ChromePdfRenderOptions
            {
                MarginTop = 20,
                MarginBottom = 20,
                MarginLeft = 10,
                MarginRight = 10,
                PrintHtmlBackgrounds = true
            }
        };

        var pdf = renderer.RenderHtmlAsPdf(html);

        // Add metadata
        pdf.MetaData.Author = "Invoice System";
        pdf.MetaData.Title = $"Invoice #{invoice.Number}";
        pdf.MetaData.CreationDate = DateTime.Now;

        return File(pdf.BinaryData, "application/pdf", $"Invoice-{invoice.Number}.pdf");
    }
}
Imports System.Threading.Tasks
Imports System.Web.Mvc

Public Class InvoiceController
    Inherits Controller

    Private ReadOnly _invoiceService As IInvoiceService

    Public Async Function DownloadInvoicePdf(invoiceId As Integer) As Task(Of ActionResult)
        ' Load data within authenticated context
        Dim invoice = Await _invoiceService.GetInvoiceAsync(invoiceId)

        If invoice Is Nothing OrElse invoice.UserId <> User.Identity.GetUserId() Then
            Return HttpNotFound()
        End If

        ' Create view model
        Dim viewModel As New InvoiceDetailsViewModel With {
            .Invoice = invoice,
            .Company = Await _invoiceService.GetCompanyDetailsAsync(),
            .LineItems = Await _invoiceService.GetLineItemsAsync(invoiceId),
            .TaxDetails = Await _invoiceService.GetTaxDetailsAsync(invoiceId)
        }

        ' Render to HTML string
        Dim html As String = Me.RenderPartialViewToString("~/Views/Invoice/DetailsPdf.cshtml", viewModel)

        ' Add custom styling for PDF
        html = $"
            <html>
            <head>
                <style>
                    body {{ font-family: Arial, sans-serif; }}
                    .invoice-header {{ background-color: #f0f0f0; padding: 20px; }}
                    .line-items {{ width: 100%; border-collapse: collapse; }}
                    .line-items th, .line-items td {{ border: 1px solid #ddd; padding: 8px; }}
                </style>
            </head>
            <body>
                {html}
            </body>
            </html>"

        ' Convert to PDF with options
        Dim renderer As New ChromePdfRenderer With {
            .RenderingOptions = New ChromePdfRenderOptions With {
                .MarginTop = 20,
                .MarginBottom = 20,
                .MarginLeft = 10,
                .MarginRight = 10,
                .PrintHtmlBackgrounds = True
            }
        }

        Dim pdf = renderer.RenderHtmlAsPdf(html)

        ' Add metadata
        pdf.MetaData.Author = "Invoice System"
        pdf.MetaData.Title = $"Invoice #{invoice.Number}"
        pdf.MetaData.CreationDate = DateTime.Now

        Return File(pdf.BinaryData, "application/pdf", $"Invoice-{invoice.Number}.pdf")
    End Function
End Class
$vbLabelText   $csharpLabel

인증 솔루션을 구현하기 전에 IronPDF 제대로 설치되었고 라이선스 키가 구성되었는지 확인하십시오.

당신이 할 수 있는 다른 일들을 알아볼 준비가 되셨나요? PDF 변환 방법 튜토리얼 페이지를 방문하세요.

자주 묻는 질문

로그인 양식 뒤에 있는 HTML 콘텐츠를 PDF로 변환하려면 어떻게 해야 하나요?

IronPDF는 로그인 인증을 통해 HTML을 PDF로 변환하는 다양한 방법을 제공합니다. TLS 네트워크 인증을 위해 ChromeHttpLoginCredentials API를 사용하거나, System.Net.WebClient 또는 HttpClient를 사용하여 적절한 인증 헤더와 함께 HTML 콘텐츠를 다운로드한 후 IronPDF로 PDF 변환을 수행할 수 있습니다.

ChromeHttpLoginCredentials는 무엇이며 어떻게 사용하나요?

ChromeHttpLoginCredentials는 IronPDF의 네트워크 인증 처리 API입니다. ChromePdfRenderer의 LoginCredentials 속성에 사용자 이름과 비밀번호를 설정하여 이 API를 사용할 수 있습니다. 이렇게 하면 IronPDF가 비밀번호로 보호된 URL을 PDF로 렌더링할 때 자동으로 인증합니다.

PDF 변환 시 HTML 폼 기반 로그인을 처리할 수 있나요?

예, IronPDF는 HTML 폼 기반 로그인을 지원합니다. 권장되는 방법은 System.Net.WebClient 또는 HttpClient를 사용하여 로그인 프로세스를 처리하고, 인증된 HTML 콘텐츠를 다운로드한 다음, IronPDF의 RenderHtmlAsPdf 메서드를 사용하여 다운로드한 HTML을 PDF로 변환하는 것입니다.

인증된 페이지에서 이미지나 스타일시트 같은 HTML 자산을 어떻게 다운로드할 수 있나요?

다운로드한 HTML을 파싱하고 이미지 및 스타일시트와 같은 자산 URL을 추출하려면 HtmlAgilityPack을 사용할 수 있습니다. 그런 다음 System.Net.WebClient를 사용하여 동일한 인증 헤더로 각 자산을 다운로드하고, 마지막으로 IronPDF를 사용하여 전체 HTML 패키지를 PDF로 변환합니다.

인증 토큰이나 헤더를 처리하는 가장 좋은 방법은 무엇일까요?

IronPDF를 인증 토큰과 함께 사용할 때는 HttpClient 또는 WebClient를 사용하여 인증 헤더(예: Bearer 토큰)와 함께 HTML을 다운로드하세요. 인증된 HTML 콘텐츠를 메모리에 로드하거나 디스크에 저장한 후에는 IronPDF의 ChromePdfRenderer를 사용하여 PDF로 변환하세요.

MVC 로그인 인증에 대한 해결 방법이 있을까요?

네, IronPDF는 MVC 로그인 인증 시나리오에 대한 해결 방법을 제공합니다. 권장되는 방법은 먼저 표준 .NET HTTP 클라이언트를 사용하여 HTML 콘텐츠를 인증하고 다운로드한 다음, 인증된 HTML을 IronPDF의 렌더링 엔진에 직접 전달하는 것입니다. 이렇게 하면 IronPDF에서 인증을 처리하도록 할 필요가 없습니다.

커티스 차우
기술 문서 작성자

커티스 차우는 칼턴 대학교에서 컴퓨터 과학 학사 학위를 취득했으며, Node.js, TypeScript, JavaScript, React를 전문으로 하는 프론트엔드 개발자입니다. 직관적이고 미적으로 뛰어난 사용자 인터페이스를 만드는 데 열정을 가진 그는 최신 프레임워크를 활용하고, 잘 구성되고 시각적으로 매력적인 매뉴얼을 제작하는 것을 즐깁니다.

커티스는 개발 분야 외에도 사물 인터넷(IoT)에 깊은 관심을 가지고 있으며, 하드웨어와 소프트웨어를 통합하는 혁신적인 방법을 연구합니다. 여가 시간에는 게임을 즐기거나 디스코드 봇을 만들면서 기술에 대한 애정과 창의성을 결합합니다.

시작할 준비 되셨나요?
Nuget 다운로드 18,318,263 | 버전: 2026.4 방금 출시되었습니다
Still Scrolling Icon

아직도 스크롤하고 계신가요?

빠른 증거를 원하시나요? PM > Install-Package IronPdf
샘플을 실행하세요 HTML이 PDF로 변환되는 것을 지켜보세요.