티스토리 뷰

 

참고 : 이 문서는 macos 를 기준으로 작성 되었다. 운영체제만 다를뿐 대부분 비슷비슷 하다.

           C#, ASP 등 MS 관련 플렛폼에 대한 사전 지식이 없이 검색으로 시작 하였기 때문에, 비효율적이거나

           잘못 설명 되는 부분이 많을 것이다. 검색 -> 테스트 코드 작성 -> 실행 -> 게시물 작성 의 반복...

           진행중 시행 착오를 포함 하고, 문제를 해결 하는 과정을 담은 문서라, 다소 길고 지저분 하다.

           멍청한 짓일 지도 모르겠으나, 본인의 경우 이전에 Spring Boot 로 개발한 경험으로 가능한 부분들은

           Spring Boot 스타일의 설정이나 개발 패턴을 따라 가보려고 한다.

 

 

 

1. 비어 있는(ASP.NET Core Empty) 프로젝트 생성 

터미널을 열고 아래의 명령어를 입력 한다.

 

freecatz:vscode $ dotnet --info
.NET SDK(global.json 반영):
 Version:   6.0.200
 Commit:    4c30de7899

런타임 환경:
 OS Name:     Mac OS X
 OS Version:  12.2
 OS Platform: Darwin
 RID:         osx.12-x64
 Base Path:   /usr/local/share/dotnet/sdk/6.0.200/

Host (useful for support):
  Version: 6.0.2
  Commit:  839cdfb0ec

.NET SDKs installed:
  6.0.200 [/usr/local/share/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 6.0.2 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 6.0.2 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

To install additional .NET runtimes or SDKs:
  https://aka.ms/dotnet-download

freecatz:vscode $ dotnet --version
6.0.200

freecatz:vscode $ pwd
/Users/freecatz/devel/workspace/vscode
freecatz:vscode $ dotnet new web -o helloWorld
"ASP.NET Core Empty" 템플릿이 성공적으로 생성되었습니다.


생성 후 작업 처리 중...
/Users/freecatz/devel/workspace/vscode/helloWorld/helloWorld.csproj에서 'dotnet restore' 실행 중 ...
  복원할 프로젝트를 확인하는 중...
/Users/freecatz/devel/workspace/vscode/helloWorld/helloWorld.csproj을(를) 86 ms 동안 복원했습니다.
복원에 성공했습니다.

 

프로젝트의 템플릿은 아래의 "더보기" 를 클릭 하여 확인 하거나, 터미널에서 "dotnet new --list" 명령을 이용 하도록 한다.

 

 

생성된 프로젝트를 vscode 에서 열도록(open) 한다.

 

새로운 터미널을 열고, 아래의 명령어를 이용하여 프로젝트가 잘 동작 하는지 확인 한다.

 

freecatz:helloWorld $ dotnet watch run
watch : Hot reload enabled. For a list of supported edits, see https://aka.ms/dotnet/hot-reload. Press "Ctrl + R" to restart.
watch : Building...
  복원할 프로젝트를 확인하는 중...
  복원할 모든 프로젝트가 최신 상태입니다.
  helloWorld -> /Users/freecatz/devel/workspace/vscode/helloWorld/bin/Debug/net6.0/helloWorld.dll
watch : Started
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://localhost:7194
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5143
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/freecatz/devel/workspace/vscode/helloWorld/

 

정상적으로 비어 있는 프로젝트가 생성 되었다.

 

 

2. application 의 port 변경 

위의 그림과 같이 launchSettions.json 파일의 "applicationUrl" 의 값을 수정 할 것이다.

 

https 는 나중에 nginx 에서 처리 해 줄 것이라 생각하고 https 항목을 삭제 후, 포트를 8080으로 변경 하였다.

launchBrowser 항목이 true 인 경우 자동으로 브라우저를 호출해 주는 것으로 보인다.

터미널에서 "dotnet watch run" 명령으로 실행해 보도록 하자.

 

 

8080 포트로 변경이 되었다.

 

 

 

3.  내가 조금 더 알아 보기 쉬운 코드로 변경

ASP.NET Core Empty 프로젝트에서 기본 샘플로 제공되는 코드는 dotnet 경험이 있는 개발자들에게 보기 편하지만, 본인 처럼 처음 접하는 개발자에게는 다소 이해 하기 어려운 부분이 있어, 대체 할 수 있는 코드를 검색해 적용 해 보았다.

 

위의 코드를 아래의 그림과 같이 변경 하였다.

 

사용된 코드는 아래의 "더보기" 를 클릭 하여 확인 하도록 한다.

 

더보기
/*
 * 기존에 제공된 샘플 코드
 */ 
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();


/*
 * 변경된 코드
 */ 
public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        var app = builder.Build();

        app.MapGet("/", () => "Hello World!");

        app.Run();
    }
}

 

변경한 코드가 정상 구동 되는지 터미널에서 실행 하여 확인해 보자.

 

freecatz:helloWorld $ dotnet clean
...중략...
     1>"/Users/freecatz/devel/workspace/vscode/helloWorld/helloWorld.csproj" 프로젝트를 빌드했습니다(Clean 대상).

빌드했습니다.
    경고 0개
    오류 0개

경과 시간: 00:00:00.54

freecatz:helloWorld $ dotnet watch run

 

정상적으로 동작 한다.

 

 

4.  static file hosting 을 위한 설정

이제 변경이 없는 이미지나 자바스크립트 파일과 같은 정적파일 호스팅을 위한 설정을 해보도록 하자.

 

 

위의 그림과 같이 프로젝트 루트 디렉토리에 static 디렉토리를 만들고 적당한 이미지 파일을 하나 넣어 두도록 한다.

 

 

 

그리고 Program.cs 파일을 수정 하도록 한다. 위에서 사용된 코드는 아래의 "더보기" 를 클릭 하여 복사 할 수 있다.

 

더보기
using Microsoft.Extensions.FileProviders; // 추가된 부분
public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        var app = builder.Build();

        // 추가된 부분
        app.UseStaticFiles(new StaticFileOptions()
        {
            FileProvider = new PhysicalFileProvider(
                Path.Combine(Directory.GetCurrentDirectory(), @"static")),
            RequestPath = new PathString("/static")
        });

        app.MapGet("/", () => "Hello World!");

        app.Run();
    }
}

 

그리고 프로젝트를 실행 하여 브라우저에서 확인 해보독 하자.

 

정상적으로 정적 파일들을 호출 할 수 있게 되었다.

 

중요!!!!

이 글을 쓰던 중 알게된 부분인데, 위의 Program.cs 파일의 설정대로 닷넷 런타임을 포함한 단일 파일로 배포를 하게 되는 경우 실제 웹 경로에서는 프로그램이 실행 되는 위치에서 static 디렉토리를 참조 하는데 있어 문제가 발생 하였다. 닷넷 런타임을 포함한 단일 파일로 빌드 하기 위해 아래의 명령을 사용 하였다.

freecatz:helloWorld $ dotnet publish -c Release -r osx-x64 -p:PublishSingleFile=true --self-contained true
.NET용 Microsoft (R) Build Engine 버전 17.1.0+ae57d105c
Copyright (C) Microsoft Corporation. All rights reserved.

  복원할 프로젝트를 확인하는 중...
  복원할 모든 프로젝트가 최신 상태입니다.
  helloWorld -> /Users/freecatz/devel/workspace/vscode/helloWorld/bin/Release/net6.0/osx-x64/helloWorld.dll
  helloWorld -> /Users/freecatz/devel/workspace/vscode/helloWorld/bin/Release/net6.0/osx-x64/publish/
freecatz:helloWorld $ cd ./bin/Release/net6.0/osx-x64/publish/
freecatz:publish $ ./helloWorld 
...길어서 중략...
Unhandled exception. System.IO.DirectoryNotFoundException: /Users/freecatz/devel/workspace/vscode/helloWorld/bin/Release/net6.0/osx-x64/publish/static/
   at Microsoft.Extensions.FileProviders.PhysicalFileProvider..ctor(String root, ExclusionFilters filters)
   at Program.Main(String[] args) in /Users/freecatz/devel/workspace/vscode/helloWorld/Program.cs:line 66
Abort trap: 6
freecatz:publish $ 

이 문제를 해결 하기 위해서 여러 문서를 찾아 보았지만, 아직 찾지 못한 상태이며 다음과 같은 과정을 통해서 해결 하였다.

 

Program.cs 파일 수정 

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        var app = builder.Build();

        // 삭제 할 부분
        // app.UseStaticFiles(new StaticFileOptions()
        // {
        //     FileProvider = new PhysicalFileProvider(
        //         Path.Combine(Directory.GetCurrentDirectory(), @"static")),
        //     RequestPath = new PathString("/static")
        // });

        // 추가된 부분 정적 리소스(이미지, 자바스크립트, css ...) 호스팅 : 프로젝트의 wwwroot 를 참조
        app.UseStaticFiles();

        app.MapGet("/", () => "Hello World!");

        app.Run();
    }
}

위와 같이 소스 수정 후, 정적 파일을 담기 위해 만들었던 static 디렉토리의 이름을 wwwroot 로 변경 한다.

 

 

그리고 닷넷 런타임을 포함한 단일 파일로 빌드 후 정적 파일을 다시 한번 호출 하여 확인 한다.

 

 

이전의 소스 설정대로 static 디렉토리의 사용 하고 싶다면, <프로젝트 이름>.csproj 파일을 수정 하는 방법도 있다.

 

이 프로젝트의 이름은 helloWorld 이므로, 프로젝트 디렉토리 에서 helloWorld.csproj 파일을 수정 한다.

 

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <!-- 추가된 부분 -->
  <ItemGroup>
    <Content Include="static\**\*">
        <CopyToPublishDirectory>Always</CopyToPublishDirectory>
    </Content>
  </ItemGroup> 

</Project>

 

위와 같이 수정 하고, 닷넷 런타임을 포함한 단일 파일로 빌드 하면 결과물 디렉토리에 static 디렉토리가 포함 된다. spring boot 처럼 단일 jar 파일에 포함 하여 배포 하는 방법이 있을거 같은데, 아직은 못찾았다. 앞으로 이 문서에서는 wwwroot 가 아닌 static 으로 사용할 것이다.

 

freecatz:helloWorld $ dotnet publish -c Release -r osx-x64 -p:PublishSingleFile=true --self-contained true
.NET용 Microsoft (R) Build Engine 버전 17.1.0+ae57d105c
Copyright (C) Microsoft Corporation. All rights reserved.

  복원할 프로젝트를 확인하는 중...
  복원할 모든 프로젝트가 최신 상태입니다.
/Users/freecatz/devel/workspace/vscode/helloWorld/Program.cs(71,13): warning CS8602: null 가능 참조에 대한 역참조입니다. [/Users/freecatz/devel/workspace/vscode/helloWorld/helloWorld.csproj]
  helloWorld -> /Users/freecatz/devel/workspace/vscode/helloWorld/bin/Release/net6.0/osx-x64/helloWorld.dll
  helloWorld -> /Users/freecatz/devel/workspace/vscode/helloWorld/bin/Release/net6.0/osx-x64/publish/
freecatz:helloWorld $ cd ./bin/Release/net6.0/osx-x64/publish/
freecatz:publish $ ls -alh
total 168544
drwxr-xr-x    7 freecatz  staff   224B  3 15 15:59 ./
drwxr-xr-x  320 freecatz  staff    10K  3 15 15:59 ../
-rw-r--r--    1 freecatz  staff   127B  3  8 16:13 appsettings.Development.json
-rw-r--r--    1 freecatz  staff   151B  3  8 16:13 appsettings.json
-rwxr-xr-x    1 freecatz  staff    82M  3 15 15:59 helloWorld*
-rw-r--r--    1 freecatz  staff    29K  3 15 15:59 helloWorld.pdb
drwxr-xr-x    4 freecatz  staff   128B  3 15 15:59 static/

 

 

 

5. 문자열을 반환 하는 Controller 의 작성 및 등록

이 부분을 작성하는 동안 아직 view 를 반환 하는 코드를 찾지는 못했지만, 컨트롤러를 실행 하여 문자열을 반환하는 형태의 코드는 찾았다.

기존의 Program.cs 파일을 아래와 같이 수정 한다.

using Microsoft.Extensions.FileProviders;

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        
        builder.Services.AddControllers(); // 추가된 부분
        
        var app = builder.Build();

        app.UseStaticFiles();

        app.MapControllers(); // 추가된 부분

        //app.MapGet("/", () => "Hello World!"); // 주석 처리

        app.Run();
    }
}

위의 코드에서 "추가된 부분" 과 "주석 처리"  부분을 살펴 보기 바란다.

 

위의 그림과 같이 프로젝트 루트에 "Controller" 디렉토리를 생성 후, "WebController.cs" 파일을 생성 하고, 아래의 "더보기" 를 클릭 하여 사용된 코드를 붙여 넣도록 한다. 

 

참고 : 디렉토리 이름이 "Controller" 마음에 들지 않으면, 변경 하여도 된다.

           파일 이름이 "WebController.cs" 마음에 들지 않으면, 변경 하여도 된다.

 

더보기
using Microsoft.AspNetCore.Mvc;

public class WebController : Controller
{
    [HttpGet]
    [Route("/")]
    public String Index()
    {
        Console.WriteLine("index...");
        return "Hello Index";
    }
}

 

프로젝트를 실행 하고, 터미널을 확인 하도록 한다.

 

freecatz:helloWorld $ dotnet watch run
watch : Hot reload enabled. For a list of supported edits, see https://aka.ms/dotnet/hot-reload. Press "Ctrl + R" to restart.
watch : Building...
  복원할 프로젝트를 확인하는 중...
  복원할 모든 프로젝트가 최신 상태입니다.
  helloWorld -> /Users/freecatz/devel/workspace/vscode/helloWorld/bin/Debug/net6.0/helloWorld.dll
watch : Started
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:8080
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/freecatz/devel/workspace/vscode/helloWorld/
index...

 

터미널에 Console.Write("index..."); 로 찍은 문자열이 나타난다.

 

프로그램에서 반환된 문자열을 브라우저를 통해서 확인 할 수 있었다.

 

 

 

6. cshtml  view 를 반환 하는 Controller 의 작성 및 등록

이번에는 cshtml 형식의 view 를 반환하는 razor 를 이용해 보도록 한다. Program.cs 파일을 아래와 같이 수정 한다.

using Microsoft.Extensions.FileProviders;

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        
        builder.Services.AddControllers();
        builder.Services.AddRazorPages(); // 추가된 부분 
        
        var app = builder.Build();

        app.UseStaticFiles();

        app.MapControllers();

        //app.MapGet("/", () => "Hello World!"); // 필요 없으므로 제거

        app.Run();
    }
}

 

그리고, 프로젝트 루트 아래 "Views" 디렉토리를 생성 한다. 

참고 : "Views" 디렉토리 이름이 마음에 들지 않으면 변경 가능 하다.

이 문서에서 사용된 index.cshtml 파일의 내용은 아래의 "더보기" 를 클릭 하여 확인 가능 하다.

 

더보기
<!DOCTYPE html>
<html lang="ko">
<head>
    <title>index file...</title>
</head>
<body onload="pageFunc.initHtml();">

    hello index

    <div>@ViewData["Message"]</div>


</body>
<script type="text/javascript" charset="UTF-8">
/* <![CDATA[ */

'use strict';

var pageFunc = {

    initHtml: function () {
        alert('index.cshtml');
    },

};

/* ]]> */
</script>
</html>

 

기존에 작성되었던 WebController.cs 파일에서 리턴 형식을 아래의 소스와 같이 변경 한다.

 

using Microsoft.AspNetCore.Mvc;

public class WebController : Controller
{
    /* 주석 처리 또는 제거
    [HttpGet]
    [Route("/")]
    public String Index()
    {
        Console.WriteLine("index...");
        return "Hello Index";
    }
    */

    [HttpGet]
    [Route("/")]
    public IActionResult Index()
    {
        ViewData["Message"] = "잘 보이나요...??";
        return View("/Views/index.cshtml");
    }
}

 

그리고 프로젝트를 다시 실행 하여 보도록 한다.

 

컨트롤러에서 ViewData 를 이용하여 넣어준 데이터도 잘 표현 되고 있다.

위의 그림에는 없지만, 이전 단계에서 추가한 정적 이미지 파일도 정상적으로 나타나는 것을 확인 하였다.

 

 

 

7. 개발 PC 의 아이피 주소로 접근 가능 하도록 코드 변경

이전에 http://localhost:8080 으로 포트를 변경 하였다. 그렇지만, http://192.168.0.1:8080 와 같이 개발중인 PC의 아이피주소로 접근이 불가능한 상태로 아이피 주소로 접근이  가능 하도록 코드를 변경해 보고자 한다. 개인적으로 웹 개발시 http://localhost:8080 으로 접근 하는 것을 선호 하지 않는다.

Program.cs 파일을 열어 아래의 코드에서 "추가된 부분" 을 확인 하고 추가 하여 준다.

 

using Microsoft.Extensions.FileProviders;
using System.Net;           // 추가된 부분 
using System.Net.Sockets;   // 추가된 부분

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Services.AddControllers();
        builder.Services.AddRazorPages();

        var app = builder.Build();

        // 추가된 부분
        app.Urls.Add("http://localhost:8080"); // 필요 없는 경우 삭제
        foreach (var ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
        {
            if (ip.AddressFamily == AddressFamily.InterNetwork)
            {
                Console.WriteLine(ip.ToString());
                app.Urls.Add("http://" + ip.ToString() +":8080");
            }
        }

        app.UseStaticFiles();

        app.MapControllers();

        app.Run();
    }
}

 

위와 같이 코드를 수정 하고 실행 하면, 아이피 주소로 접근이 가능 하게 된다. 본인의 경우 유선랜, 무선랜 모두 사용중이라 2개의 아이피 주소로 접근이 가능 하게 되었다. 실제 운영 서버에 배포시 어떤 문제가 있을지는 모르겠지만, 아직은 운영 단계까지 사용할 예정은 아니라 위의 코드로 개발을 진행 하기로 하였다.

 

참고 : 이전에 launchSettings.json 에서 설정한 부분 보다 Program.cs 에서 설정한 부분이 우선 적용 되는 것으로 보인다.

 

 

 

8. 동적 환경 설정 파일 로드를 위한 코드 수정

spring boot 에서 개발 환경과 운영 환경에 맞게 설정된 xml 파일을 로드 하여 사용 하였다. ASP.NET Core 에서도 비슷하게 되지 않을까검색 하다 보니 링크의 문서를 MSDN 에서 찾게 되었다.

프로젝트 루트에 개발 환경에서 사용할 설정이 담겨 있는 properties-dev.xml 파일과 운영 환경에서 사용할 properties-op.xml 두개를 생성해 준다. 파일 이름이 마음에 들지 않는  경우 변경 하여도 된다.

 

아래는 properties-dev.xml  파일의 내용

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <TestXml>
    <TestKey>TEST-DEV-KEY-12345-67890</TestKey>
    <TestKeyMessage>개발 환경 테스트 키 입니다.</TestKeyMessage>
  </TestXml>
</configuration>

 

아래는 properties-op.xml  파일의 내용

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <TestXml>
    <TestKey>TEST-OP-KEY-ABCDE-FGHIJ</TestKey>
    <TestKeyMessage>운영 환경 테스트 키 입니다.</TestKeyMessage>
  </TestXml>
</configuration>

 

이제 Program.cs 파일을 수정 한다.

using Microsoft.Extensions.FileProviders;
using Microsoft.AspNetCore.Http.Features;
using System.Net;
using System.Net.Sockets;

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Services.AddControllers();
        builder.Services.AddRazorPages();

        var app = builder.Build();

        // 추가된 부분 : 환경에 따른 XML 파일 로드
        builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.Sources.Clear();

            if (!app.Environment.IsDevelopment())
            {
                // 운영 환경
                config.AddXmlFile("properties-op.xml", optional: true, reloadOnChange: true);
            } else {
                // 개발 환경
                config.AddXmlFile("properties-dev.xml", optional: true, reloadOnChange: true);
            }

            config.AddEnvironmentVariables();
        });


        // 아이피 주소로 접근 가능 하도록 설정
        foreach (var ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
        {
            if (ip.AddressFamily == AddressFamily.InterNetwork)
            {
                //Console.WriteLine(ip.ToString());
                app.Urls.Add("http://" + ip.ToString() +":8080");
            }
        }

        app.UseStaticFiles();
        app.MapControllers();

        app.Run();
    }
}

추가된 부분을 확인 하도록 한다. 소스에 따르면, 개발 환경으로 구동 시에는 properties-dev.xml 파일이 적용 되고 운영 환경으로 구동 시에는 properties-op.xml 파일이 구동 되는 것으로 보인다.

 

이어서 이전에 작성 되었던 WebController.cs 파일을 아래와 같이 수정 한다.

using Microsoft.AspNetCore.Mvc;
using BaseUtil;
public class WebController : Controller
{
    private readonly IConfiguration Configuration;  // 추가된 부분 

    // 추가된 부분 
    public WebController(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    [HttpGet]
    [Route("/")]
    public IActionResult Index()
    {
        return View("/Views/index.cshtml");
    }

	
    // 추가된 부분
    [HttpGet]
    [Route("/test")]
    public IActionResult Test()
    {
        ViewData["TestKey"] = Configuration["TestXml:TestKey"];
        return View("/Views/test.cshtml");
    }
    
}

추가된 부분을 확인 한다. WebController 의 생성자를 이용하여 설정을 주입 하는 것으로 보인다.

/test 로  접근시 ViewData 를 이용하여 데이터를 인젝션 시켜준다. 이제 test.cshtml 파일을 생성 한다.

 

이제 프로젝트 루트 아래 Views 디렉토리에 test.cshtml 파일을 생성 한다.

@functions {

    @* 1 부터 100까지의 합을 반환 하는 메서드 *@
    public string sumTest1To10()
    {
        int resultNumA = 0;
        for(int i=0; i < 10; i++){
            resultNumA += i;
        }
        return string.Format("{0:n0}", Convert.ToInt32(resultNumA));
    }

}

<!DOCTYPE html>
<html lang="ko">
<head>
    <title>테스트 페이지 입니다.</title>
</head>
<body onload="pageFunc.initHtml();">


    <p>@ViewData["TestKey"]</p>

    <p>오늘은 @DateTime.Now.ToString("yyyy년MM월dd일") 입니다.</p>

    <p id="sumTest1To10">1 부터 10까지의 합은 @sumTest1To10() 입니다.</p>

    @* cshtml 내부에서 C# 코드 블럭은 아래와 같이 사용 할 수 있다. *@
    @{
        int resultNumB = 0;
        for(int i=0; i < 100; i++){
            resultNumB += i;   
        }
    }

    <p id="sumTest1To100">1 부터 100까지의 합은 @string.Format("{0:n0}", Convert.ToInt32(@resultNumB)) 입니다.</p>

    <p id="sumTest1To1000"></p>


</body>
<script type="text/javascript" charset="UTF-8">
/* <![CDATA[ */

'use strict';

var pageFunc = {

    initHtml: function () {
        @* 자바스크립트에서 C# 코드 사용 테스트 *@
        @{
            int resultNumC = 0;
            @for (var i = 0; i < 1000; i++){
                resultNumC += i;
                @:console.log(@i);
            }
        }
        document.getElementById('sumTest1To1000').innerText = '1 부터 1000까지의 합은 @string.Format("{0:n0}", Convert.ToInt32(@resultNumC)) 입니다.';
    },

};

/* ]]> */
</script>
</html>

코드 반영을 위해 다시 실행 하거나 CTRL + R 키를 눌러 적용 하고, 브라우저를 통해 결과를 확인 한다.

 

 

properties-dev.xml 파일의 내용이 보인다.

 

 

중요!!!!

닷넷 런타임을 포함한 단일 파일로 빌드시 문제가 발생 한다. 이 문제를 해결 하기 위해 이전의 정적 리소스 파일들을 함께 배포 시키듯 .csproj 파일을 수정 하면 된다.

 

아래는 helloWorld.csproj 파일 내용

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <Content Include="static\**\*">
      <CopyToPublishDirectory>Always</CopyToPublishDirectory>
    </Content>
    <!-- 추가된 부분 -->
    <Content Include="properties-op.xml">
      <CopyToPublishDirectory>Always</CopyToPublishDirectory>
    </Content>
  </ItemGroup> 

</Project>

 

이렇게 수정 한뒤, 빌드 하고 배포 디렉토리를 보면, properties-op.xml 파일이 복사 되어 있는 것을 확인 할 수 있다.

 

freecatz:helloWorld $ pwd
/Users/freecatz/devel/workspace/vscode/helloWorld
freecatz:helloWorld $ dotnet publish -c Release -r osx-x64 -p:PublishSingleFile=true --self-contained true
.NET용 Microsoft (R) Build Engine 버전 17.1.0+ae57d105c
Copyright (C) Microsoft Corporation. All rights reserved.

  복원할 프로젝트를 확인하는 중...
  복원할 모든 프로젝트가 최신 상태입니다.
  helloWorld -> /Users/freecatz/devel/workspace/vscode/helloWorld/bin/Release/net6.0/osx-x64/helloWorld.dll
  helloWorld -> /Users/freecatz/devel/workspace/vscode/helloWorld/bin/Release/net6.0/osx-x64/publish/
freecatz:helloWorld $ cd ./bin/Release/net6.0/osx-x64/publish/
freecatz:publish $ pwd

/Users/freecatz/devel/workspace/vscode/helloWorld/bin/Release/net6.0/osx-x64/publish
freecatz:publish $ ls -alh
total 168552
drwxr-xr-x    8 freecatz  staff   256B  3 15 16:07 ./
drwxr-xr-x  320 freecatz  staff    10K  3 15 15:59 ../
-rw-r--r--    1 freecatz  staff   127B  3  8 16:13 appsettings.Development.json
-rw-r--r--    1 freecatz  staff   151B  3  8 16:13 appsettings.json
-rwxr-xr-x    1 freecatz  staff    82M  3 15 16:07 helloWorld*
-rw-r--r--    1 freecatz  staff    29K  3 15 16:07 helloWorld.pdb
-rw-r--r--    1 freecatz  staff   220B  3 15 10:44 properties-op.xml
drwxr-xr-x    4 freecatz  staff   128B  3 15 16:07 static/
freecatz:publish $ ./helloWorld
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://192.168.0.6:8080
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://192.168.255.6:8080
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/freecatz/devel/workspace/vscode/helloWorld/bin/Release/net6.0/osx-x64/publish/

 

 

 

 

 

9. 로그를 찍어 보자

개발이나 운영 환경에서 로그는 매우 중요 하다. 로그를 찍기(?) 위해 Program.cs 파일이 설정을 추가 한다.

using Microsoft.Extensions.FileProviders;
using Microsoft.AspNetCore.Http.Features;
using System.Net;
using System.Net.Sockets;

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Services.AddControllers();
        builder.Services.AddRazorPages();
        
        // 추가된 부분(아래 세줄)
        builder.Logging.ClearProviders();
        builder.Logging.AddConsole();
        builder.Logging.SetMinimumLevel(LogLevel.Warning); // 개발, 운영 환경에 따라 조정 하면 좋을듯 보임

        var app = builder.Build();

        // 환경에 따른 XML 파일 로드
        builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.Sources.Clear();

            if (!app.Environment.IsDevelopment())
            {
                // 운영 환경
                config.AddXmlFile("properties-op.xml", optional: true, reloadOnChange: true);
            } else {
                // 개발 환경
                config.AddXmlFile("properties-dev.xml", optional: true, reloadOnChange: true);
            }

            config.AddEnvironmentVariables();
        });


        // 아이피 주소로 접근 가능 하도록 설정
        foreach (var ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
        {
            if (ip.AddressFamily == AddressFamily.InterNetwork)
            {
                //Console.WriteLine(ip.ToString());
                app.Urls.Add("http://" + ip.ToString() +":8080");
            }
        }

        app.UseStaticFiles();
        app.MapControllers();

        app.Run();
    }
}

 

그리고 이전에 작성하였던 WebController.cs 파일을 수정 한다.

 

using Microsoft.AspNetCore.Mvc;
using BaseUtil;
public class WebController : Controller
{
    private readonly IConfiguration Configuration;
    private readonly ILogger Logger;  // 추가된 부분 

    // 수정된 부분 
    public WebController(IConfiguration configuration, ILogger<WebController> logger)
    {
        Configuration = configuration;
        Logger = logger;
    }

    [HttpGet]
    [Route("/")]
    public IActionResult Index()
    {
        return View("/Views/index.cshtml");
    }

	
    [HttpGet]
    [Route("/test")]
    public IActionResult Test()
    {
    	// 추가된 부분
        Logger.LogCritical("로그가 잘 보이나요?");
        Logger.LogDebug("로그가 잘 보이나요?");
        Logger.LogError("로그가 잘 보이나요?");
        Logger.LogInformation("로그가 잘 보이나요?");
        Logger.LogTrace("로그가 잘 보이나요?");
        Logger.LogWarning("로그가 잘 보이나요?");
        
        ViewData["TestKey"] = Configuration["TestXml:TestKey"];
        return View("/Views/test.cshtml");
    }
    
}

 

프로그램을 다시 실행 하고, 터미널을 확인 한다.

 

 

터미널에서 로그를 확인 하였다, 로그 레벨이나 기타 자세한 설명은 https://docs.microsoft.com/ko-kr/aspnet/core/fundamentals/logging/?view=aspnetcore-6.0 페이지를 확인 한다.

 

로그 파일은 어디에 쌓이는 걸까? 위의 링크의 페이지를 보면...

 

 

그렇다고 한다. 타사 로깅 공급자를 소개 하는 링크를 참고 한다. 파일 로깅은 다음에 기회가 있을때, 조금 더 알아 보기로 하려다가 그만...

 

검색해 보니, Nlog 가 사용하기 간편 하다고 나와 있어서 사용해 보기로 하였다. Nlog 를 사용 하기 위해 터미널에서 아래의 명령으로 패키지를 설치해 준다.

 

freecatz:helloWorld $ dotnet add package NLog --version 5.0.0-rc2
  복원할 프로젝트를 확인하는 중...
  Writing /var/folders/c_/b4psj_fn42b0pcpfws4br4gc0000gn/T/tmpuFN2u1.tmp
info : '/Users/freecatz/devel/workspace/vscode/helloWorld/helloWorld.csproj' 프로젝트에 'NLog' 패키지에 대한 PackageReference를 추가하는 중입니다.
info : /Users/freecatz/devel/workspace/vscode/helloWorld/helloWorld.csproj의 패키지를 복원하는 중...
info : 'NLog' 패키지는 '/Users/freecatz/devel/workspace/vscode/helloWorld/helloWorld.csproj' 프로젝트에 지정된 모든 프레임워크와 호환됩니다.
info : 'NLog' 패키지 '5.0.0-rc2' 버전에 대한 PackageReference가 '/Users/freecatz/devel/workspace/vscode/helloWorld/helloWorld.csproj' 파일에 추가되었습니다.
info : 자산 파일을 디스크에 쓰는 중입니다. 경로: /Users/freecatz/devel/workspace/vscode/helloWorld/obj/project.assets.json
log  : /Users/freecatz/devel/workspace/vscode/helloWorld/helloWorld.csproj을(를) 143 ms 동안 복원했습니다.

 

* 참고 : 닷넷용 패키지가 필요한 경우 https://www.nuget.org/ 에서 검색 하여 설치 할 수 있는 명령을 얻을 수 있다.

 

그리고, 기존에 콘솔에 로그를 찍기 위해 작성 했던 코드들을 모두 거둬 내도록 하고, 프로젝트 루트 디렉토리에 NLog.config 파일을 생성 한다. 다른 블로그를 보다 보니, NLog.config 파일이 없는 경우 아무런 동작을 하지 않는다고 한다.

 

아래는 NLog.config 파일 내용 

<?xml version="1.0" encoding="utf-8" ?> 
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <targets>
    
        <target name="file" 
                xsi:type="File" 
                fileName="./logs/i_am_a_log_file.log"
                archiveFileName="./logs/i_am_a_old_log_file_{#}.log" 
                archiveEvery="Day" 
                archiveNumbering="DateAndSequence" 
                archiveAboveSize="104857600" 
                archiveDateFormat="yyyy.MM.dd"
                maxArchiveFiles="14"
                layout="${date:format=HH\:mm\:ss.fff} | ${uppercase:${level:padding=-5}} | ${logger} | ${message}${onexception:${newline}${exception:format=tostring}}"
        />
        <target name="console" 
                xsi:type="Console" 
                layout="${date:format=HH\:mm\:ss.fff} | ${uppercase:${level:padding=-5}} | ${logger} | ${message}${onexception:${newline}${exception:format=tostring}}"
        />
    </targets>

    <rules>
        <logger name="*" minlevel="Debug" writeTo="file" />
        <logger name="*" minlevel="Info" writeTo="console" />
    </rules>

</nlog>

 

설정 내용은 logback 과 비슷해 보인다. 로그 파일 이름이나, 로그 레벨은 각자의 상황이나 맞게 수정 하도록 한다.  

* 참고 : 프로그램 실행시 로그 디렉토리가 없는 경우 생성 해준다.

 

그리고 WebController.cs 파일에서 NLog 를 사용하는 코드를 아래와 같이 추가 하도록 하자.

 

using Microsoft.AspNetCore.Mvc;
using BaseUtil;
using NLog; // 추가된 부분 

public class WebController : Controller
{
    private readonly IConfiguration Configuration;
    private static Logger logger = LogManager.GetCurrentClassLogger(); // 추가된 부분 

    public WebController(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    [HttpGet]
    [Route("/")]
    public IActionResult Index()
    {
        return View("/Views/index.cshtml");
    }

	
    [HttpGet]
    [Route("/test")]
    public IActionResult Test()
    {
    	// 추가된 부분
        logger.Debug("로그가 잘 보이나요?");
        logger.Error("로그가 잘 보이나요?");
        logger.Fatal("로그가 잘 보이나요?");
        
        ViewData["TestKey"] = Configuration["TestXml:TestKey"];
        return View("/Views/test.cshtml");
    }
    
}

 

터미널에서 dotnet watch run 명령으로 실행 해보면, 콘솔과 파일에 로그가 정상적으로 작성 되고 있음을 확인 할 수 있다.

 

 

개발시 로그 파일은 프로젝트 디렉토리 아래 ./bin/Debug/net6.0/logs/ 에 생성 된다.

본인의 경우는 /Users/freecatz/devel/workspace/vscode/helloWorld/bin/Debug/net6.0/logs/i_am_a_log_file.log 라는 파일에  로그가 작성 되고 있다.

 

 

NLog 의 자세한 설정이나 더 많은 사용 방법은 https://nlog-project.org/ 또는 다른 블로그를 확인 하도록 한다.

 

* 참고 : 개발 환경과, 운영 환경별로 로그 레벨을 지정 하는 C# 코드가 있나 찾아 봤더니 아래와 같은 코드를 찾을 수 있었다.

             Program.cs 파일에 아래의 추가된 코드를 확인 한다.

 

using Microsoft.Extensions.FileProviders;
using Microsoft.AspNetCore.Http.Features;
using System.Net;
using System.Net.Sockets;
using NLog; // 추가된 부분

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Services.AddControllers();
        builder.Services.AddRazorPages();
        
        var app = builder.Build();

        // 환경에 따른 XML 파일 로드
        builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.Sources.Clear();

            if (!app.Environment.IsDevelopment())
            {
                // 운영 환경
                config.AddXmlFile("properties-op.xml", optional: true, reloadOnChange: true);
                
                // 추가된 부분 : 운영 환경에서 로그 레벨 설정
                NLog.LogManager.Setup().LoadConfiguration(builder => {
                    builder.ForLogger().FilterMinLevel(NLog.LogLevel.Info).WriteToConsole();
                    builder.ForLogger().FilterMinLevel(NLog.LogLevel.Info).WriteToFile(fileName: "./logs/helloWorld-op.txt");
                });
            } else {
                // 개발 환경
                config.AddXmlFile("properties-dev.xml", optional: true, reloadOnChange: true);
                
                // 추가된 부분 : 개발 환경에서 로그 레벨 설정
                NLog.LogManager.Setup().LoadConfiguration(builder => {
                    builder.ForLogger().FilterMinLevel(NLog.LogLevel.Debug).WriteToConsole();
                    builder.ForLogger().FilterMinLevel(NLog.LogLevel.Debug).WriteToFile(fileName: "./logs/helloWorld-dev.txt");
                });
            }

            config.AddEnvironmentVariables();
        });


        // 아이피 주소로 접근 가능 하도록 설정
        foreach (var ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
        {
            if (ip.AddressFamily == AddressFamily.InterNetwork)
            {
                //Console.WriteLine(ip.ToString());
                app.Urls.Add("http://" + ip.ToString() +":8080");
            }
        }

        app.UseStaticFiles();
        app.MapControllers();

        app.Run();
    }
}

 

이 경우, NLog.config 에 설정된 일부가 코드로 설정된 부분으로 덮어 씌여 지는 것으로 보인다.

NLog 홈페이지에 보면, C#으로 설정 하는 부분이 보이는데, C# 코드로 상세한 설정을 하고자 하는 경우 NLog.Config 패키지가 필요 한 것으로 보인다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

x. dotnet core 6 에서 MySQL 10  연결

Spring Boot 에서 처럼 Connection Pool 을 생성 하여 쿼리를 하는 방향으로 알아 보고 있었는데, 검색 실력의 부족으로 샘플 코드를 못찾고 있다. 찾았던 코드들은 정상 동작 하지 않아 이 부분을 어떻게 해야 할지 감이 오지 않는다.

 

예전 Classic ASP 와 같이 페이지에서 또는 유틸리티 클래스를 만들어 커넥션 개체를 반환 하고 쿼리를 실행 하거나 하면 되긴 하겠지만, 이 보다는 조금 더 나은 방법을 찾아 보고 있다.

 

https://nsinc.tistory.com/216

 

https://lab.cliel.com/entry/ASPNET-Core-Web-API-DB%EB%A1%9C-%EB%B6%80%ED%84%B0-Model-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0-MySQLMariaDB

 

 

 

 

 

x. 배포 하기 위한 빌드

배포 하는 과정을 따로 빼려고 하였으나, 이미 이전 과정에서 여러 차례 배포 하기 위한 빌드를 진행 하였다. 긴 문서 이곳 까지 내려 왔다가 다시 올라가는 스크롤의 번거로움을 피하기 위해 아주 간단히 작성 하겠다.

이 문서에서는 닷넷 런타임을 포함한 단일 파일 빌드를 할 것이다.

freecatz:helloWorld $ pwd
/Users/freecatz/devel/workspace/vscode/helloWorld
freecatz:helloWorld $ dotnet publish -c Release -r osx-x64 -p:PublishSingleFile=true --self-contained true
.NET용 Microsoft (R) Build Engine 버전 17.1.0+ae57d105c
Copyright (C) Microsoft Corporation. All rights reserved.

  복원할 프로젝트를 확인하는 중...
  복원할 모든 프로젝트가 최신 상태입니다.
  helloWorld -> /Users/freecatz/devel/workspace/vscode/helloWorld/bin/Release/net6.0/osx-x64/helloWorld.dll
  helloWorld -> /Users/freecatz/devel/workspace/vscode/helloWorld/bin/Release/net6.0/osx-x64/publish/
freecatz:helloWorld $ cd ./bin/Release/net6.0/osx-x64/publish/
freecatz:publish $ ls
appsettings.Development.json  helloWorld*                   properties-op.xml
appsettings.json              helloWorld.pdb                static/
freecatz:publish $ ./helloWorld 
info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[62]
      User profile is available. Using '/Users/freecatz/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest.
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://192.168.0.6:8080
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://192.168.255.6:8080
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/freecatz/devel/workspace/vscode/helloWorld/bin/Release/net6.0/osx-x64/publish/

브라우저를 통해서 확인해 보도록 하자. 참고로, 특정 운영체제에 맞는 빌드를 하기 위해선 https://docs.microsoft.com/ko-kr/dotnet/core/rid-catalog 페이지를 참고 한다.

 

 

 

 

 

 

 

x. vscode 에 SVN  확장 설치

우선 SVN 이 설치 되어 있어야 한다.

freecatz:~ $ which svn
/usr/local/bin/svn

 

만약 SVN 이 설치 되어  있지 않은 경우 brew 또는 xcode-select 명령을 이용하여 설치 하도록 한다.

 

vscode 에서 SVN 확장을 설치 하기 위해 CMD + Shift + X 를 눌러 'svn' 으로 검색된 확장 패키지중에서 위의 그림의 패키지를 설치 한다.

 

 

vi 나 vscode 등의 텍스트 에디터를 이용하여 /Applications/Visual\ Studio\ Code.app/Contents/Resources/app/product.json 파일을 수정 하여 준다. 

 

"extensionAllowedProposedApi" 항목을 검색 하여, "johnstoncode.svn-scm" 의 값을 입력해 준다. 위에서 설치한 svn 확장 패키지의 이름 인 것으로 보인다. 파일을 저장 한뒤, vscode 를 종료 후, 다시 시작 하여 준다.

 

 

 

CMD + Shift + P 키를 입력 하고 나타나는 프롬프트(?) 에 'SVN:Checkout' 입력 하고, 나타나는 항목에서 엔터키를 입력 한다.

 

 

 

사용중인 SVN Repository 주소를 입력 하고, 엔터키를 입력 한다.

 

 

 

프로젝트 디렉토리의 부모 디렉토리를 선택 한뒤, "Select Repository Location" 버튼을 클릭 한다.

 

 

 

로컬에 저장하고 SVN에서 버젼 관리할 폴더 이름을 입력 하고, 엔터키를 입력 하라고 한다. 프로젝트 이름과 동일하게 작성 한다.

 

SVN 사용자 계정을 입력 하고 엔터키를 입력 한다.

 

 

SVN 사용자 계정의 비밀번호를 입력 하고 엔터키를 입력 한다.

 

 

 

아..중간 과정에 SVN Add 해주는 스크린샷을 못 찍었다.

 

 

수정된 파일은 위의 그림과 같이 보여 진다.

 

 

 

커밋 할 파일을 선택 후, 마우스 오른쪽 버튼을 클릭 하여 나타나는 메뉴에서 'Commit Selected' 를 클릭 한다.

 

 

 

freecatz:helloWorld $ pwd
/Users/freecatz/devel/workspace/vscode/helloWorld
freecatz:helloWorld $ ls -a
./                            Controller/                Views/                                            obj/
../                           NLog.config               appsettings.Development.json   package.txt
.DS_Store             Program.cs                 appsettings.json                          properties-dev.xml
.svn/                      Properties/                bin/                                                properties-op.xml
.vscode/               Service/                      helloWorld.csproj                         static/
freecatz:helloWorld $ rm -rf .svn

vscode 에서 SVN 연동 해제 하는 방법을 찾다가 못찾아서 프로젝트 디렉토리로 이동 하여 .svn 디렉토리를 삭제 하였다.

 

 

 

x. vscode 에 NuGet Package Manager  확장 설치

dotnet 명령어로 패키지 설치가 가능한 것으로 보아 이 단계는 필요치 않을 것으로 예상 된다.

 

CMD + Shift + X 키를 입력 하여 나타나는 화면에 위와 같이 nuget 를 입력 하고 설치 한다.

extension 설치가 완료 되면, vscode 를 다시 시작 하여 준다. vscode 를 다시 시작 안해도 문제는 없었다.

 

 

CMD + Shift + P 키를 입력 하여 나타나는 화면에 'nuget' 를 입력 하여 위와 같이 나타나면 정상적으로 extension 이 설치가 된 것이다.

 

 

 

 

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함