JavaScript

참조

복제

전자화된 시스템의 가장 중요한 특징은 복제다. 현실의 사물과 다르게 전자화된 시스템 위의 데이터를 복제 하는데는 비용이 거의 들지 않는다. 바로 이러한 특징이 소프트웨어를 기존의 산업과 구분하는 가장 큰 특징일 것이다. 프로그래밍에서 복제가 무엇인가를 살펴보자.

var a = 1;
var b = a;
b = 2;
console.log(a);	// 1

결과

1

결과는 당연하다. 값을 변경한 것은 변수 b이기 때문에 변수 a에 담겨있는 값은 그대로이다. 변수 b의 값에 변수 a의 값이 복제된 것이다. 이를 그림으로 표시하면 아래와 같다.

 

참조

그런데 자연의 산물이 아니라 거대한 약속의 집합인 소프트웨어의 세계에서 당연한 것은 없다. 이것이 당연하지 않은 이유는 다음 예제를 통해서 좀 더 분명하게 드러난다.

var a = {'id':1};
var b = a;
b.id = 2;
console.log(a.id);	// 2

결과

2

이 코드의 주인공은 아래와 같다.

b.id = 2;
System.out.println(a.id);	

놀라운 차이점이 있다. 변수 b에 담긴 객체의 id 값을 2로 변경했을 뿐인데 a.id의 값도 2가 된 것이다. 이것은 변수 b와 변수 a에 담긴 객체가 서로 같다는 것을 의미하다. 참조(reference)의 세계에 온 것을 환영한다.

앞서 필자는 전자화된 세계에서 가장 중요한 특징으로 복제를 들었다. 그런데 복제만으로 전자화된 시스템을 설명하는 것은 조금 부족하다. 비유하자면 복제는 파일을 복사하는 것이고 참조는 심볼릭 링크(symbolic link) 혹은 바로가기(윈도우)를 만드는 것과 비슷하다. 원본 파일에 대해서 심볼릭 링크를 만들면 원본이 수정되면 심볼릭 링크에도 그 내용이 실시간으로 반영되는 것과 같은 효과다. 심볼릭 링크를 통해서 만든 파일은 원본 파일에 대한 주소 값이 담겨 있다. 누군가 심볼릭 링크에 접근하면 컴퓨터는 심볼릭 링크에 저장된 원본의 주소를 참조해서 원본의 위치를 알아내고 원본에 대한 작업을 하게 된다. 다시 말해서 원본을 복제한 것이 아니라 원본 파일을 참조(reference)하고 있는 것이다. 덕분에 저장 장치의 용량을 절약할 수 있고, 원본 파일을 사용하고 있는 모든 복제본이 동일한 내용을 유지할 수 있게 된다. 참조는 전자화된 세계의 극치라고 할 수 있다.

프로그래밍에서 광범위하게 사용하는 라이브러리라는 개념도 일종의 참조라고 할 수 있다. 공용 라이브러리를 사용하게 되면 하나의 라이브러리를 여러 애플리케이션에서 공유해서 사용하게 된다. 라이브러리의 내용이 변경되면 이를 참조하고 있는 애플리케이션에도 내용이 반영되게 된다. 또 우리가 변수를 사용하는 이유도 말하자면 참조를 위해서라고 할 수 있을 것이다. 본질을 파악하면 이해력도 높아지고 암기할 것도 줄어든다.

아래 두 개의 구문의 차이점을 생각해보자.

a = 1;
a = {'id':1};

무엇일까? 전자는 데이터형이 숫자이고 후자는 객체다. 숫자는 원시 데이터형(기본 데이터형, Primitive Data Types)이다. 자바스크립트에서는 원시 데이터형을 제외한 모든 데이터 타입은 객체이다. 객체를 다른 말로는 참조 데이터 형(참조 자료형)이라고도 부른다. 기본 데이터형은 위와 같이 복제 되지만 참조 데이터형은 참조된다. 모든 객체는 참조 데이터형이다. 이를 그림으로 나타내면 아래와 같다.

정리하면 변수에 담겨있는 데이터가 원시형이면 그 안에는 실제 데이터가 들어있고, 객체면 변수 안에는 데이터에 대한 참조 방법이 들어있다고 할 수 있다.

함수

그럼 일종의 변수할당이라고 할 수 있는 메소드의 매개변수는 어떻게 동작하는가를 살펴보자. 조금 복잡하므로 꼼꼼하게 살펴봐야 한다. 예제를 보자.

다음은 원시 데이터 타입을 인자로 넘겼을 때의 동작 모습이다.

var a = 1;
function func(b){
    b = 2;
}
func(a);
console.log(a);

결과

1

다음은 참조 데이터 타입을 인자로 넘겼을 때 동작하는 장면이다. 

var a = {'id':1};
function func(b){
    b = {'id':2};
}
func(a);
console.log(a.id);	// 1

결과는 아래와 같다.

1

함수 func의 파라미터 b로 전달된 값은 객체 a이다. (b = a) b를 새로운 객체로 대체하는 것은 (b = {'id':2}) b가 가르키는 객체를 변경하는 것이기 때문에 객체 a에 영향을 주지 않는다.

하지만 아래는 다르다.

var a = {'id':1};
function func(b){
    b.id = 2;
}
func(a);
console.log(a.id);	// 2

파라미터 b는 객체 a의 레퍼런스다. 이 값의 속성을 바꾸면 그 속성이 소속된 객체를 대상으로 수정작업을 한 것이 되기 때문에 b의 변경은 a에도 영향을 미치게 된다. 

참조

댓글

댓글 본문
작성자
비밀번호
  1. 아사다마오리족
    프로그래밍에서 variable을읽을때는 오른쪽에서 왼쪽으로 읽는데요
    그래서
    a=b;라고할때 'b는 a라고칭한다' 라고할수잇어요.
    b라는게있는데 이걸 a라고 부를거야 라는거죠
    순서의문제같아요

    var a = 1;
    function func(b){
    b = 2;
    }
    func(a);

    여기서 1은a에요
    함수는 2를 인자값으로 만드는애네요
    function(a) 는 a라는애를 함수의 인자값(b)으로넣는거니까 오른쪽에서 왼쪽 으로 정한다했죠
    그니까 b=a가되는거져. (오른쪽에서왼쪽)

    님이말한
    a=b,
    b=c
    는 b라는애를 a라고부를거야 고
    c라는애를 아까말한b라는애으로 부를거야 네요.
    값도 변수가될수있
    오른쪽에서 왼쪽이라했으니까 오른쪽은 보통 어떤값이나 변수오고 왼쪽은 변수가 나와요
    대화보기
    • epsxk82
      좌변에는 변수이름만 올수 있고요.
      우변에는 변수이름도 올 수 있고, 원시데이터 값(1, 2, 3, 4, "string", [1,2,0.5,"four"]), 객체값({'firstField':1, 'secondField':"secound"})도 올 수 있습니다.

      프로그래밍언어에서 대입식이라는 것은 어떠한 메모리 공간(주소)에 어떠한 값을 할당하는 행위예요.
      그 어떠한 메모리 공간을 나타내는게 대입식의 좌변이고요.
      근데 우리는 그 복잡한 메모리 주소(0x23f342a3) 대신 변수를 쓴다고 했으니, 좌변은 변수가 되는 겁니다.
      두번째로, 그 어떠한 값을 나타내는게 우변입니다.
      우변에는 원시데이터 값과, 객체값이 올 수 있습니다. 여기에 한가지 더 예외적으로 이전에 선언한 메모리 주소 즉 변수도 올 수 있습니다. 그리고 우변으로 변수가 올때 이 변수가 가리키는 메모리 공간에 저장된 값이, 즉 변수값이, 원시데이터값이냐 객체값이냐에 따라 일어나는 일이 달라지는데 그것을 이 단원에서 다루고 있는 것이고요.

      a=b,
      b=c
      같은 변수b입니다. 그리고 우변에 변수이름도 올 수 있다고 말씀드렸지요?^^
      그리고 한 문맥에서 어떠한 이름을 갖는 변수는 반드시 한개만 존재할 수 있습니다. 즉 같은 이름의 변수가 여러문장에서 쓰이고 있다면 그것은 모두 같은 변수를 가르키고 있는 겁니다.

      혹시 그 외에 다른 개발관련 질문 있으시면 제 메일로 문의 주시기 바랍니다.^^ -> qdentabino@gmail.com
      아무래도 여기 주제외의 질문은 여기게시판보다는 개인적으로 질문하시는게 좋을 듯 싶습니다.
      대화보기
      • 신시내티
        우변은 원시데이터를 위한 자리
        좌변은 변수이름을 붙이는 자리.

        아 그렇군요.
        그럼
        a=b,
        b=c
        에서 저 두개의 b는 다른 건가요? 첫번째 b는 이름이고 두번째 b는 데이터인건지... ^____^;;;;;;

        (주말인데, 많이 도움을 주셔서 감사합니다. 왠지 차라도 대접해야 할것 같은 ㅎㅎㅎ)
        대화보기
        • epsxk82
          프로그래밍 언어에서,
          객체나 원시 데이터타입을 생성한다는 것은,
          즉 var customObj = {'firstFeild:1, 'secondField':"second"}; 또는 var int = 2;
          이는 컴퓨터의 메모리(램)의 어떤 공간에 그 데이터값( {'firstFeild:1, 'secondField':"second"}; 또는 2)을 할당하는 것을 말합니다.
          그리고 변수라는 것은 그 데이터값이 저장된 그 메모리 공간에 이름(customObj, int)을 붙여 주는 것입니다.

          왜 이름을 주냐고요?
          메모리를 다룰 때, 그 데이터가 저장된 공간을 식별하기 위해 본래 (내부적으로는) 변수 대신 주소체계라는것을 사용합니다. 그 주소체계는 다음과 같은 값들로 표기됩니다.
          e>0x00000000 0x12436433, 0x143df32a
          뭔가 가까이 하고 싶지않죠;; 위의 값들은 메모리의 주소값들을 16진수로 표현한 것인데요^^; 보통은 컴퓨터쪽에서는 저런식으로 메모리의 주소를 표기합니다.
          근데 변수 이름 대신에 메모리 주소값을 사용한다고 생각해보세요.
          var 0x34262346 = {'firstFeild:1, 'secondField':"second"}, var 0x34262388= 2;
          아무리 봐도 변수명을 쓰는게 가독성이 좋죠?^^;

          그리고 변수이름을 잘 짓는 습관은 매우 중요합니다.(물론 여기서 수업에서 쓰이는 예제들은 수업목적으로 만들어진 것이기 때문에 편의상 단순하게 이름을 붙이신 것이고요^^) 좋은 변수이름이란 내가 해결하고자 하는 문제(알고리즘)와 관련해서 그 변수가 쓰이는 용도에 걸맞는 이름을 가지는 것을 말합니다.
          좋은 변수이름은 다른 사람이 내 코드를 분석하거나 즉 가독성과, 내가 몇달 전/몇년 전 짜놓은 코드를 다시 읽고 수정하거나 하거나 즉 유지보수성, 또한 내가 프로그래밍을 하는데 있어서 복잡도를 줄여주는데 많은 영향을 미칩니다. 그래서 탑개발자들은 제품 코드를 짤 때, 변수나 함수이름에도 많은 공을 들입니다.
          예를 들어, 회계관리 프로그램에서 한해순익을 총 합산하는 함수를 만든다고 하면,
          function func(a)
          {
          var b = 0;
          for(i = 0; i < 12; ++i)
          {
          b += a[i];
          }

          return b;
          }

          function getTotalAnnualNetProfit(netProfitsByMonth)
          {
          var totalAnnualNetProfit = 0;
          for(var annualNetProfitIndex = 0; annualNetProfitIndex < 12; ++annualNetProfitIndex)
          {
          totalAnnualNetProfit += netProfitByMonth[annualNetProfitIndex]
          }

          return totalAnnualNetProfit;
          }
          위의 함수보다는 아래함수가 같은 일을 하는 거지만 확실히 무엇을 하고자 하는 것인지 코딩한 사람의 의도가 더 분명하게 파악이 되죠?^^


          본론으로 들어가서
          변수에 다른 변수를 대입하면 어떻게 되느냐..
          이는 등호= 우변에 오는 변수가 누구냐에 따라 다릅니다.

          우변에 오는 변수가 원시데이터라고 하면.
          메모리에 새로운 공간을 할당하고 거기에 우변변수의 값을 할당한다음 그 새로운 메모리공간에 좌변의 변수이름을 붙이는 겁니다. 이것이 여기 수업에서 이야기 하는 '복사'입니다.

          우변에 오는 변수가 객체라고 하면,
          그냥 우변에 오는 변수의 객체에 또 다른 이름을 부여하는 것입니다. 좌변변수이름을요. 이게 끝입니다^^;
          즉 위에서처럼 새로운 메모리를 할당하고 거기에 값을 할당하는 행위는 여기서 일어나지 않습니다. 그냥 기존에 만들어진 객체에 이름을 하나 더 부여하는 것입니다. 이것이 위에서 이야기 하는 '참조'입니다.
          그럼 왜 그냥 이름만 부여하고 끝나는 거이냐?
          그건 이야기 하자면 조금 복잡합니다만. 성능 문제와 관련됩니다^^;;
          자바스크립트 만이 아니라 C/C++을 제외한 Java, C#같은 다른 객체지향언어에서도 객체변수에 다른 객체변수를 대입하면 참조가 일어납니다.

          그래서
          var a = New A();
          var anotherA = a;
          위에서 변수 a와 anotherA는 결국에는 같은 메모리공간의 객체를 가르키는것이 때문에
          anotherA.field1 = 1;
          을 하게되면
          a.field1값도 1을 가지게 되는 겁니다.
          물론 반대로 a와 anotherA가 원시데이터 타입이라면 서로 다른 데이터공간에 각자의 데이터값을 가지고 있기 때문에 하나의 변수의 값을 수정하더라도 다른 변수의 값에는 변화가 없는 것이고요.
          대화보기
          • 신시내티
            문자, 숫자, 불리언은 프로퍼티나 메쏘드가 없는 원시 데이터타입이지만, 래퍼 객체으로 감싸져 있기 때문에, 객체처럼 사용되어 진다.

            원시 데이터는 복사가 된다: 복제된 사본은 원본과 서로 영향을 받지 않는다.
            반면 객체는 참조(링크)가 된다: 복제된 사본은 원본과 서로 영향을 받는다.

            단 어떠한객체가 자신만의 객체!! 를 갖고 있는 경우, 다른 객체와는 서로 간섭하지 않는다.
            어떠한 객체가 갖고 있는 것이 원시 데이터 타입 !!!인 경우에는 다른 객체와 서로 간섭한다.

            확실하게 이해한건지는 모르겠네요. ㅎㅎ 슈레딩거의 고양이보다 어렵다 ㅋㅋㅋ
          • Seo Yun Seok Tudoistube
            참조에서 새로운 객체의 속성값을 생성하지 않는다면 같은 객체를 바라보는 것의 속성값은 같다.
            저는 지금 이해를 하고 있습니다.
            외우는 게 아닙니다.
            이해를 하는것 같습니다.
            일단 외우고 있습니다...^^;;;
            감사합니다.
          • illliilllliillliii
            감사합니다~~!
          • crable
            감사합니다
          • 김소희
            잘봤습니다~!
          • 전성욱
            뮤터블의 특성에대해서 어떻게 설명하면 이해가 쉬울까 고심한 흔적이 엿보이는 강의네요
            자바때 배웠던거 다시한번 복습하고갑니다 좋은강의 감사드립니다~
          • 신입1
            감사합니다
          • 박소망
            함수와 참조에서 전역변수를 우선시 하기 때문에 a값이 1인 경우와 리턴값이 없는데 a가 2인 경우의 차이가 무엇인가요?
          • fallback
            질문이 있습니다. primitive type의 경우, 자바에서는 내부적으로 Immutable Wrapper 객체 참조로 primitive를 표현하던데, 자바 스크립트는 실제로 값 자체를 갖고 있나요? 아니면 자바와 마찬가지인가요?
          • goupspace
            좋은 지적이십니다 ^^
            자신이라는 것이 과하게 고도로 지적인것 같아(?)
            단순한 컴퓨터의 동작을 이해하기가 쉽지가 않네요..
            대화보기
            • 폭스킴
              복제와 참조의 차이를 이해하는 것이 핵심이군요.

              프로그래밍 언어라는 것이 결국 컴퓨터의 동작을 이해하는 과정이라고 봐도 무방한 것인데,

              항상 자신이라는 고도의 지적생물체(?)의 입장에서 생각하니 이해가 되지 않는 것 같습니다. ㅎ

              우리 컴퓨터의 난처한 입장을 이해하도록 노력해 봅시다. ^^
            • 맨 아래에서 두번째 예제같은 경우는 func함수에서 만든 객체 b가 객체 a와는 다른 객체라서 결과값이 1이 되지만

              맨 아래 예제같은 경우 func함수에서 또다른 객체를 만드는 것이 아니라 레퍼런스를 통해서 id의 값을 변경하도록

              하기 때문에 결과값이 2가 되네요
            • 완료!
              감사합니다. 완료!
            • hyuna
              감사합니다!!!!
            • ㅇㅇㅇ
              감사합니다. 정말좋은강의에요!
            • yihsang
              일독에 가까워지고 있습니다.
              감사합니다.
            • 신믿음
              2016. 6.1. 잘 보았습니다. 감사드립니다.
            • 이주환
              2016. 04. 26
              잘 보고 갑니다.
            • JustStudy
              고맙습니다.
              코드와 오픈소스로 점프.
            • ㅇㅇㅎ
              var a6 = {'id':1};
              var b6 = {'id':10};

              function func(b6){
              console.log("2", a6.id, b6.id); //2 1 1
              b6.id = 2;
              console.log("3", a6.id, b6.id); //3 2 2
              };

              console.log("1", a6.id, b6.id); //1 1 10

              func(a6); // <-이 인자를 b6로 바꿔보시면 이해가 되실거 같네요.

              console.log("4", a6.id, b6.id); //4 2 (10?)
              대화보기
              • 저도 초보라 잘 몰르지만 답변을 시도합니다.

                function func(b6) 여기에서 새로운 변수인 b6가 생성된거로 보입니다.
                곧 여기의 b6변수는 func함수의 지역 변수이고 마지막 4번 console.log에서 호출한 b6변수는 func 함수 밖에서 생성한 전체 변수인 것으로 보입니다. func 함수 안에서 window.b6 를 호춯해 보며는 id:10으로 나옵니다...
                대화보기
                • Kfox21
                  한 달 전 글이긴 하지만 저도 되새김질 하다가 답글 드립니다.
                  혹시 틀렸으면 지적 바랍니다.

                  var a6 = {'id':1};
                  var b6 = {'id':10}; <- (1)
                  function func(b6){ <- (2)
                  console.log("2", a6.id, b6.id); //2 1 1
                  b6.id = 2; <- 여기에서 b6과 (1)의 b6은 변수 명은 같지만 다릅니다. (2)에서 b6을 따로 선언했기 때문입니다.
                  console.log("3", a6.id, b6.id); //3 2 2
                  };
                  console.log("1", a6.id, b6.id); //1 1 10
                  func(a6);
                  console.log("4", a6.id, b6.id); //4 2 (10?)

                  감사합니다.
                  대화보기
                  • SK Kim
                    3번째 영상을 이해하기 위해 코드를 좀 변경하여 돌려봤습니다.
                    var a6 = {'id':1};
                    var b6 = {'id':10};
                    function func(b6){
                    console.log("2", a6.id, b6.id); //2 1 1
                    b6.id = 2;
                    console.log("3", a6.id, b6.id); //3 2 2
                    };
                    console.log("1", a6.id, b6.id); //1 1 10
                    func(a6);
                    console.log("4", a6.id, b6.id); //4 2 (10?)
                    결과가 1 1 10, 2 1 1, 3 2 2, 4 2 2로 예측 했는데 마지막 숫자가 10으로 나왔네요.
                    이부분이 잘 이해가 안갑니다. b6 객체가 함수 내부와 외부가 다른건지...음..
                    나머지 영상도 위와 비슷한 맥락으로 수정하면 문제가 비슷, b의 값이 원래대로 돌아가는 느낌.
                  • 아스타잔틴
                    var a = 1;
                    function func(b){
                    b = 2;
                    }
                    func(a);
                    console.log(a);
                    여기서 func(a);가 실행될 떄 a가 인자값이고 b는 매개변수니 b는 전부 a로 바뀌는거 아닌가요? 그러면 실질적으로 b라는 것은 없고 func(a);에선 a라는 값만 있게 되서 console.log(a);하면 2가 되는게... 뇌 혹사시키기 싫은데 ㅠㅠ 이해가 잘 안되네요..
                  • WayneKing
                    역시 C는 위대하다.
                  • 엔트
                    큰일이군요..이러다가 우리나라에 제2의 빌게이츠 1000만명시대가 오는거 아닌지.,.

                    감사합니다 ㅋㅋ갓블레스유~
                  • 후레
                    아버지(a)랑 아들(b)이랑 차 한대 가지고 같이 타면 아들이 차 긁어 노면 아버지 한테 맞겟죠?
                    그런데 아들이 일해서 차를 새로 뽑고(객체를 만들면) 그차를 긁어놓던 한강에 담그던 아버지는 알바 아니라는겁니다.
                    차 한대로같이 쓸때는 아들이 차긁어놓는것은 즉 자기 차가 긁히는 거지만,
                    아들이 지돈 주고 산 차 긁는다고 아버지 본인차까지 긁히는건 아니므로...
                    연결이 끊어진다는게 이런거라고 보심될듯하네요
                    대화보기
                    • 남우
                      a를 b에 대입해도 b에 동일한 이름의 객체를 만들면 연결이 끊어진다..... 인데 헷깔려요 ㅠㅠ 코드가 길어지면 여지없이 잊어버릴거 같네요 ㅎ 강의 감사합니다.
                    • 코코마
                      좋은 강의 잘 보고 있습니다.


                      본문 중 참조 파트에서

                      '이 코드의 주인공은 아래와 같다.' 아래 부분에

                      System.out.println() 대신 console.log() 이 녀석이 와야 하는 것 같습니다.
                    • 조신부리
                      감사합니다
                    • egoing
                      수정 했습니다 :)
                      대화보기
                      • Hodong
                        맨 마지막 동영상이 편집이 잘못 된건지 첫번째에 나오는 메인화면이 참조(1/3) 으로 되어 있습니다.
                      • T-BONE Steak
                        아리송 했던 부분이 확 풀리네요.
                        시야가 확 트이는 기분이랄까요 ㅎㅎㅎ
                      • 나무마루
                        샤핀님. 감사합니다. 참조와 복제 라는 개념 자체는 이해가 되는데, 객체를 새로 만들거나, 혹은 값만을 바꾸었을 때를 잘 이해하지 못한 것 같아요. 그래도 대충 개념은 생겼으니, 시간이 지나면 완벽히 알 것 같습니다. 뭔가 그런 느낌이네요. 알기엔 좀 뭔가 모자라고, 모르는 건 또 아니고요. 어쨌든 감사합니다.
                        대화보기
                        • 샤핀
                          //나무마루 님 : http://opentutorials.org......339 이 링크 3번째 강좌를 한번 보시면 이해에 도움이 되지 않을까 싶네요.
                          3/4 참조와 복제 비교 라는 자바 강좌입니다. 이고잉님이 비유를 들어서 차이점을 설명하신 강좌인데
                          도움이 되실거 같습니다. 3번째 강좌만 한번 보시고 다시 봐보세요 ^^
                          대화보기
                          • 나무마루
                            시간이 지나고 다시 읽어도 이해가 안되네? ㅋㅋ
                            대화보기
                            • 나무마루
                              음... 항상 느끼지만 이 쉬운 걸 이해 못하는 제가 참... ㅜㅜ
                            • freheart
                              이런 강의를 너무 늦게 알게되었네요.
                              이틀 만에 주파 했더니 머리가 지끈지끈 합니다 ㅎㅎ;
                              이해하기 쉽도록 잘 풀어서 설명해주셨는데도
                              뒤돌아서 혼자 생각하려니 또 쉽지않네요~
                              고퀄의 강의를 들려주셔서 진심으로 감사드립니다. (--)(__)
                            • coffee
                              여기까지 차근차근... 한달 걸렸네요
                              머리가 나쁜 건지...
                              이런 훌륭한 명강의를 한달 씩이나 걸려 듣고 있네요 ^^;;;;
                            • hehypapa
                              아니 도대체 왜 이렇게 좋은정보를 무료로 제공하시나요? 무슨 음모가 있으신가요?ㅋㅋ
                              감사합니다..
                            버전 관리
                            egoing
                            현재 버전
                            선택 버전
                            graphittie 자세히 보기