CodeGym /행동 /C# SELF /로컬 함수

로컬 함수

C# SELF
레벨 11, 레슨 4
사용 가능

1. 소개

프로그래머라면 누구나 알지: 함수(또는 메서드)는 사실상 모든 앱의 "일꾼"이야. 코드 구조화, 로직 재사용, 그리고 끝없는 복붙에서 벗어나게 도와주지. 지금까지 네가 써온 함수들은 전부 클래스나 구조체 본문에서 "이웃"처럼 있었을 거야.

근데 만약에, 네가 큰 함수 안에 그렇게 크지 않은 로직이 있는데, 그걸 살짝 분리하고 싶고, 프로젝트 전체에 공개하고 싶지 않다면? 예를 들어, 메서드 안에서만 쓰는 보조 계산 같은 거 말이야.

이럴 때 로컬 함수가 딱이야.

로컬 함수는 다른 함수(메서드, 생성자, 프로퍼티, 심지어 또 다른 로컬 함수) 안에 선언된 함수야. 이런 로컬 함수는 "부모"한테만 보이고, 그 외에는 아무도 못 봐.

로컬 함수 문법


void MainMethod()
{
    Console.WriteLine("메인 메서드 시작.");            // 메인 메서드 시작.
    int result = InnerSum(5, 7);                              // 로컬 함수 InnerSum 호출 (내부 합계)
    Console.WriteLine($"로컬 함수 결과: {result}");    // 로컬 함수 결과: {result}

    // 로컬 함수는 MainMethod 안에서 바로 선언돼
    
  
int InnerSum(int a, int b) // 두 숫자 더하는 로컬 함수 { return a + b; // a와 b의 합을 반환 }
}

이 예제에서 InnerSum 함수는 바로 MainMethod 안에 선언돼 있어. 메인 메서드 밖에서는 절대 못 써! 비밀 지령서처럼, 단둘이 있을 때만 공유할 수 있는 거지.

2. 로컬 함수 선언과 사용 위치

기본 규칙

  • 로컬 함수는 메서드, 생성자, 프로퍼티, 연산자, 심지어 또 다른 로컬 함수 안에서도 선언할 수 있어!
  • 로컬 함수는 자기 영역(scope)에서만 보여.
  • 로컬 함수는 부모 함수에서 선언 전이든 후든 호출할 수 있어(C#이 미리 다 분석해줌).

예시: 평균 점수 계산


// 평균 점수 출력하는 메인 함수
void PrintAverageScore(int[] scores)
{
    if (scores.Length == 0)
    {
        Console.WriteLine("계산할 점수가 없어."); // 계산할 점수가 없어.
        return;
    }

    double average = CalculateAverage(scores);
    Console.WriteLine($"평균 점수: {average:0.00}"); // 평균 점수

    // 평균 계산하는 로컬 함수
    
  
double CalculateAverage(int[] arr) { int sum = 0; foreach (var score in arr) sum += score; return (double)sum / arr.Length; // 평균 반환 }
} // 함수 호출: int[] myScores = { 5, 4, 3, 4, 5 }; PrintAverageScore(myScores); // 출력: 평균 점수: 4.20

편하지? PrintAverageScore 밖에서는 내부 로직, 즉 로컬 함수 CalculateAverage를 쓸 수 없어. 이런 방식 덕분에 코드를 깔끔하게 정리할 수 있고, 보조 메서드로 글로벌 네임스페이스를 어지럽히지 않아도 돼.

3. 로컬 함수와 영역(scope)

로컬 함수의 핵심은 "부모" 메서드 안에서만 보인다는 거야. 이런 동작 덕분에 클래스가 한 번만 쓰는 작은 메서드로 어질러지는 걸 막을 수 있어.


+-----------------+
|   클래스 메서드  |
|    void Foo()   |
|                 |
|   +-----------+ |
|   |  로컬 함수 | |
|   +-----------+ |
+-----------------+
로컬 함수는 선언된 메서드 안에서만 존재해

로컬 함수는 오직 선언된 메서드 안에서만 존재해. 밖에서 호출하려고 하면, 비밀 전화선이 없는데 전화하려는 거랑 똑같아.

만약 실수로 부모 메서드 밖에서 로컬 함수를 호출하면, 컴파일 에러가 나: '...' 이름이 현재 컨텍스트에 존재하지 않아.

로컬 함수는 부모의 변수를 볼 수 있어

로컬 함수는 부모 메서드의 모든 변수에 접근할 수 있어 — 이게 바로 로컬 함수의 슈퍼파워지!


void ShowStudent(string name, int age)
{
    string message = BuildMessage();
    Console.WriteLine(message);

    // 로컬 함수가 "name"과 "age" 변수를 사용함
    
  
string BuildMessage() { return $"이름: { name}, 나이: { age}"; }
}

이렇게 하면 코드가 더 짧아지고, "시끄럽지"도 않아. 매번 메서드에 파라미터 열 개씩 넘길 필요 없어 — 로컬 함수는 부모에 있는 모든 걸 "볼 수" 있으니까.

4. 반복문과 조건문 안의 로컬 함수

로컬 함수는 반복문이나 조건문 안, 어디서든 원하는 만큼 중첩해서 선언할 수 있어:


void ProcessNumbers(int[] numbers)
{
    foreach (int number in numbers)
    {
        if (number % 2 == 0)
        {
            Console.WriteLine($"{number} — 짝수");
            PrintEvenMessage();
        }
        else
        {
            Console.WriteLine($"{number} — 홀수");
        }

        
  
void PrintEvenMessage() { Console.WriteLine("이건 짝수야, 축하해!"); }
} }

근데 이런 식으로 너무 많이 쓰면 코드가 금방 읽기 힘들어질 수 있어. 로컬 함수가 너무 많거나 길어지면 특히 그렇지. 남용은 금물! 그래도 이런 기능이 있다는 건 알아두면 좋아.

로컬 함수 vs 일반 private 메서드

기준 로컬 함수 클래스의 private 메서드
영역(scope) 부모 함수만 클래스 전체
부모 변수 접근 가능 불가(파라미터로만)
가독성 향상 높음 보통
재사용성 낮음 높음
테스트 용이성 테스트 어렵다 테스트 쉽다

5. 흔한 실수와 구현 특징

실수 1: 로컬 함수 이름 오타 또는 선언 누락.
로컬 함수를 잘못된 이름으로 호출하거나 선언을 빼먹으면, 컴파일러가 CS0103 또는 CS0128 에러를 내고, 코드는 컴파일되지 않아.

실수 2: 캡처된 변수(closure) 변경.
로컬 함수가 외부 메서드의 변수를 바꾸면, 예상과 전혀 다른 결과가 나올 수 있어 — 캡처된 변수는 복사본이거나 참조일 수 있고, 바뀌면 로직에 영향이 가.

실수 3: IDE의 private 메서드 로컬 함수 변환 제안 무시.
클래스에 쓸데없는 private 메서드를 남겨두면, IDE(예: Rider)가 로컬 함수로 바꾸라고 해도 무시하면 코드에 "쓰레기"가 쌓이고 가독성이 떨어져.

실수 4: 로컬 함수 중첩이 너무 깊음.
함수가 너무 많이 중첩되면 이해와 디버깅이 힘들어져. 간결함이 유지보수의 핵심이야.

2
과제
C# SELF, 레벨 11, 레슨 4
잠금
두 숫자의 합을 계산하기 위한 로컬 함수 사용
두 숫자의 합을 계산하기 위한 로컬 함수 사용
2
과제
C# SELF, 레벨 11, 레슨 4
잠금
숫자의 짝수/홀수 여부를 확인하는 로컬 함수
숫자의 짝수/홀수 여부를 확인하는 로컬 함수
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION