웹팩 개발서버에 로컬 인증서(SSL) 설정하는 방법을 몰라서 한참 고생을....
정확하게는 로컬 인증서를 자동으로 생성하는 방법을 몰라서 고생한거긴 합니다.
문서를 좀더 잘읽었으면 삽질이 좀 줄긴 했을거 같은데 말이죠 ㅎㅎㅎㅎ
'ASP.NET Core'의 'React'템플릿에 보면 다음과 같이 2개의 파일이 있습니다.
(참고 : github - dang-gun/AspDotNetSamples/WebpackSslTest/ClientApp/OriginalFils/ )
aspnetcore-https.js : 로컬 인증서를 생성해 주는 파일
aspnetcore-react.js : 생성된 경로를 '.env.development.local'에 넣어주는 파일
이 파일들을 실행하면 로컬인증서가 생성되고 '.env.development.local'파일에 경로가 저장됩니다.
이 두 개의 파일은 .NET과 관련 없이 독립적으로 동작하는 파일입니다.
하지만 자동 생성되는 경로가 'ASP.NET Core'의 경로로 지정됩니다.
자신에게 맞게 수정하면 'ASP.NET Core'과 상관없이 사용할 수 있습니다.
'aspnetcore-https.js'와 'aspnetcore-react.js'를 실행시키려면 'package.json'에 설정하면 됩니다.
"scripts": {
"prestart": "node aspnetcore-https && node aspnetcore-react",
"start": "rimraf ./build && node scripts/start.js",
"build": "node scripts/build.js",
"test": "cross-env CI=true node scripts/test.js --env=jsdom",
"lint": "eslint ./src/"
},
이 코드에서는 "prestart"에 넣어 'start'가 호출되기 전에 동작하도록 했지만
한 번만 실행돼도 문제가 없기 때문에 다른 명령어로 넣고 수동으로 한 번만 호출해도 상관없습니다.
웹 팩 개발 서버의 'https' 항목에 인증서를 설정하면 SSL이 동작하게 됩니다.
(참고 : webpack - DevServer - devServer.https )
const fs = require('fs');
const path = require('path');
devServer: {
https: {
minVersion: 'TLSv1.1',
key: fs.readFileSync(path.join(__dirname, './server.key')),
pfx: fs.readFileSync(path.join(__dirname, './server.pfx')),
cert: fs.readFileSync(path.join(__dirname, './server.crt')),
ca: fs.readFileSync(path.join(__dirname, './ca.pem')),
passphrase: 'webpack-dev-server',
requestCert: true,
},
},
여기서 위에서 생성된 경로를 지정하면 SSL이 동작하게 됩니다.
매번 복사 붙여넣기 하는 번거로움이 싫다면 자동으로 '.env.development.local'파일을 읽도록 구성하면 됩니다.
아래 코드는 'ASP.NET Core'의 'React'템플릿의 'getHttpsConfig.js'를 참고하여 만들었습니다.
(참고 : github - dang-gun/AspDotNetSamples/WebpackSslTest/ClientApp/OriginalFils/getHttpsConfig.js )
'use strict';
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
//const chalk = require('react-dev-utils/chalk');
//const paths = require('../paths');
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
// Ensure the certificate and key provided are valid and if not
// throw an easy to debug error
function validateKeyAndCerts({ cert, key, keyFile, crtFile }) {
let encrypted;
try {
// publicEncrypt will throw an error with an invalid cert
encrypted = crypto.publicEncrypt(cert, Buffer.from('test'));
} catch (err) {
throw new Error(
`The certificate is invalid.\n${err.message}`
);
}
try {
// privateDecrypt will throw an error with an invalid key
crypto.privateDecrypt(key, encrypted);
} catch (err) {
throw new Error(
`The certificate key is invalid.\n${
err.message
}`
);
}
}
// Read file and throw an error if it doesn't exist
function readEnvFile(file, type) {
if (!fs.existsSync(file)) {
throw new Error(
`You specified ${chalk.cyan(
type
)} in your env, but the file can't be found.`
);
}
return fs.readFileSync(file);
}
// Get the https config
// Return cert files if provided in env, otherwise just true or false
function getHttpsConfig(bHttpsIs)
{
//const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env;
//const isHttps = HTTPS === 'true';
let SSL_CRT_FILE = "";
let SSL_KEY_FILE = "";
if (true === bHttpsIs)
{
let dirEnv_D_L = path.resolve(__dirname, ".env.development.local");
if (!fs.existsSync(dirEnv_D_L))
{
throw new Error(
`You specified in your env, but the file "${dirEnv_D_L}" can't be found.`
);
}
else
{
var array = fs.readFileSync(dirEnv_D_L).toString().split("\n");
for (let i in array)
{
let arrCut = array[i].split("=");
if (0 < arrCut.length)
{
switch (arrCut[0])
{
case "SSL_CRT_FILE":
SSL_CRT_FILE = arrCut[1];
break;
case "SSL_KEY_FILE":
SSL_KEY_FILE = arrCut[1];
break;
}
}
}
}//end if (!fs.existsSync(dirEnv_D_L))
}
if (bHttpsIs && SSL_CRT_FILE && SSL_KEY_FILE) {
const crtFile = path.resolve(resolveApp('.'), SSL_CRT_FILE);
const keyFile = path.resolve(resolveApp('.'), SSL_KEY_FILE);
const config = {
cert: readEnvFile(crtFile, 'SSL_CRT_FILE'),
key: readEnvFile(keyFile, 'SSL_KEY_FILE'),
};
validateKeyAndCerts({ ...config, keyFile, crtFile });
return config;
}
return bHttpsIs;
}
module.exports = getHttpsConfig;
이제 웹 팩 설정 파일에서 아래와 같이 선언하고
const getHttpsConfig = require('./getHttpsConfig');
아래와 같이 호출하면 됩니다.
devServer: {
/** 서비스 포트 */
port: "9503",
https: getHttpsConfig(true),
}
위에 나온 스크립트를 통합하여 하나의 파일로 만들 수 있습니다.
참고 : github - dang-gun/AspDotNetSamples/WebpackSslTest/ClientApp/AspNetCore_HttpsConfigGet.js
개체 이름을 바꿨기 때문에 아래와 같이 사용합니다.
const HttpsConfigGet = require('./AspNetCore_HttpsConfigGet');
devServer: {
https: HttpsConfigGet(true),
},
테스트 프로젝트 : github - dang-gun/AspDotNetSamples/WebpackSslTest/
웹 팩 설정이지만 'ASP.NET Core'를 넣은 것은 인증서 생성부분이 'ASP.NET Core'와 연관이 있어서 그렇습니다.
정확하게는 'ASP.NET Core'에서 추출한 코드라 경로가 'ASP.NET Core'로 되어있다는 것이죠.
이 부분은 자신이 원하는 데로 수정하여 사용할 수 있습니다.
수정하지 않아도 사용하는 데 지장없습니다.
('ASP.NET Core'의 유무와 관계없음.)