[개발] Pass By Value vs Pass By Reference 란?
resilient
·2022. 2. 28. 23:19
최근 첫 기술면접을 봤습니다. 면접은 무조건 여러 번을 봐야 한다는 말을 새삼 느끼게 되더군요.
면접 질문 중에 Pass by value, Pass by reference에 대해서 아시나요? 가 있었습니다. 이번 기회에 대해 제대로 알아보려고 합니다.
0. 자바스크립트(Javascript)에서의 Primitive Type(원시 타입) vs Reference Type (참조 타입)
각 언어마다 변수를 넘겨주는 방법은 다양합니다. 먼저 자바스크립트에서는 원시 타입과 참조 타입이라는 두가지 자료형이 있습니다.
원시 타입은 숫자(Number), 불린(Boolean), undefined, 문자열(String), null 이렇게 5가지가 있습니다.
참조 타입에는 객체(Object), 배열(Array), 함수(Function)이 있죠.
먼저 원시 타입 데이터는 변수에 할당될 때, 메모리 상에 고정된 크기로 저장되고 해당 변수가 원시 데이터의 값을 보관합니다. 원시 타입 자료형은 변수 초기화, 할당 시 값이 저장된 메모리 영역에 직접 접근합니다. 변수에 새 값이 할당될 때 변수에 할당된 메모리 블록에 저장된 값을 바로 변경하는 것이죠.
반면에 참조 타입 데이터는 크기가 정해져 있지 않고 변수에 할당될 때 값이 직접 해당 변수에 저장될 수 없으며 변수에는 데이터에 대한 참조가 저장이 됩니다. 변수의 값이 저장된 힙 메모리의 주소값을 저장하는 것이죠. 참조 타입은 변수의 값이 저장된 메모리 블록의 주소를 가지고 있고, 자바스크립트 엔진이 변수가 가지고 있는 메모리 주소를 이용해서 변수의 값에 접근합니다.
1. Pass By Value(값에 의한 전달) 과 Pass By Reference(참조에 의한 전달)
위에서 예를 들었던 자바스크립트에서 원시타입들은 Pass By Value 방식을 사용해서 변수를 넘겨줍니다.
Pass By Value는 복사된 데이터를 전달하여 구성해서 값을 수정해도 원본 데이터에는 영향을 주지 않도록 하는 방식입니다.
아래 예를 들어보겠습니다.
function test(a){
a = 44;
}
var a = 33;
test(a);
console.log(a); // 33, Access By Value
위의 test함수는 매개변수로 전달 받은 Number형인 a를 44로 변경하고 있지만 함수가 종료되고 출력되는 값은 33입니다. 원본의 값은 기존의 상태를 유지하고 있는 것이죠. 메모리와 연관 지어서 살펴보겠습니다.
1-1. Pass By Value(값에 의한 전달)의 동작 방식
먼저 test함수를 호출하면 stack메모리에 return 주소가 먼저 쌓이고 그 위로 높은주소에서 낮은 주소으로 매개변수 등의 값이 쌓이게 됩니다. 위 코드의 메모리 할당을 그림으로 표현하면 아래 그림과 같습니다.
var a를 선언할 때 33이라는 값은 전역변수로 프로그램이 실행될 때 동시에 데이터 영역 공간에 할당되어 종료 시까지 존재합니다. 여기서 test라는 함수가 실행되면 스택에 test함수가 들어오게 되고 그 위로 a = 44라는 값이 들어오게 되죠.
여기서 중요한점은 아래 데이터 영역의 전역 변수인 a = 33은 그대로 유지된 채로 새롭게 메모리를 할당해서 33이라는 값만 참고해서 test에 넘어가는 파라미터 값을 넘겨주지만 주소 값은 다르기 때문에 서로 다른 존재임을 알 수 있습니다.
그럼 여기서 test함수가 종료되고 스택에서 pop된 후, 해당 메모리가 모두 없어지면 어떻게 될까요?
위 사진과 같이 메모리 스택에서 회색부분이 pop 되어도 0x02번 주소의 33은 그대로 있기 때문에 값이 유지될 수 있는 것입니다. 이렇게 어떤 함수를 호출할 때 파라미터로 값을 복사해서 전달하는 방식을 Pass By Value라고 하는 것이죠.
위에서 예를 들었던 자바스크립트에서 레퍼런스 타입들은 Pass By Reference방법으로 변수를 넘겨줍니다.
Pass By Reference(참조에 의한 전달)는 주소 값을 전달하여 실제 값에 대한 Alias(별명)를 구성함으로써, 값을 수정하면 원본의 데이터가 수정되도록 하는 방식입니다.
아래 예시를 들어보겠습니다.
function test(b){
b.name = "name changed to c";
}
var b = {
name : "test object"
};
test(b);
console.log(b); // { name: 'name changed to c' }
위의 test함수는 매개변수로 전달 받은 Object형인 name을 "name changed to c"로 변경합니다. 결과적으로 함수가 종료되고 b를 출력해보면 값이 바뀐 것을 알 수 있죠. 이번에는 실제 값에 대한 Alias(별명)를 넘겼기 때문에 test함수에서 값을 변경하면 실제 전역변수의 b.name에도 영향을 주게 됩니다. 이러한 Pass By Reference의 동작 방식 역시 메모리와 연관지어서 살펴보겠습니다.
1-2. Pass By Reference(참조에 의한 전달)의 동작 방식
먼저 test함수를 호출하면 stack메모리에 return 주소가 먼저 쌓이고 그 위로 높은 주소에서 낮은 주소으로 매개변수 등의 값이 쌓이게 됩니다. 위 코드의 메모리 할당을 그림으로 표현하면 아래 그림과 같습니다. 여기까지는 Pass By Value 방법과 같죠.
하지만 잘 보시면 Pass By Value는 3이라는 값을 0x01주소로 그대로 가져오지만, 이번에는 0x01 주소에 "test obejct"라는 문자열이 아닌 0x02라는 주소 값이 담겨있습니다. 실제 값의 주소가 쌓이게 되는 것이죠.
test함수에서 파라미터로 전달받은 b는 전역변수 b가 저장된 값을 참조하는 Alias(별명)이고 b객체 안의 name을 "name changed to c"로 변경하는 것은 b가 가리키고 있는 메모리의 값을 "test object"에서 "name changed to c"로 변경하는 것입니다. 때문에 함수가 종료돼도 b를 출력해보면 "name changed to c"로 변경된 것을 알 수 있죠.
2. 자바(Java) 에서의 Primitive Type(원시 타입) vs Reference Type (참조 타입)
Java Language Specification의 (Section 4.3) 에서는 원시 값이든 객체든 상관없이 모든 데이터를 Pass By Value로 전달한다고 나와 있습니다.
앞의 포스팅에서 살펴보았듯 원시 값은 Stack 메모리에 실제 값이 저장되고, 객체는 실제 메모리를 참고하기 위한 값이 저장된다는 것을 확인했습니다. 그렇기 때문에 Java에서는 객체에 한해 확장된 규칙이 적용된다고 합니다. 바로 개체와 관련되어 복사 후 전달되는 값은 실제 메모리를 가리키는 Reference(참조값)인 포인터라는 것이죠.
Java에서 객체를 생성할 때 Dog dog = new Dog()과 같은 표현을 사용합니다. 여기서 dog은 실제로 생성된 Dog 클래스의 객체를 저장하고 있는 것이 아니고, Dog 클래스의 객체가 저장된 주소 값(포인터 값)을 가지고 있는 것이죠. 그리고 Java에서 객체를 전달한다고 하면 이러한 dog 변수가 복제되어 전달되는 것입니다. Object Section (Section 4.3.1)에 따르면 Java 언어에서는 이를 Object Reference라고 부르며, 객체가 전달될 때마다 복제됩니다.
Object Reference를 통해서는 다음과 같은 연산들이 가능하다고 합니다.
- Field 값으로의 접근
- Method Invocation
- The Cast Operator
- String의 + 연산자
- instanceof 연산자
- == 또는 != 또는? :
그렇기 때문에 우리는 Java에서 어떤 객체가 파라미터로 전달되었을 때, 필드 값에 접근하여 해당 값을 수정하는 것은 가능하지만 그 객체 자체는 변경 불가능한 것입니다.
Java의 객체 전달 방식이 Pass By Reference가 아닌가 혼란이 오는데요. 하지만 앞서 정의되었던 Pass By Reference의 정의를 살펴보면 이해가 갈 것 같습니다. Pass By Reference(참조에 의한 전달)는 주소 값을 전달하여 실제 값에 대한 Alias(별명)를 구성함으로써, 값을 수정하면 원본의 데이터가 수정되도록 하는 방식이라고 정의하였기 때문이죠. 결론적으로 원본데이터가 수정되어야 Pass By Reference방법이라고 할 수 있겠네요.
3. 정리
이번시간에는 Pass By Value, Pass By Reference에 대해서 알아보았습니다. 자바에서의 객체가 Pass By Reference방법을 사용하지 않는다는 것도 흥미로웠는데요. 앞으로 자바 공부를 더 해가면서 제대로 이해해봐야겠습니다.
감사합니다.
4. Reference
'글 > 개발' 카테고리의 다른 글
[개발/Spring] Soft delete vs Hard delete에 대한 나의 생각 (0) | 2023.03.29 |
---|---|
[Git] Git rebase, Git squash로 커밋 정리하기 (0) | 2021.11.30 |
[개발]TDD / 테스트코드 란? (0) | 2021.08.10 |
[에러/삽질] vscode 에서 Delete `␍`eslintprettier/prettier 해결 (0) | 2021.08.08 |
[에러/삽질]Cannot use import statement outside a module 해결 (0) | 2021.08.01 |