웹팩 개발서버에 로컬 인증서(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'의 유무와 관계없음.)