QnA

layers-segments

Q&A 정리: layers-segments

FSD에서 레이어의 목적과 각 레이어의 책임은 무엇인가?

FSD(Feature-Sliced Design)는 프론트엔드 코드를 책임 범위에 따라 계층(레이어)으로 나누는 설계 방법론이다. app(앱 전체 설정), pages(페이지), widgets(큰 UI 블록), features(사용자 기능), entities(비즈니스 개념), shared(공용 도구) 순으로 나뉘며, 위 레이어일수록 더 많은 책임과 의존성을 갖는다.

Their purpose is to separate code based on how much responsibility it needs and how many other modules in the app it depends on. Every layer carries special semantic meaning to help you determine how much responsibility you should allocate to your code.

Everything that makes the app run — routing, entrypoints, global styles, providers.

All kinds of app-wide matters, both in the technical sense (e.g., context providers) and in the business sense (e.g., analytics).

Here are the segments that you can typically find in this layer:

📁 routes — the router configuration 📁 store — global store configuration 📁 styles — global styles 📁 entrypoint — the entrypoint to the application code, framework-specific

Full pages or large parts of a page in nested routing.

If a UI block on a page is not reused, it's perfectly fine to keep it inside the page slice.

Large self-contained chunks of functionality or UI, usually delivering an entire use case.

actions that bring business value to the user.

Business entities that the project works with, like user or product.

Reusable functionality, especially when it's detached from the specifics of the project/business, though not necessarily.


Widgets 레이어는 언제 사용하고 언제 피해야 하는가?

Widgets는 여러 페이지에서 재사용되거나, 한 페이지 안에 독립적인 큰 UI 블록이 여러 개일 때 사용한다. 반면, 페이지의 핵심 콘텐츠이면서 다른 곳에서 재사용되지 않는 UI는 위젯으로 분리하지 않고 해당 페이지 안에 두는 것이 맞다.

The Widgets layer is intended for large self-sufficient blocks of UI. Widgets are most useful when they are reused across multiple pages, or when the page that they belong to has multiple large independent blocks, and this is one of them.

If a block of UI makes up most of the interesting content on a page, and is never reused, it should not be a widget, and instead it should be placed directly inside that page.


여러 페이지에서 재사용되는 큰 UI 블록을 Shared와 Widgets 중 어디에 놓아야 하는가?

Shared 레이어는 그 위의 레이어(Entities, Features 등)를 참조할 수 없다. 재사용하려는 UI 블록이 이런 상위 레이어의 코드를 필요로 한다면 Shared에 놓을 수 없으므로, 그 위에 위치한 Widgets 레이어에 배치해야 한다.

There's a caveat to putting large blocks of UI in Shared — the Shared layer is not supposed to know about any of the layers above. Between Shared and Pages there are three other layers: Entities, Features, and Widgets. Some projects may have something in those layers that they need in a large reusable block, and that means we can't put that reusable block in Shared, or else it would be importing from upper layers, which is prohibited. That's where the Widgets layer comes in. It is located above Shared, Entities, and Features, so it can use them all.


FSD에서 Entities와 Features의 핵심 차이는 무엇인가?

Entity는 앱이 다루는 실제 개념(예: 사용자, 상품)이고, Feature는 사용자가 그 개념으로 하는 행동(예: 로그인, 장바구니 담기)이다. 쇼핑몰로 비유하면, "상품"이 Entity이고 "상품을 장바구니에 넣기"가 Feature다.

An entity is a real-life concept that your app is working with. A feature is an interaction that provides real-life value to your app's users, the thing people want to do with your entities.

Specifically for entities/ui, it is primarily meant to reuse the same appearance across several pages in the app, and different business logic may be attached to it through props or slots.


FSD에서 feature 슬라이스의 경계를 어떻게 판단하는가? 하나의 feature에 여러 기능이 들어가면 어떤 문제가 생기는가?

하나의 feature는 사용자에게 하나의 가치를 제공하는 기능 단위여야 한다. "지도-사무실"처럼 모호한 이름이 아니라 "회의실 예약", "직원 검색"처럼 구체적인 행동 단위로 나눠야 한다. 여러 기능을 하나에 넣으면 경계가 무너져 유지보수가 어려워진다.

One feature is one useful functionality for the user. When several features are implemented in one feature, this is a violation of borders. The feature can be indivisible and growing - and this is not bad. Bad - when the feature does not answer the question "What is the business value for the user?" There can be no "map-office" feature. But booking-meeting-on-the-map, search-for-an-employee, change-of-workplace - yes.


FSD에서 Entities 레이어를 만들지 않아도 되는가? 만든다면 언제 만들어야 하는가?

Entities 레이어가 없어도 FSD 규칙에 어긋나지 않으며, 오히려 아키텍처가 단순해진다. FSD는 "필요할 때 나중에 분리하라"는 원칙을 권장하는데, Entities에 넣은 코드는 상위 모든 레이어에 영향을 줄 수 있어 섣불리 만들면 리팩토링 위험이 커지기 때문이다.

It is completely fine for the application to have no entities layer. It doesn't break FSD in any way, on the contrary, it simplifies the architecture and keeps the entities layer available for future scaling.

FSD 2.1 encourages deferred decomposition of slices instead of preemptive, and this approach also extends to entities layer.

Remember: the later you move code to the entities layer, the less dangerous your potential refactors will be — code in Entities may affect functionality of any slice on higher layers.


FSD에서 세그먼트란 무엇이며, 각 세그먼트의 역할은?

세그먼트는 슬라이스 안의 코드를 기술적 성격별로 분류하는 폴더다. ui(화면 표시), api(서버 통신), model(데이터와 비즈니스 로직), lib(보조 코드), config(설정값) 등으로 나뉜다. 이름은 "components"나 "hooks"처럼 코드의 형태가 아니라 목적을 나타내야 한다.

Their purpose is to group code by its technical nature.

Make sure that the name of these segments describes the purpose of the content, not its essence.

For example, components, hooks, and types are bad segment names because they aren't that helpful when you're looking for code.

  • ui: everything related to UI display: UI components, date formatters, styles, etc.

  • api: backend interactions: request functions, data types, mappers, etc. / for code that handles rendering and appearance

  • model: the data model: schemas, interfaces, stores, and business logic. / for storage and business logic

  • lib: library code that other modules on this slice need.

  • config: configuration files and feature flags. / for feature flags, environment variables and other forms of configuration


FSD 슬라이스에서 Public API의 역할과 리팩토링 지원 방식은?

Public API는 슬라이스가 외부에 공개하는 창구(보통 index 파일)다. 외부는 이 창구를 통해서만 슬라이스를 사용할 수 있으므로, 내부 구조를 자유롭게 리팩토링해도 창구만 유지하면 다른 코드에 영향을 주지 않는다. 건물의 정문만 알면 내부 인테리어가 바뀌어도 방문에 문제가 없는 것과 같다.

A public API is a contract between a group of modules, like a slice, and the code that uses it. It also acts as a gate, only allowing access to certain objects, and only through that public API. In practice, it's usually implemented as an index file with re-exports:

In the context of Feature-Sliced Design, the term public API refers to a slice or segment declaring what can be imported from it by other modules in the project.

For example, in JavaScript that can be an index.js file re-exporting objects from other files in the slice.

This enables freedom in refactoring code inside a slice as long as the contract with the outside world (i.e. the public API) stays the same.

The rest of the application must be protected from structural changes to the slice, like a refactoring. Only the necessary parts of the slice should be exposed.


FSD에서 Shared 레이어와 도메인 레이어의 Public API 전략은 어떻게 다른가?

Shared 레이어는 슬라이스가 없기 때문에 세그먼트마다 별도의 공개 창구를 두는 것이 편리하다. 반면 다른 레이어(Entities, Features 등)는 슬라이스별로 하나의 index 파일을 두고, 세그먼트 구조는 내부 구현으로 감추는 것이 일반적이다.

For the Shared layer that has no slices, it's usually more convenient to define a separate public API for each segment as opposed to defining one single index of everything in Shared. This keeps imports from Shared naturally organized by intent. For other layers that have slices, the opposite is true — it's usually more practical to define one index per slice and let the slice decide its own set of segments that is unknown to the outside world because other layers usually have a lot less exports.


FSD에서 Public API에 와일드카드 re-export를 피해야 하는 이유는?

와일드카드(export *)를 쓰면 슬라이스가 뭘 공개하는지 한눈에 파악할 수 없고, 내부 구현이 의도치 않게 노출될 수 있다. 누군가 노출된 내부 코드에 의존하기 시작하면 내부 리팩토링이 어려워지므로, 공개할 것만 명시적으로 나열하는 것이 안전하다.

It may be tempting to create wildcard re-exports of everything, especially in early development of the slice, because any new objects you export from your files are also automatically exported from the slice:

This hurts the discoverability of a slice because you can't easily tell what the interface of this slice is. Not knowing the interface means that you have to dig deep into the code of a slice to understand how to integrate it. Another problem is that you might accidentally expose the module internals, which will make refactoring difficult if someone starts depending on them.