C#

ref, out, in - C# 파라미터 키워드 제대로 구분하자

Juan_ 2025. 6. 4. 21:24
728x90

ref, out, in - C# 파라미터 키워드 제대로 구분하자

개발하다 보면 한 번쯤은 이런 코드를 마주친다.


public void DoSomething(ref int value) { ... }
public void DoSomething(out int result) { ... }
public void DoSomething(in int input) { ... }

처음 보면 다 비슷하게 생겼다. '결국 값을 전달하는 건데 ref, out, in 이게 도대체 뭐가 다른 건데?' 라는 생각이 들 수밖에 없다.

심지어 StackOverflow나 구글에서 검색해도 다들 "ref는 이렇고 out은 저렇고..." 이론적인 설명만 한가득이다. 그런데 그걸 그대로 복붙하듯 외워봤자 실무에선 잘 써지지 않는다.

이번 글에선 실제 상황을 기반으로 세 가지 키워드를 확실하게 정리해보자.


문제 상황: "값을 바꾸고 싶다는데 왜 안 바뀌는 거지?"


public void ChangeValue(int number)
{
    number = 100;
}

int x = 5;
ChangeValue(x);
Console.WriteLine(x); // ???

출력은 100일까? 아니다. 5가 출력된다.

왜냐면 C#은 값 타입(value type)을 함수로 넘기면 "복사본"이 전달되기 때문이다. 함수 안에서 아무리 바꿔도 원본은 그대로다.

그래서 나온 게 바로 ref, out, in이다.


ref - 값도 주고, 결과도 받고 싶을 때


public void Double(ref int num)
{
    num = num * 2;
}

int x = 10;
Double(ref x);
Console.WriteLine(x); // 20

ref원본 값을 넘기고, 함수 안에서 그 값을 바꿔서 되돌려줄 수 있다. 즉, "얘를 네가 좀 바꿔줘" 라는 느낌이다.

  • 주의: 함수 호출 전에 반드시 초기화되어 있어야 한다.

실무에서는 이런 경우에 쓴다:

  • 여러 값을 리턴하고 싶은데, return은 하나밖에 못할 때
  • 성능상 값 복사가 부담스러울 때 (큰 구조체 등)

out - 값을 받아오고 싶을 때 (반드시!)


public void TryParse(string s, out int result)
{
    result = int.Parse(s);
}

int number;
TryParse("123", out number);
Console.WriteLine(number); // 123

out은 "내가 뭔가를 계산해서 값을 넣어줄게. 너는 빈 손으로 와도 돼"라는 의미다.

  • 주의: 함수 안에서 반드시 값을 설정해야 한다.

out은 특히 Try-패턴에서 자주 쓰인다. 예를 들어 C#의 int.TryParse, DateTime.TryParse 이런 메서드들이 대표적이다.


in - 참조는 주지만, 건들지 마


public void PrintLength(in string text)
{
    Console.WriteLine(text.Length);
}

string message = "Hello World";
PrintLength(in message);

in은 참조는 넘기되, 함수 안에서 읽기만 가능하다. 수정은 안 됨.

  • 주의: C# 7.2부터 지원됨

in은 언제 쓰나?

  • 읽기 전용으로 참조를 넘기고 싶을 때
  • 성능 최적화를 위해 큰 구조체를 읽기 전용으로 넘길 때

실무 예시: 다중 결과 처리


public bool TryGetUser(string id, out User user)
{
    if (db.Contains(id)) {
        user = db.Get(id);
        return true;
    }
    user = null;
    return false;
}

User foundUser;
if (TryGetUser("hong", out foundUser))
{
    Console.WriteLine(foundUser.Name);
}

이럴 때 out이 없다면 어떻게 해야 할까?

  • User 객체를 null로 리턴하고
  • 리턴값을 따로 받고
  • 상태 체크도 따로 해야 하고...

→ out은 리턴값과 별도로 데이터를 전달하는 깔끔한 방법이다.


언제 어떤 걸 써야 할까?

구분 ref out in
의미 값 넘기고 바꿔서 받기 초기화 안 해도 됨, 바꿔서 받기 읽기 전용으로 넘기기
초기화 필요? 필요함 필요 없음 필요함
함수 내 설정 해도 되고 안 해도 됨 반드시 해야 함 못 함
용도 양방향 데이터 전달 다중 값 리턴 읽기 전용 최적화

마무리하며

ref, out, in은 전부 "참조로 넘긴다"는 공통점이 있지만, 의도와 사용법은 완전히 다르다.

  • "값을 받아오고 싶다" → out
  • "값을 넘기고 바꾸고 싶다" → ref
  • "읽기만 하고 싶다" → in

이렇게 정리해두면, 앞으로 코드를 볼 때도 작성할 때도 기준이 생긴다.

무조건 ref부터 쓰는 습관은 버리고, 그 상황에서 어떤 목적이 있는지를 먼저 따져보자. 그게 실력자의 차이다.

728x90