코딩 컨벤션
dictionary-app이 실제로 지키는 코딩 컨벤션이다. Prettier/oxlint가 자동으로 강제하는 규칙과, 코드
작성·리뷰 시 직접 신경 써야 하는 규칙을 구분한다.
Prettier (자동 강제)
Section titled “Prettier (자동 강제)”module.exports = { bracketSpacing: false, singleQuote: true, trailingComma: 'all', arrowParens: 'always', semi: false, printWidth: 90, tabWidth: 2, useTabs: false, endOfLine: 'lf',}세미콜론 없음, 싱글쿼트, trailing comma, printWidth 90 — pre-commit 훅의 lint-staged가 스테이징된
파일에 자동 적용한다(자세한 내용은 깃훅 가이드 참고).
oxlint (자동 강제)
Section titled “oxlint (자동 강제)”{ "rules": { "no-debugger": "error", "no-var": "error", "eqeqeq": "error", "curly": ["error", "all"], "dot-notation": "error", "react/rules-of-hooks": "error", "react/exhaustive-deps": "error", "react/only-export-components": "warn", "typescript/no-unused-expressions": ["error", {"allowShortCircuit": true}] }}no-var/eqeqeq/curly/dot-notation처럼 흔한 JS 실수를 막는 규칙과,react/rules-of-hooks/react/exhaustive-deps처럼 훅 오용을 막는 React 전용 규칙을 함께 쓴다.pnpm lint로 직접 실행하거나, pre-commit 훅의lint-staged(oxlint --fix)가 스테이징된.ts/.tsx파일에 자동 적용한다.
컴포넌트/함수 작성 규칙 (도구가 강제하지 않음, 직접 신경 써야 함)
Section titled “컴포넌트/함수 작성 규칙 (도구가 강제하지 않음, 직접 신경 써야 함)”function대신 화살표 연산자를 사용한다.- 이름 붙여 정의하는 함수(
export const/const)는 한 줄로 표현 가능해도 암묵적 반환(=> expr) 대신 중괄호 +return을 쓴다.나중에 조건 분기나 로그 한 줄이 추가돼도 화살표 스타일을 바꿀 필요가 없다. 배열 메소드 등에 즉석으로 넘기는 짧은 인라인 콜백(// ❌export const isNull = <T,>(value: T) => value === null// ✅export const isNull = <T,>(value: T) => {return value === null}.map((i) => i))에는 적용하지 않는다 — 이름 붙여 정의하는 함수에 한정된 규칙이다. use client구문은 사용하지 않는다.- 모델을 알 수 있거나 제네릭을 쓸 수 있는 경우 제네릭을 적극 활용한다.
- 컴포넌트 이름은 파스칼 케이스, 확장자는
.tsx(HTML을 반환하지 않으면.ts허용)로 하고, 파일명은 컴포넌트 이름과 동일하게 짓는다. export default를 쓰지 않는다 — 컴포넌트/함수 정의 시 이름 왼쪽에export를 붙여, 임포트할 때 뭉쳐서 쓸 수 있게 한다.- 프롭으로 주입하는 이벤트 핸들러 이름은
handle접두사를 쓴다 —onClick이 아니라handleClick. - 이벤트 핸들러는 JSX에 인라인 화살표 함수(
onClick={() => ...})로 쓰지 않고, 컴포넌트 함수 바디에handleClick같은 이름의 함수로 분리한 뒤 참조한다. 리스트 렌더링처럼 인자를 즉시 바인딩해야 하는 경우에도 로직 자체는 이름 붙은 핸들러에 두고, JSX에서는onClick={() => handleSelect(item.id)}처럼 인자만 넘기는 최소한의 래핑만 허용한다.
조건절·반복문은 한 줄이어도 중괄호로 감싼다
Section titled “조건절·반복문은 한 줄이어도 중괄호로 감싼다”if/else/for/while은 본문이 한 줄이어도 항상 중괄호({})로 감싼다.// ❌if (x > 0) return x// ✅if (x > 0) {return x}- oxlint
curly규칙(["error", "all"])이 강제하고, pre-commit 훅의oxlint --fix가 자동으로 고친다. - 조건이 나중에 추가되거나 로그 한 줄이 붙어도 중괄호를 새로 씌울 필요가 없다. 코드베이스 전체에서 조건절 모양도 하나로 통일된다.
타입(모델) 작성 규칙
Section titled “타입(모델) 작성 규칙”interface대신type을 쓴다.pnpm gen:api-types로 생성하는src/types/schema.ts는 생성 결과를 그대로 두므로 이 규칙의 예외이다.- 파일 하나에 서로 관련된 타입을 여러 개 정의할 수 있다. 파일명은 다음 기준으로 정한다.
- 타입 하나만 정의하는 파일: 타입 이름과 같은 파스칼 케이스(예:
ColumnNameResult.ts) - 관련 타입 여러 개를 주제로 묶는 파일: 소문자 단일 명사(예:
date.ts가DatePattern·DatePatternName을 export)
- 타입 하나만 정의하는 파일: 타입 이름과 같은 파스칼 케이스(예:
- 서로 무관한 타입을 한 파일에 섞지 않는다. 한 주제로 묶이지 않으면 파일을 나눈다.
- 이 규칙은 최상위
src/types와 페이지 전용models디렉토리에 똑같이 적용된다.src/types/date.ts export type DatePattern = {dateFormat: stringregExpPattern: RegExpdayjsFormat: string}
utils 파일 이름 규칙
Section titled “utils 파일 이름 규칙”src/utils/의 파일은 두 성격 중 하나를 따른다.- topic 파일: 관련된 헬퍼 함수 여러 개를 주제 단위로 묶는다.
파일명은 소문자 단일 명사로 짓는다(예:
string.ts가toCamelCase·formatNumber등을 export). - singleton 파일: 값이나 인스턴스 하나만 export한다.
파일명은 export 이름과 같은 camelCase로 짓는다(예:
queryClient.ts가queryClient를 export). 이런 파일에는 관련 없는 헬퍼 함수를 함께 두지 않는다. - 페이지 하위 컴포넌트끼리 공유하는 헬퍼 함수의 위치는 프로젝트 디렉토리 구성 참고.
스타일 작성 규칙
Section titled “스타일 작성 규칙”- 스타일은 별도 CSS 파일,
styled-components,sxprop, 인라인style대신 Chakra UI v3의chakra()팩토리로 작성한다. - 스타일은 컴포넌트와 같은 디렉토리의
Styled{컴포넌트 이름}.ts파일 하나에 모은다. 한 컴포넌트가 쓰는 styled 요소는 여러 개여도 파일을 쪼개지 않는다. chakra()로 만든 태그의 이름은Styled{태그 역할}형식으로 짓는다(예:StyledCard,StyledHeading).{태그 역할}은 그 요소가 화면에서 맡는 역할이다.- 감싸는 대상에 따라 두 패턴 중 하나를 쓴다.
'div'같은 순수 HTML 태그나motion.div처럼 스타일 단축 prop을 받지 못하는 대상은 2번째 인자(recipe)의base에 스타일을 정의한다.Heading/Text/Flex처럼 이미 Chakra 컴포넌트인 대상은 recipe를 빈 객체({})로 두고, 3번째 인자의defaultProps로 자주 반복되는 prop 조합만 기본값으로 지정한다.
pages/word/column/StyledColumn.ts export const StyledCard = chakra('div', {base: {bg: 'bg',borderWidth: '1px',borderColor: 'border',borderRadius: 'lg',boxShadow: 'xs',p: 5,},})export const StyledHeading = chakra(Heading,{},{defaultProps: {size: 'lg', color: 'brand.fg', mb: 4}},) chakra()호출 결과를 컴포넌트 이름으로 그대로 export하지 않는다.ComponentName.tsx는 항상 JSX를 반환하는 실제 컴포넌트여야 하고, 스타일 태그는Styled*.ts에 두고 렌더링만 한다.layout/footer/Footer.tsx import {StyledBar, StyledCopyright} from './StyledFooter'export const Footer = () => {return (<StyledBar><StyledCopyright>© {new Date().getFullYear()} mintchoco.biz</StyledCopyright></StyledBar>)}- 색상은 시맨틱 토큰(
fg,fg.muted,bg,border등)을 기본으로 쓴다. 로그인 화면처럼 라이트/다크 테마와 무관하게 같은 톤을 유지해야 하는 화면에만brand.700,red.600같은 고정 컬러 토큰을 쓴다.
용어 통일 — columnName
Section titled “용어 통일 — columnName”- 단축어들을 언더바(
_)로 이어 붙이고 도메인을 결합한 최종 문자열은 코드에서columnName으로 부른다. 과정이 아니라 실제 DB 컬럼명 그 자체를 뜻하는 값이기 때문이다. - 이 값을 만드는 함수는
generateColumnName(또는buildColumnName)으로 짓는다. - 토큰 목록·도메인 접미사·최종 컬럼명을 함께 담는 타입은
ColumnNameResult(또는GeneratedColumnName)로 짓는다. 현재 코드는pages/word/column/models/ColumnNameResult.ts에 있다.
.env/.env.*파일은 AI가 읽거나 고치지 못하도록.claude/settings.json의deny규칙으로 막혀 있다.dictionary-api(백엔드) 소스는 API 스펙 확인 같은 참조용으로만 쓴다. 백엔드 코드 수정이 필요하면dictionary-api저장소에서 따로 진행한다.- 다른 개발 규칙은 해당 문서를 따른다.
- pnpm 사용, 자주 쓰는 명령,
gen:api-types타입 동기화: 프런트엔드 개발 환경 - pre-commit 훅과
--no-verify금지, 커밋 메시지 형식: 깃훅 가이드 master직접 커밋 금지,--no-ff병합: 브랜치 전략, Git 스킬
- pnpm 사용, 자주 쓰는 명령,