인프런 어쩔코딩님의 클린코드 찍먹 강의 내용을 정리했습니다.
팀 구성원 누구나 쉽게 이해할 수 있는 코드
같이 일하고 싶은 개발자가 되고 싶다면 클린코드 개념을 배워야 합니다.
클린코드를 배우면 이해하기 쉽고, 유지보수 하기 쉬운 코드를 만들 수 있습니다.
클린코드 찍먹 강의 계획
예제는 Typescript 로 설명되어 있습니다.
코드 = 커뮤니케이션
설명을 통하지 않고 코드로만 대화해도 이해할 수 있어야 한다.
const d = ""; // 경과 시간
이런 코드를 만드는 사람이 하는 생각
- 난 잘 이해되는데?
- 어차피 코드 위아래를 보면 어떤 의미인지 알 수 있지 않아?
But
- 다른 개발자들이 이 코드를 보면 뭔지 이해가 될까?
- 시간이 지나고 직접 만든 당사자가 봐도 이해 못할 수도 있다.
변수 이름만 봐도 어떤 역할을 하는지 알 수 있어야 한다.
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 = // 주석 필요 없음
의미가 부여되어있지 않은 하드 코딩 된 숫자를 쓰지 말자.
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;
Bad
BTC 가 어떤 의미인지 바로 파악이 어렵다.
if (someCode === "BTC")
Good
변수로 변경해줘서 이해도를 높일 수 있다.
const bitcoinCode = "BTC"
if (someCode === bitcoinCode)
코드가 여러 개일 때는 열거형(enum) 사용해서 코드를 간결하게 만들자.
깊게 들어가면
- "이게 대체 무슨 말이지..?"
- 추상화 수준?
- OCP, SRP, 다형성? (책에서 언급된 단어들)
단순하고 구체적으로
- 실전에서 함수를 잘 만들 수 있는 단순하지만 쉬운 방법
- 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 값이 항상 같도록 함수를 만들자.
Pure Function_Quick 복습
특정 input 값에 대한 output 값이 항상 같아야 한다.
= "함수 밖에" 생긴 변화가 함수 안에 영향을 미쳐서는 안된다.
let outerValue = 7;
function impure(input: number) {
return input + outerValue;
}
impure(3); // 10
부수 효과를 일으키지 않아야 한다.
= "함수 안에" 생긴 변화가 함수 밖에 영향을 미쳐서는 안된다.
부수 효과를 발생 시키는 함수
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 생성 로직을 고쳐야 한다면?
패스워드 함수에 영향을 미치지 않고 작업 가능하다.
부수 효과를 발생 시키지 않도록 함수를 만들자.
함수는 한 가지의 일만 해야 한다.
여러가지 일을 하는 함수
예시 함수는 두 가지 일을 하고 있다.
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