협업하는 과정에서 가장 효율적인 파일구조는 무엇일까?
이번 게시물에서는 내가 주로 쓰는 Nextjs
의 파일구조에 대해서 설명하도록 하겠다.
📂 src
├── 📂 apis
│ ├──📂 api (서버와의 통신을 관리하는 API 관련 함수).ts
│ └──📂 service (모델 별 사용하는 서비스).ts
│ └──📂 domain (컴포넌트에서 직접 사용하는 함수).tsx
├── 📂 component.tsx (컴포넌트)
│ ├── 📂 UI (공통으로 사용되는 컴포넌트)
│ └── 📂 domain (도메인별 컴포넌트)
├── 📂 hooks (컴포넌트가 직접 사용하는 함수).tsx
├── 📂 pages (도메인 별 페이지).tsx
├── 📂 utils (라이브러리 관련 함수).ts
└── 📂 constants (상수)
├── 📂 business (비즈니스적으로 사용되는 상수)
└── 📂 develop (개발에 사용되는 상수)
├── 📂 component
├── 📂 UI
└── 📂 page
위와 같은 구조가 나온 이유는 추상화 기준 잡기와 관련이 있다.
공통적으로 묶을 수 있는 부분은 추상화 작업을 하고 네이밍을 가진 컴포넌트를 만들기로 했기 때문이다.
그래서 나는 위와같은 기준을 고려해 폴더 구조를 잡았다.
└── 📂 constants (상수)
├── 📂 business (비즈니스적으로 사용되는 상수)
└── 📂 develop (개발에 사용되는 상수)
서비스적인 상수 요소들이 담겨있다.
export const HELPER_TEXT_KOREAN_INITIAL = '한글 초성은 사용할 수 없습니다.';
export const MAX_TEXTAREA_LENGTH = 2000;
export const HELPER_TEXT_12_LENGTH = '12자리 이내여야합니다.';
개발과 관련된 상수 요소들이 담겨있다.
export const NEXT_PUBLIC_KAKAO_JS_KEY = process.env.NEXT_PUBLIC_KAKAO_JS_KEY;
export const NEXT_PUBLIC_KAKAO_URI = `${NEXT_PUBLIC_DOMAIN_HOST}/sign-in/kakao/callback`;
컴포넌트단계에서 사용할 커스텀 훅들이 담겨있다.
└── 📂 hooks
├── 📂 useDebounce.tsx
└── 📂 useInfiniteScroll.tsx
└── 📂 useEffectAfterMount.tsx
Next.js
는 페이지 라우팅을 지원한다. 예를들어 page/main
파일이 있다면 localhost:3000/main
에 가면 해당 파일의 내용들이 렌더링된다.
디자인 시스템에 관한 파일들이 존재한다.
└── 📂 styles
├── 📂 colors
└── 📂 font
└── 📂 theme.ts
라이브러리를 돕는 함수들이 담겨있다.
└── 📂 utils
├── 📂 recoil
└── 📂 react-cookie
└── 📂 react-native-webview
page는 api를 실행하기 위해 domain를 사용한다.
const [snsLogin] = useLogin();
const page = () => {
const loginSuccess = await snsLogin(callback[0]);
...
}
domain는 api의 결과를 얻고 recoil | react-native 등 domain의 결과에 따른 동작을 한다.
react ↔ service의 다리 역할을 위해 tsx형식을 사용한다.
import { authService } from '@/apis/_service';
import { sendReactNativeMessage } from '@/utils/reactNativeMessage';
import {
NEXT_PUBLIC_APPLE_URI,
NEXT_PUBLIC_NAVER_URI,
} from '@/constants/develop.constants';
import { useRouter } from 'next/router';
import { QueryParams } from '@/pages/sign-in/[...callback]';
import { useState } from 'react';
import { kakaoLogin } from '@/apis/_api/auth';
export const SignInApi = () => {
const router = useRouter();
const [error, setError] = useState<any>();
// 로그인 하는 플랫폼에 따라 redirect 해주는 API
const snsLogin = (platform: string) => {
switch (platform) {
case 'KAKAO': {
kakaoLogin();
break;
}
case 'NAVER': {
router.push(NEXT_PUBLIC_NAVER_URI);
break;
}
case 'APPLE': {
router.push(NEXT_PUBLIC_APPLE_URI);
break;
}
}
};
return [snsLogin] as const;
};
service는 하나의 기능을 담당한다.
기능은 여러 api나 여러 상호작용을 통해서 만들어진다.
export const login = async (platform: string, code: string) => {
const data = await authApi.login(platform, code);
return data;
};
api는 직접 서버와 통신하는 동작을 한다.
export const login = async (platform: string, code: string) => {
const payload = {
redirectUri: `${NEXT_PUBLIC_DOMAIN_HOST}/sign-in/${platform}/callback`,
oauthProvider: platform.toUpperCase(),
authorizationCode: code,
};
const { data } = await fetcher.post('api/v1/user/login', payload);
return data;
};
export const kakaoLogin = async () => {
window.Kakao.Auth.authorize({
redirectUri: `${NEXT_PUBLIC_DOMAIN_HOST}/callback`,
});
};
파일 구조를 잡는 방법은 사람마다 다른것 같다!
특히 비즈니스 로직
쪽이 그렇다고 생각하는데 이번 1차배포과정에서 불편함을 느껴
2차 배포때는 새로운 방향으로 리팩토링 해보려고 한다!