데이터를 위한 Back_End 공부하기 : 'Java' 개념 정리.

post-thumbnail

데이터를 위한 Back_End 공부하기 : 'Java' 개념 정리.

▽ 데이터를 위한 Back_End 공부하기 : 'Java' 개념 정리.

목   차

1-1. 클래스 란?                      
1-2. 객체 란?                     
1-3. 인스턴스 란?
                      
2-1. 필드                      
2-2 생성자
2-3 메소드
                      
번외)
3-1. 생성자 오버로딩
3-2. 메소드 오버로딩
                      
5. final 필드와 상수
                      
6. 접근 제한자
                      
7. Getter, Setter
                      
8. 오버라이딩(Overriding)
                      
10-1 캡슐화 (Encapsulation)
10-2 상속 (Inheritance)
10-3 다형성(Polymorphism)
10-4 추상화(Abstraction)
                      
11-1 SRP 단일 책임 원칙 (Single Responsibility Principle)
11-2 OCP 개방 폐쇄 원칙 (Open-Closed Principle)
11-3 LSP 리스코프 치환 원칙 (Liskov Substitution Principle)
11-4 ISP 인터페이스 분리 원칙 (Interface Segregation Principle)
11-5 DIP 의존성 역전의 원칙 (Dependency Inversion Principle)
                      
12-1 얕은 복사 (Shallow Copy)
12-2 깊은 복사 (Deep Copy)
                      
13-1 메서드(Method) 영역
13-2 힙(Heap) 영역
13-3 스택(Stack)영역
13-4 PC 레지스터(Program Counter Register)
13-5 네이티브 메서드 스택 (Native Method Stack)

📘 1-1. 클래스(Class)란?


✅ 기본 정의 (문법적 관점).

  • 자바에서 클래스는 데이터와 행동을 캡슐화한 사용자 정의 타입(User-defined Type) 입니다.

  • 클래스는 객체의 속성(Field)동작(Method)을 정의하며, 객체를 생성하기 위한 청사진(설계도) 역할을 합니다.

✅ 백엔드 개발 관점에서의 클래스.

  • 도메인 로직을 캡슐화하는 기본 단위

  • 계층 아키텍처의 구성요소 (Controller, Service, Entity, DTO 등)로 클래스를 나누어 설계

  • SOLID 원칙, 객체지향 설계 원칙을 적용하는 기본 단위

✅ 실무에서 클래스가 쓰이는 방식.

용도예시
도메인 모델User, Order, Product 등의 Entity
기능 수행 로직UserService, OrderProcessor
입출력 구조 정의UserRequestDto, UserResponseDto
컨트롤 계층UserController
예외 구조화BusinessException, NotFoundException

✅ 클래스가 중요한 이유.

  • 책임 단위를 분리하여, 유지보수를 용이하게 함

  • 팀 내 협업 및 모듈화 기준 단위가 됨

  • 적절한 추상화를 통해 코드 중복 방지, 재사용성 확보

📘 1-2. 객체(Object)란?


✅ 기본 정의 .

  • 객체는 클래스의 인스턴스(실체)로,
  • 실제로 메모리에 존재하며 클래스에 정의된 속성과 동작을 가질 수 있는
    런타임 엔터티(Runtime Entity)입니다.
User user = new User("andamiro", "dev@team.com");

✅ 실무 백엔드에서 객체는? .

  • 애플리케이션의 실행 중 실질적으로 행동하는 주체

  • DB에서 읽어온 Entity 객체, 외부 API 결과로 생성된 DTO 객체 등

  • 객체는 상태를 가지며, 해당 상태는 변경될 수 있음
    → 트랜잭션에서 이 변경은 중요한 의미를 가짐.

✅ 객체를 잘 다룬다는 의미 .

  • 캡슐화를 지켜 적절한 메서드로 상태 변경 유도

  • 객체의 생명주기를 고려 (Spring Bean 관리, 트랜잭션 범위)

  • 불필요한 상태 공유를 막기 위해 불변 객체 패턴(Immutable Object)을 설계할 수 있음

✅ 객체의 책임 분리 실전 예시.

User user = userRepository.findById(1L);
user.changeEmail("new@mail.com"); // 도메인 객체에게 책임 위임

📘 1-3. 인스턴스(Instance)란?


✅ 개념의 핵심

  • 인스턴스는 클래스에서 new 연산자를 통해 메모리에 실제로 생성된 객체를 말합니다.

  • 즉, 클래스 → 객체 생성 → 메모리에 올라감 → 인스턴스라는 과정의 결과입니다.

✅ 객체 vs 인스턴스 실무적 구분.

측면객체(Object)인스턴스(Instance)
정의클래스 기반 실체객체가 메모리에 존재하는 상태
문맥넓은 의미로 사용됨클래스와의 관계 강조 시 사용
실무 사용 예"이 객체가 어떤 역할을 하나?""User 클래스의 인스턴스를 생성함"

✅ 메모리 관점에서 보기 (JVM 구조).

  • 클래스는 메서드 영역(Method Area)에 올라가고

  • new 로 생성된 인스턴스는 힙(Heap) 영역에 위치하며,

  • 참조 변수는 스택(Stack)에 위치

User u1 = new User(); // u1: Stack, new User(): Heap

✅ 실무에서는 이렇게 이해하자.

  • “User는 클래스”

  • “user는 User의 인스턴스이자 객체”

  • 코드에서 말할 땐 대부분 "객체"라 칭하지만, 설계나 JVM 메모리 이야기할 땐 "인스턴스"라 말함

🔷요약.

용어의미실무에서의 예
클래스설계도UserService, OrderController
객체클래스를 기반으로 생성된 코드 상의 실체new User(), responseDto
인스턴스객체가 메모리에 존재하는 상태User 인스턴스가 힙 메모리에 존재

🔷클래스/객체/인스턴스를 구분해서 설계하자.

// 설계도
public class User {
    private String name;
    public void changeName(String newName) { this.name = newName; }
}

// 실체 생성
User user = new User(); // 객체이자 인스턴스
user.changeName("andamiro");

🔑 실무 설계 팁.

  • DTO는 대부분 불변 객체로 생성

  • Entity는 트랜잭션 내에서 변경이 가능한 객체

  • Service는 상태 없는 객체 (Stateless)로 사용 (싱글톤 Bean)

📘 2-1. 필드(Field)


📗 필드란 무엇인가? — 단순한 "변수" 그 이상.

  • 자바에서 '필드(Field)'는 클래스 내부에서 선언되는 '객체의 상태(state)'를 저장하는 변수입니다.

  • 즉, '객체의 속성(attribute)'으로서, 객체마다 개별적인 값을 가지게 됩니다.

  • { 필드 = 클래스의 데이터 멤버 = 객체가 가지고 있는 속성 = 인스턴스 변수 } 라고 이해 가능 !!.

public class User {
    private String name; // ← 이게 필드
    private int age;
}
  • 실제 개발 과정에서는 '필드'를 그냥 '값을 담는 그릇' 정도로만 이해하면 안 됩니다.

필드는 클래스의 책임(responsibility)을 결정짓는 구조적 핵심 입니다. !!

📗 JVM 관점 : 필드가 메모리에서 어떤 위치에 존재하는가??

  • 자바 객체가 생성되면, 그 객체의 '필드 값은 JVM의 Heap 영역'에 저장됩니다.
User user = new User("andamiro", 30);
  • 'user'는 Stack 영역에 있는 참조 변수

  • 'new User(...)'는 Heap 영역에 생성된 인스턴스.

  • 그 안의 'name', 'age'라는 필드들은 해당 'Heap' 객체에 속한 상태 정보로 저장됨.

업로드중..

📗 필드를 왜 private으로 선언해야 하는가?

  • '객체지향 설계 원칙' 중 하나인 '캡슐화(Encapsulation)'를 지키기 위해
  • '필드'는 반드시! 'private'으로 선언해야 합니다.
public class User {
    private String name;

    public String getName() {
        return name;
    }

    public void changeName(String newName) {
        this.name = newName;
    }
}

📙 '캡슐화'를 지키는 이유는???

  • 외부에서 객체의 내부 상태를 '직접적으로 변경하지 못하게 하여서 일관성을 유지 ! '

  • 객체 스스로만 자신의 상태를 바꾸도록 유도 -->> "도메인 중심 설계 가능"

  • 상태 변경의 트리거를 '메서드에 집중' 시킬 수 있음 -->> 유효성 검사, 트랜잭션 추적 가능. !

📗 실무에서 필드를 어떻게 설계하는가??

📙 불변(immutable) 필드 설계.

  • DTO 클래스, 응답 객체 등은 상태가 변하면 안 되므로 'final' 필드 + 생성자 초기화.
public class UserDto {
    private final String name;
    private final int age;

    public UserDto(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
  • 장점 : 쓰레드 안전성 & 예측 가능한 코드 & 사이드 이펙트 없음.

📙 가변(mutable) 필드 설계.

  • JPA Entity 등은 상태 변경이 필요한 객체

  • 트랜잭션 내에서 상태 변경 후, persistence context에 반영됨

@Entity
public class User {
    @Id @GeneratedValue
    private Long id;

    private String name;

    public void changeName(String newName) {
        this.name = newName;
    }
}

📗 필드의 종류.

필드 종류설명예시
인스턴스 필드객체마다 개별적으로 존재private String name;
정적(static) 필드클래스 전체에서 공유private static int count;
final 필드초기화 후 변경 불가private final String id;

📗 잘못된 설계 예시.

❌ 외부에서 필드에 직접 접근.

public class User {
    public String name; // 직접 접근 가능 → 캡슐화 위반
}

❌ Setter 남용 (불변성을 해침).

public class UserDto {
    private String name;

    public void setName(String name) {
        this.name = name;
    }
}
  • 실제 개발과정에서는 DTO의 'setter'가 무분별하게 열려 있으면, "어디서 상태가 변경되는지 추적하기 어렵고', 보안적으로도 문제가 될 수 있습니다.

    • 그래서 @Getter만 제공하고 @Setter는 최소화합니다.

📗 '필드'가 실무에서 중요한 이유.

실무 측면설명
유지보수필드를 잘 구조화하면 변경 영향도가 낮아짐 (loose coupling)
테스트필드가 불변이면 테스트 코드가 단순하고 예측 가능
협업필드 네이밍이 명확해야 의도 파악 쉬움
보안민감 데이터(예: 비밀번호)는 필드 단에서 접근 제한 필요

📗 Ex: 설계 관점의 User 클래스.

public class User {
    private final String email;
    private String nickname;
    private boolean active;

    public User(String email, String nickname) {
        this.email = email;
        this.nickname = nickname;
        this.active = true;
    }

    public void deactivate() {
        this.active = false;
    }

    public void changeNickname(String newNickname) {
        if (newNickname.length() < 3) {
            throw new IllegalArgumentException("닉네임은 3자 이상이어야 합니다.");
        }
        this.nickname = newNickname;
    }

    public String getEmail() { return email; }
    public String getNickname() { return nickname; }
}
  • 'email' : 불변 -->> 식별자.

  • 'nickname' : 가변 -->> 유효성 검사를 갖춘 setter

  • 'active' : 상태 관리 -->> 행동을 메서드로 유도.

📗 ① final 필드와 Lombok의 @RequiredArgsConstructor.

📙 개요.

  • 'final 필드'는 '한 번 초기화되면 값을 변경할 수 없는 변수'.

  • '불변 객체(immutable Object)'를 만들 때 핵심 요소.

  • Lombok은 final 필드 또는 @NonNull 필드를 포함하는 생성자를 자동 생성해줌
    → @RequiredArgsConstructor

📙 실제 개발에서 final 필드를 사용하는 이유.

장점설명
쓰레드 안전성공유 객체가 상태를 바꾸지 않으므로 동기화 필요 없음
테스트 용이성한 번 설정된 상태가 변하지 않음 (예측 가능)
설계 명확성어떤 값이 "초기화 후 불변"인지 코드로 드러남
의도 표현"이 필드는 생성자에서 반드시 초기화돼야 한다"는 의사소통 가능

📙 ex) 수동 vs Lombok

🧵 수동 방식
public class UserDto {
    private final String name;
    private final int age;

    public UserDto(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
⚡ Lombok 방식
@RequiredArgsConstructor
public class UserDto {
    private final String name;
    private final int age;
}

💡 주의: final만으로는 Bean 등록 시 불편할 수 있으니, 스프링 빈에서 사용하는 생성자는
@Autowired 또는 @RequiredArgsConstructor(onConstructor = @__(@Autowired)) 와 같이 쓰기도 함

📙 DDD 관점에서의 의미.

  • 값 객체(Value Object), 응답 DTO, Command 객체처럼 바뀌지 않아야 하는 객체에서
    final 필드 사용은 설계 원칙을 표현하는 수단입니다.

  • 특히 @Builder + @Getter + @RequiredArgsConstructor 조합은 깔끔하고 불변 설계에 적합 !

📗 ② static 필드의 사용 주의점.

📙 static 필드란?

  • 클래스 로딩 시 메모리에 한 번만 올라가며, '모든 인스턴스가 공유'

  • '인스턴스와 무관하게 전역 상태를 저장' 합니다.

📙 실제 개발과정에서 주의사항.

항목주의 내용
상태 공유모든 객체가 공유하므로, 잘못된 상태 변경이 모든 인스턴스에 영향을 줌
테스트 어려움테스트 간 전역 상태가 유지되기 때문에 독립성 깨짐
GC 대상 제외클래스가 살아있는 한 static은 GC 대상이 아님 → 메모리 누수 가능성
DI 방해Spring에서 객체를 주입받지 않고 직접 static으로 접근하면 결합도 증가

❌ 잘못된 예시.

public class UserService {
    public static String globalName = "test";
}
  • 테스트 간 충돌
  • 병렬 환경에서 예상치 못한 동작.

✅ 적절한 예시.

  • 상수(Constant) 정의용.
public class JwtProperties {
    public static final String SECRET = "JWT_SECRET_KEY";
}
  • 유틸 클래스.
public final class StringUtils {
    public static boolean isEmpty(String str) { ... }
}
  • 스프링 빈에 의존하지 않는 경우 ( 희소 )

📗 ③ JVM에서 필드 메모리 배치 (Escape Analysis 포함).

📙 개요.

  • JVM은 객체를 생성할 때 '필드를 Heap에 배치' 하지만,

  • 최적화 기법에 따라 'Escape Analysis'를 수행하여, 'Stack'에 올릴 수도 있습니다.

    • -> GC(Garbage Collection) 부하를 줄이는 핵심 최적화 전략.

📙 Escape Analysis란.

객체가 메서드 밖으로 "탈출"하는가? 를 분석하는 최적화 방법.

결과설명
Escape O다른 메서드나 쓰레드에서 참조 → Heap에 할당
Escape X메서드 내부에서만 사용 → Stack에 할당 (Escape Analysis OK)

📙 EX.

public String createFullName(String first, String last) {
    FullName name = new FullName(first, last); // Escape X → Stack 할당 가능
    return name.toString();
}

.

public void saveUser() {
    User user = new User(...);
    userRepository.save(user); // Escape O → Heap 할당
}

📙 Escape Analysis 이점.

  • GC(Garbage Collection) 비용 감소.

  • CPU 캐시 효율 향상 ( 메모리 locality 증가 )

  • Lock 제거 가능성 ( Lock Elision )

단! , 정확한 적용 여부는 JDK 버전, JVM 설정, JIT 여부에 따라 상이. !

- 확인 방법.
     - JVM 옵션 -XX:+PrintEscapeAnalysis -XX:+DoEscapeAnalysis
     - GraalVM, JMH 등을 통해 간접 테스트 가능

📗 ④ JSON 직렬화 시 필드 노출 제한 (@JsonIgnore, @JsonProperty).

📙 Json 직렬화/역직렬화 시 필드 공개를 제어하는 방법.

어노테이션역할
@JsonIgnore해당 필드는 JSON 결과에 포함되지 않음
@JsonProperty("이름")필드명을 JSON에서 다르게 매핑
@JsonInclude(Include.NON_NULL)null 값은 제외하고 직렬화

📙 EX.

public class UserResponse {

    private String name;

    @JsonIgnore
    private String password;

    @JsonProperty("user_age")
    private int age;
}
  • 출력 결과.

{
"name": "andamiro",
"user_age": 28
}

 - password는 누락.
 - age는 user_age로 표시.
 
 

📙 실제 개발 적용 케이스.

케이스설명
보안비밀번호, 토큰, 인증 관련 필드 감춤
API 규약snake_case 등 JSON 규칙에 맞춰 필드 이름 매핑
옵셔널응답에 null 필드 제외 (특히 RESTful API에서 사용)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseDto {
    private String data;
    private String errorMessage; // null이면 응답에서 제외
}

📘 2-2 생성자(Constructor)


📗 생성자란 무엇인가?

  • 자바에서 '생성자(Constructor)'는 객체가 생성될 때 자동으로 호출되어,
    '필드를 초기화하고 객체의 상태를 설정하는 특별한 메서드' 입니다.
public class User {
    private final String name;

    // 생성자
    public User(String name) {
        this.name = name;
    }
}

  • 메서드처럼 보이지만, '리턴 타입이 없음'

  • 클래스 이름과 동일해야 함. !

  • new 키워드와 함께 호출 됨.

✅ 핵심 포인트: 생성자는 객체를 “무결한 상태”로 만들기 위한 진입점. !

📗 생성자의 역할 == 객체의 불변 조건(invariant) 보장.

  • 실제 개발 환경에서 가장 중요한 생성자의 역할은
    • "불완전한 객체가 절대로! 생성되지 않도록 하는 것" 입니다. !!
    • -> 즉, 유효한 상태로만 객체가 생성되도록 보장해야 합니다.

ex) 필수 조건을 검증하기.

public class User {
    private final String email;

    public User(String email) {
        if (email == null || !email.contains("@")) {
            throw new IllegalArgumentException("유효한 이메일 주소여야 합니다.");
        }
        this.email = email;
    }
}

-> 이런 식으로 '생성자에서 validation을 수행' 함으로써,

애초에 잘못된 객체가 태어나지 않도록 차단하는 것이 실제 코드 설계의 핵심입니다.

📗 생성자의 종류와 설계 전략.

📙 기본 생성자 ( No-Args Constructor )

  • 필드 초기화를 하지 않거나, JPA 등의 프레임워크에서 프록시 객체 생성을 위해 필요. !

  • User user = new User();

⚠️ 실제 개발 과정에서는 '기본 생성자를 노출하지 않거나, protected 처리' 하는 경우가 많음.

@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
    ...
}

📙 모든 필드를 초기화하는 생성자 (All-Args Constructor)

@AllArgsConstructor
public class User {
    private String name;
    private int age;
}
  • 모든 필드를 명확하게 초기화하되, '필드 수가 많아질수록 코드 가독성이 떨어질 수 있음'

📙 필수 필드만 받는 생성자.

  • '객체 생성' 시 필요한 최소한의 정보만 받도록 설계.

  • 나머지 필드는 'setter' 혹은 '메서드'로 설정.

public class User {
    private final String email;
    private String nickname;

    public User(String email) {
        this.email = email;
    }

    public void changeNickname(String nickname) {
        this.nickname = nickname;
    }
}

📗 생성자의 과부하 (Overloading).

  • 자바는 생성자 오버로딩을 지원합니다.

    • "다양한 생성 경로를 제공하여, 유연한 객체 생성을 가능" 하게 합니다. !
public class User {
    private final String name;
    private int age;

    public User(String name) {
        this(name, 0);
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
  • 생성자 내에서 'this(...)' 사용 -->> 중복 제거.

  • 객체 생성 경로를 유연하게 구성. !

📗 Lombok과 생성자.

  • Lombok은 생성자 작성의 번거로움을 줄여줍니다.
어노테이션기능
@NoArgsConstructor기본 생성자 자동 생성
@AllArgsConstructor모든 필드 포함한 생성자 생성
@RequiredArgsConstructorfinal 또는 @NonNull 필드만 포함한 생성자 생성

📙 ex)

@RequiredArgsConstructor
public class User {
    private final String email;
    private String nickname;
}

-> 'new User("andamiro@example.com") ' 으로 객체 생성 가능.

💡 @RequiredArgsConstructor(onConstructor = @__(@Autowired)) : 스프링 DI에 사용

📗 생성자와 객체 생성 제어.

📙 생성자 접근 제한자.

  • public : 어디서든 객체 생성 가능 ( API 레벨 노출 )

  • protected : 상속 또는 동일 패키지내에서만 생성 가능 -> "프레임워크용"

  • private : 외부에서 생성자 호출 금지 -> "정적 팩토리 메서드 패턴에 사용".

public class User {
    private final String email;

    private User(String email) {
        this.email = email;
    }

    public static User fromEmail(String email) {
        return new User(email);
    }
}
  • ✅ User user = User.fromEmail("andamiro@example.com");

  • 생성자를 감추고 정적 메서드를 통해 의미 있는 생성 방식을 제공 ! ( 이름을 가질 수 있음 )

  • 객체 생성을 '비즈니스 로직' 또는 '도메인 정책' 과 연결 가능 !

📗 생성자와 JPA.

  • JPA Entity는 반드시 기본 생성자(매개변수 없는 생성자)가 필요합니다.

📙 JPA 생성자 설계 시 원칙.

규칙설명
기본 생성자 필요Hibernate가 리플렉션으로 객체 생성 시 사용
protected로 선언외부에서 생성 막고, 프록시 생성 허용
비즈니스 로직 생성자 별도 제공유효한 객체 생성을 보장하는 생성자 따로 정의
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {

    @Id @GeneratedValue
    private Long id;

    private String email;

    public User(String email) {
        this.email = email;
    }
}

📗 생성자 vs 정적 팩토리 메서드.

항목생성자정적 팩토리 메서드
이름없음있음 (of(), from(), with() 등)
리턴 타입클래스 자기 자신유연함 (서브 타입 반환 가능)
객체 생성 제어불가능가능 (캐싱, 싱글턴 등)
사용 목적단순한 생성의미 있는 생성, 다양한 전략

실제 개발 환경에선, 스프링 내부 코드나 DDD 도메인 객체에서는 생성자보다는 정적 팩토리 메서드가 더 선호되기도 함. !

📗 생성자 테스트 코드 작성.

📙 단위 테스트에서 생성자의 역할.

  • 생성자는 객체의 '초기 상태 보증' 이므로, 테스트 대상 !

  • 예외 상황까지 모두 테스트.

@Test
void createUser_withInvalidEmail_throwsException() {
    assertThrows(IllegalArgumentException.class, () -> {
        new User("invalid-email");
    });
}

📗 요약 : 생성자 사용은 이렇게.

원칙실무 적용 포인트
최소한의 불변 조건 확보생성자에서 필수 값 검증
상태 불안정 방지setter 없는 설계 + 생성자에서 초기화
외부 공개 제한정적 팩토리 메서드 패턴과 조합
테스트 가능성잘못된 파라미터 시 예외 발생 보장
JPA 호환@NoArgsConstructor(access = PROTECTED) 필수

📘 2-3 메서드(Method) – 객체의 행동(Behavior)을 정의하는 단위.


✅ 메서드란??

  • 메서드는 객체의 "동작"을 정의하며,
  • 입력(매개변수)을 받아서, '비즈니스 로직을 실행하고, 그 결과를 반환하는 기능 단위' 입니다.
public class User {
    private String name;

    public void changeName(String newName) {
        this.name = newName;
    }

    public String getName() {
        return this.name;
    }
}

✅ 실제 개발과정에서 메서드 활용 원칙.

기준설명
SRP (단일 책임 원칙)하나의 메서드는 하나의 작업만 수행해야 한다
테스트 가능성순수 함수(Pure Function)에 가까울수록 테스트 용이
재사용성중복 제거를 위한 핵심 수단
가독성이름, 길이, 책임 명확히
캡슐화객체 상태 변경은 메서드를 통해서만 이루어져야 함

✅ 메서드 시그니처 구성 요소.

public int calculateDiscount(int price, double rate) {
    return (int)(price * rate);
}
구성 요소설명
접근 제어자public, private, protected, (default)
반환 타입int, void, String, User
메서드 이름동사 + 목적어 조합 추천 (saveUser, getName, changeStatus)
매개변수입력 값. 너무 많으면 DTO 객체로 묶는 것 고려
예외 선언throws 사용 가능 (예외는 컨트롤할 수 있어야 함)

✅ 실제 개발과정에서 메서드 작성 시 주의할 원칙.

📙 메서드의 이름은 "의도를 명확히 표현"해야 함.

나쁜 예좋은 예
handle()sendWelcomeEmail()
doTask()processPayment()
action()changeOrderStatus()

✔️ 이름만 보고 "무엇을 하는지" 유추 가능해야, 협업과 유지보수에서 유리합니다.

📙 메서드 길이는 짧게 ( 10줄 이내 권장 )

  • 하나의 기능만 수행하도록 !

  • 여러 기능이 있다면 내부 메서드로 분리.

public void registerUser(UserRequest request) {
    validateEmail(request.getEmail());
    User user = createUser(request);
    userRepository.save(user);
    sendWelcomeEmail(user);
}

-->> 각 로직은 책임이 분리된 메서드로 위임!.

📙 매개변수가 많다면 DTO로 묶기.

// ❌ 너무 많은 파라미터
public void register(String email, String name, int age, String address, String phone) { ... }

// ✅ DTO로 감싸기
public void register(UserRegisterRequest request) { ... }
  • 명세가 명확해지고, 파라미터 순서 실수 방지 가능

  • 스프링에서는 '@valid'와 함께 사용 가능.

✅ 메서드의 접근 제어 전략 ( 캡슐화 )

접근자설명
private외부에 노출할 필요 없는 내부 구현
protected상속 관계에서만 사용
public외부 API로 노출됨. 신중하게 설계 필요

📌 캡슐화 : "객체의 상태를 보호하고 변화는 오직 메서드를 통해"

public class User {
    private String password;

    public void changePassword(String currentPw, String newPw) {
        if (!this.password.equals(currentPw)) {
            throw new IllegalArgumentException("비밀번호 불일치");
        }
        this.password = newPw;
    }
}

-->> 필드 직접 변경은 막고, 메서드로 상태를 통제. !

✅ 메서드의 반환값 설계.

📙 단순한 값 반환 vs 도메인 객체 반환.

public int getAge();  // 단순 타입

public OrderStatus changeStatus(OrderStatus newStatus); // 의미 있는 도메인 객체
  • 단순 타입보다 의미 있는 '도메인 객체나 enum'으로 반환하면, 코드 해석성이 높아집니다. !

📙 Optional로 null 방지.

public Optional<User> findById(Long id);
  • 실무에서는 'null' 대신, 'Optional'을 반환함으로써, NPE(NullPointException)를 사전에 차단

  • 클라이언트는 'isPresent()' 나 'orElseThrow()' 등을 통해 예외 처리.

✅ 메서드 분리 기준 예시.

❌ 안 좋은 메서드 예.

public void processOrder(Order order) {
    if (order.getStatus() == OrderStatus.CANCELLED) return;

    order.setStatus(OrderStatus.SHIPPED);
    orderRepository.save(order);
    eventPublisher.publish(new OrderShippedEvent(order));
}

-->> 너무 많은 일을 처리 : 검증, 상태 변경, 저장, 이벤트 발행까지

✅ 개선된 버전.

public void processOrder(Order order) {
    if (!canShip(order)) return;

    ship(order);
    publishEvent(order);
}
  • 각 로직을 내부 private 메서드로 분리하여, 책임을 명확히.

✅ 메서드 오버로딩 vs 오버라이딩.

구분설명
오버로딩동일한 이름, 서로 다른 시그니처 (컴파일 타임 결정)
오버라이딩상속 관계에서 메서드 재정의 (런타임 다형성 활용)
// 오버로딩
public void print(String msg) { ... }
public void print(int count) { ... }

// 오버라이딩
@Override
public void save(User user) { ... } // Repository에서 JPA 메서드 재정의

✅ 테스트 코드에서 본 메서드의 역할.

  • 객체 간 협력에서 '가장 단위가 되는 요소'

  • 단위 테스트의 주 대상이 됨.

@Test
void changeNickname_shouldUpdateCorrectly() {
    User user = new User("andamiro@example.com");
    user.changeNickname("다미로");
    assertEquals("다미로", user.getNickname());
}
  • 한 메서드에, 한 테스트 케이스가 기본.

✅ 요약.

항목실무 핵심 정리
메서드 역할객체의 행동 정의 (상태 변경 또는 정보 조회)
설계 기준SRP, 작고 명확한 책임, 캡슐화 유지
이름 규칙동사 + 목적어 (changeStatus, getUserById)
DTO 활용파라미터 많을 때 DTO로 캡슐화
반환값Optional, Enum, VO 등을 활용해 의미 부여
테스트 가능성부작용 없는 메서드 = 테스트 용이

번외).


3-1. 생성자 오버로딩.


3-2. 메소드 오버로딩.


5. final 필드와 상수.


6. 접근 제한자.


7. Getter, Setter.


8. 오버라이딩(Overriding).


10-1 캡슐화 (Encapsulation).


10-2 상속 (Inheritance).


10-3 다형성(Polymorphism).


10-4 추상화(Abstraction)


11-1 SRP 단일 책임 원칙 (Single Responsibility Principle).


11-2 OCP 개방 폐쇄 원칙 (Open-Closed Principle).


11-3 LSP 리스코프 치환 원칙 (Liskov Substitution Principle).


11-4 ISP 인터페이스 분리 원칙 (Interface Segregation Principle).


11-5 DIP 의존성 역전의 원칙 (Dependency Inversion Principle)


12-1 얕은 복사 (Shallow Copy).


12-2 깊은 복사 (Deep Copy)


13-1 메서드(Method) 영역.


13-2 힙(Heap) 영역.


13-3 스택(Stack)영역


13-4 PC 레지스터(Program Counter Register)


13-5 네이티브 메서드 스택 (Native Method Stack)


0개의 댓글