본문 바로가기
💻 개발/Frontend

React 프로젝트에서 코드 컨벤션

by llddang 2025. 2. 12.

소프트웨어 개발에서 코드 일관성은 프로젝트의 성공을 좌우하는 핵심 요소이다. 특히 여러 개발자가 협업하는 환경에서는 더욱 중요하다.

오늘은 여러 단체/개인 프로젝트를 진행하면서 만들게 된 나만의 코드 컨벤션에 대해 말해보겠다!

 

코드 컨벤션이란?

프로젝트에서 일관되게 따르는 코드 작성 규칙/ 스타일 가이드

 

프로젝트를 시작할 때 가장 먼저하는 일은 기획일 것이다.
그리고 기획 이후 실제 개발 단계로 넘어가면, 코드를 개발하기 전에 코드 컨벤션을 정해야 한다.

명확한 켄변선 없이 프로젝트를 진행하면 코드의 일관성 저하, 코드 리뷰 어려움, 새로운 팀원의 온보딩 시간 증가 등의 문제가 발생할 것이다.

이 글에서는 간단하게 디렉토리 구조부터 디렉토리/파일 네이밍 규칙, 함수 및 컴포넌트의 접두사 및 접미사 규칙 등에 대한 내용을 담고 있다.

 

 

📁 디렉토리 구조

React를 사용하면서 가장 많이 고민한 것이 디렉토리 구조였다.

React는 라이브러리의 특성상 정해진 디렉토리 구조가 없어 개발자마다 다양한 방식으로 구조화할 수 있다..

따라서 사용자 마다 다양하게 구조화하고 이에 따라 여러 디자인 패턴을 배우게 되기도 한다.

 

내가 선호하는 디렉토리 구조는 기능 또는 도메인 단위로 컴포넌트를 관리하는 방식이다. (FSD 아님)

기본 디렉토리 구조

📦 프로젝트 루트
├─ index.html
├─ public/           # 정적 파일 저장 (이미지, 파비콘, 폰트 등)
├─ src/
│  ├─ main.jsx      # 앱 진입점
│  ├─ App.jsx       # 루트 컴포넌트
│  └─ [하위 폴더들]    # 세부 구조는 아래 설명

 

주요 디렉토리 설명

1. app/

프로젝트의 핵심 설정 파일들을 관리한다.

  • CompositeProvider.tsx : 전역에서 사용되는 Provider를 엮은 컴포넌트
  • Router.tsx : 라우팅 설정

 

2. components/

재사용 가능한 컴포넌트들을 관리한다.

components/
├─ common/      # 공통 컴포넌트 (Button, Input, Modal 등)
├─ layout/      # 레이아웃 관련 컴포넌트 (Header, Footer, Sidebar)
└─ features/    # 기능/도메인별 컴포넌트
   └─ home/
      ├─ HomeAnnouncement.tsx  # 홈 페이지 공지사항
      └─ HomeBanner.tsx        # 홈 페이지 배너

components 는 common 과 layout, features 라는 하위 폴더를 가진다.

  • common/: 프로젝트에서 전반적으로 사용되는 컴포넌트 저장
  • layout/: layout에서 사용되는 컴포넌트 저장
  • features/: 기능/도메인 별로 폴더를 구조화하며, 관련된 컴포넌트를 저장

 

3. lib/

프로젝트의 유틸리티 함수, API 호출 함수, 헬퍼 등을 관리한다.

lib/
├─ apis/        # API 관련 함수 (파일명: *.api.ts)
├─ hooks/       # 커스텀 훅
└─ utils/       # 유틸리티 함수 (파일명: *.util.ts)

 

4. pages/

페이지 단위의 컴포넌트를 라우팅 경로에 맞춰 구성한다.

pages/
├─ HomePage.tsx
└─ dashboard/
   └─ DashboardPage.tsx

 

5. 상태 관리 및 타입

├─ contexts/            # Context API 관련 파일
│  └─ auth/
│     ├─ auth.context.ts
│     ├─ auth.reducer.ts
│     └─ AuthProvider.ts
├─ store/              # 전역 상태 관리 (*.slice.ts)
├─ types/              # 타입 정의
│  ├─ dto/
│  │  └─ *.dto.ts
│  └─ *.type.ts
└─ data/              # 상수 데이터 (*.constant.ts)
  • contexts/: contextAPI를 사용하기 위한 파일들 저장
  • stores/: 전역 상태 관리를 위한 slice 및 설정 파일들 저장
  • types/: TypeScript 타입 정의, DTO 인터페이스 등을 저장
  • data/: 프로젝트 전반에 사용되는 상수를 저장

 

더보기
📦 
├─ index.html
├─ public
├─ src
│  ├─ main.jsx
│  ├─ App.jsx
│  ├─ app 
│  │  ├─ CompositeProvider.jsx
│  │  └─ Router.jsx
│  ├─ components
│  │  ├─ layout
│  │  ├─ common
│  │  └─ features
│  │     └─ home
│  │        ├─ HomeAnnoncement.tsx 
│  │        └─ HomeBanner.tsx     
│  ├─ pages
│  │  ├─ dashboard
│  │  │  └─ DashboardPage.tsx
│  │  └─ HomePage.tsx
│  ├─ styles
│  ├─ lib
│  │  ├─ apis
│  │  │  └─ ~~~.api.ts 
│  │  ├─ hooks
│  │  └─ utils
│  │     └─ ~~~.util.ts
│  ├─ store
│  │  └─ ~~~.slice.ts
│  ├─ contexts
│  │  └─ auth
│  │     ├─ auth.context.ts
│  │     ├─ auth.reducer.ts
│  │     └─ AuthProvider.ts
│  ├─ data
│  │  └─ ~~~.constant.ts
│  ├─ types
│  │  ├─ dto
│  │  │  └─ ~~~.dto.ts
│  │  └─ ~~~.type.ts
│  └─ mocks
│     └─ ~~~.data.ts
├─ .env.develop
└─ .env.product

 

 

디렉토리 / 파일 네이밍 규칙

💡 네이밍 종류

  • PascalCase (파스칼 케이스)
  • camelCase (카멜 케이스)
  • kebab-case (케밥 케이스)
  • snake_case (스네이크 케이스)

디렉토리 네이밍

디렉토리 이름은 어느곳이든 상관 없이 kebab-case를 사용한다.

파일 네이밍

  • React Component
    • 리엑트 컴포넌트 파일 명은 PascalCase를 사용한다.
      [HomeAnnoncement.tsx]
      
      export default function HomeAnnoncement({ ... }: HomeAnnoncementProps) {
        const [annoncements, setAnnoncements] = useState<AnnoncementDto[]>([]);
        const router = useRouter();
      
        ...
      }
    • 컴포넌트의 명은 다른 컴포넌트와 겹치지 않도록 관련된 기능/도메인을 접미사로 붙여서 사용한다.
    • 코드를 예시로 Home 에서 사용되는 공지사항 컴포넌트는 Home이라는 접미사를 붙여서 사용한다.
  • React Hook
    • 훅을 포함한 파일의 이름은 ‘use’로 시작하는 camelCase
  • 이외의 파일
    • 이 외의 파일(data, util, dto 등)은 모두 camelCase
    • API 관련: *.api.ts
    • 유틸리티: *.util.ts
    • Slice 파일: *.slice.ts
    • Context: *.context.ts
    • Reducer: *.reducer.ts
    • 상수: *.constant.ts
    • DTO: *.dto.ts
    • 타입: *.type.ts
    • 목업 데이터: *.data.ts

 

API 함수명

  • GET API 의 경우 값을 받는다는 의미로 get 이라는 접미사를 붙여서 사용한다.
    • 예시 : getAnnoncements
  • POST/PATCH/PUT/UPDATE/DELETE API 의 경우 값을 변경하다는 의미로 mutate 라는 접미사를 붙여서 사용한다.
    • 예시 : mutateSignIn (로그인 함수)

 

이벤트 핸들러

  • handle[로직명] - 이벤트 핸들러를 선언할 때 handle이라는 접미사 붙임
    • 예시 - handleAnnoncementClick
  • on[로직명] - 이벤트 핸들러를 매개변수로 받을 때 인자명에 on이라는 접미사 붙임
    • 예시 - onAnnoncementClick
      export default function something() {
        function handleLikeButtonClick() { ... }
      
        return (
          <div>
            <PeedCard onLikeButtonClick={handleLikeButtonClick} />
          </div>
        );
      }

 

 

함수 선언 방식 (Named Function VS Arrow Function)

자바스크립트에는 두 가지의 함수 작성법이 있다.

// Named Function
function helloWorld() {
    console.log('hello world!');
}

// Arrow Function
const helloWorld = () => {
    console.log('hello world!');
}
// or
const helloWorld = () => console.log('hello world!');

Arrow Function 이 Named Function 에 비해 코드를 간략하게 작성할 수 있다. (중괄호 생략 가능)
또한 Named Function은 호출 시점에 this의 바인딩이 결정됩니다. (실행 컽넥스트에 따라 this가 바뀜)

하지만 Arrow Function을 사용할 경우, 상태와 로직의 시작이 동일하여 구분이 어렵다. (둘 다 `const`로 시작)

구분이 어렵다는 것은 가독성이 떨어지고 그렇게 되면 코드의 품질이 떨어질 수 있어서 Named Function을 선호한다!

 

Named Function의 장점

  1. 호이스팅에 따라 함수 선언 부분을 아래로 내릴 수 있음.
    printHello(); // 실행 안됨 참조 에러 뜸. ReferenceError: printHello is not define 
    const printHello = () => console.log("Hello World!");
     printHello(); // 실행 가능! Hello world 출력
     function printHello(){
         console.log("Hello world");
     }
     
  2. 디버깅이 쉬움.
    • Named Function은 함수의 이름이 호출 스택에 표시되어 어떤 함수가 호출되었는지 쉽게 알 수 있음.
    • Arrow Function의 경우 익명 함수로 처리되는 경우가 많아 호출 스택이 명확하지 않을 수 있음.

하지만 callback function 까지 Named Function으로 작성하는 것은 아니다.

callback funcion에서는 오히려 Arrow Function으로 작성하였을 때, 가독성이 높다고 생각한다.

const arr = [1, 2, 3];

const newArr1 = arr.map(function(n){ return n * 2; });
const newArr2 = arr.map((n) => n * 2);

 

 

 

후기

후... 오늘은 내가 선호하는 코드 컨벤션을 한 번 작성해봤다.

혼자서 작업할 때는 아무생각 없이 사용할 수 있지만 다른 사람한테 전달하기 위해 정리하려고 보니 생각보다 시간이 많이 든 것 같다.

다음에는 협업 프로젝트를 할 때는 위 내용을 통해 좀 더 잘 전달 할 수 있을 것 같다.

 

또한 여러 프로젝트를 경험하면서 또 나의 기준이 어떻게 달라질지 기대된다!!!