C#에서 접근 지정자(Access Specifier)는 클래스의 멤버 변수와 멤버 함수에 대한 외부 접근 범위를 결정하는 핵심 요소입니다. 적절한 접근 지정자를 사용하면 캡슐화를 구현하고 코드의 안정성과 유지보수성을 높일 수 있습니다. 이번 글에서는 가장 많이 사용되는 세 가지 접근 지정자인 public, protected, private의 차이점을 예제 코드와 함께 자세히 살펴보겠습니다.
1. Public 접근 지정자
public은 가장 개방적인 접근 수준으로, 클래스가 자신의 멤버 변수와 멤버 함수를 다른 함수 및 객체에 공개하도록 허용합니다. 즉, 모든 public 멤버는 클래스 외부 어디에서든 자유롭게 접근할 수 있습니다.
예제 코드
using System;
namespace Demo {
class Rectangle {
public double length;
public double width;
public double GetArea() {
return length * width;
}
public void Display() {
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
} //end class Rectangle
class ExecuteRectangle {
static void Main(string[] args) {
Rectangle r = new Rectangle();
r.length = 7; // 클래스 외부에서 직접 접근 가능
r.width = 10;
r.Display();
Console.ReadLine();
}
}
}실행 결과
Length: 7 Width: 10 Area: 70
위 예제에서 length와 width 필드가 public으로 선언되었기 때문에, Main 메서드에서 객체 r를 통해 직접 값을 할당할 수 있었습니다. 편리하지만, 외부에서 내부 상태를 마음대로 변경할 수 있다는 점에서 데이터 무결성 측면에는 주의가 필요합니다.
2. Protected 접근 지정자
protected는 상속 관계에서만 접근을 허용하는 접근 수준입니다. 기본(부모) 클래스의 protected 멤버는 해당 클래스 자신과 이를 상속받은 파생(자식) 클래스 내부에서만 접근할 수 있으며, 클래스 외부에서는 접근할 수 없습니다.
다음은 protected 멤버에 접근하는 방법을 보여주는 예제입니다.
예제 코드
using System;
namespace MySpecifiers {
class Demo {
protected string name = "Website";
protected void Display(string str) {
Console.WriteLine("Tabs: " + str);
}
}
class Test : Demo { // Demo 클래스를 상속
static void Main(string[] args) {
Test t = new Test();
// 상속받은 protected 멤버에 접근
Console.WriteLine("Details: " + t.name);
t.Display("Product");
t.Display("Services");
t.Display("Tools");
t.Display("Plugins");
}
}
}실행 결과
Details: Website Tabs: Product Tabs: Services Tabs: Tools Tabs: Plugins
Test 클래스는 Demo 클래스를 상속했기 때문에 부모 클래스의 protected 멤버인 name 필드와 Display() 메서드에 접근할 수 있습니다. 만약 상속 관계가 없는 외부 코드에서 접근하려고 하면 컴파일 오류가 발생합니다.
3. Private 접근 지정자
private는 가장 제한적인 접근 수준입니다. private로 선언된 멤버는 같은 클래스 내부의 멤버 함수만 접근할 수 있으며, 다른 클래스나 객체는 물론, 해당 클래스의 인스턴스를 통해서도 외부에서 접근할 수 없습니다. 참고로 클래스 멤버에 접근 지정자를 명시하지 않으면 기본값으로 private가 적용됩니다.
예제 코드
using System;
namespace Demo {
class Rectangle {
// 멤버 변수 - private 선언
private double length;
private double width;
// public 메서드를 통해서만 private 멤버에 접근
public void Acceptdetails() {
length = 10;
width = 15;
}
public double GetArea() {
return length * width;
}
public void Display() {
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
} //end class Rectangle
class ExecuteRectangle {
static void Main(string[] args) {
Rectangle r = new Rectangle();
// r.length = 10; ← 컴파일 오류! private 멤버는 외부 접근 불가
r.Acceptdetails();
r.Display();
Console.ReadLine();
}
}
}실행 결과
Length: 10 Width: 15 Area: 150
이 예제에서 length와 width는 private이므로 외부에서 직접 수정할 수 없고, 오직 클래스 내부의 public 메서드인 Acceptdetails()를 통해서만 값이 설정됩니다. 이처럼 private 멤버와 public 메서드를 조합하면 캡슐화(Encapsulation)를 효과적으로 구현할 수 있습니다.
세 가지 접근 지정자 비교 요약
| 접근 지정자 | 같은 클래스 내 | 파생 클래스(상속) | 클래스 외부 |
|---|---|---|---|
| public | O | O | O |
| protected | O | O | X |
| private | O | X | X |
마무리
접근 지정자 선택은 단순한 문법 문제가 아니라 설계 철학의 문제입니다. 일반적으로 다음 원칙을 따르는 것이 좋습니다.
- private: 내부 구현 세부사항은 최대한 숨기고, 필요한 경우에만 공개합니다.
- protected: 상속 계층에서 자식 클래스가 활용해야 하는 멤버에 사용합니다.
- public: 외부에 제공해야 하는 인터페이스(API)에만 신중하게 사용합니다.
이 세 가지 접근 지정자를 상황에 맞게 적절히 활용하면 안전하고 유지보수하기 쉬운 C# 프로그램을 작성할 수 있습니다.