포스트

[Clean Code][TIL] 의미 있는 이름

이 포스트는 로버트 C.마틴의 Clean Code 속 2장(22p ~ 38p) 내용에 대한 후기입니다.


“여느 코드 개선 노력과 마찬가지로 이름 역시 나름대로 바꿨다가는 누군가의 질책을 받을지 모른다.
그렇다고 코드를 개선하려는 노력을 중단해서는 안 된다.” (2장 38p)


이름을 잘 짓는 규칙

로버트는 이름을 잘 짓는 규칙으로 15개의 규칙을 제시 했습니다.

  1. 그릇된 정보를 피하라
  2. 의미 있게 구분하라
  3. 발음하기 쉬운 이름을 사용하라
  4. 검색하기 쉬운 이름을 사용하라
  5. 인코딩을 피하라
  6. 자신의 기억력을 자랑하지 마라
  7. 클래스 이름
  8. 메서드 이름
  9. 기발한 이름은 피하라
  10. 한 개념에 한 단어를 사용하라
  11. 말장난을 하지 마라
  12. 해법 영역에서 가져온 이름을 사용하라
  13. 문제 영역에서 가져온 이름을 사용하라
  14. 의미 있는 맥락을 추가하라
  15. 불필요한 맥락을 없애라

위의 내용 중에서 “6. 자신의 기억력을 자랑하지 마라”, “클래스 이름, 메서드 이름”, “한 개념에 한 단어를 사용해라”
4가지 개념이 특히 중요하다고 생각합니다.

프로그램은 대부분 함께 만들기 때문에 작성한 코드도 여러명이 읽습니다.
만약 명료하지 않은 이름 을 사용해 작성자만 기억하는 코드가 된다면
작성할 때는 편할 수 있지만 다른 팀원에게 직접 설명해줘야 하는 문제가 발생할 수 있습니다.

클래스와 객체 모두 특정 기능의 집합이기 때문에 집합을 표현할 수 있는 명사 혹은 명사구가 적합합니다.
한 가지의 기능만 수행하는 메서드는 기능에 대한 동사나 동사구가 적합합니다.

클래스 내부에 있는 private 변수를 수정할 때,
변수에 접근하기 위한 public 메서드 이름을 store, set 등 여러가지를 고려할 수 있습니다.
이때, 각 상황에 맞춰 A 메서드는 store, B 메서드는 set 으로 사용하는 것이 아닌
저장 역할을 담당하는 메서드의 접두어는 set으로 통일 하는 것이 일관성 있는 코드를 작성할 수 있습니다.

의미 있는 이름 사용해보기

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
enum TYPE { ACCEPT, RECV, SEND, LOGOUT };
enum SESSION_STATE { ST_FREE, ST_ALLOC };
enum PLAYER_STATE { ST_HOME, ST_NOTREADY, ST_READY, ST_STAGE };

class OVER_EXP {
public:
	WSAOVERLAPPED	_over;
	TYPE			c_type;
	WSABUF			_wsabuf;
	char			_send_buf[BUF_SIZE];

	OVER_EXP();
	OVER_EXP(char* packet);
	~OVER_EXP();
};
class SESSION {
public:
	SESSION();
	~SESSION();
	bool recycle_session();

	SESSION& operator=(const SESSION& ref);

	void set_socket(SOCKET new_socket);
	int get_prev_size();
	void set_prev_size(int in);
	void do_recv();
	void do_send(void* packet);
	void disconnect();

private:
	OVER_EXP _recv_over;
	SOCKET _socket;
	int _prev_size;
};


위의 코드는 개선하기 전 코드입니다.
이 코드를 읽어보면 중복되는 이름이 있거나 명확하지 않은 이름, 사용 해선 안되는 이름도 존재합니다.

처음 코드를 읽는 사람이 보았을 때, 가장 큰 문제점들은 다음과 같습니다.

  1. OVER_EXP 클래스의 역할 파악 불가능
  2. 변수명 첫 글자에 under bar( _ ) 를 사용
  3. set_prev_size, get_prev_size 함수의 역할 파악 불가능

먼저 OVER_EXP 클래스의 역할은 WSAOVERLAPPED 객체에 기능을 추가해서 사용하기 위한 클래스입니다.
클래스 내부 변수들은 현재 클라이언트 상태를 저장하는 c_type, 소켓 통신에서 데이터를 저장할 _wsabuf,
그리고 소켓 통신에서 데이터를 수신할 때 사용하는 임시 저장 변수 _send_buf 가 있습니다.

마지막으로 ~prev_size 함수는 소켓 통신 중 잘려서 수신 받은 데이터가 존재할 때,
해당 데이터의 크기를 정수형으로 저장하고 가져오는 함수입니다.

따라서 이 문제점을 해결하기 위해 우선 다음과 같이 작명 규칙을 만들었습니다.

TypeRule
Class, FunctionCamelCase
Variable, Class Fieldsnake_case
enum FieldKEBAB_CASE


그리고 변수명 첫 글자에 사용한 under bar는 C++ 에서 시스템 변수로 자주 사용되기 때문에
완전히 제거하기로 결정했습니다.
마지막으로 함수의 이름은 동사로 시작하고 이름이 길어지더라도 역할을 명확하게 표현할 수 있도록 수정했습니다.

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 소켓 타입임을 명시
enum SOCKET_TYPE	{ ACCEPT, RECV, SEND, LOGOUT };     
enum SESSION_TYPE	{ FREE, ALLOC };
// 접두사 제거
enum PLAYER_STATE	{ HOME, NOT_READY, READY, STAGE };  

// 카멜 표시법으로 클래스 이름 변경
class OverlappedExpansion {   
public:
	OverlappedExpansion();
	OverlappedExpansion(char* packet);
	~OverlappedExpansion();

private:

public:
  // 자세한 이름 사용
	WSAOVERLAPPED	overlapped;     
	SOCKET_TYPE		socket_type;
	WSABUF			wsa_buffer;
	char			packet_buffer[BUF_SIZE];

private:

};

// 카멜 표시법으로 클래스 이름 변경
class Session : public OverlappedExpansion {
public:
	Session();
	~Session();

	Session& operator=(const Session& ref);

	void set_socket(SOCKET new_socket);

  // 동사 + 명사 형태로 함수 이름 변경
	void recv_packet();
	void send_packet(void* packet);
	
  // 자세한 이름 사용
	int get_prev_remain_packet_size();
	void set_curr_remain_packet_size(int in);
	
	void disconnect();

	bool recycle_session();

private:

public:

private:
	SOCKET socket;

  // 자세한 이름 사용
	int remain_packet_size;
};


작명 규칙에 의해 개선된 코드는 이름들이 길어졌지만 개선되기 전 코드와 비교해서
클래스, 함수, 변수 등의 역할이 명확하게 이해되었습니다.

이번 작업 덕분에 매번 명확하지 않은 역할을 파악하고자
Visual Studio 에서 F12 버튼으로 이동하는 수고를 줄일 수 있게 되었습니다.

기존 프로젝트들에서 나쁜 코드들을 수정하고
앞으로 참여할 프로젝트들에서는 직접 설명할 필요 없이
읽기만 해도 루틴이 이해가 되는 코드를 작성하기 위해 Clean Code를 활용해 보겠습니다.

슬랙 인증

#노개북 #노마드코더 #개발자북클럽

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.