React 의 폴더 구조 방식인 FSD 에 대해 알아보겠습니다
FSD(Feature-Sliced Design)

기능분할설계 패턴으로 뷰와 로직을 분리하는 것에서 시작해서 응집도의 중요성을 강조하는 패턴입니다 .
적절하게 파일들을 폴더구조를 통해 고립시키는 것이 특징입니다
그렇다면 가장 중요한 키워드인 응집도란 무엇일까요?
React는 기본적으로 응집도를 높게 설계해야 합니다. 응집도가 높아지면 재사용성과 테스트성이 향상되지만, 코드의 흐름을 해석하기 어려워질 수 있습니다….
결합도가 높으면 한 파일에 코드가 너무 많아져 관리가 어려워집니다. 설계를 경험해보신 분들이 모두 겪는 문제인데 이를 해결하기 위해 FSD 패턴이 필요합니다.
Layer -> Slice -> Segment의 구조로 나누어집니다.LoginPage, SignupPage는 App에서 조합되며, 서로 참조하지 않습니다.UI, style, model, lib, asset 등이 있습니다.여기서 중요한것이 Layer의 하위에서 상위로의 참조는 절대 불가하다는 것입니다.
역할에 맞게 컴포넌트를 고립 시켜야합니다 !
fsd패턴은 규약입니다. 서로가 알아볼 수 있게 만들려면 그리고 react가 추구하는 방향에 옳게 개발하기 위해서는 아래 규약을 지키는것이 필수적입니다.
index.js가 존재합니다.한번 쓰이는 컴포넌트들은 최대한 묶어야 합니다 재사용성이 없거나 백엔드 통신을 통해 값이 변하는 부분을 제외한 나머지를 모두 결합시켜야합니다.
아토믹 디자인처럼 가능한 모든것을 분리하는 것 과는 조금 다르게 느껴질 수 있다고 생각합니다. - ( 아토믹 디자인도 모두 나누는 것은 아니기 때문에 비슷하기도 합니다)
개발하는 과정에서 재사용이 필요하겠는데? 싶은것만 layer를 내려서 개발을 하면 쉽게 FSD 패턴을 적용할 수 있을 것이라고 생각합니다 . 또한 state에 따라 렌더링을 구분짓고 싶을때도 layer에서 slice로 구분해서 개발을 하면 좋습니다 !
이론을 너무 오래설명 했던것 같은데 FSD 패턴을 직접 적용해보는것 만큼 좋은 방법이 없을 것 같습니다
그렇다면 FSD 패턴을 직접 만들어보면서 test 해봅시다!
JS 에서 React 로 넘어오면서 다들 node.js 를 사용했을 것이다.
브라우저 종속성을 해결해주고 js 파일을 호출할때만 메모리를 올리는등 장점이 너무나도 많은데 이러한 node를 npm 구조를 통해 쉽게 관리하는데 이 node의 npm 을 사용해봅시다!
node 기반으로 React 프로젝트 자동으로 설정해주는 패키지로 Meta에서 개발했습니다
CRA에는 Webpack기능이 내장되어있습니다. Webpack이 무엇인지는 다른 게시글에서 설명해놓았으니 참고 바랍니다 !
npx 명령어를 통해 CRA를 설치하지 않고 바로 사용가능합니다
npx create-react-app 'your-project-naem'
설치가 안되는 사용자도 있을텐데 이는 권한 문제일 가능성이 큽니다 구글링을 해보거나 아래 코드를 기입 해보세요 ! 설치할때 관리자 권한으로 설치하지 마시길 바랍니다 ! 만약 관리자 권한으로 설치했다면 권한을 변경해야만 합니다
sudo chown -R $(whoami) ~/.npm
프로젝트는 아래와 같이 구성됩니다.
package.json:package-lock.json:node_modules:src: 경로 접근이 불가능한 파일들이 위치합니다.public: 경로 접근이 가능한 파일들이 위치하며, index.html과 같은 파일이 여기에 포함됩니다.npm install:package.json을 참고하여 필요한 모듈을 다운로드합니다.src 파일에 일반적으로 우리가 사용하는 코드를 작성합니다 public의 코드는 사용자가 참조가 가능하기 때문에 대부분의 코드는 src에 작성하고 public에는 이미지를 넣을때 쓰곤 합니다 .
public의 이미지와 src 이미지 참조방식이 다릅니다
public 폴더 안에 images/logo.png 파일이 있다면, HTML이나 JSX에서 이를 다음과 같이 참조할 수 있습니다:
<img src="/images/logo.png" alt="Logo" />
src에 있을때는 import 통해서 해야합니다
import logo from './assets/logo.png';
이것만 봐도 딱 감이 오실것이라고 생각합니다
webpack의 기능이 번들링 해주는 기능이기 때문에 src의 코드를 app에서 번들링 해주고 public의 index.html에 넣어주는 것이다
이 과정을 거치고 public에 index.html을 작성하고 아래 코드를 입력하면 HMR (Hot Module Reload) 코드를 바로 화면에 반영해주도록 해서 Babel을 통해 ES5로 변환해서 브라우저에 전달합니다. 브라우저의 종속성을 고려해주기 때문에 ES5 이후 기술도 사용가능합니다! ( 노드의 핵심!! )
일반적으로 레이어는 App -> Page -> Process -> Widgets -> Features -> Entities -> Shared의 구조를 가집니다!
그중 우리는 아래중 체크된 부분만을 사용하자
ReactDOM.createRoot(document.getElementById("root")).render(
<RecoilRoot>
<App />
</RecoilRoot>
);
const App = () => {
const [theme_dark] = useDarkAtom();
return (
<BrowserRouter>
<ThemeProvider theme={theme_dark ? darkTheme : lightTheme}>
<Header />
<Page />
</ThemeProvider>
</BrowserRouter>
);
};
export default App;
const Page = () => {
return (
<>
<Aside />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/shorts" element={<Shorts />} />
<Route path="/detail" element={<Detail />} />
<Route path="/subscribe" element={<ExceptPage />} />
<Route path="/youtubeMusic" element={<ExceptPage />} />
<Route path="/myVideo" element={<ExceptPage />} />
<Route path="/offline" element={<ExceptPage />} />
</Routes>
</>
);
};
export default Page;
SPA 방식으로 설계하는 것을 추천한다
싱글 페이지 어플리케이션의 약자인데 서버와의 통신없이 페이지 전환은 엄청난 장점이라고 생각한다
부드럽고 트래픽 적고 코드 재사용하기 좋고 추천!
import Comment from "./ui/Comment";
export default Comment;
const useDetailCommentData = () => {
const [commentDataList, setCommentDataList] = React.useState([]);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState(false);
React.useEffect(() => {
const fetchData = async () => {
try {
const commentData = getCommentData();
//getData부분에 본인이 원하는 API를 실행시키면 되겠다 !
// 주소를 입력하는 방법도 있겠다
setCommentDataList(commentData);
setLoading(false);
} catch (error) {
setError(true);
}
};
fetchData();
}, []);
return [commentDataList, loading, error];
};
export default useDetailCommentData;
놀랍게도 layer만 살펴봤는데도 벌써 꽤 내용이 많습니다 .
하지만 slice는 별거 없습니다 !
slice 구조는 폴더의 집합입니다
예시를 들자면 Page 폴더의 슬라이스는
Shorts , main , …etc 가 있을것입니다.
이 슬라이스 안에 이제 각각의 세그먼트들을 조합합니다
slice에도 index.js 를 두어

자주 사용하는 슬라이스만 얘기 해보자면 model , ui , assets 정도 있습니다
slice는 다른 요소들과 조금 독특합니다

만들어진 Aside 루트 index.js 에서 다음과 같은 방식으로 export 해주면 폴더를 import 하는 것만으로 Aside 컴포넌트를 호출할 수 있기에 import 깊이도 조절할 수 있습니다. + 고립의 목적 다른 폴더들은 export 하지 않게 막기 위함도 있습니다
import Aside from "./ui/aside/Aside.js";
export default Aside;
이처럼 FSD 패턴을 공부하고 사용 해보았는데 간단한 예시를 보여드리겠습니다
src/
├── widgets/
│ └── aside/
│ ├── api/
│ │ └── asideApi.js
│ ├── model/
│ │ └── asideModel.js
│ ├── ui/
│ │ ├── Aside/
│ │ │ ├── Aside.js
│ │ │ └── style.js
│ │ └── Aside_icon/
│ │ ├── Aside_icon.js
│ │ └── style.js
│ └── index.js
├── pages/
│ ├── home/
│ │ ├── api/
│ │ │ └── homeApi.js
│ │ ├── model/
│ │ │ └── homeModel.js
│ │ ├── ui/
│ │ │ └── Home/
│ │ │ ├── Home.js
│ │ │ └── style.js
│ │ └── index.js
│ ├── detail/
│ │ ├── api/
│ │ │ └── detailApi.js
│ │ ├── model/
│ │ │ └── detailModel.js
│ │ ├── ui/
│ │ │ └── Detail/
│ │ │ ├── Detail.js
│ │ │ └── style.js
│ │ └── index.js
│ └── index.js
├── app/
│ │
│ └── index.js
└── index.js