본문 바로가기
React TIL

[React] Day_72 데일리 정리

by 림졍 2024. 12. 31.

(TIL 리팩토링 필요..)

🚀 NextJS 프로젝트 구조를 효율적으로 조직하는 방법

 

1. Next.js 폴더 구조의 중요성

유지보수성: 코드가 복잡해질수록 체계적인 구조는 유지보수를 쉽게 만듭니다.

협업 효율성: 팀원 간 작업 분배가 명확해지고 코드 탐색이 빨라집니다.

재사용성/확장성: 공통 컴포넌트와 기능별 파일을 분리하여 프로젝트 확장 시 충돌을 방지합니다.

 

2. Next.js 폴더 구조 및 라우팅 규칙

 

app/

├─ home/

  ├─ page.tsx         # /home

  ├─ HomeBanner.tsx   # 라우트 안됨 (UI 컴포넌트)

├─ about/

  ├─ page.tsx         # /about

├─ api/

  ├─ hello/

    └─ route.ts      # /api/hello (API 라우트)

├─ (admin)/

  ├─ dashboard/

    └─ page.tsx      # /dashboard (라우트 그룹)

├─ _components/        # 전역 컴포넌트 (라우트 안됨)

  ├─ Button.tsx

  ├─ Header.tsx

  ├─ Footer.tsx

 

page.tsx: 폴더가 경로가 되며 page.tsx는 해당 라우트로 매핑됩니다.

API 라우트: route.ts를 사용하여 API 엔드포인트 생성 가능.

라우터 그룹: (폴더명)을 사용해 URL에 포함되지 않으면서도 폴더 정리를 할 수 있습니다.

비공개 폴더: _components처럼 _로 시작하면 라우팅에서 제외됩니다.

 

3. 폴더 구조 전략

 

3-1. 단순 구조 (소규모 프로젝트)

 

app/

├─ home/

  ├─ page.tsx

  └─ Banner.tsx

├─ components/

  ├─ Button.tsx

  ├─ Header.tsx

  └─ Footer.tsx

 

components/ 폴더에 모든 컴포넌트 집중.

장점: 빠르게 설정 가능.

단점: 프로젝트가 커지면 파일이 뒤엉킬 수 있음.

 

3-2. 기능별 구조 (대규모 프로젝트)

 

app/

├─ user/

  ├─ page.tsx

  ├─ _components/

    ├─ UserProfile.tsx

    ├─ UserCard.tsx

├─ product/

  ├─ page.tsx

  ├─ ProductDetail.tsx

  ├─ ProductList.tsx

 

특정 기능별로 파일을 분리 (user, product).

각 폴더 안에 필요한 컴포넌트를 _components에 저장.

장점: 기능별로 모든 관련 파일이 한곳에 모임.

단점: 공통 컴포넌트가 중복될 가능성.

 

4. Atomic Design 기반 구조

 

components/

├─ atoms/

  ├─ Button.tsx

  ├─ Input.tsx

├─ molecules/

  ├─ SearchBar.tsx

  ├─ UserCard.tsx

├─ organisms/

  ├─ Header.tsx

  ├─ Footer.tsx

 

Atoms → Molecules → Organisms로 UI를 설계합니다.

장점: 재사용성이 높고 디자인 시스템과의 호환성이 뛰어남.

단점: 초기 설계 난이도가 높음.

 

5. Route Handler에서 Supabase 사용 예시

 

import { createClient } from "@/utils/supabase/server";

 

export async function POST(request: Request) {

  const supabase = await createClient();

  const body = await request.json();

  const { data, error } = await supabase.from("todos").insert(body);

  

  if (error) {

    return new Response(JSON.stringify({ error: error.message }), { status: 400 });

  }

  return new Response(JSON.stringify({ data }), { status: 200 });

}

 

서버에서 Supabase 클라이언트를 생성해 DB 연산을 수행합니다.

 

6. 로그인 및 회원가입 예제

 

"use server";

import { revalidatePath } from 'next/cache';

import { redirect } from 'next/navigation';

import { createClient } from '@/utils/supabase/server';

 

export async function login(formData: FormData) {

  const supabase = await createClient();

  const data = {

    email: formData.get('email') as string,

    password: formData.get('password') as string,

  };

  const { error } = await supabase.auth.signInWithPassword(data);

  if (error) redirect('/error');

  

  revalidatePath('/', 'layout');

  redirect('/');

}

 

서버 액션으로 로그인 구현.

실패 시 에러 페이지로 리다이렉트, 성공 시 메인 페이지로 이동.

 

7. API 트리거 예시 (회원가입 자동 동기화)

 

CREATE OR REPLACE FUNCTION public.handle_new_user()

RETURNS TRIGGER AS $$

BEGIN

  INSERT INTO public.users (id, email)

  VALUES (NEW.id, NEW.email);

  RETURN NEW;

END;

$$ LANGUAGE plpgsql;

 

CREATE TRIGGER on_auth_user_created

AFTER INSERT ON auth.users

FOR EACH ROW

EXECUTE FUNCTION public.handle_new_user();

 

새 사용자가 auth.users 테이블에 추가되면, users 테이블에 자동 삽입됩니다.

 

8. 커스텀 훅으로 데이터 로직 분리

 

기존 (데이터 로직과 UI가 혼재)

 

export function UserProfile({ userId }) {

  const [user, setUser] = useState(null);

 

  useEffect(() => {

    fetch(`/api/user/${userId}`)

      .then(res => res.json())

      .then(setUser);

  }, [userId]);

 

  return <div>{user?.name}</div>;

}

 

문제점: 데이터 패칭 로직과 UI가 강하게 결합됨.

 

개선 (커스텀 훅 사용)

 

// hooks/useUser.ts

import { useEffect, useState } from 'react';

 

export function useUser(userId: string) {

  const [user, setUser] = useState(null);

 

  useEffect(() => {

    fetch(`/api/user/${userId}`)

      .then(res => res.json())

      .then(setUser);

  }, [userId]);

 

  return user;

}

 

// UserProfile.tsx

import { useUser } from '@/hooks/useUser';

 

export default function UserProfile({ userId }) {

  const user = useUser(userId);

 

  return <div>{user?.name}</div>;

}

 

장점:

UI와 데이터 로직이 분리되어 가독성이 높아짐.

커스텀 훅으로 데이터 로직 재사용 가능.

 

9. 확장 가능한 API 서비스 (서비스 레이어)

 

// services/userService.ts

export async function getUser(userId: string) {

  const res = await fetch(`/api/user/${userId}`);

  if (!res.ok) throw new Error('Failed to fetch user');

  return res.json();

}

 

API 호출 로직을 서비스 레이어로 분리해 다른 곳에서도 재사용 가능.

 

10. 서비스 레이어와 커스텀 훅 결합

 

// UserProfile.tsx

import { useEffect, useState } from 'react';

import { getUser } from '@/services/userService';

 

export default function UserProfile({ userId }) {

  const [user, setUser] = useState(null);

 

  useEffect(() => {

    getUser(userId).then(setUser);

  }, [userId]);

 

  return <div>{user?.name}</div>;

}

 

커스텀 훅 없이도 서비스 레이어를 통해 API 호출을 쉽게 확장할 수 있습니다.

 

728x90
반응형