#30 #프로그래밍에서의 Call by Value와 Call by Reference의 차이점

서영·2026년 3월 23일
post-thumbnail

Call by Value와 Call by Reference, 도대체 뭐가 다른 걸까?

최근 자료구조를 공부하면서 메소드에 값을 전달하는 부분을 배우다가 또 하나의 의문이 생겼습니다.

"메소드에 변수를 넘겨줄 때, 변수 자체가 넘어가는 걸까? 아니면 변수에 들어있는 값만 넘어가는 걸까?"

처음에는 단순하게 생각했습니다.

int a = 10;
change(a);

그러면 achange() 메소드로 넘어가는 거니까
당연히 a 자체를 전달하는 거 아닌가?

그런데 공부하다 보니 Call by Value(값에 의한 호출)Call by Reference(참조에 의한 호출)라는 개념을 발견하게 되었습니다.

그리고 더 헷갈렸던 점은...

"Java는 Call by Reference가 아니라 Call by Value만 사용한다."

라는 이야기였습니다.

객체를 전달하면 원본 객체의 값이 변경되기도 하는데, 그렇다면 이건 Call by Reference가 아닌가?

그래서 이번 글에서는 Call by Value와 Call by Reference의 차이를 정리하고, 특히 Java에서는 왜 객체를 전달해도 Call by Value라고 하는지 알아보려고 합니다!


1. 먼저 Call이 뭐길래?

Call by Value와 Call by Reference를 이해하기 전에 먼저 Call이 무엇인지 알아야 합니다.

프로그래밍에서 Call은 말 그대로 함수를 호출한다는 의미입니다.

예를 들어 Java에서

public static void hello(String name) {
    System.out.println("안녕하세요 " + name);
}

라는 메소드가 있을 때,

hello("서영");

처럼 메소드를 호출할 수 있습니다.

이때 "서영"이라는 값을 메소드의 매개변수 name에게 전달하게 됩니다.

즉,

hello("서영")
       ↓
     name

이런 식으로 값이 전달됩니다.

그렇다면 여기서 중요한 질문이 생깁니다.

무엇을 전달하는 것일까?

이것을 설명하는 대표적인 방식이 바로

  • Call by Value
  • Call by Reference

입니다.


2. Call by Value란?

값을 복사해서 전달하는 방식

Call by Value는 말 그대로 값에 의한 호출입니다.

쉽게 말하면,

변수가 가지고 있는 값을 복사해서 전달하는 방식

이라고 생각하면 됩니다.

예를 들어보겠습니다.

public static void change(int number) {
    number = 20;
}

public static void main(String[] args) {
    int num = 10;

    change(num);

    System.out.println(num);
}

결과는 어떻게 나올까요?

10

입니다.

20이 나오지 않을까요?

change(num)을 호출할 때 num에 들어있는 10이라는 값이 number복사되어 전달되기 때문입니다.

그림으로 표현하면 다음과 같습니다.

num
┌───────┐
│  10   │
└───────┘
    │
    │ 값 복사
    ↓
number
┌───────┐
│  10   │
└───────┘

이후

number = 20;

을 실행하면

num       number
┌────┐    ┌────┐
│ 10 │    │ 20 │
└────┘    └────┘

이렇게 됩니다.

서로 다른 공간에 있는 값이기 때문에 number를 변경해도 num에는 영향을 주지 않습니다.


3. Call by Reference란?

변수의 위치를 전달하는 방식

이번에는 Call by Reference입니다.

Call by Reference는 값을 복사하는 것이 아니라 변수가 저장된 위치, 즉 참조 자체를 전달하는 방식입니다.

쉽게 표현하면,

"여기에 있는 변수 자체를 사용해!"

라고 알려주는 것입니다.

그래서 메소드 안에서 해당 값을 변경하면 호출한 쪽의 원본 변수도 변경될 수 있습니다.

개념적으로 표현하면 다음과 같습니다.

original
┌───────┐
│  10   │
└───────┘
    ↑
    │ 같은 대상을 가리킴
    │
reference

따라서 메소드 내부에서 값을 변경하면 원본에도 영향을 줄 수 있습니다.


4. 둘의 차이를 한눈에 보면?

구분Call by ValueCall by Reference
전달하는 것값의 복사본변수에 대한 참조
원본 변경보통 변경되지 않음변경될 수 있음
새로운 값 저장별도의 값에 저장원본 변수 자체를 사용할 수 있음
핵심값을 복사변수를 참조

가장 간단하게 기억한다면,

Call by Value = 값을 복사해서 전달

Call by Reference = 변수 자체를 참조하도록 전달

이라고 기억하면 됩니다! 👍


5. 그런데 Java는 Call by Reference가 없다?

여기서부터가 진짜 헷갈리는 부분입니다.

Java를 공부하다 보면 다음과 같은 코드를 볼 수 있습니다.

class Student {
    String name;
}

public static void changeName(Student student) {
    student.name = "철수";
}

public static void main(String[] args) {
    Student s = new Student();
    s.name = "영희";

    changeName(s);

    System.out.println(s.name);
}

결과는

철수

가 출력됩니다.

어라?

분명 메소드 안에서 student.name을 변경했더니 원래 s.name도 변경되었습니다.

그렇다면...

Java도 Call by Reference 아닌가?!

라고 생각하기 쉽습니다.

하지만 Java는 Call by Reference가 아닙니다.

Java는 항상 Call by Value 방식으로 값을 전달합니다.


6. 그럼 객체는 왜 원본이 변경될까?

이 부분이 Java에서 가장 많이 헷갈리는 부분입니다.

핵심은 바로

객체 자체가 전달되는 것이 아니라 객체를 가리키는 참조값이 복사되어 전달된다.

는 것입니다.

조금 어렵죠?

그림으로 보면 훨씬 쉽습니다.

Student s = new Student();

이 코드를 실행하면 실제 객체가 만들어지고 s는 그 객체를 가리키게 됩니다.

s
│
│ 참조값
↓
┌──────────────┐
│ Student 객체 │
│ name = "영희" │
└──────────────┘

그리고

changeName(s);

을 호출하면 s가 가지고 있는 참조값이 복사됩니다.

s                         student
│                            │
│ 참조값 복사                │
└──────────────┬─────────────┘
               ↓
        ┌──────────────┐
        │ Student 객체 │
        │ name = "영희" │
        └──────────────┘

즉,

s ───────────────┐
                 ↓
           [ Student 객체 ]
                 ↑
                 │
student ─────────┘

sstudent서로 다른 변수입니다.

하지만 두 변수가 같은 Student 객체를 가리키고 있는 것입니다.

그래서

student.name = "철수";

라고 하면 두 변수가 바라보고 있는 같은 객체의 내용이 변경됩니다.


7. 그런데 이것도 Call by Reference 아닌가?

여기서 많은 사람들이 헷갈립니다.

"같은 객체를 가리키고 있으니까 Call by Reference 아닌가?"

라고 생각할 수 있습니다.

하지만 차이는 참조값 자체가 복사되었다는 것입니다.

예를 들어 다음 코드를 살펴보겠습니다.

public static void change(Student student) {
    student = new Student();
    student.name = "철수";
}

public static void main(String[] args) {
    Student s = new Student();
    s.name = "영희";

    change(s);

    System.out.println(s.name);
}

결과는

영희

입니다.

왜일까요?

메소드 안에서 student에 새로운 객체를 넣었기 때문입니다.

처음

s ──────────→ [Student 객체 A]
               name = "영희"

student ─────→ [Student 객체 A]

메소드 안에서

student = new Student();

를 실행하면

s ──────────→ [Student 객체 A]
               name = "영희"

student ─────→ [Student 객체 B]
               name = "철수"

가 됩니다.

student가 다른 객체를 가리키도록 변경되었을 뿐, s가 가지고 있던 참조값은 바뀌지 않습니다.

그래서 s.name은 여전히 "영희"입니다.

이것이 Java가 Call by Reference가 아니라 Call by Value라는 중요한 근거입니다.


8. 핵심은 "참조값도 값이다"

이 부분만 기억해도 Java의 Call by Value를 상당히 쉽게 이해할 수 있습니다.

Java에서는

int num = 10;

처럼 기본형을 전달할 때는

10이라는 값
↓
복사
↓
매개변수

가 됩니다.

객체를 전달할 때는

객체를 가리키는 참조값
↓
복사
↓
매개변수

가 됩니다.

즉,

객체를 전달한다고 해서 Reference 방식으로 전달되는 것이 아닙니다.

객체의 참조값을 Value 방식으로 복사해서 전달하는 것입니다.

이 때문에 Java에서는

"Java는 Pass by Value만 지원한다."

라고 설명합니다.


9. 그렇다면 Call by Value와 Reference의 차이는?

조금 더 직관적으로 비교해보겠습니다.

Call by Value

원본 변수
┌───────┐
│  10   │
└───────┘
    │
    │ 값 복사
    ↓
매개변수
┌───────┐
│  10   │
└───────┘

두 값은 서로 독립적입니다.

그래서 매개변수를 변경해도 원본에는 영향을 주지 않습니다.


Call by Reference

원본 변수
┌───────┐
│  10   │
└───────┘
    ↑
    │ 같은 변수
    │
매개변수

같은 변수를 참조하기 때문에 매개변수를 통해 값을 변경하면 원본에도 영향을 줄 수 있습니다.


10. Java에서는 이렇게 기억하면 된다!

Java를 공부하면서 가장 헷갈리는 부분을 정리하면 다음과 같습니다.

기본형
int, double, boolean ...
        ↓
값 자체가 복사됨

참조형
Student, ArrayList, String ...
        ↓
참조값이 복사됨

둘 다 결국 Call by Value입니다.

차이는 무엇을 값으로 전달하느냐입니다.

기본형 → 실제 값

참조형 → 객체를 가리키는 참조값

이렇게 기억하면 훨씬 편합니다! ✨


11. JavaScript에서는 어떨까?

그렇다면 JavaScript는 어떨까요?

JavaScript 역시 기본적으로 Call by Value 방식으로 동작합니다.

예를 들어

let num = 10;

function change(value) {
    value = 20;
}

change(num);

console.log(num);

결과는

10

입니다.

하지만 객체를 전달하면 조금 다르게 보입니다.

const student = {
    name: "영희"
};

function change(obj) {
    obj.name = "철수";
}

change(student);

console.log(student.name);

결과는

철수

입니다.

Java와 비슷하죠?

이 역시 객체 자체가 Call by Reference로 전달된 것이 아니라, 객체를 가리키는 참조값이 복사되어 전달되기 때문이라고 이해할 수 있습니다.


12. 가장 많이 하는 착각

여기서 하나 짚고 넘어가야 할 것이 있습니다.

❌ 잘못된 이해

"객체를 전달하면 Call by Reference다."

⭕ 올바른 이해

"객체를 가리키는 참조값이 복사되어 전달된다."

특히 Java에서는 이 차이가 굉장히 중요합니다.

객체 내부의 값을 변경하는 것과

student.name = "철수";

매개변수가 다른 객체를 가리키게 만드는 것은

student = new Student();

완전히 다른 동작입니다.

이 둘을 구분하면 Call by Value를 훨씬 쉽게 이해할 수 있습니다.


마무리

오늘은 프로그래밍을 공부하면서 한 번쯤은 들어봤을 Call by Value와 Call by Reference에 대해 정리해보았습니다.

처음에는 단순히

"값을 넘기면 Call by Value, 주소를 넘기면 Call by Reference!"

정도로 생각했는데, Java에서 객체를 전달하는 과정을 살펴보니 생각보다 훨씬 복잡한 개념이었습니다. 🤯

특히 가장 중요했던 것은

Java는 Call by Reference가 아니라 항상 Call by Value이다.

라는 점이었습니다.

객체를 전달할 때도 객체 자체를 전달하는 것이 아니라 객체를 가리키는 참조값을 복사해서 전달하기 때문에 Call by Value라고 할 수 있습니다.

결국 이렇게 기억하면 됩니다.

Call by Value
→ 값을 복사해서 전달

Call by Reference
→ 변수 자체를 참조

Java
→ 무조건 Call by Value
→ 객체의 경우 "참조값"을 복사해서 전달

처음에는 "객체의 값이 바뀌는데 왜 Call by Value지??" 싶었지만,
객체와 객체를 가리키는 참조값을 분리해서 생각하니까 훨씬 이해하기 쉬웠습니다. 🌱

프로그래밍을 공부하다 보면 처음에는 그냥 외웠던 개념도
"왜 이렇게 동작하지?"라는 의문을 가지고 파고들었을 때 훨씬 오래 기억에 남는 것 같습니다.

이번에도 수업에서 생긴 작은 의문 하나 덕분에
Java의 메모리와 객체 전달 방식까지 조금 더 깊게 이해할 수 있었던 것 같아요! ✨

그럼 모두

즐코! 💻🌸


참고

  • Oracle Java Documentation
  • Java Language Specification
  • MDN Web Docs — Functions
  • MDN Web Docs — JavaScript의 객체와 참조
profile
시대를 따라가는 개발자가 아닌, 시대를 이끄는 개발자

0개의 댓글