사이드 프로젝트 개발 과정 - (파일구조 잡기)

knh6269·2024년 5월 21일
1

ootd.zip

목록 보기
5/16
post-thumbnail

도입

협업하는 과정에서 가장 효율적인 파일구조는 무엇일까?
이번 게시물에서는 내가 주로 쓰는 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  

위와 같은 구조가 나온 이유는 추상화 기준 잡기와 관련이 있다.
공통적으로 묶을 수 있는 부분은 추상화 작업을 하고 네이밍을 가진 컴포넌트를 만들기로 했기 때문이다.

  • UI에는 공통적으로 묶인 추상 컴포넌트,
  • page에는 특정 페이지에서 사용될 컴포넌트

그래서 나는 위와같은 기준을 고려해 폴더 구조를 잡았다.


상수

└── 📂 constants (상수)  
    ├── 📂 business (비즈니스적으로 사용되는 상수)
  	└── 📂 develop (개발에 사용되는 상수)

busines_constants

서비스적인 상수 요소들이 담겨있다.

export const HELPER_TEXT_KOREAN_INITIAL = '한글 초성은 사용할 수 없습니다.';
export const MAX_TEXTAREA_LENGTH = 2000;
export const HELPER_TEXT_12_LENGTH = '12자리 이내여야합니다.';

develop_constants

개발과 관련된 상수 요소들이 담겨있다.

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

page는 api를 실행하기 위해 domain를 사용한다.

const [snsLogin] = useLogin();
const page = () => {
	const loginSuccess = await snsLogin(callback[0]);
  ...
}

domain

domain는 api의 결과를 얻고 recoil | react-nativedomain의 결과에 따른 동작을 한다.

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

service는 하나의 기능을 담당한다.

기능은 여러 api나 여러 상호작용을 통해서 만들어진다.

export const login = async (platform: string, code: string) => {
  const data = await authApi.login(platform, code);

  return data;
};

api

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차 배포때는 새로운 방향으로 리팩토링 해보려고 한다!

이전글: 사이드 프로젝트 개발 과정 - (추상화 기준 잡기)

0개의 댓글