함수의 분리 (역할과 책임이 다른 것을 명확히 파악하기)
// 여기서의 ... 스프레드 연산자가 아닌 내용의 축약을 나타냄
const someArray = [...]
//bad
someArray.filter((item)=>{ ... })
.map((item)=>{ ... })
.forEach((item)=>{ ... })
//good
someArray.filter(checkValid)
.map(itemToAnotherItem)
.forEach(doSomething)
const checkValid = (item) => { return boolean; }
const itemToAnotherItem = (item) => { ... return anotherItem; }
const doSomething = (item) => { ... }
//bad
{
return<>
{newArray.filter(...).map(return <>...</>)}
</>
}
//good
const newArray = someArray.filter(checkValid);
{
return <>
{newArray.map(return <>...</>)}
</>
}
함수의 분리는 여러 함수들의 나열을 통해 내가 어떤 동작을 하는지 정확하게 예측할 수 있게 하는 것에 의의가 있습니다.
한가지 일만 하게 하기 (단일 책임 원칙)
- 함수는 명확하게 한가지 동작만을 해야하고 이름도 그에 맞게 지어져야 합니다
// bad
const calculate = (n1, n2, op) => {
if (op == "*") {
return n1 * n2;
} else if (op == "+") {
return n1 + n2;
} else if (op == "-") {
return n1 - n2;
} else {
return n1 / n2;
}
};
//good
function add(n1, n2) {
return n1 + n2;
}
function minus(n1, n2) {
return n1 - n2;
}
function multiply(n1, n2) {
return n1 * n2;
}
function divide(n1, n2) {
if(n2 != 0) {
return n1 / n2;
} else {
console.log("Error: Division by zero is not allowed.");
return null;
}
}
명령(action)과 조회(get)은 명확히 다른 함수입니다.
arguments (함수 인수)
- 함수인수의 이상적인 개수는 0개입니다. 그 다음은 1개, 그 다음은 2개 …
- 최대한 두개를 넘지 않게 조절해주는 것이 좋다고 합니다.
- 함수 인수로 boolean 을 넘기지 않도록 해야합니다.
- boolean을 넘기면 내부에 if 문이 존재한다는 뜻이고 이는 두가지 일을 하고있다고 말하는 것 입니다.
//bad
function makeFullName(firstName, lastName, isEnglish) {
if (isEnglish) {
return lastName + firstName;
} else {
return firstName + lastName;
}
}
//good
let name = {
first: 'John',
last: 'Doe'
};
function makeKoreanFullName(name) {
return name.first + name.last;
}
function makeEnglishFullName(name) {
return name.last + name.first;
}
custom hook 사용하기 ( 비지니스 로직 분리하기 )
- 서버에서 데이터를 가져오는 작업을 해야 한다고 가정해 봅시다. 이 작업은 여러 컴포넌트에서 필요로 할 확률이 높겠죠? 그럴때, "서버에서 데이터를 가져오는" 이라는 공통된 로직을 컴포넌트 밖의 커스텀 훅으로 분리해 낼 수 있습니다.
- 그걸 아주 잘 보여주는 예시가 react-query 에서 쓰이는 useQuery, useMutation 같은 hook 입니다.
import { useState, useEffect } from 'react';
import axios from 'axios';
// 커스텀 훅
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const res = await axios.get(url);
setData(res.data);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
function Component() {
const { data, loading, error } = useFetch('https://api.myserver.com/data');
if (loading) return 'Loading...';
if (error) return 'Error!';
return <div>{data && data.map(item => <div key={item.id}>{item.name}</div>)}</div>;
}
이 hook을 사용하는 컴포넌트에서는 데이터를 가져오는 로직 자체를 신경 쓸 필요 없이, 데이터의 상태(로딩 중, 에러 발생, 데이터 로드 완료)만을 관리하면 됩니다.
custom hook을 사용하면 비지니스 로직을 컴포넌트 밖으로 분리해서 따로 관리하기 용이해지고, 재사용이 필요할 경우 위 hook처럼 범용적으로 만들어 여러 컴포넌트에서 재사용할 수 있습니다.
파일 분리하고 파일 이름 명확하게 짓기
1. 파일 분리
- 파일은 그 안에 있는 기능이나 목적에 따라 분리하는 게 좋습니다. 컴포넌트나 서비스, 훅 등이 각각의 파일에 위치하면 좋겠죠. 그리고 관련된 파일들은 같은 폴더에 묶어 놓는 것도 깔끔하게 정리하는 데 도움이 됩니다.
2. 파일 이름 짓기
- 파일의 이름은 해당 파일이 무슨 역할을 하는지 바로 알 수 있도록 직관적이어야 합니다.
- 'useFetch.js'라는 커스텀 훅 파일은 네트워크 요청을 처리하는 훅을 정의하고 있다는 걸 암묵적으로 알려주죠.
일관된 네이밍 컨벤션 사용하기
- 프로젝트 전반에 걸쳐 일관된 네이밍 컨벤션을 사용하는 것도 중요합니다. 이 규칙은 파일 이름뿐만 아니라 폴더 이름, 변수 이름 등에도 적용됩니다. 컴포넌트 파일은 대문자로 시작('MyComponent.js'), 훅 파일은 'use'로 시작('useFetch.js'), 서비스 파일은 해당 서비스의 기능을 나타내는 이름('apiService.js') 등으로 규칙을 정하면 좋겠죠.
중첩된 폴더 구조 만들기
- 프로젝트가 커지고 복잡해질 경우, 폴더를 여러 단계로 중첩하여 구조를 만드는 것이 유용합니다. 이렇게 하면 관련된 코드들을 명확하게 그룹화할 수 있습니다.
/src
/components
/Button
index.js
Button.test.js
/List
index.js
List.test.js
/hooks
useFetch.js
useLocalStorage.js
/services
apiService.js
authService.js
이런 식으로 파일과 폴더를 잘 분리하고, 이름을 명확하게 짓는다면, 코드를 이해하고 찾아보고 수정하는 것이 훨씬 수월해집니다.
폴더 구조에는 정답이 없습니다. 내가 설계한대로 사용자를 이동시킬 수 있다면 그것은 명확하고 좋은 폴더구조가 되겠죠?내가 파일을 점점 찾기 힘들어진다면 명확한 분리를 하고있는지에 대한 고민도 같이 해보면 좋을 것 같습니다.
비슷한 것은 곁에 두기 ( 응집도 높이기 )
- 비슷한 코드가 모여있어서 파악하기 용이한 것을 개발자들은 응집도가 높다. 라고 표현합니다.
- 위에서 썼던것처럼 파일을 분리해서 잘 찾도록 묶어둔다던지, custom hook 으로 필요한 부분의 로직들만 모아둔다던지, 부정조건을 사용할때 조건문 바로 위에서 긍정조건으로 변경해둔다던지 하는 것들이 관련된 코드가 서로 가까이 있도록 만드는 응집도를 높여주는 스킬입니다.
- 이런 방식으로 비슷한 것은 곁에 두고, 응집도를 높이면 코드의 가독성은 물론 연관 코드를 빠르게 찾을 수 있기 때문에 유지보수성이 크게 향상됩니다.
'Clean Code' 카테고리의 다른 글
클린코드 - PR 리뷰 할 때 활용하기 (1) | 2023.11.22 |
---|---|
클린코드 - 추상화와 구체화 (1) | 2023.11.22 |
클린코드 - 조건과 탈출 (2) | 2023.11.21 |
클린코드 - 의미 있는 변수명 짓기 (0) | 2023.11.21 |
클린코드 - 클린 코드는 무엇이고 왜 써야 하는가? (0) | 2023.11.21 |