성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>Visual Studio - ASP.NET Core Web Application의 "Enable Docker Support" 옵션으로 달라지는 점</h1> <p> 비주얼 스튜디오에서 ASP.NET Core Web Application 프로젝트 생성 시 "<a target='tab' href='https://www.hanselman.com/blog/trying-out-container-tools-in-visual-studio-2019'>(Trying out Container Tools in Visual Studio 2019) Enable Docker Support</a>" 옵션을 (이 글에서는 Linux 모드로) 설정하고 생성하면 어떤 면들이 달라지는 걸까요?<br /> <br /> 가장 큰 차이점은, 우선 프로젝트에 다음과 같은 내용의 "Dockerfile"이 포함됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. # .NET 5: FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base FROM <span style='color: blue; font-weight: bold'>mcr.microsoft.com/dotnet/core/aspnet</span>:3.1-buster-slim AS <span style='color: blue; font-weight: bold'>base</span> WORKDIR /app EXPOSE 80 # .NET 5: FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build FROM <span style='color: blue; font-weight: bold'>mcr.microsoft.com/dotnet/core/sdk</span>:3.1-buster AS <span style='color: blue; font-weight: bold'>build</span> WORKDIR /src COPY ["WebApplication1/WebApplication1.csproj", "WebApplication1/"] RUN dotnet restore "WebApplication1/WebApplication1.csproj" COPY . . WORKDIR "/src/WebApplication1" RUN dotnet build "WebApplication1.csproj" -c Release -o /app/build FROM <span style='color: blue; font-weight: bold'>build</span> AS <span style='color: blue; font-weight: bold'>publish</span> RUN dotnet publish "WebApplication1.csproj" -c Release -o /app/publish FROM <span style='color: blue; font-weight: bold'>base</span> AS <span style='color: blue; font-weight: bold'>final</span> WORKDIR /app COPY --from=<span style='color: blue; font-weight: bold'>publish</span> /app/publish . ENTRYPOINT ["dotnet", "WebApplication1.dll"] </pre> <br /> 그리고 "Properties" 폴더에 "launchSettings.json" 파일에는 docker 프로파일이 하나 추가되고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:31909", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "WebApplication1": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:5000" }, <span style='color: blue; font-weight: bold'>"Docker": { "commandName": "Docker", "launchBrowser": true, "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", "publishAllPorts": true }</span> } } </pre> <br /> 비주얼 스튜디오에서 웹 애플리케이션 실행을 위한 환경을 기본적으로 "Docker" profile 모드로 선택하게 됩니다.<br /> <br /> <img alt='docker_vs2019_0.png' src='/SysWebRes/bbs/docker_vs2019_0.png' /><br /> <br /> <hr style='width: 50%' /><br /> <br /> 이 상태에서 "F5" 키를 눌러 디버깅을 실행하면 비주얼 스튜디오는 Dockerfile을 빌드해 이미지를 만드는 작업을 추가합니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> docker build -f "D:\temp\WebApplication1\Dockerfile" --force-rm -t webapplication1:dev <span style='color: blue; font-weight: bold'>--target base</span> --label "com.microsoft.created-by=visual-studio" --label "com.microsoft.visual-studio.project-name=WebApplication1" "D:\temp" </div><br /> <br /> 여기서 중요한 것은 "--target base"입니다. 이 때문에 Dockerfile의 내용 중에 사실상 "F5"로 만들어지는 docker 이미지는 다음의 명령어로만 이뤄집니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > # Use multi-stage builds # <a target='tab' href='https://docs.docker.com/develop/develop-images/multistage-build/'>https://docs.docker.com/develop/develop-images/multistage-build/</a> # 비주얼 스튜디오에서 개발 중에 사용될 목적의 이미지 생성 # <a target='tab' href='https://docs.docker.com/engine/reference/commandline/build/#specifying-target-build-stage---target'>https://docs.docker.com/engine/reference/commandline/build/#specifying-target-build-stage---target</a> FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim <span style='color: blue; font-weight: bold'>AS base</span> WORKDIR /app EXPOSE 80 # 이하 나머지 명령어는 사용되지 않음 ...[생략]... </pre> <br /> 물론 저 이미지에는 ASP.NET Core 웹 애플리케이션의 빌드 파일들이 없습니다. 그래도 괜찮습니다. 어차피 나중에 "docker run"으로 실행할 때 다음과 같이 "-v" 옵션들을 이용해 프로젝트 폴더를 컨테이너에 공유하기 때문입니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> docker run -dt -v "%USERPROFILE%\vsdbg\vs2017u5:/remote_debugger:rw" -v "D:\temp\WebApplication1:/app" -v "D:\temp:/src" -v "F:\nuget_root:/root/.nuget/fallbackpackages" -v "c:\program files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages2" -e "DOTNET_USE_POLLING_FILE_WATCHER=1" -e "ASPNETCORE_ENVIRONMENT=Development" -e "NUGET_PACKAGES=/root/.nuget/fallbackpackages" -e "NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages;/root/.nuget/fallbackpackages2" -P --name WebApplication1 --entrypoint tail webapplication1:dev -f /dev/null<br /> </div><br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > -v "%USERPROFILE%\vsdbg\vs2017u5:/remote_debugger:rw" -v "D:\temp\WebApplication1:/app" -v "D:\temp:/src" -v "F:\nuget_root:/root/.nuget/fallbackpackages" -v "c:\program files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages2" </pre> <br /> 위와 같이 공유되는 관계로, Docker Desktop에서는 해당 드라이브들을 공유시켜둬야 합니다. (제 경우에는 그냥 모두 체크해 두었습니다.)<br /> <br /> <img alt='docker_vs2019_1.png' src='/SysWebRes/bbs/docker_vs2019_1.png' /><br /> <br /> 여기서 끝이 아닙니다. 비주얼 스튜디오는 실행 중인 docker 컨테이너에 다음과 같이 "dotnet" 프로세스를 시작해 웹 애플리케이션을 구동합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > docker exec -itd f71a5003400e dotnet /app/bin/Debug/netcoreapp3.1/WebApplication1.dll </pre> <br /> <hr style='width: 50%' /><br /> <br /> F5 디버깅을 끝내고 나면, 비주얼 스튜디오는 해당 컨테이너를 실행 중인 상태로 두고 내부의 "dotnet" 프로세스만 종료시킵니다. 그리고, 다음번 F5 디버깅을 시작하게 되면, 컨테이너에 기존 실행 중인 닷넷 프로세스가 있다면 확실하게 종료시키는 명령어를 수행하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // PowerShell에서 수행 docker exec -i f71a5003400e /bin/sh -c "if PID=$(pidof dotnet); then kill $PID; fi" </pre> <br /> 다시 dotnet 프로세스를 띄우는 작업만 반복합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > docker exec -itd f71a5003400e dotnet /app/bin/Debug/netcoreapp3.1/WebApplication1.dll </pre> <br /> 정리해 보면, 비주얼 스튜디오는 docker 지원 프로젝트에 대해 (최초 한 번 이미지를 빌드하는 시간과 컨테이너 실행 시간을 제외하고) 아무런 지연 현상 없이, 따라서 기존과 동일한 성능의 개발 환경을 제공하는 것입니다. <br /> <br /> <hr style='width: 50%' /><br /> <br /> 이렇게 자동으로 생성된 이미지를,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\temp> <span style='color: blue; font-weight: bold'>docker images</span> REPOSITORY TAG IMAGE ID CREATED SIZE webapplication1 dev 54e29ea338cc 3 hours ago 207MB </pre> <br /> 직접 실행하고 싶다면 비주얼 스튜디오가 해준 동작을 그대로 재연하기만 하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > docker run -dt -v "%USERPROFILE%\vsdbg\vs2017u5:/remote_debugger:rw" -v "D:\temp\WebApplication1:/app" -v "D:\temp:/src" -v "F:\nuget_root:/root/.nuget/fallbackpackages" -v "c:\program files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages2" -e "DOTNET_USE_POLLING_FILE_WATCHER=1" -e "ASPNETCORE_ENVIRONMENT=Development" -e "NUGET_PACKAGES=/root/.nuget/fallbackpackages" -e "NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages;/root/.nuget/fallbackpackages2" -P --name WebApplication1 --entrypoint tail webapplication1:dev -f /dev/null docker exec -itd f71a5003400e dotnet /app/bin/Debug/netcoreapp3.1/WebApplication1.dll </pre> <br /> 하지만, <a target='tab' href='https://www.sysnet.pe.kr/2/0/11204#kitematic'>Kitematic 도구</a>에서는 단순히 이미지를 "CREATE"로 하게 되면 VOLUME 매핑이 없으므로 그냥 빈 깡통에 불과한 "aspnet:3.1-buster-slim" 이미지의 컨테이너만 실행됩니다. Kitematic에서도 "CREATE"로 실행 가능한 유형의 이미지를 만들려면 Dockerfile의 "base"가 아닌 "final"을 target으로 지정하면 됩니다. 그리고 이 작업을 간단하게 할 수 있는 것이, 비주얼 스튜디오에서 Dockerfile을 마우스 우 클릭해 "Build Docker Image" 메뉴를 선택하는 것입니다.<br /> <br /> <img alt='docker_vs2019_2.png' src='/SysWebRes/bbs/docker_vs2019_2.png' /><br /> <br /> Dockerfile의 final 타깃을 선택하면 우선, 다음의 작업이 선택됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > FROM <span style='color: blue; font-weight: bold'>base</span> AS <span style='color: blue; font-weight: bold'>final</span> WORKDIR /app COPY <span style='color: blue; font-weight: bold'>--from=publish</span> /app/publish . ENTRYPOINT ["dotnet", "WebApplication1.dll"] </pre> <br /> 하지만 final의 선행 작업으로 base 타깃이 지정되었기에 그에 앞서 base 먼저 구성됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base WORKDIR /app EXPOSE 80 </pre> <br /> 이후, final 타깃의 "COPY --from=publish /app/publish ."로 인해 "publish" 타깃이 다시 구성되고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > FROM build AS <span style='color: blue; font-weight: bold'>publish</span> RUN <span style='color: blue; font-weight: bold'>dotnet publish</span> "WebApplication1.csproj" -c Release -o /app/publish </pre> <br /> 이보다 앞서 "FROM build ..."로 인해 build 타깃까지 올라가게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS <span style='color: blue; font-weight: bold'>build</span> WORKDIR /src COPY ["WebApplication1/WebApplication1.csproj", "WebApplication1/"] RUN <span style='color: blue; font-weight: bold'>dotnet restore</span> "WebApplication1/WebApplication1.csproj" COPY . . WORKDIR "/src/WebApplication1" RUN <span style='color: blue; font-weight: bold'>dotnet build</span> "WebApplication1.csproj" -c Release -o /app/build </pre> <br /> 결국, Dockerfile의 final 타깃은 다음과 같은 작업을 순차적으로 진행하는 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [build 타깃] .NET Core SDK 이미지를 다운로드한 임시 이미지를 만들고, 그 이미지 내부에 csproj 파일을 복사, csproj 파일의 정보를 기반으로 "dotnet restore", 모든 소스 코드 파일을 복사, "dotnet build" 수행 [publish 타깃] "dotnet publish" 수행 [final 타깃] dotnet/core/aspnet 이미지에 [publish 타깃]에서 생성된 이미지로부터 "dotnet publish"의 결과물을 복사 컨테이너로 인스턴스화 될 때 실행될 ENTRYPOINT를 "dotnet WebApplication1.dll"로 등록. </pre> <br /> 물론 Visual Studio의 "Build Docker Image" 메뉴가 아닌, 직접 명령행을 통해 "docker build"로 다음과 같이 실행해 final 이미지를 만들어도 됩니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> docker build -f "D:\temp\WebApplication1\Dockerfile" --force-rm -t webapplication1:dev <span style='color: blue; font-weight: bold'>--target final</span> --label "com.microsoft.created-by=visual-studio" --label "com.microsoft.visual-studio.project-name=WebApplication1" "D:\temp" </div><br /> <br /> 이렇게 생성한 final 이미지를 Kitematic에서 선택해 "CREATE" 버튼을 누르면 컨테이너가 인스턴스화되고 "dotnet WebApplication1.dll" ENTRYPOINT로 인해 ASP.NET 웹 응용 프로그램이 내부에서 실행까지 됩니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> Visual Studio는 기본적으로 빌드 시에 Dockerfile의 "base" 타깃을 사용한다고 했는데, 이것을 바꾸는 것도 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Container Tools build properties ; <a target='tab' href='https://learn.microsoft.com/en-us/visualstudio/containers/container-msbuild-properties?view=vs-2019'>https://learn.microsoft.com/en-us/visualstudio/containers/container-msbuild-properties?view=vs-2019</a> </pre> <br /> 이를 위해 csproj에 DockerfileFastModeStage 속성을 추가해 Dockerfile에 있는 target을 정해줄 수 있습니다. 가령, 아래와 같이 "final"로 지정하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <DockerfileFastModeStage>final</DockerfileFastModeStage> </pre> <br /> 비주얼 스튜디오는 빌드 시마다 위에서 설명한 "final" 이미지를 구축하게 됩니다. (물론 이렇게 하면 개발 시 꽤나 불편한 사용자 경험을 하게 되므로, 대개의 경우 약간의 사용자 정의 작업을 추가한 타깃을 지정할 때 쓰게 될 것입니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 자, 그럼 빌드 오류를 하나씩 살펴볼까요? ^^<br /> <br /> 우선, 다음과 같은 식으로 오류가 발생한다면?<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> ...[생략]...<br /> 1>SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.<br /> 1>CTC1008 : Downloading the debugger vsdbg failed. Please ensure that your computer has an internet connection.<br /> 1>F:\nuget_root\microsoft.visualstudio.azure.containers.tools.targets\1.9.10\build\Container.targets(198,5): error CTC1001: <span style='color: blue; font-weight: bold'>Volume sharing is not enabled.</span> On the Settings screen in Docker Desktop, click Shared Drives, and select the drive(s) containing your project files. 1>Done building project "docker2webapp.csproj" -- FAILED.<br /> </div><br /> <br /> "%USERPROFILE%\vsdbg\vs2017u5:/remote_debugger:rw"에 해당하는 드라이브가 Docker Desktop에 공유가 안 되었기 때문입니다. 참고로, 비주얼 스튜디오는 다음과 같은 경로를 컨테이너의 볼륨으로 공유합니다.<br /> <br /> <ul> <li>(%userprofile% 폴더가 있는) C 드라이브</li> <li>프로젝트가 있는 드라이브</li> <li>nuget 보관 폴더 (기본값인 경우 "%USERPROFILE%\.nuget": 참고 <a target='tab' href='https://www.sysnet.pe.kr/2/0/12135'>https://www.sysnet.pe.kr/2/0/12135</a>)</li> </ul> <br /> 마찬가지로 위의 목록들에 대한 드라이브 공유가 안 되어 있으면 이런 식의 오류도 발생합니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> 1>------ Build started: Project: docker2webapp, Configuration: Debug Any CPU ------<br /> ...[생략]...<br /> 1>docker run -dt -v "%USERPROFILE%\vsdbg\vs2017u5:/remote_debugger:rw" -v "D:\workshop2\JenniferV\gitlab\UnitTest\AzureWebApps\AzureWebTest\docker2webapp:/app" -v "D:\workshop2\JenniferV\gitlab\UnitTest\AzureWebApps\AzureWebTest:/src" -v "F:\nuget_root:/root/.nuget/fallbackpackages" -v "c:\program files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages2" -e "DOTNET_USE_POLLING_FILE_WATCHER=1" -e "ASPNETCORE_ENVIRONMENT=Development" -e "NUGET_PACKAGES=/root/.nuget/fallbackpackages" -e "NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages;/root/.nuget/fallbackpackages2" -P --name docker2webapp --entrypoint tail docker2webapp:dev -f /dev/null<br /> <span style='color: blue; font-weight: bold'>1>docker: Error response from daemon: status code not OK but 500: {"Message":"Unhandled exception: Drive has not been shared"}.</span> 1>See 'docker run --help'.<br /> 1>F:\nuget_root\microsoft.visualstudio.azure.containers.tools.targets\1.9.10\build\Container.targets(198,5): error CTC1015: Docker command failed with exit code 125.<br /> 1>F:\nuget_root\microsoft.visualstudio.azure.containers.tools.targets\1.9.10\build\Container.targets(198,5): error CTC1015: docker: Error response from daemon: status code not OK but 500: {"Message":"Unhandled exception: Drive has not been shared"}.<br /> 1>F:\nuget_root\microsoft.visualstudio.azure.containers.tools.targets\1.9.10\build\Container.targets(198,5): error CTC1015: See 'docker run --help'.<br /> 1>F:\nuget_root\microsoft.visualstudio.azure.containers.tools.targets\1.9.10\build\Container.targets(198,5): error CTC1015: If the error persists, try restarting Docker Desktop.<br /> 1>Done building project "docker2webapp.csproj" -- FAILED.<br /> ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========<br /> </div><br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
3343
(왼쪽의 숫자를 입력해야 합니다.)