본 포스트는 내부망에서 별도 Proxy를 거치는 환경에서 Node.js 서버 구성 시 발생하는 SELF_SIGNED_CERT_IN_CHAIN 오류에 대한 원인 및 해결에 대한 글입니다.

 

 

Node.js 서버에서 Http를 생성하여 다른 서버와 통신하려 할 때 아래와 같은 오류가 발생하는 경우가 있습니다.

Error: self signed certificate in certificate chain
    at TLSSocket.onConnectSecure (node:_tls_wrap:1530:34)
    at TLSSocket.emit (node:events:390:28)
    at TLSSocket._finishInit (node:_tls_wrap:944:8)
    at TLSWrap.ssl.onhandshakedone (node:_tls_wrap:725:12) {
  code: 'SELF_SIGNED_CERT_IN_CHAIN'
}

 

위와 같은 경우는 대부분 내부망에서 외부망으로 통신할 때 별도의 Proxy 서버를 두고, 해당 Proxy 서버를 거칠 때 자체 사설 인증서로 통신할 경우에 발생합니다. 클라이언트가 서버와 통신하는 과정에서 신뢰할 수 있는 인증기관에서 발급된 인증서인지를 확인하는 과정을 거칠 때, 자체 서명된 사설 인증서가 인증 체인의 중간에 위치해서 발생하는 오류입니다.

 

테스트 용도로 사설 인증서를 사용하는 것이라면 추후 공인된 인증기관의 SSL로 변경하면 되겠지만 망분리로 인해서 발생하는 오류라면 테스트 환경일 경우에 아래 로직을 한 줄 추가해서 개발합니다.

process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

 

위 소스코드는 허가되지 않은 인증서를 거부하지 않겠다는 의미입니다.

300x250

본 포스트는 Node.js 에서 모듈 시스템 방식을 ES 방식으로 사용할 경우 __dirname을 찾을 수 없는 오류에 대한 설명입니다.

 

Node.js와 express로 서버를 구성할 때 html 등 Static resources에 대한 경로 설정을 아래와 같이 합니다.

const express = require('express');
const path = require('path');

const app = express();

app.use(express.static(path.join(__dirname, '../public')));
app.get('/', (req,res) => {
    res.sendFile(path.join(__dirname, '../public/index.html'));
});

 

위 소스코드는 static resources는 /public 하위 경로에 두고 초기 화면을 index.html로 설정한 경우이며, 서버를 구동하고 localhost 로 접속하면 /public/index.html을 응답합니다.

 

모듈 시스템을 CommonJS가 아닌 ES 방식을 사용해서 구성하면 require 되신 import ~ from을 사용하여 아래와 같이 작성할 수 있습니다.

import express from "express";
import path from "path";

const app = express();

app.use(express.static(path.join(__dirname, '../public')));
app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, '../public/index.html'));
});

 

 

위와 같이 작성한 후 서버를 구동해 보면 아래와 같은 오류가 표시됩니다.

ReferenceError: __dirname is not defined in ES module scope
This file is being treated as an ES module because it has a '.js' file extension and '/Users/a20201022/Documents/expressServer/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
    at file:///Users/a20201022/Documents/expressServer/src/app.js:7:34
    at ModuleJob.run (node:internal/modules/esm/module_job:185:25)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:281:24)
    at async loadESM (node:internal/process/esm_loader:88:5)
    at async handleMainPromise (node:internal/modules/run_main:65:12)
error Command failed with exit code 1.

 

오류 메시지 그대로 __dirname 이라는 전역 변수는 ES 모듈에서는 선언되지 않아 사용할 수 없다는 의미입니다. 해결을 위해서는 해당 변수를 아래와 같이 선언합니다.

import express from "express";
import path from "path";

const app = express();
//__dirname 선언
const __dirname = path.resolve();

app.use(express.static(path.join(__dirname, '../public')));
app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, '../public/index.html'));
});

 

위와 같이 작성한 후 서버를 구동해 보면 정상 구동됨을 확인할 수 있습니다.

300x250

본 포스트는 Node.js 에서 모듈 시스템 방식을 ES 방식을 사용했을 경우 발생하는 오류 및 해결에 대한 설명입니다.

 

 

Node.js의 모듈 시스템은 기본적으로 CommonJs 명세를 따릅니다. 하여 모듈을 사용할 경우에는 아래와 같이 require를 사용하여 필요한 모듈을 참조할 수 있습니다.

const express = require('express');

 

하지만 요즘은 많은 프로젝트 들에서 ES 기반의 모듈 시스템을 사용하여 아래와 같이 모듈을 참조할 수 있습니다.

import express from 'express';

 

위 두 구분은 동일한 역할을 하지만 Node.js 애플리케이션을 작성할 때 위와 같이 작성하면 서버를 구동할 때 아래와 같은 오류를 만나게 됩니다.

(node:1463) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/a20201022/Documents/expressServer/src/app.js:1
import express from "express";
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at Object.compileFunction (node:vm:352:18)
    at wrapSafe (node:internal/modules/cjs/loader:1031:15)
    at Module._compile (node:internal/modules/cjs/loader:1065:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:17:47

 

해당 오류를 해결하기 위해서는 package.json에 아래 한 줄을 추가합니다.

{
    "type": "module"
}

 

위 설정은 프로젝트 전체에 적용되며 해당 프로젝트에 ES 기반의 모듈 시스템을 사용할 수 있게 해줍니다.

300x250

본 포스트는 React에서 배열을 다룰 때 발생할 수 있는 오류인 Each child in a list should have a unique "key" prop 오류 원인 및 해결 방법에 대해 설명합니다.

 

 

React로 개발할 때 목록 구현을 위해 보통 아래와 같이 개발합니다.

const NewsRow = ({title, pubDate, description}) => {
    return (
        <li>
            <div className="title">
                <a href="#" dangerouslySetInnerHTML={{__html: title}}></a>
            </div>
            <div className="cont">
                <span className="date">{pubDate}</span>
                <span dangerouslySetInnerHTML={{__html: description}} />
            </div>
        </li>
    );
};

...
articles.map((v, inx) => {
    return <NewsRow {...v} />
})
...

 

위와 같이 개발하면 UI에는 목록이 표시되지만 브라우저 콘솔 로그에 아래와 같은 오류가 나타납니다.

react.development.js:217 Warning: Each child in a list should have a unique "key" prop.

Check the render method of `ListView`. See https://reactjs.org/link/warning-keys for more information.
    at NewsRow (webpack://searchNaverApi/./src/component/listview.component.jsx?:37:20)
    at ListView (webpack://searchNaverApi/./src/component/listview.component.jsx?:91:66)
    at div
    at RecoilRoot_INTERNAL (webpack://searchNaverApi/./node_modules/recoil/es/index.js?:4225:3)
    at RecoilRoot (webpack://searchNaverApi/./node_modules/recoil/es/index.js?:4391:5)
    at App

 

 

React는 사용자가 개발한 컴포넌트의 상태가 변경되면 Re-Rendering을 하는데, 컴포넌트와 DOM의 Element 간의 관계를 생성할 때 "key" prop을 사용합니다. 따라서 key prop을 설정하지 않았다면 최초 1회는 문제 없이 Rendering을 했다 하더라도, 상태가 변경됨에 따라 오류들이 발생할 수 있습니다.

 

가장 간단한 해결책은 배열 요소의 인덱스를 넘기는 방법입니다.

articles.map((v, inx) => {
    return <NewsRow key={inx} {...v} />
})

 

하지만 위 코드는 샘플에서나 사용할 뿐, 실제로는 되도록 해당 배열에서 고유한 값을 사용해야 합니다. JavaScript의 배열은 동적으로 변경이 일어날 수 있으므로 인덱스 값은 언제든지 변할 수 있습니다. 위와 같은 경우의 예라면 뉴스의 게시물 번호 등을 사용해서 해당 Row에서 변하지 않는 고유한 값을 사용하는게 가장 좋습니다.

articles.map((v, inx) => {
    return <NewsRow key={v.articleId} {...v} />
})

 

이런 형태로 설정하고 나면 브라우저의 콘솔에 오류 로그가 사라졌음을 확인할 수 있습니다.

300x250

+ Recent posts