Static이란?

상태
완료
담당자
날짜
숫자
0
여러분. 코딩을 하다보면 public static ~~ 이런 식의 코드를 자주 접하지 않으셨나요? 이는 접근제한자인 public과 static 키워드를 사용한 코드인데요.
이번에는 저번에 배운 클래스에서 더 나아가 static 키워드가 코드 내에서 어떤 역할을 하는지 왜 사용하는지 등을 알아보도록 하겠습니다.

static 키워드란?

Static 키워드는 프로그래밍에서 클래스와 관련된 다양한 기능을 지원하며 유연하고 효율적인 코드 작성을 돕는 역할을 하기에 꼭 이해하고 가야 하는 개념입니다.
Static 멤버는 클래스의 인스턴스와 무관하게 클래스 자체에 속하며, 이는 메모리 관리 및 코드의 효율성을 높이는 데 기여합니다.
하지만 static 키워드를 무조건 사용하는 것이 옳은 것은 아닙니다. 그렇다면 어떤 상황에서 사용하고 어떤 점을 주의해야하는지 한번 알아보겠습니다.

static 키워드의 사용 사례

1. 정적 필드 (Static Fields)

Static 키워드를 사용하여 클래스 수준의 데이터 멤버를 정의할 수 있습니다. 이는 클래스의 모든 인스턴스가 공유하는 데이터를 나타내며, 메모리에 한 번만 할당됩니다.
public static class GameManager { private static int score; public static void AddPoints(int points) { score += points; } public static int GetScore() { return score; } }
C#
복사
위의 예시는 게임 점수를 관리하는 코드입니다. 이런 경우에는 정적 필드로 구현하는 것이 적합할 수 있습니다.
여러분이 축구 게임을 한다고 했을 때, 당연히 상대랑 현재 점수가 몇 대 몇인지 동일하게 보여야겠죠?
GameManager 클래스의 score는 정적 필드로 선언되어 있으므로 어느 곳에서나 접근할 수 있습니다. 그래서 모든 플레이어가 동일한 게임 점수를 볼 수 있습니다.
그렇다면 이번에는 정적 필드가 부적절한 예시를 보겠습니다.
public class Player { private static int health; // 정적 필드 사용 예시 public Player() { health = 100; // 부적절한 사용 예시: 인스턴스마다 다른 값 필요 } public void TakeDamage(int damage) { health -= damage; } public int GetHealth() { return health; } }
C#
복사
위의 코드는 플레이어의 체력을 관리하는 코드입니다. 플레이어의 체력은 각자 인스턴스에 독립적으로 관리되어야합니다. 만약 이런 식으로 코드를 작성할 경우 어떤 일이 일어날까요?
Player player1 = new Player(); Player player2 = new Player(); player1.TakeDamage(10); Console.WriteLine(player1.GetHealth()); // 출력: 90 Console.WriteLine(player2.GetHealth()); // 출력: 90 (예상: 100)
C#
복사
플레이어 1이 10의 데미지를 입었으나 플레이어 1,2 모두가 정적 필드로써 체력이 공유되므로 플레이어 1,2가 모두 10의 데미지를 받은 것을 확인할 수 있습니다. 그렇기에 인스턴스마다 독립적인 값을 가져야하는 경우에는 정적 필드를 사용하는 것이 부적합할 수 있으니 주의해야합니다.

2. 정적 메서드 (Static Methods)

Static 키워드를 사용하여 클래스 수준에서 호출할 수 있는 메서드를 정의할 수 있습니다. 이 메서드는 객체 인스턴스 없이도 호출할 수 있습니다.
public class MathUtility { public static int Add(int a, int b) // 정적 메서드 { return a + b; } }
C#
복사
MathUtility.Add(3, 5)와 같이 정적 메서드를 호출할 때에는 객체 인스턴스를 생성하지 않고도 사용할 수 있습니다.
정적 메서드는 이런 식으로 상태를 유지할 필요가 없고 독립적인 기능을 제공할 때 적합합니다. 일반적으로 유틸리티 함수, 상수, 확장 메서드 등을 구현할 때 많이 사용됩니다.
그러나 상태를 유지하거나 객체 간의 관계를 유지해야 하는 경우에는 객체를 인스턴스화하고 인스턴스 메서드를 사용하는 것이 적합합니다.
public static class CharacterManager { private static List<Character> characters = new List<Character>(); public static void AddCharacter(Character character) { characters.Add(character); } public static void RemoveCharacter(Character character) { characters.Remove(character); } }
C#
복사
만약 이런 식으로 게임 캐릭터를 관리하는 코드를 작성했다고 예를 들어보겠습니다. 이렇게 코드를 작성하면 어떤 문제점이 생길까요?
CharacterManager.AddCharacter(new Character("Alice")); CharacterManager.AddCharacter(new Character("Bob")); Character alice = CharacterManager.GetCharacter("Alice"); Character bob = CharacterManager.GetCharacter("Bob"); Console.WriteLine(alice.Name); // 출력: Bob Console.WriteLine(bob.Name); // 출력: Bob
C#
복사
위의 예시에서는 두 캐릭터("Alice"와 "Bob")를 추가한 후, 이름으로 캐릭터를 검색했을 때 두 번째 캐릭터("Bob")만 반환됩니다. 이는 CharacterManager 클래스가 모든 캐릭터를 공유하는 정적 리스트를 사용하기 때문에 발생하는 문제입니다. 각 캐릭터는 고유한 상태를 가져야 하므로 인스턴스화된 객체에게 할당되어야 합니다.

3. 정적 클래스 (Static Class)

Static 키워드는 클래스 자체에 사용되어 클래스가 정적 멤버만 포함하도록 정의할 수 있습니다.
주의할 점은 이러한 클래스는 인스턴스화될 수 없으며, 모든 멤버는 정적이어야 한다는 것입니다.
public static class MathUtility { public static double Pi = 3.14159; public static double CalculateCircleArea(double radius) { return Pi * radius * radius; } public static double CalculateSquareArea(double sideLength) { return sideLength * sideLength; } }
C#
복사
위 예제에서 MathUtility 클래스는 정적 클래스로 선언되어 있으며, MathUtility.CalculateCircleArea(5)와 같이 정적 메서드를 사용할 수 있습니다.
정적 클래스는 위의 예시처럼 상태를 유지하지 않고 단순히 기능을 제공하는 경우에 적합합니다. 객체의 인스턴스화가 필요하지 않은 기능을 구현할 때 유용합니다.
그렇다면 반면에 static class를 사용하는 것이 부적합한 경우는 무엇일까요?
public static class Person { private string name; private int age; public Person(string name, int age) { this.name = name; this.age = age; } public void PrintInfo() { Console.WriteLine($"Name: {name}, Age: {age}"); } }
C#
복사
위의 예시처럼 데이터를 갖는 클래스는 각각의 인스턴스가 고유한 데이터를 유지해야 하므로 static을 사용해서는 안됩니다. 위의 Person 클래스는 각 인스턴스가 다른 이름과 나이를 가질 수 있으므로 정적 클래스가 부적합한 것입니다.

4. 정적 생성자 (Static Constructor)

정적 생성자(static constructor)는 클래스의 정적 멤버를 초기화하는 데 사용됩니다. 이 생성자는 클래스가 처음 사용될 때 한 번만 호출되며, 정적 필드 초기화나 정적 메서드 호출 등을 수행할 수 있습니다.
이 또한 어떤 경우 사용이 적합하고 어떤 경우 부적합한지 알아보겠습니다.
public static class GameSettings { private static int difficultyLevel; // 정적 생성자: 게임 설정 초기화 static GameSettings() { difficultyLevel = 1; // 기본 난이도 설정 } public static int GetDifficultyLevel() { return difficultyLevel; } public static void SetDifficultyLevel(int level) { difficultyLevel = level; } }
C#
복사
위의 코드는 게임 설정에 관련된 코드입니다.
GameSettings 클래스는 게임의 설정을 관리하는 정적 클래스입니다.
정적 생성자를 사용하여 게임 설정을 초기화합니다. 게임이 시작될 때 난이도를 기본값으로 설정합니다.
게임 설정은 한 번 초기화되고 그 후에는 변경될 수 있지만, 초기화 과정 자체는 한 번만 필요하기 때문에 정적 생성자가 적합합니다.
public static class CharacterManager { private static List<Character> characters; // 정적 생성자: 게임 캐릭터 초기화 static CharacterManager() { characters = new List<Character>(); } public static void AddCharacter(Character character) { characters.Add(character); } public static void RemoveCharacter(Character character) { characters.Remove(character); } }
C#
복사
위의 코드는 게임 캐릭터를 관리하는 코드입니다.
CharacterManager 클래스는 게임 캐릭터를 관리하는 정적 클래스로 구현되어 있습니다.
정적 생성자를 사용하여 초기화할 수 있지만, 게임 캐릭터는 동적으로 추가되고 제거되며 각각의 상태를 유지해야 합니다.
정적 생성자는 한 번 호출되고 그 후에는 다시 호출되지 않기 때문에, 게임 중에 캐릭터를 추가하거나 제거할 때 적절한 시점에서 초기화를 다시 할 수 없습니다.
게임 캐릭터는 각각 고유한 상태를 가져야 하므로, 정적 클래스로 구현하는 것이 부적절합니다.
정적 생성자는 한 번 호출되고 그 후에는 다시 호출되지 않는 특성을 가지고 있습니다. 따라서 초기화가 한 번만 필요하고, 그 이후로는 변하지 않는 데이터나 상태를 초기화하는 용도로 사용하는 것이 적절합니다. 게임에서는 리소스 로드나 초기화와 같이 게임 시작 시 한 번만 필요한 작업에 정적 생성자를 사용하는 것이 유용할 수 있지만 게임 객체를 동적으로 관리하거나 상태를 변경해야 하는 경우에는 정적 생성자를 사용하는 것이 부적절합할 수 있습니다.

static 키워드를 사용하는 이유

1.
정적 필드 및 메서드 : Static 키워드를 사용하면 클래스의 인스턴스가 생성되지 않더라도 클래스 멤버에 접근할 수 있습니다. 예를 들어, Math 클래스의 Math.PI처럼 인스턴스화 없이도 사용할 수 있는 메서드와 변수를 정의할 수 있습니다.
public class MathUtilities { public static double Pi = 3.14159; public static double Square(double number) { return number * number; } }
C#
복사
이 경우, MathUtilities.PiMathUtilities.Square(5)를 사용하여 인스턴스를 생성하지 않고도 정적 필드와 메서드를 사용할 수 있습니다.
2.
메모리 효율성: Static 멤버는 클래스 로드 시점에 한 번만 메모리에 할당되기 때문에, 여러 인스턴스가 필요 없을 때 메모리 사용을 줄일 수 있습니다. 이는 특히 대규모 데이터나 공용 유틸리티 함수에서 유용합니다.
3.
글로벌 상태 관리: Static 키워드는 애플리케이션 전역에서 공유되는 데이터를 저장하고 관리하는 데 유용합니다. 예를 들어, 로깅 시스템이나 설정 데이터를 Static 클래스로 구현할 수 있습니다.
public static class Configuration { public static string ApplicationName = "MyApp"; public static int MaxUsers = 100; }
C#
복사
이렇게 하면 애플리케이션 전체에서 Configuration.ApplicationName과 같은 데이터를 일관되게 사용할 수 있습니다.

결론

static 키워드는 유용하지만 모든 상황에서 사용해서는 안 된다는 것을 이번에 잘 이해하셨나요? 공유된 데이터나 유틸리티 기능을 구현할 때 유용하지만, 인스턴스별로 다른 상태를 유지해야 할 때는 적합하지 않다는 점을 잘 기억하시고 여러분이 코드를 작성하실 때 상황에 맞게 적절하게 구현하셨으면 좋겠습니다.
그럼 다음에는 접근제한자에 대해 알아보도록 하겠습니다.