Next.js 프로젝트, 왜 src 폴더로 구조를 다시 잡았는가

January 22, 2026

3 min read

Next.js App Router를 사용하다 보면 app 폴더 하나에 점점 많은 책임이 쌓이기 시작한다.

페이지와 레이아웃뿐만 아니라 컴포넌트, 훅, 유틸리티까지 함께 위치하게 되면서, app 폴더가 라우팅을 넘어 사실상 거대한 기능 폴더처럼 동작하게 된다.

이처럼 소스 코드가 한곳에 비대해지는 상황에 대비해, 공식 문서에서는 다음과 같은 선택지를 열어두고 있다.

Next.js also supports the common pattern of placing application code under the src folder.

Next.js는 특정 폴더 구조를 강제하기보다는, 프로젝트의 규모와 성격에 따라 관습적인 패턴인 src 구조를 선택할 수 있도록 여지를 남겨두고 있다.

나 역시 이 가이드를 바탕으로 복잡해진 app 폴더의 책임을 분산하기로 했다.


app 폴더에서 src 구조로

아래 이미지는 구조 리팩토링 전후를 비교한 모습이다.

BFF
리팩토링 전: app 폴더가 모든 역할을 맡고 있던 구조
BFF
리팩토링 후: app은 라우팅만, 기능은 src/features로 분리

리팩토링 전 구조를 보면 루트 디렉토리에 너무 많은 폴더가 나열되어 있다. 이 때문에 정작 핵심 역할을 담당하는 app 폴더가 한눈에 들어오지 않았고, 라우팅과 무관한 코드들이 뒤섞여 구조적 경계도 모호했다.

반면 src 구조를 도입한 이후에는 모든 소스 코드가 하나의 명확한 경계 안에 모이게 된다. app은 라우팅 전용으로 역할이 정리되고, 기능과 도메인 로직은 src/features 하위에서 관리된다.

폴더 구조를 새로 잡고 정리하는 데만 꼬박 3일이 걸렸다. 그 과정에서 세운 나름의 기준들을 아래에 기록해 본다.

어떤 기준으로 src 구조를 설계했는가

이번 리팩토링을 하며 가장 경계했던 것은 폴더를 단순히 잘게 쪼개는 것에만 함몰되는 것이었다.

핵심은 폴더의 개수가 아니라, 각 폴더가 어떤 책임을 가지는지 명확히 정의하는 것에 두었다.

1. app 폴더는 라우팅만 담당한다

app 폴더는 URL과 화면을 연결하는 라우팅 엔트리 역할에만 집중하도록 했다. page.tsx, layout.tsx, route.ts 외의 로직은 두지 않는 것을 원칙으로 삼았다

src/app/
 ├─ page.tsx
 ├─ layout.tsx
 └─ (route group)

실제 화면 구현, 상태 관리, 비즈니스 로직은 모두 app 바깥으로 이동시켰다. 이를 통해 app 폴더를 열었을 때 라우팅 구조가 한눈에 들어오도록 했다.

2. 화면과 도메인 로직은 feature 단위로 묶는다

라우트 기준이 아니라 도메인 기준으로 코드를 묶고자 했다. 그래서 모든 화면과 관련 로직을 features 하위로 이동했다.

src/features/student/profile/
 ├─ pages/
 ├─ components/
 ├─ hooks/
 └─ common/

이 구조에서는 특정 화면을 이해하기 위해 여러 폴더를 오갈 필요가 없다. 하나의 feature 폴더 안에서 해당 도메인의 모든 맥락을 파악할 수 있다.

3. app/page.tsx는 얇게 유지한다

app/page.tsx는 구체적인 UI를 구현하지 않고, feature의 Page 컴포넌트를 연결하는 '진입점' 역할만 수행하도록 했다.

import ProfilePage from '@/features/student/profile/pages/ProfilePage';

export default async function Page() {
  return <ProfilePage />;
}

page.tsx는 서버 컴포넌트로 두고, 클라이언트 로직이 필요한 경우 features 하위의 컴포넌트에서 use client를 선언함으로써 경계를 깔끔하게 관리할 수 있다.

프로젝트 규모가 커져도 app 폴더 내부는 늘 파일 몇 개 수준으로 유지된다. 덕분에 언제든 폴더 트리만 보고도 서비스의 전체적인 라우팅 구조를 한눈에 파악할 수 있다.


정리

꼬박 3일이 걸린 이번 리팩토링의 목적은 단순히 src 폴더 하나를 추가하는 데 있지 않았다.

가장 중요했던 것은 코드의 경계를 어떻게 정의하고, 책임을 어디에 둘 것인가에 대한 기준을 세우는 일이었다.

결국 좋은 구조란 단순히 깔끔한 폴더 트리를 만드는 것이 아니라, 코드를 읽는 것만으로도 설계자의 의도가 자연스럽게 드러나는 구조라고 생각한다.

만약 지금 루트 디렉토리가 각종 설정 파일과 소스 코드로 뒤섞여 구조를 파악하기 어려운 상태라면,

더 늦기 전에 src라는 경계를 한 번쯤 고민해볼 필요가 있다.