[Nodejs] TypeError: Cannot convert undefined or null to object

1. 현상

와 Docker를 배워서 이미지를 만들어서 github container registry에 업로드했다.

신이 나버린 나는 당연히 다른 PC를 켜서 도커를 설치하고 git clone, docker-compose up을 했다.

하지만 나의 기대와는 다르게 도커는 “운영체제가 달라 저리 치워”라고 말했다.

나는 도커의 비위를 맞춰 bulidx를 이용해서 멀티 플랫폼으로 빌드했고 다시 Compose up을 했다.

그랬는데… 이 놈이 이번에는 JS 문법 오류가 있다고 에러를 낸다.

아니 맥북에서는 잘만 돌아가는데 왜 이러냐…

에러 메시지는 다음과 같다.

study_nodejs-studynode-1  | TypeError: Cannot convert undefined or null to object
study_nodejs-studynode-1  |     at Function.keys (<anonymous>)
study_nodejs-studynode-1  |     at Object.List (/app/src/lib/template.js:34:32)
study_nodejs-studynode-1  |     at /app/src/main.js:21:29
study_nodejs-studynode-1  |     at FSReqCallback.oncomplete (node:fs:197:23)
Code language: JavaScript (javascript)

에러 코드를 기반으로 원인을 추측해 보자.

코드를 수정하고 실행해 본다.

List : function(filelist) {
    let list = "<ul>";
        if(Array.isArray(filelist)){
            for (let i = 0; i < Object.keys(filelist).length; i++) {
              list += `<li><a href="/?id=${filelist[i]}">${filelist[i]}</a></li>`;
            }
            list += "</ul>";
            return list;
        }else{
            return `filelist is not a array. filelist is ${typeof filelist}.`;
        }
}
Code language: JavaScript (javascript)

맥북에서 PC에서의 차이점은 readdir로 읽어서 array에 전달될 data/ 아래의 파일들이 없다는 것이다.

  • 게시판에 새 글을 작성해 본다.

글이 작성되고 title은 보이지만 내용이 비어있다.

이는 곧 파일이 생성되지 않았음을 의미한다.

실제 디렉터리를 보아도 그렇다.

~/D/Study_nodeJS/src | mysql !1  ls -al                                                              ok | 02:16:31 AM
total 24
drwxr-xr-x 3 ramen4598 ramen4598 4096 Mar  6 01:44 .
drwxr-xr-x 8 ramen4598 ramen4598 4096 Mar  6 02:05 ..
drwxr-xr-x 2 ramen4598 ramen4598 4096 Mar  6 02:07 lib
-rw-r--r-- 1 ramen4598 ramen4598 6515 Mar  4 19:17 main.js
-rw-r--r-- 1 ramen4598 ramen4598  734 Mar  4 19:04 style.css
// data 디렉터리가 생성되지 않았다.
Code language: JavaScript (javascript)
  • 빈 data 디렉터리를 생성한다.

혹시 readdir, writeFile 등 동작에 실행 주소로 설정된 data 디렉터리가 없어서 그런 것이 아닐까 생각이 들었다.

~/D/Study_nodeJS/src | mysql !1  mkdir data                                      ok | 02:22:58 AM 

 ~/D/Study_nodeJS/src | mysql !1  ll                                              ok | 02:23:03 AM 
total 28
drwxr-xr-x 4 ramen4598 ramen4598 4096 Mar  6 02:23 .
drwxr-xr-x 8 ramen4598 ramen4598 4096 Mar  6 02:05 ..
drwxr-xr-x 2 ramen4598 ramen4598 4096 Mar  6 02:23 data
drwxr-xr-x 2 ramen4598 ramen4598 4096 Mar  6 02:07 lib
-rw-r--r-- 1 ramen4598 ramen4598 6515 Mar  4 19:17 main.js
-rw-r--r-- 1 ramen4598 ramen4598  734 Mar  4 19:04 style.css
Code language: JavaScript (javascript)

그렇다… 경로로 설정한 디렉터리가 없어서 그랬던 거였구나…

나머지 기능들도 정상적으로 동작한다.


2. 해결책

가. .gitkeep

github에 data 디렉터리가 올라가길 원하지 않아서 .gitignore에 추가한 것이 문제가 될 줄은 몰랐다.

그래서 마지막으로 .gitignore를 수정하고 해당 버그에 대해서 test 코드를 추가해 보았다.

# /app/src/test/main.test.js
const fs = require('fs');
const dataDir = "/app/src/data";

test('filelist is defined.', async()=>{
    const filelist = await fs.readdir(dataDir, (err, filelist)=>{
        return filelist;
    });
});
Code language: PHP (php)
# .gitignore
# data/ -> data/*
./src/data/*
Code language: PHP (php)

슬프게도 github는 비어있는 디렉터리는 아예 포함시키지 않는다.

.gitkeep을 사용해서 디렉터리를 유지하는 방법도 있지만 그러면 .gitkeep이 우리 홈페이지에 노출된다.

그냥 data 디렉터리가 없으면 생성하는 코드를 추가하자.


나. fs.accessSync

File system | Node.js v25.2.1 Documentation

fs.existsSync는 더 이상 사용할 수 없다.

대신에 fs.accessSync를 활용할 수 있다.??

File system | Node.js v25.2.1 Documentation

fs.accessSync(path[, mode])
- path <string> | <Buffer> | <URL>
- mode <integer> Default: fs.constants.F_OK
Code language: HTML, XML (xml)
  • F_OK : Check if the file exists in the current directory.
  • R_OK : Check if the file is readable.
  • W_OK : Check if the file is writable.
  • constants.R_OK | constants.W_OK : Check if the file is readable and writable.

Do not use fs.access() to check for the accessibility of a file before calling fs.open()
, fs.readFile(), or fs.writeFile(). Doing so introduces a race condition, since other processes may change the file’s state between the two calls. Instead, user code should open/read/write the file directly and handle the error raised if the file is not accessible.

어..? fs.open, fs.readFile, fs.writeFile 사용 전에 확인용으로 사용하기를 추천하지 않는다.

그냥 파일에 접근했을 때 발생하는 오류를 직접 핸들링하는 편이 더 좋다고 합니다.


다. if (err)

예외처리가 참 중요하구나…

아래는 JS 에러 객체에 대한 설명이다.

Error – JavaScript | MDNMDN

fs.readdir(path, function (err, filelist) {
    const noData = `ENOENT: no such file or directory, scandir '/app/src/data'`;
  if (err && err.message === noData){
        fs.mkdirSync(path, {recursive: true});
        filelist = [""];
    }
    ...
Code language: JavaScript (javascript)

이제 잘 동작한다.


댓글 남기기