프로그래밍/기타

[Tips] 반복되는 값을 상수로 선언하자.

카프카뮈 2022. 4. 11. 00:52

심심해서 적어보는 시리즈.

트러블 슈팅 끝에 얻은 코딩 팁들을 앞으로 Tips 시리즈로 소개해드리려고 한다.

이 글은 레퍼런스 부담 없이, 간략하게만 쓸 생각이다.

요약

  • 함수 호출 횟수를 줄이면 성능이 향상된다.
  • 반복되는 값은 상수로 선언하는 것이 좋다.

반복적인 함수 호출은 비용이다

오늘의 첫번째는, 함수 호출에 대한 이야기이다.

우리가 코딩을 하다보면 종종 잊는 사실이 있는데,

함수를 호출하는 것은 생각보다 꽤 큰 비용이 드는 작업이라는 사실이 그것이다.

 

물론 알고리즘 문제풀이나 빅데이터 가공과 같은 극단적인 상황이 아니라면 충분히 감수할 수 있지만,

그래도 확실하게 티가 나는 부분들이 존재한다.

함수의 반복 호출

for(int i = 0; i < getSize(); i++) {
    // 연산...
}

위의 코드를 보자. 그냥 보기에는 명료한 코드 같다.

하지만 만약 위 반복문이 10,000번 실행되면 어떨까?

  • 당연히 getSize() 역시 10,000번 호출된다.
  • 만약 getSize()가 반복문 진행동안 쭉 같은 값을 반환한다면, 동일한 연산을 10,000번 수행하게 된다.

개선: 상수로 값 정의하기

위를 아래처럼 바꾸어 보자.

실행 시간이 확 줄어든다. 특히 해당 함수에서 연산을 수행한다면, 시간이 압도적으로 줄어들 것이다.

const int size = getSize();

for(int i = 0; i < size; i++) {
    // 연산...
}

물론 이것이 모든 경우에 적용되는 룰은 아니다.

  • 위 경우는 for문의 사이즈 조건이고, 반환값이 연산 동안 바뀔 일이 없으므로 상수로 값을 빼낼 수 있었다.
  • 아래처럼 조건에 따라 다른 값을 반환하는 함수라면, 값을 상수로 선언할 수도 없고 연산 밖으로 빼낼 수도 없다.
while(isBig(value)) {
    value++;
}

또한 컴파일러에 따라 해당 작업을 최적화해주기도 하므로, 해당 룰에 너무 집착할 필요는 없다.

다만 이와 같은 함수의 반환값을 외부로 빼는 행위는 또다른 장점을 가진다.

함수 호출을 줄여 가독성 향상

아래의 코드를 한 번 보자.

if(value < getBigNumber()){
    printf("big!");
} else if(value == getBigNumber()){
	printf("same!");
} else {
    printf("small!");
}

getBigNumber라는 함수가 반복적으로 호출되고 있다.

그 탓에 괄호가 많아져 조건문의 가독성도 떨어지고, 코드를 한 눈에 알아보기 어려워진다.

이를 아래처럼 고쳐보면 어떨까?

int bigNumber = getBigNumber();

if(value < bigNumber){
    printf("big!");
} else if(value == bigNumber){
	printf("same!");
} else {
    printf("small!");
}

전반적으로 괄호 수가 적어져 조건을 보기 편해졌다.

또한 이전에 함수로 호출한 값을 상수로 저장했으므로, 해당 값을 재활용할 수 있다.

이에 따라 함수 호출 횟수도 줄어 코드 효율도 높아진다.


반복적으로 사용되는 값을 상수로 선언하기

함수 반환값을 상수로 선언하기

반복적으로 호출되는 함수값을 상수로 저장하는 것은 또 하나의 부수적인 장점을 가지는데, 

해당 상수의 이름을 명확하게 지정하면 코드를 더 빨리 이해할 수 있다는 것이다.

아래 코드를 보자.

if(color == Color.valueOf("WHITE")) {
    // 연산...
}

사전 지식 없이 위 코드를 보면, valueOf라는 함수가 어떤 동작을 취하는지 한 번 고민을 해야 한다.

const Color COLOR_WHITE = Color.valueOf("WHITE");

(...)

if (color == COLOR_WHITE) {
    // 연산...
}

그러나 위처럼 표현하게 되면, 선언부를 확인하지 못하고 조건문만 보더라도 의미를 더 명확히 이해할 수 있다.

이 방식은 부수적인 장점을 또 가지게 된다.

 

협업 과정에서 로직을 잘못 이해해 벌어지는 실수를 막을 수 있다. 

  • 예를 들어, 새로운 동료가 해당 조건문을 고치는 과정에서 함수에 인자를 잘못 넣었다고 생각해보자! 
  • 선언부에 미리 값을 정의해두면 이러한 실수를 줄일 수 있을 것이다.

리팩토링이 쉬워진다.

  • 만약 Color.valueOf("WHITE") 라는 코드가 파일 내에 10개 존재한다고 해보자. 이때 로직이 변경되면 어떨까?
    • 모든 코드를 Color.valueOf("PINK") 로 변경해야 한다면, 10번 작업하는 것과 1번 작업하는 것의 차이는 크다.
  • 만약 Color라는 라이브러리가 삭제되어 코드를 NewColor.getWhite()로 교체한다고 생각해보자.
    • 이 경우 원래 코드는 수정 범위도 넓어지고 동료들에게 전파할 변경사항도 많아진다.
    • 그러나 선언부만 고쳐도 된다면, 아래의 로직은 변경될 필요도 없고 선언부를 모르는 사람도 안심하고 작업할 수 있다.

자주 사용되는 값 상수로 선언하기

함수 호출값 뿐 아니라 반복되는 값 역시 상수로 저장해두는 것이 좋다.

int SIZE_LIMIT = 1048576;

if(size >= SIZE_LIMIT) {
    // 연산...
}

리터럴 값이나 함수 호출, 단순 연산 등을 반복하는 것은 좋지 않기 때문이다.

  • 수정이 어렵다
  • 코드의 의미를 파악하기 어렵다.

부가설명

본 글에서는 자주 사용되는 함수 반환값이나 리터럴 등을 상수로 저장하라고 이야기했다.

이때 변수가 아닌 상수라고 표현한 이유는 다음과 같다.

 

변하지 않는 값을 상수로 선언해 코드상에 명시해야 한다.

  • 그렇지 않으면, 해당 값을 조작하는 실수를 수행할 수 있다.

만약 주기적으로 바뀌는 값이라면, 필요할 때마다 함수를 호출하거나 직접 연산하는 게 나을 수도 있다.

  • 억지로 값들을 모두 변수나 상수로 선언하면, 오히려 코드가 지저분해 질 수 있다.

평소에 코드를 짜면서 하는 생각을 간단히 정리해 보았다.

개인적인 의견이므로 잘못된 내용이 포함되었을 수 있다.

부족하고 그만큼 열려있고자 하기에, 편한 의견과 지적 부탁드립니다.

반응형