2026-08-17 06:32:15 +00:00

12 KiB

AGENTS.md

This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.


프로젝트 개요

라온누리 (Raonnuri) - 초등학생 대상 창작 글쓰기 교육 플랫폼

Tech Stack: Next.js 16, React 19, Chakra UI v3, Firebase Auth, TypeScript


개발 명령어

npm run dev        # 개발 서버 (포트 3001, webpack 사용)
npm run build      # 프로덕션 빌드
npm run lint       # ESLint 실행
npx tsc --noEmit   # 타입 체크 (빌드 전 필수)

주의:

  • Dev server는 포트 3001 사용
  • React Compiler 활성화
  • 커밋 전 타입 체크 필수 (npx tsc --noEmit)

핵심 아키텍처

Manager Pattern (필수)

모든 데이터 작업은 Manager 사용 (서비스 직접 호출 금지)

import { teamManager, userManager } from "@/managers";

// ✅ Good
const teams = await teamManager.getMyTeams();
const users = await userManager.getUsersByTeam(teamId);

// ❌ Bad - 서비스 직접 호출 금지

Available Managers:

  • teamManager - Team CRUD, member management, security level
  • userManager - User CRUD
  • draftManager - localStorage + Realtime DB 하이브리드
  • writingSessionManager - Real-time monitoring
  • writingManager, topicManager, feedManager

상세: API_SPEC.md 참조


Data Model 핵심 원칙

Firebase Auth = Single Source of Truth (이름, 이메일, 사진)

  • Firestore Users 컬렉션: uid, createdAt, lastLoginAt, settings만 저장
  • UI User 객체: Firebase Auth + Firestore 자동 결합
  • 팀별 닉네임: team.members[uid].nickname (User 아님)
  • 멤버 확인: uid in team.members

상세: DATA_MODELS.md 참조


필수 개발 규칙

다국어 지원 (i18n)

모든 새로운 UI 및 페이지는 다국어 지원이 필수입니다.

// ✅ Good
import {useTranslations} from "next-intl";
import {useRouter} from "@/i18n/routing";

const t = useTranslations('newPage');
return <h1>{t('title')}</h1>;

// ❌ Bad
return <h1> 페이지</h1>;  // 하드코딩 금지!

상세: I18N_GUIDE.md 참조


스타일 가이드

버튼에는 항상 colorPalette="brand" 사용:

// ✅ Good
<Button colorPalette="brand">클릭</Button>

// ❌ Bad
<Button>클릭</Button>  // 기본 gray 사용 금지

Semantic 토큰 우선 사용 (숫자/하드코딩 금지):

  • color="fg" / color="fg.muted"
  • bg="bg" / bg="brand.subtle"
  • borderColor="border.muted"

Icon 규칙: react-icons 필수 (이모티콘 금지)

  • react-icons/lu (Lucide) - 메인 아이콘 세트
  • 이모티콘 (🌍📝) - 사용 금지

상세: STYLE_GUIDE.md 참조


API 개발 규칙

RESTful 원칙: HTTP Method로 동작 구분

POST   /api/resource  → 생성/추가
DELETE /api/resource  → 삭제/제거
PUT    /api/resource  → 전체 수정
GET    /api/resource  → 조회

Firebase Admin SDK: adminFbClient 싱글톤 사용

// ✅ Good
import {adminFbClient} from "@/lib/firebase-admin";
const doc = await adminFbClient.collection('users').doc(uid).get();

// ❌ Bad
import {getFirestore} from "firebase-admin/firestore";
const db = getFirestore();  // 초기화 문제 발생 가능

API 응답: 헬퍼 함수 필수

import {successResponse, validationErrorResponse} from "@/lib/api-response";

// ✅ Good
return successResponse({ data });
return validationErrorResponse("에러 메시지");

// ❌ Bad
return NextResponse.json({success: false, ...});

상세: API_SPEC.md 참조


보안

초대 링크 기반 팀 참여: 디스코드 스타일 초대 시스템 사용

  • 모든 유저는 로그인 필수 (익명 인증 삭제됨)
  • 팀 참여는 초대 링크를 통해서만 가능
  • 닫힌 팀 → 초대 안 만들면 됨

XSS 방지: 백엔드 자동 sanitize

  • src/lib/server/writing.ts에서 모든 HTML 자동 세탁
  • 프론트엔드 별도 처리 불필요

상세: SECURITY.md 참조


Documentation Requirements

모든 새 기능/페이지는 다음 3개 파일 업데이트 필수:

  1. PROJECT_STRUCTURE.md - 페이지 구조, 컴포넌트 목록
  2. ROADMAP.md - 완료 작업, 예정 작업
  3. TECH_STACK.md - 아키텍처 패턴, 플로우 다이어그램

참조 문서

개발 가이드

  • API_SPEC.md - API 명세서, RESTful 원칙, Firebase Admin SDK
  • STYLE_GUIDE.md - Color 사용법, Icon 규칙, Chakra UI 테마
  • FRONTEND_DESIGN_PATTERNS.md - 컴포넌트 디자인 철학, 인터랙션/시각/애니메이션 패턴
  • I18N_GUIDE.md - 다국어 지원 가이드 (필수)
  • DEVELOPMENT_GUIDE.md - AI Delta 전송, Draft 저장, Firebase Functions

프로젝트 문서

  • PROJECT_STRUCTURE.md - 프로젝트 구조
  • TECH_STACK.md - 기술 스택, 아키텍처
  • DATA_MODELS.md - 데이터베이스 스키마
  • SECURITY.md - 보안 정책, 3단계 보안 레벨
  • ROADMAP.md - 개발 로드맵

© 2024 BlueNovaLab. All rights reserved.


코드 주석 스타일 규칙

AI가 코드에 주석을 쓸 때 지켜야 할 규칙. 이 문서가 원본이며 도구별 설정 파일은 이 문서를 참조만 한다.

0. 한 줄 요약

짧게. 왜(why)는 한 구절로만. 설명이 세 문장을 넘으면 그건 주석이 아니라 문서다.


1. 표기

  • 한국어로 쓴다. 코드 식별자·prop명·기술 용어는 원문 그대로 둔다 (flex, css, 콜백, 레이아웃, UI, undef).
  • 종결어미는 자유. 반말 평서형(~한다), 명사형(~제한), 음슴체(~함, ~됨)를 섞어 써도 된다. 짧은 쪽이 우선이고 어미는 따지지 않는다. 존댓말만 금지.
  • 대시는 하이픈 - 를 쓴다. em dash 금지.
  • 주석 안에서 마크다운 볼드 **...** 금지.
  • 홑낫표 「」 금지. 강조가 필요하면 큰따옴표.
  • 이모지 금지.
  • // ====== 류 섹션 구분선은 새로 만들지 않는다.

2. 위치 — 트레일링 vs 윗줄

대상이 한 줄이면 그 줄 끝에, 여러 줄을 감싸는 블록이면 윗줄에.

// GOOD - prop 하나 = 트레일링
<HStack
    alignContent="flex-start"                  // 요소가 위에서 아래로 정렬
    overflowY="auto"
    css={{scrollbarGutter: "stable"}}          // 스크롤 바 영역 상시 준비
>

// BAD - prop 하나인데 줄을 잡아먹음
<HStack
    // 넘칠 때 줄이 위에서부터 쌓여야 한다. 가운데로 모으면 위로 넘친
    // 만큼이 스크롤로 닿지 않아 첫 줄이 잘린다.
    alignContent="flex-start"
>
// GOOD - JSX 섹션(여러 줄)은 윗줄에
{/* 추천 단어 영역 - 없으면 섹션 자체를 숨긴다. */}
{state.openingChips.length > 0 && (
    ...
)}
  • interface 필드나 JSX prop이 여러 개일 때, 성격이 다른 것끼리는 빈 줄로 그룹을 나눈다.

3. 형식 — JSDoc은 아껴 쓴다

대상 형식
상수, 지역 변수, 내부 헬퍼 함수 한 줄 //
@param 등 태그가 실제로 필요한 함수·콜백 /** */
타입 별칭(export type), interface 선언 /** */ 한 줄. 외부에 노출되는 선언이라 JSDoc으로 둔다
컴포넌트·모듈 최상단 /** */, 요약 1줄 + 본문 1~2줄
JSX 블록 {/* */}
// GOOD
// 초안 상자의 최대 높이 제한
const SENTENCE_MAX_H_REM = 8;

// BAD - 상수에 6줄 JSDoc
/**
 * 빈칸 문장 상자 하나가 커질 수 있는 한계(rem).
 *
 * 상자는 남는 높이를 나눠 가지며 자라다가 이 값에서 멈춘다. 없으면 큰 화면에서
 * 상자 하나가 화면 절반을 차지한다. ...
 */
const SENTENCE_MAX_H_REM = 8;
// GOOD - @param이 필요하니 JSDoc 유지, 설명은 한 줄
/**
 * @param viaChip 추천 낱말을 눌러 채웠으면 true, 직접 타이핑이면 undef. (로깅용)
 */
onBlankChange: (blankId: string, value: string, viaChip?: boolean) => void;

// GOOD - 태그 없는 콜백은 한 줄 //
// 하단 선물 나중에 받기 버튼 콜백
onRewardTiming: (timing: "now" | "later") => void;

컴포넌트 최상단 JSDoc:

// GOOD
/**
 * 이야기 시작 - 초안을 받아서 빈칸 채우는 단계
 *
 * 초안을 기반으로 추천 단어를 활용해서 빈칸을 채운다.
 */
  • (폴더명) 같은 접두사를 붙이지 않는다.
  • 화면/모듈이 무엇이고 무슨 일을 하는지까지. 그 이상은 쓰지 않는다.

4. 내용 — 무엇을 쓰고 무엇을 쓰지 않는가

쓴다

  • 왜 이렇게 했는지를 한 구절로. 안 그러면 뭐가 깨지는지 짧게 붙여도 좋다.
    maxH={{md: `${SENTENCE_MAX_H_REM}rem`}}  // 단 SENTENCE_MAX_H_REM 까지만 늘어난다 - 그러면 UI가 한쪽으로 몰리게 됨
    
    {/* 빈칸 상자들 - 남은 공간을 동일한 높이로 나눠갖는다. 단 너무 크면 레이아웃이 퍼지니까 최대 높이를 주어서 제한 */}
    
  • 블록의 이름표. 그 자리가 무슨 영역인지 (추천 단어 영역, 보상 나중에 받기 미선택 css).
  • 동작 분기·숨김 조건. (없으면 섹션 자체를 숨긴다)
  • 코드만 봐서는 못 읽는 부수 효과, 순서 의존.
  • 함정 경고의 실측 수치는 남긴다. "다시 밟으면 큰일 난다"는 경고에 한해, 근거 수치 하나를 괄호로 짧게. 여러 개 나열하지 않는다.
    // blob: URL 우선 - data: URL은 콘솔에 그대로 찍혀 메인 스레드가 멈춘다(실측 10.9초)
    
    성능 함정이 아닌 곳(레이아웃 값 조정 근거 등)의 수치는 §4 "매직넘버 근거 계산"대로 자른다.

쓰지 않는다

금지 예시 (전부 삭제 대상)
과거 이력 회고 상한이 없던 시절에는 상자 하나가 화면 절반이 됐다
하지 않은 선택 서술 왼쪽 글감 레일은 여기부터 두지 않는다. 1·2단계에서 이미 알려 줬고…
다른 파일 교차 참조 (StudioConfirmBar와 같은 규칙 - 브랜드 톤 위에 놓인 …)
매직넘버 근거 계산 한 줄 문장 + 역할 안내가 5rem 안팎이므로, 그보다 넉넉하되…
CSS/언어 원리 강의 minH={0}은 반대 방향의 허락이다. 없으면 각 칸이 제 내용보다…
자명한 의도 반복 시안의 * 두 줄 - 줄바꿈을 그대로 살린다
코드를 그대로 읽어주기 // 상태 초기값 설정 / // 성공 응답
문단 나눈 장문 서술 JSDoc 안에 빈 줄로 나뉜 3~4문단

5. 분량 기준

  • 트레일링 주석(줄 끝): 1줄. 한 줄에 안 들어가면 내용을 잘라낸다. 가로로 늘리지 않는다.
  • 윗줄 주석(//, {/* */}): 2줄까지. 3줄이 필요하면 내용을 잘라낸다.
  • 컴포넌트 JSDoc: 요약 1줄 + 본문 1~2줄.
  • 주석이 코드보다 눈에 먼저 들어오면 과한 것이다. 한 파일에서 주석이 전체의 10%를 넘어가면 줄일 곳을 찾는다.

6. 자기 점검

주석을 쓰기 전에:

  1. 이 코드를 지우고 주석만 읽어도 되는 내용인가? → 그건 문서에 쓸 내용이지 주석이 아니다.
  2. 코드가 이미 말하고 있는가? → 지운다.
  3. 세 문장 이상 필요한가? → 두 문장으로 못 줄이면, 설명이 필요한 건 주석이 아니라 코드 구조다.