코드상에서 단 한번 호출하는 함수

제목 그대로,
코드상에서는 단 한번밖에 쓰이지 않는 함수도
만들어 쓰곤 하나요?

복잡한 함수는 하위 함수를 만들어 구성하고 있는데
이게 거기서 한 번밖에 안쓰이다보니, 비정상적인가 해서요.
물론 C#의 경우엔 로컬 함수를 쓰면 딱 알맞는데요.
로컬함수를 비추천하는 경우도 있는거같고, 지원 안하는 언어도 많다보니… 궁금해서 물어봅니다.

void a()
{
...
  b();
  c();
}

//(참조 1개)
b()
{
...
}
//(참조 1개)
c()
{
...
}

이런 느낌입니다.

2개의 좋아요

음… 함수의 역할이 무엇이냐에 따라 로컬 함수로 사용 할 수도 있고 따로 빼서 사용할 수도 있을 것 같습니다.

예를들면 하나의 클래스에서 가독성을 높이고 싶다.
보여줘야 할 부분만 드러내고 굳이 알 필요없거나 알기 쉽게 만들어(의도를 함축해서 나타내고 싶다) 할 경우에는 함수를 로컬로 빼서 사용한다면 좋은 예일 것 같구요.

// 하나의 매서드에 때려박자!! 아자 아자!!
Public void FoodDeliveryService1()
{
    var cook = Create Cook();
    cook.BoilWater();
    if(cook.IsWaterHot)
    {
          cook.PutNoodlesInAPot();
    }
    
    if(cook.Time is afterTreeMinutes)
    {
          .....
    }

     .....

}

아놔 이게 뭐야?? 누가 레시피 알려달래?, 됬고 빨리 요리하고 배달해줘요:rage:

// 로컬 함수를 정의해서 가독성을 높혀보자!!
Public void FoodDeliveryService2()
{
    var cook = new Cook();
    Cook.CookingTheRamen();

    var delivery = new Delivery();
    delivery.Shipping();

    ....
}

네 고객님 ^^ 만족하실까요? 별 5점 리뷰 부탁드립니다. 꾸우우우욱!!!:pray:

image


첫번째 예제는… 길이도 길고 필요한 부분인지도 모르고 그냥 쭉 읽어봐야 할것 같은 느낌이구요.
두번째 예제는 흐름 파악하기가 편한느낌? 일 것 같네요.
보자마자 '아 요리를하고 배달한다’라고 이해 할 수 있을 것 같구요, 원하는 파트(요리, 배달)를 골라서 바로 확인해 보면 되지 않을까…합니다

결론가독성 측면에서 도움이 된다거나, 로컬 함수가 한번만 쓰인다고해도 그 역할과 장점이 있다면 필요하다고 생각합니다.

Convert, Mapping 같이 뚜렷한 목표가 있는 것들은 AutoMapper...Nuget Package를 활용하거나(말거나)해서 따로 빼는 게 좋다고 생각합니다.

보안할 부분이나 다른 부분은 고수님들이 답변 해주 실 겁니다. :+1:

6개의 좋아요

단 한번 호출되는 private 함수, 괜찮단 말씀이시군요. 답변 감사합니다!

2개의 좋아요

함수가 쓰이는 횟수보다는, 코드가 논리적으로 이해하기 쉬운 구조인지 생각해보는게 먼저라고 생각합니다. 아래 관점에서 코드를 리뷰해보시면 어떨지 제안드려봅니다.

  • 함수로 따로 분리를 해야할 만큼 b나 c 함수의 코드 길이가 길다면, 리팩토링이나 최적화의 여지가 있는 것으로 볼 수 있겠습니다.
  • b와 c 함수가 a 함수에 결합이 될 수 없는 분명한 이유 (예: 어셈블리 컨텍스트나 스레드가 달리 실행되야 하는 경우)가 있는지 생각해봅니다. 역시 단순히 a의 길이가 늘어나기 때문이라면 역시 리팩토링이나 최적화의 여지가 있다고 보고 적극적인 개선을 생각하는 것이 좋을 듯 합니다,

제 경우, 보여주신 것처럼 b와 c 같은 단벌 함수를 일부러 만드는 경우가 주로 거대한 모놀리식 함수 (여기서는 a가 그렇다고 가정해보겠습니다.)를 이해하기 쉽게 리팩토링하는 과정에서 코드를 구조화할 필요가 있을 때 임시 경유처 성격으로 b나 c 같은 함수를 만들어보고, 최종적으로 그렇게 분리한 형태를 유지할지, 더 좋은 형태로 모델링할지를 판단하는 용도로 주로 활용하는 것 같습니다.

그런게 아니라면, 다만 한 블록이라도 불필요하게 콜 스택을 늘리지 않는 것이 성능 면에서든, 디버깅 편의성 면에서든 유리하다고 생각합니다.

8개의 좋아요

이번에 신입 개발자 분들이 들어와서 교육을 진행하는 중에 어떤 상황에서 로직을 함수로 빼야 할 지 궁금하다는 질문에 이렇게 답변 했습니다.

  1. 2군데 이상에서 재사용 되는 중복되는 로직이면 별도 함수로 작성하라. 아주 나중에 로직이 변경되었을 때 가물가물한 기억에 한 군데만 고쳐서 사고가 터질 수 있다.
  2. 너무 긴 코드는 본인 및 제 3자의 직관적인 코드의 가독성을 저해할 수 있으므로 Self documenting code, Self commenting code 관점에서 함수 내에 작성된 특정 동작이 함수로 빠졌을 때 가독성을 높여 줄 수 있다면 주저하지 말고 함수로 작성하라.

2의 내용은 @rkttu 님께서 언급하신 내용에 부합하는 것 같습니다.

다만, 마지막에 언급하신

불필요하게 콜 스택을 늘리지 않는 것이 성능 면에서든, 디버깅 편의성 면에서든 유리하다고

라는 부분에 대해서는 개인적으로 조금 다른 의견을 가지고 있는데요,

C#의 Inline Call과 일반 Call의 성능 비교: Adventures in Benchmarking - Method Inlining · Performance is a Feature! (mattwarren.org)

C++의 Function Call에 대한 Overhead: How much overhead is there in calling a function in C++? - Stack Overflow

실행하는 머신이나 함수 아규먼트의 개수에 따라 다르겠지만 사례에서 C#이나 C++에서 어떤 로직을 수행할 때 메소드 혹은 함수로 따로 뺄 경우의 성능상의 오버헤드는 2~3ns 수준인 것으로 나타납니다.

해당 로직이 초당 100만 번 호출 되어도 함수인지 아닌지 여부에 따라 2~3ms 정도 밖에 차이가 나지 않는다면 현 시대의 CPU는 충분히 좋은 성능을 가지고 있고 Release 빌드에서 inline 최적화 될 여지가 있으므로 과도한 함수화로 가독성을 해치지 않는 선에서는 주저하지 말고 함수로 작성하라 라는 개인적인 견해입다. :blush:

디버깅 측면에서도 함수로 뺀 부분이 오류에 관여하지 않는다면 디버거에서 "Step over(F10)"로 더 쉽게 지나갈 수 있으므로 함수로 빼는 것이 더 효율적인 케이스도 있을 것 같습니다ㅎ

12개의 좋아요