[FSD] 순환 참조를 벗어나기 위한 의존성 설계

박해성·2026년 6월 10일
post-thumbnail

🚀 Situation: 서로 의존하는 레이어


프로젝트에 FSD 아키텍처를 적용하는 과정에서 인증 기능 구조를 재정비하고 있었다.

기존에는 contexts/AuthContext.jsx 파일 내부에 Context 생성과 Provider 로직이 함께 구현되어 있었으며, 인증 관련 기능이 하나의 파일에 집중되어 있는 상태였다.

FSD 적용 과정에서 Provider 역할에 집중하여 AuthProviderapp/store로 이동했고, 인증 기능과 관련된 useAuth 훅은 로그인 기능의 일부라고 판단하여 pages/login/model로 분리하였다. (GitHub)

이 때 AuthProvider 내부에서 useAuth를 사용하며 Provider와 훅이 서로를 참조하는 구조가 만들어졌고, 무한 루프 문제가 발생하였다.


AuthProvider
  ↓
useAuth

useAuth
  ↓
AuthContext

AuthContext
  ↓
AuthProvider

이를 해결하기 위해 useAuth 내부에 있던 상태 관리와 인증 로직을 AuthProvider로 이동시켰고, 이후에는 useAuth 역시 Context 파일 내부에서 함께 export하는 방식으로 통합하였다. (GitHub)

그러나 이 과정에서 인증 기능이 App 레이어에 위치하게 되었고,
Page 레이어에서 이를 직접 참조하는 구조가 형성되었다.

또한 인증 상태는 애플리케이션 전반에서 사용하는 공통 기능이었음에도 App 레이어에 구현 내용이 집중되어 있어, 레이어 역할이 모호해지는 문제가 발생하였다.

결과적으로 인증 기능의 위치와 의존성 방향을 다시 검토할 필요가 있었고, FSD 관점에서 보다 적절한 레이어 구조를 설계해야 하는 상황이 되었다.



🎯 Task: 의존성을 단방향으로


FSD 아키텍처의 규칙에 맞게

  • 공통 Context(Auth, Modal)를 Shared 레이어로 이동
  • Context 생성과 Provider 책임 분리
  • 여러 페이지에서 사용하는 API를 Shared API로 통합
  • 기존 AppPageShared 방향의 의존성을 정리

이와 같이 과제를 정했다.



🛠️ Action: 분리하고 합치고


1. Auth Context 분리

기존 AuthProvider 파일 내부에 있던

  • AuthContext 생성
  • useAuth 훅
  • Provider 로직

중에서 Context 생성과 useAuth 훅을 shared/lib/auth로 이동하였다.

shared/lib/auth
 ├─ AuthContext.js
 └─ index.js

Provider는 이제 상태 관리 책임만 갖게 되었다.

이 때, ModalContext도 함께 정리했는데 내용이 거의 동일해서 생략


2. 공통 API Shared 레이어 통합

기존에

pages/login/api/auth.js
pages/my-page/api/user.js

에 위치하던 API가 여러 영역에서 사용되고 있었다.

이를

shared/api

로 이동하여

import { login, getUserById } from "@/shared/api";

형태로 사용할 수 있도록 통합하였다.


3. 의존성 방향 수정

기존

pages → app/store

형태의 의존성을 제거하고

pages → shared
app → shared

구조로 변경하였다.

예를 들어

import { useAuth } from "@/app/store/AuthContext";

import { useAuth } from "@/shared/lib/auth";

로 변경하여 레이어 의존성을 아래 레이어를 보도록 했다.


Action 상세 내용: (GitHub)



✨ Result: 리팩토링과 디버깅의 끝에서 얻은 것


이번 리팩토링을 통해 인증 기능과 공통 기능의 레이어 위치를 재정비하고, FSD 규칙에 맞는 의존성 구조를 구축할 수 있었다.

Before

app/store/AuthProvider
        ▲
        │
pages/login
  • Page 레이어가 App 레이어를 참조
  • 인증 로직이 App 레이어에 집중
  • Context, Provider, 상태 관리 책임이 혼재
  • 레이어 역할이 모호하고 순환 참조 위험 존재

After

app
 └─ store

pages
 └─ model

shared
 ├─ lib/auth
 ├─ lib/modal
 └─ api
pages → shared
app   → shared
  • 모든 레이어가 아래 방향으로만 의존
  • AuthContext와 ModalContext를 Shared 레이어로 이동
  • Provider는 상태 관리 책임만 담당
  • 공통 API를 Shared API로 통합
  • 인증 기능을 여러 페이지에서 재사용 가능한 구조로 개선

배운 점


특히 이번 경험을 통해 단순히 폴더 위치를 옮기는 것이 아니라, "이 기능이 프로젝트 전체에서 어떤 범위로 사용되는가"를 기준으로 레이어를 결정해야 한다는 점을 배울 수 있었다.

초기에는 인증 기능을 애플리케이션 시작 시 설정되는 기능이라고 생각하여 App 레이어에 배치했지만, 실제로는 여러 페이지와 기능에서 공통적으로 사용하는 자산에 가까웠다.

그 결과 Page 레이어에서 App 레이어를 직접 참조하게 되었고, FSD의
의존성 규칙과 맞지 않는 구조가 만들어졌다.

인증 상태와 모달 관리처럼 여러 페이지에서 사용하는 기능은 App이 아닌 Shared에 위치해야 하며, 이를 통해 FSD가 추구하는 명확한 의존성 방향을 유지할 수 있었다.

이번 리팩토링을 통해 FSD의 핵심은 폴더 구조 자체가 아니라 기능의 책임 범위와 의존성 방향을 설계하는 것이라는 점을 다시 한번 확인할 수 있었다.


profile
박 해성의 (개)발자취

2개의 댓글

comment-user-thumbnail
2026년 6월 10일

오 또하나 공부하고 갑니다!

답글 달기
comment-user-thumbnail
2026년 6월 12일

퍼가요~^^

답글 달기