JS LAB
JS 7.3 · 7부. 실무 준비

코드를 읽기 좋게

이름 짓기와 함수 분리, 조기 반환을 다룹니다.

예상 학습 시간 15분 실행 단추가 있는 예제는 고쳐서 실행해 볼 수 있습니다 난이도 중급
개념 설명

코드는 쓰는 시간보다 읽는 시간이 깁니다

한 번 작성한 코드를 여러 번 읽고 고칩니다. 읽는 사람은 대개 몇 달 뒤의 자신입니다.

여기서는 바로 오늘 적용할 수 있는 넷만 봅니다. 동작은 그대로이고 읽기만 달라집니다.

  • 이름을 뜻대로 짓습니다.
  • 함수 하나가 한 가지 일만 하게 나눕니다.
  • 맞지 않는 경우를 먼저 걸러 냅니다.
  • 주석은 를 적습니다.
최소 예제 · 편집 가능

이름이 설명을 대신합니다

고치기 전
function f(a, b) {
	return a - a * b / 100;
}

console.log(f(10000, 15));
출력
8500
고친 뒤
function applyDiscount(price, percent) {
	return price - price * percent / 100;
}

console.log(applyDiscount(10000, 15));
출력
8500

결과는 같습니다. 다만 뒤엣것은 주석 없이도 무엇인지 알 수 있습니다.

  • 무엇인지를 적습니다 — a ✕ → price
  • 함수는 동사로 시작합니다 — applyDiscount, loadUser
  • 참·거짓은 is · has 로 시작합니다 — isMember
  • i 는 반복문 안에서만 씁니다. 그 밖에서는 뜻이 없습니다.
상세 사용법

한 가지 일만 하게

아래 함수는 세 가지를 합니다 — 거르고, 계산하고, 화면 문구를 만듭니다. 그래서 이름을 붙이기도 어렵습니다.

고치기 전
function process(products) {
	let sum = 0;

	for (const p of products) {
		if (p.stock > 0) {
			sum += p.price;
		}
	}

	return `합계 ${sum}원`;
}

const products = [
	{ name: "연필", price: 1000, stock: 3 },
	{ name: "공책", price: 2000, stock: 0 }
];

console.log(process(products));
출력
합계 1000원
고친 뒤
const inStock = products => products.filter(p => p.stock > 0);

const totalPrice = products => products.reduce((sum, p) => sum + p.price, 0);

const won = amount => `합계 ${amount}원`;

const products = [
	{ name: "연필", price: 1000, stock: 3 },
	{ name: "공책", price: 2000, stock: 0 }
];

console.log(won(totalPrice(inStock(products))));
출력
합계 1000원

셋으로 나누니 각각 이름을 붙일 수 있고, 따로 시험할 수 있고, 다른 데서도 쓸 수 있습니다. 3부의 배열 메서드가 그대로 쓰였습니다.

상세 사용법

맞지 않는 경우를 먼저 걸러 냅니다

중첩이 깊어지면 어느 조건 안에 있는지를 계속 세어야 합니다. 아닌 경우를 먼저 끝내면 중첩이 사라집니다. 이것을 조기 반환 이라고 합니다.

고치기 전
function checkout(user, cart) {
	if (user) {
		if (user.isMember) {
			if (cart.length > 0) {
				return "결제합니다";
			} else {
				return "장바구니가 비었습니다";
			}
		} else {
			return "회원만 결제할 수 있습니다";
		}
	} else {
		return "로그인이 필요합니다";
	}
}

console.log(checkout({ isMember: true }, ["연필"]));
console.log(checkout(null, []));
출력
결제합니다 로그인이 필요합니다
고친 뒤
function checkout(user, cart) {
	if (!user) return "로그인이 필요합니다";
	if (!user.isMember) return "회원만 결제할 수 있습니다";
	if (cart.length === 0) return "장바구니가 비었습니다";

	return "결제합니다";
}

console.log(checkout({ isMember: true }, ["연필"]));
console.log(checkout(null, []));
출력
결제합니다 로그인이 필요합니다

마지막 줄이 정상인 경우입니다. 위의 것들은 모두 예외입니다. 4.7 과 5.6 에서 이미 이렇게 적었습니다.

상세 사용법

주석은 왜를 적습니다

쓸모없는 주석
// price 에 0.9 를 곱합니다
const sale = price * 0.9;
쓸모있는 주석
// 회원 할인 10% 는 계약상 소수점을 버립니다.
// 반올림하면 총액이 청구서와 1원씩 어긋납니다.
const sale = Math.floor(price * 0.9);

무엇을 하는지는 코드가 이미 말합니다. 주석에는 코드가 말할 수 없는 것을 적으세요 — 까닭, 정한 배경, 이렇게 하지 않으면 생기는 일.

고칠 때 함께 고치지 않은 주석은 틀린 설명이 되어 없느니만 못합니다.

흔한 실수

처음에 자주 걸리는 것

1. 이름을 줄여 적습니다

usrNm, tmp, data2 는 몇 글자 아끼고 읽는 시간을 훨씬 더 씁니다. 길어도 뜻이 분명한 편이 낫습니다.

2. 나누기만 하고 이름을 대충 짓습니다

process1, handleData 로 나누면 오히려 찾기 어려워집니다. 이름을 붙이기 어렵다면 아직 한 가지 일이 아닌 것입니다.

3. 고치면서 동작을 함께 바꿉니다

읽기 좋게 고치는 일과 기능을 바꾸는 일은 따로 합니다. 함께 하면 무엇 때문에 깨졌는지 알 수 없습니다.

실습 문제

직접 해보기

1. 이름 고치기 난이도 하

아래 이름들을 뜻이 분명하게 고쳐 보세요.

const d = 30; const flag = true; function get(x) { } const arr2 = [];
const discountPercent = 30; const isLoggedIn = true; function loadUser(id) { } const selectedItems = [];
2. 조기 반환으로 고치기 난이도 중

아래 함수를 조기 반환으로 고쳐 중첩을 없애세요. 동작은 그대로여야 합니다.

function grade(score) { if (typeof score === "number") { if (score >= 0 && score <= 100) { return score >= 60 ? "합격" : "불합격"; } else { return "범위를 벗어났습니다"; } } else { return "숫자가 아닙니다"; } }
function grade(score) { if (typeof score !== "number") return "숫자가 아닙니다"; if (score < 0 || score > 100) return "범위를 벗어났습니다"; return score >= 60 ? "합격" : "불합격"; } console.log(grade(80)); console.log(grade(120)); console.log(grade("팔십"));
요약

이 단원의 정리

  • 이름은 무엇인지 적습니다. 함수는 동사로, 참·거짓은 is · has 로 시작합니다.
  • 함수 하나가 한 가지 일만 하게 나눕니다. 이름을 붙이기 어렵다면 아직 하나가 아닙니다.
  • 맞지 않는 경우를 먼저 return 하면 중첩이 사라집니다.
  • 주석에는 무엇이 아니라 를 적습니다.
  • 읽기 좋게 고치는 일과 기능을 바꾸는 일은 따로 합니다.