์ด๋ด,
์ ๋ ์ต๊ทผ์ AWS Lambda .NET Core 2 API์์ ๋ณด์ ๋ฌธ์ ์์ฑ์ ๊ตฌํํ์ต๋๋ค. ๋๋ ๊ทธ๊ฒ์ด ํ ๋ฒ์ ์คํ๋๊ณ ์์๋ค๊ณ ๋งน์ธํ ์ ์์์ง๋ง ์ง๊ธ์ ๋จ์ํ ์คํจํ๊ณ ์์ต๋๋ค. .NET Core์ ํ์ฌ System.Drawing ๊ตฌํ์ ์ดํด๋ณด์์ต๋๋ค. ๊ทธ๋ฌ๋ ๋ด๊ฐ ์ฝ๊ณ ์๋ ๋ฐ์ ๋ฐ๋ฅด๋ฉด GDI+์ ๋ํ ๋ช ์์ ์ข ์์ฑ์ผ๋ก ์ธํด ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํ๋ซํผ ๊ฐ ํธํ์ด ๋์ง ์์ต๋๋ค. ์ถ๊ฐ ์กฐ์ฌ๋ฅผ ํตํด ์๋ Mono ํ์์ ๊ฐ๋ฐํ CompatCore.System.Drawing์ด ์์ต๋๋ค. ์๋ง๋ ์ด๊ฒ์ GDI+์ Linux ๊ธฐ๋ฐ ๊ตฌํ์ ์ฌ์ฉํ ์ ์๋ System.Drawing์ ํฌ๋ก์ค ํ๋ซํผ ๋ฒ์ ์ ๋๋ค. ๊ทธ๋ฌ๋ ์ฌ์ฉํ๋ ค๊ณ ํ๋ฉด ๋ค์ ์ค๋ฅ/์คํ ์ถ์ ์ด ๋ฐ์ํฉ๋๋ค.
System.TypeInitializationException: The type initializer for 'System.Drawing.GDIPlus' threw an exception. ---> System.DllNotFoundException: Unable to load DLL 'gdiplus': The specified module or one of its dependencies could not be found.
(Exception from HRESULT: 0x8007007E)
at System.Drawing.GDIPlus.GdiplusStartup(UInt64& token, GdiplusStartupInput& input, GdiplusStartupOutput& output)
at System.Drawing.GDIPlus..cctor()
--- End of inner exception stack trace ---
at System.Drawing.GDIPlus.GdipCreateBitmapFromScan0(Int32 width, Int32 height, Int32 stride, PixelFormat format, IntPtr scan0, IntPtr& bmp)
at System.Drawing.Bitmap..ctor(Int32 width, Int32 height, PixelFormat format)
at Ivy.Captcha.Services.CaptchaGenerationService.GenerateCaptchaImage(Int32 captchaCharLength, Int32 width, Int32 height)
at IAGE.Api.Data.Services.CaptchaApiService.GenerateCaptcha() in D:\Workspaces\iam-global-education\src\Api\IAGE.Api.Data\Services\CaptchaApiService.cs:line 63
at lambda_method.lambda_method(Closure , Object , Object[] )
at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__12.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__10.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__17.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__15.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Builder.RouterMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Elmah.Io.AspNetCore.ElmahIoMiddleware.d__6.MoveNext()
๋ค์ ์ค๋ ๋์ ๋ฐ๋ฅด๋ฉด: https://github.com/CoreCompat/CoreCompat/issues/3
runtime.linux-x64.CoreCompat.System.Drawing NuGet ํจํค์ง๋ฅผ ์ถ๊ฐํ์ฌ libgdiplus DLL ์ฐธ์กฐ๋ฅผ ํฌํจํ ์ ์์ด์ผ ํ์ต๋๋ค. ๊ทธ๋ฌ๋ ๊ทธ๊ฒ์ ๋์๊ฒ ์๋ฌด ์์ฉ์ด ์์์ต๋๋ค. ๋ง์ ์ฌ๋๋ค์ด System.Cryptography.x509Certificates์์ ๋ณธ ์ ์ฌํ ๋ฌธ์ ๊ฐ ๋๊ธฐ๋ฅผ ๋ฐ๋ผ๋ฉฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฐธ์กฐ์ ๋ค์ํ ์กฐํฉ์ ์๋ํ์ต๋๋ค. ๊ทธ๋ฌ๋ ๋๋ ์ด๊ฒ์ด ์๋ํ๋๋ก ํ๋ ์กฐํฉ์ ์์ง ์ฐพ์ง ๋ชปํ์ต๋๋ค.
AWS Lambda์์ System.Drawing ๊ธฐ๋ฅ์ ์์ํ์ฌ ์คํํ ์ ์๋ ์ฌ๋์ด ์์ต๋๊น?
Linux์์ System.Drawing์ libgdiplus๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ํด ๋ค์์ง๋ง ์๋ํ์ง๋ ์์์ต๋๋ค. Lambda์์ ์ด๋ฏธ์ง ์กฐ์์ ์ํด ์ ๋ ์ต์ ๋ฒ์ ์ Magick.NET-Q16-AnyCPU๋ฅผ ์ฌ์ฉํ์ต๋๋ค . ๋๋ ๋น์ ์ด Captcha์ ํ์ํ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๋ ์ด๋ค ๊ทธ๋ฆผ๋ ๊ทธ๋ฆฌ์ง ์์์ง๋ง ๊ทธ๊ฒ์ด ๋ช ๊ฐ์ง ๊ทธ๋ฆฌ๊ธฐ ๊ธฐ๋ฅ์ ๊ฐ์ง๊ณ ์๋ค๋ ๊ฒ์ ์์์ต๋๋ค.
๋ด ๋ฌธ์ ๋ libgdiplus๊ฐ Lambda Linux ์ธ์คํด์ค์ ๊ธฐ๋ณธ ์ค์น๊ฐ ์๋๋ผ๋ ๊ฒ์ ๋๋ค. ์๋ง๋ runtime.linux-x64.CoreCompat.System.Drawing์ ์์ฉ ํ๋ก๊ทธ๋จ ์์ ์ apt-get์ ํตํด ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํ๋ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ๋ NuGet ํจํค์ง์์ต๋๋ค. ๊ทธ๋ฌ๋ ์๋ ๋งํฌ์์ ์ด ํ๋ก์ ํธ์ ์ฝ๋๋ฅผ ์์ธํ ์ดํด๋ณธ ํ ์ด๊ฒ์ด ์ด๋ป๊ฒ ์๋ํ๋์ง ๋๋ ์ ๋์๊ฒ ์คํจํ ์ ์๋์ง ์ ํํ ์ดํดํ ์ ์์์ต๋๋ค.
๋น๋ถ๊ฐ์ ๊ณ์ํด์ ํด๊ฒฐ์ฑ ์ ์ฐพ์๋ณด๊ฒ ์ต๋๋ค. ๊ทธ๋ฌ๋ Magick.NET ๋งํฌ๋ ์ผ๋ถ ๋ณด์ ๋ฌธ์ ์ด๋ฏธ์ง๋ฅผ ์์ฑํ๋ ์ ํจํ ๋ฐฉ๋ฒ์ธ ๊ฒ ๊ฐ์ผ๋ฏ๋ก ์ด๋ฅผ ๋์ฒด ์๋จ์ผ๋ก ์ ์งํ๊ฒ ์ต๋๋ค. ๋ ๋์ ํฌ๋ก์ค ํ๋ซํผ ํธํ์ฑ์ ์ํด ๊ฐ๋ฐ๋ ๊ฒ์ผ๋ก ๋ณด์ด๋ฏ๋ก ๊ธฐ๋ณธ System.Drawing์ ์ฌ์ฉํ์ฌ ์ดํด๋ณด์์ง๋ง ์ฌ์ ํ ์ค์น๋ GDI+ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐธ์กฐํ๋ ๊ฒ์ผ๋ก ๋ณด์ ๋๋ค.
์ด Magick.NET ํจํค์ง๊ฐ Lambda์์ ์ฑ๊ณต์ ์ผ๋ก ์ฌ์ฉ๋๋ ๊ฒ์ ๋ณด์์ต๋๊น?
์ Magick.NET์ ๊ธฐ๋ณธ์ ์ผ๋ก ํฌ๋ก์ค ํ๋ซํผ ์์ ์ ์ํด ๋ช ๋ฌ ๋์ ์ ๋ฐ์ดํธ๋์์ต๋๋ค. ํ๋ก์ ํธ์ ์ถ๊ฐํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค. ์๋๋ ์ด๋ฏธ์ง ์ถ์ํ์ ๋ง๋ค๊ธฐ ์ํด ์์ฑํ Lambda ํจ์์ ๋๋ค.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Amazon.Lambda.Core" Version="1.0.0" />
<PackageReference Include="Amazon.Lambda.Serialization.Json" Version="1.1.0" />
<PackageReference Include="Amazon.Lambda.S3Events" Version="1.0.2" />
<PackageReference Include="AWSSDK.S3" Version="3.3.16.2" />
<PackageReference Include="AWSXRayRecorder.Handlers.AwsSdk" Version="2.1.0-beta" />
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="7.4.2" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Amazon.Lambda.Tools" Version="2.1.1" />
</ItemGroup>
</Project>
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Amazon.Lambda.Core;
using Amazon.Lambda.S3Events;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.XRay.Recorder.Core;
using ImageMagick;
// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]
namespace ImageResizer
{
public class Function
{
IAmazonS3 S3Client { get; set; }
/// <summary>
/// Default constructor. This constructor is used by Lambda to construct the instance. When invoked in a Lambda environment
/// the AWS credentials will come from the IAM role associated with the function and the AWS region will be set to the
/// region the Lambda function is executed in.
/// </summary>
public Function()
{
Amazon.XRay.Recorder.Handlers.AwsSdk.AWSSDKHandler.RegisterXRayForAllServices();
S3Client = new AmazonS3Client();
}
/// <summary>
/// Constructs an instance with a preconfigured S3 client. This can be used for testing the outside of the Lambda environment.
/// </summary>
/// <param name="s3Client"></param>
public Function(IAmazonS3 s3Client)
{
this.S3Client = s3Client;
}
HashSet<string> SupportedImageTypes { get; } = new HashSet<string> { ".png", ".jpg", ".jpeg" };
/// <summary>
/// This method is called for every Lambda invocation. This method takes in an S3 event object and can be used
/// to respond to S3 notifications.
/// </summary>
/// <param name="evnt"></param>
/// <param name="context"></param>
/// <returns></returns>
public async Task FunctionHandlerAsync(S3Event evnt, ILambdaContext context)
{
foreach (var record in evnt.Records)
{
var bucket = record.S3.Bucket.Name;
var key = record.S3.Object.Key;
if (key.StartsWith("thumbnails"))
{
context.Logger.LogLine($"Object s3://{bucket}/{key} is already a thumbnail");
continue;
}
if (!SupportedImageTypes.Contains(Path.GetExtension(key.ToLower())))
{
context.Logger.LogLine($"Object s3://{bucket}/{key} is not a supported image type");
continue;
}
context.Logger.LogLine($"Processing s3://{bucket}/{key}");
MemoryStream resizedImageStream;
using (var response = await this.S3Client.GetObjectAsync(bucket, key))
{
AWSXRayRecorder.Instance.BeginSubsegment("Magick Image Resize");
try
{
using (MagickImageCollection collection = new MagickImageCollection(response.ResponseStream))
{
foreach (MagickImage image in collection)
{
image.Resize(200, 200);
}
context.Logger.LogLine($" Image resized");
resizedImageStream = new MemoryStream();
collection.Write(resizedImageStream);
resizedImageStream.Position = 0;
}
}
catch(Exception e)
{
AWSXRayRecorder.Instance.AddException(e);
throw;
}
finally
{
AWSXRayRecorder.Instance.EndSubsegment();
}
}
var index = key.LastIndexOf('/');
var thumbnailKey = "thumbnails/" + (index != -1 ? key.Substring(index + 1) : key);
await this.S3Client.PutObjectAsync(new PutObjectRequest
{
BucketName = bucket,
Key = thumbnailKey,
InputStream = resizedImageStream
});
context.Logger.LogLine($" Thumbnail saved to s3://{bucket}/{thumbnailKey}");
}
}
}
}
์ด์ ๋ง Magick์ ๋น ์ ธ๋ค๊ณ ์๊ณ ๋ฉ์ง๊ฒ ์๋ํ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค. ๋น๊ต์ ๋ค์ํ ์ด๋ฏธ์ง ์กฐ์ API์ ์ก์ธ์คํ ์ ์์ ๋ฟ๋ง ์๋๋ผ ํฌ๋ก์ค ํ๋ซํผ์์ ์ ์ง์๋๋ ๊ฒ์ผ๋ก ๋ณด์ ๋๋ค. ์ ์, ๊ฐ๋ฐ ๋ ธ๋ ฅ์ ๊ฐ์ฌ๋๋ฆฝ๋๋ค. ๋ฌธ์ ์์ด ์ด ์ฅ์ ๋ฌผ์ ํด๊ฒฐํ ์ ์์ ๊ฒ์ ๋๋ค!
์ผ๋ถ ์ฌ๋๋ค์ด ์ด๋ป๊ฒ GDI+ ๊ตฌํ์ ์์ํ๊ณ ์คํํ ์ ์์๋์ง ์ฌ์ ํ ๊ถ๊ธํฉ๋๋ค. ๋ฐํ์ ์คํ ์ ์ ์ ์ ํ libgdiplus ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ Lambda์ ์ค์นํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
ImageMagick Drawables API๊ฐ ๋ด๊ฐ ๋ณด๊ณ ์๋ AWS Lambda์ ํ๋ซํผ ๊ฐ ํธํ์ด ์ ์ ํ์ง ์์ ์ ์์ต๋๋ค. ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ตฌํํ๊ณ Draw() ํจ์๋ฅผ ์คํํ๋ฉด ๋ค์ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.
ImageMagick.MagickDrawErrorException: NonconformingDrawingPrimitiveDefinition `text' @ error/draw.c/DrawImage/3402
at ImageMagick.MagickExceptionHelper.Check(IntPtr exception)
at ImageMagick.DrawingWand.NativeDrawingWand.Render()
at ImageMagick.MagickImage.Draw(IEnumerable`1 drawables)
at Ivy.Captcha.Magick.Services.MagickCaptchaGenerationService.GenerateCaptchaImage(Int32 captchaCharLength, Int32 width, Int32 height)
at IAGE.Api.Data.Services.CaptchaApiService.GenerateCaptcha() in D:\Workspaces\iam-global-education\src\Api\IAGE.Api.Data\Services\CaptchaApiService.cs:line 63
at lambda_method.lambda_method(Closure , Object , Object[] )
at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__12.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__10.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__17.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__15.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Builder.RouterMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Elmah.Io.AspNetCore.ElmahIoMiddleware.d__6.MoveNext()
๋ด๊ฐ ๋ฌด์์ ํ๋ Lambda์์ ์๋ํ๋ ๊ทธ๋ฆฌ๊ธฐ ๊ธฐ๋ฅ์ ์ป์ ์ ์๋ ๊ฒ ๊ฐ์ต๋๋ค. System.Drawing(Microsoft), CoreCompat.System.Drawing(Mono) ๋ฐ ImageMagick(3rd Party)์ ์ฌ์ฉํ๋ ค๊ณ ์๋ํ์ง๋ง ๋ชจ๋ ์์ฉ์ด ์์์ต๋๋ค. ๋ชจ๋ ์ด๋ฏธ์ง ์์ฑ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์๋ AWS Lambda๋ฅผ ์ฌ์ฉํ๋ ๋ฐ ์ด๋ ค์์ ๊ฒช๊ณ ์๋ ๋ช ๊ฐ์ง ์ฃผ์ ์์คํ ์ข ์์ฑ์ด ์๋ ๊ฒ ๊ฐ์ต๋๋ค. ๊ณ์ ์คํํ๊ณ ์ฑ๊ณตํ๋ฉด ๋ค์ ๋ณด๊ณ ํ๊ฒ ์ต๋๋ค.
Docker ์ปจํ ์ด๋๋ฅผ ์ฌ์ฉ์ ์ง์ ํ๋ ๋ฐฉ๋ฒ๊ณผ ์ ์ฌํ๊ฒ ํฅํ Lambda ๋ฐํ์์ ์ฌ์ฉ์ ์ง์ ํ ์ ์๋ ๊ธฐํ๊ฐ ์์ต๋๊น? ์ด ๋ชจ๋ ๊ฒ์ ๋ด Lambda ์ธ์คํด์ค์ libgdiplus๋ฅผ ์ค์นํ๋ ๊ฐ๋จํ ์คํ์ผ๋ก ํด๊ฒฐํ ์ ์์ต๋๋ค.
์ ๋ ๋ฐ๋ณด์ด๋ฉฐ ๋จ์ํ Google ReCaptcha๋ฅผ ์ฌ์ฉํ ์ ์์์ ๋ AWS Lambda ์๋ฒ์์ Captcha ์ด๋ฏธ์ง๋ฅผ ์์ฑํ์ง ๋ง์์ด์ผ ํ์ต๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋ด AWS Lambda๊ฐ ํ์ธ ์ฝ๋๋ง ์ฒ๋ฆฌํ๊ณ ์ด๋ค ํํ์ ๊ทธ๋ฆฌ๊ธฐ ๊ธฐ๋ฅ๋ ์ฐธ์กฐํ ํ์๊ฐ ์์ต๋๋ค.
๋๋ ์ฌ์ ํ ๋ฏธ๋์ AWS Lambda์์ ์ด๋ค ํํ์ ๊ทธ๋ฆฌ๊ธฐ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ๋ณด๊ณ ์ถ์ต๋๋ค. ๊ทธ๋ฌ๋ ๋๋ ์ด ๋ฌธ์ ๋ฅผ ๋ท์ ์ผ๋ก ๋ฏธ๋ฃจ๊ณ ๋น๋ถ๊ฐ ์ด ๋ฌธ์ ๋ฅผ ๋ด ์ชฝ์ ๋์ฐํ ๋์์ธ ๊ฒฐ์ ์ผ๋ก ์น๋ถํ ๊ฒ์ ๋๋ค.
์๋ง๋ Lambda์ ๋ฐฐํฌ ๊ณ์ธต์ ์ด๋ ์์ ์์ .Net Core์์ ์ ์ ํ ์ง์์ ์ฌ์ฉํ ์ ์์ ๋๊น์ง ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ต๋๋ค.
https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
์ Magick.NET์ ๊ธฐ๋ณธ์ ์ผ๋ก ํฌ๋ก์ค ํ๋ซํผ ์์ ์ ์ํด ๋ช ๋ฌ ๋์ ์ ๋ฐ์ดํธ๋์์ต๋๋ค. ํ๋ก์ ํธ์ ์ถ๊ฐํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค. ์๋๋ ์ด๋ฏธ์ง ์ถ์ํ์ ๋ง๋ค๊ธฐ ์ํด ์์ฑํ Lambda ํจ์์ ๋๋ค.