본문 바로가기
CS

CS 스터디 34 - 객체지향 프로그래밍 (OOP)

by gentle-tiger 2025. 7. 18.

 

Class와 Object에 대해 설명해주세요.

 클래스는 객체를 생성하기 위한 설계도로 실제 메모리에는 존재하지 않지만 객체를 만들기 위한 틀 역할을 합니다. 객체는 클래스의 인스턴스로 해당 클래스를 기반으로 생성된 실체로, 상태와 행동을 가집니다. 객체지향 프로그래밍은 객체 간의 상호작용을 중심으로 구성됩니다. 

 

Polymorphism 개념에 대해 설명하고, 개인/팀 프로젝트에 적용한 사례가 있다면 이야기해주세요.

다형성이란 동일한 인터페이스로 서로 다른 구현체를 사용할 수 있게 하는 객체지행의 핵심 개념입니다. 다형성을 통해 확장성과 유지보수성을 높일 수 있으며, 인터페이스나 추상 클래스를 통해 구현체를 유연하게 교체할 수 있습니다. 

 

Encapsulation(캡슐화)이란 무엇인가요?

캡슐화는 객체의 상태를 외부로부터 숨기고, 허용된 방식으로만 접근할 수 있도록 제어하는 개념입니다. 보통 자바에서는 필드를 private으로 선언하고, getter/setter를 통해서 데이터의 접근을 제어합니다. 이렇게 하면 허용된 방식으로만 접근하기 데이터가 변경되기 때문에 데이터 무결성을 보장할 수 있고, 외부에서 필드를 통해 직접 내부 상태를 변경하지 못하게 막을 수 있어 서비스의 안전성과 유지보수성을 확보할 수 있습니다. 

 

상속(Inheritance)의 개념을 설명하고, 상속의 장단점을 이야기해주세요.

상속은 기존 클래스의 특성과 기능을 새로운 클래스가 물려받아 재사용할 수 있게 하는 매케니즘입니다. 상속을 하게 되면 코드 재사용할 수 있고, 계층 구조를 명확히 할 수 있습니다. 또한 공통 기능을 상위 클래스에서 정의하고 하위 클래스에서는 세부 동작을 확장할 수 있어 코드 유연성이 높아집니다. 단, 잘못 사용하면 클래스 간의 결합도가 높아지고 다중 상속이 제한되는 자바에서는 구조가 복잡해질 수 있으며, 기능 변경이 상위 클래스 전체에 영향을 줄 수 있습니다. 그러한 문제를 해결하기 위해서 자바에서는 인터페이스를 통해 다중 상속을 지원하는 방식으로 이를 해결하고 있습니다. 

 

SOLID 원칙이란 무엇인지, 각 원칙을 설명해주세요.

SOLID 원칙은 객체지행 설계를 위한 다섯 가지 핵심 원칙으로, 유지보수를 효율적으로 하기 위한 소프트웨어 개발을 위한 기준입니다. 

- SRP(Single Responsibility Principle)는 "클래스는 하나의 책임만 가져야 하는 것"을 말합니다.

- OCP(Open/Closed Principle)는 "기존 코드를 변경하지 않고 확장이 가능해야 하는 것"을 말합니다. 

- LSP(Liskov Substitution Principle)는 "자식 클래스는 부모 클래스의 역할을 대체할 수 있어야 하는 것"을 말합니다.

- ISP(Interface Segregation Principle)는 "인터페이스는 구체적이고 작은 단위로 분리되어야 하는 것"을 말합니다. 

- DIP(Dependency Inversion Principle) "고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 추상화에 의존해야 하는 것"을 말합니다. 

 

인터페이스(Interface)와 추상 클래스(Abstract Class)의 차이점은 무엇인가요?

인터페이스는 행위를 명세하고, 추상 클래스는 상태와 행위를 동시에 정의할 수 있는 클래스입니다. 추상 클래스는 상태와 행위를 함꼐 정의할 수 있어 공통 기능을 하나로 묶어 관리할 수 있으며, 재사용에도 적합하지만 단일 상속만 가능합니다. 반면 인터페이스는 다중 상속이 가능하고 구현 클래스에 의무를 부여하지만, 기능단위로 묶어 정의하지는 않기 때문에 공통 메서드 정의에는 적합하지 않습니다. 

 

추상화(Abstraction)란 무엇이며, 코드로 어떻게 구현할 수 있는지 설명해주세요.

추상화란 객체의 핵심적인 속성과 동작만 정의하고, 세부 구현은 감추는 개념입니다. 자바에서는 추상 클래스나 인터페이스를 통해 추상화를 구현할 수 있으며, 예를 들어 Payment 인터페이스에서 결제 메서드를 정의하고, 실제 결제 방식은 CardPayment, KakaoPay 등의 클래스에서 구체적으로 구현함으로써 추상화를 실현할 수 있습니다.

 

다형성(Polymorphism)을 프로젝트에서 어떻게 활용했는지 예를 들어 설명해주세요.

다형성을 통해 인터페이스 기반의 유연한 설계를 구현하여, 서비스 로직의 변경 없이 구현체만 교체할 수 있도록 했습니다. 예를 들어 알림 기능을 구현할 때, Notifier인터페이스를 정의하고, EmailNotifier, SlackNotifier 등의 구현체를 만들었습니다. 이렇게 구현할 경우 주입받는 구현체만 변경함으로써 기능을 확장하거나 교체할 수 있었고, 서비스 클래스는 인터페이스에만 의존하도록 설계하였습니다. 

 

의존성 주입(Dependency Injection)이란 무엇이며, 이를 적용했을 때의 장점을 설명해주세요.

의존성 주입은 객체 간 의존 관계를 외부에서 주입받아 구성하는 설계 방식입니다. Spring Framework에서는 주로 생성자 주입을 통해 의존성을 주입하였으며, 이를 통해 객체 간 결합도를 낮추고, 테스트 코드 작성이 용이해지며, 구현체 교체 수월 및 유지보수성 향상이라는 장점이 있습니다. 실제 서비스 클래스에서 레포지토리를 주입받아 사용하는 구조로 구성하면 유닛 테스트에서 mock 데이터를 쉽게 주입할 수 있습니다. 

 

디자인 패턴 중 싱글톤(Singleton) 패턴을 설명하고, 실제로 구현할 때 고려해야 할 사항을 이야기해주세요.

싱글톤 패턴은 애플리케이션에서 하나의 인스턴스만 생성되도록 보장하는 디자인 패턴입니다. Spring에서는 기본적으로 모든 Bean이 싱글톤으로 관리됩니다. 만약 수동 구현 시에는 멀티스레드 환경에서 동기화 이슈를 고려하고, 더티 체킹이나 enum을 활용하는 것이 적절합니다. 싱글톤 객체는 여러 스레드가 동시에 공유하므로, 무상태(stateles)로 설계하면 동기화 없이도 thread-safe하게 동직할 수 있습니다. 

public class MySingleton {

    // volatile: 다중 스레드 환경에서의 안전성 확보
    private static volatile MySingleton instance;

    // 생성자 private: 외부에서 new 불가
    private MySingleton() {
        // 초기화 코드 가능
    }

    // getInstance() 메서드로 단일 인스턴스 제공
    public static MySingleton getInstance() {
        if (instance == null) { // 1차 체크
            synchronized (MySingleton.class) {
                if (instance == null) { // 2차 체크 (동기화 블록 내)
                    instance = new MySingleton();
                }
            }
        }
        return instance;
    }
}

 

객체지향 설계에서 SOLID 원칙 중 단일 책임 원칙(SRP)이 지켜지지 않으면 어떤 문제가 발생할 수 있나요? 이를 해결한 경험이 있다면 설명해 주세요.

단일 책임 원칙이 지켜지지 않으면 클래스가 여러 역할을 하기 되어 변경에 취약하게 됩니다. 예를 들어 서비스 클래스에서 비즈니스 로직과 DB 통신을 모두 처리한다면, 코드 복잡성이 늘어나고 문제 발생 원인을 명확하게 파악하지 못할 수있습니다. 이를 해결하기 위해 Controller, Service, Repository를 명확히 분리하고, 역할별로 책임을 분산함으로써 유지보수와 테스트 효율을 개선하는 것이 바람직합니다. 

 

의존성 주입(Dependency Injection)을 프로젝트에 어떻게 적용했고, 이를 통해 얻은 장점은 무엇인가요?

Spring의 @Conponent와 @Autowired, 생성자 주입을 활용해 의존성을 외부에서 주입받아았습니다. 이를 통해 테스트 시 의존 객체를 쉽게 교체할 수 있었으며, 단위 테스트 시 mock 객체를 주입해 테스트 범위를 좁하는 데에도 효과적이었습니다. 

 

다형성(Polymorphism)을 통해 인터페이스와 구현체를 분리하여 설계한 예시를 설명해주세요.

다형성을 활용해 인터페이스와 구현치를 분리하였습니다. 예를 들어 알림 기능을 구현할 때, Notifier인터페이스를 정의하고, EmailNotifier, SlackNotifier 등의 구현체를 만들었습니다. 이렇게 구현할 경우 주입받는 구현체만 변경함으로써 기능을 확장하거나 교체할 수 있었고, 서비스 클래스는 인터페이스에만 의존하도록 설계하였습니다. 

 

상속(Inheritance) 대신 구성(Composition)을 사용하는 것이 더 적합한 경우는 언제인가요? 이유와 함께 설명해 주세요.

구성은 클래스 간 결합도를 낮추고, 더 유연한 기능 조합이 필요할 때 적합하고, 상속은 강한 결합을 만들며 기능이 고정된 계층 구조를 가집니다. 예를 들어 이메일, 문자, 푸시 등 다양한 알림 전송 방식이 필요한 시스템에서는 전송 전략을 객체로 분리해 조합하는 방식으로 설계하면 기능 확장이 용이합니다. 

 

디자인 패턴 중 싱글톤(Singleton)을 사용했을 때 발생할 수 있는 문제점과 해결 방안을 설명해 주세요.

싱글톤 패턴은 상태를 가지면 공유 자원으로 인해 동시성 문제가 발생할 수 있습니다. 멀티스레드 환경에서는 동기화 처리가 필요하며, 설계 시 상태를 가지지 않도록 해야 합니다. 또한 테스트 환경에서는 전역 상태로 인해 테스트 독립성이 깨질 수 있습니다. 이를 해결하기 위해 DI 컨테이너를 사용해 객체 생성을 관리하고, 상태를 갖지 않는 서비스로 설계함으로서 문제를 방지합니다.