ChamomileGuides 3.0.4 Help

파일 & 코드 컨벤션

2.1 Folder & File Convention

  • 현재 프로젝트는 Next.js 13.5.8에서 App Router를 사용하는 것을 기준으로 폴더가 구성되어 있습니다.

  • src 폴더는 선택 사항입니다. 현재 프로젝트에서는 src 폴더를 사용하고 있으며, 애플리케이션 코드를 분리할 수 있기때문에, 사용하는 것을 권장합니다.

  • 관련 문서: src Directory

Routing

  • Next.js에서 App Router를 사용할 때 폴더명은 라우팅에 영향을 미치기 때문에, 알아두어야 할 사항들이 있습니다.

    1. 일반 라우트: folder - 일반적인 라우팅 세그먼트

    2. 동적 라우트: [folder] - 동적 라우트 세그먼트. folder에 slug 변수값 대입

    3. 라우트 그룹: (folder) - 라우팅에 영향을 주지 않고 그룹화

    4. 비공개 폴더: _folder - 라우팅에서 폴더 및 모든 자식 세그먼트 제외

    5. 병렬/가로채기 라우트: [...folder], [[...folder]], @folder, (.)folder, (..)folder, (..)(..)folder, (...)folder

  • 관련 문서: Project Structure

Folder Naming Convention

  • kebab-case (각 단어를 소문자로 쓰고 대시로 구분하는 규칙)로 작성하는 것을 권장합니다.

  • Next.js에서는 폴더명에 따라 라우팅이 되고, 폴더명이 곧 URL 세그먼트가 됩니다. URL에서는 kebab-case 사용이 권장됩니다.

  • Next.js에서 공식적으로 kebab-case를 강제한 것은 아니지만, 여러 공식 문서나 예제 코드에서 kebab-case를 사용하는 것을 확인할 수 있습니다.

File Naming Convention

  • 페이지 파일: page.tsx - 각 폴더의 루트에 위치하며, 해당 폴더가 라우트의 엔드포인트가 됩니다.

  • 레이아웃 파일: layout.tsx - 해당 폴더와 하위 폴더의 공통 레이아웃을 정의합니다.

  • 템플릿 파일: template.tsx - 레이아웃과 유사하지만, 각 라우트마다 새로운 인스턴스를 생성합니다.

  • 로딩 파일: loading.tsx - 해당 라우트의 로딩 상태를 정의합니다. 현재 프로젝트에서는, 루트 레이아웃과 같은 레벨에 1개만 두어 관리합니다.

  • 에러 파일: error.tsx - 해당 라우트의 에러 상태를 정의합니다. 현재 프로젝트에서는, 루트 레이아웃과 같은 레벨에 1개만 두어 관리합니다.

  • 기타 컴포넌트 파일: component-name.tsx - 일반 컴포넌트 파일명은 kebab-case로 작성합니다.

2.2 Code Convention

Convention

  • 모든 팀원들이 프로젝트 시작 전에 컨벤션을 협의하고 숙지한 후 개발을 진행합니다.

  • ESLint와 Prettier 설정을 통일한 후 개발을 시작합니다.

Code Style

  • 이름을 정의할 때는 약어 사용을 자제하며, 변수명만 보고 의미를 알 수 있도록 단어 그대로 사용하는 것을 권장합니다.

const cntMng = 1; // (X) const countManager = 1; // (O)
  • 들여쓰기 : 들여쓰기는 Tab 문자가 아닌 공백(Space) 문자를 사용하며 공백 2칸을 기본값으로 사용합니다.

  • 세미콜론 : 세미콜론을 사용하여 문장을 끝냅니다.

  • 문자열 : 문자열에는 일관되게 싱글쿼트(')를 사용합니다.

  • 문자열 연결 : 문자열 연결을 할때는 가독성을 위해 template strings를 사용합니다.

const sayHiBad = 'How are you, ' + name + '?'; // (X) const sayHiGood = `How are you, ${name}?`; // (O)
  • 한 줄 길이 제한 : 한 줄의 최대 길이를 100자로 제한합니다.

  • 복수형 명명 : 복수형에는 's' 대신 'List'를 사용합니다.-

const menus = []; // (X) const menuList = []; // (O)
  • 비교 연산자 : ==와 != 대신 ===와 !==를 사용합니다.

  • 끝 쉼표 : 끝 쉼표를 추가해줍니다. 끝 쉼표가 없으면 git diffs에 불필요한 변경사항이 저장될 수 있습니다.

// bad - 마지막에 쉼표가 없는 경우 git diff const hero = { firstName: 'Florence', - lastName: 'Nightingale' + lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'] }; // good - 마지막에 쉼표가 있는 경우 git diff const hero = { firstName: 'Florence', lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'], };

Components

  • 컴포넌트 파일은 해당 컴포넌트를 사용하는 파일들이 위치한 가장 가까운 상위 폴더 아래에 있는 _components 폴더에 생성합니다.

  • 가능한 한 컴포넌트를 분리하여 한 파일당 코드가 너무 길어지지 않도록 합니다.

  • 컴포넌트 분리 기준

    1. 단일 책임 원칙: 각 컴포넌트는 하나의 명확한 역할을 수행해야 합니다.

    2. 재사용 가능성: 비슷한 기능을 하는 부분은 독립된 컴포넌트로 분리하여 재사용성을 높입니다.

    3. 가독성: 코드를 작고 독립적인 컴포넌트로 나누어 가독성을 높입니다.

    4. 상태와 라이프사이클: 컴포넌트가 관리하는 상태와 라이프사이클 메서드를 고려하여 분리합니다.

    5. UI 요소: 서로 다른 UI 요소는 별도의 컴포넌트로 분리합니다.

Variables

  • camelCase로 작성합니다.

    • 예: userAge, userName

  • ECMAScript 사양에 정의된 JavaScript 변수명 명명 규칙을 따릅니다:

    • if, else, for 등 예약어 사용 금지

    • 변수명 첫 글자에 숫자 사용 금지

    • 특수 문자 사용 금지 (단, $_는 예외)

  • 의미 있는 변수명을 사용하여 코드의 가독성을 높입니다.

    • 예: data 대신 userData, response 대신 apiResponse

  • useState[변수명, set + 변수명] 형태로 명명합니다.

    • 예: [count, setCount], [isLoggedIn, setIsLoggedIn]

  • boolean 변수는 is, has, can 등의 접두사를 사용하여 명명합니다.

const userAge = 30; const [userName, setUserName] = useState<string>(''); const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);

Functions

  • camelCase로 작성합니다.

  • 일반적으로 함수는 여러 가지 행위를 나타내는 것이기 때문에 첫 단어는 동사로 시작합니다.

    • 예: getUser, setUserName

  • 동사만으로 의미 전달이 불명확한 경우에는 동사 + 명사 형태로 표기합니다.

    • 예: fetchUserData, updateProfilePicture

  • 함수 선언문, 함수 표현식보다 화살표 함수를 권장합니다. 호이스팅을 방지하고, 일관된 스코프 관리를 제공하며, 코드의 예측 가능성과 가독성을 높일 수 있습니다.

    • 예: const fetchData = () => { ... };

// 함수 선언문 (비권장) function fetchData() { return { name: "홍길동", age: 30 }; } // (X) // 함수 표현식 (비권장) const fetchData = function() { return { name: "홍길동", age: 30 }; }; // (X) // 화살표 함수 (권장) const fetchData = () => { return { name: "홍길동", age: 30 }; }; // (O)

Iterators

  • for 문 대신 forEach, map, reduce 등의 고차 함수를 사용하여 코드의 가독성을 높입니다.

const numbers = [1, 2, 3, 4, 5]; // bad let sum = 0; for (let num of numbers) { sum += num; } sum === 15; // good let sum = 0; numbers.forEach((num) => { sum += num; }); sum === 15; // best (함수형 프로그래밍 사용) const sum = numbers.reduce((total, num) => total + num, 0); sum === 15; // bad const increasedByOne = []; for (let i = 0; i < numbers.length; i++) { increasedByOne.push(numbers[i] + 1); } // good const increasedByOne = []; numbers.forEach((num) => { increasedByOne.push(num + 1); }); // best (함수형 프로그래밍 유지) const increasedByOne = numbers.map((num) => num + 1);

Uppercase Constants

  • 상수는 변경되지 않는 값을 의미하므로, const 키워드를 사용하여 선언합니다.

  • SNAKE_CASE 대문자와 밑줄(_ )을 사용하여 명명합니다.

  • 상수를 사용하는 경우는 다음과 같습니다:

    1. 실행하기 이전에 값을 알 수 있는 경우: 예를 들어, 색상 코드와 같은 값은 미리 정의할 수 있습니다.

    2. 기억하기 힘든 값을 변수에 할당해 별칭으로 사용하는 경우: 복잡하거나 긴 값을 간단한 변수명으로 대체하여 사용합니다.

    3. 하드코딩 값: 코드 내에서 변경되지 않는 고정된 값을 상수로 정의합니다.

    4. 환경 변수: 애플리케이션의 환경 설정 값을 상수로 정의하여 사용합니다.

const COLOR_BLACK = '#000000'; const MAX_USERS = 100; const API_URL = 'https://api.example.com'; NEXT_PUBLIC_API_URL_BASE=https://api.example.com
  • (주의) 일반적인 데이터 값은 const로 선언되었더라도, 위의 Variable 규칙에 따라 camelCase로 작성합니다.

const userName = 'admin'; const userAge = 30;

Hooks

  • 기본 훅: React의 기본 훅(useState, useEffect 등)은 컴포넌트 상단에 정의합니다.

  • 커스텀 훅: 커스텀 훅은 use로 시작하는 이름을 사용하며, 훅의 목적을 명확히 설명합니다. 예: useGetQuery

Event Handler

  • 이벤트 핸들러 함수는 handle 접두사를 사용하여 명명합니다.

    • 예: handleClick, handleSubmit

  • 이벤트 핸들러 함수는 이벤트 객체를 매개변수로 받아야 합니다.

    • 예: const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { ... };

  • 이벤트 핸들러 함수는 가능한 한 짧고 간결하게 작성하며, 필요한 경우 별도의 함수로 분리합니다.

  • 이벤트 핸들러 함수는 JSX 요소의 속성으로 직접 전달하지 않고, 변수에 할당하여 전달합니다.

// 이벤트 핸들러 예시 const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { console.log('Button clicked'); }; const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); console.log('Form submitted'); }; return ( <div> <button onClick={handleClick}>Click Me</button> <form onSubmit={handleSubmit}> <input type="text" /> <button type="submit">Submit</button> </form> </div> );

Typescript

  • 타입 정의: 모든 함수, 변수, 컴포넌트에 대해 명시적으로 타입을 정의합니다.

  • 무분별한 any 사용을 지양합니다.

  • Type 대신에 Interface 를 사용하여 통일하도록 합니다. Interface는 확장이 가능하고, 객체의 구조를 명확하게 정의할 수 있어 코드의 재사용성과 유지보수성을 높입니다. 예외 : 리터럴 (Literal), 튜플 (Tuple), 유니온 (Union) 등이 꼭 필요시에는 Type 사용합니다.

  • 타입명은 PascalCase로 작성합니다. Interface는 I 접두사를 사용 합니다. 타입명의 일관성과 가독성을 높이기 위함입니다.

interface IFetchData { endpoint: string; }

Props

  • 페이지 컴포넌트의 Props의 Interface를 명명할 때는 다음 규칙을 따릅니다.

  • 컴포넌트 이름 + Props 형태로 작성합니다. Interface를 사용하지만, I 접두사를 미사용합니다.

interface UserInfoProps { userName: string; userId: string; } export function UserInfo({ userName, userId }: UserInfoProps) { return <div>PAGE</div>; }

Comment

  • 소스코드에 대한 이해를 돕기 위해 주석을 작성합니다.

  • vscode의 Comment Anchors Plugin extension을 사용하여 하이라이팅 합니다.

  • 각 주석에 유형(Anchor)을 추가하여 작성합니다.

  • 주석은 // 를 사용하여 작성합니다.

  • 여러 줄 주석이 필요한 경우 /** ... */를 사용합니다.

  • 각 앵커 유형은 대문자로 작성하며, 콜론(:) 뒤에 설명을 추가합니다.

Comment : Anchor Type

  • NOTE : 특정 코드에 대한 주석이나 설명을 추가할 때 사용합니다.

  • TODO : 해야 할 작업을 표시할 때 사용합니다.

  • FIXME : 수정이 필요한 부분을 표시할 때 사용합니다.

  • REVIEW : 코드 리뷰가 필요한 부분을 표시할 때 사용합니다.

  • STUB : 아직 구현되지 않은 부분을 표시할 때 사용합니다.

  • ANCHOR : 일반적인 주석 앵커로, 코드 내에서 중요한 위치를 표시할 때 사용합니다.

  • Comment Anchors Plugin 적용시 주석 하이라이팅 예시

    주석 하이라이팅 이미지

/** * NOTE: 이 파일은 간단한 입력 폼을 포함한 예제 컴포넌트입니다. */ 'use client'; import { useState } from 'react'; export default function SimpleForm() { const [inputValue, setInputValue] = useState<string>(''); /** * NOTE: 입력 값 변경 핸들러 * 입력 필드의 값이 변경될 때 호출됩니다. * @param {React.ChangeEvent<HTMLInputElement>} event - 입력 이벤트 객체 */ //TODO: 입력 값 변경 시 호출되는 핸들러를 구현해야 합니다. const handleChange: ChangeEventHandler<HTMLInputElement> = (event) => { setInputValue(event.target.value); }; /** * NOTE: 폼 제출 핸들러 * 폼이 제출될 때 호출됩니다. * @param {React.ChangeEvent<HTMLInputElement>} event - 입력 이벤트 객체 */ // REVIEW: 폼 제출 로직에 대한 리뷰가 필요합니다. const handleSubmit: FormEventHandler<HTMLFormElement> = (event) => { event.preventDefault(); console.log('Form submitted with value:', inputValue); // FIXME: 폼 제출 후 입력 값을 초기화하는 로직을 추가해야 합니다. }; // STUB: 아직 구현되지 않은 함수입니다. 폼 제출 후 추가 작업을 포함할 예정입니다. const handlePostSubmit = () => { // TODO: handlePostSubmit 함수 구현 }; return ( <div> <form onSubmit={handleSubmit}> <input type="text" value={inputValue} onChange={handleChange} /> <button type="submit">Submit</button> </form> </div> ); }

Comment : Page Comment

  • 파일(화면)에 대한 주석을 작성합니다. jsx, js, tsx, ts 파일 최상단에 필수로 작성합니다.

  • NOTE에 화면설명을 작성합니다.

  • 담당자를 식별하기 위해 author와 since를 작성합니다. (담당자가 모호한 파일에서는 생략)

  • 그 외 description, version, copyright 등이 관례적으로 쓰였으나 사용성이 떨어지므로, 본 프로젝트에서는 생략합니다.

/** * NOTE: 사용자 관리 페이지 * @author 김XX * @since 2024-12-13 */ 'use client'; import { ChangeEventHandler, FormEventHandler, useState } from 'react'; import { useParams, useRouter } from 'next/navigation'; export default function ManagingUserPage() { // 페이지 구현 내용 }
/** * NOTE: 로그인 페이지 컴포넌트 */ 'use client'; import { ChangeEventHandler, FormEventHandler, useState } from 'react'; import { useParams, useRouter } from 'next/navigation'; import { signIn } from 'next-auth/react'; export default function Login() { // 로그인 페이지 구현 내용 }

Comment : Block Comment

  • 블럭 주석은 파일, 함수, 메서드 등에 대한 설명을 제공하며, 함수 선언부 바로 위에 작성됩니다.

  • 함수 안에서 사용하는 경우, 설명하는 코드와 같은 간격의 들여쓰기(Indent)를 적용합니다.

  • param, restuns를 명시합니다.

/** * NOTE: 입력받은 데이터를 엑셀 파일로 다운로드합니다. * @param data 엑셀 파일로 변환할 데이터 배열 * @param fileName 저장할 엑셀 파일의 이름 * @returns 엑셀 파일 다운로드 트리거 */ const downloadExcel = (data: any[], fileName: string): void => { // TODO: 엑셀 파일 생성 및 다운로드 로직 작성 // 예: 엑셀 라이브러리를 사용하여 파일 생성 후 다운로드 트리거 };

TanstackQuery QueryKey

  • QueryKey는 camelCase로 작성합니다.

  • QueryKey는 고유해야 하며, 동일한 데이터를 가져오는 다른 쿼리와 중복되지 않도록 합니다.

  • 명확하고 직관적인 이름을 사용하여 QueryKey의 의미를 쉽게 이해할 수 있도록 합니다.

Semantic Tag

  • Semantic 태그는 HTML5에서 문서의 구조와 의미를 명확히 하기 위해 사용됩니다.

  • 주요 태그로는 header, aside, nav, main, article, section, footer가 있습니다. - 본 프로젝트에서는 header, aside, nav, main이 layout에서 적용되어 있으며, 각 화면(page.tsx) 개발시에는 main 내부 요소인 article, section 태그만 사용하면 됩니다.

2.3 Commit Convention

  • 커밋 메시지를 작성할 때 사용자 간 원활한 소통을 위해 일관된 형식을 사용하도록 합니다.

  • 3가지 영역(제목, 본문, 꼬리말)으로 나누어집니다.

  • 태그: 제목 형태로 : 뒤에만 space를 넣습니다.

type: Subject body footer

Commit Message : Type

  • Commit Message Type은 아래와 같이 분류되며, 소문자로 작성합니다.

  • feat: 새로운 기능 추가

  • fix: 버그 수정

  • docs: 문서 내용 변경

  • style: 포맷팅, 세미콜론 누락, 코드 변경이 없는 경우 등

  • refactor: 코드 리팩토링

  • test: 테스트 코드 작성

  • chore: 빌드 수정, 패키지 매니저 설정, 운영 코드 변경이 없는 경우

  • rename: 파일 혹은 폴더명 수정이나 이동

  • remove: 파일 삭제 작업만 수행

Commit Message : Subject (제목)

  • 최대 50글자가 넘지 않습니다.

  • 마침표와 특수기호는 사용하지 않습니다.

  • 첫글자는 대문자로 표기하며 과거시제를 사용하지 않습니다.

  • 간결하게 요점만 서술합니다.

Commit Message : Body (본문)

  • 한 줄당 72자 이내로 작성합니다.

  • 내용을 최대한 상세히 적습니다.

  • 무엇 진행했는지 설명해야 합니다.

  • 선택사항입니다.

  • 이슈 트래커의 ID를 작성합니다.

  • 어떤 이슈와 관련된 커밋인지(Resolves), 그 외 참고할 사항이 있는지(See also)로 작성하면 좋습니다.

Commit Message Example

feat: 사용자 로그인 기능 구현 JWT를 사용한 사용자 로그인 기능을 구현했습니다. middleware에서 세션 체크를 수행합니다. Resolves: #123
fix: 사용자 인증 미들웨어 오류 수정 인증 미들웨어의 오타로 인해 사용자가 로그인할 수 없는 문제를 수정했습니다. Resolves: #124
docs: README에 프로젝트 설정 방법 추가 프로젝트 설정 방법을 README 파일에 추가했습니다. 새로운 개발자가 쉽게 프로젝트를 설정할 수 있도록 돕습니다. See also: #125
style: layout.tsx 코드 포맷팅 수정 코드 유지보수성을 높이기 위해 layout.tsx의 구조를 변경하였습니다.
refactor: 사용자 데이터 처리 로직 단순화 복잡성을 줄이고 성능을 향상시키기 위해 사용자 데이터 처리 로직을 리팩토링했습니다.
test: 사용자 로그인 페이지에 대한 테스트 추가 사용자 로그인 페이지의 모든 기능이 예상대로 작동하는지 확인하기 위해 테스트를 추가했습니다.
chore: next.config.js 업데이트 프로젝트 설정을 위해 next.config.js 파일을 업데이트했습니다. 새로운 환경 변수와 설정을 추가했습니다.
rename: _components 폴더 내 파일명 수정 프로젝트 구조를 더 잘 조직하기 위해 components 폴더 내 파일명을 수정했습니다. 예: Header.js를 header.js로 변경
remove: 불필요한 이미지 파일 삭제 최근 디자인 업데이트 후 더 이상 필요하지 않은 오래된 이미지 파일을 삭제했습니다.
Last modified: 21 4월 2025