C# 캡슐화 마스터하기: 접근 지정자로 코드 보호와 설계 개선
캡슐화(Encapsulation)는 '하나 이상의 항목을 물리적 또는 논리적 패키지 안에 감싸는 과정'으로 정의됩니다. 객체 지향 프로그래밍에서 캡슐화는 구현 세부 사항에 대한 접근을 차단합니다.
추상화(Abstraction)와 캡슐화는 객체 지향 프로그래밍의 관련 기능입니다. 추상화는 관련 정보를 보이게 하고, 캡슐화는 프로그래머가 원하는 수준의 추상화를 구현할 수 있게 합니다.
캡슐화는 접근 지정자(Access Specifiers)를 사용하여 구현합니다. 접근 지정자는 클래스 멤버의 범위와 가시성을 정의합니다. C#은 다음 접근 지정자를 지원합니다.
- Public
- Private
- Protected
- Internal
- Protected Internal
Public 접근 지정자
Public 멤버는 클래스 외부에서 접근 가능합니다. 멤버 변수와 메서드를 다른 함수와 객체에 노출합니다.
예제
using System;
namespace RectangleApplication
{
class Rectangle
{
// 멤버 변수 - public
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());
}
}
class ExecuteRectangle
{
static void Main(string[] args)
{
Rectangle r = new Rectangle();
r.length = 4.5; // 외부에서 직접 접근 가능
r.width = 3.5;
r.Display(); // public 메서드 호출
Console.ReadLine();
}
}
}
출력: Length: 4.5, Width: 3.5, Area: 15.75
Private 접근 지정자
Private 멤버는 같은 클래스의 함수만 접근 가능합니다. 클래스 인스턴스라도 private 멤버에는 접근할 수 없습니다. 캡슐화의 핵심입니다.
예제
using System;
namespace RectangleApplication
{
class Rectangle
{
// 멤버 변수 - private
private double length;
private double width;
public void AcceptDetails()
{
Console.WriteLine("길이 입력: ");
length = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("너비 입력: ");
width = Convert.ToDouble(Console.ReadLine());
}
public double GetArea() { return length * width; }
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
class ExecuteRectangle
{
static void Main(string[] args)
{
Rectangle r = new Rectangle();
r.AcceptDetails(); // public 메서드를 통해 private 데이터 설정
r.Display();
Console.ReadLine();
}
}
}
출력: 길이/너비 입력 후 Area 계산 결과 출력
Protected 접근 지정자
자식 클래스가 기본 클래스의 멤버 변수와 메서드에 접근할 수 있게 합니다. 상속 구현에 사용됩니다. (상속 장에서 자세히 다룸)
Internal 접근 지정자
현재 어셈블리(애플리케이션) 내에서만 멤버에 접근할 수 있게 합니다. 같은 어셈블리 내의 어떤 클래스나 메서드에서도 접근 가능합니다.
예제
using System;
namespace RectangleApplication
{
class Rectangle
{
internal double length;
internal double width;
double GetArea() // 접근 지정자 없음 → 기본값: private
{
return length * width;
}
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
class ExecuteRectangle
{
static void Main(string[] args)
{
Rectangle r = new Rectangle();
r.length = 4.5; // 같은 어셈블리이므로 접근 가능
r.width = 3.5;
r.Display();
Console.ReadLine();
}
}
}
주의: 멤버에 접근 지정자를 명시하지 않으면 기본값은 private입니다.
Protected Internal 접근 지정자
같은 애플리케이션 내의 자식 클래스를 제외하고, 다른 클래스 객체와 함수로부터 멤버를 숨깁니다. 상속 구현 시에도 사용됩니다.
접근 지정자 요약표
| 지정자 | 클래스 내부 | 파생 클래스(같은 어셈블리) | 파생 클래스(다른 어셈블리) | 같은 어셈블리 내 다른 클래스 | 다른 어셈블리 |
| public | O | O | O | O | O |
| private | O | X | X | X | X |
| protected | O | O | O | X | X |
| internal | O | O | X | O | X |
| protected internal | O | O | O | O | X |
핵심 원칙: 멤버 변수는 private으로 숨기고, 필요한 기능만 public 메서드(프로퍼티)로 노출하세요. 이것이 캡슐화의 본질입니다.