---
title: "넘어가는 것은 값의 사본이다"
description: "메서드 안에서 매개변수를 바꿔도 밖의 원본은 그대로입니다. 값을 적어 보낸 것이지 변수를 보낸 것이 아닙니다."
author: "유니얼"
pubDate: 2026-09-08
category: "programming/csharp/basics"
tags: ["C#", "기초"]
canonical: https://blog.unialgames.com/posts/csharp-method-parameters/
---
메서드를 부를 때 괄호에 값을 넣습니다. 그럼 그 값이 어떻게 넘어갈까요.

```csharp
void AddTen(int n)
{
    n = n + 10;
    Console.WriteLine(n);
}

int score = 50;
AddTen(score);
Console.WriteLine(score);
```

`AddTen` 안에서는 `60` 이 나옵니다. 그럼 마지막 줄은 무엇이 나올까요.

`50` 입니다.

## 부르는 쪽 이름과 받는 쪽 이름은 남남이다

먼저 말을 정리하고 갑니다.

- 정의에 적는 것이 **매개변수**입니다. 위에서 `int n` 입니다.
- 호출할 때 실제로 넣는 것이 **인수**입니다. 위에서 `score` 입니다.

이름이 달라도 됩니다. 같아도 됩니다. **서로 아무 상관이 없기 때문입니다.**
매개변수 이름은 메서드 안에서만 통합니다. 밖에서는 무슨 이름이든 상관없습니다.

```csharp
int score = 50;
int point = 30;

AddTen(score);
AddTen(point);
AddTen(80);        // 이름 없는 값을 그대로 넣어도 됩니다
```

`AddTen` 은 받은 것이 `score` 인지 `point` 인지 `80` 인지 모릅니다.
**값만 받습니다.**

## 값이 복사되어 들어간다

앞에서 변수는 값을 담는 자리라고 했습니다.
메서드를 부를 때 일어나는 일은 **그 값을 새 자리에 옮겨 적는 것**입니다.

<x-flow>
<script type="application/json">
{
  "label": "밖과 안이 다른 자리다",
  "dwell": 1.5,
  "lines": [
    "void AddTen(int n)",
    "{",
    "    n = n + 10;",
    "    Console.WriteLine(n);",
    "}",
    "",
    "int score = 50;",
    "AddTen(score);",
    "Console.WriteLine(score);"
  ],
  "cases": [
    {
      "label": "score = 50",
      "state": "밖의 score = 50",
      "path": [
        { "line": 6, "why": "밖에 자리 하나가 생기고 50 이 들어갑니다.", "state": "밖의 score = 50" },
        { "line": 7, "why": "호출합니다. score 를 보내는 것이 아니라 그 안의 값 50 을 적어 보냅니다." },
        { "line": 0, "why": "안에 새 자리 n 이 생기고 50 이 복사되어 들어갑니다.", "state": "밖의 score = 50 · 안의 n = 50" },
        { "line": 2, "why": "n 을 고칩니다. 고쳐지는 것은 안의 자리입니다.", "state": "밖의 score = 50 · 안의 n = 60" },
        { "line": 3, "why": "안에서는 60 이 맞습니다.", "out": "60" },
        { "line": 8, "why": "메서드가 끝나면 안의 자리는 사라집니다. 밖은 처음 그대로입니다.", "state": "밖의 score = 50", "out": "50" }
      ],
      "skip": [1, 4],
      "note": "두 개의 자리가 있었고, 고친 것은 그중 하나였습니다."
    }
  ]
}
</script>
</x-flow>

**자리가 두 개**입니다. 밖의 `score` 와 안의 `n` 은 다른 자리입니다.
`n` 을 고쳐도 `score` 는 모릅니다.

종이에 적은 숫자를 불러 주고 상대가 자기 종이에 받아 적은 것과 같습니다.
상대가 자기 종이의 숫자를 고쳐도 내 종이는 그대로입니다.

## 그래서 값을 바꾸려면 돌려받는다

밖의 값을 바꾸고 싶으면 **돌려받아서 다시 담습니다.**

```csharp
int AddTen(int n)
{
    return n + 10;
}

int score = 50;
score = AddTen(score);      // 돌려받은 것을 다시 담습니다
Console.WriteLine(score);   // 60
```

앞에서 문자열 메서드가 이랬습니다. `s.ToUpper()` 는 아무 일도 안 일어나고
`s = s.ToUpper()` 라야 바뀌었습니다.

이유가 이제 보입니다. 특별한 규칙이 아니라 **메서드가 원래 그렇게 동작하기 때문**입니다.
받은 것을 고쳐도 부른 쪽에는 안 보이니, 결과를 돌려주는 것 말고는 알릴 방법이 없습니다.

## 안에서 마음대로 써도 된다

사본이라는 성질에는 좋은 점도 있습니다.
**매개변수를 안에서 자유롭게 고쳐 써도 밖에 영향이 없습니다.**

```csharp
int Countdown(int n)
{
    int total = 0;

    while (n > 0)
    {
        total += n;
        n--;              // 매개변수를 직접 줄여도 됩니다
    }

    return total;
}
```

`n` 을 반복문 변수처럼 써 버렸습니다. 그래도 부른 쪽 값은 멀쩡합니다.
따로 변수를 만들어 옮겨 담을 필요가 없습니다.

다만 읽기에는 헷갈릴 수 있습니다. 매개변수는 "받은 값" 이라는 뜻이 강해서,
중간에 다른 뜻으로 바뀌면 읽는 사람이 되짚어야 합니다.
길어지는 메서드에서는 새 변수를 만드는 편이 낫습니다.

## 목록을 넘기면 결과가 다르다

여기까지는 `int` 하나를 넘겼습니다. 그런데 `List` 나 배열을 넘기면 이야기가 달라집니다.

```csharp
void AddItem(List<int> list)
{
    list.Add(99);
}

List<int> nums = new List<int> { 1, 2 };
AddItem(nums);
Console.WriteLine(nums.Count);    // 3
```

이번에는 밖이 바뀝니다. 앞에서 본 것과 반대입니다.

지금 단계에서는 **그런 경우가 있다는 것만** 알아 두면 됩니다.
이유를 제대로 말하려면 `List` 를 담은 변수 안에 실제로 무엇이 들어 있는지를 봐야 하는데,
그건 클래스와 객체를 다룰 때 정리합니다.

당장 기억할 것은 하나입니다.
**숫자나 글자 하나를 넘겼다면 안에서 무엇을 하든 밖은 그대로입니다.**
바뀌길 원하면 `return` 으로 돌려받습니다.

## 정리

- 정의에 적는 것이 매개변수, 호출할 때 넣는 것이 인수입니다.
- 매개변수 이름은 메서드 안에서만 통합니다. 밖의 변수 이름과 상관없습니다.
- 넘어가는 것은 **값의 사본**입니다. 안과 밖은 다른 자리입니다.
- 그래서 안에서 매개변수를 고쳐도 밖은 그대로입니다.
- 밖의 값을 바꾸려면 `return` 으로 돌려주고 다시 담습니다.
- 매개변수를 안에서 마음대로 써도 안전합니다. 다만 읽기에 헷갈릴 수 있습니다.
- `List` 나 배열처럼 여러 개를 담는 것을 넘기면 결과가 다릅니다.