하나의 큰 파일을 여러 개의 작은 파일로 분리하는 과정, Modularize에 대해 알아봅시다.
이번 시간에는 파일을 Component 단위로 분리하는 법에 대해서 알아보겠습니다. 여태까지 우리는 index.js 안에서 모든 Component들을 만들고, 수정했습니다. 그런데 우리의 프로젝트가 점점 커진다면 하나의 파일 안에서 모든 Component들을 만드는 것이 가독성이 떨어지고, 특정 Component를 찾는 시간이 오래 걸릴 것입니다. 그래서 개발자들은 Component 단위로 각각 다른 파일을 만들고, 서로 다른 파일 간 Component를 내보내고(export) 불러오는(import) 방법을 통해 가독성도 높이고, 기능도 그대로 유지하게 하곤 합니다. 오늘은 그 방법에 대해 알아보겠습니다.
혹시 우리가 처음 create-react-app으로 프로젝트를 만들었던 때를 기억하시나요? 그 때, 우리는 index.js 뿐 아니라 App.js라는 파일도 가지고 있었습니다.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
여기에서 보면, index.js에서 import App from ‘./App’이라는 문구가 있고, App.js에서는 export default App을 통하여 App이라는 이름의 Component를 내보내고 있습니다. 사실 대부분의 React 프로젝트에서는 우리가 사용한 것처럼 index.js만 사용하기보다는, 위와 같은 방법으로 index.js, App.js 그리고 추가적으로 파일을 만들어서 사용하는 방법을 택하고 있습니다. 그럼 이제 파일을 분리하는 방법에 대해 알아봅시다.
이럴 때는 위와 같은 방식으로 내보내고 불러오면 됩니다.
내보내는 파일에서
export default 내보낼_객체_이름;
불러오는 파일에서
import 새로_받을_이름 from '내보낸_파일의_위치';
내보내는 파일에서는 하나의 객체만을 내보내기 때문에, 이 파일의 기본 export 라는 뜻으로 default 키워드를 사용합니다.
이 방법의 경우 하나의 파일에서 하나의 객체만을 내보내기 때문에, 새로 받을 이름을 원래의 이름과 상관없이 받을 수 있습니다. (하지만 동일하게 받는 것이 관례입니다.)
이럴 때는 default 키워드 없이 export 후, import 할 때 destructuring 문법을 사용하여 받아올 수 있습니다.
내보내는 파일에서
export const 객체1; export const 객체2;
불러오는 파일에서
import { 객체1, 객체2 } from '내보낸_파일의_위치';
이 경우에는 destructuring 문법이기 때문에 받아오고 싶은 객체만 사용하면 됩니다. 또한 내보내는 객체의 이름과 불러오는 객체의 이름이 같아야 합니다. 만약 다르게 하고 싶다면 아래와 같은 문법을 사용하면 됩니다.
import { 객체1 as 나만의객체이름 } from '내보낸_파일의_위치';
이제 위 내용을 우리의 프로젝트에 적용시켜봅시다. 하나의 파일로 만들어진 Component들을 여러 개의 파일로 분리하는 방법은 아래의 순서를 따르시면 됩니다.
위의 순서를 따르고 나면, 아래와 같은 상태가 됩니다.
index.js
import React, { Component } from "react";
import ReactDom from "react-dom";
import FCHello from "./FCHello";
import CCHello from "./CCHello";
class App extends Component {
render() {
return (
<>
<FCHello name="Function Component" />
<CCHello name="Class Component" />
</>
);
}
}
ReactDom.render(<App />, document.getElementById("root"));
FCHello.js
import React, { useState } from "react";
function FCHello({ name }) {
const [number, setNumber] = useState(0);
return (
<>
<h1>Hello {name}!</h1>
<h2>{number}</h2>
<button
onClick={() => {
setNumber(number + 1);
}}
>
Plus
</button>
</>
);
}
export default FCHello;
CCHello.js
import React, { Component } from "react";
class CCHello extends Component {
state = {
number: 0,
};
render() {
const { name } = this.props;
return (
<>
<h1>Hello {name}!</h1>
<h2>{this.state.number}</h2>
<button
onClick={() => {
this.setState({ number: this.state.number + 1 });
}}
>
Plus
</button>
</>
);
}
}
export default CCHello;
위 과정을 정상적으로 진행했는지는, 서버를 구동했을 때 에러가 나지 않으면 됩니다.
우리가 React 프로젝트를 사용하면서 파일을 내보내고 받을 방법은 위에서 사용한 방법이 대부분입니다. 하지만 import와 export를 사용하는 방법은 이것보다 더 많은 방법이 있습니다. 예를 들면 이런 것들이 있습니다.
import defaultExport from "module-name";
import * as name from "module-name";
import { export1 } from "module-name";
import { export1 as alias1 } from "module-name";
import { export1 , export2 } from "module-name";
import { foo , bar } from "module-name/path/to/specific/un-exported/file";
import { export1 , export2 as alias2 , [...] } from "module-name";
import defaultExport, { export1 [ , [...] ] } from "module-name";
import defaultExport, * as name from "module-name";
import "module-name";
var promise = import("module-name");
// Exporting individual features
export let name1, name2, ..., nameN; // also var, const
export let name1 = ..., name2 = ..., ..., nameN; // also var, const
export function functionName(){...}
export class ClassName {...}
// Export list
export { name1, name2, ..., nameN };
// Renaming exports
export { variable1 as name1, variable2 as name2, ..., nameN };
// Exporting destructured assignments with renaming
export const { name1, name2: bar } = o;
// Default exports
export default expression;
export default function (...) { ... } // also class, function*
export default function name1(...) { ... } // also class, function*
export { name1 as default, ... };
// Aggregating modules
export * from ...; // does not set the default export
export * as name1 from ...; // Draft ECMAScript(r) 2O21
export { name1, name2, ..., nameN } from ...;
export { import1 as name1, import2 as name2, ..., nameN } from ...;
export { default } from ...;
지금 이 모든 내용을 익히는 것은 의미도 없고 공부의 능률을 떨어뜨릴 수 있어, 필요할 때 아래 링크를 통해 필요한 기능을 찾는 것을 추천드립니다.