본문 바로가기
프로그래밍/파이썬

10장. 클래스와 인스턴스 개념

by 사업가 Jay 2025. 5. 19.

프로그래밍을 하다 보면 반복되는 동작과 구조를 효율적으로 관리하고 싶을 때가 많습니다. 예를 들어, ‘학생’이라는 개념을 다룰 때마다 이름, 나이, 학번을 매번 변수로 선언하고 처리한다면 너무 비효율적이지 않을까요? 바로 이런 순간에 클래스(class)라는 개념이 등장합니다.

클래스는 현실 세계의 개념이나 사물을 소프트웨어 안에서 재현하기 위한 틀입니다. 그리고 이 틀을 바탕으로 만들어진 실체가 인스턴스(instance)입니다. 이 장에서는 클래스를 어떻게 정의하고, 인스턴스를 어떻게 생성하며, 이를 통해 객체지향 프로그래밍(OOP)의 기초를 어떻게 다질 수 있는지 함께 배워봅니다.

단순한 개념 설명에 그치지 않고, 직접 클래스를 정의하고 활용해보는 예제도 준비했으니, 이 글을 따라 오다 보면 자연스럽게 클래스와 인스턴스의 차이점과 역할이 몸에 익을 것입니다. 이제 객체 지향의 세계로 한 걸음 더 들어가볼까요?

  1. 클래스란 무엇인가?
  2. 인스턴스란 무엇인가?
  3. 클래스와 인스턴스의 차이
  4. Java에서 클래스 정의하기
  5. new 키워드와 인스턴스 생성
  6. 필드와 메서드 개념 이해
  7. 클래스 내부에서 this 키워드 사용하기
  8. 생성자(Constructor)의 역할과 활용
  9. 다수의 인스턴스 생성과 독립성 이해
  10. 클래스를 활용한 객체지향 프로그래밍 기초 정리

클래스란 무엇인가?

클래스(Class)는 객체지향 프로그래밍에서 가장 기본이 되는 구조입니다. 쉽게 말해, 클래스는 객체를 만들기 위한 설계도입니다. 우리가 자주 접하는 예시로는 '자동차', '학생', '책' 같은 것이 있습니다. 예를 들어 '학생'이라는 클래스를 정의하면, 이름, 나이, 학번 등의 정보를 포함할 수 있고, ‘공부한다’, ‘출석한다’ 같은 행동도 포함할 수 있습니다.

클래스는 속성(필드, 멤버 변수)과 동작(메서드)을 묶어서 하나의 구조체로 표현합니다. 이를 통해 코드의 재사용성과 유지보수성을 높이고, 현실 세계의 개념을 소프트웨어 안에서 보다 직관적으로 다룰 수 있게 됩니다.

아래는 간단한 Java 클래스 정의 예시입니다:


public class Student {
    String name;
    int age;
    String studentId;

    void study() {
        System.out.println(name + "이(가) 공부 중입니다.");
    }

    void attend() {
        System.out.println(name + "이(가) 출석했습니다.");
    }
}

위 예제에서 Student 클래스는 이름(name), 나이(age), 학번(studentId)라는 속성과, 공부(study), 출석(attend)이라는 행동을 가지고 있습니다. 이 구조는 현실의 ‘학생’ 개념을 프로그램 속에서 그대로 표현한 것입니다.

학습 포인트: 클래스는 하나의 객체가 가져야 할 상태와 행동을 정의합니다. 실제 프로그램에서는 이런 클래스를 바탕으로 여러 개의 인스턴스를 만들어 다수의 객체를 관리할 수 있게 됩니다. 다음 항목에서는 이 인스턴스라는 개념에 대해 구체적으로 살펴보겠습니다.

🔝 목차로 돌아가기

인스턴스란 무엇인가?

인스턴스(Instance)는 클래스라는 설계도를 바탕으로 실제 메모리에 생성된 객체(Object)를 말합니다. 즉, 클래스는 추상적인 개념이고, 인스턴스는 그 개념을 구체화한 실체입니다. 자동차 공장이 '자동차 설계도(클래스)'를 바탕으로 실제 자동차(인스턴스)를 만들어내는 것과 같은 원리입니다.

프로그래밍에서 클래스를 정의한다고 해서 프로그램이 실행되지는 않습니다. 그 클래스를 바탕으로 new 키워드를 사용해 인스턴스를 만들어야 비로소 동작하는 객체가 생성됩니다.

다음은 위에서 정의한 Student 클래스를 기반으로 인스턴스를 생성하는 예제입니다:


public class Main {
    public static void main(String[] args) {
        // Student 클래스의 인스턴스 생성
        Student s1 = new Student();
        s1.name = "홍길동";
        s1.age = 20;
        s1.studentId = "202501";

        // 메서드 호출
        s1.study();
        s1.attend();
    }
}

위 코드에서 Student s1 = new Student();Student 클래스의 인스턴스를 생성하는 코드입니다. 이렇게 생성된 s1 객체는 Student 클래스가 가진 모든 필드와 메서드를 사용할 수 있습니다.

객체를 여러 개 만들 수 있다는 점도 중요합니다. Student s2 = new Student();처럼 또 다른 학생 객체를 만들고, 그 객체에 다른 이름과 나이를 설정할 수 있어 유연한 데이터 표현이 가능해집니다.

학습 포인트: 클래스는 단 하나이지만, 인스턴스는 여러 개 생성할 수 있습니다. 인스턴스는 각각 독립적인 상태를 가지므로, 객체 단위로 데이터를 분리해 다룰 수 있게 해줍니다. 다음 항목에서는 클래스와 인스턴스의 차이를 표 형식으로 명확히 정리해보겠습니다.

🔝 목차로 돌아가기

클래스와 인스턴스의 차이

클래스와 인스턴스는 객체지향 프로그래밍에서 매우 밀접한 관계를 가지지만, 개념적으로는 분명히 구분됩니다. 클래스는 설계도(청사진)이고, 인스턴스는 그 설계도를 바탕으로 만들어낸 실제 객체입니다. 이 차이를 명확히 이해하는 것이 객체지향 프로그래밍의 첫걸음입니다.

아래 표는 클래스와 인스턴스의 차이를 정리한 것입니다:

구분 클래스 (Class) 인스턴스 (Instance)
정의 객체를 만들기 위한 설계도 클래스를 바탕으로 생성된 구체적인 객체
역할 속성과 동작을 정의 정의된 속성과 동작을 실제로 수행
메모리 정의만 되어 있고 메모리에 할당되지 않음 new로 생성될 때 메모리에 할당됨
수량 1개 클래스 정의 여러 개의 인스턴스 생성 가능
예시 Student s1, s2, s3 (각각 다른 학생 객체)

예를 들어, Student라는 클래스를 만들어두고 Student s1 = new Student();를 실행하면 s1이라는 인스턴스가 생성됩니다. 같은 클래스를 사용해 s2, s3도 만들 수 있으며, 이들은 서로 독립된 상태를 갖습니다.

학습 포인트: 클래스와 인스턴스는 “설계도와 실체”의 관계로 이해하면 가장 쉽습니다. 클래스는 어떤 객체를 만들어야 하는지 정의하고, 인스턴스는 그 정의를 바탕으로 만들어진 실제 객체입니다. 다음 항목에서는 Java에서 클래스를 어떻게 정의하는지 실습해보겠습니다.

🔝 목차로 돌아가기

Java에서 클래스 정의하기

Java에서 클래스를 정의하는 것은 매우 간단합니다. 클래스는 class 키워드를 사용하여 선언하며, 보통 하나의 Java 파일에는 하나의 public 클래스를 정의합니다. 클래스 안에는 필드(멤버 변수), 생성자, 메서드 등이 포함되어 객체의 상태와 동작을 정의합니다.

기본적인 클래스 정의 문법은 다음과 같습니다:


public class 클래스이름 {
    // 필드 (멤버 변수)
    자료형 변수이름;

    // 생성자 (옵션)
    클래스이름() {
        // 초기화 작업
    }

    // 메서드
    반환형 메서드이름(매개변수) {
        // 동작 정의
    }
}

이제 위 문법을 활용해서 실제로 Student 클래스를 정의해보겠습니다:


public class Student {
    // 필드
    String name;
    int age;
    String studentId;

    // 메서드
    void study() {
        System.out.println(name + "이(가) 공부합니다.");
    }

    void introduce() {
        System.out.println("안녕하세요, 제 이름은 " + name + "이고, 나이는 " + age + "살입니다.");
    }
}

위 클래스는 세 개의 필드와 두 개의 메서드를 갖고 있으며, 객체가 생성된 후 데이터를 저장하고, 특정 동작을 수행할 수 있는 구조입니다. 이 클래스는 다른 클래스에서 인스턴스를 생성하여 실제 동작하게 됩니다.

클래스 이름은 보통 대문자로 시작하고, 카멜 표기법(CamelCase)을 따르는 것이 자바의 관례입니다. 또한, 각 클래스는 자신의 역할에 충실해야 하며, 너무 많은 책임을 가지지 않도록 설계하는 것이 객체지향의 좋은 습관입니다.

학습 포인트: 클래스를 정의하는 것은 단순한 선언 이상의 의미를 가집니다. 필드와 메서드의 구성은 객체가 어떤 데이터를 가지고 어떤 동작을 수행할 수 있는지를 결정하며, 이후 인스턴스 생성과 동작 호출의 기반이 됩니다. 다음 장에서는 new 키워드를 이용해 인스턴스를 생성하는 방법을 살펴보겠습니다.

🔝 목차로 돌아가기

new 키워드와 인스턴스 생성

자바에서 클래스를 정의한 후, 그것을 실제로 사용하려면 반드시 인스턴스(객체)를 생성해야 합니다. 이때 사용하는 것이 바로 new 키워드입니다. new는 클래스의 생성자를 호출해 새로운 객체를 메모리에 할당하고, 그 주소값을 참조 변수에 저장해줍니다.

즉, new는 설계도(class)를 바탕으로 실제 제품(instance)을 만드는 ‘생산 명령어’라 할 수 있습니다. 생성된 인스턴스는 Heap 영역에 메모리가 할당되고, 참조 변수는 그 주소를 Stack 영역에 저장합니다.

다음은 Student 클래스의 인스턴스를 생성하는 예제입니다:


public class Main {
    public static void main(String[] args) {
        // Student 클래스 인스턴스 생성
        Student s1 = new Student();
        s1.name = "김코딩";
        s1.age = 21;
        s1.studentId = "S202504";

        // 메서드 호출
        s1.introduce();   // 안녕하세요, 제 이름은 김코딩이고, 나이는 21살입니다.
        s1.study();       // 김코딩이(가) 공부합니다.
    }
}

위 코드에서 Student s1 = new Student();는 다음 단계를 거칩니다:

  1. new로 Student 클래스의 생성자 호출
  2. Heap 메모리에 Student 객체 생성
  3. 생성된 객체의 주소를 s1 변수에 저장
  4. s1을 통해 해당 객체의 필드 및 메서드에 접근

하나의 클래스를 기반으로 여러 개의 객체를 만들 수 있으며, 각각 독립적인 데이터를 가질 수 있다는 것이 핵심입니다.

학습 포인트: new 키워드는 단순히 객체를 만드는 역할을 넘어서, 클래스에서 정의한 설계대로 실제 객체를 구성하고 메모리에 적재하는 핵심 역할을 합니다. 이 과정은 객체지향 프로그래밍에서 가장 중요한 '실체화' 단계이므로 반드시 익숙해져야 합니다. 다음 항목에서는 클래스 내부에 정의할 수 있는 구성요소인 필드와 메서드를 자세히 알아보겠습니다.

🔝 목차로 돌아가기

필드와 메서드 개념 이해

클래스는 객체가 가지는 상태와 동작을 정의하는 틀입니다. 이때 객체의 상태(state)필드(Field)를 통해 정의하고, 동작(behavior)메서드(Method)를 통해 정의합니다. 필드와 메서드는 클래스의 가장 기본적인 구성 요소이며, 인스턴스가 생성되었을 때 객체 내부의 데이터와 기능을 구체화하는 역할을 합니다.

🧩 필드(Field)

필드는 클래스 내부에서 선언된 변수로, 객체가 가지는 정보를 저장합니다. 예를 들어 이름, 나이, 학번 같은 값이 여기에 해당합니다.

🛠️ 메서드(Method)

메서드는 객체가 수행할 수 있는 동작을 정의하는 함수입니다. 클래스 내부에 정의되며, 객체를 통해 호출됩니다. 예를 들어 공부한다(study), 자기소개한다(introduce) 같은 동작이 메서드입니다.

아래는 필드와 메서드를 포함한 클래스 정의 예제입니다:


public class Student {
    // 필드 정의
    String name;
    int age;

    // 메서드 정의
    void study() {
        System.out.println(name + "이(가) 공부 중입니다.");
    }

    void introduce() {
        System.out.println("안녕하세요, 저는 " + name + "이고, 나이는 " + age + "살입니다.");
    }
}

그리고 이를 사용하는 코드 예시는 다음과 같습니다:


public class Main {
    public static void main(String[] args) {
        Student s = new Student();
        s.name = "이자바";
        s.age = 22;

        s.introduce();
        s.study();
    }
}

이 예제에서 s.name, s.age는 필드 값을 설정하는 것이고, s.introduce(), s.study()는 메서드를 호출하여 객체가 동작하도록 만드는 과정입니다.

학습 포인트: 필드와 메서드는 클래스의 내부를 구성하는 핵심 요소입니다. 필드는 객체의 데이터를 저장하고, 메서드는 그 데이터를 기반으로 동작을 수행합니다. 이를 통해 클래스는 단순한 데이터 묶음이 아닌, **기능을 가진 데이터 단위(객체)**로 진화하게 됩니다. 다음 장에서는 클래스 내부에서 메서드 간 데이터 전달에 자주 사용되는 this 키워드에 대해 알아보겠습니다.

🔝 목차로 돌아가기

클래스 내부에서 this 키워드 사용하기

자바에서 this 키워드는 클래스 내부에서 현재 인스턴스 자신을 가리키는 참조 변수입니다. 객체 지향 프로그래밍에서 객체는 각각 독립적인 상태(필드)를 가지므로, 클래스 내부의 메서드에서 자신의 필드를 명확하게 구분할 필요가 있습니다. 특히, 메서드의 매개변수와 필드 이름이 같을 때 this를 사용하지 않으면 의도치 않은 동작이 발생할 수 있습니다.

다음은 this 키워드의 주요 사용 사례입니다:

  1. 필드와 메서드 매개변수 이름이 겹칠 때 명확히 구분
  2. 다른 메서드를 호출할 때 현재 객체를 넘기기 위해
  3. 생성자에서 생성자 오버로딩을 호출할 때 (this())

아래는 this 키워드를 사용하는 예제입니다:


public class Student {
    String name;
    int age;

    // 생성자: 매개변수와 필드 이름이 같음
    Student(String name, int age) {
        this.name = name;  // this 없으면 로컬 변수에 값을 할당하게 됨
        this.age = age;
    }

    void introduce() {
        System.out.println("안녕하세요, 저는 " + this.name + "이고, " + this.age + "살입니다.");
    }
}

그리고 이 클래스를 사용하는 코드는 다음과 같습니다:


public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("홍길동", 20);
        s1.introduce();
    }
}

위 예제에서 생성자의 매개변수 nameage는 필드 이름과 같기 때문에, this.name = name; 형태로 구분해주어야 합니다. 만약 this를 생략하면 매개변수인 지역 변수에 다시 자신을 할당하는 결과가 되어, 필드는 여전히 초기화되지 않습니다.

학습 포인트: this 키워드는 클래스 내부에서 자기 자신의 인스턴스를 명확하게 참조하기 위해 매우 중요한 역할을 합니다. 특히 생성자나 메서드 내에서 필드와 매개변수 이름이 중복되는 경우, this 없이 코드를 작성하면 의도와 다른 결과를 초래할 수 있습니다. 다음 장에서는 생성자(Constructor)의 개념과 역할을 구체적으로 알아보겠습니다.

🔝 목차로 돌아가기

생성자(Constructor)의 역할과 활용

생성자(Constructor)는 클래스의 인스턴스를 생성할 때 자동으로 호출되는 특수한 메서드입니다. 주된 목적은 객체 생성 시 필드(멤버 변수)를 초기화하는 것입니다. 메서드처럼 보이지만 반환형이 없고, 클래스 이름과 정확히 동일한 이름을 사용해야 합니다.

생성자는 명시적으로 정의하지 않아도 기본 생성자(Default Constructor)가 자동으로 제공되지만, 사용자가 생성자를 직접 정의하면 기본 생성자는 제공되지 않습니다. 따라서 여러 생성자를 만들 경우 오버로딩(Overloading)을 통해 다양한 초기화 방법을 제공할 수 있습니다.

아래는 생성자를 활용한 Student 클래스의 예제입니다:


public class Student {
    String name;
    int age;

    // 사용자 정의 생성자
    Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    void introduce() {
        System.out.println("저는 " + name + "이고, 나이는 " + age + "살입니다.");
    }
}

이제 이 클래스를 사용하는 코드를 보겠습니다:


public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("김자바", 23);
        s1.introduce();  // 출력: 저는 김자바이고, 나이는 23살입니다.
    }
}

위 예제에서는 new Student("김자바", 23)를 통해 생성자가 호출되고, nameage가 초기화됩니다. 이후 introduce() 메서드를 호출하면 설정된 값이 출력됩니다.

🧪 생성자 오버로딩

생성자는 매개변수 개수나 타입에 따라 여러 개 정의할 수 있습니다. 이를 오버로딩이라 하며, 다양한 초기화 방법을 제공합니다.


public class Student {
    String name;
    int age;

    // 기본 생성자
    Student() {
        this.name = "이름없음";
        this.age = 0;
    }

    // 매개변수가 있는 생성자
    Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

학습 포인트: 생성자는 객체를 만들면서 동시에 필요한 데이터를 설정할 수 있게 해주는 유용한 도구입니다. 매개변수를 다양하게 구성해 오버로딩을 제공하면, 객체 사용의 유연성도 높아집니다. 다음 장에서는 클래스를 기반으로 생성된 여러 인스턴스가 어떻게 서로 독립적으로 존재하는지를 알아보겠습니다.

🔝 목차로 돌아가기

다수의 인스턴스 생성과 독립성 이해

클래스는 하나의 설계도이지만, 그 설계도를 기반으로 여러 개의 인스턴스(객체)를 만들 수 있습니다. 그리고 이렇게 만들어진 각 인스턴스는 서로 독립적인 상태(필드 값)를 유지합니다. 즉, 하나의 객체가 변경된다고 해서 다른 객체에 영향을 주지 않습니다.

예를 들어, 같은 Student 클래스를 사용해 두 명의 학생을 만들고, 각자의 이름과 나이를 설정할 수 있습니다. 이때 객체들은 서로 전혀 영향을 주지 않고 각자의 데이터를 독립적으로 가지고 있습니다.

다음은 다수의 인스턴스를 생성하고 각각을 독립적으로 사용하는 예제입니다:


public class Student {
    String name;
    int age;

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

    void introduce() {
        System.out.println("안녕하세요, 저는 " + name + "이고, " + age + "살입니다.");
    }
}

public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("홍길동", 20);
        Student s2 = new Student("임꺽정", 25);

        s1.introduce();  // 홍길동 정보 출력
        s2.introduce();  // 임꺽정 정보 출력

        // s1의 age를 수정
        s1.age = 21;

        // 각 인스턴스 상태 확인
        s1.introduce();  // 나이 21로 변경됨
        s2.introduce();  // 여전히 나이 25
    }
}

위 코드에서 s1s2는 같은 클래스를 기반으로 만들어졌지만, 각각 독립된 메모리 공간을 가지므로 s1.age를 변경해도 s2.age에는 영향을 주지 않습니다.

이러한 객체 간 독립성은 프로그램을 모듈화하고 확장하는 데 큰 장점을 제공합니다. 동일한 구조를 반복해서 재사용할 수 있으면서도, 각 객체마다 고유한 데이터를 가질 수 있기 때문입니다.

학습 포인트: 객체지향에서 클래스는 재사용 가능한 구조이며, 인스턴스를 통해 그 구조를 다양하게 활용할 수 있습니다. 다수의 인스턴스를 만들 수 있고, 이들은 독립적인 데이터를 보존하므로 복잡한 프로그램도 유연하게 설계할 수 있습니다. 이제 마지막으로, 우리가 지금까지 배운 클래스와 인스턴스 개념을 정리하고 객체지향 프로그래밍의 기초를 마무리해보겠습니다.

🔝 목차로 돌아가기

클래스를 활용한 객체지향 프로그래밍 기초 정리

지금까지 우리는 클래스와 인스턴스의 개념, 그리고 그 활용법에 대해 단계별로 살펴보았습니다. 객체지향 프로그래밍(Object-Oriented Programming, OOP)의 시작은 바로 클래스를 정의하고, 인스턴스를 생성하는 것입니다. 이는 데이터와 기능을 하나로 묶어 자기 자신만의 책임과 동작을 가진 객체를 만들 수 있도록 도와줍니다.

클래스는 일종의 설계도이며, 그 설계도를 바탕으로 생성된 인스턴스는 현실 세계의 사물을 프로그램 안에서 표현하는 방식입니다. 이러한 객체 단위의 사고방식은 복잡한 프로그램을 구성할 때 매우 유리합니다. 예를 들어, 학생, 자동차, 주문서, 계좌 등 현실의 어떤 대상이든 클래스로 정의할 수 있으며, 각 인스턴스는 고유한 상태와 동작을 갖습니다.

아래는 클래스와 인스턴스를 바탕으로 구성된 객체지향 사고의 기본 구조입니다:


public class BankAccount {
    String owner;
    int balance;

    // 생성자
    BankAccount(String owner, int balance) {
        this.owner = owner;
        this.balance = balance;
    }

    void deposit(int amount) {
        balance += amount;
        System.out.println(amount + "원 입금되었습니다.");
    }

    void withdraw(int amount) {
        if (balance >= amount) {
            balance -= amount;
            System.out.println(amount + "원 출금되었습니다.");
        } else {
            System.out.println("잔액이 부족합니다.");
        }
    }

    void showBalance() {
        System.out.println(owner + "님의 잔액: " + balance + "원");
    }
}

이제 이를 사용하는 코드는 다음과 같을 수 있습니다:


public class Main {
    public static void main(String[] args) {
        BankAccount account = new BankAccount("홍길동", 10000);
        account.deposit(5000);
        account.withdraw(3000);
        account.showBalance();
    }
}

위 예제는 현실의 은행 계좌를 하나의 객체로 표현한 것입니다. BankAccount는 상태(owner, balance)와 동작(deposit, withdraw)을 함께 가지고 있으며, 각 인스턴스는 자신의 책임을 갖고 동작합니다.

학습 포인트: 클래스와 인스턴스를 이해하면 객체지향 프로그래밍의 첫 문을 열게 됩니다. 이 개념은 상속, 캡슐화, 다형성 같은 더 깊은 개념의 바탕이 되며, 프로그램을 더 유연하고 견고하게 만들어줍니다. 앞으로 다양한 미니 프로젝트나 실전 개발을 통해 클래스 설계에 익숙해진다면, 객체지향 사고는 자연스럽게 체득될 것입니다.

🔝 목차로 돌아가기