ASP.NET Core 5.1 · 5부. 배포와 실전

환경별 설정

개발과 운영을 구분합니다.

예상 학습 시간 16분 난이도 실전
개념 설명

같은 코드가 자리마다 다르게 돌아야 합니다

4.4에서 설정 파일이 겹쳐 읽힌다고 했습니다. 그 "겹치는 기준" 이 환경 입니다. 개발 기계에서는 자세한 오류를 보고 싶고, 운영에서는 보이면 안 됩니다.

환경은 ASPNETCORE_ENVIRONMENT 환경 변수가 정합니다. 적지 않으면 Production 입니다. 이름은 우리가 정할 수 있지만, 아래 셋이 관례이고 프레임워크가 그 이름을 알고 있습니다.

이름어디
Development개발 기계입니다. 1.2에서 본 launchSettings.json 이 이 값을 넣어 줍니다.
Staging운영과 같게 꾸며 두고 마지막으로 확인하는 곳입니다.
Production실제로 사용하는 곳입니다. 기본값입니다.

기본값이 Production 인 것은 안전한 쪽이기 때문입니다. 잊고 적지 않았을 때 운영으로 도는 편이, 개발용으로 도는 것보다 낫습니다.

최소 예제

세 환경으로 각각 띄워 보면

Program.cs
app.MapGet("/", (IHostEnvironment env, IConfiguration c) => new {
    환경 = env.EnvironmentName,
    개발인가 = env.IsDevelopment(),
    운영인가 = env.IsProduction(),
    이름 = c["Site:Name"]
});
받은 응답
// ASPNETCORE_ENVIRONMENT=Development {"환경":"Development","개발인가":true,"운영인가":false,"이름":"개발 이름"} 읽은 파일: appsettings.json, appsettings.Development.json // ASPNETCORE_ENVIRONMENT=Staging {"환경":"Staging","개발인가":false,"운영인가":false,"이름":"시험 이름"} 읽은 파일: appsettings.json, appsettings.Staging.json // 아무것도 적지 않았을 때 {"환경":"Production","개발인가":false,"운영인가":true,"이름":"운영 이름"} 읽은 파일: appsettings.json, appsettings.Production.json

이 코드는 호스트가 있어야 하므로 브라우저에서 실행할 수 없습니다.

Staging 은 개발도 운영도 아닙니다. 둘 다 false 입니다. if (!env.IsDevelopment()) 로 적으면 Staging 도 운영 취급이 되고, if (env.IsProduction()) 로 적으면 Staging 만 빠집니다. 어느 쪽을 뜻하는지 정해서 적어야 합니다.

appsettings.Production.json 은 만들지 않았는데도 목록에 있습니다. 없어도 오류가 나지 않습니다. 있으면 읽고 없으면 넘어갑니다.

상세 사용법

같은 예외가 다르게 보입니다

이것이 환경을 나누는 가장 큰 까닭입니다. 연결 문자열이 섞인 예외를 일부러 내 두 환경에서 각각 불러 보았습니다.

app.MapGet("/boom", () => {
    throw new InvalidOperationException(
        "연결 문자열 Server=db01;Password=1234 로 접속 실패");
});
Development — 961바이트
{
  "title": "System.InvalidOperation…",
  "status": 500,
  "detail": "연결 문자열 Server=db01;
             Password=1234 로 접속 실패",
  "exception": { 호출 스택 … },
  "traceId": "00-af02d658…"
}
Production — 201바이트
{
  "title": "An error occurred while
            processing your request.",
  "status": 500,
  "traceId": "00-3c4d4ce8…"
}

왼쪽에 비밀번호가 그대로 있습니다. 예외 메시지·호출 스택·파일 경로가 모두 나갑니다. 고칠 때는 이만한 것이 없지만, 운영에서 이것이 나가면 그대로 사고입니다.

2.5에서 "500은 예외 메시지를 흘리지 않는다" 고 한 것이 이 오른쪽입니다. 그때 Production 으로 띄웠기 때문이고, 개발에서는 다르게 보입니다.

환경으로 갈라 두는 코드는 이렇게 적습니다.

if (app.Environment.IsDevelopment())
    app.UseDeveloperExceptionPage();
else
    app.UseExceptionHandler();
상세 사용법

환경이 바꾸는 것들

직접 적지 않아도 환경에 따라 달라지는 것이 여럿입니다. 앞의 단원에서 하나씩 만난 것들입니다.

무엇Development그 밖
예외 응답메시지·호출 스택이 나갑니다제목과 traceId 만 나갑니다
범위 검사(4.1)켜집니다 — 잘못 등록하면 기동이 막힙니다꺼집니다 — 조용히 잘못 동작합니다
사용자 비밀(4.4)읽습니다읽지 않습니다
정적 파일 캐시짧게 둡니다길게 둡니다

둘째 줄이 4.1의 그 함정입니다. 개발에서 잡히는 것이 운영에서는 잡히지 않습니다. 환경을 나누는 것이 편의만은 아니라는 뜻입니다.

운영에서 환경을 정하는 자리

1.2에서 launchSettings.json 이 배포본에 들어가지 않는다고 했습니다. 그러므로 운영에서는 다른 곳에서 정해야 합니다.

# 리눅스·컨테이너
export ASPNETCORE_ENVIRONMENT=Production

# 윈도우 IIS — web.config 의 환경 변수
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Production" />

# 그때 한 번만
dotnet Api.dll --environment Staging

적지 않으면 Production 이므로, 운영에서는 사실 적지 않아도 됩니다. 그래도 적어 두는 편이 낫습니다. 무엇으로 도는지 눈에 보이고, Staging 을 늘릴 때 자리가 이미 있습니다.

버전 배지

환경 이름은 우리가 정할 수 있습니다

아무 글자나

QA·Demo 처럼 원하는 이름을 둘 수 있습니다. 그러면 appsettings.QA.json 을 읽습니다. 다만 IsDevelopment() 같은 메서드는 정해진 세 이름에만 맞으므로 env.IsEnvironment("QA") 로 봅니다.

대소문자를 가립니다. development 라고 적으면 IsDevelopment() 는 참이지만 appsettings.development.json 을 찾습니다. 리눅스에서는 파일 이름도 대소문자를 가려 읽히지 않습니다.

실습 문제

직접 해보기

1. 환경 확인하기 난이도 하

지금 어떤 환경으로 돌고 있는지 화면에 내보내는 주소를 만들어보세요. 그리고 환경 변수를 바꿔 가며 확인해보십시오.

IHostEnvironment 를 받으면 됩니다. 1.2의 프로필을 사용하지 않으려면 --no-launch-profile 입니다.
app.MapGet("/env", (IHostEnvironment e) => e.EnvironmentName); # 확인 dotnet run --no-launch-profile # Production dotnet run --no-launch-profile --environment Staging # Staging dotnet run # Development # ↑ launchSettings.json 이 넣어 줍니다(1.2)
2. Staging 을 어느 쪽으로 둘 것인가 난이도 중

아래 셋을 Staging 에서 어떻게 두어야 할지 정하고 코드를 적어보세요.

① 자세한 예외 화면
② 검색 로봇에게 색인하지 말라고 알리기
③ 결제를 실제로 승인하기
Staging 은 운영과 같게 꾸며 두고 확인하는 곳입니다. 다만 밖에서 볼 수 있습니다.
// ① 끕니다. 밖에서 접근할 수 있으므로 운영과 같이 둡니다. if (app.Environment.IsDevelopment()) app.UseDeveloperExceptionPage(); // ② 켭니다. 운영이 아닌 곳은 모두 막습니다. if (!app.Environment.IsProduction()) 로봇막기(); // ③ 끕니다. 운영에서만 실제로 승인합니다. if (app.Environment.IsProduction()) 실제결제(); else 시늉만(); // ① 은 IsDevelopment, ②③ 은 IsProduction 으로 갈랐습니다. // 어느 쪽 기준인지가 항목마다 다릅니다.
3. 운영에서 스택이 나왔습니다 난이도 상

운영 사이트에서 오류가 났는데 호출 스택과 연결 문자열이 그대로 보였습니다. 무엇을 살펴야 할지, 그리고 다시 생기지 않게 하려면 어떻게 할지 적어보세요.

그 화면은 어떤 조건에서 켜집니까. 그 조건이 참이 되는 길이 몇 가지입니까.
// 살펴볼 곳 // 1. ASPNETCORE_ENVIRONMENT 가 정말 Production 인가. // /env 같은 주소를 두어 눈으로 확인합니다. 배포 스크립트가 // Development 를 남겨 두었을 수 있습니다. // 2. UseDeveloperExceptionPage 를 조건 없이 부르고 있지 않은가. // if 를 빠뜨리면 어느 환경에서나 켜집니다. // 3. 예외 메시지에 비밀값을 담고 있지 않은가. // 다시 생기지 않게 // · 배포 뒤 확인 항목에 환경 이름을 넣습니다. // · 예외 메시지에 연결 문자열을 담지 않습니다. 그 자체가 결함입니다. // 담기지 않으면 화면이 켜져도 새지 않습니다. // · 비밀값은 4.4 대로 설정에서 읽고, 로그에도 그대로 찍지 않습니다.
요약
  • 환경은 ASPNETCORE_ENVIRONMENT 가 정하고, 적지 않으면 Production 입니다.
  • 환경 이름으로 appsettings.{환경}.json 을 찾습니다. 없어도 오류가 나지 않습니다.
  • 같은 예외가 개발에서는 호출 스택까지, 운영에서는 제목만 나갑니다.
  • Staging 은 개발도 운영도 아닙니다. 어느 쪽 기준으로 갈랐는지 항목마다 확인하십시오.
  • 운영에서는 launchSettings.json 이 없으므로 환경 변수나 web.config 로 정합니다.