시작하며: "이 객체는 상태가 없는데, static 메서드로 만들면 좋을까요?"
프로그래밍을 하다 보면 "이 객체는 내부에 상태가 없는데, 굳이 인스턴스를 만들어 사용할 필요가 있을까? static으로 만들어도 되는 거 아닐까?"라는 질문이 생길 수 있습니다. 이 글은 제가 코드를 작성하거나 공부를 할 때 고민한 내용들을 바탕으로 객체의 상태와 static 메서드에 대한 기본 개념을 쉽게 설명하려고 합니다.
객체란? 객체는 상태와 행동을 가진다
객체지향 프로그래밍에서 객체는 보통 내부에 상태(데이터)를 가지고 있고, 이 상태에 따라 다르게 행동하는 메서드를 가집니다. 즉, 객체는 상태와 행동을 한 단위로 묶어주는 역할을 합니다. 예를 들어 볼까요?
Java에서 메서드는 클래스의 상태(예: 객체가 가지고 있는 값)에 따라 결과가 달라질 수 있습니다. 하지만 static 메서드는 클래스에 속해 있을 뿐 객체의 상태에 영향을 받지 않는 메서드입니다. 즉, 특정 인스턴스와 상관없이 언제나 같은 방식으로 동작합니다. 아래에서 더 구체적으로 알아보겠습니다.
public class Dog {
private String name; // 상태
public Dog(String name) {
this.name = name;
}
public void bark() { // 행동
System.out.println(name + "가 짖습니다.");
}
}
위 예시에서 Dog 객체는 name이라는 상태를 가지며, bark()라는 행동을 수행합니다. name이 다르면 bark() 행동도 다른 결과를 출력하게 되죠.
그런데, 만약에 상태가 없는 객체는 어떻게 할까요? 객체에 상태가 없다는 건 무슨 의미일까요?
상태가 없는 객체와 static 메서드
상태가 없는 객체란, 클래스 내부에 필드가 없고 단순히 작업만 수행하는 객체를 말합니다. 예를 들어 문자열을 특정 문자 기준으로 나누는 기능을 하는 객체를 생각해볼까요? 문자열을 분리하는 기능은 특정 상태 없이 입력값만 있으면 수행할 수 있겠죠. 이를 코드로 간단히 표현하면:
public class StringParser {
public String[] splitByComma(String input) {
return input.split(",");
}
}
위 StringParser 클래스에는 상태가 없습니다. 입력값(input)을 받아 쉼표 기준으로 문자열을 나누는 행동만 하기 때문이죠.
이럴 때, 상태가 없기 때문에 굳이 StringParser의 인스턴스를 만들 필요가 있을까 고민할 수 있습니다. 이런 경우 메서드를 static으로 바꾸어 바로 사용할 수 있습니다
public class StringParser {
public static String[] splitByComma(String input) { // static 메서드
return input.split(",");
}
public static void main(String[] args) {
String[] result = StringParser.splitByComma("a,b,c"); // 쉽게 사용 가능
for (String str : result) {
System.out.println(str); // 결과를 출력
}
}
}
이제 StringParser.splitByComma("a,b,c")처럼 클래스 이름으로 바로 호출할 수 있습니다. 인스턴스를 생성하지 않아도 되죠. 이처럼 상태가 없는 경우 static 메서드로 만들어 간편하게 사용할 수 있습니다.
함수와 메서드의 차이
함수와 메서드의 차이를 알아야 더 이해가 쉬울 겁니다.
- 함수: 입력값만 가지고 결과를 계산합니다. 즉, 외부에 영향을 주거나 받지 않고 독립적으로 동작합니다.
- 메서드: 특정 클래스 내부에 속한 함수로, 클래스의 상태에도 접근할 수 있습니다.
자바에서는 모든 함수가 클래스 안에 있어 메서드라고 부릅니다. 하지만 그 중 static으로 선언된 메서드는 상태를 사용하지 않기 때문에 일반적인 함수와 비슷한 역할을 합니다.
그렇다면 언제 static 메서드를 쓰면 좋을까?
1. 상태가 없는 작업이라면 static으로 선언해도 좋습니다.
예를 들어, 숫자를 더하거나 문자열을 자르는 등의 작업은 상태가 필요 없으니 static 메서드로 선언하는 것이 좋습니다.
public class MathUtils {
public static int add(int a, int b) {
return a + b;
}
}
위 add 메서드는 상태 없이 단순히 두 숫자를 더합니다. MathUtils.add(3, 5)처럼 호출할 수 있죠.
2. 상태가 필요한 경우에는 static 메서드로 만들지 않는 것이 좋습니다
만약 클래스가 상태를 가지고, 그 상태를 메서드가 활용해야 한다면 static으로 선언하면 안 됩니다.
예를 들어 Counter라는 클래스가 있다고 합시다. 이 클래스는 count라는 상태를 가지고 있고, 메서드는 count를 증가시키는 역할을 합니다.
public class Counter {
private int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
}
이 경우 increment와 getCount 메서드는 count라는 상태를 사용합니다. 이렇게 내부 상태가 있는 경우는 static이 아닌 인스턴스 메서드로 선언해야 합니다.
static 메서드는 항상 좋을까??
위의 내용들을 읽으면서 생각을 해보면 언제 쓰는지는 이해를 했지만 필드가 없다면 항상 써도 괜찮은건가? 라는 의문이 들겁니다.
아래에 static의 장,단점들을 보고 결론을 보시면 좋을 것 같습니다.
static 메서드의 장점
- 간단한 유틸리티 기능을 쉽게 사용할 수 있습니다
static 메서드는 클래스 이름만으로 바로 호출할 수 있어, 객체를 생성하지 않고도 간단한 기능을 사용할 수 있습니다. 예를 들어 Math.pow(2, 3)처럼 자주 사용하는 수학 연산을 static 메서드로 제공하면 편리하죠. - 메모리 사용 효율성이 좋습니다
static 메서드는 메모리에 한 번만 로드되어 모든 곳에서 동일한 메서드를 사용합니다. 따라서 여러 인스턴스에서 동일한 기능을 반복해서 구현하는 것보다 메모리를 절약할 수 있습니다. 특히 인스턴스를 생성하지 않아도 되는 만큼 메모리 효율이 좋아집니다. - 간단한 상수, 설정값 등을 관리하기 쉽습니다
상수나 특정 설정값을 한 곳에서 관리하고 싶을 때, static 변수를 선언해 어디서든 접근 가능하게 하면 코드 유지보수가 쉬워집니다. 예를 들어, MAX_USERS, DATABASE_URL 같은 상수를 static으로 정의해 두면 전역적으로 활용할 수 있습니다.
static 메서드의 단점
static 메서드에는 몇 가지 단점도 있습니다.
- 다형성을 활용할 수 없습니다
다형성은 여러 객체가 같은 메서드를 가지고 있으면서도 다르게 행동하는 것을 말합니다. static 메서드는 클래스에 직접 속해 있어 다형성을 사용할 수 없습니다. - 테스트가 어렵습니다
static 메서드는 외부에서 대체하기 어렵습니다. 예를 들어, 어떤 클래스가 특정 조건에 따라 다른 결과를 반환하도록 테스트하려면 가짜 객체로 바꾸어 주입해야 하는데, static 메서드의 경우 그렇게 할 수 없습니다.
결론: static 메서드를 사용할 때는 신중히 선택해야 한다
상태가 없는 메서드를 무조건 static으로 만드는 것이 정답은 아닙니다. 상태가 없더라도 객체의 다형성이 필요하거나 독립적인 테스트가 필요한 경우에는 굳이 static 메서드를 사용할 필요가 없습니다.
정리하자면:
- 상태가 필요 없고 독립적으로 동작하는 기능이라면 static 메서드를 고려해볼 수 있습니다.
- 다형성이나 상태 관리가 필요한 경우에는 인스턴스 메서드를 사용하는 것이 좋습니다.
static과 인스턴스 메서드의 차이를 이해하고 필요에 따라 선택하면 더 유연하고 견고한 코드를 작성할 수 있다라고 판단이 되었습니다.
'IT' 카테고리의 다른 글
AngularJS Git Commit Message Convention이란? (1) | 2024.11.15 |
---|---|
배포 중에도 서비스 중단 없이! - 무중단 배포 전략 알아보기 (1) | 2024.11.13 |
Light house 그리고 웹사이트 품질 테스트? (0) | 2024.11.12 |
@Builder, 빌더 (0) | 2024.11.11 |
Implements vs extends (4) | 2024.11.10 |