PUBLISHED
Steiger
작성일: 2026.01.05

FSD(Feature-Sliced Design)는 프론트엔드 아키텍처를 장기적으로 유지하기에 매우 유용한 방법론이다. 레이어와 슬라이스를 명확히 구분하고 의존 방향을 제한함으로써, 프로젝트 규모가 커질수록 코드베이스의 복잡도를 구조적으로 통제할 수 있다. 다만 이 장점은 동시에 부담이 되기도 한다. 규칙이 명확한 만큼, 이를 사람의 기억과 자율에만 맡기기에는 구조적 제약이 상당히 크다.
실무에서는 이러한 제약이 곧잘 문제로 드러난다. 규칙을 알고 있어도 바쁜 개발 과정에서 잘못된 import가 생기고, public API를 우회한 접근이 누적되며, 어느 순간부터 구조는 서서히 무너진다. 리뷰로 이를 모두 잡아내기에는 한계가 있고, ESLint 같은 도구는 코드 스타일과 문법만 다룰 뿐 아키텍처 자체를 감시하지는 않는다. 이 지점에서 Steiger가 의미를 갖는다. Steiger는 FSD의 규칙을 코드 차원에서 강제하는, 아키텍처 전용 린터다.
Steiger라는 이름은 독일어에서 유래했다. 광산에서 작업 질서와 안전을 관리하는 감독관을 의미하는 단어로, “구조를 감시하고 단계가 무너지지 않게 관리한다”는 역할을 그대로 담고 있다. Steiger는 코드를 예쁘게 만드는 도구가 아니라, 프로젝트의 구조적 질서를 지키는 도구다. FSD가 이상적인 설계라면, Steiger는 그 설계를 현실에서 유지 가능하게 만들어주는 장치라고 볼 수 있다.
Steiger는 무엇을 검사하는가
물론 Steiger를 도입한다고 해서 프로젝트 구조가 자동으로 좋아지지는 않는다. 다만 구조가 나빠지는 방향으로 무의식적으로 흘러가는 것을 확실하게 막아준다. 특히 신규 코드나 신규 팀원이 추가될 때, 암묵적인 규칙을 문서 대신 도구로 전달할 수 있다는 점이 크다. 리뷰어의 기억력에 의존하던 구조 규칙이 CI 단계에서 명시적인 에러로 드러난다.
Steiger를 이해할 때 가장 먼저 분명히 해야 할 점은, 이 도구가 무엇을 보지 않는가이다. Steiger는 함수 구현이나 비즈니스 로직의 옳고 그름, 코드 스타일에는 관심이 없다. 오직 “이 코드가 이 위치에 존재해도 되는가”, 그리고 “이 import가 이 방향으로 향해도 되는가”만을 본다. 이는 매우 의도적인 설계다. FSD에서 문제가 되는 지점은 대부분 로직이 아니라 구조이며, Steiger는 그 구조적 붕괴를 코드 리뷰 이전 단계에서 차단하는 안전장치에 가깝다.
프로젝트 도입
Steiger의 도입은 기술적으로는 단순하지만, 태도 측면에서는 신중해야 한다. 설치 자체는 일반적인 개발 도구와 크게 다르지 않다. Steiger 본체와 FSD 플러그인을 devDependency로 추가한 뒤, CLI로 실행하면 된다.
npm install -D steiger @feature-sliced/steiger-plugin
npx steiger ./src이때 중요한 점은, 설정 파일 없이 한 번 실행해보는 것이다. 최초 실행 결과는 “지금 당장 고쳐야 할 목록”이 아니라, 현재 프로젝트가 FSD 규칙과 얼마나 어긋나 있는지를 보여주는 진단 리포트에 가깝다. 기존 프로젝트라면 경고나 에러가 다수 출력되는 것이 오히려 정상이다.
실제 도입 단계에서는 설정 파일이 핵심이 된다. Steiger는 zero-config로도 동작하지만, 실무에서는 거의 항상 steiger.config.ts를 작성하게 된다. 이는 규칙을 강화하기 위함이 아니라, 현실적인 도입을 가능하게 하기 위함이다. 가장 기본적인 설정은 FSD에서 권장하는 규칙 세트를 그대로 불러오는 것이다.
import { defineConfig } from 'steiger'
import fsd from '@feature-sliced/steiger-plugin'
export default defineConfig([
...fsd.configs.recommended,
])이 설정만으로도 Steiger는 레이어 간 잘못된 의존, public API 우회, 구조적으로 문제가 되는 import를 감지한다. 다만 이 상태를 그대로 CI에 올리는 것은 대부분의 기존 프로젝트에서 무리가 된다. 그래서 Steiger 도입의 핵심은 “전면 적용”이 아니라 점진적 도입이다.
점진적 도입의 기본 전략은 레이어별로 규칙 강도를 조절하는 것이다. 예를 들어 shared 레이어는 초기에 규칙을 느슨하게 가져가는 경우가 많다. shared는 재사용 코드가 빠르게 쌓이는 공간인 동시에, 구조 정리가 가장 늦게 이루어지는 영역이기 때문이다.
import fsd from '@feature-sliced/steiger-plugin';
import { defineConfig } from 'steiger';
export default defineConfig([
...fsd.configs.recommended,
{
files: ['./src/shared/**'],
rules: {
'fsd/public-api': 'off',
'fsd/no-public-api-sidestep': 'off',
},
},
]);이 방식은 구조를 포기하는 것이 아니라, 지금은 어디까지를 구조로 관리할 것인지 명시적으로 선택하는 것에 가깝다. Steiger의 장점은 이러한 타협을 설정 파일로 분명하게 남길 수 있다는 데 있다. 사람의 암묵적인 합의가 아니라, 코드로 남는 합의다.
결과적으로 Steiger의 도입은 “구조를 한 번에 정리하는 작업”이 아니다. 기존 코드는 그대로 두되, 신규 코드부터 규칙 안에서 작성되도록 유도하는 장치다. 시간이 지나면서 코드베이스의 평균적인 구조 품질이 서서히 올라가고, 구조가 무너지는 속도는 눈에 띄게 느려진다. 이 정도만 달성해도 Steiger를 도입한 목적은 충분히 달성한 셈이다.
Steiger Rules
Steiger의 규칙들은 일반적인 린터 룰과 성격이 다르다. 어떤 코드를 써야 하는지를 지시하지 않고, 어떤 구조적 선택이 허용되는지와 허용되지 않는지를 명확히 구분한다. 다시 말해, Steiger의 룰은 코드 품질을 높이기 위한 권장사항이 아니라, 아키텍처의 경계를 정의하는 제약 조건에 가깝다.
가장 핵심적인 규칙은 레이어 간 의존 방향을 제한하는 룰들이다. 상위 레이어가 하위 레이어에 의존하는 것은 허용되지만, 그 반대는 명확히 금지된다. 이 규칙은 FSD의 근간을 이루는 원칙으로, 구조가 무너지는 대부분의 원인을 사전에 차단한다. Steiger는 import 경로를 기준으로 이 규칙을 기계적으로 검사하기 때문에, 개발자의 의도와 무관하게 구조 위반을 정확히 드러낸다.
또 하나 중요한 범주는 public API와 관련된 룰이다. FSD에서 슬라이스나 세그먼트는 외부에 노출하는 진입점을 명확히 가져야 하며, 내부 구현은 감춰져야 한다. Steiger는 public API 파일의 존재 여부를 검사하고, 이를 우회해 내부 파일을 직접 import하는 패턴을 에러로 처리한다. 이 규칙은 단기적으로는 번거로울 수 있지만, 장기적으로는 구조적 결합을 눈에 띄게 줄여준다.

문서를 보다 보면 [disabled]로 표시된 룰들을 발견하게 된다. 이는 해당 룰이 폐기되었거나 비추천이라는 의미가 아니다. 기본 preset에는 포함되지 않았지만, 의도적으로 선택해서 사용할 수 있는 강한 제약이라는 뜻이다. 같은 레이어 내 슬라이스 간 import를 금지하는 룰처럼, 구조적 순도를 크게 높이는 대신 실무 비용이 큰 규칙들이 여기에 속한다. Steiger는 이런 선택을 사용자에게 맡기고, 기본값으로 강제하지 않는다.
이 점에서 Steiger의 룰 설계는 매우 현실적이다. 모든 프로젝트에 동일한 정답을 강요하지 않고, 어떤 규칙이 어떤 비용을 가지는지 명확히 드러낸다. 중요한 것은 “모든 룰을 켜는 것”이 아니라, 팀이 합의한 구조를 어떤 규칙으로 지킬 것인지 결정하는 것이다. Steiger의 룰은 그 결정을 코드로 고정시키는 수단이며, 그 자체가 아키텍처에 대한 명시적인 선언이 된다.
빌드 과정과 연결
Steiger를 언제 실행할 것인가는 단순한 설정 문제가 아니라, 이 도구를 어떤 성격으로 사용할 것인가에 대한 선택이다. 로컬에서만 돌리는 보조 도구로 둘 수도 있고, 빌드 과정의 일부로 편입시켜 강제력을 부여할 수도 있다. FSD의 규칙을 “권장 사항”이 아니라 “깨지면 안 되는 계약”으로 취급한다면, 후자가 훨씬 자연스럽다.
가장 일반적인 방식은 Steiger를 별도의 스크립트로 분리한 뒤, 빌드 이전 단계에서 실행하는 것이다. 예를 들어 다음과 같이 스크립트를 정의할 수 있다.
{
"scripts": {
"lint:fsd": "steiger ./src",
"build": "npm run lint:fsd && next build"
}
}이렇게 하면 구조 규칙을 위반한 상태에서는 빌드 자체가 진행되지 않는다. 타입 에러나 테스트 실패와 마찬가지로, 구조 위반 역시 배포를 막는 명확한 실패 조건이 된다. 이 지점에서 Steiger는 단순한 분석 도구가 아니라, 빌드 안정성을 구성하는 한 요소가 된다.
CI 환경에서는 이 효과가 더 분명해진다. 로컬에서는 실수로 넘어갈 수 있는 구조 위반이, CI 단계에서는 반드시 드러난다. 특히 신규 팀원이 추가되거나, 기존 구조를 잘 모르는 상태에서 기능을 수정하는 경우에 효과가 크다. 구조 규칙은 더 이상 문서나 암묵적인 지식이 아니라, 파이프라인에 내장된 자동 검증 로직이 된다.
다만 여기서도 점진적 도입의 원칙은 유지되어야 한다. 기존 프로젝트라면 처음부터 모든 위반을 빌드 실패로 처리하기보다는, 핵심 룰만 활성화한 상태로 연결하는 것이 현실적이다. 예를 들어 레이어 간 의존이나 public API 관련 룰만 빌드에 연결하고, 나머지는 경고 수집 용도로 남겨둘 수 있다. Steiger는 이런 선택을 설정 파일 단위로 명확히 분리할 수 있게 설계되어 있다.
결과적으로 Steiger를 빌드 과정에 연결한다는 것은, “우리 프로젝트에서 구조는 선택 사항이 아니다”라는 선언에 가깝다. 코드는 고칠 수 있지만, 무너진 구조는 되돌리기 어렵다. Steiger를 빌드 파이프라인에 포함시키는 순간, 구조는 더 이상 리뷰의 영역이 아니라 시스템이 자동으로 보장하는 계약이 된다.