[Small-tip] 클린코드 핵심 원리 (Inflearn)

Mubby·2024년 3월 20일
0

소소팁(9xx)

목록 보기
5/5
post-thumbnail

인프런 어쩔코딩님의 클린코드 찍먹 강의 내용을 정리했습니다.


클린코드란?

팀 구성원 누구나 쉽게 이해할 수 있는 코드

같이 일하고 싶은 개발자가 되고 싶다면 클린코드 개념을 배워야 합니다.
클린코드를 배우면 이해하기 쉽고, 유지보수 하기 쉬운 코드를 만들 수 있습니다.

클린코드 찍먹 강의 계획

  • 변수 (Variable)
    1. 커뮤니케이션의 기본
    2. 의도를 분명히 밝혀라.
    3. Magic Number & Hard Coded Value
  • 함수 (Function)
    1. Pure Function (순수 함수)
    2. Side-effect (부수 효과)
    3. 한 가지의 일만 하자.

예제는 Typescript 로 설명되어 있습니다.


변수1. 커뮤니케이션의 기본

코드 = 커뮤니케이션

설명을 통하지 않고 코드로만 대화해도 이해할 수 있어야 한다.

커뮤니케이션 실패 사례

const d = ""; // 경과 시간

이런 코드를 만드는 사람이 하는 생각
 - 난 잘 이해되는데?
 - 어차피 코드 위아래를 보면 어떤 의미인지 알 수 있지 않아?

But
 - 다른 개발자들이 이 코드를 보면 뭔지 이해가 될까?
 - 시간이 지나고 직접 만든 당사자가 봐도 이해 못할 수도 있다.

변수2. 의도를 명확히 밝혀라.

변수 이름만 봐도 어떤 역할을 하는지 알 수 있어야 한다.

변수명은 구체적으로 작성하자.

Bad
 작성자만 알 수 있는 약어로 쓰여져 있다.

if (t === DaysType.Day) {
}
// t가 무슨 의미일까요?

const cbm = (d: Days[]) => d.map((dd: Days) => dd.month());
// cbm, d, dd 가 무슨 의미일까요?

const sd = data.get("someDateData");
// sd가 무슨 의미일까요?

Good
 변수명을 구체적으로 작성해 줌으로써 다른 개발자들도 이해하기가 수월하다.

if (selectedDay === DaysType.Day) {
}
// t => selectedDay (선택한 요일)

const getSelectedMonth = (selectedDays: Days[]) =>
selectedDays.map((item: Days) => item.month());
// cbm => getSelectedMonth (선택한 Month 를 조회하는 함수)
// d => selectedDays (선택된 요일들)
// dd => item (selectedDays 구성요소)

const selectedDate = data.get("someDateData");
// sd => selectedDate (선택된 날짜)

주석은 해결책이 아니다.

Bad
 주석을 써서 부족한 설명을 보완한다?

const t = ""; // 유저가 선택한 날짜

 아래처럼 주석이 쓰여있는 범위를 벗어난다면?

/**
	* 이
	* 후
	* 다
	* 른
	* 코
	* 드
	* 들
	*/
if (t === DaysType.Day) {
}
// t가 무슨 의미인지 확인하려면 t가 선언된 부분의 주석을 확인해야한다.

 if 문에서 t를 읽은 개발자는 t가 어떤 의미인지 알 수 있을까?

Good
 의미 있고 구체적인 변수명으로 변경해주면 주석이 필요 없다.
   ⇒ 좋은 코드일수록 주석이 필요 없음

const selectedDate = "";
/**
	* 이
	* 후
	* 다
	* 른
	* 코
	* 드
	* 들
	*/
if (selectedDay === DaysType.Day) {
}
// t => selectedDay

중언부언 하지 마라.

Bad
 일반적인 단어들의 조합으로 변수가 어떤 역할을 하는지 알 수 없다.

const stringArray = // 댓글 목록

Good
 대상의 특징을 명확하게 드러낼 수 있는 용어를 사용해야 한다.

const replyComments = // 주석 필요 없음

정리

  • 작성자만 알 수 있는 약자를 사용하지 말자.
  • 주석을 덧대서 설명하기보다 구체적인 변수명을 사용하자.
  • 역할을 설명할 수 있는 명확한 단어를 쓰자.

변수3. Magic Number & Hard Coded Value

Magic Number 금지

의미가 부여되어있지 않은 하드 코딩 된 숫자를 쓰지 말자.

Bad 1.
 왜 140 이라는 숫자가 기준이지? 무슨 의미지?

if (comments.length > 140) {
	throw new Error("exceed comment length");
}

Good 1.
 변수명만 보고도 140이라는 숫자가 어떤 의미인지 알 수 있다.

const twitterCommentLimitCount = 140;
if (comments.length > twitterCommentLimitCount) {
	throw new Error("exceed comment length");
}

Bad 2.
 숫자가 여러 코드에서 중복됨 기준이 140 자에서 280 자로 글자가 늘어난다면?

if (comments.length > 140) {
	throw new Error("exceed comment length");
}
// ... 이하 코드 생략

if (comments.length < 140) {
	// ...
}

Good 2.
 변수에 처음 할당된 숫자만 변경해주면 일괄 변경 된다.

const twitterCommentLimitCount = 280; // 140 => 280
if (comments.length > twitterCommentLimitCount) {
	throw new Error("exceed comment length");
}
// ... 이하 코드 생략

if (comments.length < twitterCommentLimitCount) {
	// ...
}

Bad 3.
 Array 에 특정 Index 조회할 때도 단순히 숫자만 사용한다면?

travelCities[0].someValue;

 특정 index number 가 왜 쓰였는지 이해가 어려운 경우도 있다.

Good 3.
 "여행한 도시 중에서 출발 도시를 조회하는 코드군!"

const departureCityIdx = 0;
travelCities[departureCityIdx].someValue;

 또는 ES6 문법 중 array destructuring 을 사용해서 표현하자.

const [departureCity] = travelCities;
departureCity.someValue;

Hard Coded Value 는 변수 또는 열거형으로

Bad
 BTC 가 어떤 의미인지 바로 파악이 어렵다.

if (someCode === "BTC")

Good
 변수로 변경해줘서 이해도를 높일 수 있다.

const bitcoinCode = "BTC"
if (someCode === bitcoinCode)

 코드가 여러 개일 때는 열거형(enum) 사용해서 코드를 간결하게 만들자.


함수를 잘 만든다는 것은?

깊게 들어가면
 - "이게 대체 무슨 말이지..?"
 - 추상화 수준?
 - OCP, SRP, 다형성? (책에서 언급된 단어들)

단순하고 구체적으로
 - 실전에서 함수를 잘 만들 수 있는 단순하지만 쉬운 방법
 - Pure Function
 - 함수는 한 가지 일만 한다.

함수1. Pure Function

Pure Function

특정 input 값에 대한 output 값이 항상 같아야 한다.

부수 효과가 없어야 한다.

순수하지 않은 함수
 impure 함수에 3을 넣으면 항상 10 이 나올까?

let outerValue = 7;
function impure(input: number) {
	return input + outerValue;
}
impure(3); // 10

 outerValue 값이 7에서 100으로 바뀐다면?

impure(3); // 10 -> 103

 impure 함수에 3을 넣으면 10이 나와야 하지만, 103이 나온다.

순수하지 않은 함수가 왜 문제일까?
 함수 바깥에서 쓰는 변수는 다른 곳에서도 쓰이는 변수일 확률이 크다.

let outerValue = 7;
function otherImpure(input: number) {
	if (input > outerValue) {
		return input;
	}
	return outerValue;
}
function impure(input: number) {
	return input + outerValue;
}
impure(3); // 10

 otherImpure 함수에서도 outerValue 를 사용하고 있음

만약 otherImpure 에서 쓰이는 outerValue 를 100으로 변경해야 한다면?
 "outerValue 를 100으로 바꾸면 되겠네?"

let outerValue = 100;
function otherImpure(input: number) {
	if (input > outerValue) {
		return input;
	}
	return outerValue;
}

 "아 근데 혹시 다른 데서도 쓰였나?"

function impure(input: number) {
	return input + outerValue;
}
impure(3); // 10 => 103

 결과적으로 bug 또는 기능 장애로 이어진다.

=>

Pure Function 으로 바꾸면
 함수에서 사용하는 값은 파라미터로 받도록 수정한다.

function pure(input: number, add: number) {
	return input + add;
}
impure(3, 7); // 10

 함수 바깥의 변수(outerValue)가 7에서 100으로 변경 돼도 영향이 없다.

let outerValue = 100;
function pure(input: number, add: number) {
	return input + add;
}
impure(3, 7); // 10

특정 input 값에 대한 output 값이 항상 같도록 함수를 만들자.

함수2. No Side-effect

Pure Function_Quick 복습
  특정 input 값에 대한 output 값이 항상 같아야 한다.
   = "함수 밖에" 생긴 변화가 함수 안에 영향을 미쳐서는 안된다.

let outerValue = 7;
function impure(input: number) {
	return input + outerValue;
}
impure(3); // 10

No Side-effect

부수 효과를 일으키지 않아야 한다.
  = "함수 안에" 생긴 변화가 함수 밖에 영향을 미쳐서는 안된다.

부수 효과를 발생 시키는 함수
 outerValue 를 특정 조건에서 1씩 증가 시키고 있다.

let outerValue = 7;
function impure(input: number) {
	if (input > outerValue) {
		return input;
	}
	outerValue++; // 1씩 증가 시킴
	return outerValue;
}

실제 상황에서 발생할 수 있는 문제
 유저가 입력한 패스워드가 맞는지 확인해주는 함수인데 User Session 을 초기화하는 작업도 하네?

checkPassword (password : string) {
	// 패스워드가 맞는지 체크해서 true/false 리턴
	if (isValid) {
		Session.init();
		return true;
	}
	return false;
}

checkPassword 함수를 삭제한다면?
 유저 세션을 초기화하는 부분도 같이 삭제된다.

유저 Session 생성 로직을 고쳐야 한다면?
 패스워드 체크 로직을 건드려서 bug 발생 시킬 수 있다.

=>

부수 효과를 발생 시키지 않도록 바꿔본다면
 checkPassword 함수 내에서 유저 Session 을 초기화하지 않는다.

checkPassword (password : string) {
	// 패스워드가 맞는지 체크해서 true/false 리턴
	if (isValid) {
		// session 부분은 밖으로 빼냄
	return true;
	}
	return false;
}
const isValid = checkPassword('cleanCode123');
if (isValid) {
	Session.init(); // 부수효과를 분리함
}

checkPassword 함수를 삭제한다면?
 유저 세션을 초기화하는 부분에 영향 없다.

유저 Session 생성 로직을 고쳐야 한다면?
 패스워드 함수에 영향을 미치지 않고 작업 가능하다.


부수 효과를 발생 시키지 않도록 함수를 만들자.

함수3. 한 가지의 일만 해야 한다.

함수는 한 가지의 일만 해야 한다.

여러가지 일을 하는 함수
 예시 함수는 두 가지 일을 하고 있다.

checkPassword (password : string) {
	if (isValid) {
		Session.init();
		return true;
	}
	return false;
}

  - 패스워드가 맞는지 체크
  - 유저 세션을 초기화

 그래서 더 적합한 함수 명은 checkPasswordAndInitSession 이다.


한 가지 일을 하는 함수
 패스워드만 체크하도록 함수를 변경한다.

// 패스워드가 맞는지 체크해서 true/false 리턴
checkPassword (password : string) {
	if (isValid) {
		return true;
	}
	return false;
}
const isValid = checkPassword('cleanCode123');
if (isValid) {
	// 유저 세션을 초기화
	Session.init();
}

단일 책임 원칙
 - Single-responsibility principle
 - 모듈, 객체, 함수에 다 적용 가능하다.
Tips
 - 함수 이름에 And 가 들어가게 되면 여러가지 일을 하는 함수라는 뜻


참고사이트:
어쩔코딩님의 클린코드 찍먹 강의_Inflearn

0개의 댓글