Menu

Node.js path 모듈: path.join과 path.resolve 차이

path 모듈은 운영체제에 맞는 방식으로 파일 경로를 만들고 나눕니다. path.join은 조각을 이어 붙이고, path.resolve는 현재 폴더에서 시작해 절대 경로로 바꿉니다.

이 페이지에는 실행 가능한 에디터가 있습니다. 편집하고 실행하면 결과를 바로 볼 수 있습니다.

path.join()은 경로 조각을 올바른 구분자로 잇고 ..와 중복된 슬래시를 정리하지만, 상대 경로 조각으로 시작했다면 결과도 상대 경로로 남깁니다. path.resolve()는 같은 일을 오른쪽에서 왼쪽으로 하고 항상 절대 경로를 반환합니다. 현재 작업 디렉터리에서 시작하고, 중간에 절대 경로 조각이 있으면 거기서 경로를 다시 시작합니다. 경로를 만들 때는 join, 절대 경로가 필요할 때는 resolve를 쓰세요.

users/ada/notes.txt, 다음으로 users/grace/todo.txt(..가 ada를 지웠습니다), 그리고 /srv/app/public/index.html과 src/lib/utils.js가 출력되었습니다. 남는 슬래시와 빈 조각은 사라졌습니다. join은 /public/을 새 시작점으로 보지 않고 그냥 다음 조각으로 봅니다.

path.join vs path.resolve

차이는 조각이 /로 시작하거나 첫 조각이 상대 경로일 때 드러납니다.

중간에 /public이 있으면 join은 /srv/app/public/img를 만들었고, resolve는 그 앞을 모두 버리고 /public/img를 만들었습니다. ..는 둘 다 같은 방식으로 처리했습니다. 마지막 두 줄은 join이 public/index.html을 상대 경로로 남긴 반면, resolve는 현재 폴더를 앞에 붙여 절대 경로로 만들었음을 보여 줍니다.

path.joinpath.resolve
결과입력처럼 상대 또는 절대 경로항상 절대 경로
/로 시작하는 조각그냥 다른 조각거기서 경로를 다시 시작
시작점없음현재 작업 디렉터리
인자 없음'.'현재 작업 디렉터리
주 용도조각으로 경로 만들기사용자 입력이나 설정을 절대 경로로 바꾸기

basename, dirname, extname, parse

이 함수들은 경로를 나눕니다. 문자열만 보기 때문에 파일이 실제로 있을 필요는 없습니다.

cat.final.jpg, cat.final, /home/ada/photos, .jpg가 출력되었습니다. extname은 마지막 점 뒤의 부분만 가져갑니다. path.parse는 전부를 한 번에 반환하고(root, dir, base, ext, name), path.format은 그런 객체를 다시 문자열로 바꿉니다.

__dirname과 __filename

CommonJS 파일에서 __dirname은 그 파일이 들어 있는 폴더의 절대 경로이고, __filename은 파일 자체의 경로입니다. 'data.json' 같은 상대 경로는 사용자가 node를 실행한 위치를 기준으로 하므로, 누가 다른 폴더에서 스크립트를 실행하는 순간 깨집니다. 대신 __dirname으로 경로를 만드세요.

config.json dark와 index.js가 출력되었습니다. 전체 경로는 컴퓨터마다 다릅니다. 그래서 직접 타이핑하지 않고 path.join(__dirname, ...)으로 만드는 것입니다.

ES 모듈의 __dirname: import.meta.dirname

ES 모듈에는 __dirname이 없어서, 쓰면 ReferenceError: __dirname is not defined in ES module scope가 발생합니다. Node.js 20.11부터는 import.meta.dirname과 import.meta.filename이 그 역할을 합니다.

index.js와 data/users.json이 출력되었습니다. 20.11 이전 Node.js에서는 모듈 URL에서 폴더를 구합니다. node:url의 fileURLToPath로 path.dirname(fileURLToPath(import.meta.url))이라고 씁니다.

문자열로 경로를 잇지 않는 이유

'data' + '/' + file은 macOS와 리눅스에서는 동작하지만, 구분자가 \인 윈도우에서는 실패하고, .., 겹친 슬래시, 빠진 슬래시도 처리하지 않습니다. path는 실행 중인 시스템의 규칙을 씁니다. 윈도우에서의 결과는 어느 컴퓨터에서든 path.win32로 확인할 수 있습니다.

const path = require('path');

path.posix.join('users', 'ada', 'notes.txt'); // 'users/ada/notes.txt'
path.win32.join('users', 'ada', 'notes.txt'); // 'users\\ada\\notes.txt'
path.sep;                                     // '/' on macOS and Linux, '\\' on Windows

path.join의 결과를 fs.readFile에 넘기면 같은 코드가 모든 시스템에서 파일을 읽습니다.

흔한 실수

extname이 이중 확장자를 알 거라고 기대하는 것. path.extname('archive.tar.gz')는 '.gz'이고, .env 같은 점 파일은 확장자가 아예 없습니다. path.extname('.env')는 ''입니다. 전체 접미사가 중요하다면 이름의 끝을 직접 확인하세요.

join이 사용자를 폴더 안에 가둬 줄 거라고 믿는 것. path.join('/srv/app', '../../etc/passwd')는 '/etc/passwd'입니다. 요청으로 받은 이름의 파일을 읽기 전에 경로를 resolve하고, 여전히 내 폴더로 시작하는지 확인하세요.

자주 묻는 질문

__dirname과 process.cwd()의 차이는 뭔가요?

__dirname은 현재 소스 파일이 있는 폴더이고 바뀌지 않습니다. process.cwd()는 사용자가 node를 실행한 폴더입니다. 프로그램을 자기 폴더에서 시작했을 때만 둘이 같습니다.

__dirname과 함께 쓸 때 path.join과 path.resolve 중 무엇을 써야 하나요?

path.join(__dirname, 'data.json')이라면 둘 다 됩니다. __dirname이 이미 절대 경로이기 때문입니다. 사용자가 입력한 경로처럼 조각 자체가 절대 경로일 수 있고 그 경로가 이기길 원한다면 resolve를 쓰세요.

URL에 path 모듈을 써도 되나요?

쓰지 마세요. URL은 항상 /를 쓰고 ..와 쿼리 문자열에 대한 자체 규칙이 있습니다. new URL('../img/logo.png', 'https://example.com/docs/page')처럼 URL 클래스를 쓰세요.

Coddy 프로그래밍 언어 일러스트

Coddy로 코딩 배우기

시작하기