✅ 코드의 재사용성과 구조화를 위한 핵심 개념
Java에서 프로그램을 잘 구성하려면, 단순히 기능만 구현하는 것을 넘어 코드를 잘게 나누고 구조화하는 능력이 필요해요. 바로 이 역할을 하는 것이 메서드(Method)입니다.
메서드는 특정 기능을 하나의 블록으로 정의해서 필요할 때마다 호출하고 재사용할 수 있는 구조예요.
메서드란 무엇인가?
메서드(Method)란, 특정 기능을 수행하는 코드 블록입니다. 함수(function)와 유사하지만, 자바에서는 메서드는 반드시 클래스 내부에 정의되어야 해요. 메서드를 사용하면 중복된 코드를 줄이고 프로그램을 더 구조적으로 만들 수 있어요.
public static int add(int a, int b) {
return a + b;
}
1. 메서드의 기본 구조
[접근제어자] [static] 반환형 메서드이름(매개변수) {
// 실행할 코드
return 반환값;
}
구성 요소 | 설명 |
접근 제어자 | public, private, protected 등. 외부 접근 허용 수준 결정 |
정적 여부 | static 여부에 따라 클래스 소속 혹은 객체 소속 결정 |
반환형 | 메서드가 리턴하는 값의 타입 (int, void, String, 등) |
메서드 이름 | 카멜 케이스 명명 권장 (getUser, updateStatus 등) |
매개변수 | 외부에서 값을 받아오기 위한 변수 선언 (없을 수도 있음) |
본문 | {} 안의 코드 블록. 메서드가 실제 수행하는 작업 |
return 문 | (반환형이 void가 아닐 경우) 메서드가 돌려줄 값을 명시 |
2. 메서드 호출
메서드는 정의만 해서는 동작하지 않고 호출(call)해야 실행됩니다.
add(10, 3);
- add는 메서드 이름
- (10, 3)은 인자(Argument) ← 실제로 전달하는 값
3. 매개변수와 인자의 개념 차이
- 매개변수(Parameter): 메서드를 정의할 때 괄호 안에 선언한 변수
- 인자(Argument): 메서드를 호출할 때 전달하는 실제 값
// 정의
void introduce(String name, int age) { ... }
// 호출
introduce("민팁", 30);
"민팁"과 30은 각각 name, age에 대응하는 인자입니다.
4. return 문
📌 반환값이 있는 메서드인 경우
public static int add(int a, int b) {
return a + b;
}
- 반환형은 int
- return a + b는 결과값을 호출한 곳으로 돌려줍니다.
📌 반환값이 없는 메서드인 경우
public static void hello() {
System.out.println("Hello World!");
}
- 반환형이 void일 땐 반환 값이 없으므로 생략해도 됩니다. (혹은 return;)
5. 메서드 오버로딩 (Overloading)
같은 이름의 메서드를 매개변수만 다르게 해서 여러 개 정의하는 것을 메서드 오버로딩(Overloading)이라고 합니다. 즉, 이름은 같지만 서로 다른 방식으로 호출될 수 있는 메서드들을 정의하는 것이에요. 이 기능을 사용하면 한 가지 기능에 대해 다양한 입력을 처리할 수 있고, 코드 이름 일관성이 유지되어 가독성과 사용성을 높일 수 있습니다.
구분 | 설명 |
매개변수의 개수가 다르다 | add(int a), add(int a, int b) |
매개변수의 타입이 다르다 | add(int a), add(double a) |
매개변수의 순서가 다르다 | print(String s, int n), print(int n, String s) |
6. static 메서드 vs 인스턴스 메서드
자바에서 클래스 내부에 선언한 메서드는 크게 두 종류로 나뉩니다.
구분 | 설명 | 설계 관점 |
static 메서드 | 클래스 자체에 소속된 메서드. 객체 생성 없이 호출 가능 | 도구적 기능(Util), 공통 연산 |
인스턴스 메서드 | 객체(인스턴스)를 생성한 후, 그 객체를 통해 호출해야 함 | 행위, 상태 변화 (Entity 중심) |
메서드 설계 시 주의할 점
1. 하나의 메서드는 하나의 책임만 (SRP: Single Responsibility Principle)
❌ 하나의 메서드가 여러 일을 하면?
- 메서드 수정 시 다른 기능까지 의도치 않게 망가질 수 있음 (유지보수, 확장성 감소)
- 테스트가 어렵고, 버그 발생 가능성도 높아짐
✅ 하나의 메서드가 하나의 책임만 가지면?
- 로직 수정 시 영향 최소화
- 다른 기능에서도 재사용 가능해짐
- 기능별 테스트 가능 (유닛 테스트 구성 쉬움)
2. 매개변수 개수는 3개 이하가 이상적입니다.
// ❌
void registerUser(String name, int age, String phone, String email, String address, String company)
// ✅
void registerUser(RegisterUserRequest request)
❌ 매개변수가 너무 많으면?
- 메서드 호출 시 무슨 값이 무슨 의미인지 알기 어려움
✅ 3개 이하로 제한하고, 많으면 객체로 감싸세요.
- 실수로 매개변수 순서 바뀌는 문제 방지
- 유지보수성 향상 (필드 추가 시 메서드 시그니처 변경 없이 처리)
3. boolean 인자는 가능한 피하고, 명확한 의미를 갖는 enum 등으로 대체
// ❌ true가 무슨 뜻인지 알 수 없음
sendNotification(user, true);
// ✅ 방법 1: enum으로 의미 명확
sendNotification(user, NotificationType.EMAIL);
// ✅ 방법 2: 메서드 분리하여 의미 명확
sendEmail(user);
sendSMS(user);
❌ boolean 인자는 의미가 불분명 합니다.
- sendNotification(user, true); true가 무슨 뜻인지 알 수 없음
✅ 명확한 의미가 드러나는 enum이나 메서드 분할을 사용
- 코드 읽는 사람이 의미를 바로 이해
- 디버깅 시 조건 로직 추적이 쉬움
4. void 메서드라도 의미 있는 부작용(side-effect)을 포함해야 합니다.
❌ 반환값 없는 void 메서드가 의미 없이 남용되면?
- 결과가 없고, 사이드 이펙트만 존재 → 디버깅과 테스트 모두 어렵고 불확실해집니다.
✅ 반환값이 있을 수 있다면 명확히 return 사용
- 테스트 시 예상 값 검증 가능
- 체이닝이나 함수형 구조 사용 가능
- 순수 함수로 관리 → 안전성과 확장성 확보
5. 메서드의 이름은 의도가 담겨있어야 합니다.
❌ 메서드 이름이 추상적이면 역할을 알기 어렵다
- doProcess(), handleData() 이런 이름의 메서드들은 무슨 기능을 하는지 알기 어렵습니다.
✅ 메서드 이름을 구체적으로 작성
- 팀원/협업자에게 의도가 바로 전달되고, 자동 문서화 시 함수명이 곧 설명이 됩니다.
- 로직 변경 시 리팩터링 포인트가 명확해집니다.