create-react-app으로 리액트 프로젝트를 생성해 보자. create-react-app은 리액트의 보일러 플레이트 코드를 생성하기 때문에 직접 개발 환경을 구성하지 않더라도 별도의 설정 없이 프로젝트를 바로 시작할 수 있다.
package.json 내의 script 명령을 보면 이런 게 있다.
"start": "react-scripts start",
이 코드는 node_modules./bin 폴더에 있는 react-scripts.js 파일을 실행시킨다.
react-scripts는 create-react-app의 스크립트를 모아놓은 곳이다. react-scripts start는 개발 환경을 셋업하고, 핫 리로딩이 적용된 서버를 실행시킨다.
react-scripts.js 파일을 열어보면 대략 다음과 같이 되어있다.
...생략
if (['build', 'eject', 'start', 'test'].includes(script)) {
const result = spawn.sync(
'node',
nodeArgs
.concat(require.resolve('../scripts/' + script))
.concat(args.slice(scriptIndex + 1)),
{ stdio: 'inherit' }
);
...생략
process.exit(result.status);
}
...생략
build, eject, start, test 키워드가 있을 경우, ../scripts/
폴더 내의 파일을 찾게 된다.
react-scripts start 명령어의 경우, 키워드는 start니까 ../scripts/
폴더 내에 있는 start.js 파일을 열어보자.
...생략
const paths = require('../config/paths');
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
...생략
checkRequiredFiles()
로 배열 내의 파일 경로가 유효한 경로인지 확인한다. checkRequiredFiles()
는 react-dev-utill 내에 있다. 만약 파일이 없거나 경로가 유효하지 확인하고, 전부 존재한다면 if문 아래에 있는 개발 서버 실행 코드를 실행시킨다.
../config/paths
내에 있는 paths.appHtml, paths.appIndexJs을 확인해보자.
paths.js 파일은 eject 이전의 경로, eject 이후의 경로 그리고 publish 이전의 경로를 나눠서 관리한다.
const moduleFileExtensions = [
'web.mjs',
'mjs',
'web.js',
'js',
'web.ts',
'ts',
'web.tsx',
'tsx',
'json',
'web.jsx',
'jsx',
];
const resolveModule = (resolveFn, filePath) => {
const extension = moduleFileExtensions.find(extension =>
fs.existsSync(resolveFn(`${filePath}.${extension}`))
);
if (extension) {
return resolveFn(`${filePath}.${extension}`);
}
return resolveFn(`${filePath}.js`);
};
const resolveOwn = relativePath => path.resolve(__dirname, '..', relativePath);
module.exports = {
appHtml: resolveOwn('template/public/index.html'),
appIndexJs: resolveModule(resolveOwn, 'template/src/index'),
};
resolveModule(resolveOwn, 'template/src/index')
인 이유는 appIndexJs가 자바스크립트 파일이 아닐 수도 있기 때문이다. (ts, jsx 등등…)
resolveOwn()을 호출하면 현재 모듈의 디렉토리 이름을 포함한 경로를 반환한다.
resolveModule()에 resolveOwn()함수와 template/src/index
를 넘기면 현재 모듈의 디렉토리 + 파일 경로 + 확장자를 합쳐 경로를 만들고 resolveOwn()를 호출하게 된다.
결론적으로는 현재 프로젝트에 있는 index.js와 index.html가 존재하는지 파악해, 경로에 파일이 존재하는 경우 개발 서버를 실행시키는 거라 할 수 있겠다.
References
- https://stackoverflow.com/questions/41738421/how-react-js-index-js-file-contacting-index-html-for-id-references
- https://stackoverflow.com/questions/50722133/what-exactly-is-the-react-scripts-start-command