Post

[☕JAVA] 객체 지향 프로그래밍(OOP)

[☕JAVA] 객체 지향 프로그래밍(OOP)

이번 포스팅에서는 절차 지향 프로그래밍에서 출발해 점진적으로 코드를 개선하며 진정한 객체 지향 프로그래밍이 무엇인지 익혀먹어 보자.


절차 지향 vs. 객체 지향 🤔

프로그래밍 패러다임은 크게 두 가지로 나눌 수 있다.

  • 절차 지향 프로그래밍 (Procedural Programming)
    • 실행 순서, 즉 “어떻게” 동작할지에 초점을 맞추는 방식
    • 데이터와 그 데이터를 처리하는 기능(함수)이 서로 분리되어 있음
  • 객체 지향 프로그래밍 (Object-Oriented Programming)
    • 객체, 즉 “무엇을” 만들지에 초점을 맞추는 방식
    • 데이터(속성)와 그 데이터를 처리하는 기능(메서드)이 하나의 ‘객체’ 안에 함께 묶여 있음

음악 플레이어를 만드는 과정을 통해 두 방식의 차이를 직접 경험해 보자


절차 지향으로 음악 플레이어 만들기 🎧

먼저 가장 단순한 절차 지향 방식으로 코드를 작성해 보자.
데이터와 모든 로직이 main 메서드 안에 순서대로 나열됨

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// MusicPlayerMain1
public static void main(String[] args) {
    int volume = 0;
    boolean isOn = false;

    //음악 플레이어 켜기
    isOn = true;
    System.out.println("음악 플레이어를 시작합니다");

    //볼륨 증가
    volume++;
    System.out.println("음악 플레이어 볼륨:" + volume);
    
    // ... (중략) ...

    //음악 플레이어 끄기
    isOn = false;
    System.out.println("음악 플레이어를 종료합니다");
}

이 방식은 코드가 짧을 땐 직관적이지만, 기능이 복잡해질수록 데이터와 로직이 얽혀 유지보수가 매우 어려워진다!

개선 1단계: 데이터 묶기

관련 데이터(volume, isOn)를 MusicPlayerData라는 클래스로 묶어보자. 이렇게 하면 데이터 관리가 조금 더 용이해진다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// MusicPlayerData.java
public class MusicPlayerData {
    int volume = 0;
    boolean isOn = false;
}

// MusicPlayerMain2.java
public static void main(String[] args) {
    MusicPlayerData data = new MusicPlayerData();

    // 음악 플레이어 켜기
    data.isOn = true;
    // 볼륨 증가
    data.volume++;
    //...
}

개선 2단계: 메서드 추출

이제 각 기능을 별도의 메서드로 추출해서 모듈화해보자. 이렇게 하면 코드의 중복을 제거하고, 각 기능의 역할을 명확히 알 수 있다.

모듈화: 쉽게 이야기해서 레고 블럭과 비슷하다. 필요한 블럭을 가져다 꼽아서 사용하는 매커니즘과 같음.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// MusicPlayerMain3.java
public static void main(String[] args) {
    MusicPlayerData data = new MusicPlayerData();
    on(data);
    volumeUp(data);
    // ...
}

static void on(MusicPlayerData data) {
    data.isOn = true;
    System.out.println("음악 플레이어를 시작합니다");
}

static void volumeUp(MusicPlayerData data) {
    data.volume++;
    System.out.println("음악 플레이어 볼륨:" + data.volume);
}
//...

코드가 훨씬 깔끔해졌지만, 이것은 여전히 절차 지향의 한계를 가진다.
데이터는 MusicPlayerData 객체에, 기능은 MusicPlayerMain3의 메서드에 분리되어 있지만, 데이터와 기능이 따로 놀기 때문에 여전히 관리 포인트가 두 곳이다.


객체 지향 프로그래밍: 속성과 기능을 하나로

객체 지향의 핵심: 데이터(속성)와 기능(메서드)을 하나의 클래스 안에 온전히 묶는 것

MusicPlayer 클래스 안에 플레이어의 속성(volume, isOn)과 기능(on(), off(), volumeUp() 등)을 모두 넣어보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// MusicPlayer.java
public class MusicPlayer {
    int volume = 0;
    boolean isOn = false;

    void on() {
        isOn = true;
        System.out.println("음악 플레이어를 시작합니다");
    }

    void off() {
        isOn = false;
        System.out.println("음악 플레이어를 종료합니다");
    }

    void volumeUp() {
        volume++;
        System.out.println("음악 플레이어 볼륨:" + volume);
    }

    void volumeDown() {
        volume--;
        System.out.println("음악 플레이어 볼륨:" + volume);
    }

    void showStatus() {
        System.out.println("음악 플레이어 상태 확인");
        if (isOn) {
            System.out.println("음악 플레이어 ON, 볼륨:" + volume);
        } else {
            System.out.println("음악 플레이어 OFF");
        }
    }
}

이제 이 MusicPlayer를 사용하는 코드는 매우 단순하고 직관적으로 변하는 걸 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
// MusicPlayerMain4.java
public static void main(String[] args) {
    MusicPlayer player = new MusicPlayer();

    player.on();
    player.volumeUp();
    player.volumeUp();
    player.volumeDown();
    player.showStatus();
    player.off();
}

MusicPlayer를 사용하는 쪽에서는 이제 volume이나 isOn 같은 내부 데이터에 전혀 신경 쓸 필요가 없다!
그저 player라는 객체가 제공하는 기능(on(), volumeUp() 등)을 호출하기만 하면 된다.

이처럼 속성과 기능을 하나의 캡슐처럼 묶어서 외부에 필요한 기능만 제공하는 것을 캡슐화(Encapsulation)라고 한다.


왜 객체 지향이 좋은가? ✨

  • 직관적인 코드: 실제 세상의 사물을 다루는 것처럼 프로그래밍할 수 있어 코드가 더 친숙하고 이해하기 쉽다
  • 높은 유지보수성: 기능이 변경되어도 해당 객체의 내부만 수정하면 된다. 객체를 사용하는 쪽의 코드는 바꿀 필요가 없어 변경의 영향 범위가 적다.

객체 지향 프로그래밍은 단순히 데이터를 묶는 것을 넘어, 프로그램을 더 유연하고 확장 가능하게 만드는 강력한 패러다임이다!


핵심 정리

  • 절차 지향: 데이터기능이 분리되어 있고, 프로그램의 실행 순서에 초점을 둠
  • 객체 지향: 데이터(속성)기능(메서드)을 하나의 객체 안에 묶고, 객체 간의 상호작용에 초점을 둠
  • 캡슐화: 객체 지향의 핵심 특징으로, 속성과 기능을 하나로 묶어 내부 구현을 숨기고 외부에 필요한 기능만 제공
This post is licensed under CC BY 4.0 by the author.