GN⁺: AI Blindspots – AI 코딩 중에 발견한 LLM의 맹점들
(ezyang.github.io)AI 코딩 중에 발견한 LLM의 맹점들. Claude Sonnet 기준
- Stop Digging → 문제 발생 시 방향 전환 어려움
- Use Static Types → 정적 타입 설정 필요
- Black Box Testing → 구현 세부 정보에 과도하게 의존
- Use MCP Servers → MCP 서버 설정 및 안전성 문제
- Preparatory Refactoring → 필요 없는 리팩토링 수행 가능
- Mise en Place → 환경 설정 실패 시 문제 발생
- Stateless Tools → 상태 의존 도구에서 문제 발생
- Respect the Spec → 명세 위반 가능성 높음
- Bulldozer Method → 반복 작업 과다 수행
- Memento → 맥락 이해 부족 문제 발생
- Requirements, not Solutions → 요구 사항 명확화 필요
- Scientific Debugging → 추측 기반 수정 시 문제 발생
- Use Automatic Code Formatting → 코드 스타일 불일치 발생
- The Tail Wagging the Dog → 중요 작업보다 사소한 문제에 집착
- Keep Files Small → 큰 파일 수정 시 문제 발생
- Know Your Limits → 모델이 자신의 한계 인식 부족
- Read the Docs → 학습된 지식 외의 정보에서 오류 발생
- Culture Eats Strategy → 코드 스타일 일관성 부족
- Walking Skeleton → 최소한의 시스템 작동 우선 필요
- Rule of Three → 코드 중복 시 리팩토링 필요
문제에 빠져들지 않음 (Stop Digging)
- 현재 LLM은 작업 중 문제가 발생해도 스스로 이를 중단하고 방향을 바꾸는 능력이 부족함
- 예: 기능 X 구현 중 기능 Y를 먼저 구현해야 하는 상황이 발생해도 LLM은 원래 작업(X) 완료 시도
- LLM이 명령을 충실히 수행한다는 점에서는 장점이지만, 문제를 자각하고 방향 전환이 어려움
- 문제를 피하기 위한 전략
- 계획 수립 단계에서 추론 모델을 사용해 작업의 우선순위 및 선행 작업 결정
- Sonnet 같은 에이전트 LLM은 파일을 읽고 작업 계획을 수립함 → 사용자가 명시적으로 지시하지 않아도 필요한 작업을 파악 가능
- 이상적으로는 LLM이 문제를 인지하고 사용자에게 확인 요청 가능해야 함
- 그러나 이는 컨텍스트를 소모하기 때문에 별도의 감시 LLM이 이를 처리하는 것이 더 나을 수 있음
-
Example
- Monte Carlo 시뮬레이션의 난수 샘플링 방식 수정 후 Claude Code에 테스트 수정 요청
- 새로운 구현이 비결정적이라 테스트 결과가 통과/실패가 랜덤하게 발생
- Claude Code는 이를 인지하지 못하고 테스트 조건을 완화해 문제 해결 시도
- 대신 시뮬레이션이 결정론적이 되도록 리팩토링 제안했어야 함
- Monte Carlo 시뮬레이션의 난수 샘플링 방식 수정 후 Claude Code에 테스트 수정 요청
정적 타입 사용 (Use Static Types)
- 동적 타입 vs 정적 타입 시스템 논쟁은 프로토타이핑의 용이성과 장기적인 유지 보수 간의 균형 문제
- LLM이 보일러플레이트 코드와 리팩토링을 처리할 수 있어 프로토타이핑에 적합한 언어 선택 부담 감소
- 따라서 프로토타이핑보다 장기 유지 보수에 유리한 언어 선택 가능
- 타입 오류 수정 전략
- 에이전트 설정에서 LLM이 변경 후 발생한 타입 오류를 인지하도록 구성
- 이를 통해 수정이 필요한 다른 파일도 쉽게 파악 가능
- 주의점
- Python 및 JavaScript의 경우 점진적 타입 시스템 사용 → 타입 체커 설정을 엄격하게 구성 필요
- Rust는 원칙적으로 LLM에 적합하지만 현재 Python/JavaScript만큼 잘 생성되지 않음
블랙 박스 테스트 (Black Box Testing)
- 블랙 박스 테스트는 컴포넌트의 내부 구조를 모른 채 기능을 테스트하는 방식
- LLM은 구현 파일이 컨텍스트에 포함되기 때문에 블랙 박스 테스트 원칙 준수 어려움
- Sonnet 3.7(Cursor 사용)의 경우 코드 일관성을 유지하려는 경향 → 테스트 파일의 중복 제거 시도
- 그러나 블랙 박스 테스트에서는 중복 유지가 버그 탐지에 유리함
- 이상적인 해결책
- LLM이 로드한 파일에서 구현 세부 정보를 마스킹 또는 요약 가능해야 함
- 아키텍트가 정보 은닉 경계를 명확히 정의해야 함
-
Example
- Sonnet 3.7이 실패한 테스트 수정 시 하드코딩된 상수를 원본 알고리즘 기반으로 계산하도록 수정
- 원래 상수를 그대로 유지해야 했음
- Sonnet 3.7이 실패한 테스트 수정 시 하드코딩된 상수를 원본 알고리즘 기반으로 계산하도록 수정
MCP 서버 사용 (Use MCP Servers)
- Model Context Protocol(MCP) 서버는 LLM이 환경과 상호작용할 수 있는 표준 인터페이스 제공
- Cursor 에이전트 모드 및 Claude Code에서 MCP 서버를 광범위하게 사용
- 별도의 RAG 시스템 없이 LLM이 MCP 호출로 필요한 파일 검색 및 수정 가능
- 모델이 테스트 또는 빌드를 수행한 후 즉시 문제 수정 가능
- 사용자 정의 MCP 서버 작성 고려 사항
- Cursor에서 YOLO 모드 활성화 후 Cursor 규칙에 셸 명령 추가 가능
- 위험함 → 임의의 셸 명령이 환경을 손상시킬 수 있음
- 대안: 특정 명령만 노출하는 사용자 정의 MCP 서버 작성 → 안전성 강화
- 단, 2025년 3월 기준 Cursor에서 프로젝트별 MCP 서버 설정은 미흡
- Cursor에서 YOLO 모드 활성화 후 Cursor 규칙에 셸 명령 추가 가능
-
Example
- Sonnet 3.7이 TypeScript 프로젝트 타입 체크 및 오류 수정 시 MCP 사용
- 터미널 출력을 수동으로 복사-붙여넣기할 필요 없이 자동 처리
- 그러나 잘못된 명령(
npm run typecheck
)을 추론하는 경우 발생 가능
- Sonnet 3.7이 TypeScript 프로젝트 타입 체크 및 오류 수정 시 MCP 사용
준비 리팩토링 (Preparatory Refactoring)
- 준비 리팩토링은 변경 작업 전 먼저 리팩토링을 수행해 작업을 쉽게 만드는 전략
- 리팩토링은 의미 보존 작업이므로 실제 변경보다 평가가 용이함
- 먼저 리팩토링 후 변경 작업 수행 → 검토 및 오류 수정이 쉬워짐
- 현재 LLM의 문제점
- 사전 리팩토링 없이 모든 작업을 한 번에 처리하려는 경향
- 필요 없는 정리 작업까지 수행 → 과도한 리팩토링 발생 가능
- Cursor Sonnet 3.7은 명령 수행 정확도가 떨어짐 → 비관련 리팩토링 발생 가능
- 개선 방안
- LLM이 수정 전 리팩토링 단계에서만 코드 수정하도록 명시적 지시 필요
- LLM이 편집할 코드 범위를 명확히 정의 → 불필요한 수정 방지
-
Example
- LLM에 import 오류 수정 지시 → 수정 후 람다 함수에 타입 주석 추가
- 주석 중 일부가 잘못 추가돼 에이전트 루프 발생
- LLM에 import 오류 수정 지시 → 수정 후 람다 함수에 타입 주석 추가
미장 플라스 (Mise en Place)
- 요리에서 미장 플라스는 작업 전에 모든 재료와 도구를 정리해두는 것
- LLM에서 미장 플라스는 작업 전에 필요한 규칙, MCP 및 개발 환경을 완전히 설정하는 것
- Sonnet 3.7은 깨진 환경을 고치는 데 취약함
- StackOverflow에서 명령어 복사-붙여넣기로 문제 해결 시도 → 환경 손상 위험
- 작업 전에 환경을 올바르게 설정해 Sonnet이 디버깅 루프에 빠지지 않도록 해야 함
-
Example
- npm link 문제로 VSCode에서 다른 로컬 프로젝트의 import 인식 실패
- Cursor가 lint 및 테스트 수정 중 이 문제 해결에 집착했으나
npm unlink
실행 필요성 인식 실패
- Cursor가 lint 및 테스트 수정 중 이 문제 해결에 집착했으나
- npm link 문제로 VSCode에서 다른 로컬 프로젝트의 import 인식 실패
상태 없는 도구 사용 (Stateless Tools)
- 도구는 상태를 저장하지 않고 매번 독립적으로 실행되어야 함
- 셸은 현재 작업 디렉토리 상태에 의존 → 상태 저장으로 인한 혼란 발생 가능
- Sonnet 3.7은 현재 작업 디렉토리 상태를 정확히 추적하지 못함
- 모든 명령을 프로젝트 루트 디렉토리에서 실행 가능하도록 설정 필요
- 개선 방안
- 상태 변경이 필요한 도구 명령어 사용 최소화
- 상태가 반드시 필요한 경우 현재 상태를 모델에 지속적으로 제공해 일관성 유지
-
Example
- TypeScript 프로젝트가 common, backend, frontend의 세 모듈로 구성된 경우
- Cursor가 루트에서 실행 시 적절한 디렉토리로
cd
필요 → 디렉토리 혼란 발생 - 각 모듈을 개별 작업공간으로 열어 작업하니 문제 해결됨
- Cursor가 루트에서 실행 시 적절한 디렉토리로
- TypeScript 프로젝트가 common, backend, frontend의 세 모듈로 구성된 경우
스펙 준수 (Respect the Spec)
- 시스템 변경 시 수정 가능한 부분과 수정 불가능한 부분을 명확히 구분해야 함
- 공개 API 수정 시 하위 호환성 깨짐 방지 필요
- 외부 시스템과 통합 시 실제 존재하는 API에 맞춰야 함 → 원하는 대로 수정 불가
- 테스트 실패 시 테스트 삭제 금지 → 원인을 파악하고 수정해야 함
- LLM의 문제점
- 명세 위반 가능성 높음 → 테스트 삭제, API 변경 등 자유롭게 수행
- 명세 준수는 상식적이지만 프롬프트에 명시해야 할 수도 있음
- 일부 경계는 코드 리뷰를 통해만 발견 가능
-
Example
- Sonnet이 테스트 수정 실패 후 테스트 내용을
assert True
로 대체 - public 함수가
pass
키를 포함한 dict 반환 → Sonnet이pass_
로 변경 시도 (예약어 문제)
- Sonnet이 테스트 수정 실패 후 테스트 내용을
불도저 방식 (Bulldozer Method)
- 불도저 방식은 단순 반복 작업을 통해 문제를 해결하고 학습 효과로 속도를 높이는 전략
- AI 코딩은 본질적으로 반복 작업에 강함 → 충분한 토큰 사용 시 대규모 리팩토링 가능
- 사람이 "너무 작업량이 많다"며 포기한 문제도 LLM이 해결 가능
- 단, LLM은 같은 작업을 반복할 수 있으므로 실제로 무엇을 하고 있는지 검토 필요
-
Example
- Haskell이나 Rust에서 핵심 함수 수정 시 광범위한 리팩토링 필요
- LLM이 컴파일 오류 읽기 → 수정 → 다시 컴파일 과정을 자동화 가능
- 하드코딩된 테스트 값 수정 시 LLM이 테스트 재실행 후 자동 수정 수행
- Haskell이나 Rust에서 핵심 함수 수정 시 광범위한 리팩토링 필요
메멘토 (Memento)
- LLM은 상태를 기억하지 못함 → 매 작업마다 코드베이스를 처음부터 다시 이해해야 함
- 프롬프트, 명시적/암시적 컨텍스트, 에이전트 모드에서 모델이 불러온 파일만으로 작업 수행
- 매 작업마다 코드베이스를 재이해 → 초기 설정 실패 시 오작동 가능성 높음
- 문제 방지 전략
- LLM이 참조할 수 있는 문서를 명확히 제공
- 모델이 필요한 정보를 쉽게 찾을 수 있도록 설정
- 프로젝트 전체 맥락을 제공한 후 주요 변경 요청 수행
-
Example
- Sonnet 3.7에 기존 프로젝트의 엔드 투 엔드 테스트 계획 수립 요청
- 프로젝트의 전체 목적이 테스트라고 오해 → README를 테스트 중심으로 수정
- Sonnet 3.7에 기존 프로젝트의 엔드 투 엔드 테스트 계획 수립 요청
요구 사항 명확화 (Requirements, not Solutions)
- 소프트웨어 엔지니어링에서 흔한 실수는 요구 사항을 명확히 정의하지 않고 바로 해결책을 제안하는 것
- 문제 공간이 충분히 제한되면 요구 사항만 명확히 정의해도 해결책이 자동으로 결정됨
- 요구 사항이 명확하지 않으면 해결책에 대해 불필요한 논쟁 발생 가능
- LLM의 문제점
- LLM은 요구 사항을 알지 못함 → 훈련된 패턴에서 가장 확률이 높은 답변 생성
- 명확한 요구 사항 없이 작업 요청 시 엉뚱한 결과 발생 가능
- 프롬프트 수정으로 잘못된 해석 수정 가능 → 잘못된 해석이 컨텍스트에 남아 있으면 수정 어려움
- 개선 방안
- 특정 방식의 해결책이 필요하면 이를 명시적으로 지시
- LLM은 명령을 정확히 따르므로 잘못된 방식으로 지시하면 부정확한 결과 발생 가능
-
Example
- Sonnet에 시각화 생성 요청 시 기본적으로 SVG 생성
- "상호작용 가능" 명시 시 React 기반 애플리케이션 생성 → 키워드 하나로 큰 차이 발생
- Sonnet에 시각화 생성 요청 시 기본적으로 SVG 생성
과학적 디버깅 (Scientific Debugging)
- 버그 수정 방식은 두 가지로 나뉨
- 무작위로 수정 시도 후 운에 맡김
- 시스템 작동 방식을 논리적으로 분석해 실제 상태와 예상 상태의 불일치 원인 파악
- 과학적 디버깅(논리적 분석)이 장기적으로 더 나은 접근 방식
- LLM의 문제점
- LLM은 추론 능력이 부족해 과학적 접근이 어려움
- "정답 추측" 후 바로 수정 시도 → 실패 시 무작위 수정 반복(에이전트 루프)
- 디버깅은 Grok 3, DeepSeek-R1 같은 추론 모델이 더 적합
- 개선 방안
- 모델이 원인을 분석하도록 명령하거나 사용자가 원인 제공 → 수정 성공률 향상
- 문제 원인을 정확히 알려주면 모델이 더 나은 해결책 제안 가능
-
Example
- Sonnet 3.7이 pip가 없는 기본 uv 환경에서 패키지 설치 오류 발생
- 원인 파악 실패 후 무작위 시도 반복 → 토큰 낭비 및 디버깅 실패
- Sonnet 3.7이 pip가 없는 기본 uv 환경에서 패키지 설치 오류 발생
자동 코드 포맷팅 사용 (Use Automatic Code Formatting)
- 자동 코드 포맷팅 도구(gofmt, rustfmt, black 등)는 일관된 코드 스타일 유지에 유용
- LLM은 기계적인 규칙(예: 빈 줄에 공백 없음, 78자 줄 길이 제한 등) 준수에 취약
- 포맷팅은 도구에 맡기고 LLM은 복잡한 작업에 집중하도록 해야 함
- 린트 수정에도 동일 원칙 적용
- 자동 수정 가능한 린트 사용 권장
- LLM의 리소스를 복잡한 문제 해결에 집중하도록 할 것
꼬리가 개를 흔든다 (The Tail Wagging the Dog)
- 사소한 문제가 더 중요한 문제를 좌우하는 상황을 의미
- 저수준 문제 해결에 집착해 전체 코드 작성 목적을 잊어버리는 경우 발생 가능
- LLM은 채팅 세션에서 모든 정보를 컨텍스트에 포함 → 중요도 판단 어려움
- 개선 방안
- 초기에 명확한 프롬프트 제공 → LLM이 중요한 작업에 집중하도록 유도
- Claude Code는 하위 에이전트를 통해 특정 작업을 수행해 글로벌 컨텍스트 오염 방지
-
Example
- LLM에 특정 작업 방법을 생각해보라고 요청 시 생각이 아닌 실제 작업 수행 시도 발생 가능
파일 크기 작게 유지 (Keep Files Small)
- 코드 파일 크기에 대한 논쟁은 오래 지속됨
- 단일 책임 원칙(파일당 하나의 클래스) 적용 vs 상황에 따라 대형 파일 허용
- 파일 크기가 너무 크면 RAG 시스템에서 파일 단위 컨텍스트 로딩 시 문제 발생 가능
- Cursor 같은 IDE에서 패치 적용 실패 가능 → 성공해도 적용 시간 길어짐
- 예: Cursor 0.45.17에서 64KB 파일의 55개 수정 적용에 상당한 시간 소요
- Sonnet 3.7은 128KB 이상의 파일 수정이 어려움(컨텍스트 윈도우 200K 토큰 제한)
- 개선 방안
- 파일 크기를 작게 유지 → LLM이 자동으로 import 등 처리 가능
-
Example
- Sonnet 3.7이 471KB의 Python 파일에서 작은 테스트 클래스 이동 시도
- 수정은 작았으나 Cursor 패처에서 수정 적용 실패
- Sonnet 3.7이 471KB의 Python 파일에서 작은 테스트 클래스 이동 시도
한계를 인식하기 (Know Your Limits)
- 도구 부족이나 역량 한계 상황에서 문제를 인식하고 도움 요청 필요
- Sonnet 3.7은 자신의 한계를 인식하는 데 약함
- 명확한 프롬프트 제공 시 한계 인식 가능 → 특정 주제에서 환각 발생 경고 설정 필요
- 문제점
- Sonnet 3.7은 자신이 셸 명령 실행 가능하다고 잘못 인식
- 셸 명령이 없을 경우 무작위 셸 스크립트 생성 시도 → 환경 손상 위험
- "X를 실행하겠다"라고 말한 후 완전히 다른 Y에 대한 호출 생성 발생 가능
- Sonnet 3.7은 자신이 셸 명령 실행 가능하다고 잘못 인식
- 개선 방안
- 프롬프트 수정 또는 원하는 작업만 수행하는 전용 도구 제공
- 특정 도구 제공 시 엉뚱한 셸 호출 방지 가능
- 프롬프트 수정 또는 원하는 작업만 수행하는 전용 도구 제공
-
Example
- Sonnet 3.7이 파일 실행 권한 부여 시 엉뚱한 셸 스크립트 생성 시도
- 명령 오류 발생 후 잘못된 수정 시도 반복 발생
- Sonnet 3.7이 파일 실행 권한 부여 시 엉뚱한 셸 스크립트 생성 시도
문서 읽기 (Read the Docs)
- 새로운 프레임워크나 라이브러리 학습 시 튜토리얼 코드 수정으로 간단한 작업 가능
- 하지만 궁극적으로는 문서를 처음부터 끝까지 읽어 전체 동작 방식 이해 필요
- LLM의 장점
- 인기 있는 프레임워크는 사전 학습된 경우가 많아 대부분의 사용법을 기억
- 그러나 비주류 도구나 지식 컷오프 이후에 나온 도구는 환각 발생 가능
- Sonnet은 웹 검색 미지원 → 수동으로 문서 제공 필요
- Cursor에서는 URL 제공 시 자동으로 컨텍스트에 포함 가능
-
Example
- LLM에 Python 함수 호출을 위한 YAML 작성 요청 시 잘못된 설정 생성
- 문서 제공 후 수정 성공 및 출력 형식 개선
- LLM에 Python 함수 호출을 위한 YAML 작성 요청 시 잘못된 설정 생성
문화가 전략을 이긴다 (Culture Eats Strategy)
- 팀의 문화가 전략 실행 능력에 결정적 영향 미침
- LLM은 미리 학습된 스타일과 컨텍스트 윈도우에 따라 코드 생성
- 컨텍스트에 자주 등장하는 라이브러리나 스타일 선호
- 명시되지 않으면 기본 스타일 적용
- LLM 스타일 수정 전략
- Cursor 규칙 수정(프롬프트 변경)
- 기존 코드 스타일을 원하는 형태로 리팩토링 → 다음 토큰 예측에 영향
- 코드베이스 크기가 프롬프트보다 더 큰 영향 → 코드베이스 수정이 근본 해결책
-
Example
- Sonnet 3.7은 Python에서 동기 코드를 선호
- 비동기 코드 생성을 위해 기존 코드 대부분을 async로 포팅 후 성공
- Sonnet 3.7은 Python에서 동기 코드를 선호
워킹 스켈레톤 (Walking Skeleton)
- 워킹 스켈레톤은 최소한의 엔드 투 엔드 시스템 구현 전략
- 완벽하지 않아도 전체 시스템이 동작하게 만든 후 세부 개선 진행
- LLM 코딩 시대에는 전체 시스템을 빠르게 구축하기 더 쉬워짐
- 시스템이 작동하면 다음 단계가 명확해짐 → 빠르게 작동 상태에 도달하는 것이 중요
- LLM은 작성한 코드를 직접 사용할 수 없기 때문에 동작 상태 확보가 중요
세 번째 규칙 (Rule of Three)
- 동일 코드 복제는 두 번까지 허용, 세 번째 복제 시 리팩토링 필요
- DRY(Don't Repeat Yourself) 원칙의 개선된 버전
- 중복 제거 시점 명확화 → 세 번째 복제에서 리팩토링 수행
- LLM의 문제점
- LLM은 코드 중복 생성 경향
- 프롬프트 없이 수정 요청 시 코드 전체를 새로 복제해 수정 수행
- 중복 제거는 모델이 자발적으로 결정해야 수행됨 → 명확한 지시 필요
- 개선 방안
- 명시적으로 중복 제거 지시 필요
- 기존 코드에 중복이 많으면 모델이 중복을 계속 생성할 수 있음
-
Example
- LLM에 테스트 코드 작성 요청 시 동일 로직이 여러 테스트에 중복 발생
- 명시적으로 보조 메서드 생성 지시 후 해결됨
- 에이전트 모드
- LLM에 테스트 코드 작성 요청 시 동일 로직이 여러 테스트에 중복 발생
Hacker News 의견
-
LLM은 인간과 다른 방식으로 실수를 하며, 이를 잡아내기 어려움
- 인간의 실수를 잡아내는 데는 오랜 경험이 있지만, LLM의 사고방식을 이해하기 어려움
- LLM의 오류를 잡아내는 시스템 설계가 어려움
-
LLM은 요구사항을 모르면 훈련 데이터에서 가장 가능성 있는 답을 채워 넣음
- 고객이 원하는 것을 정확히 설명해야 AI가 프로그래머를 대체할 수 있음
-
소프트웨어 엔지니어링에서 요구사항을 명확히 하는 것이 중요함
- 요구사항을 명확히 하면 해결책이 자연스럽게 결정됨
- 새로운 프레임워크나 라이브러리를 배울 때는 문서를 꼼꼼히 읽는 것이 좋음
- 버그를 고칠 때는 시스템의 가정을 체계적으로 검토하는 것이 중요함
- 코드 중복은 세 번째 발생 시 리팩토링하는 것이 좋음
-
LLM은 "매우 똑똑한 초급 프로그래머" 수준의 코딩 능력을 가짐
- 큰 그림을 보는 능력이 부족하고, 요청된 것만 수행함
- 모델은 계속 개선될 것으로 예상됨
-
LLM은 너무 많은 답변을 하려고 함
- 충분한 데이터를 주지 않으면 잘못된 답변을 생성함
- LLM이 "더 많은 정보가 필요함"이라고 말할 수 있으면 좋겠음
-
블로그의 게시물이 많아지면서 정리가 필요함
- 좋은 조직 시스템을 찾지 못했음
-
LLM과 코딩할 때의 유용한 조언
- 정적 타입 사용에 대한 의견 차이 있음
- Clojure가 Typescript보다 더 나은 결과를 줌
- LLM은 함수 중심의 접근 방식에 더 적합함
-
LLM은 계산과 산술에 약함
- 코드 생성 시 정확한 위치에서 숫자를 가져오는 것이 중요함
- LLM이 생성한 코드를 디버깅하는 데 시간이 걸림
-
인간 코더와 함께 고려해야 할 사항
- 제품 관리자도 주목해야 함
-
세 가지 LLM이 존재하지 않는 "버그"를 발견한 사례
- 최적화된 코드는 아니지만 버그는 아님
- 코드 블록 간의 거리가 짧았음