_

Always be tactful

멋쟁이사자처럼/정기 세션

[🦁3] 객체 지향 설계: OOP

funczun 2025. 3. 28. 03:28

 


시청할 것


우아한객체지향


이번 주 키워드 정리


상속(Inheritance) vs 조합(Composition)

1. 상속(Inheritance)

  • 부모 클래스의 속성과 메서드를 자식 클래스가 물려받는 방식
  • 코드 재사용성이 높지만, 부모 클래스에 종속되어 강한 결합(High Coupling) 발생
  • 부모 클래스가 변경되면 자식 클래스에도 영향을 미쳐 유지보수가 어려워질 수 있음

2. 조합(Composition)

  • 기존 클래스를 포함(Has-A 관계)하여 기능을 재사용하는 방식
  • 강한 결합을 피할 수 있어 유지보수가 용이하고 유연성이 높음
  • 상속보다 유연하며, 런타임 시 객체를 변경할 수도 있음

🚀 언제 사용해야 할까?

  • 상속: 명확한 "is-a" 관계(예: Car is a Vehicle)
  • 조합: 유연한 "has-a" 관계(예: Car has an Engine)
  • 가능하면 조합을 우선적으로 고려하고, 정말 필요한 경우에만 상속을 사용하자.

메서드 오버로딩(Overloading) vs 오버라이딩(Overriding)

1. 오버로딩(Overloading)

  • 같은 클래스 내에서 메서드 이름은 같지만 매개변수의 타입 또는 개수가 다른 경우
  • 정적 바인딩(컴파일 타임에 결정)
  • 메서드 시그니처(이름 + 매개변수)가 다르면 같은 이름의 메서드를 여러 개 만들 수 있음
class MathUtils {
    int add(int a, int b) {
        return a + b;
    }

    double add(double a, double b) {
        return a + b;
    }
}

 

2. 오버라이딩(Overriding)

  • 부모 클래스의 메서드를 자식 클래스에서 재정의하는 것
  • 동적 바인딩(런타임에 결정)
  • 메서드 이름, 매개변수, 반환 타입이 모두 같아야 함
class Parent {
    void show() {
        System.out.println("부모 클래스");
    }
}

class Child extends Parent {
    @Override
    void show() {
        System.out.println("자식 클래스");
    }
}

 

🚀 차이점 요약

구분 오버로딩(Overloading) 오버라이딩(Overriding)
정의 같은 이름의 메서드를 매개변수 다르게 여러 개 정의 부모 클래스의 메서드를 자식 클래스에서 재정의
실행 시점 컴파일 타임(정적 바인딩) 런타임(동적 바인딩)
적용 대상 같은 클래스 내에서 상속 관계에서 부모-자식 클래스 간
반환 타입 다를 수도 있음 반드시 같아야 함

DTO(Data Transfer Object)란?

  • 데이터를 전달하기 위한 객체
  • 보통 Getter, Setter만 포함하며, 비즈니스 로직을 가지지 않음
  • Controller ↔ Service, Service ↔ Repository 간 데이터 교환에 사용
  • 불변 객체(Immutable)로 만들어야 안전함

📌 예제

public class UserDTO {
    private String name;
    private int age;

    public UserDTO(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
}

 

🚀 DTO를 쓰는 이유?

  1. 엔티티(Entity) 보호: 직접 노출하면 보안/유지보수 문제 발생
  2. 레이어 간 데이터 전달 용이
  3. 불필요한 데이터 제외: 필요한 정보만 전달 가능
  4. API 응답 형식 통일
결국 핵심은,
직접 노출시키지 않으면서 필요한 데이터만 전달 가능하다는 것이다.

객체지향 생활체조 (소트웍스 앤솔러지)

객체지향 원칙을 더 잘 따르기 위한 일종의 개발 가이드라인

🔹 1️⃣ 모든 원시값과 문자열을 포장한다.

원시값을 객체로 만들면, 그 값이 어떤 값이며, 왜 쓰이고 있는지를 알릴 수 있다.
→ int 값 자체는 아무 의미도 없다. 시간, 돈 같은 원시값들도 객체로 만들어 의도를 전달하자.
메서드 이름과 매개변수 이름을 짓는 데 노력하자.

🔹 2️⃣ 1급 컬렉션을 사용한다.

컬렉션(List, Set, Map 등)을 직접 사용하지 않고 이를 감싸는 클래스를 만든다.
→ 원시값뿐만 아니라, 컬렉션도 객체로 감싸 관리해야 한다.
본래 컬렉션이 제공하는 다양한 메서드들에 대해, 외부에 노출할 메서드를 직접 조절할 수 있다.
참고로, 컬렉션을 포함한 클래스는 다른 멤버 변수를 지니지 않아야 한다.

🔹 3️⃣ 클래스가 가능한 적은 인스턴스 변수를 갖도록 한다.

객체의 상태를 최소화하여 결합도를 낮추고 응집도를 높인다.
→ 인스턴스 변수가 많은 클래스일수록 응집력 있는 단일 작업을 설명하기 어렵다.
객체의 크기를 작게 유지하는 것이 중요하며, 필요하면 새로운 클래스로 분리한다.

🔹 4️⃣ 모든 엔티티를 작게 유지한다.

하나의 객체는 한 가지 책임만 가지도록 한다.
→ 50줄 이상 되는 클래스와 10개 파일 이상 되는 패키지는 없어야 한다.
만약 이러한 클래스가 있다면 높은 확률로 한 가지 이상의 책임을 하는 것이다.

🔹 5️⃣ 한 메서드에 오직 한 단계의 들여쓰기만 한다.

if, for, while 등의 중첩을 최소화하여 가독성을 높인다.
→ 메서드당 하나의 제어 구조나 하나의 문장 단락으로 구성되도록 노력한다.
중첩된 제어 구조가 있다면, 다단계 추상화를 코드로 짠 것으로, 하나 이상의 일을 하고 있을 확률이 높다.

🔹 6️⃣ else 예약어를 사용하지 않는다.

조건문을 단순하게 만들고, 조기 반환(early return)을 활용한다.
→ 불필요한 중첩을 없애고 가독성을 높인다.

🔹 7️⃣ 한 줄에 점을 하나만 찍는다.

디미터 법칙(Law of Demeter)을 지켜 결합도를 낮춘다.
→ 객체가 다른 객체의 내부 구조를 알지 못하도록 캡슐화를 유지해야 한다.
어떠한 코드 한 줄에 점이 하나 이상 있으면 다른 곳에서 동작이 일어나고 있을 확률이 높다.
캡슐화의 요점은 클래스 경계를 벗어나 알 필요 없는 타입으로 진입하지 않는 것이다.

🔹 8️⃣ 명령과 조회를 분리한다.

void 메서드는 상태를 변경하고, return 메서드는 값을 조회하는 역할만 한다.
→ 명령(사이드 이펙트)과 조회를 분리하면 코드의 예측 가능성이 높아진다.

🔹 9️⃣ Getter, Setter, Property를 사용하지 않는다.

데이터를 직접 꺼내지 말고 객체에 메시지(요청)를 보내도록 한다.
→ 데이터를 꺼내서 조작하는 것이 아니라, 객체가 스스로 책임을 수행해야 한다.

 

추가로 "줄여 쓰지 않는다."까지 알고 있으면 더 좋다.

이는 멋사 1차 과제 후 받은 전체 피드백에도 포함되는 내용이다!

멋사 피드백 바로가기

'멋쟁이사자처럼 > 정기 세션' 카테고리의 다른 글

[🦁2] Web App & Spring MVC #2  (2) 2025.03.23
[🦁2] Web App & Spring MVC #1  (0) 2025.03.21
[🦁1] Git & GitHub  (0) 2025.03.14