[Java] 메서드 완벽 가이드: 선언·호출·반환부터 설계 원칙까지

✅ 코드의 재사용성과 구조화를 위한 핵심 개념

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은 각각 nameage에 대응하는 인자입니다.

 

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() 이런 이름의 메서드들은 무슨 기능을 하는지 알기 어렵습니다.

✅ 메서드 이름을 구체적으로 작성

  • 팀원/협업자에게 의도가 바로 전달되고, 자동 문서화 시 함수명이 곧 설명이 됩니다.
  • 로직 변경 시 리팩터링 포인트가 명확해집니다.