Java

접근 제어자

자유와 규제

프로그래밍 도구의 기본적인 목표는 생각하는 것을 자유롭게 표현할 수 있도록 하는 것이다. 하지만 자유만으로는 부족하다. 프로그래밍은 작은 것에서 거대한 것, 단순한 것에서 복잡한 것, 단독 작업에서 협업으로 나아가게 된다. 이러한 변화를 수용하기 위해서는 다양한 규제가 필요해지게 된다. 우리 수업을 통해서 지금까지 경험한 대표적인 규제 중의 하나는 데이터 타입을 들 수 있겠다. 어떤 변수가 있을 때 그 변수에 어떤 데이터 타입이 들어있는지, 또 어떤 메소드가 어떤 데이터 타입의 데이터를 리턴하는지를 명시함으로써 사용하는 입장에서는 안심하고 변수와 메소드를 사용할 수 있게 된다. 물론 도구 설계자의 취향이나, 도구의 목적에 따라서 이러한 규제는 채택 되기도 하고, 배제 되기도 한다. 여기에 정답은 없다.

지금부터 배울 추상 클래스, final, 접근 제어자, 인터페이스 등은 바로 이 규제에 해당하는 것이다. 사려 깊은 규제라면 그것이 목적해야 하는 바는 분명해야 한다. 자유에 질서를 부여함으로서 자유를 촉진하는 것이다. 이번 시간에는 규제 중의 하나인 접근 제어자에 대해서 알아보자.

접근 제어자

접근 제어자는 클래스의 맴버(변수와 메소드)들의 접근 권한을 지정한다. 이게 무엇을 의미하는지는 아래의 코드를 보자.

package org.opentutorials.javatutorials.accessmodifier;
class A {
    public String y(){
		return "public void y()";
	}
	private String z(){
		return "public void z()";
	}
	public String x(){
		return z();
	}
}
public class AccessDemo1 {
	public static void main(String[] args) {
		A a = new A();
		System.out.println(a.y());
		// 아래 코드는 오류가 발생한다.
		//System.out.println(a.z());
		System.out.println(a.x());
	}
}

아래 코드는 실행된다.

System.out.println(a.y());

하지만 아래의 코드는 오류를 발생시킨다.

System.out.println(a.z());

오류의 내용은 아래와 같다.

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    The method z() from the type A is not visible
	at org.opentutorials.javatutorials.accessmodifier.AccessDemo1.main(AccessDemo1.java:15)

즉 메소드 z에 접근 할 수 없다는 의미다. 메소드 z의 본체를 보자.

private String z(){
	return "public void z()";
}

메소드가 키워드 private으로 시작되고 있다. private은 클래스(A) 밖에서는 접근 할 수 없다는 의미다. 바로 이 private의 자리에 오는 것들을 접근 제어자(access modifier)라고 한다. 그럼 사용할 수 없는 메소드를 왜 정의하고 있는 것일까? 내부적으로 사용하기 위해서다. 다음 코드를 보자.

System.out.println(a.x());

메소드 x의 본체는 아래와 같다.

public String x(){
	return z();
}

접근 제어자가 public이기 때문에 호출 할 수 있다. 그리고 메소드의 내용을 보면 내부적으로 메소드 z를 호출하고 있다. 메소드 z는 정상적으로 호출된다. 왜냐하면 메소드 x와 메소드 z는 같은 클래스의 소속이기 때문이다. 따라서 메소드 x에서 z를 호출 할 수 있는 것이다.

접근 제어자를 사용하는 이유

접근 제어자는 매우 중요한 개념이다. 하지만 그 중요함은 기본적으로는 이해의 영역이지만 근본적으로는 공감의 영역이다. 규모있는 에플리케이션을 만드는 과정에서 경험하게 되는 수 많은 막장들로 인한 깊은 절망감을 경험해보지 않았다면 접근 제어자와 같은 개념들은 관념적인 것으로 치부되기 쉽다. 에플리케이션이 커진다는 것은 다른 말로 망가질 확률이 커진다는 의미와 같다. 특히 로직이 망가지는 첫번째 용의자는 사용자다. 즉 객체를 사용하는 입장에서 객체 내부적으로 사용하는 변수나 메소드에 접근함으로서 개발자가 의도하지 못한 오동작을 일으키게 되는 것이다.

 

이런 문제로부터 객체의 로직을 보호하기 위해서는 맴버에 따라서 외부의 접근을 허용하거나 차단해야 할 필요가 생긴다. 마치 은행이 누구나 접근 할 수 있는 창구와 관계자외에는 출입이 엄격하게 통제되는 금고를 구분하고 있는 이유와 같다.

접근 제어자를 사용하는 또 다른 이유는 사용자에게 객체를 조작 할 수 있는 수단만을 제공함으로서 결과적으로 객체의 사용에 집중 할 수 있도록 돕기 위함이다.

그럼 우리의 계산기를 좀 더 견고하고 사용하기 좋은 에플리케이션으로 만들어보자.

package org.opentutorials.javatutorials.accessmodifier;

class Calculator{
    private int left, right;
    
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    }
    private int _sum(){
        return this.left+this.right;
    }
    public void sumDecoPlus(){
        System.out.println("++++"+_sum()+"++++");
    }
    public void sumDecoMinus(){
    	System.out.println("----"+_sum()+"----");
    }
}
 
public class CalculatorDemo {
    public static void main(String[] args) {        
        Calculator c1 = new Calculator();
        c1.setOprands(10, 20);
        c1.sumDecoPlus();
        c1.sumDecoMinus();
    }
}

기존 코드와의 차이점은 아래와 같다.

실행 결과는 아래와 같다.

++++30++++
----30----

우선 인스턴스 필드인 left와 right가 private으로 지정되었다.

int left, right;

이 두개의 변수는 객체 외부에서 호출될 필요가 없다. 따라서 외부로부터 이 변수를 숨기기 위해서 접근 제어자로 private을 지정했다.

또한 메소드 _sum이 추가 되었는데 실제 계산은 이 메소드가 내부적으로 처리하고, 계산된 결과를 외부에 출력해주는 메소드는 sumDecoPlus, sumDecoMinus에서 처리한다.

이상과 같은 조치를 통해서 사용자가 접근하면 안되거나 접근 할 필요가 없는 맴버에 대한 접근을 규제할 수 있게 되었다. 어떤 맴버에 대한 접근을 허용할 것인가를 작업자의 판단에 달렸다.

세밀한 제어

접근 제어자는 public과 private외에도 두가지가 더 있다. protected과 default가 그것이다. protected는 상속 관계에 있다면 서로 다른 패키지에 있는 클래스의 접근도 허용한다. default는 접근 제어 지시자가 없는 경우를 의미하는데, 접근 제어자가 없는 메소드는 같은 패키지에 있고 상속 관계에 있는 메소드에 대해서만 접근을 허용한다. 아래 그림은 접근 제어자 별로 접근의 허용범위를 그림으로 나타낸 것이다. 안쪽에 있을수록 접근 통제가 삼엄하고, 밖에 있을수록 접근이 허용된다.  (출처)

 

  public protected default private
같은 패키지, 같은 클래스 허용 허용 허용 허용
같은 패키지, 상속 관계 허용 허용 허용 불용
같은 패키지, 상속 관계 아님 허용 허용 허용 불용
다른 패키지, 상속 관계 허용 허용 불용 불용
다른 패키지, 상속 관계 아님 허용 불용 불용 불용

위의 표는 매우 중요하다. 하지만 이걸 억지로 외우려하면 뇌를 혹사시키는 것이 된다. 필지가 제안하는 방법은 필자처럼 직접 코드를 작성해서 경우의 수를 완성해보는 것이다. 그리고 그 결과에 따라서 표를 작성해보면 좋을 것 같다. 무엇보다 애매한 것들에 대해서 직접 확인해보는 습관을 정착시키는 것도 좋은 일이다.

위의 관계는 필드(변수)에도 적용되기 때문에 변수를 위한 예제는 따로 언급하지 않겠다. 또한 클래스 맴버(static)에게도 적용된다. 궁금하면 직접 예제를 만들어보자.

클래스의 접근 제어자

지금까지는 클래스 맴버에 대한 접근 제어자를 살펴봤다. 이번에 살펴볼 것은 클래스의 접근 제어자다. 클래스도 접근 제어자가 있다. 클래스의 접근 제어자는 총 2개로 public과 default이다. default는 접근 제어자를 붙이지 않은 경우 default가 된다. 클래스의 접근 제어자는 패키지와 관련된 개념이다. 즉 접근 제어자가 public인 클래스는 다른 패키지의 클래스에서도 사용할 수 있고, default인 경우는 같은 패키지에서만 사용 가능하다.

두개의 클래스를 만들자. 각각의 클래스의 접근 지시자는 이름에 이미 암시되어 있다.

package org.opentutorials.javatutorials.accessmodifier.inner;
public class PublicClass {}
package org.opentutorials.javatutorials.accessmodifier.inner;
class DefaultClass {}

위의 클래스들과 같은 패키지에서 이 클래스들을 사용해보자. 문제 없다.

package org.opentutorials.javatutorials.accessmodifier.inner;
public class ClassAccessModifierInnerPackage {
    PublicClass publicClass = new PublicClass();
	DefaultClass defaultClass = new DefaultClass();
}

이번에는 다른 패키지에 있는 클래스에서 사용해보자.

package org.opentutorials.javatutorials.accessmodifier.outter;
import org.opentutorials.javatutorials.accessmodifier.inner.*;
public class ClassAccessModifierOuterPackage {
    PublicClass publicClass = new PublicClass();
	//DefaultClass defaultClass = new DefaultClass();
}

주석으로 처리한 부분은 오류가 발생한다. DefaultClass의 접근 제어자가 default이기 때문이다.

한가지 중요한 제약 사항이 있다. public 클래스가 포함된 소소코드는 public 클래스의 클래스 명과 소스코드의 파일명이 같아야 한다. 코드를 보자. 이 코드의 이름은 PublicNameDemo.java이다.

package org.opentutorials.javatutorials.accessmodifier.inner;
//public class PublicName {}
public class PublicNameDemo {}

주석처리된 부분은 오류가 발생한다. 퍼블릭 클래스의 이름과 소스코드의 이름이 일치하지 않기 때문이다. 그 말은 하나의 소스 코드에는 하나의 public 클래스가 존재 할 수 있다는 의미다.

수업을 마치며

접근 제어자는 그것이 무엇인지, 또 어떤 접근 제어자가 있는지 정도만 일단 알아두자. 그리고 당분간은 public과 private만 구분해서 사용만해도 더 안전하고 결고한 에플리케이션을 만들 수 있을 것이다. 다시 한번 강조 하지만 각박하게 외우지 말자. 느긋하게 이해하자. 충분한 이해는 암기의 양을 비약적으로 줄여준다.

댓글

댓글 본문
작성자
비밀번호
  1. Left와 right를 private으로 지정할 경우 다른 클래스에서 접근해서 값을 변경하지 못하고, 따라서 코드 작성자가 의도한 바와 같이 setOprands()의 매개변수를 통해서만 값을 지정할 수 있게 되는 이점이 있는 것 같습니다.
    대화보기
    • protected 접근지정자는 같은 패키지일 경우에는 상속관계가 아니여도 접근이 가능합니다.

      단, 같은 패키지가 아닐경우에는 상속관계가 아니면 접근을 할 수 없습니다.

      [ 같은 패키지, 상속 관계 아님 : 허용 ] 입니다.

      protected 에 기준을 맞춘다면
      상속 인지 아닌지로 접근 가능 여부를 판단하기 전에
      같은 패키지 인지 아닌지도 먼저 보시면 헷갈리지 않을 거 같습니다. ^^

      혹시 제가 틀린부분이 있다면 또 누군가가 댓글 달아 주셨으며녀 합니다.^^
      대화보기
      • JustStudy
        고맙습니다
      • 김트라슈
        감사합니다
        아래 레니타키님이 깔끔 정리해주셨네요. 이또한 감사
      • somnium
        좋은수업 항상 감사합니다~
      • 감사합니다 ~ !!
      • 오빠는다르다
        감사합니다!!
      • 레니타키
        private : 같은클래스 안에서만 쓸때
        default : 같은 패키지에서 쓸떄
        protected : 상속관계나 같은 패키지에서 쓸떄
        public : 아무대서나 쓸때

        public class는 클래스명과 java파일명이 같아야하므로 java파일내 하나만 존재
      • 꾸르잼
      • ㅇㅇ
        사용자는 그 변수에 직접접근 하면 안되고 동작방법만 알아야 되서 그런거 아닐까요
        우리가 리모콘을 쓸때 버튼만 누르는 것처럼
        대화보기
        • 초급코딩
          혹시 left 와 right 의 접근제어자를 private 으로 할 이유가 있나요?
        • 박첩구드
          오 이거 좋네요,,,
          대화보기
          • Byunghawk Lee
            잘 보고 갑니다
          • 코코몽
            나는 소시지 코코몽
            장난꾸러기 코코몽
            뚝딱 발명왕 코코몽
            골목대장 코코몽

            발명품은 엉터리
            떴다하면 사고뭉치
            잘난 척 하긴 우주 최고지
            그래도 내 친구

            나는 소시지 코코몽
            장난꾸러기 코코몽
            뚝딱 발명왕 코코몽
            골목대장 코코몽
            모두 모여라
            대화보기
            • 강천성
              4번째 영상 마지막에 만든 nothing객체에서 호출할때 other._public()이게아니라
              nothing._public() 이게맞자않나요??
            • 코코딩
              public : 프로젝트 폴더
              protected : 패키지들
              default : 패키지
              private : 클래스

              이렇게 유효범위만 정의해놓으면

              기억하기 좋고
              좀 더 이해하기 쉬운것 같아요
            • 최 봉재
              상속에 상관없이 호출가능해요!
              직접 코드를 작성해보시면 이해가 빠르지않을까요?
              대화보기
              • https://www.youtube.com......FZc 비교적 간단한 설명이 있어서 퍼왔습니다.8:06초부터 보시면됩니다.
              • egoing
                그렇네요. 영상을 새로 만들어서 교체했습니다. 조언 감사합니다 :)
                대화보기
                • thexl
                  강의 너무 잘 보고 있습니다. 제 홈페이지가 이 사이트에요!

                  강의 본문에 다음과 같이 써있는데,

                  "default는 접근 제어 지시자가 없는 경우를 의미하는데, 접근 제어자가 없는 메소드는 같은 패키지에 있고 상속 관계에 있는 메소드에 대해서만 접근을 허용한다."

                  이 말에서 default는 (같은 패키지 && 상속관계)인 경우에만 허용해준다는 뜻으로 보입니다.

                  근데 default는 같은 패키지이기만 상속관계와 상관없이 허용되는 것 아닌가요?
                • sgkim20
                  ...
                • egoing
                  하나의 java 파일에 복수의 public 클래스를 한번 만들어보시면 아실 수 있을 것 같아요. ^^
                  대화보기
                  • 오이호빵
                    요즘 하루하루 강의를 들으며 열심히 공부 하려 하는 학생입니다.
                    이번 주제에 관해 궁금 한 점이 생겨서 댓글 올립니다.
                    맨마지막 단락에 public 클래스가 포함된 소스코드는 public 클래스의 클래스 명과 소스코드의 파일명이 같아야 한다.
                    퍼블릭 클래스의 이름과 소스코드의 이름이 일치하지 않기 때문이다. 그 말은 하나의 소스 코드에는 하나의 public 클래스가 존재 할 수 있다는 의미다.
                    -> 이 말이 이해가 잘되지 않아서요........ 저 말이 public 클래스는 소스코드에 한번 밖에 사용할 수 없는건가요 ?
                    혹시 조금더 쉽게 알려주실수 있으신지 해서 올립니다.
                    수업 정말 잘 듣고 있습니다.
                    감사합니다.
                  • egoing
                    오래된 강의라 어떤 부분에 문제가 있는지 파악하는데 쉽지 않네요. 불편 하시겠지만 조금 더 상세히 문제가 있는 부분을 지적해주시면 검토해서 다른 분들이 혼란스럽지 않도록 정정하겠습니다. 하루에도 수십 건의 문의를 받고 있어서 대응이 좀 어렵네요 :)
                    대화보기
                    • Akinaro
                      protected는 같은 패키지일경우에는 비상속관계라도 허용이 가능한것 같네요
                      대화보기
                      • Akinaro
                        protected 는 상속인관계일때 허용한다 면서 상속 관계가 아닌 곳에 왜 허용인거죠 ;;??
                        말씀도 그렇게하시면 움찔하시고 비상속관계부분에 상속일경우 허용한다면서 동그라미를치시네..
                      • 우왕
                        게이야 노무노무 신기하농!! 앞으로 이런강좌 더 만들어주길 바란다 이기야 삼기야 사기야 넘어져도 칠전팔기야 노무 이기딲딲!
                      • 환글
                        음~~ 접근제어자(modifier(변경자))가 이렇게 사용되는 군요.
                      • junghoon
                        강의가 정말 쉬울까요로 끝났네요 ㅋㅋㅋ 잘 듣고 있어용~!!!
                      • jeyul
                        세밀한 제어, 위에서 3번째 줄에 있는 default 접근 제어자에 대한 설명이 잘 못 되었습니다.
                        그리고 클래스B의 메소드의 콘솔 출력 문자열을 다음과 같이 기술해야 될 것 같아요.
                        public class B {
                        ...
                        private void _private(){ System.out.println("private void _private()"); }
                        protected void _protected(){ System.out.println("protected void _protected()"); }
                        void _default(){ System.out.println("void _default()"); }
                        ...
                        }
                      • jeyul
                        자바를 배우면서 팩키지란 용어가 아리송했는데 강의 덕분에 팩키지와 default, 클래스 접근제어자가
                        이해가 되네요. ^^
                      • tateossian
                        너무 잘듣고 있습니다. 감사합니다!!
                      • egoing
                        수정했습니다. ^^
                        대화보기
                        • eun9312
                          "접근제어자를 사용하는 이유" 밑에 있는 예제에서 실행결과가 잘못 올라온것 같네요.
                          수고가 많으십니다.
                        • quine
                          수고하십니다.

                          접근 제어자를 사용하는 이유의 CalculatorDemo.java 실행결과가 다른거죠?

                          ++++30++++
                          ----30----

                          요렇게 나와야 될 것 같네요.

                          강의 잘 듣고 있습니다. 큰 도움이 됩니다.
                        • 하하하
                          늘 언제나 나오던 public을 왜 써야하는가에 대한 의문이 드디어 풀렸네요~
                          강의 너무 잘 듣고 있습니다. 감사합니다 :D
                        • manta
                          혼자서라면 오랫동안 혼란스러웠을 것을 간명하게 정리해 주셨습니다. 고맙습니다.
                        • 자바란
                          감사해요 초보인 저에게 한줄기 햇살같은 명강의네요~~ 점점 갈수록 난이도가 느껴집니다 그래도화이팅할게요~자바고수를향해~!!
                        버전 관리
                        egoing
                        현재 버전
                        선택 버전
                        graphittie 자세히 보기