pathモジュールは、ファイルやフォルダのパス文字列を組み立てたり分解したりするためのNode.js標準モジュールです。よく使う2つの違いは、path.joinが「渡された部品をつなげて整えるだけ」、path.resolveが「右から順にたどって必ず絶対パスを返す」という点にあります。

この記事では、同じ引数をjoinとresolveの両方に渡した出力を14パターン並べ、Windows形式とmacOS・Linux形式の結果もOS別の表にしました。basename・dirname・extname・parse・format・relative・normalize・sepの入出力早見表と、ESMで __dirname の代わりに使う import.meta.dirname(Node.js 20.11以降)と fileURLToPath の両方を、動かした版の結果つきで載せています。出力はすべてNode.js 22.22.2で実際に実行したものです。

pathモジュールの早見表

やりたいこと 書き方 結果の例
部品をつなげる path.join('a', 'b', 'c.txt') a/b/c.txt
絶対パスにする path.resolve('a', 'b') /home/taro/app/a/b
自分のファイルの場所を基準にする(CommonJS) path.join(__dirname, 'data', 'a.txt') /home/taro/app/data/a.txt
同上(ESM、20.11以降) path.join(import.meta.dirname, 'data', 'a.txt') /home/taro/app/data/a.txt
ファイル名を取り出す path.basename('/a/b/c.txt') c.txt
拡張子なしのファイル名 path.basename('/a/b/c.txt', '.txt') c
フォルダ部分を取り出す path.dirname('/a/b/c.txt') /a/b
拡張子を取り出す path.extname('c.tar.gz') .gz
まとめて分解する path.parse('/a/b/c.txt') { root, dir, base, ext, name }
部品から組み立てる path.format({ dir: '/a', name: 'c', ext: '.md' }) /a/c.md
相対パスを求める path.relative('/a/b', '/a/c/d') ../c/d
.. や重複した / を整える path.normalize('/a//b/../c') /a/c
絶対パスか調べる path.isAbsolute('/a') true
区切り文字 path.sep macOS・Linuxは /、Windowsは \
比べる点 path.join path.resolve
返す値 つなげて整えた文字列(相対のままもある) 必ず絶対パス
途中に / で始まる引数 ただの部品としてつなげる そこから先をやり直す(左側を捨てる)
現在の作業フォルダ(cwd) 使わない 絶対パスにならないときに前へ足す
引数なし・空文字 . cwd
末尾の / 残る 取り除かれる

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Node.js pathモジュールの使い方|joinとresolveの違い早見表」

この記事の確認環境

項目 内容
実行した版 Node.js 22.22.2(Linux x64)
版による違いの確認 import.meta.dirname は18.20.8 / 20.18.3 / 22.22.2 / 24.21.0で実行
macOS・Linux形式 Linux上で実行(path と path.posix は同じ結果)
Windows形式 Linux上で path.win32 を使って実行。Windowsの実機での実行は未実行
仕様の確認 Node.js公式ドキュメントのPathのページとECMAScript modulesのページを開いて確認

path.win32 は、どのOSで動かしてもWindowsと同じ規則で文字列を処理する入口です。cwdを使わない計算(joinやbasenameなど)は実機と同じ結果になります。cwdを使う計算(相対パスのresolve)はcwdの部分だけが実機と異なるため、この記事の表ではその部分を (cwd) と書いています。作業フォルダのパスは /home/taro/app に置き換えています(表示は環境に合わせて置き換えています)。

読み込み方

// CommonJS
const path = require('node:path');

// ESM
import path from 'node:path';

'path' と 'node:path' はどちらも同じモジュールです。node: を付けると、Node.jsの標準モジュールであることがひと目でわかります。

path.joinとpath.resolveの違いを実行結果で比べる

同じ引数を両方に渡した14パターン

次のスクリプトを /home/taro/app をcwdにして実行しました。

const path = require('node:path');

const cases = [
  ['a', 'b', 'c.txt'],
  ['/a', 'b'],
  ['a', '/b'],
  ['/a', '/b', 'c'],
  ['a', '../b'],
  ['a/b', '../../..', 'c'],
  ['a', '', 'b'],
  [''],
  [],
  ['a/', '/b/'],
  ['a', './b', '.'],
  ['/a/b', '../c', './d.txt'],
  ['.', 'a'],
  ['a//b', 'c/..'],
];

for (const c of cases) {
  console.log(JSON.stringify(c), '|', path.join(...c), '|', path.resolve(...c));
}

macOS・Linux形式の実行結果です。

No. 引数 path.join path.resolve
1 'a', 'b', 'c.txt' a/b/c.txt /home/taro/app/a/b/c.txt
2 '/a', 'b' /a/b /a/b
3 'a', '/b' a/b /b
4 '/a', '/b', 'c' /a/b/c /b/c
5 'a', '../b' b /home/taro/app/b
6 'a/b', '../../..', 'c' ../c /home/taro/c
7 'a', '', 'b' a/b /home/taro/app/a/b
8 '' . /home/taro/app
9 (引数なし) . /home/taro/app
10 'a/', '/b/' a/b/ /b
11 'a', './b', '.' a/b /home/taro/app/a/b
12 '/a/b', '../c', './d.txt' /a/c/d.txt /a/c/d.txt
13 '.', 'a' a /home/taro/app/a
14 'a//b', 'c/..' a/b /home/taro/app/a/b

表から読み取れる5つの決まり

  • joinは全部つなげてから整える。 途中の引数が / で始まっていても部品として扱います(No.3は a/b、No.4は /a/b/c)
  • resolveは右から左へ読み、絶対パスが現れたらそこで止まる。 cd コマンドを順に実行したときの行き先と同じ考え方です(No.3は cd a のあと cd /b をして /b、No.4は /b/c)
  • resolveは絶対パスにならなければcwdを前に足す。 No.1・5・7・11・13・14がこれに当たります
  • 空の結果は、joinでは .、resolveではcwd。 引数なしの path.resolve() は process.cwd() と同じ値でした
  • 末尾の / はjoinでは残り、resolveでは取り除かれる。 No.10がこの違いです

No.2とNo.12のように、先頭の引数が絶対パスで、あとの引数が / で始まらない場合は、両方とも同じ結果になります。path.join(__dirname, 'data', 'a.txt') と path.resolve(__dirname, 'data', 'a.txt') が同じ値になるのはこのためです。

どちらを使うかの判断表

状況 向いているもの 理由
自分のファイルの場所を基準に、決まった相対位置を指す path.join(__dirname, ...) 結果が引数だけで決まり、cwdに左右されない
コマンドの引数などで受け取ったパスを絶対パスにする path.resolve(input) 相対ならcwd基準、絶対ならそのまま、と利用者の感覚に合う
基準フォルダと、絶対かもしれないパスを組み合わせる path.resolve(base, input) 絶対パスが来たらそちらを優先できる
相対パスのまま部品をつなげたい(URLの一部、表示用など) path.join(...) cwdが足されない
設定ファイルに書かれた相対パスを、設定ファイルの場所を基準に解決する path.resolve(path.dirname(configPath), value) 相対・絶対のどちらが書かれても扱える

「結果をcwdに左右されたくないときはjoinに絶対パスの基準を渡す、相対でも絶対でも受け取りたいときはresolve」と覚えると選びやすくなります。

WindowsとmacOS・Linuxでの結果の違い

同じ引数のOS別の結果

同じ14パターンを path.win32 でも実行しました。違いが出る主なものを並べます。

引数 関数 macOS・Linux Windows
'a', 'b', 'c.txt' join a/b/c.txt a\b\c.txt
'a', 'b', 'c.txt' resolve /home/taro/app/a/b/c.txt (cwd)\a\b\c.txt
'/a', 'b' join /a/b \a\b
'a', '/b' resolve /b \b(実機では現在のドライブが付く)
'a', '../b' join b b
'a/b', '../../..', 'c' join ../c ..\c
'a/', '/b/' join a/b/ a\b\
'/a/b', '../c', './d.txt' join /a/c/d.txt \a\c\d.txt
'' join . .

Windows形式では、引数に / を書いても結果は \ にそろえられます。 逆にmacOS・Linux形式では \ は区切りではなく普通の文字として扱われます。

ドライブ文字を含むパス(Windows形式)

引数 path.win32.join path.win32.resolve
'C:\\app', 'data', 'a.txt' C:\app\data\a.txt C:\app\data\a.txt
'C:\\app', '\\data' C:\app\data C:\data
'C:\\app', 'D:\\data' C:\app\D:\data D:\data
'C:\\app', '..', 'x' C:\x C:\x
'C:/app', 'sub/file.txt' C:\app\sub\file.txt C:\app\sub\file.txt
'\\\\server\\share', 'dir', 'a.txt' \\server\share\dir\a.txt \\server\share\dir\a.txt

ここでもjoinとresolveの性格の違いが出ます。別ドライブの絶対パスを渡すと、joinはそのままつなげて C:\app\D:\data という使えないパスになり、resolveは D:\data に切り替えます。 利用者が入力したパスを受け取る処理ではresolveが向いています。

OSによって変わる値の一覧

項目 macOS・Linux Windows
path.sep / \
path.delimiter(PATH環境変数の区切り) : ;
絶対パスの例 /home/taro/app C:\Users\taro\app
isAbsolute('C:\\a') false true
isAbsolute('\\a') false true
basename('C:\\app\\a.txt') C:\app\a.txt(全体が1つの名前) a.txt
normalize('C:/app//src/../a.txt') C:/app/a.txt C:\app\a.txt
relative('C:\\app\\src', 'C:\\app\\public')(win32) — ..\public
relative('C:\\app', 'D:\\data')(win32) — D:\data(ドライブが違うと絶対パスが返る)
parse('C:\\app\\src\\a.txt').root(win32) — C:\

Windowsで作られたパス文字列をLinuxのサーバーで処理するような場面では、path.basename がパス全体を返します。相手のOSが決まっているときは、path.win32.basename(...) のようにOSを明示した入口を使うと、動かす場所に関係なく同じ結果になります。

OSの違いを吸収する書き方

書き方 結果
'data/' + name と文字列でつなぐ Windowsでも多くの場面で動くが、\ と / が混ざった表示になる
path.join('data', name) 動かしているOSの区切り文字でそろう
p.split(path.sep) 動かしているOSの区切りで分割できる
p.split(path.win32.sep).join(path.posix.sep) \ を / に置き換える(URLや設定ファイル向け)
process.env.PATH.split(path.delimiter) PATH環境変数をOSに合った区切りで分割できる

入出力早見表:分解する・組み立てる・整える

ここからの結果は、すべてmacOS・Linux形式での実行結果です。

basename・dirname・extname

呼び出し 結果
path.basename('/home/taro/app/src/report.final.tar.gz') report.final.tar.gz
path.basename('/home/taro/app/src/report.final.tar.gz', '.gz') report.final.tar
path.basename('/home/taro/app/') app(末尾の / は無視される)
path.dirname('/home/taro/app/src/report.final.tar.gz') /home/taro/app/src
path.dirname('/home/taro/app/') /home/taro
path.dirname('file.txt') .
path.extname('report.final.tar.gz') .gz(最後のドットから後ろだけ)
path.extname('.bashrc') ''(先頭ドットだけの名前は拡張子なし)
path.extname('README') ''
path.extname('a.') .

.tar.gz のような二重の拡張子は、extnameでは .gz だけが返ります。 .tar.gz 全体を扱いたいときは、name.endsWith('.tar.gz') のように文字列として判定します。

parseとformat

path.parse はパスを5つの部品に分け、path.format はその逆を行います。

console.log(path.parse('/home/taro/app/src/report.final.tar.gz'));
{
  root: '/',
  dir: '/home/taro/app/src',
  base: 'report.final.tar.gz',
  ext: '.gz',
  name: 'report.final.tar'
}
部品 上の例での値 意味
root / ルート(Windows形式では C:\ など)
dir /home/taro/app/src フォルダ部分(rootを含む)
base report.final.tar.gz ファイル名全体(name + ext)
name report.final.tar 拡張子を除いたファイル名
ext .gz 拡張子(最後のドットから後ろ)

「dir + 区切り文字 + base」で元のパスに戻り、baseは「name + ext」に分かれる、という2段の関係です。formatの例を並べます。

呼び出し 結果 決まり
path.parse('.bashrc') { root: '', dir: '', base: '.bashrc', ext: '', name: '.bashrc' } 先頭ドットの名前はnameに入る
path.format({ dir: '/home/taro', base: 'a.txt' }) /home/taro/a.txt dir + 区切り + base
path.format({ dir: '/x', name: 'a', ext: '.md' }) /x/a.md baseがなければname + ext
path.format({ dir: '/x', name: 'a', ext: 'md' }) /x/a.md extにドットがなければ補われる
path.format({ root: '/', base: 'a.txt', ext: '.md' }) /a.txt baseがあるとnameとextは使われない
path.format({ root: '/r', dir: '/x', base: 'a.txt' }) /x/a.txt dirがあるとrootは使われない

formatの優先順位は「dirがrootより優先」「baseがname + extより優先」の2つです。parseした結果の拡張子だけを変えたいときは、baseを渡さずにnameとextを渡します(次の「コピペ用の実例」を参照)。

relative・normalize・isAbsolute・sep

呼び出し 結果
path.relative('/home/taro/app/src', '/home/taro/app/public/img') ../public/img
path.relative('/a/b', '/a/b/c/d') c/d
path.relative('/a/b', '/a/b') ''(同じ場所は空文字)
path.normalize('/a//b/../c/./d/') /a/c/d/(末尾の / は残る)
path.normalize('') .
path.normalize('a/../..') ..
path.isAbsolute('/a') true
path.isAbsolute('./a') false
path.sep /
path.delimiter :
path.matchesGlob('/a/b.js', '/a/*.js') true
path.win32.toNamespacedPath('C:\\app') \\?\C:\app

path.matchesGlob は比較的新しい関数で、今回は20.20.2で関数として存在すること、22.22.2と24.21.0で上の結果になることを確認しました。18.20.8では typeof path.matchesGlob が undefined だったため、複数の版で動かすコードでは存在を確かめてから使います。

文字列以外を渡したとき

path.join('a', null);
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received null

pathの関数は文字列だけを受け取ります。 環境変数や設定値が未設定で undefined のまま渡るとこのエラーになるので、渡す前に既定値を入れておくと安定します。

ESMでの__dirnameの置き換え方

CommonJSでは __dirname(そのファイルがあるフォルダ)と __filename(そのファイルのパス)が最初から使えます。ESM(.mjs、または "type": "module" の .js)にはこの2つがないため、次のどちらかで置き換えます。

方法1:import.meta.dirname(Node.js 20.11以降)

// esm.mjs
import path from 'node:path';

console.log('import.meta.dirname  :', import.meta.dirname);
console.log('import.meta.filename :', import.meta.filename);
console.log(path.join(import.meta.dirname, 'data', 'a.txt'));
import.meta.dirname  : /home/taro/app
import.meta.filename : /home/taro/app/esm.mjs
/home/taro/app/data/a.txt

公式ドキュメントでは、追加された版はv20.11.0 / v21.2.0、安定版になったのはv22.16.0 / v24.0.0と記載されています。file: のモジュールでだけ使える点も書かれています。

方法2:fileURLToPath(ESMが使える全版)

// esm.mjs
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

console.log('import.meta.url :', import.meta.url);
console.log('__filename      :', __filename);
console.log('__dirname       :', __dirname);
import.meta.url : file:///home/taro/app/esm.mjs
__filename      : /home/taro/app/esm.mjs
__dirname       : /home/taro/app

版ごとの結果と選び方

版 import.meta.dirname fileURLToPath(import.meta.url)
18.20.8 undefined 動く
20.18.3 /home/taro/app 動く
22.22.2 /home/taro/app 動く
24.21.0 /home/taro/app 動く

fileURLToPath の列は、18.20.8 / 20.18.3 / 20.20.2 / 22.11.0 / 22.22.2 / 24.21.0で同じ書き方が動くことを確認しています。

状況 向いている書き方
Node.js 20.11以降だけで動かす import.meta.dirname
18系でも動かす、配布するライブラリ fileURLToPath(import.meta.url) + path.dirname
CommonJS __dirname

URLのpathnameをそのまま使わない

new URL('./x', import.meta.url).pathname でもパスらしい文字列が取れますが、日本語や空白が入ると結果が変わります。

const url = new URL('./デー タ/a.txt', import.meta.url);
console.log('URL.pathname  :', url.pathname);
console.log('fileURLToPath :', fileURLToPath(url));
URL.pathname  : /home/taro/app/%E3%83%87%E3%83%BC%20%E3%82%BF/a.txt
fileURLToPath : /home/taro/app/デー タ/a.txt

pathnameはURLとしてエンコードされた文字列なので、ファイルのパスとして使うときは fileURLToPath を通します。 逆にパスからURLを作るときは pathToFileURL を使います。

import { pathToFileURL } from 'node:url';
console.log(pathToFileURL('/home/taro/app/a b.txt').href);
file:///home/taro/app/a%20b.txt

コピペ用の実例

拡張子を差し替える・ファイル名の末尾に文字を足す

const path = require('node:path');

function replaceExt(file, ext) {
  const { dir, name } = path.parse(file);
  return path.format({ dir, name, ext });
}

function addSuffix(file, suffix) {
  const { dir, name, ext } = path.parse(file);
  return path.format({ dir, name: name + suffix, ext });
}

console.log(replaceExt('/home/taro/app/docs/guide.md', '.html'));
console.log(addSuffix('/home/taro/app/img/photo.png', '@2x'));
/home/taro/app/docs/guide.html
/home/taro/app/img/photo@2x.png

parseの結果からbaseを外し、nameとextを渡すのがポイントです。baseを残したままだと、formatはbaseを優先して元のファイル名を返します。

基準フォルダの外を指していないか確かめる

利用者が入力したファイル名を基準フォルダの下で開くときは、組み立てたあとに「基準フォルダの内側か」を確かめます。

const path = require('node:path');

function resolveInside(baseDir, userPath) {
  const target = path.resolve(baseDir, userPath);
  const rel = path.relative(baseDir, target);
  const inside = rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel);
  return { target, rel, inside };
}

console.log(resolveInside('/home/taro/app/uploads', 'a/b.png'));
console.log(resolveInside('/home/taro/app/uploads', '../secret.txt'));
console.log(resolveInside('/home/taro/app/uploads', '/etc/hosts'));
{
  target: '/home/taro/app/uploads/a/b.png',
  rel: 'a/b.png',
  inside: true
}
{
  target: '/home/taro/app/secret.txt',
  rel: '../secret.txt',
  inside: false
}
{ target: '/etc/hosts', rel: '../../../../etc/hosts', inside: false }

resolveで絶対パスにしてから、relativeの結果が .. で始まらないことを確かめる流れです。joinだけでは .. を含む入力が基準フォルダの外へ出ることがあるため、入力を受け取る処理ではこの確認を入れておくと安心です。rel !== '' の条件は、基準フォルダそのものを指す入力(空文字や .)を対象外にするためのものです。フォルダ自体を許可したい処理では、この条件を外します。シンボリックリンクまで考える場合は、fs.realpath で実際のパスに直してから比べます(この部分は未実行)。

区切り文字を / にそろえる・PATHを分割する

const toPosix = (p) => p.split(path.win32.sep).join(path.posix.sep);
console.log(toPosix('src\\components\\Button.vue'));

console.log('/usr/local/bin:/usr/bin:/bin'.split(path.delimiter));
src/components/Button.vue
[ '/usr/local/bin', '/usr/bin', '/bin' ]

つまずきやすい箇所と直し方

起きること 理由 直し方
path.resolve('data', '/a.txt') が /a.txt になる resolveは絶対パスが現れたら左側を捨てる つなげたいだけなら path.join を使う
実行する場所によって結果が変わる 相対パスをresolveするとcwdが基準になる __dirname / import.meta.dirname を基準にする
__dirname is not defined in ES module scope ESMには __dirname がない import.meta.dirname、または fileURLToPath
import.meta.dirname が undefined Node.jsが20.11より前 path.dirname(fileURLToPath(import.meta.url))
extname('a.tar.gz') が .gz 最後のドットから後ろだけを返す endsWith('.tar.gz') で判定する
format で拡張子が変わらない baseがnameとextより優先される baseを渡さずnameとextを渡す
Linux上でWindowsのパスのbasenameが取れない \ が区切りとして扱われない path.win32.basename を使う
日本語を含むパスが %E3%83... になる URLのpathnameをそのまま使っている fileURLToPath を通す
The "path" argument must be of type string undefined や null を渡している 渡す前に既定値を入れる

書く前のチェックリスト

確認すること 目安
パスを + で文字列連結していないか path.join に置き換える
相対パスの基準は決まっているか ファイルの場所なら __dirname / import.meta.dirname、利用者の入力ならcwd
resolveに / で始まる引数を渡していないか 渡すと左側が捨てられる
ESMで __dirname を使っていないか import.meta.dirname(20.11以降)または fileURLToPath
入力されたパスが基準フォルダの外へ出ないか resolve + relativeで確かめる
別のOSで作られたパスを処理していないか path.win32 / path.posix を明示する
二重の拡張子を扱っていないか extnameは最後の1つだけを返す

よくある質問

Q. path.joinとpath.resolveはどう使い分けますか? A. joinは渡した部品をつなげて整えるだけで、cwdを使いません。resolveは右から順にたどって必ず絶対パスを返し、絶対パスにならないときはcwdを前に足します。ファイルの場所を基準に決まった位置を指すなら path.join(__dirname, ...)、利用者から受け取った相対・絶対どちらもありうるパスを扱うなら path.resolve(...) が向いています。

Q. ESMで__dirnameを使うにはどうすればよいですか? A. Node.js 20.11以降なら import.meta.dirname がそのまま使えます。18系でも動かす場合は、fileURLToPath(import.meta.url) でファイルのパスを取り出し、path.dirname でフォルダ部分にします。今回の実行では、18.20.8で import.meta.dirname は undefined、20.18.3以降ではフォルダのパスが返りました。

Q. Windowsでも同じコードで動きますか? A. path.join や path.resolve を使っていれば、区切り文字はOSに合わせて自動で切り替わります。違いが出るのは、パスを / で直接分割している箇所や、別のOSで作られたパス文字列を処理する箇所です。前者は path.sep、後者は path.win32 / path.posix を使うとそろえられます。この記事のWindows形式の結果は path.win32 をLinux上で動かしたもので、実機での実行は未実行です。

Q. path.parseとpath.formatはどんなときに使いますか? A. ファイル名の一部だけを変えたいときに便利です。parseでroot・dir・base・name・extに分け、変えたい部品を差し替えてformatで組み立て直します。拡張子を変えるときは、baseを渡さずnameとextを渡すのがポイントです。

まとめ

  • joinは「つなげて整える」、resolveは「右からたどって必ず絶対パスを返す」
  • resolveは途中に絶対パスがあると左側を捨てる(resolve('a', '/b') は /b)。joinは部品としてつなげる(a/b)
  • cwdに左右されたくないときは path.join(__dirname, ...)、ESMでは import.meta.dirname(20.11以降)
  • 18系でも動かすESMは path.dirname(fileURLToPath(import.meta.url))
  • extnameは最後のドットから後ろだけ、formatは「dirがrootより優先」「baseがname + extより優先」
  • Windows形式では区切りが \ にそろい、別ドライブの絶対パスはresolveが切り替える
  • 入力されたパスはresolve + relativeで基準フォルダの内側か確かめる

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Node.js pathモジュールの使い方|joinとresolveの違い早見表」

株式会社RJCでは、Node.jsのパス操作の考え方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?