Clean Code
1. 의도를 드러내는 이름을 사용해라 (Use intention revealing names)
// Bad
int d; // elapsed time in days
// Good
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;
// Bad
public List<int[]> getThem() {
List<int[]> list1 = new ArrayList<int[]>();
for (int[] x : theList) // theList 에는 어떤게 들어가는지?
if (x[0] == 4) // theList 의 0 번째가 뭔지?, 4의 값이 뭔지?
list1.add(x);
return list1; // 반환되는 목록은 뭔지?
}
// 🖐 의미를 정확히 알기 어렵다.
// Good
public List<Cell> getFlaggedCells() {
List<Cell> flaggedCells = new ArrayList<Cell>();
for (Cell cell : gameBoard)
if (cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}
- 나쁜 코드의 경우는 해당 코드를 읽는 사람에게 가정을 바란다는 점이다.
- 좋은 코드의 경우는 어떤 의도인지를 명확하게 드러낸다.
2. 그릇된 정보를 피해라 (Avoid Disinformation)
1) 널리 쓰이는 의미가 있는 단어를 다른 의미로 사용하지 말자
- 예를 들어 hp 는 hypotenuse 직각 삼각형의 빗변을 약어로 썼다고 하면, (사실 주석이나, 누군가가 말해주지 않는다면) 그 의미가 불명확하다.
// Bad
// 아래와 같은 naming 들은 의미가 불명확하다.
int hp;
int ax;
int sco;
2) List
- 실제 List 가 아닌 이상 xxxList 와 같이 변수명을 붙이지 말고,
accountGroup
,bunchOfAccounts
,accounts
등으로 명명하자. - Collection 인 경우 xxxList 보다는
accounts
와 같은 복수형을 쓰는게 더 좋다.
// Bad
int accountList;
// Good
String[] accountList = {"yukeun", "tester"};
3) 비슷해 보이는 단어를 지양하자
XYZControllerForEfficientHandlingOfStrings
와XTZControllerForEfficientStorageOfStrings
의 차이점을 찾아내는데, 시간이 오래 걸린다. 비슷한 이름을 사용하지 않도록 유의하자.
// Bad
XYZControllerForEfficientHandlingOfStrings;
XTZControllerForEfficientStorageOfStrings;
4) 비슷해 보이는 단어를 지양하자
- 소문자 l 과 숫자1, 대문자 O와 숫자 0과 같이 헷갈리는 문자는 쓰지 말자.
// Bad
int a = l;
if ( O == l )
a = O1;
else
l = 01;
3. 의미 있게 구분하라 (Make Meaningful Distinctions)
1) 연속된 숫자를 덧붙이는 경우
[a1, a2 .... ]
와 같이 숫자로 구분하지 말아라.
// Bad
public static void copyChars(char a1[], char a2[]) {
for (int i = 0; i < a1.length; i++) {
a2[i] = a1[i];
}
}
// Good
public static void copyChars(char source[], char destination[]) {
for (int i = 0; i < source.length; i++) {
destination[i] = source[i];
}
}
2) 불용어를 추가하는 경우
// 1) ProductInfo, ProductData
// Bad
public class ProductInfo {}
public class ProductData {}
// 2) naming
// Bad
getActiveAccount()
getActiveAccounts()
getActiveAccountInfo()
// 3)
// Bad
String nameString;
// 4) a, the
// message theMessage
// Bad
String message;
String theMessage;
4. 발음하기 쉬운 이름을 사용하라 (Use Pronounceable Names)
// Bad
class DtaRcrd102 {
private Date genymdhms;
private Date modymdhms;
private final String pszqint = "102";
/* ... */
};
// Good
class Customer {
private Date generationTimestamp;
private Date modificationTimestamp;
private final String recordId = "102";
/* ... */
};
5. 검색하기 쉬운 이름을 사용하라 (Use Searchable Names)
// Bad
// 여기에서 34, 4, 5 를 코드에서 찾기 어렵다.
for (int j = 0; j < 34; j++) {
s += (t[j] * 4) / 5;
}
// Good
// 대신, realDaysPerIdealDay, WORK_DAYS_PER_WEEK, NUMBER_OF_TASKS 는 코드에서 찾기 수월해진다.
int realDaysPerIdealDay = 4;
final int WORK_DAYS_PER_WEEK = 5;
final int NUMBER_OF_TASKS = taskEstimate.length;
int sum = 0;
for (int j = 0; j < NUMBER_OF_TASKS; j++) {
int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
int realTaskWeeks = realTaskDays / WORK_DAYS_PER_WEEK;
sum += realTaskWeeks;
}
6. 인코딩을 피하라 (Avoid Encodings)
1) 헝가리안 표기법 (Hungarian Notation)
// Bad
// 변수명에 해당 변수의 Type 을 적지 마라.
PhoneNumber phoneString;
2) 멤버 변수 접두어 (Member Prefixes)
// Bad
public class Part {
private String m_dsc; // The textual description
void setName(String name) {
m_dsc = name;
}
}
// Good
public class Part {
String description;
void setDescription(String description) {
this.description = description;
}
}
3) 인터페이스와 구현 (Interfaces and Implementations)
Do / Don't | Interface class | Concreate(Implementations class) |
---|---|---|
Don't | IShapeFactory | ShapeFactory |
Do | ShapeFactory | ShapeFactoryImp |
Do | ShapeFactory | CSShapeFactory |
7. 자신의 기억력을 자랑하지 마라 (Avoid Mental Mapping)
8. 클래스 이름 (Class Names)
// Good
Customer, WikiPage, Account, AddressParser ...
9. 메서드 이름 (Method Names)
// Good
postPayment, deletePayment, deletePage, save ...
// Bad
Complex fulcrumPoint = new Complex(23.0);
// Good
Complex fulcrumPoint = Complex.FromRealNumber(23.0);
10. 기발한 이름은 피해라 (Don't Be Cute)
// Bad
HolyHandGrenade
// Good
DeleteTimes
// Bad
whack()
// Good
kill()
11. 한 개념에 한 단어를 사용하라 (Pick One Word per Concept)
12. 말 장난 하지 마라 (Don't Pun)
// Bad
public static String add(String message, String messageToAppend) // A + B
public List<Element> add(Element element) // List 에 추가한다는 의미
// Good
public static String add(String message, String messageToAppend)
public List<Element> append(Element element) // append or insert
13. 해법 영역에서 가져온 이름을 사용하라 (Use Solution Domain Names)
JobQueue
, AccountVisitor(Visitor pattern)
등을 사용하지 않을 이유가 없다.14. 문제 영역에서 가져온 이름을 사용하라 (Use Problem Domain Names)
15. 의미 있는 맥락을 추가하라 (Add Meaningful Context)
// Bad
private void printGuessStatistics(char candidate, int count) {
String number;
String verb;
String pluralModifier;
if (count == 0) {
number = "no";
verb = "are";
pluralModifier = "s";
} else if (count == 1) {
number = "1";
verb = "is";
pluralModifier = "";
} else {
number = Integer.toString(count);
verb = "are";
pluralModifier = "s";
}
String guessMessage = String.format("There %s %s %s%s", verb,
number, candidate, pluralModifier );
print(guessMessage);
}
// Good
public class GuessStatisticsMessage {
private String number;
private String verb;
private String pluralModifier;
public String make(char candidate, int count) {
createPluralDependentMessageParts(count);
return String.format("There %s %s %s%s", verb, number, candidate, pluralModifier );
}
private void createPluralDependentMessageParts(int count) {
if (count == 0) {
thereAreNoLetters();
} else if (count == 1) {
thereIsOneLetter();
} else {
thereAreManyLetters(count);
}
}
private void thereAreManyLetters(int count) {
number = Integer.toString(count);
verb = "are";
pluralModifier = "s";
}
private void thereIsOneLetter() {
number = "1";
verb = "is";
pluralModifier = "";
}
private void thereAreNoLetters() {
number = "no";
verb = "are";
pluralModifier = "s";
}
}
16. 불필요한 맥락을 없애라 (Don't Add Gratuitous Context)
Specific Naming Conventions
1. get/set 메서드는 속성에 대해서 직접적인 접근을 할 때 사용해라
employee.getName();
employee.setName(name);
matrix.getElement(2, 4);
matrix.setElement(2, 4, value);
2. is 키워드는 boolean 타입의 변수나 boolean 타입을 리턴하는 메서드 앞에 붙여라
// Bad
// isStatus isFlag 는 적합하지 않다.
isStatus
isFlag
boolean setter( get/set is/set )
// Good
// Setter 메서드에는 다음과 같이 접두어가 설정 되어 있어야 한다.
void setFound(boolean isFound)
// Good
// is 에 대한 대안으로는 has, can, should 접두사가 있다.
boolean hasLicense();
boolean canEvaluate();
boolean shouldAbort = false;
3. 어떤 것을 계산하는 경우에는 compute 키워드를 메서드 앞에 붙일 수 있다
// 잠재적인 시간 소모적 작업이라는 즉각적인 단서를 제공하고,
// 반복적으로 사용하는 경우 결과를 캐시하는 것을 고려 할 수 있다.
// 용어를 일관되게 사용하면 가독성이 향상된다.
valueSet.computeAverage();
matrix.computeInverse();
4. 어떤 것을 찾을 경우에는 find 키워드를 메서드 앞에 붙일 수 있다
node.findShortestPath(Node destinationsNode);
matrix.findSmallestElement();
5. Object 나 어떤 확정적인 작업의 초기화를 할 경우는 initialize 키워드를 메서드 앞에 붙일 수 있다
// 영국식 initialise 보다 미국식 initialize 를 사용해라.
// init 이라는 단어는 피해라.
printer.initializeFontSet();
6. 사용자 인터페이스 구성 요소 이름에는 요소 유형이 접미사로 붙어야 한다
widthScale, nameTextField, leftScrollbar, mainPanel, fileToggle, minLabel, printerDialog
7. Collection 이나 배열 타입 변수 이름은 복수 형태로 이름을 지어라
Collection<Point> points;
int[] values;
8. object의 갯수를 나타낼 경우는 n 키워드를 변수 앞에 붙일 수 있다
nPoint, nLine
9. 엔티티의 번호를 나타낼 경우에는 No 키워드를 변수 뒤에 붙일 수 있다
tableNo, employeeNo
10. iterator 변수들은 i, j, k 등과 같은 이름을 사용해라
for (Iterator i = points.iterator(); i.hasNext(); ) {
:
}
for (int i = 0; i < nTables; i++) {
:
}
11. 대응 되는 단어를 사용해서 이름을 지어라
get/set, add/remove, create/destroy, start/stop, insert/delete,
increment/decrement, old/new, begin/end, first/last, up/down, min/max,
next/previous, old/new, open/close, show/hide, suspend/resume, etc.
12. 되도록 축약형을 쓰지 말아라
computeAverage(); // NOT: compAvg();
ActionEvent event; // NOT: ActionEvent e;
catch (Exception exception) { // NOT: catch (Exception e) {
// 일반적인 단어는 축약해서는 안된다.
cmd instead of command
comp instead of compute
cp instead of copy
e instead of exception
init instead of initialize
pt instead of point
etc.
// 약어를 통해 알려진 도메인 문구는 축약해서 사용해라.
HypertextMarkupLanguage instead of html
CentralProcessingUnit instead of cpu
PriceEarningRatio instead of pe
etc.
13. boolean 타입 변수 이름은 부정적인 의미로 쓰지 마라
bool isError; // NOT: isNoError
bool isFound; // NOT: isNotFound
14. 서로 관련이 있는 상수들은 상수 앞에 공통 단어를 붙여주는 것이 좋다
final int COLOR_RED = 1;
final int COLOR_GREEN = 2;
final int COLOR_BLUE = 3;
interface Color
{
final int RED = 1;
final int GREEN = 2;
final int BLUE = 3;
}
15. 예외 클래스는 클래스 뒤에 Exception 키워드를 붙여라
class AccessException extends Exception
{
:
}
16. 디폴트 인터페이스는 인터페이스 앞에 Default 키워드를 붙여라
class DefaultTableCellRenderer implements TableCellRenderer
{
:
}
17. 싱글톤 패턴을 구현한 클래스에서 인스턴스를 리턴하는 메서드 이름은 getInstance 라고 붙여라
public final class UnitManager
{
private final static UnitManager instance_ = new UnitManager();
private UnitManager()
{
:
}
public static UnitManager getInstance() // NOT: get() or instance() or unitManager() etc.
{
return instance_;
}
}
18. 팩토리 패턴을 구현한 클래스에서 인스턴스를 리턴하는 메서드 앞에 new 키워드를 쓸 수 있다
class PointFactory
{
public Point newPoint(...)
{
...
}
}
19. 함수(객체를 반환하는 메서드)는 반환하는 이름을 따서 명명해야 하며 프로시저(void 메서드)는 수행하는 작업을 따라 이름을 지정해야 한다
Best practices for java naming convention in programming
1. Java 네이밍 컨벤션을 따라라 (Follow Java Naming Convention)
구분 | 내용 |
---|---|
클래스 이름 | 대문자로 시작해야 하며, 명사여야 한다 |
인터페이스 이름 | 대문자로 시작해야 하며, 형용사여야 한다 |
메서드 이름 | 소문자로 시작해야 하며, 동사여야 한다 |
변수 이름 | 소문자로 시작해야 한다 |
패키지 이름 | 소문자여야 한다 |
상수 이름 | 대문자여야 한다 |
2. 의미 없는 변수 이름은 피해라 (Avoid Meaningless Names of Variables)
3. 항상 의미 있는 변수 이름을 사용해라 (Always use Meaningful Names of Variables)
4. 긴 이름보다 짧은 이름이 바람직함 (Shorter name preferable over longer name )
5. 짧은 형식보다 설명적인 이름을 선호한다 (Prefer descriptive name over short form)
6. 비슷한 이름은 피해라 (Avoid Similar Names)
7. 메서드 명명의 일관성 (Consistency in Naming of Methods)
close()
, finish()
, kill()
등을 사용하는 프로젝트의 경우 이러한 이름은 일관성이 없으므로 리소스 해제를 위해 항상 하나의 이름으로 구성한다.Code Complete
Considerations in Choosing Good Names
// Bad
x = x - xx;
xxx = fido + SalesTax( fido );
x = x + LateFee( x1, x ) + xxx;
x = x + Interest( x1, x );
// Good
balance = balance - lastPayment;
monthlyTotal = newPurchases + SalesTax( newPurchases );
balance = balance + LateFee( customerID, balance ) + monthlyTotal;
balance = balance + Interest( customerID, balance );
네이밍시 가장 중요한 고려사항
- 이름이 완전하고, 변수가 나타내는 개체를 정확하게 설명한다.
- 이름은 가능한 구체적이어야하고, 모호하거나 하나 이상의 목적으로 사용 될 수 있는 일반적인 이름은 나쁜 이름이다.
Purpose of Variable | Good Names, Good Descriptors | Bad Names, Bad Desciptors |
---|---|---|
Running total of checks written to date | runningTotal, checkTotal | written, ct, checks, CHKTTL, x, x1, x2 |
Velocity of a bullet tratin | velocity, trainVelocity, velocityInMph | velt, v, tv, x, x1, x2, train |
Current date Lines per page | currentDte, todaysDate, linesPerPage | cd, current, c, x1, x2, date, lpp, lines, l, x, x1, x2 |
최적의 이름 길이
- 일반적으로 변수 이름의 길이가 평균적으로 10~16 일 때 프로그램을 디버깅 하기 위해서 들이는 노력을 최소화 할 수 있고, 변수의 평균 길이가 8 ~ 20인 프로그램은 디버깅 하기 쉽다.
- 모든 변수의 이름을 10 ~ 16의 길이로 작성하기 위해 노력할 필요는 없겠지만, 코드에 짧은 이름의 변수, 또는 굉장히 긴 이름의 변수를 보게 된다면 그 이름이 적절한지 확인 해야한다.
구분 | 예시 |
---|---|
Too Long | numberOfPeopleOnTheUsOlympicTeam numberOfSeatsInTheStadium maximunNumberOfPointsInMordernOlympics |
Too Short | n, np, ntm, n, ns, nsisd m, mp, max, points |
Just Right | numTeamMembers, teamMemberCount numSeatsInStadium, seatCount teamPointsMax, pointsRecord |
범위가 변수명에 미치는 영향
- 많은 프로그램들은 계산된 값(총계, 평균, 최대값 등)을 보관하는 변수들을 갖는다.
- 만약 변수의 이름에 Total, Sub, Average, Max, Min, Record, String, Pointer 등의 한정자를 사용해야 한다면, 이름의 끝이 이런 수정자를 입력하는 것이 좋다.
Good | Bad |
---|---|
revenueTotal expenseAverage | totalRevenue averageExpense |
- 단 예외적인 경우가 있는데, Num 한정자의 관습적인 위치이다.
- 변수의 이름 앞에 있는 Num 은 총계를 가르킨다(numCustomer : 전체 고객의 수)
- 변수의 이름 끝에 있는 Num 은 인덱스를 가르킨다(customerNum : 특정 고객의 번호)
- 이런 혼란을 피하기 위해 Num 이라는 단어를 피하고 customerCount(전체 고객의 수), customerIndex(특정 고객의 번호) 와 같은 이름을 쓰는 것이 좋다.
변수 이름에서의 계산값 한정자
많은 프로그램에는 계산된 값을 포함하는 변수가 있다. Total, Sum, Average, Max, Min, Record 과 같은 한정자를 사용하여 이름을 사용할 경우, 이름 끝에 수식어를 넣는다. 이 방법은 몇 가지 이점을 제공한다.
- 첫째, 변수의 의미를 가장 많이 주는 부분인 name 이 맨 앞에 있어서 가장 눈에 띄고 먼저 읽힌다.
- 둘째, 이 규칙을 제정함으로써 TotalRevenue 와 revenueTotal 을 모두 사용할 경우 발생할 수 있는 혼란을 방지 할 수 있다.
- 셋째, revenueTotal, expensionTotal, revenueAverage, expensionAverage 와 같은 일련의 이름은 보기 좋은 대칭을 가지고 있다.
- 넷째, 일관성은 가독성을 향상시키고 유지 보수를 용이하게 한다.
변수 이름에서 일반적인 반대되는 항목
begin / end first / last locked / unlocked min / max next / previous old / new opened / closed visible / invisible source / target source / destination up / down
Good Routine Names
루틴이 하는 모든 것을 표현하라
- ComputeReportTotals() 는 무엇을 하는지 모호하기 때문에 적절한 이름이 아니다.
- ComputeReportTotalsAndOpenOutputFile() 은 적절한 이름이겠지만, 너무 길다.
- 이에 대한 해결책은 부수적인 효과를 갖기보다는 직접적인 효과를 유발시키도록 프로그램을 작성하고 그에 맞게 새로운 이름을 짓는 것이다.
의미가 없거나 모호하거나 뚜렷한 특징이 없는 동사들은 피하라
- 어떤 동사들은 신축성이 있고, 모든 의미를 다룬다.
- HandleCalculation(), PerformServices(), OutputUser(), ProcessInput(), DealWithOutput() 과 같은 이름은 그 루틴이 무엇을 하는지 말해주지 않는다.
- 루틴의 이름에 뚜렷한 특징이 없기 때문에 문제가 되기도 한다.
- HandleOutput() → FormatAndPorintOut() 으로 변경된다면 루틴이 수행하는 작업에 대해 꽤 잘 알 수 있다.
- 다른 경우에는 일상적인 작업이 수행되기 때문에 동사가 모호하다.
- 이런 경우 해당 루틴을 적절하게 리팩토링하여 명확한 처리를 하도록 해야 한다.
루틴 이름을 숫자만으로 구분하지 말라
- Part1(), Part2() 또는 OutputUser1(), OutputUser2() 와 같은 이름은 잘못된 이름이다.
- 루틴의 이름 끝에 있는 숫자는 루틴이 표현하는 서로 다른 추상화에 대해서 아무런 정보를 제공하지 않는다.
불필요하게 긴 이름을 짓지마라
- 연구에 따르면 최적의 변수 이름의 평균 길이는 9~15 자이다.
함수의 이름을 지을 때 리턴 값에 대한 설명을 사용하라
- 리턴 값을 따서 이름을 작성하는 것은 좋은 방법이다.
- cos(), customerId.Next(), print.IsReady(), pen.CurrentColor() 는 함수가 리턴하는 것을 정확하게 보여주기 때문에 좋은 이름이다.
프로시저의 이름을 지을 때, 확실한 의미를 갖는 동사 다음에 객체를 사용하라
- 프로시저의 이름은 프로시저가 무엇을 하는지를 반영해야 하기 때문에, 객체에 대한 연산은 동사 + 객체의 형태의 이름을 갖는다.
- PrintDocument(), ClacMonthlyRevenues(), CheckOrderInfo(), RepaginateDocument() 는 모두 좋은 프로시저 이름이다.
공통적인 연산을 위한 규약을 만들어라
employee.id.Get()
dependent.GetId()
supervisor()
candidate.id()
위 코드는 모두 특정 객체의 식별자를 얻기 위한 코드이다.
Employee 클래스는 자신의 id 객체를 노출하고 id 객체는 Get() 루틴을 노출했으며,
Dependent 클래스는 GetId() 루틴을 노출했다.
Supervisor 클래스는 id 를 기본 리턴 값으로 만들었고,
Candidate 클래스는 id 객체의 기본 리턴 값이 id 라는 사실을 이용하여 id 객체를 노출 시켰다.
id 를 가져오는 이름 규칙이 있었다면 이러한 난잡한 코드가 생성되는 현상은 막을 수 있을 것이다.
함수 이름 짓는 가장 쉬운 방법
이름을 짓는 쉬운 절차
1) 함수가 할 일을 육하 원칙을 준수해서 한글로 적는다.
"사용자가 이름을 입력하고 등록 버튼을 클릭하면,
시스템이 사용자 이름을 input 태그에서 가져와서 이름 입력 여부와 글자 수를 확인한 뒤,
입력이 안 되었으면 스크립트를 중단하고 input 태그를 활성화하여 사용자가 쓸 수 있게 하고,
글자 수가 한글 두 글자 이하면 확인을 요청하여 사용자가 확인 할 수 있게 한다."
2) 시스템이 할 일만 남기고 나머지는 없앤다.
사용자가 이름을 입력하고 등록 버튼을 클릭하면, 시스템이
사용자 이름을 input 태그에서 가져와서 이름 입력 여부와 글자 수를 확인한 뒤,
입력이 안 되었으면 스크립트를 중단하고 input 태그를 활성화하여 사용자가 쓸 수 있게 하고,
글자 수가 한글 두 글자 이하면 확인을 요청하여 사용자가 확인 할 수 있게 한다.
3) 문장을 조각내고 불필요한 설명을 없앤다.
사용자 이름을 input 태그에서 가져온다.
사용자 이름의 입력 여부와 글자 수를 확인한다.
입력이 안 되었으면 스크립트를 중단하고 input 태그를 활성화 한다.
글자 수가 한글 두 글자 이하면 확인을 요청한다.
4) 논리적인 문장으로 바꾼다.
사용자 이름을 input 태그에서 가져온다.
사용자 이름의 글자 수를 확인한다.
만약 글자 수가 0(=null) 이면 input 태그를 활성화 한다.
만약 글자 수가 1 or 2 면 사용자에게 확인을 요청한다.
5) 1함수 1업무 규칙에 따라 함수를 결정한다.
(함수1) 사용자 이름을 input 태그에서 가져온다.
(함수2) 사용자 이름의 글자 수가 2글자 이하면 다음과 같이 처리한다.
→ 만약 글자수가 0(=null) 이면 input 태그를 활성화한다.
→ 만약 글자수가 1 or 2 면 사용자에게 확인을 요청한다.
6) 함수 문장을 영어로 바꾼다.
(함수1) 사용자 이름을 input 태그에서 가져온다.
→ get user's name from the text input field
(함수2) 사용자 이름의 글자 수가 2글자 이하면 다음과 같이 처리한다.
→ do something if user's name contains under 2 characters
7) 이해하는 데 크게 관계 없는 단어를 없앤다.
8) 필요하다면 짧고 포괄적인 단어로 바꾼다.
(함수1) get user's name from the text input field
→ get user name from input field
(함수2) do something if user's name contains under 2 characters
→ check if user name contains under 2 characters
9) 띄어쓰기를 없애고 두 번째 단어부터는 첫 글자를 대문자로 바꾼다.
(함수1) getUserNameFromInputField
(함수2) checkIfUserNameContainsUnder2Characters
10) 함수를 사용할 때 의미상 없어도 되는 단어를 없앤다.
(함수1) getUserNameFromField()
(함수2) checkUserNameUnder2Characters()
SMART 한 방법으로 변수 이름 짓기
easy to Search : 검색하기 쉽고
easy to Mix : 조합하기 쉽고
easy to Agree : 수긍하기 쉽고
easy to Remember : 기억하기 쉽고
easy to Type : 입력하기 쉽고
변수명 지어주는 사이트
https://www.curioustore.com/#!/