if else 조건문 리팩토링 하기 - 소셜 로그인
[Javascript 미세팁] if else 리팩토링 #1 (feat. 객체 + 함수)을 보고 정리한 내용입니다.
조건문 코드 개선해보기
네이버, 페이스북, 구글, 카카오 등 소셜 로그인 로직을 작성해보자.
■ if else문으로 작성해보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| const naverLogin = (id) => {
// 로직
return "네이버";
};
const kakaoLogin = (id) => {
// 로직
return "카카오";
};
const facebookLogin = (id) => {
// 로직
return "페이스북";
};
const googleLogin = (id) => {
// 로직
return "구글";
};
const socialLogin = (where, id) => {
let domain = "";
if (where === "naver") {
domain = naverLogin();
} else if (where === "kakao") {
domain = kakaoLogin();
} else if (where === "facebook") {
domain = facebookLogin();
} else if (where === "google") {
domain = googleLogin();
}
return `${domain} ${id}`;
};
console.log(socialLogin("naver", "hyemin"));
// "naver hyemin"
console.log(socialLogin("google", "hyemin"));
// "google hyemin"
|
- 가독성이 떨어지고,
facebook
으로 로그인하려면 where이 naver인지, 카카오인지 확인하고 facebook인지 확인하기때문에 성능에도 좋지 않다.
■ where의 값으로 switch문으로 바꿔 가독성과 성능 높이기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| const socialLogin = (where, id) => {
let domain = "";
switch (where) {
case "naver":
domain = naverLogin(id);
break;
case "kakao":
domain = kakaoLogin(id);
break;
case "facebook":
domain = facebookLogin(id);
break;
case "google":
domain = googleLogin(id);
break;
}
return `${domain} ${id}`;
};
console.log(socialLogin("naver", "hyemin"));
// "naver hyemin"
console.log(socialLogin("google", "hyemin"));
// "google hyemin"
|
- if-else보다는 가독성이 높아졌지만, break가 생겼다.
■ break를 없애기 위해 switch문을 별도의 함수로 분리하자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| const socialLoginMap = (where, id) => {
switch (where) {
case "naver":
return naverLogin(id);
case "kakao":
return kakaoLogin(id);
case "facebook":
return facebookLogin(id);
case "google":
return googleLogin(id);
}
};
const socialLogin = (where, id) => {
const domain = socialLoginMap(where);
return `${domain} ${id}`;
};
console.log(socialLogin("naver", "hyemin"));
// "naver hyemin"
console.log(socialLogin("google", "hyemin"));
// "google hyemin"
|
socialLoginMap
함수를 보면 규칙이 있는 것을 확인할 수 있고, return만 있기때문에 객체로 변경할 수 있다.
■ switch문이 들어있는 함수를 객체로 바꾸자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| const socialLoginMap = {
naver: naverLogin,
kakao: kakaoLogin,
facebook: facebookLogin,
google: googleLogin
};
const socialLogin = (where, id) => {
const domain = socialLoginMap[where](id);
return `${domain} ${id}`;
};
console.log(socialLogin("naver", "hyemin"));
// "naver hyemin"
console.log(socialLogin("google", "hyemin"));
// "google hyemin"
|
- 객체의 맵핑이 들어가있는 부분은 함수들로만 이루어져있고, socialLogin 함수는 함수 본연의 기능만 하고 있는 것을 확인할 수 있다. (역할 분리)
socialLoginMap
객체에 데이터를 몰아놓고, 실제 동작하는 부분은 별도의 socialLogin
함수로 존재
■ 비교해보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
// if-else문
const socialLogin = (where, id) => {
let domain = "";
if (where === "naver") {
domain = naverLogin();
} else if (where === "kakao") {
domain = kakaoLogin();
} else if (where === "facebook") {
domain = facebookLogin();
} else if (where === "google") {
domain = googleLogin();
}
return `${domain} ${id}`;
// 객체 사용하여 역할 분리한 로직
const socialLoginMap = {
naver: naverLogin,
kakao: kakaoLogin,
facebook: facebookLogin,
google: googleLogin
};
const socialLogin = (where, id) => {
const domain = socialLoginMap[where](id);
return `${domain} ${id}`;
};
|
- if else문보다 가독성이 높아지고, if-else에 비해 성능이 좋아짐
if else 조건문 리팩토링 하기 - 구간별 적용 사례 (계절)
- 3월 ~ 5월 : 봄
- 6월 ~ 8월 : 여름
- 9월 ~ 11월 : 가을
- 12월 ~ 2월 : 겨울
■ if문으로 작성하기
1
2
3
4
5
6
| const getSeason = (month) => {
if (month >= 3 && month <= 5) return "봄";
if (month >= 6 && month <= 8) return "여름";
if (month >= 9 && month <= 11) return "가을";
if (month >= 12 || month <= 2) return "겨울";
};
|
■ switch문으로 바꾸기
- 정수 형태로만 연속성을 가지기 때문에 값이 딱 12개만 존재
- 그래서 값별로 케이스를 전부 지정할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| const getSeason = (month) => {
switch (month) {
case 3:
case 4:
case 5:
return "봄";
case 6:
case 7:
case 8:
return "여름";
case 9:
case 10:
case 11:
return "가을";
case 12:
case 1:
case 2:
return "겨울";
}
};
|
간단하긴 하지만 정수였기때문에 위와 같은 코드를 작성할 수 있었다.
- 3개씩 나눠져있는 규칙을 바탕으로 변형을 주면 더 깔끔하게 작성할 수 있다.
■ 조금 더 깔끔하게 작성해보자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| const getSeason = (month) => {
const shiftedMonth = month - 3;
switch (month) {
case 0:
case 1:
case 2:
return "봄";
case 3:
case 4:
case 5:
return "여름";
case 6:
case 7:
case 8:
return "가을";
case 9:
case -1:
case -2:
return "겨울";
}
};
|
- 이렇게 작성해놓고 보니, -1, -2가 거슬린다…
■ -1, -2 처리하기
- -1 을 10으로, -2를 11로 만들려면 month - 3에 12를 더하면 된다.
- 하지만 전부에 12를 더하면 값이 12부터 시작할테니까 12를 더한 것에 대한 나머지를 구하면 된다.
1
2
3
4
5
6
| const month = (month - 3 + 12) % 12;
// (10 - 3 + 12) % 12 = 7
// (12 - 3 + 12) % 12 = 9
// (1 - 3 + 12) % 12 = 10
// (2 - 3 + 12) % 12 = 10
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| const getSeason = (month) => {
const shiftedMonth = (month + 9) % 12;
switch (month) {
case 0:
case 1:
case 2:
return "봄";
case 3:
case 4:
case 5:
return "여름";
case 6:
case 7:
case 8:
return "가을";
case 9:
case 10:
case 11:
return "겨울";
}
};
|
- 위의 코드를 보면
- case 0 ~ 2는 3으로 나눴을 때 몫이 0인 값
- case 3 ~ 6는 3으로 나눴을 때 몫이 1인 값
- case 7 ~ 8는 3으로 나눴을 때 몫이 2인 값
- case 9 ~ 11는 3으로 나눴을 때 몫이 3인 값
- 인것을 확인할 수 있다.
■ shiftedMonth를 3으로 나눠서 보다 짧게 코드를 작성해보자
1
2
3
4
5
6
7
8
9
10
11
12
13
| const getSeason = (month) => {
const shiftedMonth = Math.floor(((month + 9) % 12) / 3);
switch (month) {
case 0:
return "봄";
case 1:
return "여름";
case 2:
return "가을";
case 3:
return "겨울";
}
};
|
■ 정리된 코드를 객체로 추출해보자
1
2
3
4
5
6
7
8
9
10
11
| const seasonMap = {
0: "봄",
1: "여름",
2: "가을",
3: "겨울"
};
const getSeason = (month) => {
const shiftedMonth = Math.floor(((month + 9) % 12) / 3);
return seasonMap[shiftedMonth];
};
|
- seasonMap을 보면 0부터 시작하고, 1, 2, 3이 key값인 것을 확인할 수 있다.
■ 배열로 바꾸기
1
2
3
4
5
| const seasons = ["봄", "여름", "가을", "겨울"];
const getSeason = (month) => {
const shiftedMonth = Math.floor(((month + 9) % 12) / 3);
return seasons[shiftedMonth];
};
|
■ 조금 더 정리해보자
1. shiftedMonth가 한번 사용되니 return값에 직접 대입하기
1
2
3
4
| const seasons = ["봄", "여름", "가을", "겨울"];
const getSeason = (month) => {
return seasons[Math.floor(((month + 9) % 12) / 3)];
};
|
1
2
| const seasons = ["봄", "여름", "가을", "겨울"];
const getSeason = (month) => seasons[Math.floor(((month + 9) % 12) / 3)];
|
2. shiftedMonth도 분리시키기
1
2
3
| const seasons = ["봄", "여름", "가을", "겨울"];
const getSeasonIndex = (month) => Math.floor(((month + 9) % 12) / 3);
const getSeason = (month) => seasons[getSeasonIndex(month)];
|
비교해보기
1
2
3
4
5
6
7
8
9
10
11
12
| // if문
const getSeason = (month) => {
if (month >= 3 && month <= 5) return "봄";
if (month >= 6 && month <= 8) return "여름";
if (month >= 9 && month <= 11) return "가을";
if (month >= 12 || month <= 2) return "겨울";
};
// 코드를 분리한 로직
const seasons = ["봄", "여름", "가을", "겨울"];
const getSeasonIndex = (month) => Math.floor(((month + 9) % 12) / 3);
const getSeason = (month) => seasons[getSeasonIndex(month)];
|
- 이 영상을 찍은 유튜버는 if문이 복잡해보인다고 하였으나, 두개의 로직을 처음 보는 사람이라면 if문의 코드가 더 쉽게 읽힐 것이라는 생각이 들었다.
- 댓글에서도 if문을 사용해서 더 직관적으로 적는 것이 읽기 쉽다는 사람이 있었다.
getSeasonNameFromMonthNumber
과 같이 getSeasonIndex
함수명을 조금 더 직관적으로 보일 것 같다는 의견도 있었다. 하지만 함수명이 너무 길어서 작성하기 힘들 것 같다…
1
2
3
4
| const seasons = ["봄", "여름", "가을", "겨울"];
const getSeasonNameFromMonthNumber = (month) =>
Math.floor(((month + 9) % 12) / 3);
const getSeason = (month) => seasons[getSeasonNameFromMonthNumber(month)];
|
- 코드를 리팩토링하는 것에는 정답이 없고, 내가 속해 있는 팀별 성향에 맞게 작성하면 되는 것 같다.
- 어떤 사람은 if문에 작성된 코드가 더 쉽게 읽히고, 어떤 사람은 아래 코드가 더 효율적이라고 생각하기 때문이다.
정리
if else가 중복되면 switch case로 바꿔보자.
=> 나름의 규칙성을 발견하기 쉬워진다.
=> 규칙을 발견하면 이를 바탕으로 객체로 추출 가능하다!
객체를 함수 외부로 분리하면
공통로직 / 분기별 실제 동작 정의가 분리되어 읽기도 수월해지고, 수정사항이 생겨도 공통로직은 건드리지 않아도 되어 편리하다.
리팩토링할 소재를 발견할 수도 있고,
소재가 발견되지 않더라도 객체 형태로 전환한 그 자체로 이미 깨끗해진다.