[Part 1 코드 정리법]
09 설명하는 상수
10 명시적인 매개변수
11 비슷한 코드끼리
12 도우미 추출
13 하나의 더미
14 설명하는 주석
15 불필요한 주석 지우기
[Part 2 관리]
16 코드 정리 구분
Part 1 코드 정리법
09 설명하는 상수
- 리터럴 상수(literal constant)는 상징적인 상수(symbolic constant)로 만들기
=> 리터럴 상수? 소스 코드에 기록된 텍스트 표현한 것
=> 상징적인 상수? 변수처럼 고정 값 클래스 중 하나를 취할 수 있는 기호를 써서 상수를 정의한 것
// 코드정리 전
if response.code = 404
// 코드정리 후
PAGE_NOT_FOUND := 404
if response.code = PAGE_NOT_FOUND
- 같은 리터럴 상수가 두 곳에서 쓰일 때 다른 의미로 쓰이는지 확인
=> 한번에 바뀌어야 하거나 함께 이해해야 하는 상수들을 한곳에 모아두고, 다른 이유로 묶인 변수들은 분리
=> ONE = 1과 같이 빈약한 의미를 가지는 상수에 대해서는 굳이 상징적인 상수로 정리할 필요는 없음
// 나쁜 예시
ONE = 1
[BOLEE 질문] 1, 2번 코드 중 더 나은 코드는? 이유는?
1번
return await this.boardService.save(id);
2번
const board = await this.boardService.save(id);
return board
10 명시적인 매개변수
- 루틴에서 매개변수를 명시적으로 전달하지 않으면 코드를 읽을 때 어떤 데이터가 필요한지 알기 어려움
=> 이런 경우 이후에 매개변수를 변경해 암묵적으로 사용하는 일이 발생할 수 있음
*루틴? 하나의 목적에 도달하기 위한 개별적인 함수나 프로시저, 메서드
// 맵에서 매개변수가 블록으로 전달되는 경우가 흔히 발생함
// 코드정리 전
params = { a: 1, b: 2 }
foo(params)
function foo(params) // 이후에 params.a, params.b의 형태로 사용
// 코드정리 후
function foo(params)
foo_body(params.a, params.b)
function foo_body(a, b) // 이후에 a, b의 형태로 사용
- [BOLEE] 코드 더보기
코드정리 전 | 코드정리 후 | |
TypeScript | class UserService { private currentUser: User; constructor(user: User) { this.currentUser = user; } getUserInfo() { // currentUser가 어디서 오는지 명확하지 않음 return this.currentUser.info; } } const user = new User('John Doe', 'john@example.com'); const userService = new UserService(user); console.log(userService.getUserInfo()); |
class UserService { getUserInfo(user: User) { // user 매개변수를 명시적으로 전달받음 return user.info; } } const user = new User('John Doe', 'john@example.com'); const userService = new UserService(); console.log(userService.getUserInfo(user)); |
Java | public class UserService { private User currentUser; public UserService(User user) { this.currentUser = user; } public String getUserInfo() { // currentUser가 어디서 오는지 명확하지 않음 return this.currentUser.getInfo(); } } User user = new User("John Doe", "john@example.com"); UserService userService = new UserService(user); System.out.println(userService.getUserInfo()); |
public class UserService { public String getUserInfo(User user) { // user 매개변수를 명시적으로 전달받음 return user.getInfo(); } } User user = new User("John Doe", "john@example.com"); UserService userService = new UserService(); System.out.println(userService.getUserInfo(user)); |
[BOLEE 질문] 실제로 매개변수를 명시적으로 전달하지 않아 이후에 매개변수를 변경해 암묵적으로 사용한 적이 있는지?
11 비슷한 코드끼리
- 긴 코드 덩어리에서 구분이 되는 두 부분 사이에 빈 줄을 넣어 분리
=> 제대로 된 SW 설계는 유연성을 확보해 이후의 설계를 더 쉽게 만들지만, 그렇지 않은 경우 큰 문제가 될 수 있음
12 도우미 추출
- 메서드 추출 리팩터링
=> 루틴 속 코드 중 목적이 분명하고 나머지 코드와는 상호작용이 적은 코드 불록의 경우 도우미(helper)로 추출
=> 도우미는 작동 방식이 아닌 목적에 따라 네이밍하는 것이 좋음
- [BOLEE] 코드 더보기
// 도우미 네이밍
// 작동방식에 따른 네이밍 => 함수의 내부 구현에 대한 정보
function bubbleSort ...
function processData ...
function manageUsers ...
// 목적에 따른 네이밍 => 함수가 무엇을 하는지, 어떤 목적을 달성하는지에 대한 정보
function sortArray ...
function filterInvalidEntries ...
function deactivateInactiveUsers ...
- 도우미 추출 시 특수한 경우
1) 큰 루틴 안에서 몇 줄을 변경해야 하는 경우?
해당 줄들을 도우미로 추출하고 도우미 안의 내용만 변경한 뒤 적절하다고 판단한 뒤에 도우미를 호출하는 문장에 반영
// 코드정리 전
routine()
그대로 두는 코드
바꾸려는 코드
그대로 두는 코드
// 코드정리 후
helper()
바꾸려는 코드
routine()
그대로 두는 코드
helper()
그대로 두는 코드
2) 호출 순서를 보장하기 위한 시간적 결합을 표현하는 경우 ( b()보다 먼저 a()를 호출해야 하는 경우 )?
// 코드정리 전
foo.a()
foo.b()
// 코드정리 후
ab()
a()
b()
- [BOLEE] 코드 더보기
코드정리 전 | 코드정리 후 *ab()를 호출하면 a()와 b()가 순서대로 실행됨 |
|
TypeScript | const foo = new Foo(); foo.a(); foo.b(); |
class Foo { a(): void { // a()의 작업 } b(): void { // b()의 작업 } ab(): void { this.a(); this.b(); } } // 사용 예시 const foo = new Foo(); foo.ab(); |
Java | foo.a(); foo.b(); |
public class Foo { public void a() { // a()의 작업 } public void b() { // b()의 작업 } public void ab() { a(); b(); } } // 사용 예시 Foo foo = new Foo(); foo.ab(); |
13 하나의 더미
- 코드가 너무 여러 작은 조각으로 나뉘어져 있다면 필요한 만큼의 코드를 하나의 더미처럼 느껴질 때까지 흩어진 코드를 모아 정리
=> 코드를 만드는 데 가장 큰 비용은 코드 작성이 아닌 이해하는 데 드는 비용임
=> 작은 코드 조각을 지향하는 목적은 코드를 한 번에 조금씩 이해할 수 있도록 하는 것이기도 하지만 때로 작은 조각들을 서로 엮어 이해하기에 어렵게 되기도 함
- 관련 예시?
1) 길고 반복되는 인자(argument) 목록
2) 반복되는 코드 및 조건문
3) 부적절한 이름의 도우미
4) 공유돼 변경에 노출된 데이터 구조
14 설명하는 주석
- 코드에서 명확하지 않은 내용에 대해 다른 사람의 관점에서 생각하고 예상 질문에 대해 주석을 추가
// 코드의 결함을 발견한 경우의 주석 예시
// 새로운 경우를 한 개 더 추가하려면 ../foo를 반드시 변경해야 합니다.
[BOLEE 질문] 주석 작성 여부를 결정하는 자신만의 기준은 어떻게 되는지?
- [BOLEE] 좋은 주석, 나쁜 주석? (참조 클린코드)
https://velog.io/@hangem422/clean-code-comment
15 불필요한 주석 지우기
- 코드만으로 내용을 모두 이해할 수 있다면 주석 삭제하기
=> 시간이 지나 코드가 변경될 경우 주석과 코드가 서로 맞지 않거나 주석이 불필요한 경우가 생길 수 있음
=> 코드를 읽는 사람의 시간도 비용이므로 불필요한 주석은 삭제해야 함
// 코드 정리 전
if (! generator)
// generator가 없다면 default 반환
return getDefaultGenerator()
...generator 설정 관련 코드
// 코드 정리 후 (주석 삭제)
if (! generator)
return getDefaultGenerator()
...generator 설정 관련 코드
Part 2 관리
- 코드 정리는 리팩터링으로 가는 관문
=> 코드 정리를 개인 개발 흐름에 맞추는 방법!
1) 코드 정리는 언제 시작하나요?
2) 코드 정리는 언제 멈추나요?
3) 코드의 구조를 변경하는 코드 정리와 시스템의 동작 변경을 어떻게 결합할 수 있을까요?
16 코드 정리 구분
- 코드 정리는 별도의 PR로 만들고, PR당 몇 개의 코드 정리만 넣기
- 코드 정리 단계
1) 변경을 구분하지 않은 상태에서 다수의 변경을 반영
=> 변경 대상? 프로그램 동작 변경(프로그램을 실행하면서 찾아내기), 프로그램 구조 변경(코드를 자세히 보며 찾아내기)
=> 공통 흐름을 알아채 비슷한 코드끼리 정리해 설명하는 도우미 만들기
2) 순서가 있는 일련의 코드 정리는 또는 동작 변경을 별도의 PR로 만들기
=> 크고 포괄적인 PR? 전체 그림을 보여주지만, 검토하는 입장에서 유용한 피드백을 제공하기에 너무 큰 덩어리일 수 있음
=> 아주 작은 PR? 소소한 피드백을 유도할 수 있지만 무시될 수도 있음
[BOLEE 질문] PR 크기에 있어 '크고 포괄적인', '아주 작은'을 판단하는 정량적인 기준이 있는지?
'개발독서 > 코드품질' 카테고리의 다른 글
내 코드가 그렇게 이상한가요 (9장 설계의 건정성) (0) | 2024.05.03 |
---|---|
내 코드가 그렇게 이상한가요 (3~4장 클래스 설계 & 불변 활용하기) (0) | 2024.03.24 |
내 코드가 그렇게 이상한가요 (1~2장 잘못된 구조의 문제 깨닫기 & 설계 첫걸음) (0) | 2024.03.19 |