원문: The Psychology of Clean Code: Why We Write Messy React Components
우리 모두 클린 코드를 작성해야 한다는 것을 알고 있습니다. 관련한 책을 읽기도하고, 세미나도 참석하며 여러 원칙을 이해하고 공감했습니다. 그런데도 여전히 지저분한 리액트 컴포넌트를 작성하고 있는 자신을 발견하게 됩니다. 왜 그럴까요? 그 이유는 기술력 부족이 아니라, 우리의 심리적인 요인에 있습니다.
인지 부하의 함정
다음과 같이 일반적인 상황을 생각해 보세요.
const UserDashboard = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [filter, setFilter] = useState("");
const [sortBy, setSortBy] = useState("name");
const [page, setPage] = useState(1);
const [totalPages, setTotalPages] = useState(1);
useEffect(() => {
fetchUsers();
}, [filter, sortBy, page]);
const fetchUsers = async () => {
try {
setLoading(true);
const response = await fetch(`/api/users?filter=${filter}&sort=${sortBy}&page=${page}`);
const data = await response.json();
setUsers(data.users);
setTotalPages(data.totalPages);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
const handleFilterChange = (e) => setFilter(e.target.value);
const handleSortChange = (e) => setSortBy(e.target.value);
const handlePageChange = (newPage) => setPage(newPage);
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;
return (
<div>
<FilterBar filter={filter} onFilterChange={handleFilterChange} sortBy={sortBy} onSortChange={handleSortChange} />
<UserList users={users} />
<Pagination currentPage={page} totalPages={totalPages} onPageChange={handlePageChange} />
</div>
);
};
이 컴포넌트는 나쁘지는 않지만, 그렇다고 좋다고 할 수도 없습니다. 너무 많은 일을 담당하고 있고, 다양한 관심사를 한곳에서 다루고 있어, 유지보수가 어려운 구조입니다. 그럼에도 불구하고, 이런 코드는 우리가 시간에 쫓기거나 빠르게 작업하려 할 때 자주 작성하게 되는 컴포넌트의 전형적인 모습입니다.
우리가 지저분한 코드를 작성하는 이유
1. 계획 오류
우리는 항상 작업에 걸리는 시간을 과소평가합니다. 이는 다음과 같은 결과를 초래합니다.
- 마감일을 맞추기 위해 서두름
- 지름길을 선택함
- 리팩터링을 건너뜀
- 모범 사례를 무시함
2. 매몰 비용 오류
한 번 코드를 작성하고 나면 우리는 그것을 쉽게 바꾸려 하지 않습니다. 그 이유는 다음과 같습니다.
- 이미 많은 시간을 투자했기 때문에
- 자신의 해결책에 감정적으로 집착하게 되기 때문에
- 기존 기능이 동작하지 않을까봐 두렵기 때문에
3. 복잡성 편향
우리는 종종 다음과 같은 행동을 합니다.
- 단순한 해결책을 지나치게 복잡하게 만듦
- 나중에 필요할지도 모를 기능을 미리 추가함
- 너무 이른 시점에 추상화를 시도함
- 절대 발생하지 않을지도 모를 예외 상황을 위해 코드를 작성함
4. 결정 피로와 인지 부하
신경과학 연구에 따르면, 우리의 뇌는 제한된 의사결정 능력을 가지고 있습니다. Diederich와 Trueblood(2018)*의 연구 결과는 다음과 같습니다.
- 개발자는 2시간 연속 코딩 후 오류율이 30% 증가함
- 컴포넌트에 상태 변수(state variable)가 하나 추가될 때마다 인지 부하가 37% 증가함
- 복잡한 컴포넌트는 멀티태스킹과 유사한 "신경 전환 비용(neural switching cost)"을 유발함
이러한 이유로 우리는 종종 다음과 같은 행동을 하게 됩니다.
- 올바른 추상화보다는 빠른 해결책을 선택함
- 기존 로직을 리팩터링 하기보다는 코드를 중복해서 작성함
- 문제를 즉시 해결하기보다는 TODO 주석만 남겨둠
Sweller(1988)의 인지 부하 이론(Cognitive Load Theory)*에 따르면, 작업 기억(working memory)은 한 번에 4±1개의 정보 덩어리만 처리할 수 있습니다. 컴포넌트가 여러 관심사(데이터 페칭, 상태 관리, UI 렌더링 등)를 동시에 처리하게 되면 이 한계를 초과하여 코드 품질이 저하됩니다.
악순환 끊어내기
1. 작게 시작하고 점진적으로 발전시키기
위에서 본 거대한 컴포넌트 대신, 다음과 같이 단순하게 시작할 수 있습니다.
const UserDashboard = () => {
const { users, loading, error } = useUsers();
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;
return <UserList users={users} />;
};
그런 다음, 필요에 따라 점진적으로 기능을 추가합니다:
const UserDashboard = () => {
const { users, loading, error } = useUsers();
const { filter, setFilter } = useFilter();
const { sortBy, setSortBy } = useSort();
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;
return (
<div>
<FilterBar filter={filter} onFilterChange={setFilter} sortBy={sortBy} onSortChange={setSortBy} />
<UserList users={users} />
</div>
);
};
2. 심리적 안전감 조성하기
- 리팩토링을 위한 시간을 따로 마련하기
- 실수를 인정하는 것이 괜찮다는 분위기 만들기
- 코드 리뷰를 장려하기
- 클린 코드의 좋은 예시를 칭찬하고 공유하기
3. "보이스카웃 규칙" 활용하기
코드를 발견했을 때보다 더 깨끗하게 만들어두세요. 이는 다음을 의미합니다.
- 작은 문제들을 발견하는 즉시 수정하기
- 점진적으로 리팩터링하기
- 작업하면서 문서화하기
- 팀원들과 지식을 공유하기
실용적인 전략들
1. 5분 규칙
코드를 작성하기 전에 스스로에게 물어보세요.
- 이것이 작동할 수 있는 가장 단순한 방법은 무엇일까?
- 5분 안에 이 문제를 해결할 수 있을까?
- 내가 해야 할 최소한의 작업은 무엇일까?
2. "코드 리뷰" 테스트
코드를 커밋하기 전에 스스로에게 물어보세요.
- 이 코드를 코드 리뷰에서 보여도 자부심을 느낄 수 있을까?
- 이것이 이 문제를 해결하는 가장 깔끔한 방법일까?
- 이 코드를 더 좋게 만들 수 있는 방법은 무엇일까?
3. "미래의 나" 테스트
다음을 고려해 보세요.
- 미래의 내가 이 코드를 이해할 수 있을까?
- 미래의 내가 이 코드를 쉽게 수정할 수 있을까?
- 미래의 내가 과거의 나에게 이 코드를 작성해 줘서 고마워할까?
그리고 이것도 같이 고려해 보세요.
- 이 컴포넌트의 책임을 한 문장으로 설명할 수 있을까?
- 새로운 기능을 추가하려면 3개 이상의 파일을 수정해야 할까?
- '내가 여기서 뭘 생각했던 거지?'라는 코드 패턴이 있나?
결론
클린 코드 작성은 단순히 기술적 역량의 문제가 아닙니다. 우리의 심리적 편향을 이해하고 이를 극복하려는 노력이 필요합니다. 이러한 패턴들을 인식하고 제시된 전략들을 실행함으로써, 우리는 더 나은 코드를 작성하고 더 유지보수하기 쉬운 애플리케이션을 만들 수 있습니다.
기억하세요. 클린 코드는 완벽함을 추구하는 것이 아닙니다. 작고 꾸준한 개선을 이루어내고, 지름길을 택하려는 우리의 자연스러운 경향에 주의를 기울이는 것입니다.
추가 읽을거리
책과 개요
- Clean Code by Robert C. Martin
- The Psychology of Computer Programming by Gerald M. Weinberg
- Thinking, Fast and Slow by Daniel Kahneman
- Cognitive Load (Wikipedia overview)
- The Science of Developer Productivity (ACM Queue)
학술 연구
- Ego Depletion: Is the Active Self a Limited Resource? by Baumeister et al. (1998). Journal of Personality and Social Psychology.
- Task Switching by Monsell (2003). Trends in Cognitive Sciences.
- Cognitive Architecture and Instructional Design by Sweller et al. (1998). Educational Psychology Review.
- Cognitive Load Theory by Sweller (1988). Educational Psychology Review.
- Multi-attribute Choice Experiments by Diederich and Trueblood (2018). Journal of Mathematical Psychology. (이 논문의 온라인 링크는 찾지 못했습니다, 죄송합니다!)
🚀 한국어로 된 프런트엔드 아티클을 빠르게 받아보고 싶다면 Korean FE Article을 구독해주세요!
'개발 > 번역' 카테고리의 다른 글
| [번역] CSS 길이 단위 이해하기 (1) | 2025.09.02 |
|---|---|
| [번역] React.memo 완벽 해부: 언제 쓸모 있고 언제 쓸모없는가 (10) | 2025.08.11 |
| [번역] 리액트의 개방-폐쇄 원칙: 확장 가능한 컴포넌트 만들기 (3) | 2025.06.29 |
| [번역] 리액트에서의 의존성 역전: 테스트하기 쉬운 컴포넌트 만들기 (0) | 2025.05.25 |
| [번역] 일반적인 리액트 라이브러리 아키텍처 (0) | 2025.04.17 |