🎯 1주차 Unit 2.1 — 메서드의 구조

Psj·2026년 5월 6일

F-lab

목록 보기
23/238

🎯 Unit 2.1 — 메서드의 구조

F-lab Java 1주차 / Phase 2 / Unit 2.1 본격 학습 자료
9-섹션 마스터 프롬프트 형식으로 깊이 파헤친다.

선수 지식: Unit 1.2 (클래스와 객체의 본질)
다음 Unit: 2.2 — 가변인자 (Varargs)


🌍 1. 세상 속 비유

메서드 = "전자레인지의 버튼"

전자레인지를 떠올려보세요. 전자레인지에는 다양한 버튼이 있습니다:

  • "시작" 버튼 — 누르면 데우기 시작 (입력 없음, 결과 없음)
  • "+30초" 버튼 — 누르면 시간이 30초 추가 (입력 없음, 시간 변경)
  • "해동" 버튼 — 무게를 입력하면 적절한 시간 자동 설정 (입력: 무게, 결과: 설정된 시간)
  • "온도 조회" 버튼 — 누르면 현재 음식 온도 표시 (입력 없음, 결과: 온도)

각 버튼은 세 가지 정보를 가집니다:

  1. 이름 — "해동", "시작", "온도 조회"
  2. 입력 — 받을 수도, 안 받을 수도 있음
  3. 결과 — 줄 수도, 안 줄 수도 있음

이게 메서드의 본질입니다. 객체가 가진 "버튼" 들이 메서드.


더 일상적인 비유 — "ATM 기기"

ATM의 메뉴를 보면:

ATM 버튼입력결과
"잔액 조회"비밀번호잔액
"출금"비밀번호, 금액현금 + 영수증
"입금"현금영수증
"화면 종료"없음없음

각 버튼이 메서드, ATM이 객체 입니다.

당신은 ATM의 내부 동작(데이터베이스 조회, 카드 인증, 현금 카운터 작동...) 을 몰라도, 버튼만 누르면 됩니다.

메서드는 객체에게 일을 시키는 명령 창구.


비유의 핵심 정리

비유 요소자바 메서드
버튼 이름메서드명
버튼 입력매개변수
버튼 결과반환값
버튼 누르는 행위메서드 호출
누가 누를 수 있나접근 제어자

🔥 2. 탄생 배경

메서드가 없던 시절 — 무한 반복 코드

초기 프로그래밍 (어셈블리, 초기 BASIC) 에서는 함수(메서드) 라는 개념이 없거나 약했습니다.

같은 작업을 반복하려면:

[프로그램]
  주문 1번 처리:
    - 가격 계산 코드 100줄
    - 세금 계산 코드 50줄
    - 결제 코드 80줄
    
  주문 2번 처리:
    - 가격 계산 코드 100줄  ← 똑같은 코드 또 작성
    - 세금 계산 코드 50줄   ← 또 작성
    - 결제 코드 80줄        ← 또 작성
    
  주문 3번 처리:
    - ... 또 똑같이 ...

문제:

  • 같은 로직을 매번 복사 → 코드 폭증
  • 가격 계산 방식 바뀌면? 모든 곳을 수정
  • 한 곳이라도 빠뜨리면 버그
  • 가독성 X

함수 (Function) 의 등장

이 문제를 해결하려고 "함수" 가 등장:

함수 정의: 가격_계산하기 = (...코드 100줄...)

[프로그램]
  주문 1번 처리:
    - 가격_계산하기() 호출
    - 세금_계산하기() 호출
    - 결제하기() 호출
    
  주문 2번 처리:
    - 가격_계산하기()  ← 같은 함수 재사용
    - 세금_계산하기()
    - 결제하기()

효과:

  • 코드 한 번만 작성
  • 수정도 한 번만 하면 모든 곳에 반영
  • 가독성 ↑

함수는 "재사용 가능한 코드 단위" 라는 혁명적 발명.


메서드 (Method) — 객체지향의 함수

자바 같은 객체지향 언어에서는 함수가 객체에 묶이면서 이름이 메서드(method) 로 바뀝니다.

함수 (Function)메서드 (Method)
소속독립적 (전역)클래스에 속함
호출함수명()객체.메서드명()
데이터 접근인자로 받음자기 객체의 필드 직접 접근
C의 printf()자바의 obj.toString()

→ 메서드는 "객체의 행동을 정의하는 함수".


핵심 통찰

"메서드는 객체가 할 수 있는 일을 정의한 약속이다."

객체에게 메서드는:

  • 자기 능력의 목록 (이 객체는 무엇을 할 수 있나)
  • 외부와 소통하는 통로 (외부에서 어떻게 부탁할 수 있나)
  • 데이터 보호의 관문 (캡슐화의 핵심 도구)

💣 3. 없으면 생기는 문제

메서드 없이 객체를 만들면?

객체가 데이터(필드)만 있고 메서드가 없다면 어떤 일이 벌어질까요?

// 메서드 없는 빈혈 객체 ❌
public class BankAccount {
    public int balance;  // 잔액 (public이 문제 더 키움)
}

이 객체를 사용하려면 외부에서 모든 일을 직접 해야 합니다:

BankAccount account = new BankAccount();
account.balance = 10000;

// 입금하려면?
account.balance = account.balance + 5000;  // 외부에서 직접 계산

// 출금하려면?
if (account.balance >= 3000) {
    account.balance = account.balance - 3000;  // 외부에서 검증 + 계산
} else {
    System.out.println("잔액 부족");
}

// 다른 곳에서도 같은 입금/출금이 필요할 때?
// → 같은 코드 또 작성 ❌

문제 1: 모든 사용자가 내부 동작을 알아야 함.
문제 2: 같은 로직이 여러 곳에 중복.
문제 3: 누구나 balance 를 직접 만짐 → 무결성 X.
문제 4: 음수 잔액도 가능 (account.balance = -99999).


메서드를 도입하면

public class BankAccount {
    private int balance;  // private — 직접 접근 X
    
    public void deposit(int amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("입금액은 양수여야 합니다");
        }
        this.balance += amount;
    }
    
    public void withdraw(int amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("출금액은 양수여야 합니다");
        }
        if (this.balance < amount) {
            throw new IllegalStateException("잔액 부족");
        }
        this.balance -= amount;
    }
    
    public int getBalance() {
        return this.balance;
    }
}

사용:

BankAccount account = new BankAccount();
account.deposit(10000);    // 입금
account.withdraw(3000);    // 출금
int current = account.getBalance();

// 안전성 ✅
account.withdraw(99999);   // → 예외! 잔액 부족
account.deposit(-100);     // → 예외! 양수만 가능
// account.balance = -1;   // → 컴파일 에러! private

효과 비교

메서드 없음메서드 있음
코드 중복매번 외부에서 작성한 번 정의 후 재사용
데이터 보호누구나 직접 변경메서드를 통해서만
검증 로직호출자가 알아서메서드 안에 한 번만
변경 영향모든 호출자 수정메서드만 수정
가독성낮음높음

메서드는 객체지향의 핵심 도구. 메서드 없이는 캡슐화도, 다형성도 불가능.


✅ 4. 해결책 — 메서드의 구조

메서드 시그니처 (Signature) ⭐

자바 메서드의 표준 형태:

[접근제어자] [기타제어자] 반환타입 메서드명(매개변수목록) [throws 예외] {
    // 메서드 본문
    return 반환값;
}

실제 예시:

public static int calculateTotal(int price, int quantity) throws IllegalArgumentException {
    if (price < 0 || quantity < 0) {
        throw new IllegalArgumentException("음수 불가");
    }
    return price * quantity;
}

각 부분을 분해해보면:

부분예시역할
접근 제어자public누가 호출할 수 있는가
기타 제어자static인스턴스 없이 호출 가능
반환 타입int어떤 결과를 주는가
메서드명calculateTotal무엇을 하는가
매개변수(int price, int quantity)무엇을 입력받는가
예외 선언throws IllegalArgumentException어떤 예외를 던질 수 있는가
본문{ ... }실제 로직
반환문return price * quantity;결과를 돌려줌

4가지 접근 제어자 ⭐ (면접 단골)

제어자같은 클래스같은 패키지자식 클래스 (다른 패키지)그 외
public
protected
(default)
private

원칙: 가장 좁은 범위로 시작 → 필요할 때만 넓힘 (캡슐화 원칙)

실무 예시:

public class Fare {
    private int amount;              // 외부 차단 — 가장 안전
    
    public int getAmount() { ... }   // 외부 조회용 — 공개
    
    protected void onAmountChanged() { ... }  // 자식만 사용 가능
    
    void internalUpdate() { ... }    // 같은 패키지만 (default)
    
    private void validate() { ... }  // 클래스 내부만
}

암기 팁: public > protected > default > private 순으로 좁아짐.


반환 타입 종류

// 1. 기본형
public int getAge() { return 25; }
public double getPrice() { return 9.99; }
public boolean isActive() { return true; }

// 2. 객체
public String getName() { return "Alice"; }
public Customer getCustomer() { return this.customer; }

// 3. 배열
public int[] getScores() { return new int[]{90, 85, 70}; }

// 4. 컬렉션
public List<String> getTags() { return List.of("a", "b"); }

// 5. 반환 없음
public void doSomething() {
    System.out.println("작업 완료");
    // return 없음 (자동 종료) 또는 return; 가능
}

voidreturn 의 미묘한 관계

void 는 "값을 반환하지 않는다" 는 의미:

public void greet(String name) {
    System.out.println("Hello, " + name);
    // return 생략 가능 (자동 종료)
}

그러나 return; (값 없는 return) 은 사용 가능 — 메서드 즉시 종료용:

public void greet(String name) {
    if (name == null) {
        return;  // ✅ 즉시 종료, 이후 코드 실행 X
    }
    System.out.println("Hello, " + name);  // name이 null이면 실행 안 됨
}

return value; 는 불가:

public void doSomething() {
    return 42;  // ❌ 컴파일 에러: void 메서드는 값 반환 X
}

매개변수 (Parameter) vs 인자 (Argument)

자주 헷갈리는 용어:

// 매개변수 (Parameter) — 메서드 정의 시
public int add(int a, int b) {  // ← a, b가 매개변수
    return a + b;
}

// 인자 (Argument) — 호출 시 전달하는 실제 값
int result = add(3, 5);  // ← 3, 5가 인자

비유:

  • 매개변수 = 음식점 메뉴판의 "옵션" (사이즈: 소/중/대)
  • 인자 = 손님이 실제로 선택한 값 ("중 사이즈 주세요")

🏗️ 5. 내부 동작 원리

메서드 호출 시 메모리에서 일어나는 일

public class Calculator {
    public int add(int a, int b) {
        int sum = a + b;
        return sum;
    }
}

Calculator calc = new Calculator();
int result = calc.add(3, 5);

JVM 내부 흐름 ⭐ :

[Stack 영역]                    [Heap 영역]
  main()                          
  - calc (참조) ───────→  Calculator 인스턴스
  - result                        ↑
                                  │
  add() 호출 시                   │
  ┌──────────────────┐           │
  │ add()  ← 새 스택 프레임      │
  │ - this (자기 객체) ──────────┘
  │ - a = 3                       
  │ - b = 5                       
  │ - sum = 8                     
  └──────────────────┘           
  ↓ return 후                     
  add() 스택 프레임 제거          
  result = 8                      

핵심 동작:
1. add(3, 5) 호출 시 Stack에 새 프레임 생성
2. 매개변수 a=3, b=5새 프레임에 복사 (Pass by Value)
3. 메서드 안의 지역변수 sum 도 새 프레임에 생성
4. return 시 결과값을 반환받는 변수에 복사
5. 메서드 종료 → Stack 프레임 제거

이게 4주차 JVM 메모리 모델의 기초.


this — 메서드 호출의 숨은 주인공

public class Counter {
    private int count = 0;
    
    public void increment() {
        this.count++;  // this = 호출한 객체
    }
}

Counter c1 = new Counter();
Counter c2 = new Counter();

c1.increment();  // this = c1, c1.count = 1
c1.increment();  // this = c1, c1.count = 2
c2.increment();  // this = c2, c2.count = 1

JVM의 비밀 ⭐ :

  • c1.increment() 는 사실 컴파일러가 Counter.increment(c1) 으로 변환
  • 즉, 첫 번째 인자가 자동으로 this 로 전달됨
  • 우리 눈에는 안 보일 뿐

→ Java가 객체와 메서드를 연결하는 핵심 메커니즘.


메서드 오버로딩 ⭐

"같은 이름, 다른 시그니처"

public class Calculator {
    public int add(int a, int b) { return a + b; }
    public double add(double a, double b) { return a + b; }
    public int add(int a, int b, int c) { return a + b + c; }
    public String add(String a, String b) { return a + b; }
}

calc.add(1, 2);          // 첫 번째 호출
calc.add(1.5, 2.5);      // 두 번째 호출
calc.add(1, 2, 3);       // 세 번째 호출
calc.add("Hi", " Bob");  // 네 번째 호출

오버로딩 규칙 ⭐ :

  • ✅ 메서드명 같음
  • ✅ 매개변수 타입 다름 OR
  • ✅ 매개변수 개수 다름 OR
  • ✅ 매개변수 순서 다름
  • 반환 타입만 다른 건 X
public int foo() { ... }
public String foo() { ... }  // ❌ 컴파일 에러

왜 반환 타입만 다르면 안 되나?

  • 호출 시점에서 컴파일러가 어느 메서드인지 모름
  • obj.foo(); 만으로는 int 메서드인지 String 메서드인지 구별 불가

JVM 내부의 메서드 식별 ⭐ :

  • JVM은 메서드를 "메서드명 + 매개변수 시그니처" 로 식별
  • 반환 타입은 식별에 포함 안 됨

Pass by Value — 자바의 인자 전달 방식

자바는 항상 값으로 전달(Pass by Value) 합니다. (4주차에서 깊이 다룸)

기본형 매개변수:

public void changeNumber(int x) {
    x = 100;  // 메서드 내부의 x만 변경
}

int num = 10;
changeNumber(num);
System.out.println(num);  // 10 (변경 안 됨)

객체 매개변수 (참조의 값 전달):

public void changeName(Person p) {
    p.setName("Bob");  // 객체 내부 변경 — 외부 영향 ✅
}

public void replacePerson(Person p) {
    p = new Person("Bob");  // 메서드 내부의 참조만 변경
}

Person alice = new Person("Alice");
changeName(alice);
System.out.println(alice.getName());  // "Bob" (객체 내부 변경됨)

Person charlie = new Person("Charlie");
replacePerson(charlie);
System.out.println(charlie.getName());  // "Charlie" (참조 변경 안 됨)

→ 4주차에서 자세히 다룰 주제. 지금은 "자바는 항상 값 전달" 만 기억.


💻 6. 실전 코드 예시

ILIC 운임 시스템으로 메서드를 다양한 형태로 보겠습니다.

예시 1: 기본 메서드 형태들

public class FareService {
    private final FareRepository repository;
    
    // 1. 매개변수 X, 반환 X
    public void initialize() {
        System.out.println("FareService 초기화");
    }
    
    // 2. 매개변수 X, 반환 O
    public int getCount() {
        return repository.count();
    }
    
    // 3. 매개변수 O, 반환 X
    public void delete(Long id) {
        repository.deleteById(id);
    }
    
    // 4. 매개변수 O, 반환 O
    public Fare findById(Long id) {
        return repository.findById(id)
            .orElseThrow(() -> new EntityNotFoundException("운임 없음"));
    }
    
    // 5. 여러 매개변수, 복잡한 반환
    public List<Fare> search(Long customerId, FareStatus status, LocalDate from, LocalDate to) {
        return repository.findByCriteria(customerId, status, from, to);
    }
}

예시 2: 접근 제어자 활용

public class Fare {
    private int amount;       // 외부 접근 불가
    private FareStatus status;
    
    // public — 외부 API
    public void changeAmount(int newAmount) {
        validateAmount(newAmount);  // 내부 호출
        this.amount = newAmount;
        notifyChange();
    }
    
    // protected — 자식 클래스에서 오버라이드 가능
    protected void notifyChange() {
        System.out.println("운임 변경됨");
    }
    
    // package-private (default) — 같은 패키지만
    void internalReset() {
        this.amount = 0;
        this.status = FareStatus.DRAFT;
    }
    
    // private — 클래스 내부만
    private void validateAmount(int amount) {
        if (amount < 0) {
            throw new IllegalArgumentException("음수 불가");
        }
    }
    
    public int getAmount() { return amount; }
}

좋은 설계의 신호:

  • private 가 가장 많음 (내부 구현)
  • public 은 명확한 외부 API만
  • protected, default 는 의도적인 경우만

예시 3: 메서드 오버로딩 활용

public class FareCalculator {
    
    // 기본 거리 계산
    public int calculate(int distance) {
        return distance * 500;
    }
    
    // 거리 + 무게
    public int calculate(int distance, int weight) {
        return distance * 500 + weight * 100;
    }
    
    // 거리 + 무게 + 긴급 여부
    public int calculate(int distance, int weight, boolean urgent) {
        int base = calculate(distance, weight);
        return urgent ? base * 2 : base;
    }
    
    // FareRequest 객체로
    public int calculate(FareRequest request) {
        return calculate(
            request.getDistance(), 
            request.getWeight(), 
            request.isUrgent()
        );
    }
}

// 사용 — 같은 이름으로 다양한 호출
calc.calculate(100);                              // 50000
calc.calculate(100, 50);                          // 55000
calc.calculate(100, 50, true);                    // 110000
calc.calculate(new FareRequest(100, 50, true));   // 110000

효과: 사용자는 calculate 라는 한 이름 만 기억하면 됨. 다양한 시나리오를 한 메서드명으로 처리.


예시 4: void와 return; 의 활용

public class FareValidator {
    
    public void validate(Fare fare) {
        // Early Return 패턴 ⭐
        if (fare == null) {
            return;  // 즉시 종료
        }
        if (fare.getAmount() < 0) {
            throw new IllegalArgumentException("음수 운임");
        }
        if (fare.getStatus() == null) {
            throw new IllegalStateException("상태 없음");
        }
        // 모든 검증 통과
        System.out.println("검증 OK");
    }
}

Early Return 패턴:

  • 조건에 안 맞으면 즉시 return
  • 중첩 if를 줄여 가독성 향상

Bad (중첩):

public void validate(Fare fare) {
    if (fare != null) {
        if (fare.getAmount() >= 0) {
            if (fare.getStatus() != null) {
                // 검증 OK — 들여쓰기 지옥 ❌
            }
        }
    }
}

Good (Early Return):

public void validate(Fare fare) {
    if (fare == null) return;
    if (fare.getAmount() < 0) throw new IllegalArgumentException();
    if (fare.getStatus() == null) throw new IllegalStateException();
    
    // 메인 로직 — 평평한 구조 ✅
}

⚠️ 7. 주의사항 & 흔한 실수

실수 1: 메서드명을 동사가 아닌 명사로

// ❌ 명사 — 메서드인지 변수인지 모호
public int total() { ... }
public String name() { ... }

// ✅ 동사 — 의도 명확
public int calculateTotal() { ... }
public String getName() { ... }
public boolean isActive() { ... }  // 진위형은 is/has/can으로

규칙 ⭐ :

  • 일반 메서드: 동사 (calculate, find, save)
  • 조회 메서드: get (getName, getAge)
  • 진위형: is/has/can (isActive, hasPermission, canDelete)

실수 2: 매개변수가 너무 많음

// ❌ 매개변수 7개
public Fare createFare(
    Long customerId,
    int amount,
    String currency,
    LocalDate departureDate,
    String origin,
    String destination,
    boolean urgent
) { ... }

문제:

  • 호출 시 순서 헷갈림
  • 같은 타입이 여러 개면 실수 위험
createFare(1L, 50000, "KRW", date, "서울", "부산", true);
createFare(1L, 50000, "KRW", date, "부산", "서울", true);  // 출발-도착 바뀜! ❌

해결 — 객체로 묶기:

public Fare createFare(FareCreateRequest request) { ... }

public record FareCreateRequest(
    Long customerId,
    int amount,
    String currency,
    LocalDate departureDate,
    Route route,        // origin, destination 묶음
    boolean urgent
) {}

원칙: 매개변수 4개 이상이면 객체로 묶기 검토.


실수 3: 매개변수를 메서드 안에서 변경

// ❌ 매개변수를 변경 — 혼란 야기
public int processAmount(int amount) {
    amount = amount * 2;  // 매개변수 자체를 변경
    if (amount > 10000) {
        amount = 10000;
    }
    return amount;
}

문제:

  • 원래 입력값이 무엇이었는지 추적 어려움
  • 디버깅 어려움

해결 — 매개변수는 final, 새 변수 사용:

public int processAmount(final int amount) {
    int result = amount * 2;
    if (result > 10000) {
        result = 10000;
    }
    return result;
}

실수 4: void 메서드인데 결과를 return하려 함

public void doSomething() {
    int result = ...;
    return result;  // ❌ 컴파일 에러
}

해결:

  • 결과가 필요하면 반환 타입 변경
  • 결과가 필요 없으면 return; 만 (값 없이)

실수 5: 너무 긴 메서드 (God Method)

// ❌ 메서드 하나가 200줄
public void processOrder(Order order) {
    // 1. 검증 (50줄)
    // 2. 계산 (50줄)
    // 3. 저장 (30줄)
    // 4. 알림 (40줄)
    // 5. 로깅 (30줄)
}

문제:

  • 한 메서드가 너무 많은 일을 함 (SRP 위반)
  • 테스트 불가능
  • 가독성 X

해결 — 작은 메서드로 분리:

public void processOrder(Order order) {
    validate(order);
    int total = calculate(order);
    save(order);
    notify(order);
    log(order);
}

private void validate(Order order) { ... }
private int calculate(Order order) { ... }
private void save(Order order) { ... }
private void notify(Order order) { ... }
private void log(Order order) { ... }

권장: 메서드는 한 화면에 들어오는 정도 (보통 20줄 이내).


실수 6: 오버로딩 남용으로 모호함 발생

public class Calculator {
    public void process(int x) { ... }
    public void process(Integer x) { ... }  // ⚠️ Auto-boxing 모호함
}

calc.process(10);  // int? Integer? 컴파일러가 우선순위 결정

원칙:

  • 오버로딩은 명백히 다른 시나리오 일 때만
  • 비슷한 타입(int/Integer, long/Long) 으로 오버로딩은 피하기

🔗 8. 연관 개념 맵

직접 이어지는 학습

[Unit 2.1: 메서드의 구조]  ← 지금 여기
        ↓
[Unit 2.2: 가변인자] — 다음 학습
        ↓
[Unit 2.3: 상속과 생성자 체이닝]
        ↓
[Unit 2.4: 다형성] ★ — OOP의 정점

이 Unit의 개념이 활용되는 곳

1주차 내:

  • Phase 3 (SOLID): SRP — 메서드 하나가 한 가지 일만
  • Phase 4 (JVM 메모리): 메서드 호출 시 Stack 프레임 생성
  • Phase 5 (GC): 메서드 종료 시 지역변수 소멸

미래 주차:

  • 3주차 (제네릭): 메서드에 타입 매개변수 추가 (<T> T foo(T input))
  • 3주차 (람다): 메서드를 값처럼 다루기
  • 8-9주차 (AOP): 메서드 호출을 프록시로 가로채기
  • 15주차 (Spring MVC): HandlerAdapter가 메서드 시그니처 분석해서 자동 호출
  • 18주차 (Security): @PreAuthorize 가 메서드 단위 권한 검증

메서드와 디자인 패턴

패턴메서드 활용
Strategy인터페이스 메서드를 다양하게 구현
Template Method상위 메서드가 흐름, 하위 메서드가 구현
Factory Method객체 생성을 메서드로 캡슐화
Builder메서드 체이닝으로 객체 구성

→ 5주차에서 본격 학습.


면접 단골 질문 매핑

질문이 Unit에서의 답
"메서드 오버로딩이 뭔가요?"같은 이름, 다른 시그니처 (타입/개수/순서)
"오버로딩과 오버라이딩의 차이는?"오버로딩=같은 클래스 다른 시그니처, 오버라이딩=상속에서 메서드 재정의 (Unit 2.4)
"접근 제어자 4가지 차이는?"public > protected > default > private
"Pass by Value vs Reference?"자바는 항상 Pass by Value (4주차에서 자세히)
"void에서 return 가능?"값 없는 return; 만 가능

📝 9. 핵심 요약 — 3줄 정리

1️⃣ 메서드는 "객체의 행동을 정의하는 단위" 다.

절차지향의 함수가 객체에 묶인 형태로, 시그니처(접근제어자 + 반환타입 + 메서드명 + 매개변수) 로 구성된다. 메서드 없이는 캡슐화도 다형성도 불가능하므로, 객체지향의 가장 기본 도구 다.

2️⃣ 접근 제어자로 "누가 호출할 수 있는가" 를 통제한다.

private 부터 시작해서 필요할 때만 넓히는 것이 캡슐화의 핵심. public > protected > default > private 순으로 좁아지며, 좁을수록 안전하다. ILIC 같은 큰 시스템에서는 의도적인 접근 제어가 유지보수 비용을 결정한다.

3️⃣ 좋은 메서드는 "한 가지 일만, 짧게, 명확한 이름으로" 한다.

매개변수 4개 이상이면 객체로 묶기, 메서드는 한 화면에 들어오게 (20줄 내외), 동사 시작 이름 (calculate, find, save), 매개변수는 안에서 변경 X — 이 규칙들이 코드 품질을 결정한다.


🎓 학습 자기 점검

기본 이해

  • 메서드 시그니처의 5가지 구성 요소를 말할 수 있다
  • 4가지 접근 제어자의 차이를 표로 그릴 수 있다
  • void 메서드에서 return; 의 의미를 설명할 수 있다
  • 매개변수와 인자의 차이를 정확히 구별한다

실전 적용

  • ILIC의 한 클래스를 골라 메서드 시그니처를 분석할 수 있다
  • private 메서드를 늘리고 public을 줄이는 리팩토링을 할 수 있다
  • 매개변수 5개 이상인 메서드를 객체로 리팩토링할 수 있다

면접 대비 (1-2분 답변)

  • "메서드 오버로딩과 오버라이딩의 차이는?" 답변 가능
  • "왜 private이 좋은가?" 답변 가능 (캡슐화 관점)
  • "메서드 시그니처에 반환 타입은 포함되나?" 답변 가능 (NO — 식별에 미사용)

자기 점검 질문 답변

Q1: void 반환 타입의 메서드에서 return을 쓸 수 있는가?

YES. 단, 값 없는 return; 만 가능. 메서드를 즉시 종료 하는 용도.

public void greet(String name) {
    if (name == null) {
        return;  // ✅ 즉시 종료
    }
    System.out.println("Hello, " + name);
}

public void doSomething() {
    return 42;  // ❌ 값이 있는 return 불가
}

활용: Early Return 패턴 — 조건에 맞지 않으면 즉시 종료해서 중첩 if 줄이기.


Q2: 매개변수 없는 메서드와 있는 메서드의 호출 방식 차이는?

호출 시 괄호 안에 인자를 넘기느냐의 차이:

// 매개변수 없음 — 빈 괄호
obj.method();

// 매개변수 있음 — 인자 전달
obj.method(arg1, arg2);

⚠️ 주의 — 괄호는 항상 필요:

obj.method;    // ❌ 메서드 호출 아님 (메서드 참조로 해석되거나 에러)
obj.method();  // ✅ 호출

⚠️ 메서드 참조 와의 차이 (3주차 람다와 연결):

list.forEach(System.out::println);  // 메서드 참조 (호출 X)
list.forEach(x -> System.out.println(x));  // 람다

다음 Unit으로

  • 가변인자(Varargs) 를 학습할 준비 완료
  • "매개변수 개수가 가변일 때" 의 시나리오가 궁금하다

profile
Software Developer

0개의 댓글