뉴로슬롭 리뷰
뉴로슬롭은 스타일이 아니라 확인되지 않은 주장과 존재 이유가 없는 코드입니다. 아래 각 발견 사항은 코드 라인, 명령어 출력, 소비자 부재 등 사실로 검증됩니다. 취향에 따른 의견이나 린터가 잡아내는 내용은 포함되지 않습니다.
원칙.
- 추가보다 삭제가 낫습니다. 죽은 옵션은 문서화하지 말고 삭제합니다. 마케팅 문구는 사실로 대체하거나 제거합니다.
- 주장은 코드로 검증하며, 기억에 의존하지 않습니다. README의 줄이 거짓인지 참인지 판단하기 전에, 해당 파일을 열어 확인합니다.
- 증거 없는 발견은 발견이 아닙니다.
file:line또는 명령어 출력을 제시할 수 없으면 작성하지 않습니다. - 수정은 요청 시에만 합니다. 기본적으로 리뷰 결과는 목록 형태입니다.
1. 작업 순서
- 범위를 결정합니다: diff(
git diff main...HEAD), 특정 파일, 또는 공개 전 전체 저장소. - 3절의 검색을 실행합니다. 이는 후보를 찾는 단계이며, 최종 발견 사항이 아닙니다.
- 각 후보를 코드를 읽어 확인하고 거짓 양성은 제외합니다.
- 검색으로 잡히지 않는 항목에 대해 2절의 체크리스트를 적용합니다: README 대 코드, UI 상태, 추상화.
- 애플리케이션을 실행할 수 있다면 페이지를 확인합니다. 템플릿 화면과 스타일이 없는 컴포넌트는 즉시 드러납니다.
- 4절의 형식으로 보고서를 출력합니다.
2. 체크리스트
2.1. 생성기 및 템플릿 잔재
- 생성기의 시작 페이지 및 블록: "Get started by editing", 프레임워크 및 도구 문서 링크,
utm_source=…-project, "Give us a star", 도구의 클라우드 서비스 블록. - 템플릿 파일:
example.spec.ts, 템플릿 제목만 확인하는 테스트;public/내 템플릿 SVG 및 이미지; 생성기 README. - 빈 더미: 이미 파일이 있는 폴더의
.gitkeep; 빈index.ts; 코드가 없는 레이어 폴더.
2.2. 죽은 코드 및 미완성
- "not implemented" / "pending"을 throw하는 설정 값 또는
switch분기. - 확장 지점처럼 보이는 단일 값 열거형(
type: "graphql" | "msw"에서msw가 구현되지 않은 경우). - 주석 처리된 코드, 이유나 참조 없는
TODO, 사용되지 않는 export,package.json에서 어디에도 import되지 않는 의존성. package.json에서 실행되지 않는 스크립트(삭제된 파일, 존재하지 않는 타겟 참조).
2.3. 코드와 상충되는 문서
README, ADR, 주석 및 커밋 설명의 모든 주장을 검증합니다:
- 이름 및 경로: 언급된 파일, 명령어, 환경 변수가 존재하는지 확인(
next.config.ts라고 텍스트에 쓰여 있지만 저장소에는next.config.js가 있으면 발견). - 동작: "요청이 직접 전달됩니다", "토큰이 JS에 접근 불가능합니다", "open redirect로부터 보호됩니다" — 이 동작을 수행하는 코드를 찾고 경계 조건을 확인합니다.
- 버전 및 숫자: 라이브러리 버전, 토큰 수명, 커버리지.
- 현재 시제로 쓰인 미래: 계획된 것이 마치 완료된 것처럼 설명됨.
- Markdown 링크와 앵커가 올바른 곳을 가리키는지 확인(앵커는 제목 수정 시 깨짐).
2.4. 어조
- 사실 대신 가치 판단 단어: premium, state-of-the-art, cutting-edge, blazing/lightning-fast, seamless, robust, powerful, modern, production-ready, enterprise-grade, "단순한 것이 아닌", "프리미엄", "현대적인", "강력한".
- 문서 제목, UI, 로그 및 커밋 메시지의 이모지.
- "Designed with ❤️", "Carefully crafted", 의미 없는 배지.
- 설명적인 주석(
getUser()위의 "// 사용자 가져오기"), 역사적인 주석("// 수정됨", "// X로 업데이트됨"), 대문자 주석, 설명 없는 "IMPORTANT" 표시. - "🚀 Production Roadmap" 및 "(Premium Aesthetic)" 스타일의 제목 및 주석.
대체는 사실입니다: 무엇을 하는지, 왜 그런지, 어떤 제약이 있는지.
2.5. 불필요한 추상화
- 테스트에 페이크가 없는 단일 구현체 인터페이스 또는 팩토리.
- 소비자나 기록된 이유(ADR, README 섹션)가 없는 레이어, 영역, 모노레포 패키지, 상태 관리자, Storybook.
- 단순히 이름만 바꾸는 래퍼(
useAuthApi = () => useStrictContext(Ctx)— 허용 가능; 래퍼 위의 래퍼는 허용 불가). - "미래를 위한": 아무도 전달하지 않는 매개변수, 단일 값만 있는 설정 옵션.
2.6. 오류 및 비동기
- 메시지 텍스트로 오류 유형 결정(
message === "unauthorized",includes("invalid password")) — 이유 주석 및 테스트 없음. - 오류 배열의 첫 번째 요소만 확인(
errors[0]). throw { status, ... }—Error대신 객체 사용.- 빈
catch, 빈 데이터를 반환하는catch,.catch(() => {}). - 대기 중인 Promise가 완료되었는지 확인하지 않고 공유 inflight Promise 사용(토큰 리프레시 "기다렸다가 재시도"하지만 성공 여부 확인 없음).
- 서버의
error.message를 그대로 사용자에게 표시. - 로딩, 오류, 빈 목록 상태 누락.
2.7. 보안 및 개인 정보
- 로그의 개인 정보: 이메일, 전화번호, 이름; 토큰; 요청 본문.
- 저장소 또는 히스토리 내 비밀(
git log -p), 인덱스의.env,NEXT_PUBLIC_접두사가 있는 비밀. - 검증 없는 매개변수 리디렉션(
callbackUrl,next,returnTo), 또는startsWith("/")/!startsWith("//")만 검증하는 경우 —/\evil.com및 제어 문자를 통과시킴. localStorage/sessionStorage의 토큰; 읽은 후 주소 표시줄에서 제거되지 않는 URL 내 토큰.- 살균 처리 없는
dangerouslySetInnerHTML.
2.8. UI
- 테마에 없는 클래스: shadcn/ui에서 복사된 토큰(
bg-card,text-muted-foreground,bg-primary)이 해당 토큰이 없는 프로젝트에 있음 — 컴포넌트가 조용히 스타일 없이 렌더링됨. - 토큰이 존재함에도 컴포넌트에 원시 HEX 및 임의 값 사용.
onChange에서 설정되는 "필드 입력됨" 상태: 자동 완성 및reset()에서 깨짐.- 변경 가능한 목록에서
key={index}; 폼 내부에type없는 버튼;aria-label없는 아이콘 버튼. viewBox에 의해 잘린 필터(그림자, 글로우)가 있는 디자인의 SVG — 로고 주변에 보이는 직사각형.
2.9. 테스트
- 동작이 아닌 목(mock)을 검증하는 테스트; 큰 트리의 스냅샷.
- 공용 또는 프로덕션 백엔드에 대해 데이터를 변경하는 e2e 테스트.
- jsdom이 CSS를 적용하지 않아 통과하는 테스트(클래스로 "숨겨진" 요소를 테스트가 찾음), 그리고 너무 넓은 선택자(
/password/i는 "Show password"와도 일치).
2.10. Git
- 단일 커밋에 관련 없는 변경 사항; "fix stuff" 커밋.
- 인덱스의 생성된 파일 및 빌드 아티팩트(의도적으로 커밋된 생성된 API 클라이언트 제외).
- 커밋 메시지가 diff 내용과 다름.
3. 후보 검색
명령어는 후보를 제공합니다. 각 일치 항목은 코드를 읽어 확인합니다.
# 생성기 템플릿 및 마케팅
rg -n -i "utm_source|get started by editing|give us a star|nx cloud|welcome to|carefully crafted|designed with" \
--glob '!**/node_modules/**' --glob '!*.lock' --glob '!pnpm-lock.yaml'
rg -n -i "premium|state-of-the-art|cutting-edge|blazing|lightning|seamless|robust|enterprise-grade|production-ready|премиальн|современн|мощн" \
--glob '!**/node_modules/**'
# 코드, UI 및 문서의 이모지
rg -n --pcre2 '[\x{1F300}-\x{1FAFF}\x{2600}-\x{27BF}]' --glob '!**/node_modules/**' --glob '!*.lock'
# 미완성
rg -n -i "not implemented|pending implementation|todo|fixme|hack" --glob '!**/node_modules/**'
# 취약한 오류 처리
rg -n "errors\?\.\[0\]|errors\[0\]|message ===|message\.includes|\.includes\([\'\"][a-z ]+[\'\"]\)" src app
rg -n "throw \{" src app
rg -n "catch \{\s*\}|catch \(\w*\) \{\s*\}|\.catch\(\(\) => \{\s*\}\)" src app
# 개인 정보가 포함된 로그
rg -n "(logger|console)\.\w+\(.*(email|phone|password|token)" src app
# 리디렉션 및 토큰
rg -n "callbackUrl|returnTo|redirectTo|searchParams.get\([\'\"]next" src app
rg -n "localStorage|sessionStorage" src app
# 토큰 없는 shadcn 클래스: 클래스를 찾아 @theme / tailwind 설정과 대조
rg -n -o "\b(bg|text|border)-(card|muted|popover|accent|primary|secondary|destructive)(-foreground)?\b" src app | sort -u
# 사용되지 않는 의존성 및 export (knip 설치된 경우)
pnpm dlx knip
# 히스토리의 비밀
git log -p --all | rg -n -i "(api[_-]?key|secret|password|token)\s*[:=]\s*[\'\"][A-Za-z0-9_\-]{16,}"
git ls-files | rg -n "(^|/)\.env($|\.)" | rg -v "\.env\.example$"
4. 보고서 형식
발견 사항은 중요도 내림차순으로 정렬합니다: 보안 및 개인 정보 → 문서의 거짓 주장 → 오류 처리 버그 → 죽은 코드 및 미완성 → 템플릿 → 어조.
<파일>:<줄> — <카테고리>
내용: <한 문장: 문제점, 사실 포함>
중요한 이유: <사용자, 리뷰어 또는 유지보수에 미치는 영향>
수정: <구체적인 방법; 대부분 "삭제" 또는 "사실로 대체: …">
마지막에는 검사했지만 발견 사항이 없는 내용과 검사하지 않은 내용(예: 애플리케이션을 실행하지 않음)을 기재합니다. 발견 사항이 없으면 억지로 찾지 않고 그대로 명시합니다.