코딩 잘하는 팁 세가지, DRY, KISS, YAGNI _ 유뷰트 드림코딩

라용·2022년 10월 1일
1

유튜브 드림코딩 영상을 보고 정리한 내용입니다.

DRY - Don't Repeat Yourself

반복하지 말라는 의미입니다. 상반되는 단어로 WET 이 있습니다. WET 은 매번 작성하고(Write Every Time) 모든 걸 두 번씩 작성하고(Write Everything Twice) 모든 사람들의 시간을 낭비하는(Waste Everyone's Time) 방식입니다. 코드를 볼 때 기능이나 로직이 반복해서 등장한다면 DRY 해야 합니다. DRY 하게 한 곳에만 로직을 작성하고 여러 곳에서 가져다 쓴다면 유지 보수에 용이합니다.

// bad
function greetings(user) {
	return `hi ${user.firstName} ${user.lastName}`
}

function goodbye(user) {
	return `bye ${user.firstName} ${user.lastName}`
}

위 코드에는 유저의 이름이 반복해서 들어갑니다. 만약 middleName 을 추가한다면 각각 추가해주어야 합니다. 이를 DRY 하게 바꿔주고 싶다면 유저의 풀네임을 리턴하는 함수를 따로 만들어 사용할 수 있습니다.

// good
function greetings(user) {
	return `hi ${user.fullName()}`;
}

function goodbye(user) {
	return `bye ${user.fullName()}`
}

class User {
	fullName() {
		return `${this.firstName} ${this.lastName}`
	}
}

만약 아래와 같은 함수가 있다면, DRY 하게 수정해야 할까요? 꼭 그렇진 않습니다. 드라이는 특정 코드의 중복만 얘기하는 것이 아니라 로직이나 의도등이 중복되지 않는 것을 의미합니다. 아래 코드는 하나의 함수에서 존재하느 코드이고 다른 곳에서 중복적으로 사용하진 않습니다.

function validateBody(body) {
	if(!body.id) {
		throw new Error('Validation failed. The attribute id is missing.');
	}
	if(!body.name) {
		throw new Error('Validation failed. The attribute id is missing.');
	}
	if(!body.count) {
		throw new Error('Validation failed. The attribute id is missing.');
	}
}

// 위 코드를 아래처럼 바꿀 수 있지만, 꼭 그럴 필요는 없음

function validateBody(body) {
	const attibutes = ['id', 'name', 'count'];
	attributes.forEach(attribute => {
		if (!body[attibute]) {
			throw new Error(
				`Validation failed. The ${attribute} id is missing.`
			);
		}
	})
}

KISS - Keep It Simple, Stupid

심플하고, 멍청하게 유지하자는 의미입니다. 대부분의 시스템은 심플하게 만들었을 때 잘 동작합니다. 시스템을 디자인할 때 심플함을 목적으로 하고 불필요한 복잡성을 피해야 합니다. 10줄자리 코드를 1줄로 바꾸기 위해 화려한 테크닉으로 가독성을 떨어뜨리기 보다는 누구나 이해할 수 있게 심플하게 작성하는 게 좋습니다. 함수의 이름, 매개변수, 구현사항 코드를 보고 이해할 수 있게 한가지 기능을 수행하는 함수를 작성하는 게 좋습니다. 한가지 책임만 담당하는 클래스를 만드는 게 좋고, ui 를 담당하는 컴포넌트에는 별도의 비즈니스 로직을 더하지 않는 게 좋습니다. 키스는 전체적인 시스템을 개선하게 도와줍니다.

//배열을 받아서 isEvne 이 true 면 처음으로 나오는 짝수 값을 아니면 홀수의 값을 리턴하는 함수

function getFirst(array, isEvne) {
	return array.find( x => (isEvne ? x % 2 === 0 : x % 2 !== 0));
}

// 위 함수는 한 줄로 간결하지만 한 눈에 이해하기 어려울 수 있음
// 조금 풀어서 쓰면 아래와 같음

function getFirst(array, isEven) {
	if(isEven) {
		return array.find(x => x % 2 === 0);
	} else {
		return array.find(x => x % 2 !== 0);
	}
}

// 하지만 함수에 전달되는 인자의 값이 true 인지 false 인지에 따라 다른 동작을 하게 만드는 것은 심플하지 않음
// 함수명과 인자만 보고 그 역할과 리턴값을 예상할 수 있게 나눈다면,

function getFirstOdd(array) {
	return array.find(x => x % 2 !== 0);
}

function getFirstEven(array) {
	return array.find(x => x % 2 === 0);
}

두가지 기능을 수행하는 함수는 좋지 않습니다. 각각의 기능을 수행하는 함수로 나누는 게 좋습니다. 조금 더 복잡한 예제를 살펴보면,

class UserOrderService {
	userDb;
	orderDb;
	paymentClient;
	processUserOrder(userId, orderId) {
		const user = userDb.select(/*db query*/);
		if (!user) {
			throw Error('...');
		}
		const order = orderDb.select(/*db query*/);
		if (!order) {
			throw Error('...');
		}
		paymentClient
			.connect(/*url*/)
			.then(/*retry*/)
			.catch(/*retry*/);
		orderDb.updateOrder(order, PAID);
	}
}

위 클래스는 유저 데이터에 접속하고 결제 상품 데이터에 접속하고 각각 에러가 없으면 이를 결제로 이동해서 이런저런 처리를 하고 해당 정보를 주문 창에 업데이트까지 해주어야 합니다. 이를 KISS 하게 수정해본다면, 개별적으로 만들어 볼 수 있습니다.

class UserService {
	userDb;
	getUser() {
		return userDb.select(/*db query*/);
	}
}

class OrderService {
	orderDb;
	createOrder(user, product) {}
	getOrder(orderId) {
		return orderDb.select(/*db query*/)
	}
	updateOrder(order) {
		orderDb.updateOrder(order, PAID);
	}
}

class PaymentsService {
	paymentClient;
	processPayment(orderRequest) {
		return paymentClient
			.connect(/*url*/)
			.then(/*process payment*/)
			.catch(/*retry*/);
	}
}

이 외에도 UI 를 그려주는 코드와 기능을 구현하는 코드를 분리해서 작성하는 것이 좋습니다.

YAGNI - You Ain't Gonna Need It

필요없는 기능을 작성하지 말라는 의미입니다. 필요하지 않은 기능, 지금 당장 사용하지 않는 기능, 지나치게 미래지향적인 기능은 만들지 않는 게 좋습니다. 코드를 깨끗하게 변경이 쉽게 유지보수 용이하게 확장성 있게 작성해야 합니다.

function deleteuser(id, softDelete = false) {
	if (softDelete) {
		return this._softDelete(id);
	}
	return db.removeById(id);
}

// 위 코드는 사용자를 데이터 베이스에서 지우는 것을 넘어 sortDelete 를 만들어서 지워졌다고 마크만 해두는 것
// 미래에 필요하지 않을 기능 때문에 복잡성을 추가하는 것은 좋지 않다.
// 만약 넣는다면 다른 코드에서도 해당 기능으로 업데이트가 필요함
// 아래처럼 간단하게 작성

function deleteUser(id) {
	return db.removeById(id);
}
profile
Today I Learned

0개의 댓글