Spring/스프링입문을 위한 자바객체지향의 원리와 이해

캡슐화

1space 2025. 6. 30. 16:25

스프링입문을 위한 자바 객체지향의 원리와 이해』로 공부한 내용을 정리한 글입니다.

 

접근 제어자: 정보 은닉의 핵심 원리

객체지향 프로그래밍에서 "캡슐화(encapsulation)"란, 객체의 내부 상태(데이터)를 외부에서 직접 접근하지 못하도록 감추고, 허용된 방식으로만 접근할 수 있게 만드는 정보 은닉(information hiding) 기법입니다.

자바에서는 이 캡슐화를 접근 제어자(access modifier)를 통해 구현합니다. 클래스 멤버(필드와 메서드)에 접근을 제한함으로써, 외부로부터의 불필요하거나 위험한 접근을 방지하고, 소프트웨어의 유지보수성과 안정성을 높입니다.

 

자바의 4가지 접근 제어자

접근 제어자 클래스 내부 같은 패키지 다른 패키지의 자식 클래스 외부 클래스
private ✅  ❌  ❌  ❌ 
(default) ✅  ✅  ❌  ❌ 
protected ✅  ✅  ✅  ❌ 
public ✅  ✅  ✅  ✅ 
 

private

가장 제한적인 접근 제어자입니다. 같은 클래스 내부에서만 접근 가능합니다. 다른 클래스, 자식 클래스, 심지어 같은 패키지에 있어도 접근할 수 없습니다.

(default) (아무것도 쓰지 않음)

접근 제한자가 생략되었을 경우입니다. 같은 패키지 내에서는 접근 가능하지만, 다른 패키지에서는 접근할 수 없습니다.

protected

같은 패키지 + 다른 패키지의 자식 클래스에서 접근 가능합니다. 외부 클래스에서는 접근 불가입니다.

public

가장 개방적인 접근 제어자입니다. 어디서든 접근이 가능합니다.

 

코드와 UML로 이해하는 접근 제어

예시 클래스: ClassA

package encapsulation01.packageOne;

public class ClassA {
    private int pri;
    int def;
    protected int pro;
    public int pub;

    void runSomething() {
        // 클래스 내부이므로 모든 멤버 접근 가능
    }

    static void runStaticThing() { }
}

클래스 구조도

클래스 다이어그램과 함께 보면 다음과 같이 구성되어 있습니다:

  • ClassA 클래스는 같은 패키지인 ClassB, ClassAA, 다른 패키지의 ClassAB, ClassC와 관계를 맺고 있습니다.
  • 각각의 클래스가 ClassA의 멤버에 접근할 수 있는지 여부는 제어자의 종류에 따라 달라집니다.

예를 들어, 같은 패키지인 ClassB에서는 def, pro, pub 멤버에 접근할 수 있지만 pri는 접근할 수 없습니다.

 

캡슐화를 적용한 설계 예시

  • 내부 변수 pri, def, pro 등은 외부에서 직접 접근을 막고,
  • 외부에서는 runSomething() 같은 메서드를 통해서만 내부 데이터를 조작하거나 확인할 수 있도록 제한합니다.
  • 특히 메서드 내에서 클래스 자신(this)의 멤버에 접근하는 경우에는 모든 접근 제어자가 허용됩니다.

이런 방식으로 외부로부터 내부 구현을 보호하고, 유지보수를 편하게 할 수 있습니다.

 

접근 제어자 정리 팁

  • 무조건 public이 좋지 않다! → 설계 의도에 따라 정보 은닉 수준을 조절해야 합니다.
  • 자식 클래스에서 접근하게 하려면 protected를 고려하라
  • 패키지 간 분리 여부를 고려하라 → default는 다른 패키지에서는 못 씀

 

자바에서 변수 전달 방식: Call By Value vs Call By Reference

Java를 공부하다 보면 가장 많이 헷갈리는 개념 중 하나가 바로 변수의 전달 방식입니다. 함수나 메서드에 변수를 넘길 때, 값 자체를 넘기는지, 아니면 주소를 넘기는지에 따라 프로그램의 동작이 완전히 달라질 수 있기 때문입니다.

자바는 모든 변수 전달을 Call By Value(값에 의한 호출) 방식으로 처리하지만, 객체를 다룰 때는 마치 참조(Call By Reference) 방식처럼 보이는 경우도 있어 혼란을 줍니다. 예제를 통해 명확하게 이해해 봅시다.

 

예제 1: 기본형 변수의 복사 – CallByValue.java

public class CallByValue {
    public static void main(String[] args) {
        int a = 10;
        int b = a;

        b = 20;

        System.out.println(a); // 10
        System.out.println(b); // 20
    }
}
 

 

  • a의 값을 b에 복사했기 때문에 a와 b는 서로 독립적인 변수입니다.
  • b의 값을 변경해도 a에는 영향이 없습니다.

👉 기본형 데이터는 값 그 자체가 복사됩니다.

 

예제 2: 참조형 변수의 복사 – CallByReference.java

public class CallByReference {
    public static void main(String[] args) {
        Animal ref_a = new Animal();
        Animal ref_b = ref_a;

        ref_a.age = 10;
        ref_b.age = 20;

        System.out.println(ref_a.age); // 20
        System.out.println(ref_b.age); // 20
    }
}

class Animal {
    public int age;
}

 

  • ref_a와 ref_b는 같은 객체(주소)를 참조하고 있습니다.
  • ref_b.age = 20으로 값을 바꾸면 ref_a.age도 20이 됩니다.
  • 둘 다 같은 객체 인스턴스를 가리키고 있기 때문입니다.

👉 참조형은 값(객체의 주소)을 복사하지만, 실제 객체는 하나입니다.

 

정리: 자바의 변수 전달 방식은?

기본형 (int, double 등) 값 자체를 복사 (진짜 Call By Value)
참조형 (객체) 참조값(주소)을 복사 (주소에 의한 Call By Value처럼 보임)
  • 객체를 넘기면 주소값이 복사되므로 실제 객체는 공유됩니다.
  • 그러나 자바는 근본적으로 Call By Reference를 지원하지 않습니다.

요약

  • 자바는 항상 Call By Value입니다.
  • 객체도 주소 값만 복사되기 때문에 객체는 공유됩니다.
  • 혼동을 줄이려면 “기본형은 값 복사, 참조형은 주소 복사”로 이해하면 됩니다.